Skip to content

Commit e41c034

Browse files
fix: don't call detectChanges when fixture is destroyed (#57)
1 parent 6989a66 commit e41c034

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

projects/testing-library/src/lib/testing-library.ts

+18-9
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export async function render<SutType, WrapperType = SutType>(
3131
renderOptions: RenderComponentOptions<SutType> | RenderDirectiveOptions<SutType, WrapperType> = {},
3232
): Promise<RenderResult<SutType>> {
3333
const {
34-
detectChanges = true,
34+
detectChanges: detectChangesOnRender = true,
3535
declarations = [],
3636
imports = [],
3737
providers = [],
@@ -42,12 +42,12 @@ export async function render<SutType, WrapperType = SutType>(
4242
componentProperties = {},
4343
componentProviders = [],
4444
excludeComponentDeclaration = false,
45-
routes
45+
routes,
4646
} = renderOptions as RenderDirectiveOptions<SutType, WrapperType>;
4747

4848
TestBed.configureTestingModule({
4949
declarations: addAutoDeclarations(sut, { declarations, excludeComponentDeclaration, template, wrapper }),
50-
imports: addAutoImports({imports, routes}),
50+
imports: addAutoImports({ imports, routes }),
5151
providers: [...providers],
5252
schemas: [...schemas],
5353
});
@@ -66,15 +66,24 @@ export async function render<SutType, WrapperType = SutType>(
6666

6767
await TestBed.compileComponents();
6868

69-
if (detectChanges) {
70-
fixture.detectChanges();
69+
let isAlive = true;
70+
fixture.componentRef.onDestroy(() => (isAlive = false));
71+
72+
function detectChanges() {
73+
if (isAlive) {
74+
fixture.detectChanges();
75+
}
76+
}
77+
78+
if (detectChangesOnRender) {
79+
detectChanges();
7180
}
7281

7382
const eventsWithDetectChanges = Object.keys(fireEvent).reduce(
7483
(events, key) => {
7584
events[key] = (element: HTMLElement, options?: {}) => {
7685
const result = fireEvent[key](element, options);
77-
fixture.detectChanges();
86+
detectChanges();
7887
return result;
7988
};
8089
return events;
@@ -93,8 +102,8 @@ export async function render<SutType, WrapperType = SutType>(
93102
const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href');
94103

95104
let result;
96-
await zone.run(() => result = router.navigate([basePath + href]));
97-
fixture.detectChanges();
105+
await zone.run(() => (result = router.navigate([basePath + href])));
106+
detectChanges();
98107
return result;
99108
}
100109
const debugElement = fixture.debugElement.query(By.directive(sut));
@@ -104,7 +113,7 @@ export async function render<SutType, WrapperType = SutType>(
104113
debugElement,
105114
container: fixture.nativeElement,
106115
debug: (element = fixture.nativeElement) => console.log(prettyDOM(element)),
107-
detectChanges: () => fixture.detectChanges(),
116+
detectChanges,
108117
...getQueriesForElement(fixture.nativeElement, queries),
109118
...eventsWithDetectChanges,
110119
type: createType(eventsWithDetectChanges),

projects/testing-library/tests/detect-changes.spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,13 @@ describe('detectChanges', () => {
4040

4141
expect(getByTestId('button').innerHTML).toBe('Button updated after 400ms');
4242
}));
43+
44+
test('does not throw on a destroyed fixture', async () => {
45+
const { getByTestId, type, fixture } = await render(FixtureComponent, { imports: [ReactiveFormsModule] });
46+
47+
fixture.destroy();
48+
49+
type(getByTestId('input'), 'What a great day!');
50+
expect(getByTestId('button').innerHTML).toBe('Button');
51+
});
4352
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Component } from '@angular/core';
2+
import { render } from '../src/public_api';
3+
4+
@Component({
5+
selector: 'fixture',
6+
template: `
7+
<input type="text" data-testid="input" />
8+
`,
9+
})
10+
class FixtureComponent {}
11+
12+
test('does not call detect changes when fixture is destroyed', async () => {
13+
const component = await render(FixtureComponent);
14+
15+
component.fixture.destroy();
16+
17+
// should otherwise throw
18+
component.input(component.getByTestId('input'), { target: { value: 'Bonjour' } });
19+
component.type(component.getByTestId('input'), 'Alles klar');
20+
});

0 commit comments

Comments
 (0)