@@ -2433,4 +2433,341 @@ describe('ViewConfigPanel', () => {
24332433 fireEvent . click ( screen . getByTestId ( 'toggle-virtualScroll' ) ) ;
24342434 expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'virtualScroll' , true ) ;
24352435 } ) ;
2436+
2437+ // ── Spec alignment: toggle interaction tests for all switch fields ──
2438+
2439+ it ( 'toggles collapseAllByDefault and calls onViewUpdate' , ( ) => {
2440+ const onViewUpdate = vi . fn ( ) ;
2441+ render (
2442+ < ViewConfigPanel
2443+ open = { true }
2444+ onClose = { vi . fn ( ) }
2445+ activeView = { mockActiveView }
2446+ objectDef = { mockObjectDef }
2447+ onViewUpdate = { onViewUpdate }
2448+ />
2449+ ) ;
2450+
2451+ fireEvent . click ( screen . getByTestId ( 'toggle-collapseAllByDefault' ) ) ;
2452+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'collapseAllByDefault' , true ) ;
2453+ } ) ;
2454+
2455+ it ( 'toggles showDescription and calls onViewUpdate' , ( ) => {
2456+ const onViewUpdate = vi . fn ( ) ;
2457+ render (
2458+ < ViewConfigPanel
2459+ open = { true }
2460+ onClose = { vi . fn ( ) }
2461+ activeView = { mockActiveView }
2462+ objectDef = { mockObjectDef }
2463+ onViewUpdate = { onViewUpdate }
2464+ />
2465+ ) ;
2466+
2467+ fireEvent . click ( screen . getByTestId ( 'toggle-showDescription' ) ) ;
2468+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'showDescription' , false ) ;
2469+ } ) ;
2470+
2471+ it ( 'toggles clickIntoRecordDetails and calls onViewUpdate' , ( ) => {
2472+ const onViewUpdate = vi . fn ( ) ;
2473+ render (
2474+ < ViewConfigPanel
2475+ open = { true }
2476+ onClose = { vi . fn ( ) }
2477+ activeView = { mockActiveView }
2478+ objectDef = { mockObjectDef }
2479+ onViewUpdate = { onViewUpdate }
2480+ />
2481+ ) ;
2482+
2483+ fireEvent . click ( screen . getByTestId ( 'toggle-clickIntoRecordDetails' ) ) ;
2484+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'clickIntoRecordDetails' , false ) ;
2485+ } ) ;
2486+
2487+ it ( 'toggles addDeleteRecordsInline and calls onViewUpdate' , ( ) => {
2488+ const onViewUpdate = vi . fn ( ) ;
2489+ render (
2490+ < ViewConfigPanel
2491+ open = { true }
2492+ onClose = { vi . fn ( ) }
2493+ activeView = { mockActiveView }
2494+ objectDef = { mockObjectDef }
2495+ onViewUpdate = { onViewUpdate }
2496+ />
2497+ ) ;
2498+
2499+ fireEvent . click ( screen . getByTestId ( 'toggle-addDeleteRecordsInline' ) ) ;
2500+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'addDeleteRecordsInline' , false ) ;
2501+ } ) ;
2502+
2503+ // ── Conditional rendering: sharing visibility hidden when disabled ──
2504+
2505+ it ( 'hides sharing visibility select when sharing is not enabled' , ( ) => {
2506+ render (
2507+ < ViewConfigPanel
2508+ open = { true }
2509+ onClose = { vi . fn ( ) }
2510+ activeView = { { ...mockActiveView , sharing : { enabled : false } } }
2511+ objectDef = { mockObjectDef }
2512+ />
2513+ ) ;
2514+
2515+ expect ( screen . getByTestId ( 'toggle-sharing-enabled' ) ) . toBeInTheDocument ( ) ;
2516+ expect ( screen . queryByTestId ( 'select-sharing-visibility' ) ) . not . toBeInTheDocument ( ) ;
2517+ } ) ;
2518+
2519+ it ( 'hides sharing visibility select when sharing is undefined' , ( ) => {
2520+ render (
2521+ < ViewConfigPanel
2522+ open = { true }
2523+ onClose = { vi . fn ( ) }
2524+ activeView = { mockActiveView }
2525+ objectDef = { mockObjectDef }
2526+ />
2527+ ) ;
2528+
2529+ expect ( screen . queryByTestId ( 'select-sharing-visibility' ) ) . not . toBeInTheDocument ( ) ;
2530+ } ) ;
2531+
2532+ // ── Conditional rendering: navigation width hidden when mode is page ──
2533+
2534+ it ( 'hides navigation width when mode is page' , ( ) => {
2535+ render (
2536+ < ViewConfigPanel
2537+ open = { true }
2538+ onClose = { vi . fn ( ) }
2539+ activeView = { { ...mockActiveView , navigation : { mode : 'page' } } }
2540+ objectDef = { mockObjectDef }
2541+ />
2542+ ) ;
2543+
2544+ expect ( screen . queryByTestId ( 'input-navigation-width' ) ) . not . toBeInTheDocument ( ) ;
2545+ } ) ;
2546+
2547+ it ( 'hides navigation openNewTab when mode is drawer' , ( ) => {
2548+ render (
2549+ < ViewConfigPanel
2550+ open = { true }
2551+ onClose = { vi . fn ( ) }
2552+ activeView = { { ...mockActiveView , navigation : { mode : 'drawer' } } }
2553+ objectDef = { mockObjectDef }
2554+ />
2555+ ) ;
2556+
2557+ expect ( screen . queryByTestId ( 'toggle-navigation-openNewTab' ) ) . not . toBeInTheDocument ( ) ;
2558+ } ) ;
2559+
2560+ // ── All 5 rowHeight buttons: click each value ──
2561+
2562+ it ( 'clicks all 5 rowHeight buttons and verifies onViewUpdate for each' , ( ) => {
2563+ const onViewUpdate = vi . fn ( ) ;
2564+ render (
2565+ < ViewConfigPanel
2566+ open = { true }
2567+ onClose = { vi . fn ( ) }
2568+ activeView = { mockActiveView }
2569+ objectDef = { mockObjectDef }
2570+ onViewUpdate = { onViewUpdate }
2571+ />
2572+ ) ;
2573+
2574+ const heights = [ 'compact' , 'short' , 'medium' , 'tall' , 'extra_tall' ] ;
2575+ heights . forEach ( ( h ) => {
2576+ fireEvent . click ( screen . getByTestId ( `row-height-${ h } ` ) ) ;
2577+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'rowHeight' , h ) ;
2578+ } ) ;
2579+ expect ( onViewUpdate ) . toHaveBeenCalledTimes ( heights . length ) ;
2580+ } ) ;
2581+
2582+ // ── Boundary: empty actions input ──
2583+
2584+ it ( 'handles empty bulkActions input gracefully' , ( ) => {
2585+ const onViewUpdate = vi . fn ( ) ;
2586+ render (
2587+ < ViewConfigPanel
2588+ open = { true }
2589+ onClose = { vi . fn ( ) }
2590+ activeView = { { ...mockActiveView , bulkActions : [ 'delete' ] } }
2591+ objectDef = { mockObjectDef }
2592+ onViewUpdate = { onViewUpdate }
2593+ />
2594+ ) ;
2595+
2596+ fireEvent . click ( screen . getByText ( 'console.objectView.bulkActions' ) ) ;
2597+ fireEvent . change ( screen . getByTestId ( 'input-bulkActions' ) , { target : { value : '' } } ) ;
2598+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'bulkActions' , [ ] ) ;
2599+ } ) ;
2600+
2601+ it ( 'handles empty rowActions input gracefully' , ( ) => {
2602+ const onViewUpdate = vi . fn ( ) ;
2603+ render (
2604+ < ViewConfigPanel
2605+ open = { true }
2606+ onClose = { vi . fn ( ) }
2607+ activeView = { { ...mockActiveView , rowActions : [ 'edit' ] } }
2608+ objectDef = { mockObjectDef }
2609+ onViewUpdate = { onViewUpdate }
2610+ />
2611+ ) ;
2612+
2613+ fireEvent . click ( screen . getByText ( 'console.objectView.rowActions' ) ) ;
2614+ fireEvent . change ( screen . getByTestId ( 'input-rowActions' ) , { target : { value : '' } } ) ;
2615+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'rowActions' , [ ] ) ;
2616+ } ) ;
2617+
2618+ // ── Boundary: long label in title input ──
2619+
2620+ it ( 'handles long label value in view title input' , ( ) => {
2621+ const longLabel = 'A' . repeat ( 200 ) ;
2622+ const onViewUpdate = vi . fn ( ) ;
2623+ render (
2624+ < ViewConfigPanel
2625+ open = { true }
2626+ onClose = { vi . fn ( ) }
2627+ activeView = { { ...mockActiveView , label : longLabel } }
2628+ objectDef = { mockObjectDef }
2629+ onViewUpdate = { onViewUpdate }
2630+ />
2631+ ) ;
2632+
2633+ expect ( screen . getByTestId ( 'view-title-input' ) ) . toHaveValue ( longLabel ) ;
2634+ } ) ;
2635+
2636+ // ── Boundary: special characters in emptyState fields ──
2637+
2638+ it ( 'handles special characters in emptyState fields' , ( ) => {
2639+ const onViewUpdate = vi . fn ( ) ;
2640+ render (
2641+ < ViewConfigPanel
2642+ open = { true }
2643+ onClose = { vi . fn ( ) }
2644+ activeView = { mockActiveView }
2645+ objectDef = { mockObjectDef }
2646+ onViewUpdate = { onViewUpdate }
2647+ />
2648+ ) ;
2649+
2650+ fireEvent . change ( screen . getByTestId ( 'input-emptyState-title' ) , { target : { value : '<script>alert("xss")</script>' } } ) ;
2651+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'emptyState' , expect . objectContaining ( {
2652+ title : '<script>alert("xss")</script>' ,
2653+ } ) ) ;
2654+ } ) ;
2655+
2656+ // ── pageSizeOptions input interaction ──
2657+
2658+ it ( 'updates pageSizeOptions via input' , ( ) => {
2659+ const onViewUpdate = vi . fn ( ) ;
2660+ render (
2661+ < ViewConfigPanel
2662+ open = { true }
2663+ onClose = { vi . fn ( ) }
2664+ activeView = { mockActiveView }
2665+ objectDef = { mockObjectDef }
2666+ onViewUpdate = { onViewUpdate }
2667+ />
2668+ ) ;
2669+
2670+ const input = screen . getByTestId ( 'input-pagination-pageSizeOptions' ) ;
2671+ fireEvent . change ( input , { target : { value : '10, 25, 50, 100' } } ) ;
2672+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'pagination' , expect . objectContaining ( {
2673+ pageSizeOptions : [ 10 , 25 , 50 , 100 ] ,
2674+ } ) ) ;
2675+ } ) ;
2676+
2677+ it ( 'filters invalid pageSizeOptions values (non-positive, NaN)' , ( ) => {
2678+ const onViewUpdate = vi . fn ( ) ;
2679+ render (
2680+ < ViewConfigPanel
2681+ open = { true }
2682+ onClose = { vi . fn ( ) }
2683+ activeView = { mockActiveView }
2684+ objectDef = { mockObjectDef }
2685+ onViewUpdate = { onViewUpdate }
2686+ />
2687+ ) ;
2688+
2689+ const input = screen . getByTestId ( 'input-pagination-pageSizeOptions' ) ;
2690+ fireEvent . change ( input , { target : { value : 'abc, 50, -10, 0' } } ) ;
2691+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'pagination' , expect . objectContaining ( {
2692+ pageSizeOptions : [ 50 ] ,
2693+ } ) ) ;
2694+ } ) ;
2695+
2696+ // ── Boundary: densityMode enum selection ──
2697+
2698+ it ( 'changes densityMode to all enum values' , ( ) => {
2699+ const onViewUpdate = vi . fn ( ) ;
2700+ render (
2701+ < ViewConfigPanel
2702+ open = { true }
2703+ onClose = { vi . fn ( ) }
2704+ activeView = { mockActiveView }
2705+ objectDef = { mockObjectDef }
2706+ onViewUpdate = { onViewUpdate }
2707+ />
2708+ ) ;
2709+
2710+ const select = screen . getByTestId ( 'select-densityMode' ) ;
2711+ [ 'compact' , 'comfortable' , 'spacious' ] . forEach ( ( mode ) => {
2712+ fireEvent . change ( select , { target : { value : mode } } ) ;
2713+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'densityMode' , mode ) ;
2714+ } ) ;
2715+ } ) ;
2716+
2717+ // ── Conditional rendering: addRecord sub-editor hidden when not enabled ──
2718+
2719+ it ( 'hides addRecord sub-editor when addRecordViaForm is false' , ( ) => {
2720+ render (
2721+ < ViewConfigPanel
2722+ open = { true }
2723+ onClose = { vi . fn ( ) }
2724+ activeView = { { ...mockActiveView , addRecordViaForm : false } }
2725+ objectDef = { mockObjectDef }
2726+ />
2727+ ) ;
2728+
2729+ expect ( screen . queryByTestId ( 'select-addRecord-position' ) ) . not . toBeInTheDocument ( ) ;
2730+ } ) ;
2731+
2732+ // ── Sharing visibility select changes value ──
2733+
2734+ it ( 'changes sharing visibility and calls onViewUpdate' , ( ) => {
2735+ const onViewUpdate = vi . fn ( ) ;
2736+ render (
2737+ < ViewConfigPanel
2738+ open = { true }
2739+ onClose = { vi . fn ( ) }
2740+ activeView = { { ...mockActiveView , sharing : { enabled : true , visibility : 'private' } } }
2741+ objectDef = { mockObjectDef }
2742+ onViewUpdate = { onViewUpdate }
2743+ />
2744+ ) ;
2745+
2746+ fireEvent . change ( screen . getByTestId ( 'select-sharing-visibility' ) , { target : { value : 'organization' } } ) ;
2747+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'sharing' , expect . objectContaining ( {
2748+ enabled : true ,
2749+ visibility : 'organization' ,
2750+ } ) ) ;
2751+ } ) ;
2752+
2753+ // ── ARIA live select enum ──
2754+
2755+ it ( 'changes ARIA live to all enum values' , ( ) => {
2756+ const onViewUpdate = vi . fn ( ) ;
2757+ render (
2758+ < ViewConfigPanel
2759+ open = { true }
2760+ onClose = { vi . fn ( ) }
2761+ activeView = { mockActiveView }
2762+ objectDef = { mockObjectDef }
2763+ onViewUpdate = { onViewUpdate }
2764+ />
2765+ ) ;
2766+
2767+ const select = screen . getByTestId ( 'select-aria-live' ) ;
2768+ [ 'polite' , 'assertive' , 'off' ] . forEach ( ( mode ) => {
2769+ fireEvent . change ( select , { target : { value : mode } } ) ;
2770+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'aria' , expect . objectContaining ( { live : mode } ) ) ;
2771+ } ) ;
2772+ } ) ;
24362773} ) ;
0 commit comments