Skip to content

Commit 9384230

Browse files
committed
fix: don't invoke ngOnChanges when no properties are provided (#326)
Closes #323 BREAKING CHANGE: This change is made to have the same behavior as the run time behavior. BEFORE: The `ngOnChanges` lifecycle is always invoked when a component is rendered. AFTER: The `ngOnChanges` lifecycle is only invoked if a component is rendered with `componentProperties`.
1 parent 02dcdb1 commit 9384230

File tree

2 files changed

+27
-14
lines changed

2 files changed

+27
-14
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export async function render<SutType, WrapperType = SutType>(
210210
let isAlive = true;
211211
fixture.componentRef.onDestroy(() => (isAlive = false));
212212

213-
if (hasOnChangesHook(fixture.componentInstance)) {
213+
if (hasOnChangesHook(fixture.componentInstance) && Object.keys(properties).length > 0) {
214214
const changes = getChangesObj(null, componentProperties);
215215
fixture.componentInstance.ngOnChanges(changes);
216216
}

projects/testing-library/tests/render.spec.ts

+26-13
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,18 @@ import { render, fireEvent, screen } from '../src/public_api';
2222
})
2323
class FixtureComponent {}
2424

25-
test('creates queries and events', async () => {
26-
const view = await render(FixtureComponent);
25+
describe('DTL functionality', () => {
26+
it('creates queries and events', async () => {
27+
const view = await render(FixtureComponent);
2728

28-
/// We wish to test the utility function from `render` here.
29-
// eslint-disable-next-line testing-library/prefer-screen-queries
30-
fireEvent.input(view.getByTestId('input'), { target: { value: 'a super awesome input' } });
31-
// eslint-disable-next-line testing-library/prefer-screen-queries
32-
expect(view.getByDisplayValue('a super awesome input')).toBeInTheDocument();
33-
// eslint-disable-next-line testing-library/prefer-screen-queries
34-
fireEvent.click(view.getByText('button'));
29+
/// We wish to test the utility function from `render` here.
30+
// eslint-disable-next-line testing-library/prefer-screen-queries
31+
fireEvent.input(view.getByTestId('input'), { target: { value: 'a super awesome input' } });
32+
// eslint-disable-next-line testing-library/prefer-screen-queries
33+
expect(view.getByDisplayValue('a super awesome input')).toBeInTheDocument();
34+
// eslint-disable-next-line testing-library/prefer-screen-queries
35+
fireEvent.click(view.getByText('button'));
36+
});
3537
});
3638

3739
describe('standalone', () => {
@@ -201,13 +203,13 @@ describe('Angular component life-cycle hooks', () => {
201203
}
202204

203205
ngOnChanges(changes: SimpleChanges) {
204-
if (changes.name && this.nameChanged) {
205-
this.nameChanged(changes.name.currentValue, changes.name.isFirstChange());
206+
if (this.nameChanged) {
207+
this.nameChanged(changes.name?.currentValue, changes.name?.isFirstChange());
206208
}
207209
}
208210
}
209211

210-
it('will call ngOnInit on initial render', async () => {
212+
it('invokes ngOnInit on initial render', async () => {
211213
const nameInitialized = jest.fn();
212214
const componentProperties = { nameInitialized };
213215
const view = await render(FixtureWithNgOnChangesComponent, { componentProperties });
@@ -218,7 +220,7 @@ describe('Angular component life-cycle hooks', () => {
218220
expect(nameInitialized).toHaveBeenCalledWith('Initial');
219221
});
220222

221-
it('will call ngOnChanges on initial render before ngOnInit', async () => {
223+
it('invokes ngOnChanges on initial render before ngOnInit', async () => {
222224
const nameInitialized = jest.fn();
223225
const nameChanged = jest.fn();
224226
const componentProperties = { nameInitialized, nameChanged, name: 'Sarah' };
@@ -232,6 +234,17 @@ describe('Angular component life-cycle hooks', () => {
232234
/// expect `nameChanged` to be called before `nameInitialized`
233235
expect(nameChanged.mock.invocationCallOrder[0]).toBeLessThan(nameInitialized.mock.invocationCallOrder[0]);
234236
});
237+
238+
it('does not invoke ngOnChanges when no properties are provided', async () => {
239+
@Component({ template: `` })
240+
class TestFixtureComponent implements OnChanges {
241+
ngOnChanges() {
242+
throw new Error('should not be called');
243+
}
244+
}
245+
246+
await render(TestFixtureComponent);
247+
});
235248
});
236249

237250
test('waits for angular app initialization before rendering components', async () => {

0 commit comments

Comments
 (0)