diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 147a8ba3ec..3ce7fbc450 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -299,6 +299,11 @@ jobs: command: | vp test vp check --fix + - name: vitest-playwright-repro + node-version: 24 + command: | + npx playwright install chromium + vp test exclude: # frm-stack uses Docker (testcontainers) which doesn't work the same way on Windows - os: windows-latest diff --git a/ecosystem-ci/repo.json b/ecosystem-ci/repo.json index 63fb7b3f26..cfe198f8af 100644 --- a/ecosystem-ci/repo.json +++ b/ecosystem-ci/repo.json @@ -108,5 +108,10 @@ "branch": "main", "hash": "419653665e4f0688ad3cac68a34673fdd0632b55", "forceFreshMigration": true + }, + "vitest-playwright-repro": { + "repository": "https://github.com/why-reproductions-are-required/vitest-playwright-repro.git", + "branch": "main", + "hash": "f7252170025c01ec482fa9ad43e09b965f46928f" } } diff --git a/packages/cli/snap-tests-global/migration-from-vitest-config/snap.txt b/packages/cli/snap-tests-global/migration-from-vitest-config/snap.txt index 190eaba12a..ad63e3e64b 100644 --- a/packages/cli/snap-tests-global/migration-from-vitest-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-from-vitest-config/snap.txt @@ -44,6 +44,7 @@ export default defineConfig({ "devDependencies": { "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vitest": "npm:@voidzero-dev/vite-plus-test@latest", + "playwright": "*", "vite-plus": "latest" }, "pnpm": { diff --git a/packages/cli/snap-tests-global/migration-from-vitest-files/snap.txt b/packages/cli/snap-tests-global/migration-from-vitest-files/snap.txt index 1e5bd0e51d..49a6c41b9f 100644 --- a/packages/cli/snap-tests-global/migration-from-vitest-files/snap.txt +++ b/packages/cli/snap-tests-global/migration-from-vitest-files/snap.txt @@ -19,6 +19,7 @@ VITE+ - The Unified Toolchain for the Web "devDependencies": { "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vitest": "npm:@voidzero-dev/vite-plus-test@latest", + "playwright": "*", "vite-plus": "latest" }, "pnpm": { diff --git a/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt b/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt index bc115eb91e..551041e487 100644 --- a/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt +++ b/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt @@ -70,6 +70,7 @@ export default defineConfig({ "@vitejs/plugin-react": "^4.2.0", "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vitest": "npm:@voidzero-dev/vite-plus-test@latest", + "playwright": "*", "vite-plus": "latest" }, "pnpm": { diff --git a/packages/cli/src/migration/__tests__/migrator.spec.ts b/packages/cli/src/migration/__tests__/migrator.spec.ts index 47a9086448..63c41cdebe 100644 --- a/packages/cli/src/migration/__tests__/migrator.spec.ts +++ b/packages/cli/src/migration/__tests__/migrator.spec.ts @@ -129,6 +129,60 @@ describe('rewritePackageJson', () => { rewritePackageJson(pkg, PackageManager.yarn, true); expect(pkg).toMatchSnapshot(); }); + + it('should preserve playwright when removing @vitest/browser-playwright', async () => { + const pkg = { + devDependencies: { + '@vitest/browser': '^4.0.0', + '@vitest/browser-playwright': '^4.0.0', + vitest: '^4.0.0', + }, + }; + rewritePackageJson(pkg, PackageManager.pnpm); + expect(pkg.devDependencies).toHaveProperty('playwright', '*'); + expect(pkg.devDependencies).not.toHaveProperty('@vitest/browser'); + expect(pkg.devDependencies).not.toHaveProperty('@vitest/browser-playwright'); + }); + + it('should preserve webdriverio when removing @vitest/browser-webdriverio', async () => { + const pkg = { + devDependencies: { + '@vitest/browser': '^4.0.0', + '@vitest/browser-webdriverio': '^4.0.0', + vitest: '^4.0.0', + }, + }; + rewritePackageJson(pkg, PackageManager.pnpm); + expect(pkg.devDependencies).toHaveProperty('webdriverio', '*'); + expect(pkg.devDependencies).not.toHaveProperty('@vitest/browser-webdriverio'); + }); + + it('should not overwrite playwright if already in devDependencies', async () => { + const pkg = { + devDependencies: { + '@vitest/browser-playwright': '^4.0.0', + playwright: '^1.40.0', + vitest: '^4.0.0', + }, + }; + rewritePackageJson(pkg, PackageManager.pnpm); + expect(pkg.devDependencies).toHaveProperty('playwright', '^1.40.0'); + }); + + it('should not add playwright if already in dependencies', async () => { + const pkg = { + dependencies: { + playwright: '^1.40.0', + }, + devDependencies: { + '@vitest/browser-playwright': '^4.0.0', + vitest: '^4.0.0', + }, + }; + rewritePackageJson(pkg, PackageManager.pnpm); + expect(pkg.dependencies).toHaveProperty('playwright', '^1.40.0'); + expect(pkg.devDependencies).not.toHaveProperty('playwright'); + }); }); describe('parseNvmrcVersion', () => { diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index e844f42584..5b3151d68d 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -78,6 +78,13 @@ const REMOVE_PACKAGES = [ '@vitest/browser-webdriverio', ] as const; +// When a browser provider package is removed, its runtime peer dependency +// must be preserved in devDependencies so browser tests continue to work. +const BROWSER_PROVIDER_PEER_DEPS: Record = { + '@vitest/browser-playwright': 'playwright', + '@vitest/browser-webdriverio': 'webdriverio', +}; + function warnMigration(message: string, report?: MigrationReport) { addMigrationWarning(report, message); if (!report) { @@ -1203,14 +1210,27 @@ export function rewritePackageJson( } // remove packages that are replaced with vite-plus for (const name of REMOVE_PACKAGES) { - if (pkg.devDependencies?.[name]) { - delete pkg.devDependencies[name]; + const wasInDevDeps = !!pkg.devDependencies?.[name]; + const wasInDeps = !!pkg.dependencies?.[name]; + if (wasInDevDeps) { + delete pkg.devDependencies![name]; needVitePlus = true; } - if (pkg.dependencies?.[name]) { - delete pkg.dependencies[name]; + if (wasInDeps) { + delete pkg.dependencies![name]; needVitePlus = true; } + // e.g., removing @vitest/browser-playwright should keep `playwright` in devDeps + const peerDep = BROWSER_PROVIDER_PEER_DEPS[name]; + if ( + (wasInDevDeps || wasInDeps) && + peerDep && + !pkg.devDependencies?.[peerDep] && + !pkg.dependencies?.[peerDep] + ) { + pkg.devDependencies ??= {}; + pkg.devDependencies[peerDep] = '*'; + } } if (needVitePlus) { // add vite-plus to devDependencies diff --git a/packages/test/build.ts b/packages/test/build.ts index 3defd727f0..9f2d1cf3da 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -1637,6 +1637,18 @@ async function patchVitestBrowserPackage() { ); } + // 5. Patch version to use VP_VERSION, preventing the "Running mixed versions" warning + const versionPattern = /var version = "(\d+\.\d+\.\d+[^"]*)"/; + const beforeVersion = content; + content = content.replace(versionPattern, 'var version = process.env.VP_VERSION || "$1"'); + if (content === beforeVersion) { + throw new Error( + 'Failed to patch version in @vitest/browser/index.js: pattern not found. ' + + 'This likely means vitest code has changed and the patch needs to be updated.', + ); + } + console.log(' Patched version to use VP_VERSION env var'); + await writeFile(browserIndexPath, content, 'utf-8'); console.log(' Successfully patched @vitest/browser/index.js'); }