diff --git a/packages/mdc-checkbox/foundation.js b/packages/mdc-checkbox/foundation.js index 1794ab31de1..8ebb18e481b 100644 --- a/packages/mdc-checkbox/foundation.js +++ b/packages/mdc-checkbox/foundation.js @@ -178,6 +178,7 @@ class MDCCheckboxFoundation extends MDCFoundation { } const oldState = this.currentCheckState_; const newState = this.determineCheckState_(); + if (oldState === newState) { return; } @@ -259,7 +260,7 @@ class MDCCheckboxFoundation extends MDCFoundation { } updateAriaChecked_() { - // Ensure aria-checked is set to mixed if checkbox is in indeterminate state. + // Ensure aria-checked is set to if checkbox is in indeterminate state. if (this.adapter_.isIndeterminate()) { this.adapter_.setNativeControlAttr( strings.ARIA_CHECKED_ATTR, strings.ARIA_CHECKED_INDETERMINATE_VALUE); diff --git a/test/unit/mdc-checkbox/foundation.test.js b/test/unit/mdc-checkbox/foundation.test.js index 2b05c7976a1..5ea57beecf9 100644 --- a/test/unit/mdc-checkbox/foundation.test.js +++ b/test/unit/mdc-checkbox/foundation.test.js @@ -85,7 +85,11 @@ function setupChangeHandlerTest() { foundation.init(); const change = (newState) => { - td.when(mockAdapter.getNativeControl()).thenReturn(newState); + td.when(mockAdapter.hasNativeControl()).thenReturn(!!newState); + if (newState) { + td.when(mockAdapter.isChecked()).thenReturn(newState.checked); + td.when(mockAdapter.isIndeterminate()).thenReturn(newState.indeterminate); + } changeHandler(); }; @@ -131,8 +135,8 @@ test('#init adds the upgraded class to the root element', () => { }); test('#init adds aria-checked="mixed" if checkbox is initially indeterminate', () => { - const {foundation, mockAdapter, nativeControl} = setupTest(); - nativeControl.indeterminate = true; + const {foundation, mockAdapter} = setupTest(); + td.when(mockAdapter.isIndeterminate()).thenReturn(true); foundation.init(); td.verify(mockAdapter.setNativeControlAttr('aria-checked', strings.ARIA_CHECKED_INDETERMINATE_VALUE)); @@ -199,111 +203,24 @@ test('#destroy handles case when WebIDL attrs cannot be overridden (Safari)', () }); }); -test('#setChecked updates the value of nativeControl.checked', () => { - const {foundation, nativeControl} = setupTest(); - foundation.setChecked(true); - assert.isOk(foundation.isChecked()); - assert.isOk(nativeControl.checked); - foundation.setChecked(false); - assert.isNotOk(foundation.isChecked()); - assert.isNotOk(nativeControl.checked); -}); - -test('#setChecked works when no native control is returned', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.doesNotThrow(() => foundation.setChecked(true)); -}); - -test('#isChecked returns false when no native control is returned', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.isNotOk(foundation.isChecked()); -}); - -test('#setIndeterminate removes aria-checked when indeterminate is false', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.init(); - foundation.setIndeterminate(false); - td.verify(mockAdapter.removeNativeControlAttr('aria-checked')); -}); - -test('#setIndeterminate works when no native control is returned', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.doesNotThrow(() => foundation.setIndeterminate(true)); -}); - -test('#isIndeterminate returns false when no native control is returned', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.isNotOk(foundation.isIndeterminate()); -}); - test('#setDisabled updates the value of nativeControl.disabled', () => { - const {foundation, nativeControl} = setupTest(); + const {foundation, mockAdapter} = setupTest(); foundation.setDisabled(true); - assert.isOk(foundation.isDisabled()); - assert.isOk(nativeControl.disabled); - foundation.setDisabled(false); - assert.isNotOk(foundation.isDisabled()); - assert.isNotOk(nativeControl.disabled); + td.verify(mockAdapter.setNativeControlDisabled(true), {times: 1}); }); test('#setDisabled adds mdc-checkbox--disabled class to the root element when set to true', () => { const {foundation, mockAdapter} = setupTest(); - const nativeControl = {disabled: false}; - td.when(mockAdapter.getNativeControl()).thenReturn(nativeControl); foundation.setDisabled(true); td.verify(mockAdapter.addClass(cssClasses.DISABLED)); }); test('#setDisabled removes mdc-checkbox--disabled class from the root element when set to false', () => { const {foundation, mockAdapter} = setupTest(); - const nativeControl = {disabled: true}; - td.when(mockAdapter.getNativeControl()).thenReturn(nativeControl); foundation.setDisabled(false); td.verify(mockAdapter.removeClass(cssClasses.DISABLED)); }); -test('#isDisabled returns false when no native control is returned', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.isNotOk(foundation.isDisabled()); -}); - -test('#setDisabled works when no native control is returned', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.doesNotThrow(() => foundation.setDisabled(true)); -}); - -test('#getValue returns the value of nativeControl.value', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn({value: 'value'}); - assert.equal(foundation.getValue(), 'value'); -}); - -test('#getValue returns null if getNativeControl() does not return anything', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.isNull(foundation.getValue()); -}); - -test('#setValue sets the value of nativeControl.value', () => { - const {foundation, mockAdapter} = setupTest(); - const nativeControl = {value: null}; - td.when(mockAdapter.getNativeControl()).thenReturn(nativeControl); - foundation.setValue('new value'); - assert.equal(nativeControl.value, 'new value'); -}); - -test('#setValue exits gracefully if getNativeControl() does not return anything', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNativeControl()).thenReturn(null); - assert.doesNotThrow(() => foundation.setValue('new value')); -}); - testChangeHandler('unchecked -> checked animation class', { checked: true, indeterminate: false, @@ -472,6 +389,7 @@ test('"checked" property change hook works correctly', () => { const {foundation, mockAdapter, nativeControl} = setupTest(); const clock = lolex.install(); td.when(mockAdapter.isAttachedToDOM()).thenReturn(true); + td.when(mockAdapter.hasNativeControl()).thenReturn(true); withMockCheckboxDescriptorReturning({ get: () => {}, @@ -480,10 +398,8 @@ test('"checked" property change hook works correctly', () => { configurable: true, }, () => { foundation.init(); - td.when(mockAdapter.getNativeControl()).thenReturn({ - checked: true, - indeterminate: false, - }); + td.when(mockAdapter.isChecked()).thenReturn(true); + td.when(mockAdapter.isIndeterminate()).thenReturn(false); nativeControl.checked = !nativeControl.checked; td.verify(mockAdapter.addClass(cssClasses.ANIM_UNCHECKED_CHECKED)); }); @@ -495,6 +411,7 @@ test('"indeterminate" property change hook works correctly', () => { const {foundation, mockAdapter, nativeControl} = setupTest(); const clock = lolex.install(); td.when(mockAdapter.isAttachedToDOM()).thenReturn(true); + td.when(mockAdapter.hasNativeControl()).thenReturn(true); withMockCheckboxDescriptorReturning({ get: () => {}, @@ -503,10 +420,9 @@ test('"indeterminate" property change hook works correctly', () => { configurable: true, }, () => { foundation.init(); - td.when(mockAdapter.getNativeControl()).thenReturn({ - checked: false, - indeterminate: true, - }); + td.when(mockAdapter.isChecked()).thenReturn(false); + td.when(mockAdapter.isIndeterminate()).thenReturn(true); + nativeControl.indeterminate = !nativeControl.indeterminate; td.verify(mockAdapter.addClass(cssClasses.ANIM_UNCHECKED_INDETERMINATE)); }); diff --git a/test/unit/mdc-checkbox/mdc-checkbox.test.js b/test/unit/mdc-checkbox/mdc-checkbox.test.js index 345d6d8ad3f..1f3af06dab5 100644 --- a/test/unit/mdc-checkbox/mdc-checkbox.test.js +++ b/test/unit/mdc-checkbox/mdc-checkbox.test.js @@ -240,3 +240,44 @@ test('adapter#isAttachedToDOM returns false when root is not attached to DOM', ( const {component} = setupTest(); assert.isNotOk(component.getDefaultFoundation().adapter_.isAttachedToDOM()); }); + +test('#adapter.isIndeterminate returns true when checkbox is indeterminate', () => { + const {cb, component} = setupTest(); + cb.indeterminate = true; + assert.isTrue(component.getDefaultFoundation().adapter_.isIndeterminate()); +}); + +test('#adapter.isIndeterminate returns false when checkbox is not indeterminate', () => { + const {cb, component} = setupTest(); + cb.indeterminate = false; + assert.isFalse(component.getDefaultFoundation().adapter_.isIndeterminate()); +}); + +test('#adapter.isChecked returns true when checkbox is checked', () => { + const {cb, component} = setupTest(); + cb.checked = true; + assert.isTrue(component.getDefaultFoundation().adapter_.isChecked()); +}); + +test('#adapter.isChecked returns false when checkbox is not checked', () => { + const {cb, component} = setupTest(); + cb.checked = false; + assert.isFalse(component.getDefaultFoundation().adapter_.isChecked()); +}); + +test('#adapter.hasNativeControl returns true when checkbox exists', () => { + const {component} = setupTest(); + assert.isTrue(component.getDefaultFoundation().adapter_.hasNativeControl()); +}); + +test('#adapter.setNativeControlDisabled returns true when checkbox is disabled', () => { + const {cb, component} = setupTest(); + component.getDefaultFoundation().adapter_.setNativeControlDisabled(true); + assert.isTrue(cb.disabled); +}); + +test('#adapter.setNativeControlDisabled returns false when checkbox is not disabled', () => { + const {cb, component} = setupTest(); + component.getDefaultFoundation().adapter_.setNativeControlDisabled(false); + assert.isFalse(cb.disabled); +});