diff --git a/assets/src/js/_acf-field-checkbox.js b/assets/src/js/_acf-field-checkbox.js index 3ade175a..50021f8f 100644 --- a/assets/src/js/_acf-field-checkbox.js +++ b/assets/src/js/_acf-field-checkbox.js @@ -76,17 +76,9 @@ }, onClickToggle: function ( e, $el ) { - // Vars. - const inputs = this.$inputs(); - const hasUnchecked = $inputs.not( ':checked' ).length > 0; - inputs.each( function () { - $inputs.each( function () { - jQuery( this ) - .prop( 'checked', hasUnchecked ) - .trigger( 'change' ); - } ); - } ); - $el.prop( 'checked', hasUnchecked ); + var $inputs = this.$inputs(); + var checked = $el.prop( 'checked' ); + $inputs.prop( 'checked', checked ).trigger( 'change' ); }, onClickCustom: function ( e, $el ) { diff --git a/tests/js/fields/checkbox.test.js b/tests/js/fields/checkbox.test.js new file mode 100644 index 00000000..3f9c5db4 --- /dev/null +++ b/tests/js/fields/checkbox.test.js @@ -0,0 +1,82 @@ +/** + * Unit tests for checkbox field type + */ + +describe( 'Checkbox Field', () => { + let fieldDefinition; + let mockField; + + beforeEach( () => { + // Reset mocks + fieldDefinition = null; + + // Mock acf.Field.extend to capture the field definition + global.acf = { + Field: { + extend: jest.fn( ( definition ) => { + fieldDefinition = definition; + return definition; + } ), + }, + registerFieldType: jest.fn(), + }; + + // Load the checkbox field module (this will call acf.Field.extend) + jest.isolateModules( () => { + require( '../../../assets/src/js/_acf-field-checkbox.js' ); + } ); + + // Create a mock field instance with the captured definition + mockField = { + ...fieldDefinition, + $: jest.fn(), + }; + } ); + + afterEach( () => { + delete global.acf; + } ); + + describe( 'onClickToggle', () => { + it( 'should check all inputs when toggle is checked', () => { + // Mock $inputs with chainable jQuery methods + const mockInputs = { + prop: jest.fn().mockReturnThis(), + trigger: jest.fn().mockReturnThis(), + }; + + // Mock $el (the toggle checkbox) as checked + const mockToggle = { + prop: jest.fn().mockReturnValue( true ), + }; + + // Mock this.$inputs() to return our mock + mockField.$inputs = jest.fn().mockReturnValue( mockInputs ); + + // Call the method + fieldDefinition.onClickToggle.call( mockField, {}, mockToggle ); + + // Verify all inputs were checked + expect( mockInputs.prop ).toHaveBeenCalledWith( 'checked', true ); + expect( mockInputs.trigger ).toHaveBeenCalledWith( 'change' ); + } ); + + it( 'should uncheck all inputs when toggle is unchecked', () => { + const mockInputs = { + prop: jest.fn().mockReturnThis(), + trigger: jest.fn().mockReturnThis(), + }; + + const mockToggle = { + prop: jest.fn().mockReturnValue( false ), + }; + + mockField.$inputs = jest.fn().mockReturnValue( mockInputs ); + + fieldDefinition.onClickToggle.call( mockField, {}, mockToggle ); + + expect( mockInputs.prop ).toHaveBeenCalledWith( 'checked', false ); + expect( mockInputs.trigger ).toHaveBeenCalledWith( 'change' ); + } ); + } ); +} ); diff --git a/tests/js/setup-tests.js b/tests/js/setup-tests.js index 2c57f35d..1c576833 100644 --- a/tests/js/setup-tests.js +++ b/tests/js/setup-tests.js @@ -17,6 +17,14 @@ global.jQuery = jest.fn( ( html ) => { } ); global.$ = global.jQuery; +// Mock ACF global for field type tests +global.acf = { + Field: { + extend: jest.fn( ( def ) => def ), + }, + registerFieldType: jest.fn(), +}; + // Mock WordPress packages that are externalized jest.mock( '@wordpress/data',