From d7f91c125e799311af2f2ea791bab38edd6a1295 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 6 Jun 2024 08:24:10 -0700 Subject: [PATCH] test: Convert some material tests to zoneless (#29204) --- .../autocomplete/autocomplete-trigger.ts | 49 +++--- .../autocomplete/autocomplete.spec.ts | 92 ++++++---- .../autocomplete/autocomplete.zone.spec.ts | 157 ++++++++++++++++++ .../badge/testing/badge-harness.spec.ts | 15 +- .../bottom-sheet/bottom-sheet.spec.ts | 4 +- .../button-toggle/button-toggle.spec.ts | 39 +++-- .../testing/button-toggle-group.spec.ts | 17 +- .../testing/button-toggle-harness.spec.ts | 12 +- src/material/button/button.spec.ts | 32 +++- src/material/checkbox/checkbox.spec.ts | 56 +++++-- 10 files changed, 358 insertions(+), 115 deletions(-) create mode 100644 src/material/autocomplete/autocomplete.zone.spec.ts diff --git a/src/material/autocomplete/autocomplete-trigger.ts b/src/material/autocomplete/autocomplete-trigger.ts index 4005313b42c1..bbfb7756f71f 100644 --- a/src/material/autocomplete/autocomplete-trigger.ts +++ b/src/material/autocomplete/autocomplete-trigger.ts @@ -7,16 +7,27 @@ */ import {addAriaReferencedId, removeAriaReferencedId} from '@angular/cdk/a11y'; +import {Directionality} from '@angular/cdk/bidi'; +import {DOWN_ARROW, ENTER, ESCAPE, TAB, UP_ARROW, hasModifierKey} from '@angular/cdk/keycodes'; +import { + ConnectedPosition, + FlexibleConnectedPositionStrategy, + Overlay, + OverlayConfig, + OverlayRef, + PositionStrategy, + ScrollStrategy, +} from '@angular/cdk/overlay'; +import {_getEventTarget} from '@angular/cdk/platform'; +import {TemplatePortal} from '@angular/cdk/portal'; +import {ViewportRuler} from '@angular/cdk/scrolling'; +import {DOCUMENT} from '@angular/common'; import { - afterNextRender, AfterViewInit, - booleanAttribute, ChangeDetectorRef, Directive, ElementRef, - forwardRef, Host, - inject, Inject, InjectionToken, Injector, @@ -27,38 +38,27 @@ import { Optional, SimpleChanges, ViewContainerRef, + afterNextRender, + booleanAttribute, + forwardRef, + inject, } from '@angular/core'; -import {DOCUMENT} from '@angular/common'; -import {Directionality} from '@angular/cdk/bidi'; -import {DOWN_ARROW, ENTER, ESCAPE, TAB, UP_ARROW, hasModifierKey} from '@angular/cdk/keycodes'; -import {_getEventTarget} from '@angular/cdk/platform'; -import {TemplatePortal} from '@angular/cdk/portal'; -import {ViewportRuler} from '@angular/cdk/scrolling'; -import { - FlexibleConnectedPositionStrategy, - Overlay, - OverlayConfig, - OverlayRef, - PositionStrategy, - ScrollStrategy, - ConnectedPosition, -} from '@angular/cdk/overlay'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import { + MatOption, MatOptionSelectionChange, _countGroupLabelsBeforeOption, _getOptionScrollPosition, - MatOption, } from '@angular/material/core'; import {MAT_FORM_FIELD, MatFormField} from '@angular/material/form-field'; -import {defer, fromEvent, merge, Observable, of as observableOf, Subject, Subscription} from 'rxjs'; -import {delay, filter, map, switchMap, take, tap, startWith} from 'rxjs/operators'; -import {MatAutocompleteOrigin} from './autocomplete-origin'; +import {Observable, Subject, Subscription, defer, fromEvent, merge, of as observableOf} from 'rxjs'; +import {delay, filter, map, startWith, switchMap, take, tap} from 'rxjs/operators'; import { - MatAutocompleteDefaultOptions, MAT_AUTOCOMPLETE_DEFAULT_OPTIONS, MatAutocomplete, + MatAutocompleteDefaultOptions, } from './autocomplete'; +import {MatAutocompleteOrigin} from './autocomplete-origin'; /** * Provider that allows the autocomplete to register as a ControlValueAccessor. @@ -456,6 +456,7 @@ export class MatAutocompleteTrigger // Implemented as part of ControlValueAccessor. setDisabledState(isDisabled: boolean) { this._element.nativeElement.disabled = isDisabled; + this._changeDetectorRef.markForCheck(); } _handleKeydown(event: KeyboardEvent): void { diff --git a/src/material/autocomplete/autocomplete.spec.ts b/src/material/autocomplete/autocomplete.spec.ts index 8c2ae19924ff..e3fb456880b0 100644 --- a/src/material/autocomplete/autocomplete.spec.ts +++ b/src/material/autocomplete/autocomplete.spec.ts @@ -16,7 +16,6 @@ import { ChangeDetectionStrategy, Component, ElementRef, - NgZone, OnDestroy, OnInit, Provider, @@ -25,7 +24,6 @@ import { ViewChild, ViewChildren, ViewEncapsulation, - provideZoneChangeDetection, } from '@angular/core'; import { ComponentFixture, @@ -57,12 +55,6 @@ import { } from './index'; describe('MDC-based MatAutocomplete', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); - let overlayContainerElement: HTMLElement; // Creates a test component fixture. @@ -366,6 +358,7 @@ describe('MDC-based MatAutocomplete', () => { it('should not mess with label placement if set to never', fakeAsync(() => { fixture.componentInstance.floatLabel = 'never'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -388,6 +381,7 @@ describe('MDC-based MatAutocomplete', () => { it('should not mess with label placement if set to always', fakeAsync(() => { fixture.componentInstance.floatLabel = 'always'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -476,6 +470,7 @@ describe('MDC-based MatAutocomplete', () => { it('should not emit the `opened` event when no options are being shown', () => { fixture.componentInstance.filteredStates = fixture.componentInstance.states = []; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -486,6 +481,7 @@ describe('MDC-based MatAutocomplete', () => { it('should emit the `opened` event if the options come in after the panel is shown', fakeAsync(() => { fixture.componentInstance.filteredStates = fixture.componentInstance.states = []; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -496,6 +492,7 @@ describe('MDC-based MatAutocomplete', () => { fixture.componentInstance.filteredStates = fixture.componentInstance.states = [ {name: 'California', code: 'CA'}, ]; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -529,6 +526,7 @@ describe('MDC-based MatAutocomplete', () => { it('should not emit the `closed` event when no options were shown', () => { fixture.componentInstance.filteredStates = fixture.componentInstance.states = []; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -546,6 +544,7 @@ describe('MDC-based MatAutocomplete', () => { .toBe(false); fixture.componentInstance.autocompleteDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(input, 'focusin'); @@ -558,6 +557,7 @@ describe('MDC-based MatAutocomplete', () => { it('should continue to update the model if the autocomplete is disabled', () => { fixture.componentInstance.autocompleteDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); typeInElement(input, 'hello'); @@ -570,6 +570,7 @@ describe('MDC-based MatAutocomplete', () => { expect(input.getAttribute('aria-haspopup')).toBe('listbox'); fixture.componentInstance.autocompleteDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(input.hasAttribute('aria-haspopup')).toBe(false); @@ -742,6 +743,7 @@ describe('MDC-based MatAutocomplete', () => { it('should not clear the selected option if it no longer matches the input text while typing with requireSelection', waitForAsync(async () => { const fixture = createComponent(SimpleAutocomplete); fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -873,6 +875,7 @@ describe('MDC-based MatAutocomplete', () => { fixture.componentInstance.panel.displayWith = null; fixture.componentInstance.options.toArray()[1].value = 'test value'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const options = overlayContainerElement.querySelectorAll( @@ -1025,9 +1028,6 @@ describe('MDC-based MatAutocomplete', () => { it('should disable the input when used with a value accessor and without `matInput`', () => { fixture.destroy(); TestBed.resetTestingModule(); - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); const plainFixture = createComponent(PlainAutocompleteInputWithFormControl); plainFixture.detectChanges(); @@ -1052,6 +1052,7 @@ describe('MDC-based MatAutocomplete', () => { it('should transfer the theme to the autocomplete panel', () => { fixture.componentInstance.theme = 'warn'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -1358,6 +1359,7 @@ describe('MDC-based MatAutocomplete', () => { state.height = 64; } }); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const trigger = fixture.componentInstance.trigger; @@ -1520,6 +1522,7 @@ describe('MDC-based MatAutocomplete', () => { it('should close the panel when tabbing away from a trigger without results', fakeAsync(() => { fixture.componentInstance.states = []; fixture.componentInstance.filteredStates = []; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); input.focus(); flush(); @@ -1828,6 +1831,7 @@ describe('MDC-based MatAutocomplete', () => { it('should add a custom aria-labelledby to the panel', () => { fixture.componentInstance.ariaLabelledby = 'myLabelId'; + fixture.changeDetectorRef.markForCheck(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -1841,8 +1845,10 @@ describe('MDC-based MatAutocomplete', () => { it('should trim aria-labelledby if the input does not have a label', () => { fixture.componentInstance.hasLabel = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.ariaLabelledby = 'myLabelId'; + fixture.changeDetectorRef.markForCheck(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -1854,6 +1860,7 @@ describe('MDC-based MatAutocomplete', () => { it('should clear aria-labelledby from the panel if an aria-label is set', () => { fixture.componentInstance.ariaLabel = 'My label'; + fixture.changeDetectorRef.markForCheck(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -1866,6 +1873,7 @@ describe('MDC-based MatAutocomplete', () => { it('should clear aria-labelledby if the form field does not have a label', () => { fixture.componentInstance.hasLabel = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -1878,6 +1886,7 @@ describe('MDC-based MatAutocomplete', () => { it('should support setting a custom aria-label', () => { fixture.componentInstance.ariaLabel = 'Custom Label'; + fixture.changeDetectorRef.markForCheck(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -2000,6 +2009,7 @@ describe('MDC-based MatAutocomplete', () => { it('should remove autocomplete-specific aria attributes when autocomplete is disabled', () => { fixture.componentInstance.autocompleteDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(input.getAttribute('role')).toBeFalsy(); @@ -2166,6 +2176,7 @@ describe('MDC-based MatAutocomplete', () => { let fixture = createComponent(SimpleAutocomplete); fixture.componentInstance.states = fixture.componentInstance.states.slice(0, 1); fixture.componentInstance.filteredStates = fixture.componentInstance.states.slice(); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let inputEl = fixture.debugElement.query(By.css('input'))!.nativeElement; @@ -2190,6 +2201,7 @@ describe('MDC-based MatAutocomplete', () => { for (let i = 0; i < 20; i++) { fixture.componentInstance.filteredStates.push({code: 'FK', name: 'Fake State'}); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); } @@ -2216,6 +2228,7 @@ describe('MDC-based MatAutocomplete', () => { it('should be able to force below position even if there is not enough space', waitForAsync(async () => { let fixture = createComponent(SimpleAutocomplete); fixture.componentInstance.position = 'below'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let inputReference = fixture.debugElement.query(By.css('.mdc-text-field'))!.nativeElement; @@ -2242,6 +2255,7 @@ describe('MDC-based MatAutocomplete', () => { it('should be able to force above position even if there is not enough space', waitForAsync(async () => { let fixture = createComponent(SimpleAutocomplete); fixture.componentInstance.position = 'above'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); let inputReference = fixture.debugElement.query(By.css('.mdc-text-field'))!.nativeElement; @@ -2294,6 +2308,7 @@ describe('MDC-based MatAutocomplete', () => { fixture.detectChanges(); fixture.componentInstance.position = 'below'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); await openPanel(); @@ -2384,6 +2399,7 @@ describe('MDC-based MatAutocomplete', () => { it('should be able to preselect the first option', waitForAsync(async () => { fixture.componentInstance.trigger.autocomplete.autoActiveFirstOption = true; + fixture.changeDetectorRef.markForCheck(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2438,6 +2454,7 @@ describe('MDC-based MatAutocomplete', () => { .toBe(false); fixture.componentInstance.trigger.autocomplete.autoActiveFirstOption = true; + fixture.changeDetectorRef.markForCheck(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2458,6 +2475,7 @@ describe('MDC-based MatAutocomplete', () => { it('should be able to preselect the first option when the floating label is disabled', waitForAsync(async () => { fixture.componentInstance.floatLabel = 'never'; fixture.componentInstance.trigger.autocomplete.autoActiveFirstOption = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -2530,12 +2548,14 @@ describe('MDC-based MatAutocomplete', () => { }; fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}]; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); await openAndSelectFirstOption(); expect(spy).toHaveBeenCalledTimes(1); fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}]; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); await openAndSelectFirstOption(); @@ -2627,6 +2647,7 @@ describe('MDC-based MatAutocomplete', () => { const input = fixture.nativeElement.querySelector('input'); const {stateCtrl, trigger, states} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); stateCtrl.setValue(states[1]); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2659,6 +2680,7 @@ describe('MDC-based MatAutocomplete', () => { const input = fixture.nativeElement.querySelector('input'); const {stateCtrl, trigger, states} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); stateCtrl.setValue(states[1]); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2692,6 +2714,7 @@ describe('MDC-based MatAutocomplete', () => { const {stateCtrl, trigger, states} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; trigger.autocomplete.autoSelectActiveOption = true; + fixture.changeDetectorRef.markForCheck(); stateCtrl.setValue(states[1]); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2720,6 +2743,7 @@ describe('MDC-based MatAutocomplete', () => { const input = fixture.nativeElement.querySelector('input'); const {stateCtrl, trigger, states} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); stateCtrl.setValue(states[1]); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2758,6 +2782,7 @@ describe('MDC-based MatAutocomplete', () => { const input = fixture.nativeElement.querySelector('input'); const {stateCtrl, trigger, states} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); stateCtrl.setValue(states[1]); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2786,6 +2811,7 @@ describe('MDC-based MatAutocomplete', () => { const input = fixture.nativeElement.querySelector('input'); const {stateCtrl, trigger, states} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); stateCtrl.setValue(states[1]); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2794,6 +2820,7 @@ describe('MDC-based MatAutocomplete', () => { expect(stateCtrl.value).toEqual({code: 'CA', name: 'California'}); fixture.componentInstance.states = fixture.componentInstance.filteredStates = []; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); trigger.openPanel(); @@ -2817,6 +2844,7 @@ describe('MDC-based MatAutocomplete', () => { const input = fixture.nativeElement.querySelector('input'); const {stateCtrl, trigger} = fixture.componentInstance; fixture.componentInstance.requireSelection = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -2934,6 +2962,7 @@ describe('MDC-based MatAutocomplete', () => { // tslint:disable-next-line:ban xit('should not prevent escape key propagation when there are no options', waitForAsync(async () => { fixture.componentInstance.filteredStates = fixture.componentInstance.states = []; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); await new Promise(r => setTimeout(r)); @@ -3023,6 +3052,7 @@ describe('MDC-based MatAutocomplete', () => { const fixture = createComponent(AutocompleteWithNumbers); fixture.componentInstance.selectedNumber = 0; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); @@ -3118,6 +3148,7 @@ describe('MDC-based MatAutocomplete', () => { expect(classList).toContain('class-two'); fixture.componentInstance.panelClass = 'class-three class-four'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(classList).not.toContain('class-one'); @@ -3190,6 +3221,7 @@ describe('MDC-based MatAutocomplete', () => { fixture = createComponent(SimpleAutocomplete); fixture.detectChanges(); fixture.componentInstance.trigger.autocomplete.autoSelectActiveOption = true; + fixture.changeDetectorRef.markForCheck(); }); it( @@ -3416,6 +3448,7 @@ describe('MDC-based MatAutocomplete', () => { it('should have correct width when opened', () => { const widthFixture = createComponent(SimpleAutocomplete); widthFixture.componentInstance.width = 300; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.openPanel(); @@ -3429,6 +3462,7 @@ describe('MDC-based MatAutocomplete', () => { widthFixture.detectChanges(); widthFixture.componentInstance.width = 500; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.openPanel(); @@ -3442,6 +3476,7 @@ describe('MDC-based MatAutocomplete', () => { const widthFixture = createComponent(SimpleAutocomplete); widthFixture.componentInstance.width = 300; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.openPanel(); @@ -3453,6 +3488,7 @@ describe('MDC-based MatAutocomplete', () => { expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(300); widthFixture.componentInstance.width = 500; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); input.focus(); @@ -3496,6 +3532,7 @@ describe('MDC-based MatAutocomplete', () => { const widthFixture = createComponent(SimpleAutocomplete); widthFixture.componentInstance.width = 300; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.openPanel(); @@ -3506,6 +3543,7 @@ describe('MDC-based MatAutocomplete', () => { expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(300); widthFixture.componentInstance.width = 400; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); dispatchFakeEvent(window, 'resize'); @@ -3518,6 +3556,7 @@ describe('MDC-based MatAutocomplete', () => { const widthFixture = createComponent(SimpleAutocomplete); widthFixture.componentInstance.width = 300; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.openPanel(); @@ -3532,9 +3571,11 @@ describe('MDC-based MatAutocomplete', () => { const widthFixture = createComponent(SimpleAutocomplete); widthFixture.componentInstance.width = 300; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.autocomplete.panelWidth = 'auto'; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.componentInstance.trigger.openPanel(); widthFixture.detectChanges(); @@ -3547,9 +3588,11 @@ describe('MDC-based MatAutocomplete', () => { const widthFixture = createComponent(SimpleAutocomplete); widthFixture.componentInstance.width = 300; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.detectChanges(); widthFixture.componentInstance.trigger.autocomplete.panelWidth = 400; + widthFixture.changeDetectorRef.markForCheck(); widthFixture.componentInstance.trigger.openPanel(); widthFixture.detectChanges(); @@ -3640,6 +3683,7 @@ describe('MDC-based MatAutocomplete', () => { fixture.detectChanges(); fixture.componentInstance.states.push('Puerto Rico'); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); fixture.detectChanges(); @@ -3714,6 +3758,7 @@ describe('MDC-based MatAutocomplete', () => { fixture.detectChanges(); fixture.componentInstance.connectedTo = fixture.componentInstance.alternateOrigin; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -3741,6 +3786,7 @@ describe('MDC-based MatAutocomplete', () => { fixture.detectChanges(); fixture.componentInstance.connectedTo = fixture.componentInstance.alternateOrigin; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); @@ -3791,6 +3837,7 @@ describe('MDC-based MatAutocomplete', () => { const fixture = createComponent(AutocompleteWithDifferentOrigin); fixture.detectChanges(); fixture.componentInstance.connectedTo = fixture.componentInstance.alternateOrigin; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -3805,32 +3852,11 @@ describe('MDC-based MatAutocomplete', () => { expect(fixture.componentInstance.trigger.panelOpen).toBe(true); })); - it('should emit from `autocomplete.closed` after click outside inside the NgZone', waitForAsync(async () => { - const inZoneSpy = jasmine.createSpy('in zone spy'); - - const fixture = createComponent(SimpleAutocomplete); - fixture.detectChanges(); - - fixture.componentInstance.trigger.openPanel(); - fixture.detectChanges(); - await new Promise(r => setTimeout(r)); - - const subscription = fixture.componentInstance.trigger.autocomplete.closed.subscribe(() => - inZoneSpy(NgZone.isInAngularZone()), - ); - await new Promise(r => setTimeout(r)); - - dispatchFakeEvent(document, 'click'); - - expect(inZoneSpy).toHaveBeenCalledWith(true); - - subscription.unsubscribe(); - })); - describe('a11y', () => { it('should display checkmark for selection by default', () => { const fixture = createComponent(AutocompleteWithNgModel); fixture.componentInstance.selectedState = 'New York'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); fixture.componentInstance.trigger.openPanel(); diff --git a/src/material/autocomplete/autocomplete.zone.spec.ts b/src/material/autocomplete/autocomplete.zone.spec.ts new file mode 100644 index 000000000000..0f799d7999b2 --- /dev/null +++ b/src/material/autocomplete/autocomplete.zone.spec.ts @@ -0,0 +1,157 @@ +import {OverlayModule} from '@angular/cdk/overlay'; +import {dispatchFakeEvent} from '@angular/cdk/testing/private'; +import { + Component, + NgZone, + OnDestroy, + Provider, + QueryList, + Type, + ViewChild, + ViewChildren, + provideZoneChangeDetection, +} from '@angular/core'; +import {TestBed, waitForAsync} from '@angular/core/testing'; +import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {Subscription} from 'rxjs'; +import {MatOption} from '../core'; +import {MatFormField, MatFormFieldModule} from '../form-field'; +import {MatInputModule} from '../input'; +import {MatAutocomplete} from './autocomplete'; +import {MatAutocompleteTrigger} from './autocomplete-trigger'; +import {MatAutocompleteModule} from './module'; + +describe('MDC-based MatAutocomplete Zone.js integration', () => { + // Creates a test component fixture. + function createComponent(component: Type, providers: Provider[] = []) { + TestBed.configureTestingModule({ + imports: [ + MatAutocompleteModule, + MatFormFieldModule, + MatInputModule, + FormsModule, + ReactiveFormsModule, + NoopAnimationsModule, + OverlayModule, + ], + providers: [provideZoneChangeDetection(), ...providers], + declarations: [component], + }); + + TestBed.compileComponents(); + + return TestBed.createComponent(component); + } + it('should emit from `autocomplete.closed` after click outside inside the NgZone', waitForAsync(async () => { + const inZoneSpy = jasmine.createSpy('in zone spy'); + + const fixture = createComponent(SimpleAutocomplete); + fixture.detectChanges(); + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + await new Promise(r => setTimeout(r)); + + const subscription = fixture.componentInstance.trigger.autocomplete.closed.subscribe(() => + inZoneSpy(NgZone.isInAngularZone()), + ); + await new Promise(r => setTimeout(r)); + + dispatchFakeEvent(document, 'click'); + + expect(inZoneSpy).toHaveBeenCalledWith(true); + + subscription.unsubscribe(); + })); +}); + +const SIMPLE_AUTOCOMPLETE_TEMPLATE = ` + + @if (hasLabel) { + State + } + + + + @for (state of filteredStates; track state) { + + {{ state.code }}: {{ state.name }} + + } + +`; + +@Component({template: SIMPLE_AUTOCOMPLETE_TEMPLATE}) +class SimpleAutocomplete implements OnDestroy { + stateCtrl = new FormControl<{name: string; code: string} | string | null>(null); + filteredStates: any[]; + valueSub: Subscription; + floatLabel = 'auto'; + position = 'auto'; + width: number; + disableRipple = false; + autocompleteDisabled = false; + hasLabel = true; + requireSelection = false; + ariaLabel: string; + ariaLabelledby: string; + panelClass = 'class-one class-two'; + theme: string; + openedSpy = jasmine.createSpy('autocomplete opened spy'); + closedSpy = jasmine.createSpy('autocomplete closed spy'); + + @ViewChild(MatAutocompleteTrigger, {static: true}) trigger: MatAutocompleteTrigger; + @ViewChild(MatAutocomplete) panel: MatAutocomplete; + @ViewChild(MatFormField) formField: MatFormField; + @ViewChildren(MatOption) options: QueryList; + + states: {code: string; name: string; height?: number; disabled?: boolean}[] = [ + {code: 'AL', name: 'Alabama'}, + {code: 'CA', name: 'California'}, + {code: 'FL', name: 'Florida'}, + {code: 'KS', name: 'Kansas'}, + {code: 'MA', name: 'Massachusetts'}, + {code: 'NY', name: 'New York'}, + {code: 'OR', name: 'Oregon'}, + {code: 'PA', name: 'Pennsylvania'}, + {code: 'TN', name: 'Tennessee'}, + {code: 'VA', name: 'Virginia'}, + {code: 'WY', name: 'Wyoming'}, + ]; + + constructor() { + this.filteredStates = this.states; + this.valueSub = this.stateCtrl.valueChanges.subscribe(val => { + this.filteredStates = val + ? this.states.filter(s => s.name.match(new RegExp(val as string, 'gi'))) + : this.states; + }); + } + + displayFn(value: any): string { + return value ? value.name : value; + } + + ngOnDestroy() { + this.valueSub.unsubscribe(); + } +} diff --git a/src/material/badge/testing/badge-harness.spec.ts b/src/material/badge/testing/badge-harness.spec.ts index 8d5369519a43..d44c0fa337ea 100644 --- a/src/material/badge/testing/badge-harness.spec.ts +++ b/src/material/badge/testing/badge-harness.spec.ts @@ -1,7 +1,7 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatBadgeModule, MatBadgePosition, MatBadgeSize} from '@angular/material/badge'; import {MatBadgeHarness} from './badge-harness'; @@ -12,7 +12,6 @@ describe('MatBadgeHarness', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [MatBadgeModule, BadgeHarnessTest], - providers: [provideZoneChangeDetection()], }).compileComponents(); fixture = TestBed.createComponent(BadgeHarnessTest); @@ -30,6 +29,7 @@ describe('MatBadgeHarness', () => { expect(await badge.getText()).toBe('Simple badge'); fixture.componentInstance.simpleContent = 'Changed simple badge'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getText()).toBe('Changed simple badge'); }); @@ -51,6 +51,7 @@ describe('MatBadgeHarness', () => { expect(await badge.isOverlapping()).toBe(true); fixture.componentInstance.overlap = false; + fixture.changeDetectorRef.markForCheck(); expect(await badge.isOverlapping()).toBe(false); }); @@ -59,6 +60,7 @@ describe('MatBadgeHarness', () => { expect(await badge.isDisabled()).toBe(true); fixture.componentInstance.disabled = false; + fixture.changeDetectorRef.markForCheck(); expect(await badge.isDisabled()).toBe(false); }); @@ -67,6 +69,7 @@ describe('MatBadgeHarness', () => { expect(await badge.isHidden()).toBe(true); fixture.componentInstance.hidden = false; + fixture.changeDetectorRef.markForCheck(); expect(await badge.isHidden()).toBe(false); }); @@ -77,15 +80,19 @@ describe('MatBadgeHarness', () => { expect(await badge.getPosition()).toBe('above after'); instance.position = 'below'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getPosition()).toBe('below after'); instance.position = 'below before'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getPosition()).toBe('below before'); instance.position = 'above'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getPosition()).toBe('above after'); instance.position = 'above before'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getPosition()).toBe('above before'); }); @@ -96,9 +103,11 @@ describe('MatBadgeHarness', () => { expect(await badge.getSize()).toBe('medium'); instance.size = 'small'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getSize()).toBe('small'); instance.size = 'large'; + fixture.changeDetectorRef.markForCheck(); expect(await badge.getSize()).toBe('large'); }); }); diff --git a/src/material/bottom-sheet/bottom-sheet.spec.ts b/src/material/bottom-sheet/bottom-sheet.spec.ts index 3826b3e0de1e..7fdabe91fe22 100644 --- a/src/material/bottom-sheet/bottom-sheet.spec.ts +++ b/src/material/bottom-sheet/bottom-sheet.spec.ts @@ -20,7 +20,6 @@ import { ViewChild, ViewContainerRef, ViewEncapsulation, - provideZoneChangeDetection, } from '@angular/core'; import { ComponentFixture, @@ -62,7 +61,7 @@ describe('MatBottomSheet', () => { BottomSheetWithInjectedData, ShadowDomComponent, ], - providers: [provideZoneChangeDetection(), {provide: Location, useClass: SpyLocation}], + providers: [{provide: Location, useClass: SpyLocation}], }).compileComponents(); })); @@ -642,6 +641,7 @@ describe('MatBottomSheet', () => { viewContainerFixture.detectChanges(); await viewContainerFixture.whenStable(); + viewContainerFixture.detectChanges(); expect(document.activeElement!.tagName) .withContext('Expected first tabbable element (input) in the dialog to be focused.') diff --git a/src/material/button-toggle/button-toggle.spec.ts b/src/material/button-toggle/button-toggle.spec.ts index 92c6defac09f..bba68c81416c 100644 --- a/src/material/button-toggle/button-toggle.spec.ts +++ b/src/material/button-toggle/button-toggle.spec.ts @@ -1,14 +1,7 @@ import {dispatchMouseEvent} from '@angular/cdk/testing/private'; -import { - Component, - DebugElement, - QueryList, - ViewChild, - ViewChildren, - provideZoneChangeDetection, -} from '@angular/core'; import {CommonModule} from '@angular/common'; -import {ComponentFixture, fakeAsync, flush, TestBed, tick} from '@angular/core/testing'; +import {Component, DebugElement, QueryList, ViewChild, ViewChildren} from '@angular/core'; +import {ComponentFixture, TestBed, fakeAsync, flush, tick} from '@angular/core/testing'; import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms'; import {By} from '@angular/platform-browser'; import { @@ -20,11 +13,6 @@ import { } from './index'; describe('MatButtonToggle with forms', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -296,16 +284,18 @@ describe('MatButtonToggle with forms', () => { fixture.detectChanges(); instance.values.shift(); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(instance.toggles.map(t => t.checked)).toEqual([false, true]); instance.values.unshift('a'); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(instance.toggles.map(t => t.checked)).toEqual([false, false, true]); }); - it('should preserve the pre-selected option if it removed and re-added', () => { + it('should preserve the pre-selected option if it is removed and re-added', () => { const fixture = TestBed.createComponent(ButtonToggleGroupWithFormControlAndDynamicButtons); const instance = fixture.componentInstance; instance.control.setValue('a'); @@ -313,10 +303,12 @@ describe('MatButtonToggle with forms', () => { expect(instance.toggles.map(t => t.checked)).toEqual([true, false, false]); instance.values.shift(); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(instance.toggles.map(t => t.checked)).toEqual([false, false]); instance.values.unshift('a'); + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(instance.toggles.map(t => t.checked)).toEqual([true, false, false]); @@ -324,11 +316,6 @@ describe('MatButtonToggle with forms', () => { }); describe('MatButtonToggle without forms', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -410,6 +397,7 @@ describe('MatButtonToggle without forms', () => { it('should disable click interactions when the group is disabled', () => { testComponent.isGroupDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); buttonToggleNativeElements[0].click(); @@ -417,6 +405,7 @@ describe('MatButtonToggle without forms', () => { expect(buttonToggleInstances[0].disabled).toBe(true); testComponent.isGroupDisabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonToggleInstances[0].disabled).toBe(false); @@ -431,6 +420,7 @@ describe('MatButtonToggle without forms', () => { expect(groupNativeElement.getAttribute('aria-disabled')).toBe('false'); testComponent.isGroupDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(groupNativeElement.getAttribute('aria-disabled')).toBe('true'); @@ -442,6 +432,7 @@ describe('MatButtonToggle without forms', () => { expect(buttons.every(input => input.disabled)).toBe(false); testComponent.isGroupDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttons.every(input => input.disabled)).toBe(true); @@ -496,6 +487,7 @@ describe('MatButtonToggle without forms', () => { expect(groupNativeElement.classList).not.toContain('mat-button-toggle-vertical'); groupInstance.vertical = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(groupNativeElement.classList).toContain('mat-button-toggle-vertical'); @@ -541,6 +533,7 @@ describe('MatButtonToggle without forms', () => { expect(groupInstance.value).toBeFalsy(); testComponent.groupValue = 'test1'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(groupInstance.value).toBe('test1'); @@ -549,6 +542,7 @@ describe('MatButtonToggle without forms', () => { expect(buttonToggleInstances[1].checked).toBe(false); testComponent.groupValue = 'test2'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(groupInstance.value).toBe('test2'); @@ -576,6 +570,7 @@ describe('MatButtonToggle without forms', () => { expect(groupInstance.selected).toBe(buttonToggleInstances[0]); testComponent.renderFirstToggle = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); tick(); @@ -645,6 +640,7 @@ describe('MatButtonToggle without forms', () => { it('should disable click interactions when the group is disabled', () => { testComponent.isGroupDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); buttonToggleNativeElements[0].click(); @@ -695,6 +691,7 @@ describe('MatButtonToggle without forms', () => { expect(groupNativeElement.classList).not.toContain('mat-button-toggle-vertical'); groupInstance.vertical = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(groupNativeElement.classList).toContain('mat-button-toggle-vertical'); @@ -1003,6 +1000,7 @@ describe('MatButtonToggle without forms', () => { fixture.componentInstance.possibleValues = ['Five', 'Six', 'Seven']; fixture.componentInstance.value = 'Seven'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.toggleGroup.value).toBe('Seven'); @@ -1019,6 +1017,7 @@ describe('MatButtonToggle without forms', () => { expect(fixture.componentInstance.toggles.toArray()[2].checked).toBe(false); fixture.componentInstance.value = [0, false]; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(fixture.componentInstance.toggles.toArray()[0].checked).toBe(true); diff --git a/src/material/button-toggle/testing/button-toggle-group.spec.ts b/src/material/button-toggle/testing/button-toggle-group.spec.ts index 8158bf8971be..02802b67fdb0 100644 --- a/src/material/button-toggle/testing/button-toggle-group.spec.ts +++ b/src/material/button-toggle/testing/button-toggle-group.spec.ts @@ -1,18 +1,13 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {MatButtonToggleModule, MatButtonToggleAppearance} from '@angular/material/button-toggle'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {MatButtonToggleAppearance, MatButtonToggleModule} from '@angular/material/button-toggle'; import {MatButtonToggleGroupHarness} from './button-toggle-group-harness'; describe('MatButtonToggleGroupHarness', () => { let fixture: ComponentFixture; let loader: HarnessLoader; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -39,6 +34,7 @@ describe('MatButtonToggleGroupHarness', () => { const group = await loader.getHarness(MatButtonToggleGroupHarness); expect(await group.isDisabled()).toBe(false); fixture.componentInstance.disabled = true; + fixture.changeDetectorRef.markForCheck(); expect(await group.isDisabled()).toBe(true); }); @@ -53,6 +49,7 @@ describe('MatButtonToggleGroupHarness', () => { expect(disabledGroups.length).toBe(0); fixture.componentInstance.disabled = true; + fixture.changeDetectorRef.markForCheck(); enabledGroups = await loader.getAllHarnesses( MatButtonToggleGroupHarness.with({disabled: false}), @@ -68,13 +65,15 @@ describe('MatButtonToggleGroupHarness', () => { const group = await loader.getHarness(MatButtonToggleGroupHarness); expect(await group.isVertical()).toBe(false); fixture.componentInstance.vertical = true; + fixture.changeDetectorRef.markForCheck(); expect(await group.isVertical()).toBe(true); }); - it('should get whether the group appearance', async () => { + it('should get the group appearance', async () => { const group = await loader.getHarness(MatButtonToggleGroupHarness); expect(await group.getAppearance()).toBe('standard'); fixture.componentInstance.appearance = 'legacy'; + fixture.changeDetectorRef.markForCheck(); expect(await group.getAppearance()).toBe('legacy'); }); }); diff --git a/src/material/button-toggle/testing/button-toggle-harness.spec.ts b/src/material/button-toggle/testing/button-toggle-harness.spec.ts index 7a75f1abfb9e..595f87b045c2 100644 --- a/src/material/button-toggle/testing/button-toggle-harness.spec.ts +++ b/src/material/button-toggle/testing/button-toggle-harness.spec.ts @@ -1,7 +1,7 @@ -import {Component, provideZoneChangeDetection} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {Component} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatButtonToggleModule} from '@angular/material/button-toggle'; import {MatButtonToggleHarness} from './button-toggle-harness'; @@ -9,11 +9,6 @@ describe('MatButtonToggleHarness', () => { let fixture: ComponentFixture; let loader: HarnessLoader; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideZoneChangeDetection()], - }); - }); beforeEach(async () => { await TestBed.configureTestingModule({ imports: [MatButtonToggleModule, ButtonToggleHarnessTest], @@ -108,6 +103,7 @@ describe('MatButtonToggleHarness', () => { it('should toggle the button value', async () => { fixture.componentInstance.disabled = false; + fixture.changeDetectorRef.markForCheck(); const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(MatButtonToggleHarness); await checkedToggle.toggle(); await uncheckedToggle.toggle(); @@ -117,6 +113,7 @@ describe('MatButtonToggleHarness', () => { it('should check the button toggle', async () => { fixture.componentInstance.disabled = false; + fixture.changeDetectorRef.markForCheck(); const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(MatButtonToggleHarness); await checkedToggle.check(); await uncheckedToggle.check(); @@ -126,6 +123,7 @@ describe('MatButtonToggleHarness', () => { it('should uncheck the button toggle', async () => { fixture.componentInstance.disabled = false; + fixture.changeDetectorRef.markForCheck(); const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(MatButtonToggleHarness); await checkedToggle.uncheck(); await uncheckedToggle.uncheck(); diff --git a/src/material/button/button.spec.ts b/src/material/button/button.spec.ts index 6f914fedac5a..8e9785f68b9d 100644 --- a/src/material/button/button.spec.ts +++ b/src/material/button/button.spec.ts @@ -1,15 +1,14 @@ -import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; -import {ApplicationRef, Component, DebugElement, provideZoneChangeDetection} from '@angular/core'; -import {By} from '@angular/platform-browser'; -import {MatButtonModule, MatButton, MatFabDefaultOptions, MAT_FAB_DEFAULT_OPTIONS} from './index'; -import {MatRipple, ThemePalette} from '@angular/material/core'; import {createMouseEvent, dispatchEvent} from '@angular/cdk/testing/private'; +import {ApplicationRef, Component, DebugElement} from '@angular/core'; +import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; +import {MatRipple, ThemePalette} from '@angular/material/core'; +import {By} from '@angular/platform-browser'; +import {MAT_FAB_DEFAULT_OPTIONS, MatButton, MatButtonModule, MatFabDefaultOptions} from './index'; describe('MDC-based MatButton', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [MatButtonModule, TestApp], - providers: [provideZoneChangeDetection()], }); TestBed.compileComponents(); @@ -24,16 +23,19 @@ describe('MDC-based MatButton', () => { let aDebugElement = fixture.debugElement.query(By.css('a'))!; testComponent.buttonColor = 'primary'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.classList.contains('mat-primary')).toBe(true); expect(aDebugElement.nativeElement.classList.contains('mat-primary')).toBe(true); testComponent.buttonColor = 'accent'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.classList.contains('mat-accent')).toBe(true); expect(aDebugElement.nativeElement.classList.contains('mat-accent')).toBe(true); testComponent.buttonColor = null; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.classList).not.toContain('mat-accent'); @@ -49,6 +51,7 @@ describe('MDC-based MatButton', () => { expect(anchor.classList).not.toContain('mat-mdc-button-disabled'); fixture.componentInstance.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(button.classList).toContain('mat-mdc-button-disabled'); @@ -71,12 +74,14 @@ describe('MDC-based MatButton', () => { buttonDebugElement.nativeElement.classList.add('custom-class'); testComponent.buttonColor = 'primary'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.classList.contains('mat-primary')).toBe(true); expect(buttonDebugElement.nativeElement.classList.contains('custom-class')).toBe(true); testComponent.buttonColor = 'accent'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.classList.contains('mat-primary')).toBe(false); @@ -121,6 +126,7 @@ describe('MDC-based MatButton', () => { ).toBeFalse(); fixture.componentInstance.extended = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(extendedFabButtonDebugEl.nativeElement.classList).toContain('mat-mdc-extended-fab'); @@ -144,6 +150,7 @@ describe('MDC-based MatButton', () => { let buttonDebugElement = fixture.debugElement.query(By.css('button'))!; testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); buttonDebugElement.nativeElement.click(); @@ -159,6 +166,7 @@ describe('MDC-based MatButton', () => { .toBeFalsy(); fixture.componentInstance.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonNativeElement.disabled) .withContext('Expected button to be disabled') @@ -174,6 +182,7 @@ describe('MDC-based MatButton', () => { let buttonDebugElement = fixture.debugElement.query(By.css('a'))!; testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); buttonDebugElement.nativeElement.click(); @@ -188,6 +197,7 @@ describe('MDC-based MatButton', () => { expect(buttonDebugElement.nativeElement.hasAttribute('tabindex')).toBe(false); testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.getAttribute('tabindex')).toBe('-1'); }); @@ -200,6 +210,7 @@ describe('MDC-based MatButton', () => { expect(buttonDebugElement.nativeElement.hasAttribute('aria-disabled')).toBe(false); testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.getAttribute('aria-disabled')).toBe('true'); }); @@ -213,6 +224,7 @@ describe('MDC-based MatButton', () => { expect(buttonDebugElement.nativeElement.getAttribute('disabled')).toBeNull(); testComponent.isDisabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonDebugElement.nativeElement.hasAttribute('aria-disabled')).toBe(false); expect(buttonDebugElement.nativeElement.getAttribute('disabled')).toBeNull(); @@ -231,6 +243,7 @@ describe('MDC-based MatButton', () => { .toBe('3'); testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonElement.getAttribute('tabindex')) @@ -251,6 +264,7 @@ describe('MDC-based MatButton', () => { const appRef = TestBed.inject(ApplicationRef); const fixture = TestBed.createComponent(TestApp); fixture.componentInstance.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const anchorElement = fixture.debugElement.query(By.css('a'))!.nativeElement; @@ -295,6 +309,7 @@ describe('MDC-based MatButton', () => { expect(buttonRippleInstance.disabled).toBeFalsy(); testComponent.rippleDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonRippleInstance.disabled).toBeTruthy(); @@ -309,6 +324,7 @@ describe('MDC-based MatButton', () => { ); testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(buttonRippleInstance.disabled).toBeTruthy( @@ -365,6 +381,7 @@ describe('MDC-based MatButton', () => { beforeEach(() => { fixture = TestBed.createComponent(TestApp); fixture.componentInstance.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); button = fixture.debugElement.query(By.css('button'))!.nativeElement; }); @@ -373,6 +390,7 @@ describe('MDC-based MatButton', () => { expect(button.classList).not.toContain('mat-mdc-button-disabled-interactive'); fixture.componentInstance.disabledInteractive = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(button.classList).toContain('mat-mdc-button-disabled-interactive'); @@ -382,6 +400,7 @@ describe('MDC-based MatButton', () => { expect(button.hasAttribute('aria-disabled')).toBe(false); fixture.componentInstance.disabledInteractive = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(button.getAttribute('aria-disabled')).toBe('true'); @@ -391,6 +410,7 @@ describe('MDC-based MatButton', () => { expect(button.getAttribute('disabled')).toBe('true'); fixture.componentInstance.disabledInteractive = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(button.hasAttribute('disabled')).toBe(false); diff --git a/src/material/checkbox/checkbox.spec.ts b/src/material/checkbox/checkbox.spec.ts index 5ee789abd89b..d7efa1223b32 100644 --- a/src/material/checkbox/checkbox.spec.ts +++ b/src/material/checkbox/checkbox.spec.ts @@ -1,20 +1,14 @@ import {dispatchFakeEvent} from '@angular/cdk/testing/private'; -import { - ChangeDetectionStrategy, - Component, - DebugElement, - Type, - provideZoneChangeDetection, -} from '@angular/core'; -import {ComponentFixture, fakeAsync, flush, flushMicrotasks, TestBed} from '@angular/core/testing'; -import {ThemePalette} from '@angular/material/core'; +import {ChangeDetectionStrategy, Component, DebugElement, Type} from '@angular/core'; +import {ComponentFixture, TestBed, fakeAsync, flush, flushMicrotasks} from '@angular/core/testing'; import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms'; +import {ThemePalette} from '@angular/material/core'; import {By} from '@angular/platform-browser'; import { - MatCheckboxDefaultOptions, MAT_CHECKBOX_DEFAULT_OPTIONS, MatCheckbox, MatCheckboxChange, + MatCheckboxDefaultOptions, MatCheckboxModule, } from './index'; @@ -24,7 +18,6 @@ describe('MDC-based MatCheckbox', () => { function createComponent(componentType: Type) { TestBed.configureTestingModule({ imports: [MatCheckboxModule, FormsModule, ReactiveFormsModule, componentType], - providers: [provideZoneChangeDetection()], }).compileComponents(); return TestBed.createComponent(componentType); @@ -57,6 +50,7 @@ describe('MDC-based MatCheckbox', () => { expect(inputElement.checked).toBe(false); testComponent.isChecked = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.checked).toBe(true); @@ -66,6 +60,7 @@ describe('MDC-based MatCheckbox', () => { .toBe(false); testComponent.isChecked = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.checked).toBe(false); @@ -85,6 +80,7 @@ describe('MDC-based MatCheckbox', () => { const rippleSelector = '.mat-ripple-element:not(.mat-checkbox-persistent-ripple)'; testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(checkboxElement, 'mousedown'); dispatchFakeEvent(checkboxElement, 'mouseup'); @@ -93,6 +89,7 @@ describe('MDC-based MatCheckbox', () => { flush(); testComponent.isDisabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(checkboxElement, 'mousedown'); dispatchFakeEvent(checkboxElement, 'mouseup'); @@ -107,6 +104,7 @@ describe('MDC-based MatCheckbox', () => { expect(inputElement.indeterminate).toBe(false); testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); @@ -114,6 +112,7 @@ describe('MDC-based MatCheckbox', () => { expect(inputElement.indeterminate).toBe(true); testComponent.isIndeterminate = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); @@ -123,6 +122,7 @@ describe('MDC-based MatCheckbox', () => { it('should set indeterminate to false when input clicked', fakeAsync(() => { testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.indeterminate).toBe(true); @@ -145,6 +145,7 @@ describe('MDC-based MatCheckbox', () => { expect(testComponent.isIndeterminate).toBe(false); testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.indeterminate).toBe(true); @@ -170,6 +171,7 @@ describe('MDC-based MatCheckbox', () => { it('should not set indeterminate to false when checked is set programmatically', fakeAsync(() => { testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); @@ -178,6 +180,7 @@ describe('MDC-based MatCheckbox', () => { expect(testComponent.isIndeterminate).toBe(true); testComponent.isChecked = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.checked).toBe(true); @@ -186,6 +189,7 @@ describe('MDC-based MatCheckbox', () => { expect(testComponent.isIndeterminate).toBe(true); testComponent.isChecked = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.checked).toBe(false); @@ -222,6 +226,7 @@ describe('MDC-based MatCheckbox', () => { it('should change from indeterminate to checked on click', fakeAsync(() => { testComponent.isChecked = false; testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.checked).toBe(false); @@ -248,12 +253,14 @@ describe('MDC-based MatCheckbox', () => { expect(inputElement.disabled).toBe(false); testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.disabled).toBe(true); expect(inputElement.disabled).toBe(true); testComponent.isDisabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.disabled).toBe(false); @@ -263,6 +270,7 @@ describe('MDC-based MatCheckbox', () => { it('should not toggle `checked` state upon interation while disabled', fakeAsync(() => { testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); checkboxNativeElement.click(); @@ -271,6 +279,7 @@ describe('MDC-based MatCheckbox', () => { it('should overwrite indeterminate state when clicked', fakeAsync(() => { testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); inputElement.click(); @@ -290,6 +299,7 @@ describe('MDC-based MatCheckbox', () => { it('should generate a unique id for the checkbox input if no id is set', fakeAsync(() => { testComponent.checkboxId = null; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxInstance.inputId).toMatch(/mat-mdc-checkbox-\d+/); @@ -307,6 +317,7 @@ describe('MDC-based MatCheckbox', () => { it('should add a css class to position the label before the checkbox', fakeAsync(() => { testComponent.labelPos = 'before'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxNativeElement.querySelector('.mdc-form-field')!.classList).toContain( @@ -364,6 +375,7 @@ describe('MDC-based MatCheckbox', () => { expect(inputElement.checked).toBe(false); testComponent.isChecked = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flush(); @@ -385,11 +397,13 @@ describe('MDC-based MatCheckbox', () => { it('should forward the required attribute', fakeAsync(() => { testComponent.isRequired = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputElement.required).toBe(true); testComponent.isRequired = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputElement.required).toBe(false); @@ -420,6 +434,7 @@ describe('MDC-based MatCheckbox', () => { it('should forward the value to input element', fakeAsync(() => { testComponent.checkboxValue = 'basic_checkbox'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputElement.value).toBe('basic_checkbox'); @@ -447,6 +462,7 @@ describe('MDC-based MatCheckbox', () => { it('should not show ripples when disabled', fakeAsync(() => { const rippleSelector = '.mat-ripple-element:not(.mat-checkbox-persistent-ripple)'; testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(checkboxElement, 'mousedown'); @@ -457,6 +473,7 @@ describe('MDC-based MatCheckbox', () => { flush(); testComponent.isDisabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(checkboxElement, 'mousedown'); @@ -471,6 +488,7 @@ describe('MDC-based MatCheckbox', () => { it('should remove ripple if matRippleDisabled input is set', fakeAsync(() => { const rippleSelector = '.mat-ripple-element:not(.mat-checkbox-persistent-ripple)'; testComponent.disableRipple = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(checkboxElement, 'mousedown'); @@ -481,6 +499,7 @@ describe('MDC-based MatCheckbox', () => { flush(); testComponent.disableRipple = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); dispatchFakeEvent(checkboxElement, 'mousedown'); @@ -496,10 +515,12 @@ describe('MDC-based MatCheckbox', () => { describe('color behaviour', () => { it('should apply class based on color attribute', fakeAsync(() => { testComponent.checkboxColor = 'primary'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxNativeElement.classList.contains('mat-primary')).toBe(true); testComponent.checkboxColor = 'accent'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxNativeElement.classList.contains('mat-accent')).toBe(true); })); @@ -508,12 +529,14 @@ describe('MDC-based MatCheckbox', () => { checkboxNativeElement.classList.add('custom-class'); testComponent.checkboxColor = 'primary'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxNativeElement.classList.contains('mat-primary')).toBe(true); expect(checkboxNativeElement.classList.contains('custom-class')).toBe(true); testComponent.checkboxColor = 'accent'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxNativeElement.classList.contains('mat-primary')).toBe(false); @@ -523,6 +546,7 @@ describe('MDC-based MatCheckbox', () => { it('should default to accent if no color is passed in', fakeAsync(() => { testComponent.checkboxColor = undefined; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(checkboxNativeElement.classList).toContain('mat-accent'); })); @@ -550,6 +574,7 @@ describe('MDC-based MatCheckbox', () => { it('should not set `indeterminate` to false on click if check is set', fakeAsync(() => { testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); inputElement.click(); fixture.detectChanges(); flush(); @@ -580,6 +605,7 @@ describe('MDC-based MatCheckbox', () => { it('should not change `indeterminate` on click if noop is set', fakeAsync(() => { testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); inputElement.click(); fixture.detectChanges(); flush(); @@ -591,6 +617,7 @@ describe('MDC-based MatCheckbox', () => { it(`should not change 'checked' or 'indeterminate' on click if noop is set`, fakeAsync(() => { testComponent.isChecked = true; testComponent.isIndeterminate = true; + fixture.changeDetectorRef.markForCheck(); inputElement.click(); fixture.detectChanges(); flush(); @@ -599,6 +626,7 @@ describe('MDC-based MatCheckbox', () => { expect(inputElement.indeterminate).toBe(true); testComponent.isChecked = false; + fixture.changeDetectorRef.markForCheck(); inputElement.click(); fixture.detectChanges(); flush(); @@ -770,12 +798,15 @@ describe('MDC-based MatCheckbox', () => { it('should preserve given tabIndex when the checkbox is disabled then enabled', fakeAsync(() => { testComponent.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.customTabIndex = 13; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); testComponent.isDisabled = false; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputElement.tabIndex).toBe(13); @@ -903,6 +934,7 @@ describe('MDC-based MatCheckbox', () => { inputElement.focus(); fixture.componentInstance.isDisabled = true; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); flushMicrotasks(); @@ -1045,6 +1077,7 @@ describe('MatCheckboxDefaultOptions', () => { configure({color: 'primary'}); const fixture: ComponentFixture = TestBed.createComponent(SingleCheckbox); fixture.componentInstance.checkboxColor = 'warn'; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!; expect(checkboxDebugElement.nativeElement.classList).not.toContain('mat-primary'); @@ -1056,6 +1089,7 @@ describe('MatCheckboxDefaultOptions', () => { configure({clickAction: 'noop'}); const fixture: ComponentFixture = TestBed.createComponent(SingleCheckbox); fixture.componentInstance.checkboxColor = undefined; + fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!; expect(checkboxDebugElement.nativeElement.classList).toContain('mat-accent');