11import { setupRtl } from '@codecademy/gamut-tests' ;
22import { fireEvent } from '@testing-library/dom' ;
33import { act } from '@testing-library/react' ;
4+ import userEvent from '@testing-library/user-event' ;
45
56import { ConnectedForm , ConnectedFormGroup , SubmitButton } from '../../..' ;
67import { NestedConnectedCheckboxOption } from '../../types' ;
@@ -43,7 +44,13 @@ const TestForm: React.FC<{
4344 defaultValues ?: { skills ?: string [ ] } ;
4445 validationRules ?: any ;
4546 disabled ?: boolean ;
46- } > = ( { defaultValues = { } , validationRules, disabled } ) => (
47+ options ?: NestedConnectedCheckboxOption [ ] ;
48+ } > = ( {
49+ defaultValues = { } ,
50+ validationRules,
51+ disabled,
52+ options = mockOptions ,
53+ } ) => (
4754 < ConnectedForm
4855 defaultValues = { defaultValues }
4956 validationRules = { validationRules }
@@ -53,7 +60,7 @@ const TestForm: React.FC<{
5360 disabled = { disabled }
5461 field = { {
5562 component : ConnectedNestedCheckboxes ,
56- options : mockOptions ,
63+ options,
5764 onUpdate : mockOnUpdate ,
5865 } }
5966 label = "nested checkboxes field"
@@ -100,10 +107,10 @@ describe('ConnectedNestedCheckboxes', () => {
100107 const expressCheckbox = view . getByLabelText ( 'Express.js' ) . closest ( 'li' ) ;
101108
102109 // Check margin-left styles for indentation
103- expect ( frontendCheckbox ) . toHaveStyle ( { marginLeft : '0px ' } ) ; // level 0
104- expect ( reactCheckbox ) . toHaveStyle ( { marginLeft : '24px ' } ) ; // level 1
105- expect ( nodeCheckbox ) . toHaveStyle ( { marginLeft : '24px ' } ) ; // level 1
106- expect ( expressCheckbox ) . toHaveStyle ( { marginLeft : '48px ' } ) ; // level 2
110+ expect ( frontendCheckbox ) . toHaveStyle ( { marginLeft : '0 ' } ) ; // level 0
111+ expect ( reactCheckbox ) . toHaveStyle ( { marginLeft : '1.5rem ' } ) ; // level 1
112+ expect ( nodeCheckbox ) . toHaveStyle ( { marginLeft : '1.5rem ' } ) ; // level 1
113+ expect ( expressCheckbox ) . toHaveStyle ( { marginLeft : '3rem ' } ) ; // level 2
107114 } ) ;
108115
109116 it ( 'should render with unique IDs for each checkbox' , ( ) => {
@@ -137,7 +144,7 @@ describe('ConnectedNestedCheckboxes', () => {
137144
138145 it ( 'should render parent as indeterminate when some children are selected' , ( ) => {
139146 const { view } = renderView ( {
140- defaultValues : { skills : [ 'react' , 'vue' ] } , // only some frontend
147+ defaultValues : { skills : [ 'react' , 'vue' ] } ,
141148 } ) ;
142149
143150 const frontendCheckbox = view . getByLabelText (
@@ -159,7 +166,7 @@ describe('ConnectedNestedCheckboxes', () => {
159166
160167 it ( 'should render deeply nested parent states correctly' , ( ) => {
161168 const { view } = renderView ( {
162- defaultValues : { skills : [ 'express' , 'fastify' ] } , // all node children
169+ defaultValues : { skills : [ 'express' , 'fastify' ] } ,
163170 } ) ;
164171
165172 const nodeCheckbox = view . getByLabelText ( 'Node.js' ) ;
@@ -187,6 +194,16 @@ describe('ConnectedNestedCheckboxes', () => {
187194 // Deeply nested children should also be checked
188195 expect ( view . getByLabelText ( 'Express.js' ) ) . toBeChecked ( ) ;
189196 expect ( view . getByLabelText ( 'Fastify' ) ) . toBeChecked ( ) ;
197+
198+ // onUpdate should have been called with all expanded values during initialization
199+ expect ( mockOnUpdate ) . toHaveBeenCalledWith ( [
200+ 'backend' ,
201+ 'node' ,
202+ 'express' ,
203+ 'fastify' ,
204+ 'python' ,
205+ 'ruby' ,
206+ ] ) ;
190207 } ) ;
191208
192209 it ( 'should allow unchecking children that were auto-checked by default parent selection' , async ( ) => {
@@ -373,13 +390,15 @@ describe('ConnectedNestedCheckboxes', () => {
373390 } ) ;
374391
375392 it ( 'should not respond to clicks when disabled' , async ( ) => {
376- const { view } = renderView ( { disabled : true } ) ;
393+ const { view } = renderView ( {
394+ disabled : true ,
395+ } ) ;
377396
378397 const reactCheckbox = view . getByLabelText ( 'React' ) ;
398+ expect ( reactCheckbox ) . toBeDisabled ( ) ;
399+ expect ( reactCheckbox ) . not . toBeChecked ( ) ;
379400
380- await act ( async ( ) => {
381- fireEvent . click ( reactCheckbox ) ;
382- } ) ;
401+ await userEvent . click ( reactCheckbox ) ;
383402
384403 expect ( reactCheckbox ) . not . toBeChecked ( ) ;
385404 expect ( mockOnUpdate ) . not . toHaveBeenCalled ( ) ;
@@ -432,13 +451,7 @@ describe('ConnectedNestedCheckboxes', () => {
432451
433452 describe ( 'edge cases' , ( ) => {
434453 it ( 'should handle empty options array' , ( ) => {
435- const TestFormEmpty = ( ) => (
436- < ConnectedForm defaultValues = { { } } onSubmit = { jest . fn ( ) } >
437- < ConnectedNestedCheckboxes name = "skills" options = { [ ] } />
438- </ ConnectedForm >
439- ) ;
440-
441- const { view } = setupRtl ( TestFormEmpty , { } ) ( { } ) ;
454+ const { view } = renderView ( { options : [ ] } ) ;
442455
443456 // Should render empty list
444457 const list = view . container . querySelector ( 'ul' ) ;
@@ -447,39 +460,27 @@ describe('ConnectedNestedCheckboxes', () => {
447460 } ) ;
448461
449462 it ( 'should handle options without nested children' , ( ) => {
450- const flatOptions : NestedConnectedCheckboxOption [ ] = [
451- { value : 'option1' , label : 'Option 1' } ,
452- { value : 'option2' , label : 'Option 2' } ,
453- ] ;
454-
455- const TestFormFlat = ( ) => (
456- < ConnectedForm defaultValues = { { } } onSubmit = { jest . fn ( ) } >
457- < ConnectedNestedCheckboxes name = "skills" options = { flatOptions } />
458- </ ConnectedForm >
459- ) ;
460-
461- const { view } = setupRtl ( TestFormFlat , { } ) ( { } ) ;
463+ const { view } = renderView ( {
464+ options : [
465+ { value : 'option1' , label : 'Option 1' } ,
466+ { value : 'option2' , label : 'Option 2' } ,
467+ ] ,
468+ } ) ;
462469
463470 expect ( view . getByLabelText ( 'Option 1' ) ) . toBeInTheDocument ( ) ;
464471 expect ( view . getByLabelText ( 'Option 2' ) ) . toBeInTheDocument ( ) ;
465472 } ) ;
466473
467474 it ( 'should handle numeric values correctly' , ( ) => {
468- const numericOptions : NestedConnectedCheckboxOption [ ] = [
469- {
470- value : 1 ,
471- label : 'Parent Option' ,
472- options : [ { value : 2 , label : 'Child Option' } ] ,
473- } as any , // Type assertion for testing numeric values
474- ] ;
475-
476- const TestFormNumeric = ( ) => (
477- < ConnectedForm defaultValues = { { } } onSubmit = { jest . fn ( ) } >
478- < ConnectedNestedCheckboxes name = "skills" options = { numericOptions } />
479- </ ConnectedForm >
480- ) ;
481-
482- const { view } = setupRtl ( TestFormNumeric , { } ) ( { } ) ;
475+ const { view } = renderView ( {
476+ options : [
477+ {
478+ value : 1 ,
479+ label : 'Parent Option' ,
480+ options : [ { value : 2 , label : 'Child Option' } ] ,
481+ } as any , // Type assertion for testing numeric values
482+ ] ,
483+ } ) ;
483484
484485 expect ( view . getByLabelText ( 'Parent Option' ) ) . toHaveAttribute (
485486 'id' ,
@@ -518,8 +519,8 @@ describe('ConnectedNestedCheckboxes', () => {
518519 const list = view . container . querySelector ( 'ul' ) ;
519520 const listItems = view . container . querySelectorAll ( 'li' ) ;
520521
521- expect ( list ) . toHaveAttribute ( 'role' , 'list' ) ;
522- expect ( listItems ) . toHaveLength ( 8 ) ; // Total flattened options
522+ expect ( list ) . toBeInTheDocument ( ) ;
523+ expect ( listItems ) . toHaveLength ( 11 ) ; // Total flattened options
523524
524525 listItems . forEach ( ( item ) => {
525526 expect ( item ) . toHaveStyle ( { listStyle : 'none' } ) ;
0 commit comments