Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e4acbbd

Browse files
feat: support ngOnChanges with correct simple change object within rerender
ref #365
1 parent dc4c22f commit e4acbbd

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-8
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export interface RenderResult<ComponentType, WrapperType = ComponentType> extend
5555
/**
5656
* @description
5757
* Re-render the same component with different properties.
58-
* This creates a new instance of the component.
58+
* Properties not passed in again are removed.
5959
*/
6060
rerender: (
6161
properties?: Pick<

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

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,38 @@ export async function render<SutType, WrapperType = SutType>(
123123

124124
await renderFixture(componentProperties, componentInputs, componentOutputs);
125125

126+
let renderedPropKeys = Object.keys(componentProperties);
127+
let renderedInputKeys = Object.keys(componentInputs);
128+
let renderedOutputKeys = Object.keys(componentOutputs);
126129
const rerender = async (
127130
properties?: Pick<RenderTemplateOptions<SutType>, 'componentProperties' | 'componentInputs' | 'componentOutputs'>,
128131
) => {
129-
await renderFixture(
130-
properties?.componentProperties ?? {},
131-
properties?.componentInputs ?? {},
132-
properties?.componentOutputs ?? {},
133-
);
132+
const newComponentInputs = properties?.componentInputs ?? {};
133+
for (const inputKey of renderedInputKeys) {
134+
if (!Object.prototype.hasOwnProperty.call(newComponentInputs, inputKey)) {
135+
delete (fixture.componentInstance as any)[inputKey];
136+
}
137+
}
138+
setComponentInputs(fixture, newComponentInputs);
139+
renderedInputKeys = Object.keys(newComponentInputs);
140+
141+
const newComponentOutputs = properties?.componentOutputs ?? {};
142+
for (const outputKey of renderedOutputKeys) {
143+
if (!Object.prototype.hasOwnProperty.call(newComponentOutputs, outputKey)) {
144+
delete (fixture.componentInstance as any)[outputKey];
145+
}
146+
}
147+
setComponentOutputs(fixture, newComponentOutputs);
148+
renderedOutputKeys = Object.keys(newComponentOutputs);
149+
150+
const newComponentProps = properties?.componentProperties ?? {};
151+
const changes = updateProps(fixture, renderedPropKeys, newComponentProps);
152+
if (hasOnChangesHook(fixture.componentInstance)) {
153+
fixture.componentInstance.ngOnChanges(changes);
154+
}
155+
renderedPropKeys = Object.keys(newComponentProps);
156+
157+
fixture.componentRef.injector.get(ChangeDetectorRef).detectChanges();
134158
};
135159

136160
const changeInput = (changedInputProperties: Partial<SutType>) => {
@@ -360,6 +384,31 @@ function getChangesObj(oldProps: Record<string, any> | null, newProps: Record<st
360384
);
361385
}
362386

387+
function updateProps<SutType>(
388+
fixture: ComponentFixture<SutType>,
389+
prevRenderedPropsKeys: string[],
390+
newProps: Record<string, any>,
391+
) {
392+
const componentInstance = fixture.componentInstance as Record<string, any>;
393+
const simpleChanges: SimpleChanges = {};
394+
395+
for (const key of prevRenderedPropsKeys) {
396+
if (!Object.prototype.hasOwnProperty.call(newProps, key)) {
397+
simpleChanges[key] = new SimpleChange(componentInstance[key], undefined, false);
398+
delete componentInstance[key];
399+
}
400+
}
401+
402+
for (const [key, value] of Object.entries(newProps)) {
403+
if (value !== componentInstance[key]) {
404+
simpleChanges[key] = new SimpleChange(componentInstance[key], value, false);
405+
}
406+
}
407+
setComponentProperties(fixture, newProps);
408+
409+
return simpleChanges;
410+
}
411+
363412
function addAutoDeclarations<SutType>(
364413
sut: Type<SutType> | string,
365414
{

‎projects/testing-library/tests/rerender.spec.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
import { Component, Input } from '@angular/core';
1+
import { Component, Input,OnChanges,SimpleChanges } from '@angular/core';
22
import { render, screen } from '../src/public_api';
33

4+
let ngOnChangesSpy: jest.Mock;
45
@Component({
56
selector: 'atl-fixture',
67
template: ` {{ firstName }} {{ lastName }} `,
78
})
8-
class FixtureComponent {
9+
class FixtureComponent implementsOnChanges{
910
@Input() firstName = 'Sarah';
1011
@Input() lastName?: string;
12+
ngOnChanges(changes: SimpleChanges): void {
13+
ngOnChangesSpy(changes);
14+
}
1115
}
1216

17+
beforeEach(() => {
18+
ngOnChangesSpy = jest.fn();
19+
});
20+
1321
test('rerenders the component with updated props', async () => {
1422
const { rerender } = await render(FixtureComponent);
1523
expect(screen.getByText('Sarah')).toBeInTheDocument();
@@ -54,6 +62,22 @@ test('rerenders the component with updated props and resets other props', async
5462
const firstName2 = 'Chris';
5563
await rerender({ componentProperties: { firstName: firstName2 } });
5664

65+
expect(screen.getByText(`${firstName2}`)).toBeInTheDocument();
5766
expect(screen.queryByText(`${firstName2} ${lastName}`)).not.toBeInTheDocument();
5867
expect(screen.queryByText(`${firstName} ${lastName}`)).not.toBeInTheDocument();
68+
69+
expect(ngOnChangesSpy).toHaveBeenCalledTimes(2); // one time initially and one time for rerender
70+
const rerenderedChanges = ngOnChangesSpy.mock.calls[1][0] as SimpleChanges;
71+
expect(rerenderedChanges).toEqual({
72+
lastName: {
73+
previousValue: 'Peeters',
74+
currentValue: undefined,
75+
firstChange: false,
76+
},
77+
firstName: {
78+
previousValue: 'Mark',
79+
currentValue: 'Chris',
80+
firstChange: false,
81+
},
82+
});
5983
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /