diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts index f148ee2..257bef0 100644 --- a/projects/testing-library/src/lib/models.ts +++ b/projects/testing-library/src/lib/models.ts @@ -61,7 +61,7 @@ export interface RenderResult extend properties?: Pick< RenderTemplateOptions, 'componentProperties' | 'componentInputs' | 'componentOutputs' | 'detectChangesOnRender' - >, + > & { partialUpdate?: boolean }, ) => Promise; /** * @description diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts index 48128cc..1be2f22 100644 --- a/projects/testing-library/src/lib/testing-library.ts +++ b/projects/testing-library/src/lib/testing-library.ts @@ -182,10 +182,16 @@ export async function render( properties?: Pick< RenderTemplateOptions, '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 ?? {}; @@ -198,7 +204,13 @@ export async function render( 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)) { @@ -387,12 +399,13 @@ function update( fixture: ComponentFixture, values: RenderTemplateOptions['componentInputs' | 'componentProperties'], ) => void, + partialUpdate: boolean, ) { const componentInstance = fixture.componentInstance as Record; 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]; } diff --git a/projects/testing-library/tests/rerender.spec.ts b/projects/testing-library/tests/rerender.spec.ts index 9c25257..571d642 100644 --- a/projects/testing-library/tests/rerender.spec.ts +++ b/projects/testing-library/tests/rerender.spec.ts @@ -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'; @@ -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();