Skip to content

Commit

Permalink
feat: add partialUpdate to rerender (#427)
Browse files Browse the repository at this point in the history
Closes #411
  • Loading branch information
timdeschryver authored Dec 2, 2023
1 parent a0dfa35 commit d5486f1
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 5 deletions.
2 changes: 1 addition & 1 deletion projects/testing-library/src/lib/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export interface RenderResult<ComponentType, WrapperType = ComponentType> extend
properties?: Pick<
RenderTemplateOptions<ComponentType>,
'componentProperties' | 'componentInputs' | 'componentOutputs' | 'detectChangesOnRender'
>,
> & { partialUpdate?: boolean },
) => Promise<void>;
/**
* @description
Expand Down
21 changes: 17 additions & 4 deletions projects/testing-library/src/lib/testing-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,16 @@ export async function render<SutType, WrapperType = SutType>(
properties?: Pick<
RenderTemplateOptions<SutType>,
'componentProperties' | 'componentInputs' | 'componentOutputs' | 'detectChangesOnRender'
>,
> & { partialUpdate?: boolean },
) => {
const newComponentInputs = properties?.componentInputs ?? {};
const changesInComponentInput = update(fixture, renderedInputKeys, newComponentInputs, setComponentInputs);
const changesInComponentInput = update(
fixture,
renderedInputKeys,
newComponentInputs,
setComponentInputs,
properties?.partialUpdate ?? false,
);
renderedInputKeys = Object.keys(newComponentInputs);

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

const newComponentProps = properties?.componentProperties ?? {};
const changesInComponentProps = update(fixture, renderedPropKeys, newComponentProps, setComponentProperties);
const changesInComponentProps = update(
fixture,
renderedPropKeys,
newComponentProps,
setComponentProperties,
properties?.partialUpdate ?? false,
);
renderedPropKeys = Object.keys(newComponentProps);

if (hasOnChangesHook(fixture.componentInstance)) {
Expand Down Expand Up @@ -387,12 +399,13 @@ function update<SutType>(
fixture: ComponentFixture<SutType>,
values: RenderTemplateOptions<SutType>['componentInputs' | 'componentProperties'],
) => void,
partialUpdate: boolean,
) {
const componentInstance = fixture.componentInstance as Record<string, any>;
const simpleChanges: SimpleChanges = {};

for (const key of prevRenderedKeys) {
if (!Object.prototype.hasOwnProperty.call(newValues, key)) {
if (!partialUpdate && !Object.prototype.hasOwnProperty.call(newValues, key)) {
simpleChanges[key] = new SimpleChange(componentInstance[key], undefined, false);
delete componentInstance[key];
}
Expand Down
58 changes: 58 additions & 0 deletions projects/testing-library/tests/rerender.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,35 @@ test('rerenders the component with updated inputs and resets other props', async
});
});

test('rerenders the component with updated inputs and keeps other props when partial is true', async () => {
const firstName = 'Mark';
const lastName = 'Peeters';
const { rerender } = await render(FixtureComponent, {
componentInputs: {
firstName,
lastName,
},
});

expect(screen.getByText(`${firstName} ${lastName}`)).toBeInTheDocument();

const firstName2 = 'Chris';
await rerender({ componentInputs: { firstName: firstName2 }, partialUpdate: true });

expect(screen.queryByText(firstName)).not.toBeInTheDocument();
expect(screen.getByText(`${firstName2} ${lastName}`)).toBeInTheDocument();

expect(ngOnChangesSpy).toHaveBeenCalledTimes(2); // one time initially and one time for rerender
const rerenderedChanges = ngOnChangesSpy.mock.calls[1][0] as SimpleChanges;
expect(rerenderedChanges).toEqual({
firstName: {
previousValue: 'Mark',
currentValue: 'Chris',
firstChange: false,
},
});
});

test('rerenders the component with updated props and resets other props with componentProperties', async () => {
const firstName = 'Mark';
const lastName = 'Peeters';
Expand Down Expand Up @@ -118,6 +147,35 @@ test('rerenders the component with updated props and resets other props with com
});
});

test('rerenders the component with updated props keeps other props when partial is true', async () => {
const firstName = 'Mark';
const lastName = 'Peeters';
const { rerender } = await render(FixtureComponent, {
componentProperties: {
firstName,
lastName,
},
});

expect(screen.getByText(`${firstName} ${lastName}`)).toBeInTheDocument();

const firstName2 = 'Chris';
await rerender({ componentProperties: { firstName: firstName2 }, partialUpdate: true });

expect(screen.queryByText(firstName)).not.toBeInTheDocument();
expect(screen.getByText(`${firstName2} ${lastName}`)).toBeInTheDocument();

expect(ngOnChangesSpy).toHaveBeenCalledTimes(2); // one time initially and one time for rerender
const rerenderedChanges = ngOnChangesSpy.mock.calls[1][0] as SimpleChanges;
expect(rerenderedChanges).toEqual({
firstName: {
previousValue: 'Mark',
currentValue: 'Chris',
firstChange: false,
},
});
});

test('change detection gets not called if `detectChangesOnRender` is set to false', async () => {
const { rerender } = await render(FixtureComponent);
expect(screen.getByText('Sarah')).toBeInTheDocument();
Expand Down

0 comments on commit d5486f1

Please sign in to comment.