From 230c1a13af418b1b9bdbf94144867c093c1bcf63 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 20:21:17 +0200 Subject: [PATCH 01/13] build(lint): re-add eslint-plugins Add it to main apps/example-app/eslint.json and projects/testing-library/eslint.json , since rules apply only to jest test files. (Not to apps/example-app-karma "plugin:jest/style", "plugin:testing-library/angular", "plugin:jest-dom/recommended" --- .eslintrc.json | 7 ------- apps/example-app/.eslintrc.json | 17 +++++++++++++++++ projects/testing-library/.eslintrc.json | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 3e27a3b7..72c48a4c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -102,13 +102,6 @@ ] } }, - { - "files": ["*.spec.ts"], - "extends": ["plugin:jest/recommended"], - "rules": { - "jest/expect-expect": "off" - } - }, { "files": ["*.html"], "rules": {} diff --git a/apps/example-app/.eslintrc.json b/apps/example-app/.eslintrc.json index 9f90c874..b4a49b20 100644 --- a/apps/example-app/.eslintrc.json +++ b/apps/example-app/.eslintrc.json @@ -27,6 +27,23 @@ ] } }, + { + "files": ["*.spec.ts"], + "env": { + "jest": true + }, + "extends": [ + "plugin:jest/recommended", + "plugin:jest/style", + "plugin:testing-library/angular", + "plugin:jest-dom/recommended" + ], + "rules": { + "testing-library/prefer-explicit-assert": "error", + "jest/consistent-test-it": ["error"], + "jest/expect-expect": "off" + } + }, { "files": ["*.html"], "extends": ["plugin:@nrwl/nx/angular-template"], diff --git a/projects/testing-library/.eslintrc.json b/projects/testing-library/.eslintrc.json index 4ad0bed8..b3f0005e 100644 --- a/projects/testing-library/.eslintrc.json +++ b/projects/testing-library/.eslintrc.json @@ -27,6 +27,23 @@ ] } }, + { + "files": ["*.spec.ts"], + "env": { + "jest": true + }, + "extends": [ + "plugin:jest/recommended", + "plugin:jest/style", + "plugin:testing-library/angular", + "plugin:jest-dom/recommended" + ], + "rules": { + "testing-library/prefer-explicit-assert": "error", + "jest/consistent-test-it": ["error"], + "jest/expect-expect": "off" + } + }, { "files": ["*.html"], "extends": ["plugin:@nrwl/nx/angular-template"], From faf5d9639e9b67ae1c004c31f662968dc66e0fc1 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 20:22:24 +0200 Subject: [PATCH 02/13] fix(lint): apply linting autofix --- .../examples/07-with-ngrx-mock-store.spec.ts | 2 +- .../app/examples/12-service-component.spec.ts | 4 ++-- .../testing-library/tests/auto-cleanup.spec.ts | 10 +++++----- projects/testing-library/tests/debug.spec.ts | 6 +++--- .../tests/detect-changes.spec.ts | 6 +++--- projects/testing-library/tests/find-by.spec.ts | 8 ++++---- .../testing-library/tests/navigate.spec.ts | 4 ++-- .../tests/render-template.spec.ts | 4 ++-- projects/testing-library/tests/render.spec.ts | 18 +++++++++--------- .../testing-library/tests/rerender.spec.ts | 4 ++-- .../wait-for-element-to-be-removed.spec.ts | 4 ++-- .../testing-library/tests/wait-for.spec.ts | 4 ++-- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts b/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts index b6b32629..936168e5 100644 --- a/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts +++ b/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts @@ -23,5 +23,5 @@ test('works with provideMockStore', async () => { fireEvent.click(screen.getByText(/seven/i)); - expect(store.dispatch).toBeCalledWith({ type: '[Item List] send', item: 'Seven' }); + expect(store.dispatch).toHaveBeenCalledWith({ type: '[Item List] send', item: 'Seven' }); }); diff --git a/apps/example-app/src/app/examples/12-service-component.spec.ts b/apps/example-app/src/app/examples/12-service-component.spec.ts index dee607e3..a80de740 100644 --- a/apps/example-app/src/app/examples/12-service-component.spec.ts +++ b/apps/example-app/src/app/examples/12-service-component.spec.ts @@ -29,7 +29,7 @@ test('renders the provided customers with manual mock', async () => { }); const listItems = screen.getAllByRole('listitem'); - expect(listItems.length).toBe(customers.length); + expect(listItems).toHaveLength(customers.length); customers.forEach((customer) => screen.getByText(new RegExp(customer.name, 'i'))); }); @@ -59,7 +59,7 @@ test('renders the provided customers with createMock', async () => { }); const listItems = screen.getAllByRole('listitem'); - expect(listItems.length).toBe(customers.length); + expect(listItems).toHaveLength(customers.length); customers.forEach((customer) => screen.getByText(new RegExp(customer.name, 'i'))); }); diff --git a/projects/testing-library/tests/auto-cleanup.spec.ts b/projects/testing-library/tests/auto-cleanup.spec.ts index a22b646c..c69eda62 100644 --- a/projects/testing-library/tests/auto-cleanup.spec.ts +++ b/projects/testing-library/tests/auto-cleanup.spec.ts @@ -10,7 +10,7 @@ class FixtureComponent { } describe('Angular auto clean up - previous components only get cleanup up on init (based on root-id)', () => { - test('first', async () => { + it('first', async () => { await render(FixtureComponent, { componentProperties: { name: 'first', @@ -18,7 +18,7 @@ describe('Angular auto clean up - previous components only get cleanup up on ini }); }); - test('second', async () => { + it('second', async () => { await render(FixtureComponent, { componentProperties: { name: 'second', @@ -29,13 +29,13 @@ describe('Angular auto clean up - previous components only get cleanup up on ini }); describe('ATL auto clean up - after each test the containers get removed', () => { - test('first', async () => { + it('first', async () => { await render(FixtureComponent, { removeAngularAttributes: true, }); }); - test('second', () => { - expect(document.body.innerHTML).toEqual(''); + it('second', () => { + expect(document.body).toBeEmptyDOMElement(); }); }); diff --git a/projects/testing-library/tests/debug.spec.ts b/projects/testing-library/tests/debug.spec.ts index 29845d3b..de414eca 100644 --- a/projects/testing-library/tests/debug.spec.ts +++ b/projects/testing-library/tests/debug.spec.ts @@ -16,7 +16,7 @@ test('debug', async () => { debug(); - expect(console.log).toBeCalledWith(expect.stringContaining('rawr')); + expect(console.log).toHaveBeenCalledWith(expect.stringContaining('rawr')); (console.log).mockRestore(); }); @@ -27,7 +27,7 @@ test('debug allows to be called with an element', async () => { debug(btn); - expect(console.log).not.toBeCalledWith(expect.stringContaining('rawr')); - expect(console.log).toBeCalledWith(expect.stringContaining(`I'm a button`)); + expect(console.log).not.toHaveBeenCalledWith(expect.stringContaining('rawr')); + expect(console.log).toHaveBeenCalledWith(expect.stringContaining(`I'm a button`)); (console.log).mockRestore(); }); diff --git a/projects/testing-library/tests/detect-changes.spec.ts b/projects/testing-library/tests/detect-changes.spec.ts index 5dae8254..8bd2afaa 100644 --- a/projects/testing-library/tests/detect-changes.spec.ts +++ b/projects/testing-library/tests/detect-changes.spec.ts @@ -21,7 +21,7 @@ class FixtureComponent implements OnInit { } describe('detectChanges', () => { - test('does not recognize change if execution is delayed', async () => { + it('does not recognize change if execution is delayed', async () => { const { getByTestId } = await render(FixtureComponent, { imports: [ReactiveFormsModule] }); fireEvent.input(getByTestId('input'), { @@ -32,7 +32,7 @@ describe('detectChanges', () => { expect(getByTestId('button').innerHTML).toBe('Button'); }); - test('exposes detectChanges triggering a change detection cycle', fakeAsync(async () => { + it('exposes detectChanges triggering a change detection cycle', fakeAsync(async () => { const { getByTestId, detectChanges } = await render(FixtureComponent, { imports: [ReactiveFormsModule], }); @@ -49,7 +49,7 @@ describe('detectChanges', () => { expect(getByTestId('button').innerHTML).toBe('Button updated after 400ms'); })); - test('does not throw on a destroyed fixture', async () => { + it('does not throw on a destroyed fixture', async () => { const { getByTestId, fixture } = await render(FixtureComponent, { imports: [ReactiveFormsModule] }); fixture.destroy(); diff --git a/projects/testing-library/tests/find-by.spec.ts b/projects/testing-library/tests/find-by.spec.ts index c55430ee..56063e20 100644 --- a/projects/testing-library/tests/find-by.spec.ts +++ b/projects/testing-library/tests/find-by.spec.ts @@ -12,24 +12,24 @@ class FixtureComponent { } describe('screen', () => { - test('waits for element to be added to the DOM', async () => { + it('waits for element to be added to the DOM', async () => { await render(FixtureComponent); await expect(screen.findByText('I am visible')).resolves.toBeTruthy(); }); - test('rejects when something cannot be found', async () => { + it('rejects when something cannot be found', async () => { await render(FixtureComponent); await expect(screen.findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); }); }); describe('rendered component', () => { - test('waits for element to be added to the DOM', async () => { + it('waits for element to be added to the DOM', async () => { const { findByText } = await render(FixtureComponent); await expect(findByText('I am visible')).resolves.toBeTruthy(); }); - test('rejects when something cannot be found', async () => { + it('rejects when something cannot be found', async () => { const { findByText } = await render(FixtureComponent); await expect(findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); }); diff --git a/projects/testing-library/tests/navigate.spec.ts b/projects/testing-library/tests/navigate.spec.ts index ca0d8ad8..74c2b13d 100644 --- a/projects/testing-library/tests/navigate.spec.ts +++ b/projects/testing-library/tests/navigate.spec.ts @@ -19,7 +19,7 @@ test('should navigate correctly', async () => { navigate('details'); - expect(navSpy).toBeCalledWith(['details']); + expect(navSpy).toHaveBeenCalledWith(['details']); }); test('should pass queryParams if provided', async () => { @@ -32,7 +32,7 @@ test('should pass queryParams if provided', async () => { navigate('details?sortBy=name&sortOrder=asc'); - expect(navSpy).toBeCalledWith(['details'], { + expect(navSpy).toHaveBeenCalledWith(['details'], { queryParams: { sortBy: 'name', sortOrder: 'asc', diff --git a/projects/testing-library/tests/render-template.spec.ts b/projects/testing-library/tests/render-template.spec.ts index 73420211..500de602 100644 --- a/projects/testing-library/tests/render-template.spec.ts +++ b/projects/testing-library/tests/render-template.spec.ts @@ -120,7 +120,7 @@ test('overrides output properties', async () => { }); describe('removeAngularAttributes', () => { - test('should remove angular attributes', async () => { + it('should remove angular attributes', async () => { await render('
', { declarations: [OnOffDirective], removeAngularAttributes: true, @@ -130,7 +130,7 @@ describe('removeAngularAttributes', () => { expect(document.querySelector('[id]')).toBeNull(); }); - test('is disabled by default', async () => { + it('is disabled by default', async () => { await render('
', { declarations: [OnOffDirective], }); diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts index fe2ae565..102ad20f 100644 --- a/projects/testing-library/tests/render.spec.ts +++ b/projects/testing-library/tests/render.spec.ts @@ -30,7 +30,7 @@ test('creates queries and events', async () => { }); describe('removeAngularAttributes', () => { - test('should remove angular attribute', async () => { + it('should remove angular attribute', async () => { await render(FixtureComponent, { removeAngularAttributes: true, }); @@ -39,7 +39,7 @@ describe('removeAngularAttributes', () => { expect(document.querySelector('[id]')).toBeNull(); }); - test('is disabled by default', async () => { + it('is disabled by default', async () => { await render(FixtureComponent, { removeAngularAttributes: false, }); @@ -55,7 +55,7 @@ describe('animationModule', () => { }) class FixtureModule {} describe('excludeComponentDeclaration', () => { - test('does not throw if component is declared in an imported module', async () => { + it('does not throw if component is declared in an imported module', async () => { await render(FixtureComponent, { imports: [FixtureModule], excludeComponentDeclaration: true, @@ -63,13 +63,13 @@ describe('animationModule', () => { }); }); - test('adds NoopAnimationsModule by default', async () => { + it('adds NoopAnimationsModule by default', async () => { await render(FixtureComponent); const noopAnimationsModule = TestBed.inject(NoopAnimationsModule); expect(noopAnimationsModule).toBeDefined(); }); - test('does not add NoopAnimationsModule if BrowserAnimationsModule is an import', async () => { + it('does not add NoopAnimationsModule if BrowserAnimationsModule is an import', async () => { await render(FixtureComponent, { imports: [BrowserAnimationsModule], }); @@ -104,23 +104,23 @@ describe('Angular component life-cycle hooks', () => { } } - test('will call ngOnInit on initial render', async () => { + it('will call ngOnInit on initial render', async () => { const nameInitialized = jest.fn(); const componentProperties = { nameInitialized }; const component = await render(FixtureWithNgOnChangesComponent, { componentProperties }); component.getByText('Initial'); - expect(nameInitialized).toBeCalledWith('Initial'); + expect(nameInitialized).toHaveBeenCalledWith('Initial'); }); - test('will call ngOnChanges on initial render before ngOnInit', async () => { + it('will call ngOnChanges on initial render before ngOnInit', async () => { const nameInitialized = jest.fn(); const nameChanged = jest.fn(); const componentProperties = { nameInitialized, nameChanged, name: 'Sarah' }; const component = await render(FixtureWithNgOnChangesComponent, { componentProperties }); component.getByText('Sarah'); - expect(nameChanged).toBeCalledWith('Sarah', true); + expect(nameChanged).toHaveBeenCalledWith('Sarah', true); // expect `nameChanged` to be called before `nameInitialized` expect(nameChanged.mock.invocationCallOrder[0]).toBeLessThan(nameInitialized.mock.invocationCallOrder[0]); }); diff --git a/projects/testing-library/tests/rerender.spec.ts b/projects/testing-library/tests/rerender.spec.ts index f9f82ca0..6ef880d4 100644 --- a/projects/testing-library/tests/rerender.spec.ts +++ b/projects/testing-library/tests/rerender.spec.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { screen } from '@testing-library/dom'; +import { screen } from '@testing-library/angular'; import { render } from '../src/public_api'; @Component({ @@ -49,7 +49,7 @@ test('will call ngOnChanges on rerender', async () => { }); component.getByText(name); - expect(nameChanged).toBeCalledWith(name, false); + expect(nameChanged).toHaveBeenCalledWith(name, false); }); @Component({ diff --git a/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts b/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts index 0995c8ca..1f2f8eae 100644 --- a/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts +++ b/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts @@ -18,7 +18,7 @@ test('waits for element to be removed (callback)', async () => { await waitForElementToBeRemoved(() => screen.getByTestId('im-here')); - expect(screen.queryByTestId('im-here')).toBeNull(); + expect(screen.queryByTestId('im-here')).not.toBeInTheDocument(); }); test('waits for element to be removed (element)', async () => { @@ -26,7 +26,7 @@ test('waits for element to be removed (element)', async () => { await waitForElementToBeRemoved(screen.getByTestId('im-here')); - expect(screen.queryByTestId('im-here')).toBeNull(); + expect(screen.queryByTestId('im-here')).not.toBeInTheDocument(); }); test('allows to override options', async () => { diff --git a/projects/testing-library/tests/wait-for.spec.ts b/projects/testing-library/tests/wait-for.spec.ts index 116faaea..22b05b1e 100644 --- a/projects/testing-library/tests/wait-for.spec.ts +++ b/projects/testing-library/tests/wait-for.spec.ts @@ -20,11 +20,11 @@ class FixtureComponent { test('waits for assertion to become true', async () => { await render(FixtureComponent); - expect(screen.queryByText('Success')).toBeNull(); + expect(screen.queryByText('Success')).not.toBeInTheDocument(); fireEvent.click(screen.getByTestId('button')); - await waitFor(() => screen.getByText('Success')); + await screen.findByText('Success'); screen.getByText('Success'); }); From 8440fd80cba886e2e09d54a72fb268c92d1cae5d Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 21:12:10 +0200 Subject: [PATCH 03/13] fix(lint): fix linting errors --- .../app/examples/15-dialog.component.spec.ts | 1 + projects/testing-library/tests/debug.spec.ts | 8 ++- .../tests/detect-changes.spec.ts | 20 +++---- .../testing-library/tests/find-by.spec.ts | 8 +-- .../testing-library/tests/fire-event.spec.ts | 8 +-- .../tests/issues/issue-188.spec.ts | 6 +- .../tests/issues/issue-230.spec.ts | 2 +- .../tests/issues/issue-67.spec.ts | 8 +-- .../providers/component-provider.spec.ts | 14 ++--- .../tests/providers/module-provider.spec.ts | 18 +++--- .../tests/render-template.spec.ts | 57 ++++++++++--------- projects/testing-library/tests/render.spec.ts | 19 ++++--- .../testing-library/tests/rerender.spec.ts | 23 +++----- .../testing-library/tests/wait-for.spec.ts | 2 +- 14 files changed, 98 insertions(+), 96 deletions(-) diff --git a/apps/example-app/src/app/examples/15-dialog.component.spec.ts b/apps/example-app/src/app/examples/15-dialog.component.spec.ts index 694af3a7..f0fbd465 100644 --- a/apps/example-app/src/app/examples/15-dialog.component.spec.ts +++ b/apps/example-app/src/app/examples/15-dialog.component.spec.ts @@ -37,6 +37,7 @@ test('closes the dialog via the backdrop', async () => { // using fireEvent because of: // unable to click element as it has or inherits pointer-events set to "none" + // eslint-disable-next-line testing-library/no-node-access fireEvent.click(document.querySelector('.cdk-overlay-backdrop')); await waitForElementToBeRemoved(() => screen.getByRole('dialog')); diff --git a/projects/testing-library/tests/debug.spec.ts b/projects/testing-library/tests/debug.spec.ts index de414eca..e1ad1dff 100644 --- a/projects/testing-library/tests/debug.spec.ts +++ b/projects/testing-library/tests/debug.spec.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { render } from '../src/public_api'; +import { render, screen } from '../src/public_api'; @Component({ selector: 'atl-fixture', @@ -14,6 +14,7 @@ test('debug', async () => { jest.spyOn(console, 'log').mockImplementation(); const { debug } = await render(FixtureComponent); + // eslint-disable-next-line testing-library/no-debug debug(); expect(console.log).toHaveBeenCalledWith(expect.stringContaining('rawr')); @@ -22,9 +23,10 @@ test('debug', async () => { test('debug allows to be called with an element', async () => { jest.spyOn(console, 'log').mockImplementation(); - const { debug, getByTestId } = await render(FixtureComponent); - const btn = getByTestId('btn'); + const { debug } = await render(FixtureComponent); + const btn = screen.getByTestId('btn'); + // eslint-disable-next-line testing-library/no-debug debug(btn); expect(console.log).not.toHaveBeenCalledWith(expect.stringContaining('rawr')); diff --git a/projects/testing-library/tests/detect-changes.spec.ts b/projects/testing-library/tests/detect-changes.spec.ts index 8bd2afaa..766bf31a 100644 --- a/projects/testing-library/tests/detect-changes.spec.ts +++ b/projects/testing-library/tests/detect-changes.spec.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { fakeAsync, tick } from '@angular/core/testing'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { delay } from 'rxjs/operators'; -import { render, fireEvent } from '../src/public_api'; +import { render, fireEvent, screen } from '../src/public_api'; @Component({ selector: 'atl-fixture', @@ -22,22 +22,22 @@ class FixtureComponent implements OnInit { describe('detectChanges', () => { it('does not recognize change if execution is delayed', async () => { - const { getByTestId } = await render(FixtureComponent, { imports: [ReactiveFormsModule] }); + await render(FixtureComponent, { imports: [ReactiveFormsModule] }); - fireEvent.input(getByTestId('input'), { + fireEvent.input(screen.getByTestId('input'), { target: { value: 'What a great day!', }, }); - expect(getByTestId('button').innerHTML).toBe('Button'); + expect(screen.getByTestId('button').innerHTML).toBe('Button'); }); it('exposes detectChanges triggering a change detection cycle', fakeAsync(async () => { - const { getByTestId, detectChanges } = await render(FixtureComponent, { + const { detectChanges } = await render(FixtureComponent, { imports: [ReactiveFormsModule], }); - fireEvent.input(getByTestId('input'), { + fireEvent.input(screen.getByTestId('input'), { target: { value: 'What a great day!', }, @@ -46,19 +46,19 @@ describe('detectChanges', () => { tick(500); detectChanges(); - expect(getByTestId('button').innerHTML).toBe('Button updated after 400ms'); + expect(screen.getByTestId('button').innerHTML).toBe('Button updated after 400ms'); })); it('does not throw on a destroyed fixture', async () => { - const { getByTestId, fixture } = await render(FixtureComponent, { imports: [ReactiveFormsModule] }); + const { fixture } = await render(FixtureComponent, { imports: [ReactiveFormsModule] }); fixture.destroy(); - fireEvent.input(getByTestId('input'), { + fireEvent.input(screen.getByTestId('input'), { target: { value: 'What a great day!', }, }); - expect(getByTestId('button').innerHTML).toBe('Button'); + expect(screen.getByTestId('button').innerHTML).toBe('Button'); }); }); diff --git a/projects/testing-library/tests/find-by.spec.ts b/projects/testing-library/tests/find-by.spec.ts index 56063e20..212a151f 100644 --- a/projects/testing-library/tests/find-by.spec.ts +++ b/projects/testing-library/tests/find-by.spec.ts @@ -25,12 +25,12 @@ describe('screen', () => { describe('rendered component', () => { it('waits for element to be added to the DOM', async () => { - const { findByText } = await render(FixtureComponent); - await expect(findByText('I am visible')).resolves.toBeTruthy(); + await render(FixtureComponent); + await expect(screen.findByText('I am visible')).resolves.toBeTruthy(); }); it('rejects when something cannot be found', async () => { - const { findByText } = await render(FixtureComponent); - await expect(findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); + await render(FixtureComponent); + await expect(screen.findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); }); }); diff --git a/projects/testing-library/tests/fire-event.spec.ts b/projects/testing-library/tests/fire-event.spec.ts index 7a9a7f0d..f204e6bb 100644 --- a/projects/testing-library/tests/fire-event.spec.ts +++ b/projects/testing-library/tests/fire-event.spec.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { render, fireEvent } from '../src/public_api'; +import { render, fireEvent, screen } from '../src/public_api'; @Component({ selector: 'atl-fixture', @@ -8,10 +8,10 @@ import { render, fireEvent } from '../src/public_api'; class FixtureComponent {} test('does not call detect changes when fixture is destroyed', async () => { - const component = await render(FixtureComponent); + const view = await render(FixtureComponent); - component.fixture.destroy(); + view.fixture.destroy(); // should otherwise throw - fireEvent.input(component.getByTestId('input'), { target: { value: 'Bonjour' } }); + fireEvent.input(screen.getByTestId('input'), { target: { value: 'Bonjour' } }); }); diff --git a/projects/testing-library/tests/issues/issue-188.spec.ts b/projects/testing-library/tests/issues/issue-188.spec.ts index 7b3d8263..8077358a 100644 --- a/projects/testing-library/tests/issues/issue-188.spec.ts +++ b/projects/testing-library/tests/issues/issue-188.spec.ts @@ -1,6 +1,6 @@ // https://github.com/testing-library/angular-testing-library/issues/188 import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { render } from '../../src/public_api'; +import { render, screen } from '../../src/public_api'; @Component({ template: `

Hello {{ formattedName }}

`, @@ -18,7 +18,7 @@ class BugOnChangeComponent implements OnChanges { } test('should output formatted name after rendering', async () => { - const { getByText } = await render(BugOnChangeComponent, { componentProperties: { name: 'name' } }); + await render(BugOnChangeComponent, { componentProperties: { name: 'name' } }); - getByText('Hello NAME'); + expect(screen.getByText('Hello NAME')).toBeInTheDocument(); }); diff --git a/projects/testing-library/tests/issues/issue-230.spec.ts b/projects/testing-library/tests/issues/issue-230.spec.ts index c1e5cf35..fe004b62 100644 --- a/projects/testing-library/tests/issues/issue-230.spec.ts +++ b/projects/testing-library/tests/issues/issue-230.spec.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { render, waitFor, screen } from '@testing-library/angular'; +import { render, waitFor, screen } from '../../src/public_api'; @Component({ template: ` `, diff --git a/projects/testing-library/tests/issues/issue-67.spec.ts b/projects/testing-library/tests/issues/issue-67.spec.ts index 642f394d..4f1a2b21 100644 --- a/projects/testing-library/tests/issues/issue-67.spec.ts +++ b/projects/testing-library/tests/issues/issue-67.spec.ts @@ -1,6 +1,6 @@ // https://github.com/testing-library/angular-testing-library/issues/67 import { Component } from '@angular/core'; -import { render } from '../../src/public_api'; +import { render, screen } from '../../src/public_api'; @Component({ template: ` @@ -20,10 +20,10 @@ test('first step to reproduce the bug: skip this test to avoid the error or remo }); test('second step: bug happens :`(', async () => { - const { getByLabelText, getByTestId } = await render(BugGetByLabelTextComponent); + await render(BugGetByLabelTextComponent); - const checkboxByTestId = getByTestId('checkbox'); - const checkboxByLabelTest = getByLabelText('TEST'); + const checkboxByTestId = screen.getByTestId('checkbox'); + const checkboxByLabelTest = screen.getByLabelText('TEST'); expect(checkboxByTestId).toBe(checkboxByLabelTest); }); diff --git a/projects/testing-library/tests/providers/component-provider.spec.ts b/projects/testing-library/tests/providers/component-provider.spec.ts index 723a0317..3c3ec0cf 100644 --- a/projects/testing-library/tests/providers/component-provider.spec.ts +++ b/projects/testing-library/tests/providers/component-provider.spec.ts @@ -1,15 +1,15 @@ import { Injectable } from '@angular/core'; import { Component } from '@angular/core'; -import { render } from '../../src/public_api'; +import { render, screen } from '../../src/public_api'; test('shows the service value', async () => { - const { getByText } = await render(FixtureComponent); + await render(FixtureComponent); - getByText('foo'); + expect(screen.getByText('foo')).toBeInTheDocument(); }); test('shows the provided service value', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { componentProviders: [ { provide: Service, @@ -22,11 +22,11 @@ test('shows the provided service value', async () => { ], }); - getByText('bar'); + expect(screen.getByText('bar')).toBeInTheDocument(); }); test('shows the provided service value with template syntax', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { componentProviders: [ { provide: Service, @@ -39,7 +39,7 @@ test('shows the provided service value with template syntax', async () => { ], }); - getByText('bar'); + expect(screen.getByText('bar')).toBeInTheDocument(); }); @Injectable() diff --git a/projects/testing-library/tests/providers/module-provider.spec.ts b/projects/testing-library/tests/providers/module-provider.spec.ts index 82dbee64..bd39b81b 100644 --- a/projects/testing-library/tests/providers/module-provider.spec.ts +++ b/projects/testing-library/tests/providers/module-provider.spec.ts @@ -1,25 +1,25 @@ import { Injectable } from '@angular/core'; import { Component } from '@angular/core'; -import { render } from '../../src/public_api'; +import { render, screen } from '../../src/public_api'; test('shows the service value', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { providers: [Service], }); - getByText('foo'); + expect(screen.getByText('foo')).toBeInTheDocument(); }); test('shows the service value with template syntax', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { providers: [Service], }); - getByText('foo'); + expect(screen.getByText('foo')).toBeInTheDocument(); }); test('shows the provided service value', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { providers: [ { provide: Service, @@ -32,11 +32,11 @@ test('shows the provided service value', async () => { ], }); - getByText('bar'); + expect(screen.getByText('bar')).toBeInTheDocument(); }); test('shows the provided service value with template syntax', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { providers: [ { provide: Service, @@ -49,7 +49,7 @@ test('shows the provided service value with template syntax', async () => { ], }); - getByText('bar'); + expect(screen.getByText('bar')).toBeInTheDocument(); }); @Injectable() diff --git a/projects/testing-library/tests/render-template.spec.ts b/projects/testing-library/tests/render-template.spec.ts index 500de602..60c2a074 100644 --- a/projects/testing-library/tests/render-template.spec.ts +++ b/projects/testing-library/tests/render-template.spec.ts @@ -1,6 +1,6 @@ import { Directive, HostListener, ElementRef, Input, Output, EventEmitter, Component } from '@angular/core'; -import { render, fireEvent } from '../src/public_api'; +import { render, fireEvent, screen } from '../src/public_api'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector @@ -44,78 +44,81 @@ class GreetingComponent { } test('the directive renders', async () => { - const component = await render('
', { + const view = await render('
', { declarations: [OnOffDirective], }); - expect(component.container.querySelector('[onoff]')).toBeInTheDocument(); + // eslint-disable-next-line testing-library/no-container + expect(view.container.querySelector('[onoff]')).toBeInTheDocument(); }); test('the component renders', async () => { - const component = await render('', { + const view = await render('', { declarations: [GreetingComponent], }); - expect(component.container.querySelector('greeting')).toBeInTheDocument(); - expect(component.getByText('Hello Angular!')).toBeInTheDocument(); + // eslint-disable-next-line testing-library/no-container + expect(view.container.querySelector('greeting')).toBeInTheDocument(); + expect(screen.getByText('Hello Angular!')).toBeInTheDocument(); }); test('the directive renders (compatibility with the deprecated signature)', async () => { - const component = await render(OnOffDirective, { + const view = await render(OnOffDirective, { template: '
', }); - expect(component.container.querySelector('[onoff]')).toBeInTheDocument(); + // eslint-disable-next-line testing-library/no-container + expect(view.container.querySelector('[onoff]')).toBeInTheDocument(); }); test('uses the default props', async () => { - const component = await render('
', { + await render('
', { declarations: [OnOffDirective], }); - fireEvent.click(component.getByText('init')); - fireEvent.click(component.getByText('on')); - fireEvent.click(component.getByText('off')); + fireEvent.click(screen.getByText('init')); + fireEvent.click(screen.getByText('on')); + fireEvent.click(screen.getByText('off')); }); test('overrides input properties', async () => { - const component = await render('
', { + await render('
', { declarations: [OnOffDirective], }); - fireEvent.click(component.getByText('init')); - fireEvent.click(component.getByText('hello')); - fireEvent.click(component.getByText('off')); + fireEvent.click(screen.getByText('init')); + fireEvent.click(screen.getByText('hello')); + fireEvent.click(screen.getByText('off')); }); test('overrides input properties via a wrapper', async () => { // `bar` will be set as a property on the wrapper component, the property will be used to pass to the directive - const component = await render('
', { + await render('
', { declarations: [OnOffDirective], componentProperties: { bar: 'hello', }, }); - fireEvent.click(component.getByText('init')); - fireEvent.click(component.getByText('hello')); - fireEvent.click(component.getByText('off')); + fireEvent.click(screen.getByText('init')); + fireEvent.click(screen.getByText('hello')); + fireEvent.click(screen.getByText('off')); }); test('overrides output properties', async () => { const clicked = jest.fn(); - const component = await render('
', { + await render('
', { declarations: [OnOffDirective], componentProperties: { clicked, }, }); - fireEvent.click(component.getByText('init')); + fireEvent.click(screen.getByText('init')); expect(clicked).toHaveBeenCalledWith('on'); - fireEvent.click(component.getByText('on')); + fireEvent.click(screen.getByText('on')); expect(clicked).toHaveBeenCalledWith('off'); }); @@ -141,14 +144,14 @@ describe('removeAngularAttributes', () => { }); test('updates properties and invokes change detection', async () => { - const component = await render('
', { + const view = await render('
', { declarations: [UpdateInputDirective], componentProperties: { value: 'value1', }, }); - component.getByText('value1'); - component.fixture.componentInstance.value = 'updated value'; - component.getByText('updated value'); + expect(screen.getByText('value1')).toBeInTheDocument(); + view.fixture.componentInstance.value = 'updated value'; + expect(screen.getByText('updated value')).toBeInTheDocument(); }); diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts index 102ad20f..45979918 100644 --- a/projects/testing-library/tests/render.spec.ts +++ b/projects/testing-library/tests/render.spec.ts @@ -10,7 +10,7 @@ import { } from '@angular/core'; import { NoopAnimationsModule, BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TestBed } from '@angular/core/testing'; -import { render, fireEvent } from '../src/public_api'; +import { render, fireEvent, screen } from '../src/public_api'; @Component({ selector: 'atl-fixture', @@ -22,11 +22,11 @@ import { render, fireEvent } from '../src/public_api'; class FixtureComponent {} test('creates queries and events', async () => { - const component = await render(FixtureComponent); + await render(FixtureComponent); - fireEvent.input(component.getByTestId('input'), { target: { value: 'a super awesome input' } }); - component.getByDisplayValue('a super awesome input'); - fireEvent.click(component.getByText('button')); + fireEvent.input(screen.getByTestId('input'), { target: { value: 'a super awesome input' } }); + expect(screen.getByDisplayValue('a super awesome input')).toBeInTheDocument(); + fireEvent.click(screen.getByText('button')); }); describe('removeAngularAttributes', () => { @@ -107,9 +107,9 @@ describe('Angular component life-cycle hooks', () => { it('will call ngOnInit on initial render', async () => { const nameInitialized = jest.fn(); const componentProperties = { nameInitialized }; - const component = await render(FixtureWithNgOnChangesComponent, { componentProperties }); + await render(FixtureWithNgOnChangesComponent, { componentProperties }); - component.getByText('Initial'); + expect(screen.getByText('Initial')).toBeInTheDocument(); expect(nameInitialized).toHaveBeenCalledWith('Initial'); }); @@ -117,9 +117,10 @@ describe('Angular component life-cycle hooks', () => { const nameInitialized = jest.fn(); const nameChanged = jest.fn(); const componentProperties = { nameInitialized, nameChanged, name: 'Sarah' }; - const component = await render(FixtureWithNgOnChangesComponent, { componentProperties }); - component.getByText('Sarah'); + await render(FixtureWithNgOnChangesComponent, { componentProperties }); + + expect(screen.getByText('Sarah')).toBeInTheDocument(); expect(nameChanged).toHaveBeenCalledWith('Sarah', true); // expect `nameChanged` to be called before `nameInitialized` expect(nameChanged.mock.invocationCallOrder[0]).toBeLessThan(nameInitialized.mock.invocationCallOrder[0]); diff --git a/projects/testing-library/tests/rerender.spec.ts b/projects/testing-library/tests/rerender.spec.ts index 6ef880d4..c98bd1bc 100644 --- a/projects/testing-library/tests/rerender.spec.ts +++ b/projects/testing-library/tests/rerender.spec.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { screen } from '@testing-library/angular'; -import { render } from '../src/public_api'; +import { render, screen } from '../src/public_api'; @Component({ selector: 'atl-fixture', @@ -11,15 +10,13 @@ class FixtureComponent { } test('will rerender the component with updated props', async () => { - const component = await render(FixtureComponent); - component.getByText('Sarah'); + const { rerender } = await render(FixtureComponent); + expect(screen.getByText('Sarah')).toBeInTheDocument(); const name = 'Mark'; - component.rerender({ - name, - }); + rerender({ name }); - component.getByText(name); + expect(screen.getByText(name)).toBeInTheDocument(); }); @Component({ @@ -40,15 +37,13 @@ class FixtureWithNgOnChangesComponent implements OnChanges { test('will call ngOnChanges on rerender', async () => { const nameChanged = jest.fn(); const componentProperties = { nameChanged }; - const component = await render(FixtureWithNgOnChangesComponent, { componentProperties }); - component.getByText('Sarah'); + const { rerender } = await render(FixtureWithNgOnChangesComponent, { componentProperties }); + expect(screen.getByText('Sarah')).toBeInTheDocument(); const name = 'Mark'; - component.rerender({ - name, - }); + rerender({ name }); - component.getByText(name); + expect(screen.getByText(name)).toBeInTheDocument(); expect(nameChanged).toHaveBeenCalledWith(name, false); }); diff --git a/projects/testing-library/tests/wait-for.spec.ts b/projects/testing-library/tests/wait-for.spec.ts index 22b05b1e..e963b0c4 100644 --- a/projects/testing-library/tests/wait-for.spec.ts +++ b/projects/testing-library/tests/wait-for.spec.ts @@ -25,7 +25,7 @@ test('waits for assertion to become true', async () => { fireEvent.click(screen.getByTestId('button')); await screen.findByText('Success'); - screen.getByText('Success'); + expect(screen.getByText('Success')).toBeInTheDocument(); }); test('allows to override options', async () => { From c8786e8e2806b0ca9b6842f480d6bbe6b3e8b7a4 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 21:16:34 +0200 Subject: [PATCH 04/13] build(deps): update (es)linting dependencies --- package.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index af3e7ed8..03ae77f1 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ }, "devDependencies": { "@angular-devkit/build-angular": "12.1.0", - "@angular-eslint/eslint-plugin": "~12.0.0", - "@angular-eslint/eslint-plugin-template": "~12.0.0", - "@angular-eslint/template-parser": "~12.0.0", + "@angular-eslint/eslint-plugin": "~12.2.0", + "@angular-eslint/eslint-plugin-template": "~12.2.0", + "@angular-eslint/template-parser": "~12.2.0", "@angular/cli": "12.1.0", "@angular/compiler-cli": "12.1.1", "@angular/language-service": "12.1.1", @@ -67,15 +67,15 @@ "@types/jasmine": "~3.5.0", "@types/jest": "^26.0.23", "@types/node": "14.14.37", - "@typescript-eslint/eslint-plugin": "4.22.0", - "@typescript-eslint/parser": "4.22.0", + "@typescript-eslint/eslint-plugin": "4.28.2", + "@typescript-eslint/parser": "4.28.2", "cpy-cli": "^3.1.1", "eslint": "^7.25.0", - "eslint-config-prettier": "8.2.0", - "eslint-plugin-import": "^2.23.4", - "eslint-plugin-jest": "^24.3.5", - "eslint-plugin-jest-dom": "3.8.0", - "eslint-plugin-testing-library": "^4.0.1", + "eslint-config-prettier": "8.3.0", + "eslint-plugin-import": "2.23.4", + "eslint-plugin-jest": "24.3.6", + "eslint-plugin-jest-dom": "3.9.0", + "eslint-plugin-testing-library": "4.9.0", "husky": "^6.0.0", "jasmine-core": "~3.7.0", "jasmine-spec-reporter": "~5.0.0", From 2ac614b4e80811c0c89dae03f6870c3d3ed307b7 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 21:32:21 +0200 Subject: [PATCH 05/13] chore(example-app): add ScrollingModule to AppModule Needed for the 13-scrolling.component test. Which used @angular/cdk/scrolling --- apps/example-app/src/app/app.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/example-app/src/app/app.module.ts b/apps/example-app/src/app/app.module.ts index 6b5357a9..fb4ccaf7 100644 --- a/apps/example-app/src/app/app.module.ts +++ b/apps/example-app/src/app/app.module.ts @@ -21,6 +21,7 @@ import { ComponentWithProviderComponent } from './examples/05-component-provider import { WithNgRxStoreComponent, reducer } from './examples/06-with-ngrx-store'; import { WithNgRxMockStoreComponent } from './examples/07-with-ngrx-mock-store'; import { MasterComponent, DetailComponent, HiddenDetailComponent } from './examples/09-router'; +import { ScrollingModule } from '@angular/cdk/scrolling'; function reducerItems() { return ['One', 'Two', 'Three']; @@ -53,6 +54,7 @@ function reducerItems() { MatSidenavModule, MatToolbarModule, AppRoutingModule, + ScrollingModule, StoreModule.forRoot({ value: reducer, items: reducerItems, From 47884598ac35992e3300b0b93623cb68212b92d1 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 21:44:32 +0200 Subject: [PATCH 06/13] build(lint): adjust settings for eslint Following the guide https://github.com/testing-library/eslint-plugin-testing-library#run-the-plugin-only-against-test-files --- .eslintrc.json | 9 ++++++++- apps/example-app/.eslintrc.json | 8 +------- projects/jest-utils/.eslintrc.json | 11 +++++++++++ projects/testing-library/.eslintrc.json | 8 +------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 72c48a4c..40a33190 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,7 @@ { "root": true, "ignorePatterns": ["**/*"], - "plugins": ["@nrwl/nx"], + "plugins": ["@nrwl/nx", "testing-library"], "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], @@ -109,6 +109,13 @@ { "files": ["*.ts", "*.js"], "extends": ["prettier"] + }, + { + "files": ["*.spec.ts"], + "extends": ["plugin:testing-library/angular"], + "rules": { + "testing-library/prefer-explicit-assert": "error" + } } ] } diff --git a/apps/example-app/.eslintrc.json b/apps/example-app/.eslintrc.json index b4a49b20..897bee00 100644 --- a/apps/example-app/.eslintrc.json +++ b/apps/example-app/.eslintrc.json @@ -32,14 +32,8 @@ "env": { "jest": true }, - "extends": [ - "plugin:jest/recommended", - "plugin:jest/style", - "plugin:testing-library/angular", - "plugin:jest-dom/recommended" - ], + "extends": ["plugin:jest/recommended", "plugin:jest/style", "plugin:jest-dom/recommended"], "rules": { - "testing-library/prefer-explicit-assert": "error", "jest/consistent-test-it": ["error"], "jest/expect-expect": "off" } diff --git a/projects/jest-utils/.eslintrc.json b/projects/jest-utils/.eslintrc.json index 90906e5b..785bfebc 100644 --- a/projects/jest-utils/.eslintrc.json +++ b/projects/jest-utils/.eslintrc.json @@ -27,6 +27,17 @@ ] } }, + { + "files": ["*.spec.ts"], + "env": { + "jest": true + }, + "extends": ["plugin:jest/recommended", "plugin:jest/style", "plugin:jest-dom/recommended"], + "rules": { + "jest/consistent-test-it": ["error"], + "jest/expect-expect": "off" + } + }, { "files": ["*.html"], "extends": ["plugin:@nrwl/nx/angular-template"], diff --git a/projects/testing-library/.eslintrc.json b/projects/testing-library/.eslintrc.json index b3f0005e..4089aa79 100644 --- a/projects/testing-library/.eslintrc.json +++ b/projects/testing-library/.eslintrc.json @@ -32,14 +32,8 @@ "env": { "jest": true }, - "extends": [ - "plugin:jest/recommended", - "plugin:jest/style", - "plugin:testing-library/angular", - "plugin:jest-dom/recommended" - ], + "extends": ["plugin:jest/recommended", "plugin:jest/style", "plugin:jest-dom/recommended"], "rules": { - "testing-library/prefer-explicit-assert": "error", "jest/consistent-test-it": ["error"], "jest/expect-expect": "off" } From 2ddaacafca767fa587eb844514f22688d2f4f7b9 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 21:56:30 +0200 Subject: [PATCH 07/13] fix(lint): fix additional linting errors --- .../example-app-karma/src/app/issues/issue-222.spec.ts | 4 ++-- projects/jest-utils/tests/create-mock.spec.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) 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 2d9a2900..e92c078a 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 @@ -7,7 +7,7 @@ it('https://github.com/testing-library/angular-testing-library/issues/222', asyn }, }); - screen.getByText('Hello Sarah'); + expect(screen.getByText('Hello Sarah')).toBeTruthy(); rerender({ name: 'Mark' }); - screen.getByText('Hello Mark'); + expect(screen.getByText('Hello Mark')).toBeTruthy(); }); diff --git a/projects/jest-utils/tests/create-mock.spec.ts b/projects/jest-utils/tests/create-mock.spec.ts index 98b94314..085739cd 100644 --- a/projects/jest-utils/tests/create-mock.spec.ts +++ b/projects/jest-utils/tests/create-mock.spec.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { fireEvent, render } from '@testing-library/angular'; +import { fireEvent, render, screen } from '@testing-library/angular'; import { createMock, provideMock, Mock } from '../src/public_api'; @@ -30,22 +30,22 @@ test('mocks all functions', () => { }); test('provides a mock service', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { providers: [provideMock(FixtureService)], }); const service = TestBed.inject(FixtureService); - fireEvent.click(getByText('Print')); + fireEvent.click(screen.getByText('Print')); expect(service.print).toHaveBeenCalledTimes(1); }); test('is possible to write a mock implementation', async () => { - const { getByText } = await render(FixtureComponent, { + await render(FixtureComponent, { providers: [provideMock(FixtureService)], }); const service = TestBed.inject(FixtureService) as Mock; - fireEvent.click(getByText('Print')); + fireEvent.click(screen.getByText('Print')); expect(service.print).toHaveBeenCalled(); }); From 122c6c12cbfe1c307afa32d628ad61cb5820bbb9 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 22:03:33 +0200 Subject: [PATCH 08/13] refactor(fire-event): extract fixture from render Do not use view.fixture --- projects/testing-library/tests/fire-event.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/testing-library/tests/fire-event.spec.ts b/projects/testing-library/tests/fire-event.spec.ts index f204e6bb..ebb85017 100644 --- a/projects/testing-library/tests/fire-event.spec.ts +++ b/projects/testing-library/tests/fire-event.spec.ts @@ -8,9 +8,9 @@ import { render, fireEvent, screen } from '../src/public_api'; class FixtureComponent {} test('does not call detect changes when fixture is destroyed', async () => { - const view = await render(FixtureComponent); + const { fixture } = await render(FixtureComponent); - view.fixture.destroy(); + fixture.destroy(); // should otherwise throw fireEvent.input(screen.getByTestId('input'), { target: { value: 'Bonjour' } }); From 49d43d087a3df25bc36e98130fc90d47dfd6b053 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 22:17:52 +0200 Subject: [PATCH 09/13] build(karma): add jasmine plugins --- apps/example-app-karma/.eslintrc.json | 8 ++++++++ apps/example-app-karma/src/app/issues/issue-222.spec.ts | 1 + apps/example-app-karma/tsconfig.spec.json | 2 +- package.json | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/example-app-karma/.eslintrc.json b/apps/example-app-karma/.eslintrc.json index 57e6cfc7..f1a2cfb5 100644 --- a/apps/example-app-karma/.eslintrc.json +++ b/apps/example-app-karma/.eslintrc.json @@ -27,6 +27,14 @@ ] } }, + { + "files": ["*.spec.ts"], + "env": { + "jasmine": true + }, + "plugins": ["jasmine"], + "extends": ["plugin:jasmine/recommended"] + }, { "files": ["*.html"], "extends": ["plugin:@nrwl/nx/angular-template"], 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 e92c078a..740d8184 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 @@ -9,5 +9,6 @@ it('https://github.com/testing-library/angular-testing-library/issues/222', asyn expect(screen.getByText('Hello Sarah')).toBeTruthy(); rerender({ name: 'Mark' }); + expect(screen.getByText('Hello Mark')).toBeTruthy(); }); diff --git a/apps/example-app-karma/tsconfig.spec.json b/apps/example-app-karma/tsconfig.spec.json index 430cf757..4d73b53d 100644 --- a/apps/example-app-karma/tsconfig.spec.json +++ b/apps/example-app-karma/tsconfig.spec.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", - "types": ["jasmine", "node"] + "types": ["jasmine", "node", "@testing-library/jasmine-dom"] }, "files": ["src/test.ts", "src/polyfills.ts"], "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] diff --git a/package.json b/package.json index 03ae77f1..77734042 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@nrwl/node": "12.5.1", "@nrwl/nx-plugin": "12.5.1", "@nrwl/workspace": "12.5.1", + "@testing-library/jasmine-dom": "^1.2.0", "@testing-library/jest-dom": "^5.11.10", "@types/jasmine": "~3.5.0", "@types/jest": "^26.0.23", @@ -73,6 +74,7 @@ "eslint": "^7.25.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-import": "2.23.4", + "eslint-plugin-jasmine": "^4.1.2", "eslint-plugin-jest": "24.3.6", "eslint-plugin-jest-dom": "3.9.0", "eslint-plugin-testing-library": "4.9.0", From 2197583a395531fd08bdafe465508bfbe852f70e Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Tue, 6 Jul 2021 22:26:20 +0200 Subject: [PATCH 10/13] fix(lint): include all files from example-app-karma --- apps/example-app-karma/tsconfig.editor.json | 7 +++++++ apps/example-app-karma/tsconfig.spec.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 apps/example-app-karma/tsconfig.editor.json diff --git a/apps/example-app-karma/tsconfig.editor.json b/apps/example-app-karma/tsconfig.editor.json new file mode 100644 index 00000000..26575d80 --- /dev/null +++ b/apps/example-app-karma/tsconfig.editor.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "types": ["jasmine", "node", "@testing-library/jasmine-dom"] + } +} diff --git a/apps/example-app-karma/tsconfig.spec.json b/apps/example-app-karma/tsconfig.spec.json index 4d73b53d..f4b0d715 100644 --- a/apps/example-app-karma/tsconfig.spec.json +++ b/apps/example-app-karma/tsconfig.spec.json @@ -5,5 +5,5 @@ "types": ["jasmine", "node", "@testing-library/jasmine-dom"] }, "files": ["src/test.ts", "src/polyfills.ts"], - "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] + "include": ["**/*.spec.ts", "**/*.d.ts"] } From 41bb00fd88eab6d3bb9a8d29342d02fa891bcecb Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Wed, 7 Jul 2021 08:32:18 +0200 Subject: [PATCH 11/13] refactor(find-by-text): use utility functions instead of screen --- projects/testing-library/tests/find-by.spec.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/projects/testing-library/tests/find-by.spec.ts b/projects/testing-library/tests/find-by.spec.ts index 212a151f..962a8359 100644 --- a/projects/testing-library/tests/find-by.spec.ts +++ b/projects/testing-library/tests/find-by.spec.ts @@ -25,12 +25,16 @@ describe('screen', () => { describe('rendered component', () => { it('waits for element to be added to the DOM', async () => { - await render(FixtureComponent); - await expect(screen.findByText('I am visible')).resolves.toBeTruthy(); + const { findByText } = await render(FixtureComponent); + /// We whish to test the utility function from `render` here. + // eslint-disable-next-line testing-library/prefer-screen-queries + await expect(findByText('I am visible')).resolves.toBeTruthy(); }); it('rejects when something cannot be found', async () => { - await render(FixtureComponent); - await expect(screen.findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); + const { findByText } = await render(FixtureComponent); + /// We whish to test the utility function from `render` here. + // eslint-disable-next-line testing-library/prefer-screen-queries + await expect(findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); }); }); From d74e306587c51d2bd83bfb3201b55dd3bcd0e055 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Wed, 7 Jul 2021 08:36:20 +0200 Subject: [PATCH 12/13] refactor(render): use utility functions instead of screen --- projects/testing-library/tests/render.spec.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts index 45979918..c8200fae 100644 --- a/projects/testing-library/tests/render.spec.ts +++ b/projects/testing-library/tests/render.spec.ts @@ -22,11 +22,15 @@ import { render, fireEvent, screen } from '../src/public_api'; class FixtureComponent {} test('creates queries and events', async () => { - await render(FixtureComponent); - - fireEvent.input(screen.getByTestId('input'), { target: { value: 'a super awesome input' } }); - expect(screen.getByDisplayValue('a super awesome input')).toBeInTheDocument(); - fireEvent.click(screen.getByText('button')); + const view = await render(FixtureComponent); + + /// We whish to test the utility function from `render` here. + // eslint-disable-next-line testing-library/prefer-screen-queries + fireEvent.input(view.getByTestId('input'), { target: { value: 'a super awesome input' } }); + // eslint-disable-next-line testing-library/prefer-screen-queries + expect(view.getByDisplayValue('a super awesome input')).toBeInTheDocument(); + // eslint-disable-next-line testing-library/prefer-screen-queries + fireEvent.click(view.getByText('button')); }); describe('removeAngularAttributes', () => { @@ -107,9 +111,11 @@ describe('Angular component life-cycle hooks', () => { it('will call ngOnInit on initial render', async () => { const nameInitialized = jest.fn(); const componentProperties = { nameInitialized }; - await render(FixtureWithNgOnChangesComponent, { componentProperties }); + const view = await render(FixtureWithNgOnChangesComponent, { componentProperties }); - expect(screen.getByText('Initial')).toBeInTheDocument(); + /// We whish to test the utility function from `render` here. + // eslint-disable-next-line testing-library/prefer-screen-queries + expect(view.getByText('Initial')).toBeInTheDocument(); expect(nameInitialized).toHaveBeenCalledWith('Initial'); }); @@ -118,11 +124,13 @@ describe('Angular component life-cycle hooks', () => { const nameChanged = jest.fn(); const componentProperties = { nameInitialized, nameChanged, name: 'Sarah' }; - await render(FixtureWithNgOnChangesComponent, { componentProperties }); + const view = await render(FixtureWithNgOnChangesComponent, { componentProperties }); - expect(screen.getByText('Sarah')).toBeInTheDocument(); + /// We whish to test the utility function from `render` here. + // eslint-disable-next-line testing-library/prefer-screen-queries + expect(view.getByText('Sarah')).toBeInTheDocument(); expect(nameChanged).toHaveBeenCalledWith('Sarah', true); - // expect `nameChanged` to be called before `nameInitialized` + /// expect `nameChanged` to be called before `nameInitialized` expect(nameChanged.mock.invocationCallOrder[0]).toBeLessThan(nameInitialized.mock.invocationCallOrder[0]); }); }); From 3100e1562a0e6544afd90c2f8934ba04d5b2c4ee Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Wed, 7 Jul 2021 18:31:46 +0200 Subject: [PATCH 13/13] Apply suggestions from code review --- projects/testing-library/tests/find-by.spec.ts | 4 ++-- projects/testing-library/tests/render.spec.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/testing-library/tests/find-by.spec.ts b/projects/testing-library/tests/find-by.spec.ts index 962a8359..9d499fda 100644 --- a/projects/testing-library/tests/find-by.spec.ts +++ b/projects/testing-library/tests/find-by.spec.ts @@ -26,14 +26,14 @@ describe('screen', () => { describe('rendered component', () => { it('waits for element to be added to the DOM', async () => { const { findByText } = await render(FixtureComponent); - /// We whish to test the utility function from `render` here. + /// We wish to test the utility function from `render` here. // eslint-disable-next-line testing-library/prefer-screen-queries await expect(findByText('I am visible')).resolves.toBeTruthy(); }); it('rejects when something cannot be found', async () => { const { findByText } = await render(FixtureComponent); - /// We whish to test the utility function from `render` here. + /// We wish to test the utility function from `render` here. // eslint-disable-next-line testing-library/prefer-screen-queries await expect(findByText('I am invisible', {}, { timeout: 40 })).rejects.toThrow('x'); }); diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts index c8200fae..678da79c 100644 --- a/projects/testing-library/tests/render.spec.ts +++ b/projects/testing-library/tests/render.spec.ts @@ -24,7 +24,7 @@ class FixtureComponent {} test('creates queries and events', async () => { const view = await render(FixtureComponent); - /// We whish to test the utility function from `render` here. + /// We wish to test the utility function from `render` here. // eslint-disable-next-line testing-library/prefer-screen-queries fireEvent.input(view.getByTestId('input'), { target: { value: 'a super awesome input' } }); // eslint-disable-next-line testing-library/prefer-screen-queries @@ -113,7 +113,7 @@ describe('Angular component life-cycle hooks', () => { const componentProperties = { nameInitialized }; const view = await render(FixtureWithNgOnChangesComponent, { componentProperties }); - /// We whish to test the utility function from `render` here. + /// We wish to test the utility function from `render` here. // eslint-disable-next-line testing-library/prefer-screen-queries expect(view.getByText('Initial')).toBeInTheDocument(); expect(nameInitialized).toHaveBeenCalledWith('Initial'); @@ -126,7 +126,7 @@ describe('Angular component life-cycle hooks', () => { const view = await render(FixtureWithNgOnChangesComponent, { componentProperties }); - /// We whish to test the utility function from `render` here. + /// We wish to test the utility function from `render` here. // eslint-disable-next-line testing-library/prefer-screen-queries expect(view.getByText('Sarah')).toBeInTheDocument(); expect(nameChanged).toHaveBeenCalledWith('Sarah', true);