diff --git a/AGENTS.md b/AGENTS.md index 6c710d1639..3f2a0fa850 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,6 +45,7 @@ website/ # demo site (Vite + TanStack Router) - Browser tests use `vite-plus/test/browser` + Playwright. `test/setupBrowser.ts` configures `page.render()` via `vitest-browser-react` and registers custom locators via `locators.extend()` — prefer `page.getGrid()`, `page.getCell({ name })`, `page.getRow()`, `page.getHeaderCell()`, `page.getActiveCell()`, etc. over raw `page.getByRole()`. - Test helpers in `test/browser/utils.tsx`: `setup()`, `getRowWithCell()`, `getCellsAtRowIndex()`, `validateCellPosition()`, `scrollGrid()`, `safeTab()`, `testCount()`, `testRowCount()`. - `test/failOnConsole.ts` fails tests on unexpected console warnings/errors. +- Firefox runs test files in parallel. Firefox sends the mouse events of all its pages with the same pointer ID, so each test file that drags a resize or drag-fill handle, which captures the pointer, runs in a separate Firefox instance with a browser of its own: add such test files to `firefoxPointerCaptureTests` in `vite.config.ts`. Use `--project "*firefox*"` to run all the Firefox instances. - **Never run visual regression tests** — screenshots are environment-dependent, so visual regression tests must run in CI only. ## Tooling diff --git a/vite.config.ts b/vite.config.ts index 4fe6cec15b..415f693cc5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,9 +1,10 @@ +import { basename } from 'node:path'; import react from '@vitejs/plugin-react'; import { ecij } from 'ecij/plugin'; import { Features } from 'lightningcss'; import { defineConfig, lazyPlugins } from 'vite-plus'; import { playwright, type PlaywrightProviderOptions } from 'vite-plus/test/browser-playwright'; -import type { BrowserCommandContext } from 'vite-plus/test/node'; +import type { BrowserCommandContext, BrowserInstanceOption } from 'vite-plus/test/node'; import pkg from './package.json' with { type: 'json' }; @@ -11,6 +12,7 @@ const isCI = process.env.CI === 'true'; const isCIWorkflow = process.env.GITHUB_WORKFLOW === 'CI'; const isTest = process.env.VITEST === 'true'; const isAgent = process.env.AI_AGENT !== undefined; +const isWindows = process.platform === 'win32'; // TODO: remove when `userEvent.pointer` is supported async function resizeColumn( @@ -34,21 +36,14 @@ async function resizeColumn( } // TODO: remove when `userEvent.pointer` is supported -async function dragFill( - { page, iframe, project }: BrowserCommandContext, - from: string, - to: string -) { +async function dragFill({ page, iframe }: BrowserCommandContext, from: string, to: string) { await iframe.getByRole('gridcell', { name: from, exact: true }).click(); await iframe.locator('.rdg-cell-drag-handle').hover(); await page.mouse.down(); - await iframe.getByRole('gridcell', { name: to, exact: true }).hover(); - if (project.name.includes('webkit')) { - // let React re-render after handleDragHandlePointerMove calls setDraggedOverRowIdx() - await new Promise((resolve) => { - setTimeout(resolve, 20); - }); - } + const target = iframe.getByRole('gridcell', { name: to, exact: true }); + await target.hover(); + // the grid fills the cells it rendered as dragged over, so wait for it to render the drag + await target.and(iframe.locator('.rdg-cell-dragged-over')).waitFor(); await page.mouse.up(); } @@ -66,6 +61,23 @@ const playwrightOptions: PlaywrightProviderOptions = { viewport } }; +const firefoxProvider = playwright({ + ...playwrightOptions, + launchOptions: { + firefoxUserPrefs: { + 'accessibility.force_disabled': 1 + } + } +}); +// Firefox sends the mouse events of all its pages with the same pointer ID, +// so a page that captures the pointer also receives the other pages' mouse events. +// These tests drag a resize or drag-fill handle, which captures the pointer, +// so each of them runs in a browser of its own. +// https://github.com/microsoft/playwright/issues/43631 +const firefoxPointerCaptureTests = [ + 'browser/dragFill.test.tsx', + 'browser/column/resizable.test.tsx' +]; export default defineConfig({ staged: { @@ -264,6 +276,13 @@ export default defineConfig({ } }, slowTestThreshold: 1000, + // On Windows, WebKit slows down the mouse events of Playwright's Firefox, + // which dispatches those of all its pages one after the other, + // so with all cores but one as workers, Firefox's clicks time out. + // In a terminal, the browser instances get half the cores anyway, as in watch mode, + // since they don't inherit the `watch: false` of `vitest run`. + // https://github.com/vitest-dev/vitest/issues/11721 + ...(isWindows && { maxWorkers: '50%' }), browser: { headless: true, ui: false, @@ -290,17 +309,17 @@ export default defineConfig({ }, { browser: 'firefox', - provider: playwright({ - ...playwrightOptions, - launchOptions: { - firefoxUserPrefs: { - 'accessibility.force_disabled': 1 - } - } - }), - // TODO: remove when FF tests are stable - fileParallelism: false + exclude: firefoxPointerCaptureTests, + provider: firefoxProvider }, + // the names sort after `browser (webkit)`, so with few workers, as in CI, + // WebKit's tests start before these + ...firefoxPointerCaptureTests.map((test): BrowserInstanceOption => ({ + name: `firefox (${basename(test).split('.')[0]})`, + browser: 'firefox', + include: [test], + provider: firefoxProvider + })), { browser: 'webkit', provider: playwright(playwrightOptions)