From 585d7c6f6388721baefbef8e8a8c1ddc34f9a644 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 16 Aug 2026 19:32:41 +0200 Subject: [PATCH 1/5] Replace @testing-library/jest-dom with built-in vitest browser assertions --- config/test/setupTests.ts | 9 -- package-lock.json | 140 +----------------- package.json | 1 - src/servers/helpers/ServerForm.tsx | 2 +- test/app/App.test.tsx | 16 +- test/common/AppUpdateBanner.test.tsx | 31 ++-- test/common/ErrorHandler.test.tsx | 30 ++-- test/common/Home.test.tsx | 16 +- test/common/MainHeader.test.tsx | 15 +- test/common/NotFound.test.tsx | 24 +-- test/common/ScrollToTop.test.tsx | 4 +- test/common/ShlinkVersions.test.tsx | 26 ++-- test/common/ShlinkVersionsContainer.test.tsx | 6 +- .../ShlinkWebComponentContainer.test.tsx | 22 +-- test/common/img/ShlinkLogo.test.tsx | 10 +- test/container/context.test.tsx | 4 +- test/servers/CreateServer.test.tsx | 51 ++++--- test/servers/DeleteServerButton.test.tsx | 15 +- test/servers/DeleteServerModal.test.tsx | 22 +-- test/servers/EditServer.test.tsx | 33 +++-- test/servers/ManageServers.test.tsx | 58 ++++---- test/servers/ManageServersRow.test.tsx | 18 +-- .../servers/ManageServersRowDropdown.test.tsx | 26 ++-- test/servers/ServersDropdown.test.tsx | 30 ++-- test/servers/ServersListGroup.test.tsx | 6 +- .../helpers/DuplicatedServersModal.test.tsx | 57 +++---- .../servers/helpers/ImportServersBtn.test.tsx | 28 ++-- test/servers/helpers/ServerError.test.tsx | 6 +- test/servers/helpers/ServerForm.test.tsx | 23 +-- vite.config.ts | 5 +- 30 files changed, 305 insertions(+), 429 deletions(-) delete mode 100644 config/test/setupTests.ts diff --git a/config/test/setupTests.ts b/config/test/setupTests.ts deleted file mode 100644 index cce5708f..00000000 --- a/config/test/setupTests.ts +++ /dev/null @@ -1,9 +0,0 @@ -import '@testing-library/jest-dom/vitest'; -import { cleanup } from '@testing-library/react'; -import { afterEach } from 'vitest'; - -// Clear all mocks and cleanup DOM after every test -afterEach(() => { - vi.clearAllMocks(); - cleanup(); -}); diff --git a/package-lock.json b/package-lock.json index 34a21dc3..71c06cb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "devDependencies": { "@shlinkio/eslint-config-js-coding-standard": "~4.1.0", "@tailwindcss/vite": "^4.3.3", - "@testing-library/jest-dom": "^7.0.0", "@testing-library/react": "^16.3.2", "@total-typescript/shoehorn": "^0.1.2", "@types/node": "^26.1.2", @@ -64,12 +63,6 @@ "vitest": "^4.0.3" } }, - "node_modules/@adobe/css-tools": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", - "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", - "dev": true - }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -3690,34 +3683,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@testing-library/jest-dom": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.0.tgz", - "integrity": "sha512-HKAH9C6mBo5yBG6yRO5i43L2iisencAo5z+o5P/saHUoY+miC5ivXRxHBJcFyB5ypPNxHJdK3BoF/3O4DIptMg==", - "dev": true, - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "picocolors": "^1.1.1", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=22", - "npm": ">=6", - "yarn": ">=1" - }, - "peerDependencies": { - "@testing-library/dom": ">=10 <11" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true - }, "node_modules/@testing-library/react": { "version": "16.3.2", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", @@ -4476,6 +4441,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -4896,11 +4862,6 @@ "node": ">=8" } }, - "node_modules/css.escape": { - "version": "1.5.1", - "dev": true, - "license": "MIT" - }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -5173,6 +5134,7 @@ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, + "peer": true, "engines": { "node": ">=6" } @@ -5871,14 +5833,6 @@ "url": "https://opencollective.com/immer" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -6756,14 +6710,6 @@ "version": "2.1.1", "license": "MIT" }, - "node_modules/min-indent": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -7325,18 +7271,6 @@ "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -7977,17 +7911,6 @@ "node": ">=10" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9268,12 +9191,6 @@ } }, "dependencies": { - "@adobe/css-tools": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", - "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", - "dev": true - }, "@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -11435,28 +11352,6 @@ } } }, - "@testing-library/jest-dom": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.0.tgz", - "integrity": "sha512-HKAH9C6mBo5yBG6yRO5i43L2iisencAo5z+o5P/saHUoY+miC5ivXRxHBJcFyB5ypPNxHJdK3BoF/3O4DIptMg==", - "dev": true, - "requires": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "picocolors": "^1.1.1", - "redent": "^3.0.0" - }, - "dependencies": { - "dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true - } - } - }, "@testing-library/react": { "version": "16.3.2", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", @@ -11906,6 +11801,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, + "peer": true, "requires": { "dequal": "^2.0.3" } @@ -12197,10 +12093,6 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, - "css.escape": { - "version": "1.5.1", - "dev": true - }, "csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -12376,7 +12268,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true + "dev": true, + "peer": true }, "detect-libc": { "version": "2.1.2", @@ -12868,10 +12761,6 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.16.tgz", "integrity": "sha512-Xs7H9rBc+kti1J6RueUvbEBkmOz7jqj11XYgf+YMXAYzu8EeE7hwZ9poLXdVfVnGmJu7QAf41T7H2KuF6QoK6Q==" }, - "indent-string": { - "version": "4.0.0", - "dev": true - }, "internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -13380,10 +13269,6 @@ "merge": { "version": "2.1.1" }, - "min-indent": { - "version": "1.0.1", - "dev": true - }, "minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -13718,14 +13603,6 @@ "victory-vendor": "^37.0.2" } }, - "redent": { - "version": "3.0.0", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, "redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -14188,13 +14065,6 @@ "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", "dev": true }, - "strip-indent": { - "version": "3.0.0", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/package.json b/package.json index 84cc07c4..d2a32829 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "devDependencies": { "@shlinkio/eslint-config-js-coding-standard": "~4.1.0", "@tailwindcss/vite": "^4.3.3", - "@testing-library/jest-dom": "^7.0.0", "@testing-library/react": "^16.3.2", "@total-typescript/shoehorn": "^0.1.2", "@types/node": "^26.1.2", diff --git a/src/servers/helpers/ServerForm.tsx b/src/servers/helpers/ServerForm.tsx index a615aede..019f3ea7 100644 --- a/src/servers/helpers/ServerForm.tsx +++ b/src/servers/helpers/ServerForm.tsx @@ -28,7 +28,7 @@ export const ServerForm: FC = ({ onSubmit, initialValues, child const handleSubmit = usePreventDefault(() => onSubmit({ name, url, apiKey, forwardCredentials })); return ( -
+ setName(e.target.value)} required /> setUrl(e.target.value)} required /> diff --git a/test/app/App.test.tsx b/test/app/App.test.tsx index b768994f..e75a2858 100644 --- a/test/app/App.test.tsx +++ b/test/app/App.test.tsx @@ -1,10 +1,10 @@ import type { HttpClient } from '@shlinkio/shlink-js-sdk'; -import { act, screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; +import { act } from 'react'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import { App } from '../../src/app/App'; import { ContainerProvider } from '../../src/container/context'; -import type { ServerWithId } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithStore } from '../__helpers__/setUpTest'; @@ -30,8 +30,8 @@ describe('', () => { { initialState: { servers: { - abc123: fromPartial({ id: 'abc123', name: 'abc123 server' }), - def456: fromPartial({ id: 'def456', name: 'def456 server' }), + abc123: fromPartial({ id: 'abc123', name: 'abc123 server' }), + def456: fromPartial({ id: 'def456', name: 'def456 server' }), }, settings: fromPartial({}), appUpdated: false, @@ -54,7 +54,7 @@ describe('', () => { ['/other', 'Oops! We could not find requested route.'], ])('renders expected route', async (activeRoute, expectedComponent) => { await setUp(activeRoute); - expect(screen.getByText(expectedComponent)).toBeInTheDocument(); + await expect.element(page.getByText(expectedComponent)).toBeInTheDocument(); }); it.each([ @@ -63,12 +63,12 @@ describe('', () => { ['/', true], ])('renders expected classes on shlink-wrapper based on current pathname', async (pathname, isFlex) => { await setUp(pathname); - const shlinkWrapper = screen.getByTestId('shlink-wrapper'); + const shlinkWrapper = page.getByTestId('shlink-wrapper'); if (isFlex) { - expect(shlinkWrapper).toHaveClass('flex'); + await expect.element(shlinkWrapper).toHaveClass('flex'); } else { - expect(shlinkWrapper).not.toHaveClass('flex'); + await expect.element(shlinkWrapper).not.toHaveClass('flex'); } }); }); diff --git a/test/common/AppUpdateBanner.test.tsx b/test/common/AppUpdateBanner.test.tsx index 24af528d..a2e87c9c 100644 --- a/test/common/AppUpdateBanner.test.tsx +++ b/test/common/AppUpdateBanner.test.tsx @@ -1,4 +1,4 @@ -import { act, screen, waitFor } from '@testing-library/react'; +import { page } from 'vitest/browser'; import { AppUpdateBanner } from '../../src/common/AppUpdateBanner'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithEvents } from '../__helpers__/setUpTest'; @@ -6,40 +6,33 @@ import { renderWithEvents } from '../__helpers__/setUpTest'; describe('', () => { const onClose = vi.fn(); const forceUpdate = vi.fn(); - const setUp = async () => { - const result = await act(() => - renderWithEvents(), - ); - await waitFor(() => screen.getByRole('alert')); - - return result; - }; + const setUp = () => renderWithEvents(); it('passes a11y checks', () => checkAccessibility(setUp())); it('renders initial state', async () => { - await setUp(); + setUp(); - expect(screen.getByRole('heading')).toHaveTextContent('This app has just been updated!'); - expect(screen.queryByText('Restarting...')).not.toBeInTheDocument(); - expect(screen.getByText('Restart now')).not.toHaveAttribute('disabled'); + await expect.element(page.getByRole('heading')).toHaveTextContent('This app has just been updated!'); + await expect.element(page.getByText('Restarting...')).not.toBeInTheDocument(); + await expect.element(page.getByText('Restart now')).not.toHaveAttribute('disabled'); }); it('invokes toggle when alert is closed', async () => { - const { user } = await setUp(); + const { user } = setUp(); expect(onClose).not.toHaveBeenCalled(); - await user.click(screen.getByLabelText('Close')); + await user.click(page.getByLabelText('Close')); expect(onClose).toHaveBeenCalled(); }); it('triggers the update when clicking the button', async () => { - const { user } = await setUp(); + const { user } = setUp(); expect(forceUpdate).not.toHaveBeenCalled(); - await user.click(screen.getByText(/^Restart now/)); + await user.click(page.getByText(/^Restart now/)); expect(forceUpdate).toHaveBeenCalled(); - expect(await screen.findByText('Restarting...')).toBeInTheDocument(); - expect(screen.queryByText(/^Restart now/)).not.toBeInTheDocument(); + await expect.element(page.getByText('Restarting...')).toBeInTheDocument(); + await expect.element(page.getByText(/^Restart now/)).not.toBeInTheDocument(); }); }); diff --git a/test/common/ErrorHandler.test.tsx b/test/common/ErrorHandler.test.tsx index 2bbd85e2..1218c0e1 100644 --- a/test/common/ErrorHandler.test.tsx +++ b/test/common/ErrorHandler.test.tsx @@ -1,7 +1,7 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; -import type { PropsWithChildren, ReactNode } from 'react'; -import { ErrorHandler as BaseErrorHandler } from '../../src/common/ErrorHandler'; +import type { ReactNode } from 'react'; +import { page } from 'vitest/browser'; +import { ErrorHandler } from '../../src/common/ErrorHandler'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithEvents } from '../__helpers__/setUpTest'; @@ -13,8 +13,12 @@ describe('', () => { const reload = vi.fn(); const location = fromPartial({ reload }); const cons = fromPartial({ error: vi.fn() }); - const ErrorHandler = (props: PropsWithChildren) => ; - const setUp = (children: ReactNode = 'Error') => renderWithEvents({children}); + const setUp = (children: ReactNode = 'Error') => + renderWithEvents( + + {children} + , + ); beforeEach(() => { vi.spyOn(console, 'error').mockImplementation(() => {}); // Silence react errors @@ -22,26 +26,26 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('renders children when no error has occurred', () => { + it('renders children when no error has occurred', async () => { setUp(Foo); - expect(screen.getByText('Foo')).toBeInTheDocument(); - expect(screen.queryByText('Oops! This is awkward :S')).not.toBeInTheDocument(); - expect(screen.queryByRole('button')).not.toBeInTheDocument(); + await expect.element(page.getByText('Foo')).toBeInTheDocument(); + await expect.element(page.getByText('Oops! This is awkward :S')).not.toBeInTheDocument(); + await expect.element(page.getByRole('button')).not.toBeInTheDocument(); }); - it('renders error page when error has occurred', () => { + it('renders error page when error has occurred', async () => { setUp(); - expect(screen.getByText('Oops! This is awkward :S')).toBeInTheDocument(); - expect(screen.getByRole('button')).toBeInTheDocument(); + await expect.element(page.getByText('Oops! This is awkward :S')).toBeInTheDocument(); + await expect.element(page.getByRole('button')).toBeInTheDocument(); }); it('reloads page on button click', async () => { const { user } = setUp(); expect(reload).not.toHaveBeenCalled(); - await user.click(screen.getByRole('button')); + await user.click(page.getByRole('button')); expect(reload).toHaveBeenCalled(); }); }); diff --git a/test/common/Home.test.tsx b/test/common/Home.test.tsx index 5a2d6579..30fd5b76 100644 --- a/test/common/Home.test.tsx +++ b/test/common/Home.test.tsx @@ -1,6 +1,6 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import { Home } from '../../src/common/Home'; import type { ServersMap, ServerWithId } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -20,9 +20,9 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp({ '1a': fromPartial({ name: 'foo', id: '1' }) }))); - it('renders title', () => { + it('renders title', async () => { setUp(); - expect(screen.getByRole('heading', { name: 'Welcome!' })).toBeInTheDocument(); + await expect.element(page.getByRole('heading', { name: 'Welcome!' })).toBeInTheDocument(); }); it.each([ @@ -35,15 +35,17 @@ describe('', () => { 3, ], [{}, 2], - ])('shows link to create or set-up server only when no servers exist', (servers, expectedServers) => { + ])('shows link to create or set-up server only when no servers exist', async (servers, expectedServers) => { setUp(servers); - const links = screen.getAllByRole('link'); + const links = page.getByRole('link').elements(); expect(links).toHaveLength(expectedServers); if (Object.keys(servers).length === 0) { - expect(screen.getByText('This application will help you manage your Shlink servers.')).toBeInTheDocument(); - expect(screen.getByText('Learn more about Shlink')).toBeInTheDocument(); + await expect + .element(page.getByText('This application will help you manage your Shlink servers.')) + .toBeInTheDocument(); + await expect.element(page.getByText('Learn more about Shlink')).toBeInTheDocument(); } }); }); diff --git a/test/common/MainHeader.test.tsx b/test/common/MainHeader.test.tsx index 5517d85f..06567e2d 100644 --- a/test/common/MainHeader.test.tsx +++ b/test/common/MainHeader.test.tsx @@ -1,6 +1,6 @@ -import { screen } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; +import { page } from 'vitest/browser'; import { MainHeader } from '../../src/common/MainHeader'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithStore } from '../__helpers__/setUpTest'; @@ -19,9 +19,9 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('renders ServersDropdown', () => { + it('renders ServersDropdown', async () => { setUp(); - expect(screen.getByRole('button', { name: 'Servers' })).toBeInTheDocument(); + await expect.element(page.getByRole('button', { name: 'Servers' })).toBeInTheDocument(); }); it.each([ @@ -30,11 +30,10 @@ describe('', () => { ['/settings', true], ['/settings/foo', true], ['/settings/bar', true], - ])('sets link to settings as active only when current path is settings', (currentPath, isActive) => { + ])('sets link to settings as active only when current path is settings', async (currentPath, isActive) => { setUp(currentPath); - expect(screen.getByRole('menuitem', { name: /Settings$/ })).toHaveAttribute( - 'data-active', - isActive ? 'true' : 'false', - ); + await expect + .element(page.getByRole('menuitem', { name: /Settings$/ })) + .toHaveAttribute('data-active', isActive ? 'true' : 'false'); }); }); diff --git a/test/common/NotFound.test.tsx b/test/common/NotFound.test.tsx index 32e1fe85..2a1e6a19 100644 --- a/test/common/NotFound.test.tsx +++ b/test/common/NotFound.test.tsx @@ -13,29 +13,31 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('shows expected error title', () => { + it('shows expected error title', async () => { setUp(); - expect(screen.getByText('Oops! We could not find requested route.')).toBeInTheDocument(); + await expect.element(screen.getByText('Oops! We could not find requested route.')).toBeInTheDocument(); }); - it('shows expected error message', () => { + it('shows expected error message', async () => { setUp(); - expect( - screen.getByText( - "Use your browser's back button to navigate to the page you have previously come from, or just press this button.", - ), - ).toBeInTheDocument(); + await expect + .element( + screen.getByText( + "Use your browser's back button to navigate to the page you have previously come from, or just press this button.", + ), + ) + .toBeInTheDocument(); }); it.each([ [{}, '/', 'Home'], [{ to: '/foo/bar', children: 'Hello' }, '/foo/bar', 'Hello'], [{ to: '/baz-bar', children: <>Foo }, '/baz-bar', 'Foo'], - ])('shows expected link and text', (props, expectedLink, expectedText) => { + ])('shows expected link and text', async (props, expectedLink, expectedText) => { setUp(props); const link = screen.getByRole('link'); - expect(link).toHaveAttribute('href', expectedLink); - expect(link).toHaveTextContent(expectedText); + await expect.element(link).toHaveAttribute('href', expectedLink); + await expect.element(link).toHaveTextContent(expectedText); }); }); diff --git a/test/common/ScrollToTop.test.tsx b/test/common/ScrollToTop.test.tsx index 5afe9416..4b2eae1f 100644 --- a/test/common/ScrollToTop.test.tsx +++ b/test/common/ScrollToTop.test.tsx @@ -13,8 +13,8 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it.each([['Foobar'], ['Barfoo'], ['Something']])('just renders children', (children) => { + it.each([['Foobar'], ['Barfoo'], ['Something']])('just renders children', async (children) => { setUp(children); - expect(screen.getByText(children)).toBeInTheDocument(); + await expect.element(screen.getByText(children)).toBeInTheDocument(); }); }); diff --git a/test/common/ShlinkVersions.test.tsx b/test/common/ShlinkVersions.test.tsx index 7cd203fb..9d7b5ec0 100644 --- a/test/common/ShlinkVersions.test.tsx +++ b/test/common/ShlinkVersions.test.tsx @@ -19,20 +19,18 @@ describe('', () => { ['not-semver', fromPartial({ version: '1.0.0', printableVersion: 'some' }), 'latest', 'some'], ])( 'displays expected versions when selected server is reachable', - (clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => { + async (clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => { setUp({ clientVersion, selectedServer }); const [serverLink, clientLink] = screen.getAllByRole('link'); - expect(serverLink).toHaveAttribute( - 'href', - `https://github.com/shlinkio/shlink/releases/${expectedServerVersion}`, - ); - expect(serverLink).toHaveTextContent(expectedServerVersion); - expect(clientLink).toHaveAttribute( - 'href', - `https://github.com/shlinkio/shlink-web-client/releases/${expectedClientVersion}`, - ); - expect(clientLink).toHaveTextContent(expectedClientVersion); + await expect + .element(serverLink) + .toHaveAttribute('href', `https://github.com/shlinkio/shlink/releases/${expectedServerVersion}`); + await expect.element(serverLink).toHaveTextContent(expectedServerVersion); + await expect + .element(clientLink) + .toHaveAttribute('href', `https://github.com/shlinkio/shlink-web-client/releases/${expectedClientVersion}`); + await expect.element(clientLink).toHaveTextContent(expectedClientVersion); }, ); @@ -40,11 +38,13 @@ describe('', () => { ['1.2.3', null], ['1.2.3', fromPartial({ serverNotFound: true })], ['1.2.3', fromPartial({ serverNotReachable: true })], - ])('displays only client version when selected server is not reachable', (clientVersion, selectedServer) => { + ])('displays only client version when selected server is not reachable', async (clientVersion, selectedServer) => { setUp({ clientVersion, selectedServer }); const links = screen.getAllByRole('link'); expect(links).toHaveLength(1); - expect(links[0]).toHaveAttribute('href', 'https://github.com/shlinkio/shlink-web-client/releases/v1.2.3'); + await expect + .element(links[0]) + .toHaveAttribute('href', 'https://github.com/shlinkio/shlink-web-client/releases/v1.2.3'); }); }); diff --git a/test/common/ShlinkVersionsContainer.test.tsx b/test/common/ShlinkVersionsContainer.test.tsx index dabdeeb8..6d5f0dc2 100644 --- a/test/common/ShlinkVersionsContainer.test.tsx +++ b/test/common/ShlinkVersionsContainer.test.tsx @@ -20,13 +20,13 @@ describe('', () => { [null, false], [fromPartial({}), false], [fromPartial({ version: '1.0.0' }), true], - ])('renders proper col classes based on sidebar status', (selectedServer, shouldAddMargin) => { + ])('renders proper col classes based on sidebar status', async (selectedServer, shouldAddMargin) => { const { container } = setUp(selectedServer); if (shouldAddMargin) { - expect(container.firstChild).toHaveClass('md:ml-(--aside-menu-width)'); + await expect.element(container.firstChild as HTMLElement).toHaveClass('md:ml-(--aside-menu-width)'); } else { - expect(container.firstChild).not.toHaveClass('md:ml-(--aside-menu-width)'); + await expect.element(container.firstChild as HTMLElement).not.toHaveClass('md:ml-(--aside-menu-width)'); } }); }); diff --git a/test/common/ShlinkWebComponentContainer.test.tsx b/test/common/ShlinkWebComponentContainer.test.tsx index f892f128..145d9500 100644 --- a/test/common/ShlinkWebComponentContainer.test.tsx +++ b/test/common/ShlinkWebComponentContainer.test.tsx @@ -1,6 +1,6 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import { ShlinkWebComponentContainer } from '../../src/common/ShlinkWebComponentContainer'; import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -25,11 +25,11 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp(fromPartial({ version: '3.0.0' })))); - it('shows loading indicator while loading server', () => { + it('shows loading indicator while loading server', async () => { setUp(null); - expect(screen.getByText('Loading...')).toBeInTheDocument(); - expect(screen.queryByText('ShlinkWebComponent')).not.toBeInTheDocument(); + await expect.element(page.getByText('Loading...')).toBeInTheDocument(); + await expect.element(page.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); }); it.each([ @@ -38,18 +38,18 @@ describe('', () => { fromPartial({ id: 'foo', serverNotReachable: true }), /Could not connect to this Shlink server/, ], - ])('shows error for non reachable servers', (selectedServer, expectedError) => { + ])('shows error for non reachable servers', async (selectedServer, expectedError) => { setUp(selectedServer); - expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); - expect(screen.getByText(expectedError)).toBeInTheDocument(); - expect(screen.queryByText('ShlinkWebComponent')).not.toBeInTheDocument(); + await expect.element(page.getByText('Loading...')).not.toBeInTheDocument(); + await expect.element(page.getByText(expectedError)).toBeInTheDocument(); + await expect.element(page.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); }); - it('renders ShlinkWebComponent for reachable servers', () => { + it('renders ShlinkWebComponent for reachable servers', async () => { setUp(fromPartial({ version: '3.0.0' })); - expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); - expect(screen.getByText('ShlinkWebComponent')).toBeInTheDocument(); + await expect.element(page.getByText('Loading...')).not.toBeInTheDocument(); + await expect.element(page.getByText('ShlinkWebComponent')).toBeInTheDocument(); }); }); diff --git a/test/common/img/ShlinkLogo.test.tsx b/test/common/img/ShlinkLogo.test.tsx index 4b4fbd55..ec982bdc 100644 --- a/test/common/img/ShlinkLogo.test.tsx +++ b/test/common/img/ShlinkLogo.test.tsx @@ -13,22 +13,22 @@ describe('', () => { [undefined, brandColor()], ['red', 'red'], ['white', 'white'], - ])('renders expected color', (color, expectedColor) => { + ])('renders expected color', async (color, expectedColor) => { const { container } = setUp({ color }); - expect(container.querySelector('g')).toHaveAttribute('fill', expectedColor); + await expect.element(container.querySelector('g')).toHaveAttribute('fill', expectedColor); }); it.each([ [undefined, undefined], ['foo', 'foo'], ['bar', 'bar'], - ])('renders expected class', (className, expectedClassName) => { + ])('renders expected class', async (className, expectedClassName) => { const { container } = setUp({ className }); if (expectedClassName) { - expect(container.firstChild).toHaveAttribute('class', expectedClassName); + await expect.element(container.firstChild as HTMLElement).toHaveAttribute('class', expectedClassName); } else { - expect(container.firstChild).not.toHaveAttribute('class'); + await expect.element(container.firstChild as HTMLElement).not.toHaveAttribute('class'); } }); }); diff --git a/test/container/context.test.tsx b/test/container/context.test.tsx index bdcfb4ba..655124ec 100644 --- a/test/container/context.test.tsx +++ b/test/container/context.test.tsx @@ -13,7 +13,7 @@ describe('context', () => { } it('throws when used outside of ContainerProvider', () => { - expect(() => render()).toThrowError( + expect(() => render()).toThrow( 'You cannot use "useDependencies" outside of a ContainerProvider', ); }); @@ -25,7 +25,7 @@ describe('context', () => { , ), - ).toThrowError('Dependency with name "foo" not found in container'); + ).toThrow('Dependency with name "foo" not found in container'); }); it('gets dependency from container', () => { diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index a1d4cb5c..ded7a93a 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -1,7 +1,8 @@ -import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { fireEvent, waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; +import { page } from 'vitest/browser'; import { CreateServer } from '../../src/servers/CreateServer'; import type { ServersMap } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -41,35 +42,39 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('shows success message when imported is true', () => { + it('shows success message when imported is true', async () => { setUp({ serversImported: true }); - expect(screen.getByText('Servers properly imported. You can now select one from the list :)')).toBeInTheDocument(); - expect( - screen.queryByText('The servers could not be imported. Make sure the format is correct.'), - ).not.toBeInTheDocument(); - expect(screen.queryByText('ImportServersBtn')).not.toBeInTheDocument(); + await expect + .element(page.getByText('Servers properly imported. You can now select one from the list :)')) + .toBeInTheDocument(); + await expect + .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .not.toBeInTheDocument(); + await expect.element(page.getByText('ImportServersBtn')).not.toBeInTheDocument(); }); - it('shows error message when import failed', () => { + it('shows error message when import failed', async () => { setUp({ importFailed: true }); - expect( - screen.queryByText('Servers properly imported. You can now select one from the list :)'), - ).not.toBeInTheDocument(); - expect(screen.getByText('The servers could not be imported. Make sure the format is correct.')).toBeInTheDocument(); + await expect + .element(page.getByText('Servers properly imported. You can now select one from the list :)')) + .not.toBeInTheDocument(); + await expect + .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .toBeInTheDocument(); }); it('creates server data when form is submitted', async () => { const { user, history, store } = setUp(); const expectedServerId = 'the_name-the_url.com'; - await user.type(screen.getByLabelText(/^Name/), 'the_name'); - await user.type(screen.getByLabelText(/^URL/), 'https://the_url.com'); - await user.type(screen.getByLabelText(/^API key/), 'the_api_key'); + await user.type(page.getByLabelText(/^Name/), 'the_name'); + await user.type(page.getByLabelText(/^URL/), 'https://the_url.com'); + await user.type(page.getByLabelText(/^API key/), 'the_api_key'); expect(store.getState().servers[expectedServerId]).not.toBeDefined(); - fireEvent.submit(screen.getByRole('form')); + fireEvent.submit(page.getByTestId('server-form').element()); expect(store.getState().servers[expectedServerId]).toEqual( expect.objectContaining({ id: expectedServerId, @@ -80,20 +85,20 @@ describe('', () => { ); expect(history.location.pathname).toEqual(`/server/${expectedServerId}`); - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); }); it('displays dialog when trying to create a duplicated server', async () => { const { user, history } = setUp(); - await user.type(screen.getByLabelText(/^Name/), 'the_name'); - await user.type(screen.getByLabelText(/^URL/), 'https://existing_url.com'); - await user.type(screen.getByLabelText(/^API key/), 'existing_api_key'); + await user.type(page.getByLabelText(/^Name/), 'the_name'); + await user.type(page.getByLabelText(/^URL/), 'https://existing_url.com'); + await user.type(page.getByLabelText(/^API key/), 'existing_api_key'); - fireEvent.submit(screen.getByRole('form')); + fireEvent.submit(page.getByTestId('server-form').element()); - await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument()); - await user.click(screen.getByRole('button', { name: 'Discard' })); + await waitFor(() => expect(page.getByRole('dialog')).toBeInTheDocument()); + await user.click(page.getByRole('button', { name: 'Discard' })); expect(history.location.pathname).toEqual('/foo'); // Goes back to first route from history's initialEntries }); diff --git a/test/servers/DeleteServerButton.test.tsx b/test/servers/DeleteServerButton.test.tsx index 689b9fa6..ad832d33 100644 --- a/test/servers/DeleteServerButton.test.tsx +++ b/test/servers/DeleteServerButton.test.tsx @@ -1,8 +1,8 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import type { ReactNode } from 'react'; import { Router } from 'react-router'; +import { page } from 'vitest/browser'; import { DeleteServerButton } from '../../src/servers/DeleteServerButton'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithStore } from '../__helpers__/setUpTest'; @@ -21,8 +21,9 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp('Delete me'))); - it.each([['Foo bar'], ['baz'], ['something']])('renders expected content', (children) => { + it.each([['Foo bar'], ['baz'], ['something']])('renders expected content', async (children) => { const { container } = setUp(children); + expect(container.firstChild).toBeTruthy(); expect(container.firstChild).toMatchSnapshot(); }); @@ -30,19 +31,19 @@ describe('', () => { it('displays modal when button is clicked', async () => { const { user } = setUp(); - expect(screen.queryByText(/Are you sure you want to remove/)).not.toBeInTheDocument(); - await user.click(screen.getByText('Remove this server')); - expect(screen.getByText(/Are you sure you want to remove/)).toBeInTheDocument(); + await expect.element(page.getByText(/Are you sure you want to remove/)).not.toBeInTheDocument(); + await user.click(page.getByText('Remove this server')); + await expect.element(page.getByText(/Are you sure you want to remove/)).toBeInTheDocument(); }); it('navigates to home when deletion is confirmed', async () => { const { user, history } = setUp(); // Open modal - await user.click(screen.getByText('Remove this server')); + await user.click(page.getByText('Remove this server')); expect(history.location.pathname).toEqual('/foo'); - await user.click(screen.getByRole('button', { name: 'Delete' })); + await user.click(page.getByRole('button', { name: 'Delete' })); expect(history.location.pathname).toEqual('/'); }); }); diff --git a/test/servers/DeleteServerModal.test.tsx b/test/servers/DeleteServerModal.test.tsx index b6e9560c..425ceaa8 100644 --- a/test/servers/DeleteServerModal.test.tsx +++ b/test/servers/DeleteServerModal.test.tsx @@ -1,5 +1,5 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; +import { page } from 'vitest/browser'; import type { ServerWithId } from '../../src/servers/data'; import { DeleteServerModal } from '../../src/servers/DeleteServerModal'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -18,28 +18,28 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('renders a modal window', () => { + it('renders a modal window', async () => { setUp(); - expect(screen.getByRole('dialog')).toBeInTheDocument(); - expect(screen.getByRole('heading')).toHaveTextContent('Remove server'); + await expect.element(page.getByRole('dialog')).toBeInTheDocument(); + await expect.element(page.getByRole('heading')).toHaveTextContent('Remove server'); }); - it('displays the name of the server as part of the content', () => { + it('displays the name of the server as part of the content', async () => { setUp(); - expect(screen.getByText(/^Are you sure you want to remove/)).toBeInTheDocument(); - expect(screen.getByText(serverName)).toBeInTheDocument(); + await expect.element(page.getByText(/^Are you sure you want to remove/)).toBeInTheDocument(); + await expect.element(page.getByText(serverName)).toBeInTheDocument(); }); - it.each([[() => screen.getByRole('button', { name: 'Cancel' })], [() => screen.getByLabelText('Close dialog')]])( + it.each([[() => page.getByRole('button', { name: 'Cancel' })], [() => page.getByLabelText('Close dialog')]])( 'closes dialog when clicking cancel button', async (getButton) => { const { user, store } = setUp(); - expect(screen.getByRole('dialog')).toBeInTheDocument(); + await expect.element(page.getByRole('dialog')).toBeInTheDocument(); await user.click(getButton()); - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); // No server has been deleted expect(Object.keys(store.getState().servers)).toHaveLength(1); @@ -50,7 +50,7 @@ describe('', () => { const { user, store } = setUp(); expect(Object.keys(store.getState().servers)).toHaveLength(1); - await user.click(screen.getByRole('button', { name: 'Delete' })); + await user.click(page.getByRole('button', { name: 'Delete' })); expect(Object.keys(store.getState().servers)).toHaveLength(0); }); }); diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index 86744176..fbe3d0ec 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -1,7 +1,8 @@ -import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { fireEvent, waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; +import { page } from 'vitest/browser'; import type { ReachableServer, SelectedServer } from '../../src/servers/data'; import { isServerWithId } from '../../src/servers/data'; import { EditServer } from '../../src/servers/EditServer'; @@ -35,35 +36,35 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('renders nothing if selected server is not reachable', () => { + it('renders nothing if selected server is not reachable', async () => { setUp(fromPartial({})); - expect(screen.queryByText('Edit')).not.toBeInTheDocument(); - expect(screen.queryByText('Cancel')).not.toBeInTheDocument(); - expect(screen.queryByText('Save')).not.toBeInTheDocument(); + await expect.element(page.getByText('Edit')).not.toBeInTheDocument(); + await expect.element(page.getByText('Cancel')).not.toBeInTheDocument(); + await expect.element(page.getByText('Save')).not.toBeInTheDocument(); }); it('renders server title', () => { setUp(); - expect(screen.getByText(`Edit "${defaultSelectedServer.name}"`)).toBeInTheDocument(); + expect(page.getByText(`Edit "${defaultSelectedServer.name}"`)).toBeInTheDocument(); }); - it('display the server info in the form components', () => { + it('display the server info in the form components', async () => { setUp(); - expect(screen.getByLabelText(/^Name/)).toBeInTheDocument(); - expect(screen.getByLabelText(/^URL/)).toBeInTheDocument(); - expect(screen.getByLabelText(/^API key/)).toBeInTheDocument(); + await expect.element(page.getByLabelText(/^Name/)).toBeInTheDocument(); + await expect.element(page.getByLabelText(/^URL/)).toBeInTheDocument(); + await expect.element(page.getByLabelText(/^API key/)).toBeInTheDocument(); }); it('edits server and redirects to it when form is submitted', async () => { const { user, history, store } = setUp(); - await user.type(screen.getByLabelText(/^Name/), ' edited'); - await user.type(screen.getByLabelText(/^URL/), ' edited'); + await user.type(page.getByLabelText(/^Name/), ' edited'); + await user.type(page.getByLabelText(/^URL/), ' edited'); // TODO Using fire event because userEvent.click on the Submit button does not submit the form // await user.click(screen.getByRole('button', { name: 'Save' })); - fireEvent.submit(screen.getByRole('form')); + fireEvent.submit(page.getByTestId('server-form').element()); expect(store.getState().servers[defaultSelectedServer.id]).toEqual( expect.objectContaining({ @@ -81,9 +82,9 @@ describe('', () => { async ({ forwardCredentials }) => { const { user, store } = setUp({ ...defaultSelectedServer, forwardCredentials }); - await user.click(screen.getByText('Advanced options')); - await user.click(screen.getByLabelText('Forward credentials to this server on every request.')); - fireEvent.submit(screen.getByRole('form')); + await user.click(page.getByText('Advanced options')); + await user.click(page.getByLabelText('Forward credentials to this server on every request.')); + fireEvent.submit(page.getByTestId('server-form').element()); await waitFor(() => expect(store.getState().servers[defaultSelectedServer.id]).toEqual( diff --git a/test/servers/ManageServers.test.tsx b/test/servers/ManageServers.test.tsx index 7d63d57c..f87c341f 100644 --- a/test/servers/ManageServers.test.tsx +++ b/test/servers/ManageServers.test.tsx @@ -1,6 +1,7 @@ -import { screen, waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import type { ServersMap, ServerWithId } from '../../src/servers/data'; import { ManageServers } from '../../src/servers/ManageServers'; import type { ServersExporter } from '../../src/servers/services/ServersExporter'; @@ -39,71 +40,70 @@ describe('', () => { baz: createServerMock('baz'), }); const search = async (searchTerm: string) => { - await user.clear(screen.getByPlaceholderText('Search...')); - await user.type(screen.getByPlaceholderText('Search...'), searchTerm); + await user.clear(page.getByPlaceholder('Search...')); + await user.type(page.getByPlaceholder('Search...'), searchTerm); }; // Add one for the header row - const expectRows = (amount: number) => expect(screen.getAllByRole('row')).toHaveLength(amount + 1); + const expectRows = (amount: number) => expect(page.getByRole('row').elements()).toHaveLength(amount + 1); expectRows(3); - expect(screen.queryByText('No servers found.')).not.toBeInTheDocument(); + await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); await search('foo'); await waitFor(() => expectRows(1)); - expect(screen.queryByText('No servers found.')).not.toBeInTheDocument(); + await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); await search('Ba'); await waitFor(() => expectRows(2)); - expect(screen.queryByText('No servers found.')).not.toBeInTheDocument(); + await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); await search('invalid'); await waitFor(() => expectRows(1)); - expect(screen.getByText('No servers found.')).toBeInTheDocument(); + await expect.element(page.getByText('No servers found.')).toBeInTheDocument(); }); - it.each([ - [createServerMock('foo'), 3], - [createServerMock('foo', true), 4], - ])('shows different amount of columns if there are at least one auto-connect server', (server, expectedCols) => { - setUp({ server }); + it.only.each([[createServerMock('foo')], [createServerMock('foo', true)]])( + 'shows different amount of columns if there are at least one auto-connect server', + async (server) => { + setUp({ server }); - expect(screen.getAllByRole('columnheader')).toHaveLength(expectedCols); - if (server.autoConnect) { - expect(screen.getByTestId('auto-connect')).toBeInTheDocument(); - } else { - expect(screen.queryByTestId('auto-connect')).not.toBeInTheDocument(); - } - }); + if (server.autoConnect) { + await expect.element(page.getByTestId('auto-connect')).toBeInTheDocument(); + } else { + await expect.element(page.getByTestId('auto-connect')).not.toBeInTheDocument(); + } + }, + ); it.each([ [{}, 0], [{ foo: createServerMock('foo') }, 1], ])('shows export button if the list of servers is not empty', (servers, expectedButtons) => { setUp(servers); - expect(screen.queryAllByRole('button', { name: 'Export servers' })).toHaveLength(expectedButtons); + expect(page.getByRole('button', { name: 'Export servers' }).elements()).toHaveLength(expectedButtons); }); it('allows exporting servers when clicking on button', async () => { const { user } = setUp({ foo: createServerMock('foo') }); expect(exportServers).not.toHaveBeenCalled(); - await user.click(screen.getByRole('button', { name: 'Export servers' })); + await user.click(page.getByRole('button', { name: 'Export servers' })); expect(exportServers).toHaveBeenCalled(); }); - it.each([[true], [false]])('shows an error message if an error occurs while importing servers', (hasError) => { + it.each([[true], [false]])('shows an error message if an error occurs while importing servers', async (hasError) => { useTimeoutToggle.mockReturnValue([hasError, vi.fn()]); setUp({ foo: createServerMock('foo') }); if (hasError) { - expect( - screen.getByText('The servers could not be imported. Make sure the format is correct.'), - ).toBeInTheDocument(); + await expect + .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .toBeInTheDocument(); } else { - expect( - screen.queryByText('The servers could not be imported. Make sure the format is correct.'), - ).not.toBeInTheDocument(); + await expect + .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .not.toBeInTheDocument(); } }); }); diff --git a/test/servers/ManageServersRow.test.tsx b/test/servers/ManageServersRow.test.tsx index 4bd81b80..4d984ec2 100644 --- a/test/servers/ManageServersRow.test.tsx +++ b/test/servers/ManageServersRow.test.tsx @@ -1,6 +1,6 @@ import { Table } from '@shlinkio/shlink-frontend-kit'; -import { screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import type { ServerWithId } from '../../src/servers/data'; import { ManageServersRow } from '../../src/servers/ManageServersRow'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -29,12 +29,12 @@ describe('', () => { [false, 3], ])('renders expected amount of columns', (hasAutoConnect, expectedCols) => { setUp(hasAutoConnect); - expect(screen.getAllByRole('cell')).toHaveLength(expectedCols); + expect(page.getByRole('cell').elements()).toHaveLength(expectedCols); }); - it('renders an options dropdown', () => { + it('renders an options dropdown', async () => { setUp(); - expect(screen.getByRole('button', { name: 'Options' })).toBeInTheDocument(); + await expect.element(page.getByRole('button', { name: 'Options' })).toBeInTheDocument(); }); it.each([[true], [false]])('renders auto-connect icon only if server is autoConnect', (autoConnect) => { @@ -42,13 +42,13 @@ describe('', () => { expect(container).toMatchSnapshot(); }); - it('renders server props where appropriate', () => { + it('renders server props where appropriate', async () => { setUp(); - const link = screen.getByRole('link'); + const link = page.getByRole('link'); - expect(link).toHaveAttribute('href', `/server/${server.id}`); - expect(link).toHaveTextContent(server.name); - expect(screen.getByText(server.url)).toBeInTheDocument(); + await expect.element(link).toHaveAttribute('href', `/server/${server.id}`); + await expect.element(link).toHaveTextContent(server.name); + await expect.element(page.getByText(server.url)).toBeInTheDocument(); }); }); diff --git a/test/servers/ManageServersRowDropdown.test.tsx b/test/servers/ManageServersRowDropdown.test.tsx index d347df4f..74607349 100644 --- a/test/servers/ManageServersRowDropdown.test.tsx +++ b/test/servers/ManageServersRowDropdown.test.tsx @@ -1,6 +1,6 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import type { UserEvent } from 'vitest/browser'; import type { ServerWithId } from '../../src/servers/data'; import { ManageServersRowDropdown } from '../../src/servers/ManageServersRowDropdown'; @@ -21,7 +21,7 @@ describe('', () => { }, ); }; - const toggleDropdown = (user: UserEvent) => user.click(screen.getByRole('button')); + const toggleDropdown = (user: UserEvent) => user.click(page.getByRole('button')); it('passes a11y checks', async () => { const { user, container } = setUp(); @@ -34,20 +34,24 @@ describe('', () => { it('renders expected amount of dropdown items', async () => { const { user } = setUp(); - expect(screen.queryByRole('menu')).not.toBeInTheDocument(); + await expect.element(page.getByRole('menu')).not.toBeInTheDocument(); await toggleDropdown(user); - expect(screen.getByRole('menu')).toBeInTheDocument(); + await expect.element(page.getByRole('menu')).toBeInTheDocument(); - expect(screen.getAllByRole('menuitem')).toHaveLength(4); - expect(screen.getByRole('menuitem', { name: 'Connect' })).toHaveAttribute('href', '/server/abc123'); - expect(screen.getByRole('menuitem', { name: 'Edit server' })).toHaveAttribute('href', '/server/abc123/edit'); + expect(page.getByRole('menuitem').elements()).toHaveLength(4); + await expect + .element(page.getByRole('menuitem', { name: 'Connect', exact: true })) + .toHaveAttribute('href', '/server/abc123'); + await expect + .element(page.getByRole('menuitem', { name: 'Edit server' })) + .toHaveAttribute('href', '/server/abc123/edit'); }); it.each([true, false])('allows toggling auto-connect', async (autoConnect) => { const { user, store } = setUp(autoConnect); await toggleDropdown(user); - await user.click(screen.getByRole('menuitem', { name: autoConnect ? 'Do not auto-connect' : 'Auto-connect' })); + await user.click(page.getByRole('menuitem', { name: autoConnect ? 'Do not auto-connect' : 'Auto-connect' })); expect(Object.values(store.getState().servers)[0].autoConnect).toEqual(!autoConnect); }); @@ -55,12 +59,12 @@ describe('', () => { it('renders deletion modal', async () => { const { user } = setUp(); - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + expect(page.getByRole('dialog')).not.toBeInTheDocument(); await toggleDropdown(user); - await user.click(screen.getByRole('menuitem', { name: 'Remove server' })); + await user.click(page.getByRole('menuitem', { name: 'Remove server' })); - expect(screen.getByRole('dialog')).toBeInTheDocument(); + await expect.element(page.getByRole('dialog')).toBeInTheDocument(); }); it.each([[true], [false]])('renders expected size and icon', (autoConnect) => { diff --git a/test/servers/ServersDropdown.test.tsx b/test/servers/ServersDropdown.test.tsx index e9962c46..ab10f3c3 100644 --- a/test/servers/ServersDropdown.test.tsx +++ b/test/servers/ServersDropdown.test.tsx @@ -1,6 +1,6 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; +import { page } from 'vitest/browser'; import type { ServersMap } from '../../src/servers/data'; import { ServersDropdown } from '../../src/servers/ServersDropdown'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -27,7 +27,7 @@ describe('', () => { it('passes a11y checks', async () => { const { user, ...rest } = setUp(); // Open menu - await user.click(screen.getByText('Servers')); + await user.click(page.getByText('Servers')); return checkAccessibility(rest); }); @@ -35,33 +35,35 @@ describe('', () => { it('contains the list of servers and the "mange servers" button', async () => { const { user } = setUp(); - await user.click(screen.getByText('Servers')); - const items = screen.getAllByRole('menuitem'); + await user.click(page.getByText('Servers')); + const items = page.getByRole('menuitem').elements(); // We have to add two for the "Manage servers" and the "Settings" menu items expect(items).toHaveLength(Object.values(fallbackServers).length + 2); - expect(items[1]).toHaveTextContent('foo'); - expect(items[2]).toHaveTextContent('bar'); - expect(items[3]).toHaveTextContent('baz'); - expect(items[4]).toHaveTextContent('Manage servers'); + await expect.element(items[1]).toHaveTextContent('foo'); + await expect.element(items[2]).toHaveTextContent('bar'); + await expect.element(items[3]).toHaveTextContent('baz'); + await expect.element(items[4]).toHaveTextContent('Manage servers'); }); - it('contains a toggle with proper text', () => { + it('contains a toggle with proper text', async () => { setUp(); - expect(screen.getByRole('button')).toHaveTextContent('Servers'); + await expect.element(page.getByRole('button')).toHaveTextContent('Servers'); }); it('contains a button to manage servers', async () => { const { user } = setUp(); - await user.click(screen.getByText('Servers')); - expect(screen.getByRole('menuitem', { name: 'Manage servers' })).toHaveAttribute('href', '/manage-servers'); + await user.click(page.getByText('Servers')); + await expect + .element(page.getByRole('menuitem', { name: 'Manage servers' })) + .toHaveAttribute('href', '/manage-servers'); }); it('shows only create link when no servers exist yet', async () => { const { user } = setUp({}); - await user.click(screen.getByText('Servers')); - expect(screen.getByRole('menuitem', { name: 'Add a server' })).toBeInTheDocument(); + await user.click(page.getByText('Servers')); + await expect.element(page.getByRole('menuitem', { name: 'Add a server' })).toBeInTheDocument(); }); }); diff --git a/test/servers/ServersListGroup.test.tsx b/test/servers/ServersListGroup.test.tsx index 3e2dc26c..176497f9 100644 --- a/test/servers/ServersListGroup.test.tsx +++ b/test/servers/ServersListGroup.test.tsx @@ -26,14 +26,14 @@ describe('', () => { expect(screen.queryAllByRole('link')).toHaveLength(servers.length); }); - it.each([[true], [false], [undefined]])('renders proper classes for embedded', (borderless) => { + it.each([[true], [false], [undefined]])('renders proper classes for embedded', async (borderless) => { setUp({ servers, borderless }); const list = screen.getByTestId('list'); if (!borderless) { - expect(list).toHaveClass('border-y'); + await expect.element(list).toHaveClass('border-y'); } else { - expect(list).not.toHaveClass('border-y'); + await expect.element(list).not.toHaveClass('border-y'); } }); }); diff --git a/test/servers/helpers/DuplicatedServersModal.test.tsx b/test/servers/helpers/DuplicatedServersModal.test.tsx index 34e5d286..3246fa9e 100644 --- a/test/servers/helpers/DuplicatedServersModal.test.tsx +++ b/test/servers/helpers/DuplicatedServersModal.test.tsx @@ -1,5 +1,5 @@ -import { act, screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; +import { page } from 'vitest/browser'; import type { ServerData } from '../../../src/servers/data'; import { DuplicatedServersModal } from '../../../src/servers/helpers/DuplicatedServersModal'; import { checkAccessibility } from '../../__helpers__/accessibility'; @@ -9,10 +9,8 @@ describe('', () => { const onClose = vi.fn(); const onConfirm = vi.fn(); const setUp = (duplicatedServers: ServerData[] = []) => - act(() => - renderWithEvents( - , - ), + renderWithEvents( + , ); const mockServer = (data: Partial = {}) => fromPartial(data); @@ -24,9 +22,9 @@ describe('', () => { [[mockServer(), mockServer()], 2], [[mockServer(), mockServer(), mockServer()], 3], [[mockServer(), mockServer(), mockServer(), mockServer()], 4], - ])('renders expected amount of items', async (duplicatedServers, expectedItems) => { - await setUp(duplicatedServers); - expect(screen.queryAllByRole('listitem')).toHaveLength(expectedItems); + ])('renders expected amount of items', (duplicatedServers, expectedItems) => { + setUp(duplicatedServers); + expect(page.getByRole('listitem')).toHaveLength(expectedItems); }); it.each([ @@ -51,13 +49,13 @@ describe('', () => { }, ], ])('renders expected texts based on amount of servers', async (duplicatedServers, assertions) => { - await setUp(duplicatedServers); + setUp(duplicatedServers); - expect(screen.getByRole('heading')).toHaveTextContent(assertions.header); - expect(screen.getByText(assertions.firstParagraph)).toBeInTheDocument(); - expect(screen.getByText(assertions.lastParagraph)).toBeInTheDocument(); - expect(screen.getByRole('button', { name: assertions.discardBtn })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: assertions.confirmButton })).toBeInTheDocument(); + await expect.element(page.getByRole('heading')).toHaveTextContent(assertions.header); + await expect.element(page.getByText(assertions.firstParagraph)).toBeInTheDocument(); + await expect.element(page.getByText(assertions.lastParagraph)).toBeInTheDocument(); + await expect.element(page.getByRole('button', { name: assertions.discardBtn })).toBeInTheDocument(); + await expect.element(page.getByRole('button', { name: assertions.confirmButton })).toBeInTheDocument(); }); it.each([ @@ -65,37 +63,42 @@ describe('', () => { [[mockServer({ url: 'url', apiKey: 'apiKey' })]], [[mockServer({ url: 'url_1', apiKey: 'apiKey_1' }), mockServer({ url: 'url_2', apiKey: 'apiKey_2' })]], ])('displays provided server data', async (duplicatedServers) => { - await setUp(duplicatedServers); + setUp(duplicatedServers); if (duplicatedServers.length === 0) { - expect(screen.queryByRole('listitem')).not.toBeInTheDocument(); + await expect.element(page.getByRole('listitem')).not.toBeInTheDocument(); } else if (duplicatedServers.length === 1) { - const [firstItem, secondItem] = screen.getAllByRole('listitem'); + const [firstItem, secondItem] = page.getByRole('listitem').elements(); - expect(firstItem).toHaveTextContent(`URL: ${duplicatedServers[0].url}`); - expect(secondItem).toHaveTextContent(`API key: ${duplicatedServers[0].apiKey}`); + await expect.element(firstItem).toHaveTextContent(`URL: ${duplicatedServers[0].url}`); + await expect.element(secondItem).toHaveTextContent(`API key: ${duplicatedServers[0].apiKey}`); } else { expect.assertions(duplicatedServers.length); - screen.getAllByRole('listitem').forEach((item, index) => { - const server = duplicatedServers[index]; - expect(item).toHaveTextContent(`${server.url} - ${server.apiKey}`); - }); + await Promise.all( + page + .getByRole('listitem') + .elements() + .map((item, index) => { + const server = duplicatedServers[index]; + return expect.element(item).toHaveTextContent(`${server.url} - ${server.apiKey}`); + }), + ); } }); it('invokes onClose when appropriate button is clicked', async () => { - const { user } = await setUp(); + const { user } = setUp(); expect(onClose).not.toHaveBeenCalled(); - await user.click(screen.getByRole('button', { name: 'Discard' })); + await user.click(page.getByRole('button', { name: 'Discard' })); expect(onClose).toHaveBeenCalled(); }); it('invokes onConfirm when appropriate button is clicked', async () => { - const { user } = await setUp(); + const { user } = setUp(); expect(onConfirm).not.toHaveBeenCalled(); - await user.click(screen.getByRole('button', { name: 'Save duplicate' })); + await user.click(page.getByRole('button', { name: 'Save duplicate' })); expect(onConfirm).toHaveBeenCalled(); }); }); diff --git a/test/servers/helpers/ImportServersBtn.test.tsx b/test/servers/helpers/ImportServersBtn.test.tsx index e9303fd6..044f08de 100644 --- a/test/servers/helpers/ImportServersBtn.test.tsx +++ b/test/servers/helpers/ImportServersBtn.test.tsx @@ -1,5 +1,5 @@ -import { screen, waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; +import { page } from 'vitest/browser'; import type { ServerData, ServersMap, ServerWithId } from '../../../src/servers/data'; import type { ImportServersBtnProps } from '../../../src/servers/helpers/ImportServersBtn'; import { ImportServersBtn } from '../../../src/servers/helpers/ImportServersBtn'; @@ -22,35 +22,33 @@ describe('', () => { it('shows tooltip on button hover', async () => { const { user } = setUp(); - expect(screen.queryByText(/^You can create servers by importing a CSV file/)).not.toBeInTheDocument(); - await user.hover(screen.getByRole('button')); - await waitFor(() => - expect(screen.getByText(/^You can create servers by importing a CSV file/)).toBeInTheDocument(), - ); + await expect.element(page.getByText('You can create servers by importing a CSV file')).not.toBeInTheDocument(); + await user.hover(page.getByRole('button')); + await expect.element(page.getByText('You can create servers by importing a CSV file')).toBeInTheDocument(); }); it.each([ [undefined, ''], ['foo', 'foo'], ['bar', 'bar'], - ])('allows a class name to be provided', (providedClassName, expectedClassName) => { + ])('allows a class name to be provided', async (providedClassName, expectedClassName) => { setUp({ className: providedClassName }); - expect(screen.getByRole('button')).toHaveAttribute('class', expect.stringContaining(expectedClassName)); + await expect.element(page.getByRole('button')).toHaveAttribute('class', expect.stringContaining(expectedClassName)); }); it.each([ [undefined, 'Import from file'], ['foo', 'foo'], ['bar', 'bar'], - ])('has expected text', (children, expectedText) => { + ])('has expected text', async (children, expectedText) => { setUp({ children }); - expect(screen.getByRole('button')).toHaveTextContent(expectedText); + await expect.element(page.getByRole('button')).toHaveTextContent(expectedText); }); it('imports servers when file input changes', async () => { const { user } = setUp(); - await user.upload(screen.getByTestId('csv-file-input'), csvFile); + await user.upload(page.getByTestId('csv-file-input'), csvFile); expect(importServersFromFile).toHaveBeenCalledTimes(1); }); @@ -74,15 +72,15 @@ describe('', () => { importServersFromFile.mockResolvedValue([existingServerData, newServer]); - expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); - await user.upload(screen.getByTestId('csv-file-input'), csvFile); + await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); + await user.upload(page.getByTestId('csv-file-input'), csvFile); // Once the file is uploaded, non-duplicated servers are immediately created const { servers } = store.getState(); expect(Object.keys(servers)).toHaveLength(2); - expect(screen.getByRole('dialog')).toBeInTheDocument(); - await user.click(screen.getByRole('button', { name: btnName })); + await expect.element(page.getByRole('dialog')).toBeInTheDocument(); + await user.click(page.getByRole('button', { name: btnName })); // If duplicated servers are saved, there's one extra server creation if (savesDuplicatedServers) { diff --git a/test/servers/helpers/ServerError.test.tsx b/test/servers/helpers/ServerError.test.tsx index 3c4ebfaf..24ef3cd1 100644 --- a/test/servers/helpers/ServerError.test.tsx +++ b/test/servers/helpers/ServerError.test.tsx @@ -45,10 +45,10 @@ describe('', () => { notFound: ['Could not find this Shlink server.'], }, ], - ])('renders expected information based on provided server type', (selectedServer, { found, notFound }) => { + ])('renders expected information based on provided server type', async (selectedServer, { found, notFound }) => { setUp(selectedServer); - found.forEach((text) => expect(screen.getByText(text)).toBeInTheDocument()); - notFound.forEach((text) => expect(screen.queryByText(text)).not.toBeInTheDocument()); + await Promise.all(found.map((text) => expect.element(screen.getByText(text)).toBeInTheDocument())); + await Promise.all(notFound.map((text) => expect.element(screen.queryByText(text)).not.toBeInTheDocument())); }); }); diff --git a/test/servers/helpers/ServerForm.test.tsx b/test/servers/helpers/ServerForm.test.tsx index 1305c51d..30f76a25 100644 --- a/test/servers/helpers/ServerForm.test.tsx +++ b/test/servers/helpers/ServerForm.test.tsx @@ -1,4 +1,5 @@ -import { fireEvent, screen } from '@testing-library/react'; +import { fireEvent } from '@testing-library/react'; +import { page } from 'vitest/browser'; import { ServerForm } from '../../../src/servers/helpers/ServerForm'; import { checkAccessibility } from '../../__helpers__/accessibility'; import { renderWithEvents } from '../../__helpers__/setUpTest'; @@ -9,21 +10,21 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it('renders inputs', () => { + it('renders inputs', async () => { setUp(); - expect(screen.getByLabelText(/^Name/)).toBeInTheDocument(); - expect(screen.getByLabelText(/^URL/)).toBeInTheDocument(); - expect(screen.getByLabelText(/^API key/)).toBeInTheDocument(); - expect(screen.getByText('Something')).toBeInTheDocument(); - expect(screen.getByText('Advanced options')).toBeInTheDocument(); + await expect.element(page.getByLabelText(/^Name/)).toBeInTheDocument(); + await expect.element(page.getByLabelText(/^URL/)).toBeInTheDocument(); + await expect.element(page.getByLabelText(/^API key/)).toBeInTheDocument(); + await expect.element(page.getByText('Something')).toBeInTheDocument(); + await expect.element(page.getByText('Advanced options')).toBeInTheDocument(); }); it('invokes submit callback when submit event is triggered', async () => { setUp(); expect(onSubmit).not.toHaveBeenCalled(); - fireEvent.submit(screen.getByRole('form'), { preventDefault: vi.fn() }); + fireEvent.submit(page.getByTestId('server-form').element(), { preventDefault: vi.fn() }); expect(onSubmit).toHaveBeenCalled(); }); @@ -31,8 +32,8 @@ describe('', () => { const { user } = setUp(); const forwardCredentialsLabel = 'Forward credentials to this server on every request.'; - expect(screen.queryByLabelText(forwardCredentialsLabel)).not.toBeInTheDocument(); - await user.click(screen.getByText('Advanced options')); - expect(screen.getByLabelText(forwardCredentialsLabel)).toBeInTheDocument(); + await expect.element(page.getByLabelText(forwardCredentialsLabel)).not.toBeInTheDocument(); + await user.click(page.getByText('Advanced options')); + await expect.element(page.getByLabelText(forwardCredentialsLabel)).toBeInTheDocument(); }); }); diff --git a/vite.config.ts b/vite.config.ts index fc3e19e8..1b404501 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -39,6 +39,9 @@ export default defineConfig({ // Vitest config test: { + globals: true, + clearMocks: true, + // Run tests in an actual browser browser: { provider: playwright({ @@ -50,8 +53,6 @@ export default defineConfig({ screenshotFailures: false, instances: [{ browser: 'chromium' }], }, - globals: true, - setupFiles: './config/test/setupTests.ts', coverage: { provider: 'v8', reporter: ['text', 'text-summary', 'html', 'clover'], From 260bdb260c051f8655c5ef2871d6b1ed488b2451 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 16 Aug 2026 19:59:44 +0200 Subject: [PATCH 2/5] Replace direct rendering with react testing library by vitest-browser-react --- .github/dependabot.yml | 4 +-- package-lock.json | 35 +++++++++++++++++++++++++- package.json | 3 ++- test/__helpers__/setUpTest.tsx | 7 ++++-- test/common/NotFound.test.tsx | 14 +++++------ test/common/ScrollToTop.test.tsx | 4 +-- test/common/ShlinkVersions.test.tsx | 10 ++++---- test/common/img/ShlinkLogo.test.tsx | 6 ++--- test/container/context.test.tsx | 16 ++++++------ test/servers/CreateServer.test.tsx | 4 +-- test/servers/EditServer.test.tsx | 12 ++++----- test/servers/ManageServers.test.tsx | 13 +++++----- test/servers/ServersListGroup.test.tsx | 12 ++++----- 13 files changed, 86 insertions(+), 54 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2920eee3..0f708b7f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -27,9 +27,6 @@ updates: - 'react-dom' - '@types/react' - '@types/react-dom' - testing: - patterns: - - '@testing-library/*' vite: patterns: - 'vite' @@ -37,6 +34,7 @@ updates: vitest: patterns: - 'vitest' + - 'vitest-browser-react' - '@vitest/*' workbox: patterns: diff --git a/package-lock.json b/package-lock.json index 71c06cb9..65992a88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,7 +60,8 @@ "typescript": "^7.0.2", "vite": "^8.2.0", "vite-plugin-pwa": "^1.3.0", - "vitest": "^4.0.3" + "vitest": "^4.0.3", + "vitest-browser-react": "^2.2.0" } }, "node_modules/@babel/code-frame": { @@ -8801,6 +8802,31 @@ } } }, + "node_modules/vitest-browser-react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vitest-browser-react/-/vitest-browser-react-2.2.0.tgz", + "integrity": "sha512-oY3KM6305kwJMa6nHo92vVtkOsih7mjEf12dLKuphaF+9ywWPEc+qanIBd394SZ6m5LadVEaG6dicvvizOzmjA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "vitest": "^4.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -14519,6 +14545,13 @@ "why-is-node-running": "^2.3.0" } }, + "vitest-browser-react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vitest-browser-react/-/vitest-browser-react-2.2.0.tgz", + "integrity": "sha512-oY3KM6305kwJMa6nHo92vVtkOsih7mjEf12dLKuphaF+9ywWPEc+qanIBd394SZ6m5LadVEaG6dicvvizOzmjA==", + "dev": true, + "requires": {} + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", diff --git a/package.json b/package.json index d2a32829..72ea626d 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,8 @@ "typescript": "^7.0.2", "vite": "^8.2.0", "vite-plugin-pwa": "^1.3.0", - "vitest": "^4.0.3" + "vitest": "^4.0.3", + "vitest-browser-react": "^2.2.0" }, "browserslist": [ ">0.2%", diff --git a/test/__helpers__/setUpTest.tsx b/test/__helpers__/setUpTest.tsx index d04e7936..a292d7c7 100644 --- a/test/__helpers__/setUpTest.tsx +++ b/test/__helpers__/setUpTest.tsx @@ -1,17 +1,20 @@ import type { ShlinkApiClient } from '@shlinkio/shlink-js-sdk'; import type { RenderOptions } from '@testing-library/react'; -import { render } from '@testing-library/react'; +import { render as testingLibRender } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import type { PropsWithChildren, ReactElement } from 'react'; import { Provider } from 'react-redux'; +import { render as vitestRender } from 'vitest-browser-react'; import { userEvent } from 'vitest/browser'; import { ContainerProvider } from '../../src/container/context'; import type { RootState } from '../../src/store'; import { setUpStore } from '../../src/store'; +export const render = vitestRender; + export const renderWithEvents = (element: ReactElement, options?: RenderOptions) => ({ user: userEvent.setup(), - ...render(element, options), + ...testingLibRender(element, options), }); export type RenderOptionsWithState = Omit & { diff --git a/test/common/NotFound.test.tsx b/test/common/NotFound.test.tsx index 2a1e6a19..bb79d34f 100644 --- a/test/common/NotFound.test.tsx +++ b/test/common/NotFound.test.tsx @@ -1,7 +1,7 @@ -import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router'; import { NotFound } from '../../src/common/NotFound'; import { checkAccessibility } from '../__helpers__/accessibility'; +import { render } from '../__helpers__/setUpTest'; describe('', () => { const setUp = (props = {}) => @@ -14,15 +14,15 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('shows expected error title', async () => { - setUp(); - await expect.element(screen.getByText('Oops! We could not find requested route.')).toBeInTheDocument(); + const page = await setUp(); + await expect.element(page.getByText('Oops! We could not find requested route.')).toBeInTheDocument(); }); it('shows expected error message', async () => { - setUp(); + const page = await setUp(); await expect .element( - screen.getByText( + page.getByText( "Use your browser's back button to navigate to the page you have previously come from, or just press this button.", ), ) @@ -34,8 +34,8 @@ describe('', () => { [{ to: '/foo/bar', children: 'Hello' }, '/foo/bar', 'Hello'], [{ to: '/baz-bar', children: <>Foo }, '/baz-bar', 'Foo'], ])('shows expected link and text', async (props, expectedLink, expectedText) => { - setUp(props); - const link = screen.getByRole('link'); + const page = await setUp(props); + const link = page.getByRole('link'); await expect.element(link).toHaveAttribute('href', expectedLink); await expect.element(link).toHaveTextContent(expectedText); diff --git a/test/common/ScrollToTop.test.tsx b/test/common/ScrollToTop.test.tsx index 4b2eae1f..ec49f5dd 100644 --- a/test/common/ScrollToTop.test.tsx +++ b/test/common/ScrollToTop.test.tsx @@ -1,7 +1,7 @@ -import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router'; import { ScrollToTop } from '../../src/common/ScrollToTop'; import { checkAccessibility } from '../__helpers__/accessibility'; +import { render } from '../__helpers__/setUpTest.tsx'; describe('', () => { const setUp = (children = 'Foo') => @@ -14,7 +14,7 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it.each([['Foobar'], ['Barfoo'], ['Something']])('just renders children', async (children) => { - setUp(children); + const screen = await setUp(children); await expect.element(screen.getByText(children)).toBeInTheDocument(); }); }); diff --git a/test/common/ShlinkVersions.test.tsx b/test/common/ShlinkVersions.test.tsx index 9d7b5ec0..598a4d85 100644 --- a/test/common/ShlinkVersions.test.tsx +++ b/test/common/ShlinkVersions.test.tsx @@ -1,9 +1,9 @@ -import { render, screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import type { ShlinkVersionsProps } from '../../src/common/ShlinkVersions'; import { ShlinkVersions } from '../../src/common/ShlinkVersions'; import type { NonReachableServer, NotFoundServer, ReachableServer } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; +import { render } from '../__helpers__/setUpTest.tsx'; describe('', () => { const setUp = (props: ShlinkVersionsProps) => render(); @@ -20,8 +20,8 @@ describe('', () => { ])( 'displays expected versions when selected server is reachable', async (clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => { - setUp({ clientVersion, selectedServer }); - const [serverLink, clientLink] = screen.getAllByRole('link'); + const screen = await setUp({ clientVersion, selectedServer }); + const [serverLink, clientLink] = screen.getByRole('link').all(); await expect .element(serverLink) @@ -39,8 +39,8 @@ describe('', () => { ['1.2.3', fromPartial({ serverNotFound: true })], ['1.2.3', fromPartial({ serverNotReachable: true })], ])('displays only client version when selected server is not reachable', async (clientVersion, selectedServer) => { - setUp({ clientVersion, selectedServer }); - const links = screen.getAllByRole('link'); + const screen = await setUp({ clientVersion, selectedServer }); + const links = screen.getByRole('link').all(); expect(links).toHaveLength(1); await expect diff --git a/test/common/img/ShlinkLogo.test.tsx b/test/common/img/ShlinkLogo.test.tsx index ec982bdc..78c36442 100644 --- a/test/common/img/ShlinkLogo.test.tsx +++ b/test/common/img/ShlinkLogo.test.tsx @@ -1,8 +1,8 @@ import { brandColor } from '@shlinkio/shlink-frontend-kit'; -import { render } from '@testing-library/react'; import type { ShlinkLogoProps } from '../../../src/common/img/ShlinkLogo'; import { ShlinkLogo } from '../../../src/common/img/ShlinkLogo'; import { checkAccessibility } from '../../__helpers__/accessibility'; +import { render } from '../../__helpers__/setUpTest.tsx'; describe('', () => { const setUp = (props: ShlinkLogoProps = {}) => render(); @@ -14,7 +14,7 @@ describe('', () => { ['red', 'red'], ['white', 'white'], ])('renders expected color', async (color, expectedColor) => { - const { container } = setUp({ color }); + const { container } = await setUp({ color }); await expect.element(container.querySelector('g')).toHaveAttribute('fill', expectedColor); }); @@ -23,7 +23,7 @@ describe('', () => { ['foo', 'foo'], ['bar', 'bar'], ])('renders expected class', async (className, expectedClassName) => { - const { container } = setUp({ className }); + const { container } = await setUp({ className }); if (expectedClassName) { await expect.element(container.firstChild as HTMLElement).toHaveAttribute('class', expectedClassName); diff --git a/test/container/context.test.tsx b/test/container/context.test.tsx index 655124ec..51c74964 100644 --- a/test/container/context.test.tsx +++ b/test/container/context.test.tsx @@ -1,6 +1,6 @@ -import { render } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { ContainerProvider, useDependencies } from '../../src/container/context'; +import { render } from '../__helpers__/setUpTest.tsx'; describe('context', () => { describe('useDependencies', () => { @@ -12,24 +12,24 @@ describe('context', () => { return null; } - it('throws when used outside of ContainerProvider', () => { - expect(() => render()).toThrow( + it('throws when used outside of ContainerProvider', async () => { + await expect(() => render()).rejects.toThrow( 'You cannot use "useDependencies" outside of a ContainerProvider', ); }); - it('throws when requested dependency is not found in container', () => { - expect(() => + it('throws when requested dependency is not found in container', async () => { + await expect(() => render( , ), - ).toThrow('Dependency with name "foo" not found in container'); + ).rejects.toThrow('Dependency with name "foo" not found in container'); }); - it('gets dependency from container', () => { - render( + it('gets dependency from container', async () => { + await render( , diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index ded7a93a..754643cd 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; @@ -97,7 +97,7 @@ describe('', () => { fireEvent.submit(page.getByTestId('server-form').element()); - await waitFor(() => expect(page.getByRole('dialog')).toBeInTheDocument()); + await expect.element(page.getByRole('dialog')).toBeInTheDocument(); await user.click(page.getByRole('button', { name: 'Discard' })); expect(history.location.pathname).toEqual('/foo'); // Goes back to first route from history's initialEntries diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index fbe3d0ec..ea32f47e 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; @@ -86,12 +86,10 @@ describe('', () => { await user.click(page.getByLabelText('Forward credentials to this server on every request.')); fireEvent.submit(page.getByTestId('server-form').element()); - await waitFor(() => - expect(store.getState().servers[defaultSelectedServer.id]).toEqual( - expect.objectContaining({ - forwardCredentials: !forwardCredentials, - }), - ), + expect(store.getState().servers[defaultSelectedServer.id]).toEqual( + expect.objectContaining({ + forwardCredentials: !forwardCredentials, + }), ); }, ); diff --git a/test/servers/ManageServers.test.tsx b/test/servers/ManageServers.test.tsx index f87c341f..063f7b02 100644 --- a/test/servers/ManageServers.test.tsx +++ b/test/servers/ManageServers.test.tsx @@ -1,4 +1,3 @@ -import { waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; import { page } from 'vitest/browser'; @@ -44,25 +43,25 @@ describe('', () => { await user.type(page.getByPlaceholder('Search...'), searchTerm); }; // Add one for the header row - const expectRows = (amount: number) => expect(page.getByRole('row').elements()).toHaveLength(amount + 1); + const expectRows = (amount: number) => expect.poll(() => page.getByRole('row').elements()).toHaveLength(amount + 1); - expectRows(3); + await expectRows(3); await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); await search('foo'); - await waitFor(() => expectRows(1)); + await expectRows(1); await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); await search('Ba'); - await waitFor(() => expectRows(2)); + await expectRows(2); await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); await search('invalid'); - await waitFor(() => expectRows(1)); + await expectRows(1); await expect.element(page.getByText('No servers found.')).toBeInTheDocument(); }); - it.only.each([[createServerMock('foo')], [createServerMock('foo', true)]])( + it.each([[createServerMock('foo')], [createServerMock('foo', true)]])( 'shows different amount of columns if there are at least one auto-connect server', async (server) => { setUp({ server }); diff --git a/test/servers/ServersListGroup.test.tsx b/test/servers/ServersListGroup.test.tsx index 176497f9..5e993877 100644 --- a/test/servers/ServersListGroup.test.tsx +++ b/test/servers/ServersListGroup.test.tsx @@ -1,9 +1,9 @@ -import { render, screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; import type { ServerWithId } from '../../src/servers/data'; import { ServersListGroup } from '../../src/servers/ServersListGroup'; import { checkAccessibility } from '../__helpers__/accessibility'; +import { render } from '../__helpers__/setUpTest.tsx'; describe('', () => { const servers: ServerWithId[] = [fromPartial({ name: 'foo', id: '123' }), fromPartial({ name: 'bar', id: '456' })]; @@ -19,15 +19,15 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); - it.each([[servers], [[]]])('shows servers list', (servers) => { - setUp({ servers }); + it.each([[servers], [[]]])('shows servers list', async (servers) => { + const screen = await setUp({ servers }); - expect(screen.queryAllByTestId('list')).toHaveLength(servers.length ? 1 : 0); - expect(screen.queryAllByRole('link')).toHaveLength(servers.length); + expect(screen.getByTestId('list').elements()).toHaveLength(servers.length ? 1 : 0); + expect(screen.getByRole('link').elements()).toHaveLength(servers.length); }); it.each([[true], [false], [undefined]])('renders proper classes for embedded', async (borderless) => { - setUp({ servers, borderless }); + const screen = await setUp({ servers, borderless }); const list = screen.getByTestId('list'); if (!borderless) { From 7b9f084f9c1c6b89285f223dae54485d82cab3da Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 16 Aug 2026 20:32:05 +0200 Subject: [PATCH 3/5] Replace rendering with events with react testing library by vitest-browser-react --- src/servers/helpers/ServerForm.tsx | 2 +- test/__helpers__/setUpTest.tsx | 8 +++--- test/common/AppUpdateBanner.test.tsx | 7 +++--- test/common/ErrorHandler.test.tsx | 7 +++--- test/servers/CreateServer.test.tsx | 5 ++-- test/servers/EditServer.test.tsx | 11 +++----- .../helpers/DuplicatedServersModal.test.tsx | 13 +++++----- test/servers/helpers/ServerForm.test.tsx | 25 +++++++++++++------ 8 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/servers/helpers/ServerForm.tsx b/src/servers/helpers/ServerForm.tsx index 019f3ea7..a615aede 100644 --- a/src/servers/helpers/ServerForm.tsx +++ b/src/servers/helpers/ServerForm.tsx @@ -28,7 +28,7 @@ export const ServerForm: FC = ({ onSubmit, initialValues, child const handleSubmit = usePreventDefault(() => onSubmit({ name, url, apiKey, forwardCredentials })); return ( - + setName(e.target.value)} required /> setUrl(e.target.value)} required /> diff --git a/test/__helpers__/setUpTest.tsx b/test/__helpers__/setUpTest.tsx index a292d7c7..0b0972a5 100644 --- a/test/__helpers__/setUpTest.tsx +++ b/test/__helpers__/setUpTest.tsx @@ -4,6 +4,7 @@ import { render as testingLibRender } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import type { PropsWithChildren, ReactElement } from 'react'; import { Provider } from 'react-redux'; +import type { RenderOptions as VitestRenderOptions } from 'vitest-browser-react'; import { render as vitestRender } from 'vitest-browser-react'; import { userEvent } from 'vitest/browser'; import { ContainerProvider } from '../../src/container/context'; @@ -12,9 +13,9 @@ import { setUpStore } from '../../src/store'; export const render = vitestRender; -export const renderWithEvents = (element: ReactElement, options?: RenderOptions) => ({ +export const renderWithEvents = async (element: ReactElement, options?: VitestRenderOptions) => ({ user: userEvent.setup(), - ...testingLibRender(element, options), + ...(await render(element, options)), }); export type RenderOptionsWithState = Omit & { @@ -48,6 +49,7 @@ export const renderWithStore = ( return { store, - ...renderWithEvents(element, { ...options, wrapper: Wrapper }), + user: userEvent.setup(), + ...testingLibRender(element, { ...options, wrapper: Wrapper }), }; }; diff --git a/test/common/AppUpdateBanner.test.tsx b/test/common/AppUpdateBanner.test.tsx index a2e87c9c..3023cc08 100644 --- a/test/common/AppUpdateBanner.test.tsx +++ b/test/common/AppUpdateBanner.test.tsx @@ -1,4 +1,3 @@ -import { page } from 'vitest/browser'; import { AppUpdateBanner } from '../../src/common/AppUpdateBanner'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithEvents } from '../__helpers__/setUpTest'; @@ -11,7 +10,7 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders initial state', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByRole('heading')).toHaveTextContent('This app has just been updated!'); await expect.element(page.getByText('Restarting...')).not.toBeInTheDocument(); @@ -19,7 +18,7 @@ describe('', () => { }); it('invokes toggle when alert is closed', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); expect(onClose).not.toHaveBeenCalled(); await user.click(page.getByLabelText('Close')); @@ -27,7 +26,7 @@ describe('', () => { }); it('triggers the update when clicking the button', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); expect(forceUpdate).not.toHaveBeenCalled(); await user.click(page.getByText(/^Restart now/)); diff --git a/test/common/ErrorHandler.test.tsx b/test/common/ErrorHandler.test.tsx index 1218c0e1..e37606bc 100644 --- a/test/common/ErrorHandler.test.tsx +++ b/test/common/ErrorHandler.test.tsx @@ -1,6 +1,5 @@ import { fromPartial } from '@total-typescript/shoehorn'; import type { ReactNode } from 'react'; -import { page } from 'vitest/browser'; import { ErrorHandler } from '../../src/common/ErrorHandler'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithEvents } from '../__helpers__/setUpTest'; @@ -27,7 +26,7 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders children when no error has occurred', async () => { - setUp(Foo); + const page = await setUp(Foo); await expect.element(page.getByText('Foo')).toBeInTheDocument(); await expect.element(page.getByText('Oops! This is awkward :S')).not.toBeInTheDocument(); @@ -35,14 +34,14 @@ describe('', () => { }); it('renders error page when error has occurred', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByText('Oops! This is awkward :S')).toBeInTheDocument(); await expect.element(page.getByRole('button')).toBeInTheDocument(); }); it('reloads page on button click', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); expect(reload).not.toHaveBeenCalled(); await user.click(page.getByRole('button')); diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index 754643cd..425de37c 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -1,4 +1,3 @@ -import { fireEvent } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; @@ -74,7 +73,7 @@ describe('', () => { await user.type(page.getByLabelText(/^API key/), 'the_api_key'); expect(store.getState().servers[expectedServerId]).not.toBeDefined(); - fireEvent.submit(page.getByTestId('server-form').element()); + await user.click(page.getByRole('button', { name: 'Create server' })); expect(store.getState().servers[expectedServerId]).toEqual( expect.objectContaining({ id: expectedServerId, @@ -95,7 +94,7 @@ describe('', () => { await user.type(page.getByLabelText(/^URL/), 'https://existing_url.com'); await user.type(page.getByLabelText(/^API key/), 'existing_api_key'); - fireEvent.submit(page.getByTestId('server-form').element()); + await user.click(page.getByRole('button', { name: 'Create server' })); await expect.element(page.getByRole('dialog')).toBeInTheDocument(); await user.click(page.getByRole('button', { name: 'Discard' })); diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index ea32f47e..71b576d0 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -1,4 +1,3 @@ -import { fireEvent } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; @@ -13,7 +12,7 @@ describe('', () => { const defaultSelectedServer = fromPartial({ id: 'abc123', name: 'the_name', - url: 'the_url', + url: 'https://example.com', apiKey: 'the_api_key', }); const setUp = (selectedServer: SelectedServer = defaultSelectedServer) => { @@ -61,15 +60,11 @@ describe('', () => { const { user, history, store } = setUp(); await user.type(page.getByLabelText(/^Name/), ' edited'); - await user.type(page.getByLabelText(/^URL/), ' edited'); - // TODO Using fire event because userEvent.click on the Submit button does not submit the form - // await user.click(screen.getByRole('button', { name: 'Save' })); - fireEvent.submit(page.getByTestId('server-form').element()); + await user.click(page.getByRole('button', { name: 'Save' })); expect(store.getState().servers[defaultSelectedServer.id]).toEqual( expect.objectContaining({ name: 'the_name edited', - url: 'the_url edited', }), ); @@ -84,7 +79,7 @@ describe('', () => { await user.click(page.getByText('Advanced options')); await user.click(page.getByLabelText('Forward credentials to this server on every request.')); - fireEvent.submit(page.getByTestId('server-form').element()); + await user.click(page.getByRole('button', { name: 'Save' })); expect(store.getState().servers[defaultSelectedServer.id]).toEqual( expect.objectContaining({ diff --git a/test/servers/helpers/DuplicatedServersModal.test.tsx b/test/servers/helpers/DuplicatedServersModal.test.tsx index 3246fa9e..f111b4e3 100644 --- a/test/servers/helpers/DuplicatedServersModal.test.tsx +++ b/test/servers/helpers/DuplicatedServersModal.test.tsx @@ -1,5 +1,4 @@ import { fromPartial } from '@total-typescript/shoehorn'; -import { page } from 'vitest/browser'; import type { ServerData } from '../../../src/servers/data'; import { DuplicatedServersModal } from '../../../src/servers/helpers/DuplicatedServersModal'; import { checkAccessibility } from '../../__helpers__/accessibility'; @@ -22,8 +21,8 @@ describe('', () => { [[mockServer(), mockServer()], 2], [[mockServer(), mockServer(), mockServer()], 3], [[mockServer(), mockServer(), mockServer(), mockServer()], 4], - ])('renders expected amount of items', (duplicatedServers, expectedItems) => { - setUp(duplicatedServers); + ])('renders expected amount of items', async (duplicatedServers, expectedItems) => { + const page = await setUp(duplicatedServers); expect(page.getByRole('listitem')).toHaveLength(expectedItems); }); @@ -49,7 +48,7 @@ describe('', () => { }, ], ])('renders expected texts based on amount of servers', async (duplicatedServers, assertions) => { - setUp(duplicatedServers); + const page = await setUp(duplicatedServers); await expect.element(page.getByRole('heading')).toHaveTextContent(assertions.header); await expect.element(page.getByText(assertions.firstParagraph)).toBeInTheDocument(); @@ -63,7 +62,7 @@ describe('', () => { [[mockServer({ url: 'url', apiKey: 'apiKey' })]], [[mockServer({ url: 'url_1', apiKey: 'apiKey_1' }), mockServer({ url: 'url_2', apiKey: 'apiKey_2' })]], ])('displays provided server data', async (duplicatedServers) => { - setUp(duplicatedServers); + const page = await setUp(duplicatedServers); if (duplicatedServers.length === 0) { await expect.element(page.getByRole('listitem')).not.toBeInTheDocument(); @@ -87,7 +86,7 @@ describe('', () => { }); it('invokes onClose when appropriate button is clicked', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); expect(onClose).not.toHaveBeenCalled(); await user.click(page.getByRole('button', { name: 'Discard' })); @@ -95,7 +94,7 @@ describe('', () => { }); it('invokes onConfirm when appropriate button is clicked', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); expect(onConfirm).not.toHaveBeenCalled(); await user.click(page.getByRole('button', { name: 'Save duplicate' })); diff --git a/test/servers/helpers/ServerForm.test.tsx b/test/servers/helpers/ServerForm.test.tsx index 30f76a25..6356bf5e 100644 --- a/test/servers/helpers/ServerForm.test.tsx +++ b/test/servers/helpers/ServerForm.test.tsx @@ -1,17 +1,21 @@ -import { fireEvent } from '@testing-library/react'; -import { page } from 'vitest/browser'; import { ServerForm } from '../../../src/servers/helpers/ServerForm'; import { checkAccessibility } from '../../__helpers__/accessibility'; import { renderWithEvents } from '../../__helpers__/setUpTest'; describe('', () => { const onSubmit = vi.fn(); - const setUp = () => renderWithEvents(Something); + const setUp = () => + renderWithEvents( + + Something + + , + ); it('passes a11y checks', () => checkAccessibility(setUp())); it('renders inputs', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByLabelText(/^Name/)).toBeInTheDocument(); await expect.element(page.getByLabelText(/^URL/)).toBeInTheDocument(); @@ -21,15 +25,22 @@ describe('', () => { }); it('invokes submit callback when submit event is triggered', async () => { - setUp(); + const { user, ...page } = await setUp(); expect(onSubmit).not.toHaveBeenCalled(); - fireEvent.submit(page.getByTestId('server-form').element(), { preventDefault: vi.fn() }); + + // Fill required elements so the form can be submitted + await user.type(page.getByLabelText(/^Name/), 'The server'); + await user.type(page.getByLabelText(/^URL/), 'https://example.com'); + await user.type(page.getByLabelText(/^API key/), '123456'); + + await user.click(page.getByRole('button', { name: 'Submit' })); + expect(onSubmit).toHaveBeenCalled(); }); it('shows advanced options', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); const forwardCredentialsLabel = 'Forward credentials to this server on every request.'; await expect.element(page.getByLabelText(forwardCredentialsLabel)).not.toBeInTheDocument(); From c5c994a6039f6783cd89f082e541c0dee56e9acd Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 16 Aug 2026 20:55:48 +0200 Subject: [PATCH 4/5] Replace rendering with store with react testing library by vitest-browser-react --- package-lock.json | 312 ------------------ package.json | 1 - test/__helpers__/setUpTest.tsx | 11 +- test/app/App.test.tsx | 50 ++- test/common/Home.test.tsx | 5 +- test/common/MainHeader.test.tsx | 5 +- test/common/ShlinkVersionsContainer.test.tsx | 2 +- .../ShlinkWebComponentContainer.test.tsx | 7 +- test/servers/CreateServer.test.tsx | 19 +- test/servers/DeleteServerButton.test.tsx | 11 +- test/servers/DeleteServerModal.test.tsx | 30 +- test/servers/EditServer.test.tsx | 19 +- test/servers/ManageServers.test.tsx | 13 +- test/servers/ManageServersRow.test.tsx | 14 +- .../servers/ManageServersRowDropdown.test.tsx | 26 +- test/servers/ServersDropdown.test.tsx | 13 +- .../servers/helpers/ImportServersBtn.test.tsx | 11 +- test/servers/helpers/ServerError.test.tsx | 5 +- 18 files changed, 113 insertions(+), 441 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65992a88..b3efccb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "devDependencies": { "@shlinkio/eslint-config-js-coding-standard": "~4.1.0", "@tailwindcss/vite": "^4.3.3", - "@testing-library/react": "^16.3.2", "@total-typescript/shoehorn": "^0.1.2", "@types/node": "^26.1.2", "@types/react": "^19.2.18", @@ -3647,70 +3646,6 @@ "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@testing-library/react": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", - "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0 || ^19.0.0", - "@types/react-dom": "^18.0.0 || ^19.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@total-typescript/shoehorn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@total-typescript/shoehorn/-/shoehorn-0.1.2.tgz", @@ -3732,13 +3667,6 @@ "node": ">=12" } }, - "node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", - "dev": true, - "peer": true - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -4412,41 +4340,6 @@ "node": ">=14.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -4767,26 +4660,6 @@ "node": ">=6" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "peer": true - }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -5130,16 +5003,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -5148,12 +5011,6 @@ "node": ">=8" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.13", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -6654,16 +6511,6 @@ "node": "20 || >=22" } }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "peer": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -7097,38 +6944,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -11350,43 +11165,6 @@ "tailwindcss": "4.3.3" } }, - "@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, - "peer": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@testing-library/react": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", - "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5" - } - }, "@total-typescript/shoehorn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@total-typescript/shoehorn/-/shoehorn-0.1.2.tgz", @@ -11405,13 +11183,6 @@ "string.prototype.matchall": "^4.0.12" } }, - "@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", - "dev": true, - "peer": true - }, "@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -11807,31 +11578,6 @@ "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", "dev": true }, - "ansi-regex": { - "version": "5.0.1", - "dev": true, - "peer": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "peer": true, - "requires": { - "dequal": "^2.0.3" - } - }, "array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -12044,23 +11790,6 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "peer": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "peer": true - }, "common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -12290,23 +12019,11 @@ "object-keys": "^1.1.1" } }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "peer": true - }, "detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" }, - "dom-accessibility-api": { - "version": "0.5.13", - "dev": true, - "peer": true - }, "dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -13251,13 +12968,6 @@ "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", "dev": true }, - "lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "peer": true - }, "magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -13522,28 +13232,6 @@ "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "dev": true }, - "pretty-format": { - "version": "27.5.1", - "dev": true, - "peer": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true, - "peer": true - }, - "react-is": { - "version": "17.0.2", - "dev": true, - "peer": true - } - } - }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 72ea626d..d0376356 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "devDependencies": { "@shlinkio/eslint-config-js-coding-standard": "~4.1.0", "@tailwindcss/vite": "^4.3.3", - "@testing-library/react": "^16.3.2", "@total-typescript/shoehorn": "^0.1.2", "@types/node": "^26.1.2", "@types/react": "^19.2.18", diff --git a/test/__helpers__/setUpTest.tsx b/test/__helpers__/setUpTest.tsx index 0b0972a5..ff4f2760 100644 --- a/test/__helpers__/setUpTest.tsx +++ b/test/__helpers__/setUpTest.tsx @@ -1,10 +1,8 @@ import type { ShlinkApiClient } from '@shlinkio/shlink-js-sdk'; -import type { RenderOptions } from '@testing-library/react'; -import { render as testingLibRender } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import type { PropsWithChildren, ReactElement } from 'react'; import { Provider } from 'react-redux'; -import type { RenderOptions as VitestRenderOptions } from 'vitest-browser-react'; +import type { RenderOptions } from 'vitest-browser-react'; import { render as vitestRender } from 'vitest-browser-react'; import { userEvent } from 'vitest/browser'; import { ContainerProvider } from '../../src/container/context'; @@ -13,7 +11,7 @@ import { setUpStore } from '../../src/store'; export const render = vitestRender; -export const renderWithEvents = async (element: ReactElement, options?: VitestRenderOptions) => ({ +export const renderWithEvents = async (element: ReactElement, options?: RenderOptions) => ({ user: userEvent.setup(), ...(await render(element, options)), }); @@ -36,7 +34,7 @@ export type RenderOptionsWithState = Omit & { * Render provided ReactElement wrapped in a redux `Provider` and a `ContainerProvider` with a single * `buildShlinkApiClient` dependency. */ -export const renderWithStore = ( +export const renderWithStore = async ( element: ReactElement, { initialState = {}, buildShlinkApiClient = vi.fn(), ...options }: RenderOptionsWithState = {}, ) => { @@ -49,7 +47,6 @@ export const renderWithStore = ( return { store, - user: userEvent.setup(), - ...testingLibRender(element, { ...options, wrapper: Wrapper }), + ...(await renderWithEvents(element, { ...options, wrapper: Wrapper })), }; }; diff --git a/test/app/App.test.tsx b/test/app/App.test.tsx index e75a2858..33e4ca2f 100644 --- a/test/app/App.test.tsx +++ b/test/app/App.test.tsx @@ -1,8 +1,6 @@ import type { HttpClient } from '@shlinkio/shlink-js-sdk'; import { fromPartial } from '@total-typescript/shoehorn'; -import { act } from 'react'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; import { App } from '../../src/app/App'; import { ContainerProvider } from '../../src/container/context'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -13,31 +11,29 @@ vi.mock(import('../../src/common/ShlinkWebComponentContainer'), () => ({ })); describe('', () => { - const setUp = async (activeRoute = '/') => - act(() => - renderWithStore( - - ({}), - buildShlinkApiClient: vi.fn(), - useTimeoutToggle: vi.fn().mockReturnValue([false, vi.fn()]), - })} - > - - - , - { - initialState: { - servers: { - abc123: fromPartial({ id: 'abc123', name: 'abc123 server' }), - def456: fromPartial({ id: 'def456', name: 'def456 server' }), - }, - settings: fromPartial({}), - appUpdated: false, + const setUp = (activeRoute = '/') => + renderWithStore( + + ({}), + buildShlinkApiClient: vi.fn(), + useTimeoutToggle: vi.fn().mockReturnValue([false, vi.fn()]), + })} + > + + + , + { + initialState: { + servers: { + abc123: fromPartial({ id: 'abc123', name: 'abc123 server' }), + def456: fromPartial({ id: 'def456', name: 'def456 server' }), }, + settings: fromPartial({}), + appUpdated: false, }, - ), + }, ); it('passes a11y checks', () => checkAccessibility(setUp())); @@ -53,7 +49,7 @@ describe('', () => { ['/server/def456/bar', 'ShlinkWebComponentContainer'], ['/other', 'Oops! We could not find requested route.'], ])('renders expected route', async (activeRoute, expectedComponent) => { - await setUp(activeRoute); + const page = await setUp(activeRoute); await expect.element(page.getByText(expectedComponent)).toBeInTheDocument(); }); @@ -62,7 +58,7 @@ describe('', () => { ['/bar', false], ['/', true], ])('renders expected classes on shlink-wrapper based on current pathname', async (pathname, isFlex) => { - await setUp(pathname); + const page = await setUp(pathname); const shlinkWrapper = page.getByTestId('shlink-wrapper'); if (isFlex) { diff --git a/test/common/Home.test.tsx b/test/common/Home.test.tsx index 30fd5b76..50be0aba 100644 --- a/test/common/Home.test.tsx +++ b/test/common/Home.test.tsx @@ -1,6 +1,5 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; import { Home } from '../../src/common/Home'; import type { ServersMap, ServerWithId } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -21,7 +20,7 @@ describe('', () => { checkAccessibility(setUp({ '1a': fromPartial({ name: 'foo', id: '1' }) }))); it('renders title', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByRole('heading', { name: 'Welcome!' })).toBeInTheDocument(); }); @@ -36,7 +35,7 @@ describe('', () => { ], [{}, 2], ])('shows link to create or set-up server only when no servers exist', async (servers, expectedServers) => { - setUp(servers); + const page = await setUp(servers); const links = page.getByRole('link').elements(); expect(links).toHaveLength(expectedServers); diff --git a/test/common/MainHeader.test.tsx b/test/common/MainHeader.test.tsx index 06567e2d..85567ec8 100644 --- a/test/common/MainHeader.test.tsx +++ b/test/common/MainHeader.test.tsx @@ -1,6 +1,5 @@ import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; -import { page } from 'vitest/browser'; import { MainHeader } from '../../src/common/MainHeader'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithStore } from '../__helpers__/setUpTest'; @@ -20,7 +19,7 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders ServersDropdown', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByRole('button', { name: 'Servers' })).toBeInTheDocument(); }); @@ -31,7 +30,7 @@ describe('', () => { ['/settings/foo', true], ['/settings/bar', true], ])('sets link to settings as active only when current path is settings', async (currentPath, isActive) => { - setUp(currentPath); + const page = await setUp(currentPath); await expect .element(page.getByRole('menuitem', { name: /Settings$/ })) .toHaveAttribute('data-active', isActive ? 'true' : 'false'); diff --git a/test/common/ShlinkVersionsContainer.test.tsx b/test/common/ShlinkVersionsContainer.test.tsx index 6d5f0dc2..7809335e 100644 --- a/test/common/ShlinkVersionsContainer.test.tsx +++ b/test/common/ShlinkVersionsContainer.test.tsx @@ -21,7 +21,7 @@ describe('', () => { [fromPartial({}), false], [fromPartial({ version: '1.0.0' }), true], ])('renders proper col classes based on sidebar status', async (selectedServer, shouldAddMargin) => { - const { container } = setUp(selectedServer); + const { container } = await setUp(selectedServer); if (shouldAddMargin) { await expect.element(container.firstChild as HTMLElement).toHaveClass('md:ml-(--aside-menu-width)'); diff --git a/test/common/ShlinkWebComponentContainer.test.tsx b/test/common/ShlinkWebComponentContainer.test.tsx index 145d9500..c2c8a9af 100644 --- a/test/common/ShlinkWebComponentContainer.test.tsx +++ b/test/common/ShlinkWebComponentContainer.test.tsx @@ -1,6 +1,5 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; import { ShlinkWebComponentContainer } from '../../src/common/ShlinkWebComponentContainer'; import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -26,7 +25,7 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp(fromPartial({ version: '3.0.0' })))); it('shows loading indicator while loading server', async () => { - setUp(null); + const page = await setUp(null); await expect.element(page.getByText('Loading...')).toBeInTheDocument(); await expect.element(page.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); @@ -39,7 +38,7 @@ describe('', () => { /Could not connect to this Shlink server/, ], ])('shows error for non reachable servers', async (selectedServer, expectedError) => { - setUp(selectedServer); + const page = await setUp(selectedServer); await expect.element(page.getByText('Loading...')).not.toBeInTheDocument(); await expect.element(page.getByText(expectedError)).toBeInTheDocument(); @@ -47,7 +46,7 @@ describe('', () => { }); it('renders ShlinkWebComponent for reachable servers', async () => { - setUp(fromPartial({ version: '3.0.0' })); + const page = await setUp(fromPartial({ version: '3.0.0' })); await expect.element(page.getByText('Loading...')).not.toBeInTheDocument(); await expect.element(page.getByText('ShlinkWebComponent')).toBeInTheDocument(); diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index 425de37c..ccf49abf 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -1,7 +1,6 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; -import { page } from 'vitest/browser'; import { CreateServer } from '../../src/servers/CreateServer'; import type { ServersMap } from '../../src/servers/data'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -17,7 +16,11 @@ describe('', () => { const defaultServers: ServersMap = { foo: fromPartial({ url: 'https://existing_url.com', apiKey: 'existing_api_key', id: 'foo' }), }; - const setUp = ({ serversImported = false, importFailed = false, servers = defaultServers }: SetUpOptions = {}) => { + const setUp = async ({ + serversImported = false, + importFailed = false, + servers = defaultServers, + }: SetUpOptions = {}) => { let callCount = 0; const useTimeoutToggle = vi.fn().mockImplementation(() => { const result = [callCount % 2 === 0 ? serversImported : importFailed, () => null]; @@ -28,21 +31,21 @@ describe('', () => { return { history, - ...renderWithStore( + ...(await renderWithStore( , { initialState: { servers }, }, - ), + )), }; }; it('passes a11y checks', () => checkAccessibility(setUp())); it('shows success message when imported is true', async () => { - setUp({ serversImported: true }); + const page = await setUp({ serversImported: true }); await expect .element(page.getByText('Servers properly imported. You can now select one from the list :)')) @@ -54,7 +57,7 @@ describe('', () => { }); it('shows error message when import failed', async () => { - setUp({ importFailed: true }); + const page = await setUp({ importFailed: true }); await expect .element(page.getByText('Servers properly imported. You can now select one from the list :)')) @@ -65,7 +68,7 @@ describe('', () => { }); it('creates server data when form is submitted', async () => { - const { user, history, store } = setUp(); + const { user, history, store, ...page } = await setUp(); const expectedServerId = 'the_name-the_url.com'; await user.type(page.getByLabelText(/^Name/), 'the_name'); @@ -88,7 +91,7 @@ describe('', () => { }); it('displays dialog when trying to create a duplicated server', async () => { - const { user, history } = setUp(); + const { user, history, ...page } = await setUp(); await user.type(page.getByLabelText(/^Name/), 'the_name'); await user.type(page.getByLabelText(/^URL/), 'https://existing_url.com'); diff --git a/test/servers/DeleteServerButton.test.tsx b/test/servers/DeleteServerButton.test.tsx index ad832d33..1b47e7ce 100644 --- a/test/servers/DeleteServerButton.test.tsx +++ b/test/servers/DeleteServerButton.test.tsx @@ -2,15 +2,14 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import type { ReactNode } from 'react'; import { Router } from 'react-router'; -import { page } from 'vitest/browser'; import { DeleteServerButton } from '../../src/servers/DeleteServerButton'; import { checkAccessibility } from '../__helpers__/accessibility'; import { renderWithStore } from '../__helpers__/setUpTest'; describe('', () => { - const setUp = (children: ReactNode = 'Remove this server') => { + const setUp = async (children: ReactNode = 'Remove this server') => { const history = createMemoryHistory({ initialEntries: ['/foo'] }); - const result = renderWithStore( + const result = await renderWithStore( {children} , @@ -22,14 +21,14 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp('Delete me'))); it.each([['Foo bar'], ['baz'], ['something']])('renders expected content', async (children) => { - const { container } = setUp(children); + const { container } = await setUp(children); expect(container.firstChild).toBeTruthy(); expect(container.firstChild).toMatchSnapshot(); }); it('displays modal when button is clicked', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); await expect.element(page.getByText(/Are you sure you want to remove/)).not.toBeInTheDocument(); await user.click(page.getByText('Remove this server')); @@ -37,7 +36,7 @@ describe('', () => { }); it('navigates to home when deletion is confirmed', async () => { - const { user, history } = setUp(); + const { user, history, ...page } = await setUp(); // Open modal await user.click(page.getByText('Remove this server')); diff --git a/test/servers/DeleteServerModal.test.tsx b/test/servers/DeleteServerModal.test.tsx index 425ceaa8..03abc5cc 100644 --- a/test/servers/DeleteServerModal.test.tsx +++ b/test/servers/DeleteServerModal.test.tsx @@ -1,5 +1,5 @@ import { fromPartial } from '@total-typescript/shoehorn'; -import { page } from 'vitest/browser'; +import type { RenderResult } from 'vitest-browser-react'; import type { ServerWithId } from '../../src/servers/data'; import { DeleteServerModal } from '../../src/servers/DeleteServerModal'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -19,35 +19,35 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders a modal window', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByRole('dialog')).toBeInTheDocument(); await expect.element(page.getByRole('heading')).toHaveTextContent('Remove server'); }); it('displays the name of the server as part of the content', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByText(/^Are you sure you want to remove/)).toBeInTheDocument(); await expect.element(page.getByText(serverName)).toBeInTheDocument(); }); - it.each([[() => page.getByRole('button', { name: 'Cancel' })], [() => page.getByLabelText('Close dialog')]])( - 'closes dialog when clicking cancel button', - async (getButton) => { - const { user, store } = setUp(); + it.each([ + [(page: RenderResult) => page.getByRole('button', { name: 'Cancel' })], + [(page: RenderResult) => page.getByLabelText('Close dialog')], + ])('closes dialog when clicking cancel button', async (getButton) => { + const { user, store, ...page } = await setUp(); - await expect.element(page.getByRole('dialog')).toBeInTheDocument(); - await user.click(getButton()); - await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); + await expect.element(page.getByRole('dialog')).toBeInTheDocument(); + await user.click(getButton(page)); + await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); - // No server has been deleted - expect(Object.keys(store.getState().servers)).toHaveLength(1); - }, - ); + // No server has been deleted + expect(Object.keys(store.getState().servers)).toHaveLength(1); + }); it('deletes server when clicking accept button', async () => { - const { user, store } = setUp(); + const { user, store, ...page } = await setUp(); expect(Object.keys(store.getState().servers)).toHaveLength(1); await user.click(page.getByRole('button', { name: 'Delete' })); diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index 71b576d0..dfc515b4 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -1,7 +1,6 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router'; -import { page } from 'vitest/browser'; import type { ReachableServer, SelectedServer } from '../../src/servers/data'; import { isServerWithId } from '../../src/servers/data'; import { EditServer } from '../../src/servers/EditServer'; @@ -15,11 +14,11 @@ describe('', () => { url: 'https://example.com', apiKey: 'the_api_key', }); - const setUp = (selectedServer: SelectedServer = defaultSelectedServer) => { + const setUp = async (selectedServer: SelectedServer = defaultSelectedServer) => { const history = createMemoryHistory({ initialEntries: ['/foo', '/bar'] }); return { history, - ...renderWithStore( + ...(await renderWithStore( , @@ -29,27 +28,27 @@ describe('', () => { servers: isServerWithId(selectedServer) ? { [selectedServer.id]: selectedServer } : {}, }, }, - ), + )), }; }; it('passes a11y checks', () => checkAccessibility(setUp())); it('renders nothing if selected server is not reachable', async () => { - setUp(fromPartial({})); + const page = await setUp(fromPartial({})); await expect.element(page.getByText('Edit')).not.toBeInTheDocument(); await expect.element(page.getByText('Cancel')).not.toBeInTheDocument(); await expect.element(page.getByText('Save')).not.toBeInTheDocument(); }); - it('renders server title', () => { - setUp(); + it('renders server title', async () => { + const page = await setUp(); expect(page.getByText(`Edit "${defaultSelectedServer.name}"`)).toBeInTheDocument(); }); it('display the server info in the form components', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByLabelText(/^Name/)).toBeInTheDocument(); await expect.element(page.getByLabelText(/^URL/)).toBeInTheDocument(); @@ -57,7 +56,7 @@ describe('', () => { }); it('edits server and redirects to it when form is submitted', async () => { - const { user, history, store } = setUp(); + const { user, history, store, ...page } = await setUp(); await user.type(page.getByLabelText(/^Name/), ' edited'); await user.click(page.getByRole('button', { name: 'Save' })); @@ -75,7 +74,7 @@ describe('', () => { it.each([{ forwardCredentials: true }, { forwardCredentials: false }])( 'edits advanced options - forward credentials', async ({ forwardCredentials }) => { - const { user, store } = setUp({ ...defaultSelectedServer, forwardCredentials }); + const { user, store, ...page } = await setUp({ ...defaultSelectedServer, forwardCredentials }); await user.click(page.getByText('Advanced options')); await user.click(page.getByLabelText('Forward credentials to this server on every request.')); diff --git a/test/servers/ManageServers.test.tsx b/test/servers/ManageServers.test.tsx index 063f7b02..66db2ab1 100644 --- a/test/servers/ManageServers.test.tsx +++ b/test/servers/ManageServers.test.tsx @@ -1,6 +1,5 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; import type { ServersMap, ServerWithId } from '../../src/servers/data'; import { ManageServers } from '../../src/servers/ManageServers'; import type { ServersExporter } from '../../src/servers/services/ServersExporter'; @@ -33,7 +32,7 @@ describe('', () => { )); it('shows search field which allows searching servers, affecting te amount of rendered rows', async () => { - const { user } = setUp({ + const { user, ...page } = await setUp({ foo: createServerMock('foo'), bar: createServerMock('bar'), baz: createServerMock('baz'), @@ -64,7 +63,7 @@ describe('', () => { it.each([[createServerMock('foo')], [createServerMock('foo', true)]])( 'shows different amount of columns if there are at least one auto-connect server', async (server) => { - setUp({ server }); + const page = await setUp({ server }); if (server.autoConnect) { await expect.element(page.getByTestId('auto-connect')).toBeInTheDocument(); @@ -77,13 +76,13 @@ describe('', () => { it.each([ [{}, 0], [{ foo: createServerMock('foo') }, 1], - ])('shows export button if the list of servers is not empty', (servers, expectedButtons) => { - setUp(servers); + ])('shows export button if the list of servers is not empty', async (servers, expectedButtons) => { + const page = await setUp(servers); expect(page.getByRole('button', { name: 'Export servers' }).elements()).toHaveLength(expectedButtons); }); it('allows exporting servers when clicking on button', async () => { - const { user } = setUp({ foo: createServerMock('foo') }); + const { user, ...page } = await setUp({ foo: createServerMock('foo') }); expect(exportServers).not.toHaveBeenCalled(); await user.click(page.getByRole('button', { name: 'Export servers' })); @@ -93,7 +92,7 @@ describe('', () => { it.each([[true], [false]])('shows an error message if an error occurs while importing servers', async (hasError) => { useTimeoutToggle.mockReturnValue([hasError, vi.fn()]); - setUp({ foo: createServerMock('foo') }); + const page = await setUp({ foo: createServerMock('foo') }); if (hasError) { await expect diff --git a/test/servers/ManageServersRow.test.tsx b/test/servers/ManageServersRow.test.tsx index 4d984ec2..0ad92848 100644 --- a/test/servers/ManageServersRow.test.tsx +++ b/test/servers/ManageServersRow.test.tsx @@ -1,6 +1,5 @@ import { Table } from '@shlinkio/shlink-frontend-kit'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; import type { ServerWithId } from '../../src/servers/data'; import { ManageServersRow } from '../../src/servers/ManageServersRow'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -27,24 +26,23 @@ describe('', () => { it.each([ [true, 4], [false, 3], - ])('renders expected amount of columns', (hasAutoConnect, expectedCols) => { - setUp(hasAutoConnect); + ])('renders expected amount of columns', async (hasAutoConnect, expectedCols) => { + const page = await setUp(hasAutoConnect); expect(page.getByRole('cell').elements()).toHaveLength(expectedCols); }); it('renders an options dropdown', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByRole('button', { name: 'Options' })).toBeInTheDocument(); }); - it.each([[true], [false]])('renders auto-connect icon only if server is autoConnect', (autoConnect) => { - const { container } = setUp(true, autoConnect); + it.each([[true], [false]])('renders auto-connect icon only if server is autoConnect', async (autoConnect) => { + const { container } = await setUp(true, autoConnect); expect(container).toMatchSnapshot(); }); it('renders server props where appropriate', async () => { - setUp(); - + const page = await setUp(); const link = page.getByRole('link'); await expect.element(link).toHaveAttribute('href', `/server/${server.id}`); diff --git a/test/servers/ManageServersRowDropdown.test.tsx b/test/servers/ManageServersRowDropdown.test.tsx index 74607349..8b24e54d 100644 --- a/test/servers/ManageServersRowDropdown.test.tsx +++ b/test/servers/ManageServersRowDropdown.test.tsx @@ -1,6 +1,6 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; +import type { RenderResult } from 'vitest-browser-react'; import type { UserEvent } from 'vitest/browser'; import type { ServerWithId } from '../../src/servers/data'; import { ManageServersRowDropdown } from '../../src/servers/ManageServersRowDropdown'; @@ -21,21 +21,21 @@ describe('', () => { }, ); }; - const toggleDropdown = (user: UserEvent) => user.click(page.getByRole('button')); + const toggleDropdown = (user: UserEvent, page: RenderResult) => user.click(page.getByRole('button')); it('passes a11y checks', async () => { - const { user, container } = setUp(); + const { user, ...page } = await setUp(); // Open menu - await toggleDropdown(user); + await toggleDropdown(user, page); - return checkAccessibility({ container }); + return checkAccessibility(page); }); it('renders expected amount of dropdown items', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); await expect.element(page.getByRole('menu')).not.toBeInTheDocument(); - await toggleDropdown(user); + await toggleDropdown(user, page); await expect.element(page.getByRole('menu')).toBeInTheDocument(); expect(page.getByRole('menuitem').elements()).toHaveLength(4); @@ -48,27 +48,27 @@ describe('', () => { }); it.each([true, false])('allows toggling auto-connect', async (autoConnect) => { - const { user, store } = setUp(autoConnect); + const { user, store, ...page } = await setUp(autoConnect); - await toggleDropdown(user); + await toggleDropdown(user, page); await user.click(page.getByRole('menuitem', { name: autoConnect ? 'Do not auto-connect' : 'Auto-connect' })); expect(Object.values(store.getState().servers)[0].autoConnect).toEqual(!autoConnect); }); it('renders deletion modal', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); expect(page.getByRole('dialog')).not.toBeInTheDocument(); - await toggleDropdown(user); + await toggleDropdown(user, page); await user.click(page.getByRole('menuitem', { name: 'Remove server' })); await expect.element(page.getByRole('dialog')).toBeInTheDocument(); }); - it.each([[true], [false]])('renders expected size and icon', (autoConnect) => { - const { container } = setUp(autoConnect); + it.each([[true], [false]])('renders expected size and icon', async (autoConnect) => { + const { container } = await setUp(autoConnect); expect(container).toMatchSnapshot(); }); }); diff --git a/test/servers/ServersDropdown.test.tsx b/test/servers/ServersDropdown.test.tsx index ab10f3c3..53a9184a 100644 --- a/test/servers/ServersDropdown.test.tsx +++ b/test/servers/ServersDropdown.test.tsx @@ -1,6 +1,5 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; -import { page } from 'vitest/browser'; import type { ServersMap } from '../../src/servers/data'; import { ServersDropdown } from '../../src/servers/ServersDropdown'; import { checkAccessibility } from '../__helpers__/accessibility'; @@ -25,15 +24,15 @@ describe('', () => { ); it('passes a11y checks', async () => { - const { user, ...rest } = setUp(); + const { user, container, ...page } = await setUp(); // Open menu await user.click(page.getByText('Servers')); - return checkAccessibility(rest); + return checkAccessibility({ container }); }); it('contains the list of servers and the "mange servers" button', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); await user.click(page.getByText('Servers')); const items = page.getByRole('menuitem').elements(); @@ -47,12 +46,12 @@ describe('', () => { }); it('contains a toggle with proper text', async () => { - setUp(); + const page = await setUp(); await expect.element(page.getByRole('button')).toHaveTextContent('Servers'); }); it('contains a button to manage servers', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); await user.click(page.getByText('Servers')); await expect @@ -61,7 +60,7 @@ describe('', () => { }); it('shows only create link when no servers exist yet', async () => { - const { user } = setUp({}); + const { user, ...page } = await setUp({}); await user.click(page.getByText('Servers')); await expect.element(page.getByRole('menuitem', { name: 'Add a server' })).toBeInTheDocument(); diff --git a/test/servers/helpers/ImportServersBtn.test.tsx b/test/servers/helpers/ImportServersBtn.test.tsx index 044f08de..13e878f2 100644 --- a/test/servers/helpers/ImportServersBtn.test.tsx +++ b/test/servers/helpers/ImportServersBtn.test.tsx @@ -1,5 +1,4 @@ import { fromPartial } from '@total-typescript/shoehorn'; -import { page } from 'vitest/browser'; import type { ServerData, ServersMap, ServerWithId } from '../../../src/servers/data'; import type { ImportServersBtnProps } from '../../../src/servers/helpers/ImportServersBtn'; import { ImportServersBtn } from '../../../src/servers/helpers/ImportServersBtn'; @@ -20,7 +19,7 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('shows tooltip on button hover', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); await expect.element(page.getByText('You can create servers by importing a CSV file')).not.toBeInTheDocument(); await user.hover(page.getByRole('button')); @@ -32,7 +31,7 @@ describe('', () => { ['foo', 'foo'], ['bar', 'bar'], ])('allows a class name to be provided', async (providedClassName, expectedClassName) => { - setUp({ className: providedClassName }); + const page = await setUp({ className: providedClassName }); await expect.element(page.getByRole('button')).toHaveAttribute('class', expect.stringContaining(expectedClassName)); }); @@ -41,12 +40,12 @@ describe('', () => { ['foo', 'foo'], ['bar', 'bar'], ])('has expected text', async (children, expectedText) => { - setUp({ children }); + const page = await setUp({ children }); await expect.element(page.getByRole('button')).toHaveTextContent(expectedText); }); it('imports servers when file input changes', async () => { - const { user } = setUp(); + const { user, ...page } = await setUp(); await user.upload(page.getByTestId('csv-file-input'), csvFile); expect(importServersFromFile).toHaveBeenCalledTimes(1); @@ -68,7 +67,7 @@ describe('', () => { id: 'existingserver-s.test', }; const newServer: ServerData = { name: 'newServer', url: 'http://s.test/newUrl', apiKey: 'newApiKey' }; - const { user, store } = setUp({}, { [existingServer.id]: existingServer }); + const { user, store, ...page } = await setUp({}, { [existingServer.id]: existingServer }); importServersFromFile.mockResolvedValue([existingServerData, newServer]); diff --git a/test/servers/helpers/ServerError.test.tsx b/test/servers/helpers/ServerError.test.tsx index 24ef3cd1..706b1d85 100644 --- a/test/servers/helpers/ServerError.test.tsx +++ b/test/servers/helpers/ServerError.test.tsx @@ -1,4 +1,3 @@ -import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { MemoryRouter } from 'react-router'; import type { NonReachableServer, NotFoundServer, SelectedServer } from '../../../src/servers/data'; @@ -46,9 +45,9 @@ describe('', () => { }, ], ])('renders expected information based on provided server type', async (selectedServer, { found, notFound }) => { - setUp(selectedServer); + const screen = await setUp(selectedServer); await Promise.all(found.map((text) => expect.element(screen.getByText(text)).toBeInTheDocument())); - await Promise.all(notFound.map((text) => expect.element(screen.queryByText(text)).not.toBeInTheDocument())); + await Promise.all(notFound.map((text) => expect.element(screen.getByText(text)).not.toBeInTheDocument())); }); }); From 31531c1f875d704eb70a19e403ab28621fefae2b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 16 Aug 2026 21:08:53 +0200 Subject: [PATCH 5/5] Use screen terminology instead of page as documented by vite-browser-react --- test/app/App.test.tsx | 8 ++-- test/common/AppUpdateBanner.test.tsx | 20 +++++----- test/common/ErrorHandler.test.tsx | 18 ++++----- test/common/Home.test.tsx | 12 +++--- test/common/MainHeader.test.tsx | 8 ++-- test/common/NotFound.test.tsx | 12 +++--- .../ShlinkWebComponentContainer.test.tsx | 20 +++++----- test/servers/CreateServer.test.tsx | 40 +++++++++---------- test/servers/DeleteServerButton.test.tsx | 14 +++---- test/servers/DeleteServerModal.test.tsx | 28 ++++++------- test/servers/EditServer.test.tsx | 34 ++++++++-------- test/servers/ManageServers.test.tsx | 37 ++++++++--------- test/servers/ManageServersRow.test.tsx | 14 +++---- .../servers/ManageServersRowDropdown.test.tsx | 38 +++++++++--------- test/servers/ServersDropdown.test.tsx | 26 ++++++------ .../helpers/DuplicatedServersModal.test.tsx | 32 +++++++-------- .../servers/helpers/ImportServersBtn.test.tsx | 32 ++++++++------- test/servers/helpers/ServerForm.test.tsx | 30 +++++++------- 18 files changed, 213 insertions(+), 210 deletions(-) diff --git a/test/app/App.test.tsx b/test/app/App.test.tsx index 33e4ca2f..f8e1ac70 100644 --- a/test/app/App.test.tsx +++ b/test/app/App.test.tsx @@ -49,8 +49,8 @@ describe('', () => { ['/server/def456/bar', 'ShlinkWebComponentContainer'], ['/other', 'Oops! We could not find requested route.'], ])('renders expected route', async (activeRoute, expectedComponent) => { - const page = await setUp(activeRoute); - await expect.element(page.getByText(expectedComponent)).toBeInTheDocument(); + const screen = await setUp(activeRoute); + await expect.element(screen.getByText(expectedComponent)).toBeInTheDocument(); }); it.each([ @@ -58,8 +58,8 @@ describe('', () => { ['/bar', false], ['/', true], ])('renders expected classes on shlink-wrapper based on current pathname', async (pathname, isFlex) => { - const page = await setUp(pathname); - const shlinkWrapper = page.getByTestId('shlink-wrapper'); + const screen = await setUp(pathname); + const shlinkWrapper = screen.getByTestId('shlink-wrapper'); if (isFlex) { await expect.element(shlinkWrapper).toHaveClass('flex'); diff --git a/test/common/AppUpdateBanner.test.tsx b/test/common/AppUpdateBanner.test.tsx index 3023cc08..782d70e6 100644 --- a/test/common/AppUpdateBanner.test.tsx +++ b/test/common/AppUpdateBanner.test.tsx @@ -10,28 +10,28 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders initial state', async () => { - const page = await setUp(); + const screen = await setUp(); - await expect.element(page.getByRole('heading')).toHaveTextContent('This app has just been updated!'); - await expect.element(page.getByText('Restarting...')).not.toBeInTheDocument(); - await expect.element(page.getByText('Restart now')).not.toHaveAttribute('disabled'); + await expect.element(screen.getByRole('heading')).toHaveTextContent('This app has just been updated!'); + await expect.element(screen.getByText('Restarting...')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Restart now')).not.toHaveAttribute('disabled'); }); it('invokes toggle when alert is closed', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); expect(onClose).not.toHaveBeenCalled(); - await user.click(page.getByLabelText('Close')); + await user.click(screen.getByLabelText('Close')); expect(onClose).toHaveBeenCalled(); }); it('triggers the update when clicking the button', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); expect(forceUpdate).not.toHaveBeenCalled(); - await user.click(page.getByText(/^Restart now/)); + await user.click(screen.getByText(/^Restart now/)); expect(forceUpdate).toHaveBeenCalled(); - await expect.element(page.getByText('Restarting...')).toBeInTheDocument(); - await expect.element(page.getByText(/^Restart now/)).not.toBeInTheDocument(); + await expect.element(screen.getByText('Restarting...')).toBeInTheDocument(); + await expect.element(screen.getByText(/^Restart now/)).not.toBeInTheDocument(); }); }); diff --git a/test/common/ErrorHandler.test.tsx b/test/common/ErrorHandler.test.tsx index e37606bc..76337a6c 100644 --- a/test/common/ErrorHandler.test.tsx +++ b/test/common/ErrorHandler.test.tsx @@ -26,25 +26,25 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders children when no error has occurred', async () => { - const page = await setUp(Foo); + const screen = await setUp(Foo); - await expect.element(page.getByText('Foo')).toBeInTheDocument(); - await expect.element(page.getByText('Oops! This is awkward :S')).not.toBeInTheDocument(); - await expect.element(page.getByRole('button')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Foo')).toBeInTheDocument(); + await expect.element(screen.getByText('Oops! This is awkward :S')).not.toBeInTheDocument(); + await expect.element(screen.getByRole('button')).not.toBeInTheDocument(); }); it('renders error page when error has occurred', async () => { - const page = await setUp(); + const screen = await setUp(); - await expect.element(page.getByText('Oops! This is awkward :S')).toBeInTheDocument(); - await expect.element(page.getByRole('button')).toBeInTheDocument(); + await expect.element(screen.getByText('Oops! This is awkward :S')).toBeInTheDocument(); + await expect.element(screen.getByRole('button')).toBeInTheDocument(); }); it('reloads page on button click', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); expect(reload).not.toHaveBeenCalled(); - await user.click(page.getByRole('button')); + await user.click(screen.getByRole('button')); expect(reload).toHaveBeenCalled(); }); }); diff --git a/test/common/Home.test.tsx b/test/common/Home.test.tsx index 50be0aba..6b082465 100644 --- a/test/common/Home.test.tsx +++ b/test/common/Home.test.tsx @@ -20,8 +20,8 @@ describe('', () => { checkAccessibility(setUp({ '1a': fromPartial({ name: 'foo', id: '1' }) }))); it('renders title', async () => { - const page = await setUp(); - await expect.element(page.getByRole('heading', { name: 'Welcome!' })).toBeInTheDocument(); + const screen = await setUp(); + await expect.element(screen.getByRole('heading', { name: 'Welcome!' })).toBeInTheDocument(); }); it.each([ @@ -35,16 +35,16 @@ describe('', () => { ], [{}, 2], ])('shows link to create or set-up server only when no servers exist', async (servers, expectedServers) => { - const page = await setUp(servers); - const links = page.getByRole('link').elements(); + const screen = await setUp(servers); + const links = screen.getByRole('link').elements(); expect(links).toHaveLength(expectedServers); if (Object.keys(servers).length === 0) { await expect - .element(page.getByText('This application will help you manage your Shlink servers.')) + .element(screen.getByText('This application will help you manage your Shlink servers.')) .toBeInTheDocument(); - await expect.element(page.getByText('Learn more about Shlink')).toBeInTheDocument(); + await expect.element(screen.getByText('Learn more about Shlink')).toBeInTheDocument(); } }); }); diff --git a/test/common/MainHeader.test.tsx b/test/common/MainHeader.test.tsx index 85567ec8..1553f530 100644 --- a/test/common/MainHeader.test.tsx +++ b/test/common/MainHeader.test.tsx @@ -19,8 +19,8 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders ServersDropdown', async () => { - const page = await setUp(); - await expect.element(page.getByRole('button', { name: 'Servers' })).toBeInTheDocument(); + const screen = await setUp(); + await expect.element(screen.getByRole('button', { name: 'Servers' })).toBeInTheDocument(); }); it.each([ @@ -30,9 +30,9 @@ describe('', () => { ['/settings/foo', true], ['/settings/bar', true], ])('sets link to settings as active only when current path is settings', async (currentPath, isActive) => { - const page = await setUp(currentPath); + const screen = await setUp(currentPath); await expect - .element(page.getByRole('menuitem', { name: /Settings$/ })) + .element(screen.getByRole('menuitem', { name: /Settings$/ })) .toHaveAttribute('data-active', isActive ? 'true' : 'false'); }); }); diff --git a/test/common/NotFound.test.tsx b/test/common/NotFound.test.tsx index bb79d34f..69e8428a 100644 --- a/test/common/NotFound.test.tsx +++ b/test/common/NotFound.test.tsx @@ -14,15 +14,15 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('shows expected error title', async () => { - const page = await setUp(); - await expect.element(page.getByText('Oops! We could not find requested route.')).toBeInTheDocument(); + const screen = await setUp(); + await expect.element(screen.getByText('Oops! We could not find requested route.')).toBeInTheDocument(); }); it('shows expected error message', async () => { - const page = await setUp(); + const screen = await setUp(); await expect .element( - page.getByText( + screen.getByText( "Use your browser's back button to navigate to the page you have previously come from, or just press this button.", ), ) @@ -34,8 +34,8 @@ describe('', () => { [{ to: '/foo/bar', children: 'Hello' }, '/foo/bar', 'Hello'], [{ to: '/baz-bar', children: <>Foo }, '/baz-bar', 'Foo'], ])('shows expected link and text', async (props, expectedLink, expectedText) => { - const page = await setUp(props); - const link = page.getByRole('link'); + const screen = await setUp(props); + const link = screen.getByRole('link'); await expect.element(link).toHaveAttribute('href', expectedLink); await expect.element(link).toHaveTextContent(expectedText); diff --git a/test/common/ShlinkWebComponentContainer.test.tsx b/test/common/ShlinkWebComponentContainer.test.tsx index c2c8a9af..6d5eb707 100644 --- a/test/common/ShlinkWebComponentContainer.test.tsx +++ b/test/common/ShlinkWebComponentContainer.test.tsx @@ -25,10 +25,10 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp(fromPartial({ version: '3.0.0' })))); it('shows loading indicator while loading server', async () => { - const page = await setUp(null); + const screen = await setUp(null); - await expect.element(page.getByText('Loading...')).toBeInTheDocument(); - await expect.element(page.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Loading...')).toBeInTheDocument(); + await expect.element(screen.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); }); it.each([ @@ -38,17 +38,17 @@ describe('', () => { /Could not connect to this Shlink server/, ], ])('shows error for non reachable servers', async (selectedServer, expectedError) => { - const page = await setUp(selectedServer); + const screen = await setUp(selectedServer); - await expect.element(page.getByText('Loading...')).not.toBeInTheDocument(); - await expect.element(page.getByText(expectedError)).toBeInTheDocument(); - await expect.element(page.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Loading...')).not.toBeInTheDocument(); + await expect.element(screen.getByText(expectedError)).toBeInTheDocument(); + await expect.element(screen.getByText('ShlinkWebComponent')).not.toBeInTheDocument(); }); it('renders ShlinkWebComponent for reachable servers', async () => { - const page = await setUp(fromPartial({ version: '3.0.0' })); + const screen = await setUp(fromPartial({ version: '3.0.0' })); - await expect.element(page.getByText('Loading...')).not.toBeInTheDocument(); - await expect.element(page.getByText('ShlinkWebComponent')).toBeInTheDocument(); + await expect.element(screen.getByText('Loading...')).not.toBeInTheDocument(); + await expect.element(screen.getByText('ShlinkWebComponent')).toBeInTheDocument(); }); }); diff --git a/test/servers/CreateServer.test.tsx b/test/servers/CreateServer.test.tsx index ccf49abf..0a2d244b 100644 --- a/test/servers/CreateServer.test.tsx +++ b/test/servers/CreateServer.test.tsx @@ -45,38 +45,38 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('shows success message when imported is true', async () => { - const page = await setUp({ serversImported: true }); + const screen = await setUp({ serversImported: true }); await expect - .element(page.getByText('Servers properly imported. You can now select one from the list :)')) + .element(screen.getByText('Servers properly imported. You can now select one from the list :)')) .toBeInTheDocument(); await expect - .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .element(screen.getByText('The servers could not be imported. Make sure the format is correct.')) .not.toBeInTheDocument(); - await expect.element(page.getByText('ImportServersBtn')).not.toBeInTheDocument(); + await expect.element(screen.getByText('ImportServersBtn')).not.toBeInTheDocument(); }); it('shows error message when import failed', async () => { - const page = await setUp({ importFailed: true }); + const screen = await setUp({ importFailed: true }); await expect - .element(page.getByText('Servers properly imported. You can now select one from the list :)')) + .element(screen.getByText('Servers properly imported. You can now select one from the list :)')) .not.toBeInTheDocument(); await expect - .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .element(screen.getByText('The servers could not be imported. Make sure the format is correct.')) .toBeInTheDocument(); }); it('creates server data when form is submitted', async () => { - const { user, history, store, ...page } = await setUp(); + const { user, history, store, ...screen } = await setUp(); const expectedServerId = 'the_name-the_url.com'; - await user.type(page.getByLabelText(/^Name/), 'the_name'); - await user.type(page.getByLabelText(/^URL/), 'https://the_url.com'); - await user.type(page.getByLabelText(/^API key/), 'the_api_key'); + await user.type(screen.getByLabelText(/^Name/), 'the_name'); + await user.type(screen.getByLabelText(/^URL/), 'https://the_url.com'); + await user.type(screen.getByLabelText(/^API key/), 'the_api_key'); expect(store.getState().servers[expectedServerId]).not.toBeDefined(); - await user.click(page.getByRole('button', { name: 'Create server' })); + await user.click(screen.getByRole('button', { name: 'Create server' })); expect(store.getState().servers[expectedServerId]).toEqual( expect.objectContaining({ id: expectedServerId, @@ -87,20 +87,20 @@ describe('', () => { ); expect(history.location.pathname).toEqual(`/server/${expectedServerId}`); - await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); + await expect.element(screen.getByRole('dialog')).not.toBeInTheDocument(); }); it('displays dialog when trying to create a duplicated server', async () => { - const { user, history, ...page } = await setUp(); + const { user, history, ...screen } = await setUp(); - await user.type(page.getByLabelText(/^Name/), 'the_name'); - await user.type(page.getByLabelText(/^URL/), 'https://existing_url.com'); - await user.type(page.getByLabelText(/^API key/), 'existing_api_key'); + await user.type(screen.getByLabelText(/^Name/), 'the_name'); + await user.type(screen.getByLabelText(/^URL/), 'https://existing_url.com'); + await user.type(screen.getByLabelText(/^API key/), 'existing_api_key'); - await user.click(page.getByRole('button', { name: 'Create server' })); + await user.click(screen.getByRole('button', { name: 'Create server' })); - await expect.element(page.getByRole('dialog')).toBeInTheDocument(); - await user.click(page.getByRole('button', { name: 'Discard' })); + await expect.element(screen.getByRole('dialog')).toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: 'Discard' })); expect(history.location.pathname).toEqual('/foo'); // Goes back to first route from history's initialEntries }); diff --git a/test/servers/DeleteServerButton.test.tsx b/test/servers/DeleteServerButton.test.tsx index 1b47e7ce..94fbde6b 100644 --- a/test/servers/DeleteServerButton.test.tsx +++ b/test/servers/DeleteServerButton.test.tsx @@ -28,21 +28,21 @@ describe('', () => { }); it('displays modal when button is clicked', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - await expect.element(page.getByText(/Are you sure you want to remove/)).not.toBeInTheDocument(); - await user.click(page.getByText('Remove this server')); - await expect.element(page.getByText(/Are you sure you want to remove/)).toBeInTheDocument(); + await expect.element(screen.getByText(/Are you sure you want to remove/)).not.toBeInTheDocument(); + await user.click(screen.getByText('Remove this server')); + await expect.element(screen.getByText(/Are you sure you want to remove/)).toBeInTheDocument(); }); it('navigates to home when deletion is confirmed', async () => { - const { user, history, ...page } = await setUp(); + const { user, history, ...screen } = await setUp(); // Open modal - await user.click(page.getByText('Remove this server')); + await user.click(screen.getByText('Remove this server')); expect(history.location.pathname).toEqual('/foo'); - await user.click(page.getByRole('button', { name: 'Delete' })); + await user.click(screen.getByRole('button', { name: 'Delete' })); expect(history.location.pathname).toEqual('/'); }); }); diff --git a/test/servers/DeleteServerModal.test.tsx b/test/servers/DeleteServerModal.test.tsx index 03abc5cc..bf635bfb 100644 --- a/test/servers/DeleteServerModal.test.tsx +++ b/test/servers/DeleteServerModal.test.tsx @@ -19,38 +19,38 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders a modal window', async () => { - const page = await setUp(); + const screen = await setUp(); - await expect.element(page.getByRole('dialog')).toBeInTheDocument(); - await expect.element(page.getByRole('heading')).toHaveTextContent('Remove server'); + await expect.element(screen.getByRole('dialog')).toBeInTheDocument(); + await expect.element(screen.getByRole('heading')).toHaveTextContent('Remove server'); }); it('displays the name of the server as part of the content', async () => { - const page = await setUp(); + const screen = await setUp(); - await expect.element(page.getByText(/^Are you sure you want to remove/)).toBeInTheDocument(); - await expect.element(page.getByText(serverName)).toBeInTheDocument(); + await expect.element(screen.getByText(/^Are you sure you want to remove/)).toBeInTheDocument(); + await expect.element(screen.getByText(serverName)).toBeInTheDocument(); }); it.each([ - [(page: RenderResult) => page.getByRole('button', { name: 'Cancel' })], - [(page: RenderResult) => page.getByLabelText('Close dialog')], + [(screen: RenderResult) => screen.getByRole('button', { name: 'Cancel' })], + [(screen: RenderResult) => screen.getByLabelText('Close dialog')], ])('closes dialog when clicking cancel button', async (getButton) => { - const { user, store, ...page } = await setUp(); + const { user, store, ...screen } = await setUp(); - await expect.element(page.getByRole('dialog')).toBeInTheDocument(); - await user.click(getButton(page)); - await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); + await expect.element(screen.getByRole('dialog')).toBeInTheDocument(); + await user.click(getButton(screen)); + await expect.element(screen.getByRole('dialog')).not.toBeInTheDocument(); // No server has been deleted expect(Object.keys(store.getState().servers)).toHaveLength(1); }); it('deletes server when clicking accept button', async () => { - const { user, store, ...page } = await setUp(); + const { user, store, ...screen } = await setUp(); expect(Object.keys(store.getState().servers)).toHaveLength(1); - await user.click(page.getByRole('button', { name: 'Delete' })); + await user.click(screen.getByRole('button', { name: 'Delete' })); expect(Object.keys(store.getState().servers)).toHaveLength(0); }); }); diff --git a/test/servers/EditServer.test.tsx b/test/servers/EditServer.test.tsx index dfc515b4..a5cbfe62 100644 --- a/test/servers/EditServer.test.tsx +++ b/test/servers/EditServer.test.tsx @@ -35,31 +35,31 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders nothing if selected server is not reachable', async () => { - const page = await setUp(fromPartial({})); + const screen = await setUp(fromPartial({})); - await expect.element(page.getByText('Edit')).not.toBeInTheDocument(); - await expect.element(page.getByText('Cancel')).not.toBeInTheDocument(); - await expect.element(page.getByText('Save')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Edit')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Cancel')).not.toBeInTheDocument(); + await expect.element(screen.getByText('Save')).not.toBeInTheDocument(); }); it('renders server title', async () => { - const page = await setUp(); - expect(page.getByText(`Edit "${defaultSelectedServer.name}"`)).toBeInTheDocument(); + const screen = await setUp(); + expect(screen.getByText(`Edit "${defaultSelectedServer.name}"`)).toBeInTheDocument(); }); it('display the server info in the form components', async () => { - const page = await setUp(); + const screen = await setUp(); - await expect.element(page.getByLabelText(/^Name/)).toBeInTheDocument(); - await expect.element(page.getByLabelText(/^URL/)).toBeInTheDocument(); - await expect.element(page.getByLabelText(/^API key/)).toBeInTheDocument(); + await expect.element(screen.getByLabelText(/^Name/)).toBeInTheDocument(); + await expect.element(screen.getByLabelText(/^URL/)).toBeInTheDocument(); + await expect.element(screen.getByLabelText(/^API key/)).toBeInTheDocument(); }); it('edits server and redirects to it when form is submitted', async () => { - const { user, history, store, ...page } = await setUp(); + const { user, history, store, ...screen } = await setUp(); - await user.type(page.getByLabelText(/^Name/), ' edited'); - await user.click(page.getByRole('button', { name: 'Save' })); + await user.type(screen.getByLabelText(/^Name/), ' edited'); + await user.click(screen.getByRole('button', { name: 'Save' })); expect(store.getState().servers[defaultSelectedServer.id]).toEqual( expect.objectContaining({ @@ -74,11 +74,11 @@ describe('', () => { it.each([{ forwardCredentials: true }, { forwardCredentials: false }])( 'edits advanced options - forward credentials', async ({ forwardCredentials }) => { - const { user, store, ...page } = await setUp({ ...defaultSelectedServer, forwardCredentials }); + const { user, store, ...screen } = await setUp({ ...defaultSelectedServer, forwardCredentials }); - await user.click(page.getByText('Advanced options')); - await user.click(page.getByLabelText('Forward credentials to this server on every request.')); - await user.click(page.getByRole('button', { name: 'Save' })); + await user.click(screen.getByText('Advanced options')); + await user.click(screen.getByLabelText('Forward credentials to this server on every request.')); + await user.click(screen.getByRole('button', { name: 'Save' })); expect(store.getState().servers[defaultSelectedServer.id]).toEqual( expect.objectContaining({ diff --git a/test/servers/ManageServers.test.tsx b/test/servers/ManageServers.test.tsx index 66db2ab1..ddfffd9d 100644 --- a/test/servers/ManageServers.test.tsx +++ b/test/servers/ManageServers.test.tsx @@ -32,43 +32,44 @@ describe('', () => { )); it('shows search field which allows searching servers, affecting te amount of rendered rows', async () => { - const { user, ...page } = await setUp({ + const { user, ...screen } = await setUp({ foo: createServerMock('foo'), bar: createServerMock('bar'), baz: createServerMock('baz'), }); const search = async (searchTerm: string) => { - await user.clear(page.getByPlaceholder('Search...')); - await user.type(page.getByPlaceholder('Search...'), searchTerm); + await user.clear(screen.getByPlaceholder('Search...')); + await user.type(screen.getByPlaceholder('Search...'), searchTerm); }; // Add one for the header row - const expectRows = (amount: number) => expect.poll(() => page.getByRole('row').elements()).toHaveLength(amount + 1); + const expectRows = (amount: number) => + expect.poll(() => screen.getByRole('row').elements()).toHaveLength(amount + 1); await expectRows(3); - await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); + await expect.element(screen.getByText('No servers found.')).not.toBeInTheDocument(); await search('foo'); await expectRows(1); - await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); + await expect.element(screen.getByText('No servers found.')).not.toBeInTheDocument(); await search('Ba'); await expectRows(2); - await expect.element(page.getByText('No servers found.')).not.toBeInTheDocument(); + await expect.element(screen.getByText('No servers found.')).not.toBeInTheDocument(); await search('invalid'); await expectRows(1); - await expect.element(page.getByText('No servers found.')).toBeInTheDocument(); + await expect.element(screen.getByText('No servers found.')).toBeInTheDocument(); }); it.each([[createServerMock('foo')], [createServerMock('foo', true)]])( 'shows different amount of columns if there are at least one auto-connect server', async (server) => { - const page = await setUp({ server }); + const screen = await setUp({ server }); if (server.autoConnect) { - await expect.element(page.getByTestId('auto-connect')).toBeInTheDocument(); + await expect.element(screen.getByTestId('auto-connect')).toBeInTheDocument(); } else { - await expect.element(page.getByTestId('auto-connect')).not.toBeInTheDocument(); + await expect.element(screen.getByTestId('auto-connect')).not.toBeInTheDocument(); } }, ); @@ -77,30 +78,30 @@ describe('', () => { [{}, 0], [{ foo: createServerMock('foo') }, 1], ])('shows export button if the list of servers is not empty', async (servers, expectedButtons) => { - const page = await setUp(servers); - expect(page.getByRole('button', { name: 'Export servers' }).elements()).toHaveLength(expectedButtons); + const screen = await setUp(servers); + expect(screen.getByRole('button', { name: 'Export servers' }).elements()).toHaveLength(expectedButtons); }); it('allows exporting servers when clicking on button', async () => { - const { user, ...page } = await setUp({ foo: createServerMock('foo') }); + const { user, ...screen } = await setUp({ foo: createServerMock('foo') }); expect(exportServers).not.toHaveBeenCalled(); - await user.click(page.getByRole('button', { name: 'Export servers' })); + await user.click(screen.getByRole('button', { name: 'Export servers' })); expect(exportServers).toHaveBeenCalled(); }); it.each([[true], [false]])('shows an error message if an error occurs while importing servers', async (hasError) => { useTimeoutToggle.mockReturnValue([hasError, vi.fn()]); - const page = await setUp({ foo: createServerMock('foo') }); + const screen = await setUp({ foo: createServerMock('foo') }); if (hasError) { await expect - .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .element(screen.getByText('The servers could not be imported. Make sure the format is correct.')) .toBeInTheDocument(); } else { await expect - .element(page.getByText('The servers could not be imported. Make sure the format is correct.')) + .element(screen.getByText('The servers could not be imported. Make sure the format is correct.')) .not.toBeInTheDocument(); } }); diff --git a/test/servers/ManageServersRow.test.tsx b/test/servers/ManageServersRow.test.tsx index 0ad92848..e31d2052 100644 --- a/test/servers/ManageServersRow.test.tsx +++ b/test/servers/ManageServersRow.test.tsx @@ -27,13 +27,13 @@ describe('', () => { [true, 4], [false, 3], ])('renders expected amount of columns', async (hasAutoConnect, expectedCols) => { - const page = await setUp(hasAutoConnect); - expect(page.getByRole('cell').elements()).toHaveLength(expectedCols); + const screen = await setUp(hasAutoConnect); + expect(screen.getByRole('cell').elements()).toHaveLength(expectedCols); }); it('renders an options dropdown', async () => { - const page = await setUp(); - await expect.element(page.getByRole('button', { name: 'Options' })).toBeInTheDocument(); + const screen = await setUp(); + await expect.element(screen.getByRole('button', { name: 'Options' })).toBeInTheDocument(); }); it.each([[true], [false]])('renders auto-connect icon only if server is autoConnect', async (autoConnect) => { @@ -42,11 +42,11 @@ describe('', () => { }); it('renders server props where appropriate', async () => { - const page = await setUp(); - const link = page.getByRole('link'); + const screen = await setUp(); + const link = screen.getByRole('link'); await expect.element(link).toHaveAttribute('href', `/server/${server.id}`); await expect.element(link).toHaveTextContent(server.name); - await expect.element(page.getByText(server.url)).toBeInTheDocument(); + await expect.element(screen.getByText(server.url)).toBeInTheDocument(); }); }); diff --git a/test/servers/ManageServersRowDropdown.test.tsx b/test/servers/ManageServersRowDropdown.test.tsx index 8b24e54d..921727e4 100644 --- a/test/servers/ManageServersRowDropdown.test.tsx +++ b/test/servers/ManageServersRowDropdown.test.tsx @@ -21,50 +21,50 @@ describe('', () => { }, ); }; - const toggleDropdown = (user: UserEvent, page: RenderResult) => user.click(page.getByRole('button')); + const toggleDropdown = (user: UserEvent, screen: RenderResult) => user.click(screen.getByRole('button')); it('passes a11y checks', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); // Open menu - await toggleDropdown(user, page); + await toggleDropdown(user, screen); - return checkAccessibility(page); + return checkAccessibility(screen); }); it('renders expected amount of dropdown items', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - await expect.element(page.getByRole('menu')).not.toBeInTheDocument(); - await toggleDropdown(user, page); - await expect.element(page.getByRole('menu')).toBeInTheDocument(); + await expect.element(screen.getByRole('menu')).not.toBeInTheDocument(); + await toggleDropdown(user, screen); + await expect.element(screen.getByRole('menu')).toBeInTheDocument(); - expect(page.getByRole('menuitem').elements()).toHaveLength(4); + expect(screen.getByRole('menuitem').elements()).toHaveLength(4); await expect - .element(page.getByRole('menuitem', { name: 'Connect', exact: true })) + .element(screen.getByRole('menuitem', { name: 'Connect', exact: true })) .toHaveAttribute('href', '/server/abc123'); await expect - .element(page.getByRole('menuitem', { name: 'Edit server' })) + .element(screen.getByRole('menuitem', { name: 'Edit server' })) .toHaveAttribute('href', '/server/abc123/edit'); }); it.each([true, false])('allows toggling auto-connect', async (autoConnect) => { - const { user, store, ...page } = await setUp(autoConnect); + const { user, store, ...screen } = await setUp(autoConnect); - await toggleDropdown(user, page); - await user.click(page.getByRole('menuitem', { name: autoConnect ? 'Do not auto-connect' : 'Auto-connect' })); + await toggleDropdown(user, screen); + await user.click(screen.getByRole('menuitem', { name: autoConnect ? 'Do not auto-connect' : 'Auto-connect' })); expect(Object.values(store.getState().servers)[0].autoConnect).toEqual(!autoConnect); }); it('renders deletion modal', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - expect(page.getByRole('dialog')).not.toBeInTheDocument(); + expect(screen.getByRole('dialog')).not.toBeInTheDocument(); - await toggleDropdown(user, page); - await user.click(page.getByRole('menuitem', { name: 'Remove server' })); + await toggleDropdown(user, screen); + await user.click(screen.getByRole('menuitem', { name: 'Remove server' })); - await expect.element(page.getByRole('dialog')).toBeInTheDocument(); + await expect.element(screen.getByRole('dialog')).toBeInTheDocument(); }); it.each([[true], [false]])('renders expected size and icon', async (autoConnect) => { diff --git a/test/servers/ServersDropdown.test.tsx b/test/servers/ServersDropdown.test.tsx index 53a9184a..b2bc8ef4 100644 --- a/test/servers/ServersDropdown.test.tsx +++ b/test/servers/ServersDropdown.test.tsx @@ -24,18 +24,18 @@ describe('', () => { ); it('passes a11y checks', async () => { - const { user, container, ...page } = await setUp(); + const { user, container, ...screen } = await setUp(); // Open menu - await user.click(page.getByText('Servers')); + await user.click(screen.getByText('Servers')); return checkAccessibility({ container }); }); it('contains the list of servers and the "mange servers" button', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - await user.click(page.getByText('Servers')); - const items = page.getByRole('menuitem').elements(); + await user.click(screen.getByText('Servers')); + const items = screen.getByRole('menuitem').elements(); // We have to add two for the "Manage servers" and the "Settings" menu items expect(items).toHaveLength(Object.values(fallbackServers).length + 2); @@ -46,23 +46,23 @@ describe('', () => { }); it('contains a toggle with proper text', async () => { - const page = await setUp(); - await expect.element(page.getByRole('button')).toHaveTextContent('Servers'); + const screen = await setUp(); + await expect.element(screen.getByRole('button')).toHaveTextContent('Servers'); }); it('contains a button to manage servers', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - await user.click(page.getByText('Servers')); + await user.click(screen.getByText('Servers')); await expect - .element(page.getByRole('menuitem', { name: 'Manage servers' })) + .element(screen.getByRole('menuitem', { name: 'Manage servers' })) .toHaveAttribute('href', '/manage-servers'); }); it('shows only create link when no servers exist yet', async () => { - const { user, ...page } = await setUp({}); + const { user, ...screen } = await setUp({}); - await user.click(page.getByText('Servers')); - await expect.element(page.getByRole('menuitem', { name: 'Add a server' })).toBeInTheDocument(); + await user.click(screen.getByText('Servers')); + await expect.element(screen.getByRole('menuitem', { name: 'Add a server' })).toBeInTheDocument(); }); }); diff --git a/test/servers/helpers/DuplicatedServersModal.test.tsx b/test/servers/helpers/DuplicatedServersModal.test.tsx index f111b4e3..5fa968e8 100644 --- a/test/servers/helpers/DuplicatedServersModal.test.tsx +++ b/test/servers/helpers/DuplicatedServersModal.test.tsx @@ -22,8 +22,8 @@ describe('', () => { [[mockServer(), mockServer(), mockServer()], 3], [[mockServer(), mockServer(), mockServer(), mockServer()], 4], ])('renders expected amount of items', async (duplicatedServers, expectedItems) => { - const page = await setUp(duplicatedServers); - expect(page.getByRole('listitem')).toHaveLength(expectedItems); + const screen = await setUp(duplicatedServers); + expect(screen.getByRole('listitem')).toHaveLength(expectedItems); }); it.each([ @@ -48,13 +48,13 @@ describe('', () => { }, ], ])('renders expected texts based on amount of servers', async (duplicatedServers, assertions) => { - const page = await setUp(duplicatedServers); + const screen = await setUp(duplicatedServers); - await expect.element(page.getByRole('heading')).toHaveTextContent(assertions.header); - await expect.element(page.getByText(assertions.firstParagraph)).toBeInTheDocument(); - await expect.element(page.getByText(assertions.lastParagraph)).toBeInTheDocument(); - await expect.element(page.getByRole('button', { name: assertions.discardBtn })).toBeInTheDocument(); - await expect.element(page.getByRole('button', { name: assertions.confirmButton })).toBeInTheDocument(); + await expect.element(screen.getByRole('heading')).toHaveTextContent(assertions.header); + await expect.element(screen.getByText(assertions.firstParagraph)).toBeInTheDocument(); + await expect.element(screen.getByText(assertions.lastParagraph)).toBeInTheDocument(); + await expect.element(screen.getByRole('button', { name: assertions.discardBtn })).toBeInTheDocument(); + await expect.element(screen.getByRole('button', { name: assertions.confirmButton })).toBeInTheDocument(); }); it.each([ @@ -62,19 +62,19 @@ describe('', () => { [[mockServer({ url: 'url', apiKey: 'apiKey' })]], [[mockServer({ url: 'url_1', apiKey: 'apiKey_1' }), mockServer({ url: 'url_2', apiKey: 'apiKey_2' })]], ])('displays provided server data', async (duplicatedServers) => { - const page = await setUp(duplicatedServers); + const screen = await setUp(duplicatedServers); if (duplicatedServers.length === 0) { - await expect.element(page.getByRole('listitem')).not.toBeInTheDocument(); + await expect.element(screen.getByRole('listitem')).not.toBeInTheDocument(); } else if (duplicatedServers.length === 1) { - const [firstItem, secondItem] = page.getByRole('listitem').elements(); + const [firstItem, secondItem] = screen.getByRole('listitem').elements(); await expect.element(firstItem).toHaveTextContent(`URL: ${duplicatedServers[0].url}`); await expect.element(secondItem).toHaveTextContent(`API key: ${duplicatedServers[0].apiKey}`); } else { expect.assertions(duplicatedServers.length); await Promise.all( - page + screen .getByRole('listitem') .elements() .map((item, index) => { @@ -86,18 +86,18 @@ describe('', () => { }); it('invokes onClose when appropriate button is clicked', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); expect(onClose).not.toHaveBeenCalled(); - await user.click(page.getByRole('button', { name: 'Discard' })); + await user.click(screen.getByRole('button', { name: 'Discard' })); expect(onClose).toHaveBeenCalled(); }); it('invokes onConfirm when appropriate button is clicked', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); expect(onConfirm).not.toHaveBeenCalled(); - await user.click(page.getByRole('button', { name: 'Save duplicate' })); + await user.click(screen.getByRole('button', { name: 'Save duplicate' })); expect(onConfirm).toHaveBeenCalled(); }); }); diff --git a/test/servers/helpers/ImportServersBtn.test.tsx b/test/servers/helpers/ImportServersBtn.test.tsx index 13e878f2..2d7d7194 100644 --- a/test/servers/helpers/ImportServersBtn.test.tsx +++ b/test/servers/helpers/ImportServersBtn.test.tsx @@ -19,11 +19,11 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('shows tooltip on button hover', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - await expect.element(page.getByText('You can create servers by importing a CSV file')).not.toBeInTheDocument(); - await user.hover(page.getByRole('button')); - await expect.element(page.getByText('You can create servers by importing a CSV file')).toBeInTheDocument(); + await expect.element(screen.getByText('You can create servers by importing a CSV file')).not.toBeInTheDocument(); + await user.hover(screen.getByRole('button')); + await expect.element(screen.getByText('You can create servers by importing a CSV file')).toBeInTheDocument(); }); it.each([ @@ -31,8 +31,10 @@ describe('', () => { ['foo', 'foo'], ['bar', 'bar'], ])('allows a class name to be provided', async (providedClassName, expectedClassName) => { - const page = await setUp({ className: providedClassName }); - await expect.element(page.getByRole('button')).toHaveAttribute('class', expect.stringContaining(expectedClassName)); + const screen = await setUp({ className: providedClassName }); + await expect + .element(screen.getByRole('button')) + .toHaveAttribute('class', expect.stringContaining(expectedClassName)); }); it.each([ @@ -40,14 +42,14 @@ describe('', () => { ['foo', 'foo'], ['bar', 'bar'], ])('has expected text', async (children, expectedText) => { - const page = await setUp({ children }); - await expect.element(page.getByRole('button')).toHaveTextContent(expectedText); + const screen = await setUp({ children }); + await expect.element(screen.getByRole('button')).toHaveTextContent(expectedText); }); it('imports servers when file input changes', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); - await user.upload(page.getByTestId('csv-file-input'), csvFile); + await user.upload(screen.getByTestId('csv-file-input'), csvFile); expect(importServersFromFile).toHaveBeenCalledTimes(1); }); @@ -67,19 +69,19 @@ describe('', () => { id: 'existingserver-s.test', }; const newServer: ServerData = { name: 'newServer', url: 'http://s.test/newUrl', apiKey: 'newApiKey' }; - const { user, store, ...page } = await setUp({}, { [existingServer.id]: existingServer }); + const { user, store, ...screen } = await setUp({}, { [existingServer.id]: existingServer }); importServersFromFile.mockResolvedValue([existingServerData, newServer]); - await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); - await user.upload(page.getByTestId('csv-file-input'), csvFile); + await expect.element(screen.getByRole('dialog')).not.toBeInTheDocument(); + await user.upload(screen.getByTestId('csv-file-input'), csvFile); // Once the file is uploaded, non-duplicated servers are immediately created const { servers } = store.getState(); expect(Object.keys(servers)).toHaveLength(2); - await expect.element(page.getByRole('dialog')).toBeInTheDocument(); - await user.click(page.getByRole('button', { name: btnName })); + await expect.element(screen.getByRole('dialog')).toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: btnName })); // If duplicated servers are saved, there's one extra server creation if (savesDuplicatedServers) { diff --git a/test/servers/helpers/ServerForm.test.tsx b/test/servers/helpers/ServerForm.test.tsx index 6356bf5e..05da61da 100644 --- a/test/servers/helpers/ServerForm.test.tsx +++ b/test/servers/helpers/ServerForm.test.tsx @@ -15,36 +15,36 @@ describe('', () => { it('passes a11y checks', () => checkAccessibility(setUp())); it('renders inputs', async () => { - const page = await setUp(); + const screen = await setUp(); - await expect.element(page.getByLabelText(/^Name/)).toBeInTheDocument(); - await expect.element(page.getByLabelText(/^URL/)).toBeInTheDocument(); - await expect.element(page.getByLabelText(/^API key/)).toBeInTheDocument(); - await expect.element(page.getByText('Something')).toBeInTheDocument(); - await expect.element(page.getByText('Advanced options')).toBeInTheDocument(); + await expect.element(screen.getByLabelText(/^Name/)).toBeInTheDocument(); + await expect.element(screen.getByLabelText(/^URL/)).toBeInTheDocument(); + await expect.element(screen.getByLabelText(/^API key/)).toBeInTheDocument(); + await expect.element(screen.getByText('Something')).toBeInTheDocument(); + await expect.element(screen.getByText('Advanced options')).toBeInTheDocument(); }); it('invokes submit callback when submit event is triggered', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); expect(onSubmit).not.toHaveBeenCalled(); // Fill required elements so the form can be submitted - await user.type(page.getByLabelText(/^Name/), 'The server'); - await user.type(page.getByLabelText(/^URL/), 'https://example.com'); - await user.type(page.getByLabelText(/^API key/), '123456'); + await user.type(screen.getByLabelText(/^Name/), 'The server'); + await user.type(screen.getByLabelText(/^URL/), 'https://example.com'); + await user.type(screen.getByLabelText(/^API key/), '123456'); - await user.click(page.getByRole('button', { name: 'Submit' })); + await user.click(screen.getByRole('button', { name: 'Submit' })); expect(onSubmit).toHaveBeenCalled(); }); it('shows advanced options', async () => { - const { user, ...page } = await setUp(); + const { user, ...screen } = await setUp(); const forwardCredentialsLabel = 'Forward credentials to this server on every request.'; - await expect.element(page.getByLabelText(forwardCredentialsLabel)).not.toBeInTheDocument(); - await user.click(page.getByText('Advanced options')); - await expect.element(page.getByLabelText(forwardCredentialsLabel)).toBeInTheDocument(); + await expect.element(screen.getByLabelText(forwardCredentialsLabel)).not.toBeInTheDocument(); + await user.click(screen.getByText('Advanced options')); + await expect.element(screen.getByLabelText(forwardCredentialsLabel)).toBeInTheDocument(); }); });