From bfa8d069057946e7dca0a8291e59e69c143eebe4 Mon Sep 17 00:00:00 2001 From: Arjen Date: Thu, 15 Apr 2021 19:24:59 +0200 Subject: [PATCH 1/4] build(eslint): add eslint-plugin-testing-library --- .eslintrc.json | 11 +++++++++-- apps/example-app/tsconfig.spec.json | 2 +- package.json | 4 +++- projects/jest-utils/tsconfig.spec.json | 2 +- projects/testing-library/tsconfig.spec.json | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 5af8c266..b740f815 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -36,6 +36,7 @@ }, { "files": ["*.ts"], + "plugins": ["eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint"], "rules": { "@typescript-eslint/consistent-type-definitions": "error", "@typescript-eslint/dot-notation": "off", @@ -102,8 +103,14 @@ "markers": ["/"] } ] - }, - "plugins": ["eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint"] + } + }, + { + "files": ["*.spec.ts"], + "extends": ["plugin:testing-library/angular", "plugin:jest-dom/recommended"], + "rules": { + "testing-library/prefer-explicit-assert": "error" + } }, { "files": ["*.html"], diff --git a/apps/example-app/tsconfig.spec.json b/apps/example-app/tsconfig.spec.json index cfff29a5..afe056ed 100644 --- a/apps/example-app/tsconfig.spec.json +++ b/apps/example-app/tsconfig.spec.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "../../dist/out-tsc", "module": "commonjs", - "types": ["jest", "node"] + "types": ["jest", "node", "@testing-library/jest-dom"] }, "files": ["src/test-setup.ts"], "include": ["**/*.spec.ts", "**/*.d.ts"] diff --git a/package.json b/package.json index eafa5772..0c75c278 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@nrwl/node": "12.0.3", "@nrwl/nx-plugin": "12.0.3", "@nrwl/workspace": "12.0.3", - "@testing-library/jest-dom": "^5.11.0", + "@testing-library/jest-dom": "^5.11.10", "@types/jest": "~26.0.3", "@types/node": "14.14.37", "@typescript-eslint/eslint-plugin": "4.22.0", @@ -70,6 +70,8 @@ "eslint": "7.24.0", "eslint-config-prettier": "8.2.0", "eslint-plugin-import": "latest", + "eslint-plugin-jest-dom": "3.8.0", + "eslint-plugin-testing-library": "^4.0.1", "husky": "^4.2.5", "jest": "^26.1.0", "jest-preset-angular": "8.4.0", diff --git a/projects/jest-utils/tsconfig.spec.json b/projects/jest-utils/tsconfig.spec.json index ba3de654..091c5cf3 100644 --- a/projects/jest-utils/tsconfig.spec.json +++ b/projects/jest-utils/tsconfig.spec.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "module": "commonjs", - "types": ["node", "jest"] + "types": ["node", "jest", "@testing-library/jest-dom"] }, "files": ["test-setup.ts"], "include": ["**/*.spec.ts", "**/*.d.ts"] diff --git a/projects/testing-library/tsconfig.spec.json b/projects/testing-library/tsconfig.spec.json index ba3de654..091c5cf3 100644 --- a/projects/testing-library/tsconfig.spec.json +++ b/projects/testing-library/tsconfig.spec.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "module": "commonjs", - "types": ["node", "jest"] + "types": ["node", "jest", "@testing-library/jest-dom"] }, "files": ["test-setup.ts"], "include": ["**/*.spec.ts", "**/*.d.ts"] From d7e91921d002e94740617dc50a54585ec204a5cd Mon Sep 17 00:00:00 2001 From: Arjen Date: Thu, 15 Apr 2021 19:25:44 +0200 Subject: [PATCH 2/4] fix(eslint): autofix eslint-testing-library --- .../src/app/examples/00-single-component.spec.ts | 6 +++--- .../src/app/examples/01-nested-component.spec.ts | 6 +++--- .../src/app/examples/02-input-output.spec.ts | 8 ++++---- .../src/app/examples/05-component-provider.spec.ts | 12 ++++++------ .../src/app/examples/06-with-ngrx-store.spec.ts | 6 +++--- .../src/app/examples/16-input-getter-setter.spec.ts | 12 ++++++------ apps/example-app/src/app/issues/issue-106.spec.ts | 6 +++--- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/example-app/src/app/examples/00-single-component.spec.ts b/apps/example-app/src/app/examples/00-single-component.spec.ts index 460cae16..73e429bb 100644 --- a/apps/example-app/src/app/examples/00-single-component.spec.ts +++ b/apps/example-app/src/app/examples/00-single-component.spec.ts @@ -9,12 +9,12 @@ test('renders the current value and can increment and decrement', async () => { const decrementControl = screen.getByRole('button', { name: /decrement/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('0'); + expect(valueControl).toHaveTextContent('0'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('2'); + expect(valueControl).toHaveTextContent('2'); fireEvent.click(decrementControl); - expect(valueControl.textContent).toBe('1'); + expect(valueControl).toHaveTextContent('1'); }); diff --git a/apps/example-app/src/app/examples/01-nested-component.spec.ts b/apps/example-app/src/app/examples/01-nested-component.spec.ts index bbaf9c37..8f3a242d 100644 --- a/apps/example-app/src/app/examples/01-nested-component.spec.ts +++ b/apps/example-app/src/app/examples/01-nested-component.spec.ts @@ -11,12 +11,12 @@ test('renders the current value and can increment and decrement', async () => { const decrementControl = screen.getByRole('button', { name: /decrement/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('0'); + expect(valueControl).toHaveTextContent('0'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('2'); + expect(valueControl).toHaveTextContent('2'); fireEvent.click(decrementControl); - expect(valueControl.textContent).toBe('1'); + expect(valueControl).toHaveTextContent('1'); }); diff --git a/apps/example-app/src/app/examples/02-input-output.spec.ts b/apps/example-app/src/app/examples/02-input-output.spec.ts index 7c907526..663d63e0 100644 --- a/apps/example-app/src/app/examples/02-input-output.spec.ts +++ b/apps/example-app/src/app/examples/02-input-output.spec.ts @@ -18,12 +18,12 @@ test('is possible to set input and listen for output', async () => { const sendControl = screen.getByRole('button', { name: /send/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('47'); + expect(valueControl).toHaveTextContent('47'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('50'); + expect(valueControl).toHaveTextContent('50'); fireEvent.click(sendControl); expect(sendValue).toHaveBeenCalledTimes(1); @@ -44,12 +44,12 @@ test('is possible to set input and listen for output with the template syntax', const sendControl = screen.getByRole('button', { name: /send/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('47'); + expect(valueControl).toHaveTextContent('47'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('50'); + expect(valueControl).toHaveTextContent('50'); fireEvent.click(sendControl); expect(sendSpy).toHaveBeenCalledTimes(1); diff --git a/apps/example-app/src/app/examples/05-component-provider.spec.ts b/apps/example-app/src/app/examples/05-component-provider.spec.ts index 861eae76..79811245 100644 --- a/apps/example-app/src/app/examples/05-component-provider.spec.ts +++ b/apps/example-app/src/app/examples/05-component-provider.spec.ts @@ -18,14 +18,14 @@ test('renders the current value and can increment and decrement', async () => { const decrementControl = screen.getByRole('button', { name: /decrement/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('0'); + expect(valueControl).toHaveTextContent('0'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('2'); + expect(valueControl).toHaveTextContent('2'); fireEvent.click(decrementControl); - expect(valueControl.textContent).toBe('1'); + expect(valueControl).toHaveTextContent('1'); }); test('renders the current value and can increment and decrement with a mocked jest-utils service', async () => { @@ -48,14 +48,14 @@ test('renders the current value and can increment and decrement with a mocked je const decrementControl = screen.getByRole('button', { name: /decrement/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('50'); + expect(valueControl).toHaveTextContent('50'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('70'); + expect(valueControl).toHaveTextContent('70'); fireEvent.click(decrementControl); - expect(valueControl.textContent).toBe('60'); + expect(valueControl).toHaveTextContent('60'); }); test('renders the current value and can increment and decrement with provideMocked from jest-utils', async () => { diff --git a/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts b/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts index d8a80d87..b8a289bb 100644 --- a/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts +++ b/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts @@ -21,12 +21,12 @@ test('works with ngrx store', async () => { const decrementControl = screen.getByRole('button', { name: /decrement/i }); const valueControl = screen.getByTestId('value'); - expect(valueControl.textContent).toBe('0'); + expect(valueControl).toHaveTextContent('0'); fireEvent.click(incrementControl); fireEvent.click(incrementControl); - expect(valueControl.textContent).toBe('20'); + expect(valueControl).toHaveTextContent('20'); fireEvent.click(decrementControl); - expect(valueControl.textContent).toBe('10'); + expect(valueControl).toHaveTextContent('10'); }); 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 10e0e276..b43034fe 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 @@ -6,8 +6,8 @@ test('should run logic in the input setter and getter', async () => { const valueControl = screen.getByTestId('value'); const getterValueControl = screen.getByTestId('value-getter'); - expect(valueControl.textContent).toBe('I am value from setter Angular'); - expect(getterValueControl.textContent).toBe('I am value from getter Angular'); + expect(valueControl).toHaveTextContent('I am value from setter Angular'); + expect(getterValueControl).toHaveTextContent('I am value from getter Angular'); }); test('should run logic in the input setter and getter while re-rendering', async () => { @@ -15,11 +15,11 @@ test('should run logic in the input setter and getter while re-rendering', async const valueControl = screen.getByTestId('value'); const getterValueControl = screen.getByTestId('value-getter'); - expect(valueControl.textContent).toBe('I am value from setter Angular'); - expect(getterValueControl.textContent).toBe('I am value from getter Angular'); + expect(valueControl).toHaveTextContent('I am value from setter Angular'); + expect(getterValueControl).toHaveTextContent('I am value from getter Angular'); await component.rerender({ value: 'React' }); - expect(valueControl.textContent).toBe('I am value from setter React'); - expect(getterValueControl.textContent).toBe('I am value from getter React'); + expect(valueControl).toHaveTextContent('I am value from setter React'); + expect(getterValueControl).toHaveTextContent('I am value from getter React'); }); diff --git a/apps/example-app/src/app/issues/issue-106.spec.ts b/apps/example-app/src/app/issues/issue-106.spec.ts index 54dd1cb2..574f8ed7 100644 --- a/apps/example-app/src/app/issues/issue-106.spec.ts +++ b/apps/example-app/src/app/issues/issue-106.spec.ts @@ -20,14 +20,14 @@ it('https://github.com/testing-library/angular-testing-library/issues/106', asyn const toggle = screen.getByTestId('toggle'); const hiddenText = screen.queryByTestId('getme'); - expect(hiddenText).toBeNull(); + expect(hiddenText).not.toBeInTheDocument(); fireEvent.click(toggle); // fails // await waitFor(() => expect(hiddenText).not.toBeNull()); // succeeds - await waitFor(() => expect(screen.queryByTestId('getme')).not.toBeNull()); + await waitFor(() => expect(screen.queryByTestId('getme')).toBeInTheDocument()); }); it('better https://github.com/testing-library/angular-testing-library/issues/106', async () => { @@ -35,7 +35,7 @@ it('better https://github.com/testing-library/angular-testing-library/issues/106 const toggle = screen.getByTestId('toggle'); const hiddenText = screen.queryByTestId('getme'); - expect(hiddenText).toBeNull(); + expect(hiddenText).not.toBeInTheDocument(); fireEvent.click(toggle); screen.getByTestId('getme'); From a4b8546db02cc024114455549db781465ff50e3e Mon Sep 17 00:00:00 2001 From: Arjen Date: Thu, 15 Apr 2021 19:40:57 +0200 Subject: [PATCH 3/4] fix(example-app): fix linting errors --- .../src/app/examples/16-input-getter-setter.spec.ts | 4 ++-- apps/example-app/src/app/issues/issue-106.spec.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 b43034fe..28d736c6 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 @@ -11,14 +11,14 @@ test('should run logic in the input setter and getter', async () => { }); test('should run logic in the input setter and getter while re-rendering', async () => { - const component = await render(InputGetterSetter, { componentProperties: { value: 'Angular' } }); + const { rerender } = 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 component.rerender({ value: 'React' }); + await rerender({ value: 'React' }); expect(valueControl).toHaveTextContent('I am value from setter React'); expect(getterValueControl).toHaveTextContent('I am value from getter React'); diff --git a/apps/example-app/src/app/issues/issue-106.spec.ts b/apps/example-app/src/app/issues/issue-106.spec.ts index 574f8ed7..52bc81e1 100644 --- a/apps/example-app/src/app/issues/issue-106.spec.ts +++ b/apps/example-app/src/app/issues/issue-106.spec.ts @@ -38,5 +38,5 @@ it('better https://github.com/testing-library/angular-testing-library/issues/106 expect(hiddenText).not.toBeInTheDocument(); fireEvent.click(toggle); - screen.getByTestId('getme'); + expect(screen.getByTestId('getme')).toBeInTheDocument(); }); From 058bc9bee8806ccfe46a0994445bf26c8eff22b8 Mon Sep 17 00:00:00 2001 From: "arjen.althoff" Date: Fri, 16 Apr 2021 11:06:52 +0200 Subject: [PATCH 4/4] fix(lint): disable eslint-rule for document.querySelector > there isn't an "accessible" way to click on the backdrop. --- apps/example-app/src/app/examples/15-dialog.component.spec.ts | 1 + 1 file changed, 1 insertion(+) 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 f5849abc..bcb41875 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 @@ -54,6 +54,7 @@ test('closes the dialog via the backdrop', async () => { await screen.findByRole('dialog'); await screen.findByRole('heading', { name: /dialog title/i }); + // eslint-disable-next-line testing-library/no-node-access fireEvent.click(document.querySelector('.cdk-overlay-backdrop')); await waitForElementToBeRemoved(() => screen.getByRole('dialog'));