diff --git a/apps/example-app-karma/src/app/issues/issue-222.spec.ts b/apps/example-app-karma/src/app/issues/issue-222.spec.ts index 17e9a02..5da35d4 100644 --- a/apps/example-app-karma/src/app/issues/issue-222.spec.ts +++ b/apps/example-app-karma/src/app/issues/issue-222.spec.ts @@ -13,16 +13,3 @@ it('https://github.com/testing-library/angular-testing-library/issues/222 with r expect(screen.getByText('Hello Mark')).toBeTruthy(); }); - -it('https://github.com/testing-library/angular-testing-library/issues/222 with change', async () => { - const { change } = await render(`
Hello {{ name}}
`, { - componentProperties: { - name: 'Sarah', - }, - }); - - expect(screen.getByText('Hello Sarah')).toBeTruthy(); - await change({ name: 'Mark' }); - - expect(screen.getByText('Hello Mark')).toBeTruthy(); -}); diff --git a/apps/example-app/src/app/examples/16-input-getter-setter.spec.ts b/apps/example-app/src/app/examples/16-input-getter-setter.spec.ts index 53dee01..4382d85 100644 --- a/apps/example-app/src/app/examples/16-input-getter-setter.spec.ts +++ b/apps/example-app/src/app/examples/16-input-getter-setter.spec.ts @@ -10,20 +10,6 @@ test('should run logic in the input setter and getter', async () => { expect(getterValueControl).toHaveTextContent('I am value from getter Angular'); }); -test('should run logic in the input setter and getter while changing', async () => { - const { change } = await render(InputGetterSetter, { componentProperties: { value: 'Angular' } }); - const valueControl = screen.getByTestId('value'); - const getterValueControl = screen.getByTestId('value-getter'); - - expect(valueControl).toHaveTextContent('I am value from setter Angular'); - expect(getterValueControl).toHaveTextContent('I am value from getter Angular'); - - await change({ value: 'React' }); - - expect(valueControl).toHaveTextContent('I am value from setter React'); - expect(getterValueControl).toHaveTextContent('I am value from getter React'); -}); - test('should run logic in the input setter and getter while re-rendering', async () => { const { rerender } = await render(InputGetterSetter, { componentProperties: { value: 'Angular' } }); diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts index b4babe7..0a9b78a 100644 --- a/projects/testing-library/src/lib/models.ts +++ b/projects/testing-library/src/lib/models.ts @@ -63,22 +63,6 @@ export interface RenderResult extend 'componentProperties' | 'componentInputs' | 'componentOutputs' | 'detectChangesOnRender' >, ) => Promise; - /** - * @deprecated - * Use rerender instead. For more info see {@link https://github.com/testing-library/angular-testing-library/issues/365 GitHub Issue} - * - * @description - * Keeps the current fixture intact and invokes ngOnChanges with the updated properties. - */ - change: (changedProperties: Partial) => void; - /** - * @deprecated - * Use rerender instead. For more info see {@link https://github.com/testing-library/angular-testing-library/issues/365 GitHub Issue} - * - * @description - * Keeps the current fixture intact, update the @Input properties and invoke ngOnChanges with the updated properties. - */ - changeInput: (changedInputProperties: Partial) => void; } export interface RenderComponentOptions { diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts index 2d492d6..5c6b0f2 100644 --- a/projects/testing-library/src/lib/testing-library.ts +++ b/projects/testing-library/src/lib/testing-library.ts @@ -162,32 +162,6 @@ export async function render( } }; - const changeInput = (changedInputProperties: Partial) => { - if (Object.keys(changedInputProperties).length === 0) { - return; - } - - setComponentInputs(fixture, changedInputProperties); - - fixture.detectChanges(); - }; - - const change = (changedProperties: Partial) => { - if (Object.keys(changedProperties).length === 0) { - return; - } - - const changes = getChangesObj(fixture.componentInstance as Record, changedProperties); - - setComponentProperties(fixture, changedProperties); - - if (hasOnChangesHook(fixture.componentInstance)) { - fixture.componentInstance.ngOnChanges(changes); - } - - fixture.componentRef.injector.get(ChangeDetectorRef).detectChanges(); - }; - const navigate = async (elementOrPath: Element | string, basePath = ''): Promise => { const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href'); const [path, params] = (basePath + href).split('?'); @@ -234,8 +208,6 @@ export async function render( detectChanges: () => detectChanges(), navigate, rerender, - change, - changeInput, // @ts-ignore: fixture assigned debugElement: fixture.debugElement, // @ts-ignore: fixture assigned diff --git a/projects/testing-library/tests/change.spec.ts b/projects/testing-library/tests/change.spec.ts deleted file mode 100644 index d6d30f4..0000000 --- a/projects/testing-library/tests/change.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; -import { render, screen } from '../src/public_api'; - -@Component({ - selector: 'atl-fixture', - template: ` {{ firstName }} {{ lastName }} `, -}) -class FixtureComponent { - @Input() firstName = 'Sarah'; - @Input() lastName?: string; -} - -test('changes the component with updated props', async () => { - const { change } = await render(FixtureComponent); - expect(screen.getByText('Sarah')).toBeInTheDocument(); - - const firstName = 'Mark'; - change({ firstName }); - - expect(screen.getByText(firstName)).toBeInTheDocument(); -}); - -test('changes the component with updated props while keeping other props untouched', async () => { - const firstName = 'Mark'; - const lastName = 'Peeters'; - const { change } = await render(FixtureComponent, { - componentProperties: { - firstName, - lastName, - }, - }); - - expect(screen.getByText(`${firstName} ${lastName}`)).toBeInTheDocument(); - - const firstName2 = 'Chris'; - change({ firstName: firstName2 }); - - expect(screen.getByText(`${firstName2} ${lastName}`)).toBeInTheDocument(); -}); - -@Component({ - selector: 'atl-fixture', - template: ` {{ propOne }} {{ propTwo }}`, -}) -class FixtureWithNgOnChangesComponent implements OnChanges { - @Input() propOne = 'Init'; - @Input() propTwo = ''; - - // eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method, @typescript-eslint/no-empty-function - ngOnChanges() {} -} - -test('calls ngOnChanges on change', async () => { - const componentInputs = { propOne: 'One', propTwo: 'Two' }; - const { change, fixture } = await render(FixtureWithNgOnChangesComponent, { componentInputs }); - const spy = jest.spyOn(fixture.componentInstance, 'ngOnChanges'); - - expect(screen.getByText(`${componentInputs.propOne} ${componentInputs.propTwo}`)).toBeInTheDocument(); - - const propOne = 'UpdatedOne'; - const propTwo = 'UpdatedTwo'; - change({ propOne, propTwo }); - - expect(spy).toHaveBeenCalledTimes(1); - expect(screen.getByText(`${propOne} ${propTwo}`)).toBeInTheDocument(); -}); - -test('does not invoke ngOnChanges on change without props', async () => { - const componentInputs = { propOne: 'One', propTwo: 'Two' }; - const { change, fixture } = await render(FixtureWithNgOnChangesComponent, { componentInputs }); - const spy = jest.spyOn(fixture.componentInstance, 'ngOnChanges'); - - expect(screen.getByText(`${componentInputs.propOne} ${componentInputs.propTwo}`)).toBeInTheDocument(); - - change({}); - expect(spy).not.toHaveBeenCalled(); - - expect(screen.getByText(`${componentInputs.propOne} ${componentInputs.propTwo}`)).toBeInTheDocument(); -}); -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'atl-fixture', - template: `
Number
`, -}) -class FixtureWithOnPushComponent { - @Input() activeField = ''; -} - -test('update properties on change', async () => { - const { change } = await render(FixtureWithOnPushComponent); - const numberHtmlElementRef = screen.queryByTestId('number'); - - expect(numberHtmlElementRef).not.toHaveClass('active'); - change({ activeField: 'number' }); - expect(numberHtmlElementRef).toHaveClass('active'); -}); diff --git a/projects/testing-library/tests/changeInputs.spec.ts b/projects/testing-library/tests/changeInputs.spec.ts deleted file mode 100644 index 8a97082..0000000 --- a/projects/testing-library/tests/changeInputs.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; -import { render, screen } from '../src/public_api'; - -@Component({ - selector: 'atl-fixture', - template: ` {{ firstName }} {{ lastName }} `, -}) -class FixtureComponent { - @Input() firstName = 'Sarah'; - @Input() lastName?: string; -} - -test('changes the component with updated props', async () => { - const { changeInput } = await render(FixtureComponent); - expect(screen.getByText('Sarah')).toBeInTheDocument(); - - const firstName = 'Mark'; - changeInput({ firstName }); - - expect(screen.getByText(firstName)).toBeInTheDocument(); -}); - -test('changes the component with updated props while keeping other props untouched', async () => { - const firstName = 'Mark'; - const lastName = 'Peeters'; - const { changeInput } = await render(FixtureComponent, { - componentInputs: { - firstName, - lastName, - }, - }); - - expect(screen.getByText(`${firstName} ${lastName}`)).toBeInTheDocument(); - - const firstName2 = 'Chris'; - changeInput({ firstName: firstName2 }); - - expect(screen.getByText(`${firstName2} ${lastName}`)).toBeInTheDocument(); -}); - -@Component({ - selector: 'atl-fixture', - template: ` {{ propOne }} {{ propTwo }}`, -}) -class FixtureWithNgOnChangesComponent implements OnChanges { - @Input() propOne = 'Init'; - @Input() propTwo = ''; - - // eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method, @typescript-eslint/no-empty-function - ngOnChanges() {} -} - -test('calls ngOnChanges on change', async () => { - const componentInputs = { propOne: 'One', propTwo: 'Two' }; - const { changeInput, fixture } = await render(FixtureWithNgOnChangesComponent, { componentInputs }); - const spy = jest.spyOn(fixture.componentInstance, 'ngOnChanges'); - - expect(screen.getByText(`${componentInputs.propOne} ${componentInputs.propTwo}`)).toBeInTheDocument(); - - const propOne = 'UpdatedOne'; - const propTwo = 'UpdatedTwo'; - changeInput({ propOne, propTwo }); - - expect(spy).toHaveBeenCalledTimes(1); - expect(screen.getByText(`${propOne} ${propTwo}`)).toBeInTheDocument(); -}); - -test('does not invoke ngOnChanges on change without props', async () => { - const componentInputs = { propOne: 'One', propTwo: 'Two' }; - const { changeInput, fixture } = await render(FixtureWithNgOnChangesComponent, { componentInputs }); - const spy = jest.spyOn(fixture.componentInstance, 'ngOnChanges'); - - expect(screen.getByText(`${componentInputs.propOne} ${componentInputs.propTwo}`)).toBeInTheDocument(); - - changeInput({}); - expect(spy).not.toHaveBeenCalled(); - - expect(screen.getByText(`${componentInputs.propOne} ${componentInputs.propTwo}`)).toBeInTheDocument(); -}); - -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'atl-fixture', - template: `
Number
`, -}) -class FixtureWithOnPushComponent { - @Input() activeField = ''; -} - -test('update properties on change', async () => { - const { changeInput } = await render(FixtureWithOnPushComponent); - const numberHtmlElementRef = screen.queryByTestId('number'); - - expect(numberHtmlElementRef).not.toHaveClass('active'); - changeInput({ activeField: 'number' }); - expect(numberHtmlElementRef).toHaveClass('active'); -}); diff --git a/projects/testing-library/tests/rerender.spec.ts b/projects/testing-library/tests/rerender.spec.ts index a06beaf..2e5ee4c 100644 --- a/projects/testing-library/tests/rerender.spec.ts +++ b/projects/testing-library/tests/rerender.spec.ts @@ -35,6 +35,7 @@ test('rerenders without props', async () => { await rerender(); expect(screen.getByText('Sarah')).toBeInTheDocument(); + expect(ngOnChangesSpy).toHaveBeenCalledTimes(1); // one time initially and one time for rerender }); test('rerenders the component with updated inputs', async () => { @@ -48,6 +49,41 @@ test('rerenders the component with updated inputs', async () => { }); test('rerenders the component with updated props and resets other props', 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 } }); + + expect(screen.getByText(firstName2)).toBeInTheDocument(); + expect(screen.queryByText(firstName)).not.toBeInTheDocument(); + expect(screen.queryByText(lastName)).not.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({ + lastName: { + previousValue: 'Peeters', + currentValue: undefined, + firstChange: false, + }, + 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'; const { rerender } = await render(FixtureComponent, {