Skip to content

Commit d5486f1

Browse files
feat: add partialUpdate to rerender (#427)
Closes #411
1 parent a0dfa35 commit d5486f1

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed

Diff for: projects/testing-library/src/lib/models.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export interface RenderResult<ComponentType, WrapperType = ComponentType> extend
6161
properties?: Pick<
6262
RenderTemplateOptions<ComponentType>,
6363
'componentProperties' | 'componentInputs' | 'componentOutputs' | 'detectChangesOnRender'
64-
>,
64+
> & { partialUpdate?: boolean },
6565
) => Promise<void>;
6666
/**
6767
* @description

Diff for: projects/testing-library/src/lib/testing-library.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,16 @@ export async function render<SutType, WrapperType = SutType>(
182182
properties?: Pick<
183183
RenderTemplateOptions<SutType>,
184184
'componentProperties' | 'componentInputs' | 'componentOutputs' | 'detectChangesOnRender'
185-
>,
185+
> & { partialUpdate?: boolean },
186186
) => {
187187
const newComponentInputs = properties?.componentInputs ?? {};
188-
const changesInComponentInput = update(fixture, renderedInputKeys, newComponentInputs, setComponentInputs);
188+
const changesInComponentInput = update(
189+
fixture,
190+
renderedInputKeys,
191+
newComponentInputs,
192+
setComponentInputs,
193+
properties?.partialUpdate ?? false,
194+
);
189195
renderedInputKeys = Object.keys(newComponentInputs);
190196

191197
const newComponentOutputs = properties?.componentOutputs ?? {};
@@ -198,7 +204,13 @@ export async function render<SutType, WrapperType = SutType>(
198204
renderedOutputKeys = Object.keys(newComponentOutputs);
199205

200206
const newComponentProps = properties?.componentProperties ?? {};
201-
const changesInComponentProps = update(fixture, renderedPropKeys, newComponentProps, setComponentProperties);
207+
const changesInComponentProps = update(
208+
fixture,
209+
renderedPropKeys,
210+
newComponentProps,
211+
setComponentProperties,
212+
properties?.partialUpdate ?? false,
213+
);
202214
renderedPropKeys = Object.keys(newComponentProps);
203215

204216
if (hasOnChangesHook(fixture.componentInstance)) {
@@ -387,12 +399,13 @@ function update<SutType>(
387399
fixture: ComponentFixture<SutType>,
388400
values: RenderTemplateOptions<SutType>['componentInputs' | 'componentProperties'],
389401
) => void,
402+
partialUpdate: boolean,
390403
) {
391404
const componentInstance = fixture.componentInstance as Record<string, any>;
392405
const simpleChanges: SimpleChanges = {};
393406

394407
for (const key of prevRenderedKeys) {
395-
if (!Object.prototype.hasOwnProperty.call(newValues, key)) {
408+
if (!partialUpdate && !Object.prototype.hasOwnProperty.call(newValues, key)) {
396409
simpleChanges[key] = new SimpleChange(componentInstance[key], undefined, false);
397410
delete componentInstance[key];
398411
}

Diff for: projects/testing-library/tests/rerender.spec.ts

+58
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,35 @@ test('rerenders the component with updated inputs and resets other props', async
8383
});
8484
});
8585

86+
test('rerenders the component with updated inputs and keeps other props when partial is true', async () => {
87+
const firstName = 'Mark';
88+
const lastName = 'Peeters';
89+
const { rerender } = await render(FixtureComponent, {
90+
componentInputs: {
91+
firstName,
92+
lastName,
93+
},
94+
});
95+
96+
expect(screen.getByText(`${firstName} ${lastName}`)).toBeInTheDocument();
97+
98+
const firstName2 = 'Chris';
99+
await rerender({ componentInputs: { firstName: firstName2 }, partialUpdate: true });
100+
101+
expect(screen.queryByText(firstName)).not.toBeInTheDocument();
102+
expect(screen.getByText(`${firstName2} ${lastName}`)).toBeInTheDocument();
103+
104+
expect(ngOnChangesSpy).toHaveBeenCalledTimes(2); // one time initially and one time for rerender
105+
const rerenderedChanges = ngOnChangesSpy.mock.calls[1][0] as SimpleChanges;
106+
expect(rerenderedChanges).toEqual({
107+
firstName: {
108+
previousValue: 'Mark',
109+
currentValue: 'Chris',
110+
firstChange: false,
111+
},
112+
});
113+
});
114+
86115
test('rerenders the component with updated props and resets other props with componentProperties', async () => {
87116
const firstName = 'Mark';
88117
const lastName = 'Peeters';
@@ -118,6 +147,35 @@ test('rerenders the component with updated props and resets other props with com
118147
});
119148
});
120149

150+
test('rerenders the component with updated props keeps other props when partial is true', async () => {
151+
const firstName = 'Mark';
152+
const lastName = 'Peeters';
153+
const { rerender } = await render(FixtureComponent, {
154+
componentProperties: {
155+
firstName,
156+
lastName,
157+
},
158+
});
159+
160+
expect(screen.getByText(`${firstName} ${lastName}`)).toBeInTheDocument();
161+
162+
const firstName2 = 'Chris';
163+
await rerender({ componentProperties: { firstName: firstName2 }, partialUpdate: true });
164+
165+
expect(screen.queryByText(firstName)).not.toBeInTheDocument();
166+
expect(screen.getByText(`${firstName2} ${lastName}`)).toBeInTheDocument();
167+
168+
expect(ngOnChangesSpy).toHaveBeenCalledTimes(2); // one time initially and one time for rerender
169+
const rerenderedChanges = ngOnChangesSpy.mock.calls[1][0] as SimpleChanges;
170+
expect(rerenderedChanges).toEqual({
171+
firstName: {
172+
previousValue: 'Mark',
173+
currentValue: 'Chris',
174+
firstChange: false,
175+
},
176+
});
177+
});
178+
121179
test('change detection gets not called if `detectChangesOnRender` is set to false', async () => {
122180
const { rerender } = await render(FixtureComponent);
123181
expect(screen.getByText('Sarah')).toBeInTheDocument();

0 commit comments

Comments
 (0)