refactor(common): modernize pipes & non bindable tests to rely on whenStable

Rely on zoneless scheduling throughout reactive forms tests instead of triggering change detection manually.
This commit is contained in:
Jaime Burgos
2026-07-30 10:56:25 -05:00
committed by GitHub
parent 5d76720e06
commit d068fc1ea0
10 changed files with 52 additions and 69 deletions
@@ -12,24 +12,18 @@ import {hasClass} from '@angular/private/testing';
import {expect} from '@angular/private/testing/matchers';
describe('non-bindable', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent, TestDirective],
});
});
it('should not interpolate children', () => {
it('should not interpolate children', async () => {
const template = '<div>{{text}}<span ngNonBindable>{{text}}</span></div>';
const fixture = createTestComponent(template);
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement).toHaveText('foo{{text}}');
});
it('should ignore directives on child nodes', () => {
it('should ignore directives on child nodes', async () => {
const template = '<div ngNonBindable><span id=child test-dec>{{text}}</span></div>';
const fixture = createTestComponent(template);
fixture.detectChanges();
await fixture.whenStable();
// We must use getDOM().querySelector instead of fixture.query here
// since the elements inside are not compiled.
@@ -37,10 +31,10 @@ describe('non-bindable', () => {
expect(hasClass(span, 'compiled')).toBeFalsy();
});
it('should trigger directives on the same node', () => {
it('should trigger directives on the same node', async () => {
const template = '<div><span id=child ngNonBindable test-dec>{{text}}</span></div>';
const fixture = createTestComponent(template);
fixture.detectChanges();
await fixture.whenStable();
const span = fixture.nativeElement.querySelector('#child');
expect(hasClass(span, 'compiled')).toBeTruthy();
});
@@ -48,7 +42,6 @@ describe('non-bindable', () => {
@Directive({
selector: '[test-dec]',
standalone: false,
})
class TestDirective {
constructor(el: ElementRef) {
@@ -59,7 +52,7 @@ class TestDirective {
@Component({
selector: 'test-cmp',
template: '',
standalone: false,
imports: [TestDirective],
})
class TestComponent {
text: string;
@@ -25,7 +25,7 @@ describe('AsyncPipe', () => {
useAutoTick();
function getChangeDetectorRefSpy() {
return jasmine.createSpyObj('ChangeDetectorRef', ['markForCheck', 'detectChanges']);
return jasmine.createSpyObj('ChangeDetectorRef', ['markForCheck']);
}
beforeEach(() => {
@@ -296,7 +296,7 @@ describe('AsyncPipe', () => {
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [AsyncPipe],
@@ -307,7 +307,7 @@ describe('AsyncPipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('foo');
@@ -40,7 +40,7 @@ describe('LowerCasePipe', () => {
expect(() => pipe.transform({} as any)).toThrowError();
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [LowerCasePipe],
@@ -51,7 +51,7 @@ describe('LowerCasePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('foo');
@@ -131,7 +131,7 @@ describe('TitleCasePipe', () => {
expect(() => pipe.transform({} as any)).toThrowError();
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [TitleCasePipe],
@@ -142,7 +142,7 @@ describe('TitleCasePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('Foo');
@@ -179,7 +179,7 @@ describe('UpperCasePipe', () => {
expect(() => pipe.transform({} as any)).toThrowError();
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [UpperCasePipe],
@@ -190,7 +190,7 @@ describe('UpperCasePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('FOO');
+10 -10
View File
@@ -83,7 +83,7 @@ describe('DatePipe', () => {
);
});
it('should use format provided in component as default format when no format is passed in', () => {
it('should use format provided in component as default format when no format is passed in', async () => {
@Component({
selector: 'test-component',
imports: [DatePipe],
@@ -95,13 +95,13 @@ describe('DatePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('1/11/17');
});
it('should use format provided in module as default format when no format is passed in', () => {
it('should use format provided in module as default format when no format is passed in', async () => {
@Component({
selector: 'test-component',
imports: [DatePipe],
@@ -116,7 +116,7 @@ describe('DatePipe', () => {
providers: [{provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'shortDate'}}],
});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('1/11/17');
@@ -171,7 +171,7 @@ describe('DatePipe', () => {
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate', '+0100')).toEqual('Jan 11, 2017');
});
it('should use timezone provided in component as default timezone when no format is passed in', () => {
it('should use timezone provided in component as default timezone when no format is passed in', async () => {
@Component({
selector: 'test-component',
imports: [DatePipe],
@@ -183,13 +183,13 @@ describe('DatePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('Jan 10, 2017');
});
it('should use timezone provided in module as default timezone when no format is passed in', () => {
it('should use timezone provided in module as default timezone when no format is passed in', async () => {
@Component({
selector: 'test-component',
imports: [DatePipe],
@@ -204,14 +204,14 @@ describe('DatePipe', () => {
providers: [{provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {timezone: '-1200'}}],
});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('Jan 10, 2017');
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [DatePipe],
@@ -222,7 +222,7 @@ describe('DatePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('Jan 11, 2017');
@@ -62,7 +62,7 @@ describe('I18nPluralPipe', () => {
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [I18nPluralPipe],
@@ -74,7 +74,7 @@ describe('I18nPluralPipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('One message.');
@@ -54,7 +54,7 @@ describe('I18nSelectPipe', () => {
expect(pipe.transform('greeting', shadowedMapping)).toEqual('hello');
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [I18nSelectPipe],
@@ -66,7 +66,7 @@ describe('I18nSelectPipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('Invite them.');
+7 -12
View File
@@ -10,7 +10,7 @@ import {ChangeDetectionStrategy} from '@angular/compiler';
import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/private/testing/matchers';
import {CommonModule, JsonPipe} from '../../index';
import {JsonPipe} from '../../index';
describe('JsonPipe', () => {
const regNewLine = '\n';
@@ -58,33 +58,28 @@ describe('JsonPipe', () => {
@Component({
selector: 'test-comp',
template: '{{data | json}}',
standalone: false,
imports: [JsonPipe],
changeDetection: ChangeDetectionStrategy.Eager,
})
class TestComp {
data: any;
}
beforeEach(() => {
TestBed.configureTestingModule({declarations: [TestComp], imports: [CommonModule]});
});
it('should work with mutable objects', () => {
it('should work with mutable objects', async () => {
const fixture = TestBed.createComponent(TestComp);
const mutable: number[] = [1];
fixture.componentInstance.data = mutable;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
mutable.push(2);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement).toHaveText('[\n 1,\n 2\n]');
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [JsonPipe],
@@ -95,7 +90,7 @@ describe('JsonPipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content.replace(/\s/g, '')).toBe('{"a":1}');
@@ -269,7 +269,7 @@ describe('KeyValuePipe', () => {
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [KeyValuePipe, JsonPipe],
@@ -280,7 +280,7 @@ describe('KeyValuePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content.replace(/\s/g, '')).toBe('[{"key":"a","value":2},{"key":"b","value":1}]');
@@ -78,7 +78,7 @@ describe('Number pipes', () => {
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [DecimalPipe],
@@ -89,7 +89,7 @@ describe('Number pipes', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('12,345');
@@ -130,7 +130,7 @@ describe('Number pipes', () => {
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [PercentPipe],
@@ -141,7 +141,7 @@ describe('Number pipes', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('1,500%');
@@ -227,7 +227,7 @@ describe('Number pipes', () => {
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [CurrencyPipe],
@@ -238,7 +238,7 @@ describe('Number pipes', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('$15.00');
+7 -12
View File
@@ -10,7 +10,7 @@ import {ChangeDetectionStrategy} from '@angular/compiler';
import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/private/testing/matchers';
import {CommonModule, SlicePipe} from '../../index';
import {SlicePipe} from '../../index';
describe('SlicePipe', () => {
let list: number[];
@@ -94,33 +94,28 @@ describe('SlicePipe', () => {
@Component({
selector: 'test-comp',
template: '{{(data | slice:1).join(",") }}',
standalone: false,
imports: [SlicePipe],
changeDetection: ChangeDetectionStrategy.Eager,
})
class TestComp {
data: any;
}
beforeEach(() => {
TestBed.configureTestingModule({declarations: [TestComp], imports: [CommonModule]});
});
it('should work with mutable arrays', () => {
it('should work with mutable arrays', async () => {
const fixture = TestBed.createComponent(TestComp);
const mutable: number[] = [1, 2];
fixture.componentInstance.data = mutable;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement).toHaveText('2');
mutable.push(3);
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement).toHaveText('2,3');
});
});
it('should be available as a standalone pipe', () => {
it('should be available as a standalone pipe', async () => {
@Component({
selector: 'test-component',
imports: [SlicePipe],
@@ -132,7 +127,7 @@ describe('SlicePipe', () => {
}
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
await fixture.whenStable();
const content = fixture.nativeElement.textContent;
expect(content).toBe('Hello');