@@ -24,8 +24,16 @@ vi.mock('@object-ui/components', async (importOriginal) => {
2424 return {
2525 ...actual ,
2626 cn : ( ...inputs : any [ ] ) => inputs . filter ( Boolean ) . join ( ' ' ) ,
27- Button : ( { children, onClick, title } : any ) => < button onClick = { onClick } title = { title } > { children } </ button > ,
28- Input : ( props : any ) => < input { ...props } data-testid = "mock-input" /> ,
27+ Button : ( { children, onClick, title, ...rest } : any ) => < button onClick = { onClick } title = { title } { ...rest } > { children } </ button > ,
28+ Input : ( props : any ) => < input { ...props } /> ,
29+ Switch : ( { checked, onCheckedChange, ...props } : any ) => (
30+ < button
31+ role = "switch"
32+ aria-checked = { checked }
33+ onClick = { ( ) => onCheckedChange ?.( ! checked ) }
34+ { ...props }
35+ />
36+ ) ,
2937 ToggleGroup : ( { children, value, onValueChange } : any ) => < div data-value = { value } onChange = { onValueChange } > { children } </ div > ,
3038 ToggleGroupItem : ( { children, value } : any ) => < button data-value = { value } > { children } </ button > ,
3139 Tabs : ( { value, onValueChange, children } : any ) => (
@@ -338,4 +346,90 @@ describe('ObjectView Component', () => {
338346 const footer = await screen . findByTestId ( 'record-count-footer' ) ;
339347 expect ( footer ) . toBeInTheDocument ( ) ;
340348 } ) ;
349+
350+ it ( 'calls dataSource.updateViewConfig when saving view config' , async ( ) => {
351+ const mockUpdateViewConfig = vi . fn ( ) . mockResolvedValue ( { } ) ;
352+ const dsWithUpdate = {
353+ ...mockDataSource ,
354+ updateViewConfig : mockUpdateViewConfig ,
355+ } ;
356+ mockAuthUser = { id : 'u1' , name : 'Admin' , role : 'admin' } ;
357+ mockUseParams . mockReturnValue ( { objectName : 'opportunity' } ) ;
358+
359+ render ( < ObjectView dataSource = { dsWithUpdate } objects = { mockObjects } onEdit = { vi . fn ( ) } /> ) ;
360+
361+ // Open config panel
362+ fireEvent . click ( screen . getByTitle ( 'console.objectView.designTools' ) ) ;
363+ fireEvent . click ( screen . getByText ( 'console.objectView.editView' ) ) ;
364+ expect ( screen . getByTestId ( 'view-config-panel' ) ) . toBeInTheDocument ( ) ;
365+
366+ // Wait for draft to be initialized from activeView, then modify
367+ const titleInput = await screen . findByDisplayValue ( 'All Opportunities' ) ;
368+ fireEvent . change ( titleInput , { target : { value : 'My Custom View' } } ) ;
369+
370+ // Save button should appear after dirty state
371+ const saveBtn = await screen . findByTestId ( 'view-config-save' ) ;
372+ fireEvent . click ( saveBtn ) ;
373+
374+ expect ( mockUpdateViewConfig ) . toHaveBeenCalledOnce ( ) ;
375+ expect ( mockUpdateViewConfig ) . toHaveBeenCalledWith (
376+ 'opportunity' ,
377+ 'all' ,
378+ expect . objectContaining ( { label : 'My Custom View' } ) ,
379+ ) ;
380+ } ) ;
381+
382+ it ( 'logs warning when dataSource.updateViewConfig is not available' , async ( ) => {
383+ const warnSpy = vi . spyOn ( console , 'warn' ) . mockImplementation ( ( ) => { } ) ;
384+ mockAuthUser = { id : 'u1' , name : 'Admin' , role : 'admin' } ;
385+ mockUseParams . mockReturnValue ( { objectName : 'opportunity' } ) ;
386+
387+ render ( < ObjectView dataSource = { mockDataSource } objects = { mockObjects } onEdit = { vi . fn ( ) } /> ) ;
388+
389+ // Open config panel
390+ fireEvent . click ( screen . getByTitle ( 'console.objectView.designTools' ) ) ;
391+ fireEvent . click ( screen . getByText ( 'console.objectView.editView' ) ) ;
392+
393+ // Make a change and save
394+ const titleInput = await screen . findByDisplayValue ( 'All Opportunities' ) ;
395+ fireEvent . change ( titleInput , { target : { value : 'Changed' } } ) ;
396+ const saveBtn = await screen . findByTestId ( 'view-config-save' ) ;
397+ fireEvent . click ( saveBtn ) ;
398+
399+ expect ( warnSpy ) . toHaveBeenCalledWith (
400+ expect . stringContaining ( 'updateViewConfig is not available' ) ,
401+ ) ;
402+ warnSpy . mockRestore ( ) ;
403+ } ) ;
404+
405+ it ( 'logs error when dataSource.updateViewConfig rejects' , async ( ) => {
406+ const errorSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
407+ const dsWithFailingUpdate = {
408+ ...mockDataSource ,
409+ updateViewConfig : vi . fn ( ) . mockRejectedValue ( new Error ( 'Network error' ) ) ,
410+ } ;
411+ mockAuthUser = { id : 'u1' , name : 'Admin' , role : 'admin' } ;
412+ mockUseParams . mockReturnValue ( { objectName : 'opportunity' } ) ;
413+
414+ render ( < ObjectView dataSource = { dsWithFailingUpdate } objects = { mockObjects } onEdit = { vi . fn ( ) } /> ) ;
415+
416+ // Open config panel
417+ fireEvent . click ( screen . getByTitle ( 'console.objectView.designTools' ) ) ;
418+ fireEvent . click ( screen . getByText ( 'console.objectView.editView' ) ) ;
419+
420+ // Make a change and save
421+ const titleInput = await screen . findByDisplayValue ( 'All Opportunities' ) ;
422+ fireEvent . change ( titleInput , { target : { value : 'Failed' } } ) ;
423+ const saveBtn = await screen . findByTestId ( 'view-config-save' ) ;
424+ fireEvent . click ( saveBtn ) ;
425+
426+ // Wait for the promise rejection to be caught
427+ await vi . waitFor ( ( ) => {
428+ expect ( errorSpy ) . toHaveBeenCalledWith (
429+ expect . stringContaining ( 'Failed to persist view config' ) ,
430+ expect . any ( Error ) ,
431+ ) ;
432+ } ) ;
433+ errorSpy . mockRestore ( ) ;
434+ } ) ;
341435} ) ;
0 commit comments