From f689d78a31757f089380c22a002518d77b8cd77a Mon Sep 17 00:00:00 2001 From: Marcin Kulpa Date: Sat, 9 Nov 2024 01:46:15 +0100 Subject: [PATCH] test: incorporate eslint-plugin-testing-library and fix reported issues (#2844) --- .eslintrc.json | 3 + package.json | 1 + pnpm-lock.yaml | 113 ++++++++++++++++++++++++++++++++++++ tests/basic.test.tsx | 2 +- tests/persistAsync.test.tsx | 4 ++ tests/shallow.test.tsx | 32 +++++----- tests/ssr.test.tsx | 8 +-- 7 files changed, 142 insertions(+), 21 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index cb587d603d..de6c1612c4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,6 +21,7 @@ "react-hooks", "import", "vitest", + "testing-library", "eslint-plugin-react-compiler" ], "parser": "@typescript-eslint/parser", @@ -109,8 +110,10 @@ }, "overrides": [ { + "extends": ["plugin:testing-library/react"], "files": ["tests/**/*.ts", "tests/**/*.tsx"], "rules": { + "testing-library/no-node-access": "off", "import/extensions": ["error", "never"], "@typescript-eslint/no-unused-vars": "off" } diff --git a/package.json b/package.json index 8ac2396d4a..2a2f452e71 100644 --- a/package.json +++ b/package.json @@ -130,6 +130,7 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-compiler": "19.0.0-beta-6fc168f-20241025", "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-testing-library": "^6.4.0", "eslint-plugin-vitest": "^0.5.4", "immer": "^10.1.1", "jsdom": "^25.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cc770778f..4e3ae4bacd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ devDependencies: eslint-plugin-react-hooks: specifier: ^5.0.0 version: 5.0.0(eslint@8.57.0) + eslint-plugin-testing-library: + specifier: ^6.4.0 + version: 6.4.0(eslint@8.57.0)(typescript@5.6.3) eslint-plugin-vitest: specifier: ^0.5.4 version: 0.5.4(@typescript-eslint/eslint-plugin@8.12.2)(eslint@8.57.0)(typescript@5.6.3)(vitest@2.1.4) @@ -1231,6 +1234,10 @@ packages: resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} dev: true + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + /@types/json5@0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true @@ -1262,6 +1269,10 @@ packages: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} dev: true + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + dev: true + /@types/use-sync-external-store@0.0.6: resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} dev: true @@ -1314,6 +1325,14 @@ packages: - supports-color dev: true + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + dev: true + /@typescript-eslint/scope-manager@7.18.0: resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1349,6 +1368,11 @@ packages: - supports-color dev: true + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@typescript-eslint/types@7.18.0: resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1359,6 +1383,27 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.6.3): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.7 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.6.3 + tsutils: 3.21.0(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3): resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1403,6 +1448,26 @@ packages: - supports-color dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.6.3): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3) + eslint: 8.57.0 + eslint-scope: 5.1.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/utils@7.18.0(eslint@8.57.0)(typescript@5.6.3): resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1435,6 +1500,14 @@ packages: - typescript dev: true + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@typescript-eslint/visitor-keys@7.18.0: resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2388,6 +2461,19 @@ packages: string.prototype.repeat: 1.0.0 dev: true + /eslint-plugin-testing-library@6.4.0(eslint@8.57.0)(typescript@5.6.3): + resolution: {integrity: sha512-yeWF+YgCgvNyPNI9UKnG0FjeE2sk93N/3lsKqcmR8dSfeXJwFT5irnWo7NjLf152HkRzfoFjh3LsBUrhvFz4eA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.6.3) + eslint: 8.57.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.12.2)(eslint@8.57.0)(typescript@5.6.3)(vitest@2.1.4): resolution: {integrity: sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==} engines: {node: ^18.0.0 || >= 20.0.0} @@ -2410,6 +2496,14 @@ packages: - typescript dev: true + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2493,6 +2587,11 @@ packages: estraverse: 5.3.0 dev: true + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -4130,10 +4229,24 @@ packages: strip-bom: 3.0.0 dev: true + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + /tslib@2.8.0: resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} dev: true + /tsutils@3.21.0(typescript@5.6.3): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.6.3 + dev: true + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} diff --git a/tests/basic.test.tsx b/tests/basic.test.tsx index c65b0fb3c5..01d3326f57 100644 --- a/tests/basic.test.tsx +++ b/tests/basic.test.tsx @@ -729,7 +729,7 @@ it('works with "undefined" state', async () => { return
str: {str}
} - const { findByText } = render( + render( , diff --git a/tests/persistAsync.test.tsx b/tests/persistAsync.test.tsx index a7a5728090..f86f3028cf 100644 --- a/tests/persistAsync.test.tsx +++ b/tests/persistAsync.test.tsx @@ -336,8 +336,12 @@ describe('persist middleware with async configuration', () => { ) await screen.findByText('count: 0') + await waitFor(() => { expect(console.error).toHaveBeenCalled() + }) + + await waitFor(() => { expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined) }) }) diff --git a/tests/shallow.test.tsx b/tests/shallow.test.tsx index 9c212f5732..431b4651cc 100644 --- a/tests/shallow.test.tsx +++ b/tests/shallow.test.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { act, fireEvent, render } from '@testing-library/react' +import { act, fireEvent, render, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { create } from 'zustand' import { useShallow } from 'zustand/react/shallow' @@ -60,12 +60,12 @@ describe('useShallow', () => { }) it('input and output selectors always return shallow equal values', () => { - const res = render( + const { rerender } = render( , ) expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(0) - fireEvent.click(res.getByTestId('test-shallow')) + fireEvent.click(screen.getByTestId('test-shallow')) const firstRender = testUseShallowSimpleCallback.mock.lastCall?.[0] @@ -73,14 +73,14 @@ describe('useShallow', () => { expect(firstRender).toBeTruthy() expect(firstRender?.selectorOutput).toEqual(firstRender?.useShallowOutput) - res.rerender( + rerender( , ) - fireEvent.click(res.getByTestId('test-shallow')) + fireEvent.click(screen.getByTestId('test-shallow')) expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2) const secondRender = testUseShallowSimpleCallback.mock.lastCall?.[0] @@ -91,25 +91,25 @@ describe('useShallow', () => { it('returns the previously computed instance when possible', () => { const state = { a: 1, b: 2 } - const res = render( + const { rerender } = render( , ) - fireEvent.click(res.getByTestId('test-shallow')) + fireEvent.click(screen.getByTestId('test-shallow')) expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(1) const output1 = testUseShallowSimpleCallback.mock.lastCall?.[0]?.useShallowOutput expect(output1).toBeTruthy() // Change selector, same output - res.rerender( + rerender( Object.keys(state)} />, ) - fireEvent.click(res.getByTestId('test-shallow')) + fireEvent.click(screen.getByTestId('test-shallow')) expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2) const output2 = @@ -137,10 +137,10 @@ describe('useShallow', () => { } expect(countRenders).toBe(0) - const res = render() + render() expect(countRenders).toBe(1) - expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c') + expect(screen.getByTestId('test-shallow').textContent).toBe('a,b,c') act(() => { useMyStore.setState({ a: 4 }) // This will not cause a re-render. @@ -153,7 +153,7 @@ describe('useShallow', () => { }) expect(countRenders).toBe(2) - expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,d') + expect(screen.getByTestId('test-shallow').textContent).toBe('a,b,c,d') }) it('does not cause stale closure issues', () => { @@ -176,12 +176,12 @@ describe('useShallow', () => { ) } - const res = render() + render() - expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,0') + expect(screen.getByTestId('test-shallow').textContent).toBe('a,b,c,0') - fireEvent.click(res.getByTestId('test-shallow')) + fireEvent.click(screen.getByTestId('test-shallow')) - expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,1') + expect(screen.getByTestId('test-shallow').textContent).toBe('a,b,c,1') }) }) diff --git a/tests/ssr.test.tsx b/tests/ssr.test.tsx index 0343fd789c..155afe884c 100644 --- a/tests/ssr.test.tsx +++ b/tests/ssr.test.tsx @@ -40,7 +40,7 @@ describe.skipIf(!React.version.startsWith('18'))( 'react-dom/client', ) - const markup = renderToString( + const view = renderToString( Loading...}> , @@ -48,7 +48,7 @@ describe.skipIf(!React.version.startsWith('18'))( const container = document.createElement('div') document.body.appendChild(container) - container.innerHTML = markup + container.innerHTML = view expect(container.textContent).toContain('bears: 0') @@ -80,7 +80,7 @@ describe.skipIf(!React.version.startsWith('18'))( return
bears: {bears}
} - const markup = renderToString( + const view = renderToString( Loading...}> , @@ -88,7 +88,7 @@ describe.skipIf(!React.version.startsWith('18'))( const container = document.createElement('div') document.body.appendChild(container) - container.innerHTML = markup + container.innerHTML = view expect(container.textContent).toContain('bears: 0')