diff --git a/e2e/react-start/basic-prerender/.gitignore b/e2e/react-start/basic-prerender/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/react-start/basic-prerender/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/react-start/basic-prerender/package.json b/e2e/react-start/basic-prerender/package.json deleted file mode 100644 index 415e0f4739..0000000000 --- a/e2e/react-start/basic-prerender/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-react-start-e2e-basic-prerender", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=prerender playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-react-start-e2e-basic": "workspace:*", - "tanstack-react-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/react-start/basic-prerender/playwright.config.ts b/e2e/react-start/basic-prerender/playwright.config.ts deleted file mode 100644 index cf2cf5d396..0000000000 --- a/e2e/react-start/basic-prerender/playwright.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: - 'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start', - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'prerender', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/react-start/basic-preview/.gitignore b/e2e/react-start/basic-preview/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/react-start/basic-preview/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/react-start/basic-preview/package.json b/e2e/react-start/basic-preview/package.json deleted file mode 100644 index ba24e8a41a..0000000000 --- a/e2e/react-start/basic-preview/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-react-start-e2e-basic-preview", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=preview playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-react-start-e2e-basic": "workspace:*", - "tanstack-react-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/react-start/basic-preview/playwright.config.ts b/e2e/react-start/basic-preview/playwright.config.ts deleted file mode 100644 index 071f336f9f..0000000000 --- a/e2e/react-start/basic-preview/playwright.config.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: `pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build && pnpm --dir ../basic preview --port ${PORT}`, - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'preview', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/react-start/basic-spa/.gitignore b/e2e/react-start/basic-spa/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/react-start/basic-spa/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/react-start/basic-spa/package.json b/e2e/react-start/basic-spa/package.json deleted file mode 100644 index 709ce8a0d6..0000000000 --- a/e2e/react-start/basic-spa/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-react-start-e2e-basic-spa", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=spa playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-react-start-e2e-basic": "workspace:*", - "tanstack-react-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/react-start/basic-spa/playwright.config.ts b/e2e/react-start/basic-spa/playwright.config.ts deleted file mode 100644 index 27e85e1f1a..0000000000 --- a/e2e/react-start/basic-spa/playwright.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: - 'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:spa && pnpm --dir ../basic start', - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'spa', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/react-start/basic-test-suite/.gitignore b/e2e/react-start/basic-test-suite/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/react-start/basic-test-suite/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/react-start/basic-test-suite/package.json b/e2e/react-start/basic-test-suite/package.json deleted file mode 100644 index aaf431e09d..0000000000 --- a/e2e/react-start/basic-test-suite/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "tanstack-react-start-e2e-basic-test-suite", - "private": true, - "sideEffects": false, - "type": "module", - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:*", - "@types/node": "^22.10.2", - "combinate": "^1.1.11" - } -} diff --git a/e2e/react-start/basic-test-suite/src/utils/getBasicAppRoot.ts b/e2e/react-start/basic-test-suite/src/utils/getBasicAppRoot.ts deleted file mode 100644 index 4725f532ed..0000000000 --- a/e2e/react-start/basic-test-suite/src/utils/getBasicAppRoot.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { join } from 'node:path' -import { getPackageName } from './getPackageName.ts' - -export function getBasicAppRoot() { - return getPackageName() === 'tanstack-react-start-e2e-basic' - ? process.cwd() - : join(process.cwd(), '../basic') -} diff --git a/e2e/react-start/basic-test-suite/src/utils/isPrerender.ts b/e2e/react-start/basic-test-suite/src/utils/isPrerender.ts deleted file mode 100644 index d5d991d454..0000000000 --- a/e2e/react-start/basic-test-suite/src/utils/isPrerender.ts +++ /dev/null @@ -1 +0,0 @@ -export const isPrerender: boolean = process.env.MODE === 'prerender' diff --git a/e2e/react-start/basic-test-suite/src/utils/isPreview.ts b/e2e/react-start/basic-test-suite/src/utils/isPreview.ts deleted file mode 100644 index 7ea362a83e..0000000000 --- a/e2e/react-start/basic-test-suite/src/utils/isPreview.ts +++ /dev/null @@ -1 +0,0 @@ -export const isPreview: boolean = process.env.MODE === 'preview' diff --git a/e2e/react-start/basic-test-suite/src/utils/isSpaMode.ts b/e2e/react-start/basic-test-suite/src/utils/isSpaMode.ts deleted file mode 100644 index b4edb829a8..0000000000 --- a/e2e/react-start/basic-test-suite/src/utils/isSpaMode.ts +++ /dev/null @@ -1 +0,0 @@ -export const isSpaMode: boolean = process.env.MODE === 'spa' diff --git a/e2e/react-start/basic-test-suite/tsconfig.json b/e2e/react-start/basic-test-suite/tsconfig.json deleted file mode 100644 index 3f3dbe6bcd..0000000000 --- a/e2e/react-start/basic-test-suite/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "include": ["src/**/*.ts"], - "compilerOptions": { - "strict": true, - "esModuleInterop": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "isolatedModules": true, - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "target": "ES2022", - "types": ["node"], - "forceConsistentCasingInFileNames": true, - "noEmit": true - } -} diff --git a/e2e/react-start/basic/.gitignore b/e2e/react-start/basic/.gitignore index a79d5cf129..b9c2c7e5f6 100644 --- a/e2e/react-start/basic/.gitignore +++ b/e2e/react-start/basic/.gitignore @@ -18,3 +18,4 @@ yarn.lock /playwright-report/ /blob-report/ /playwright/.cache/ +/dist-vite-*/ diff --git a/e2e/react-start/basic/package.json b/e2e/react-start/basic/package.json index 957372b08e..6646dc03a3 100644 --- a/e2e/react-start/basic/package.json +++ b/e2e/react-start/basic/package.json @@ -7,13 +7,11 @@ "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", - "build:spa": "MODE=spa vite build && tsc --noEmit", - "build:prerender": "MODE=prerender vite build && tsc --noEmit", "preview": "vite preview", "start": "node server.js", - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf dist; rm -rf port*.txt; playwright test --project=chromium" + "test:e2e:startDummyServer": "node -e 'import(\"./tests/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"./tests/setup/waitForDummyServer.ts\").then(m => m.default())'", + "test:e2e:stopDummyServer": "node -e 'import(\"./tests/setup/global.teardown.ts\").then(m => m.default())'", + "test:e2e:local": "playwright test --project=chromium" }, "dependencies": { "@tanstack/react-router": "workspace:^", @@ -31,7 +29,6 @@ "@playwright/test": "^1.50.1", "@tailwindcss/vite": "^4.2.2", "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-react-start-e2e-basic-test-suite": "workspace:*", "@types/js-cookie": "^3.0.6", "@types/node": "^22.10.2", "@types/react": "^19.0.8", @@ -45,10 +42,25 @@ "zod": "^3.24.2" }, "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } + "metadata": { + "playwrightModes": [ + { + "toolchain": "vite", + "mode": "ssr" + }, + { + "toolchain": "vite", + "mode": "spa" + }, + { + "toolchain": "vite", + "mode": "prerender" + }, + { + "toolchain": "vite", + "mode": "preview" + } + ] } } } diff --git a/e2e/react-start/basic/playwright.config.ts b/e2e/react-start/basic/playwright.config.ts index bf22363c94..5e46a2b1c7 100644 --- a/e2e/react-start/basic/playwright.config.ts +++ b/e2e/react-start/basic/playwright.config.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs' import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, @@ -5,20 +6,37 @@ import { } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) +const mode = process.env.MODE ?? 'ssr' +const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name +const distDir = process.env.E2E_DIST_DIR ?? 'dist' + +if (process.env.TEST_WORKER_INDEX === undefined) { + for (const portFile of [ + `port-${e2ePortKey}.txt`, + `port-${e2ePortKey}_start.txt`, + `port-${e2ePortKey}-external.txt`, + ]) { + fs.rmSync(portFile, { force: true }) + } +} + +const PORT = await getTestServerPort(e2ePortKey) +const START_PORT = await getTestServerPort(`${e2ePortKey}_start`) +const EXTERNAL_PORT = await getDummyServerPort(e2ePortKey) const baseURL = `http://localhost:${PORT}` -const ssrModeCommand = `pnpm build && pnpm start` +const commandByMode = + mode === 'preview' + ? `pnpm run test:e2e:startDummyServer && pnpm preview --outDir ${distDir} --port ${PORT}` + : `pnpm run test:e2e:startDummyServer && pnpm start` /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: '../basic-test-suite/src', + testDir: './tests', workers: 1, reporter: [['line']], - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', + globalTeardown: './tests/setup/global.teardown.ts', use: { /* Base URL to use in actions like `await page.goto('/')`. */ @@ -26,17 +44,19 @@ export default defineConfig({ }, webServer: { - command: `pnpm run test:e2e:startDummyServer && ${ssrModeCommand}`, + command: commandByMode, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', env: { - MODE: '', + MODE: mode, VITE_NODE_ENV: 'test', VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), VITE_SERVER_PORT: String(PORT), START_PORT: String(START_PORT), PORT: String(PORT), + E2E_DIST_DIR: distDir, + E2E_PORT_KEY: e2ePortKey, }, }, diff --git a/e2e/react-start/basic/server.js b/e2e/react-start/basic/server.js index 83f5ff0079..9db7619ae1 100644 --- a/e2e/react-start/basic/server.js +++ b/e2e/react-start/basic/server.js @@ -9,16 +9,19 @@ const startPort = process.env.START_PORT || 3001 const isSpaMode = process.env.MODE === 'spa' const isPrerender = process.env.MODE === 'prerender' +const distDir = process.env.E2E_DIST_DIR || 'dist' +const distClientDir = path.resolve(distDir, 'client') +const distServerEntryPath = path.resolve(distDir, 'server', 'server.js') export async function createStartServer() { - const server = (await import('./dist/server/server.js')).default + const server = (await import(distServerEntryPath)).default const nodeHandler = toNodeHandler(server.fetch) const app = express() // to keep testing uniform stop express from redirecting /posts to /posts/ // when serving pre-rendered pages - app.use(express.static('./dist/client', { redirect: !isPrerender })) + app.use(express.static(distClientDir, { redirect: !isPrerender })) app.use(async (req, res, next) => { try { @@ -50,10 +53,10 @@ export async function createSpaServer() { }), ) - app.use(express.static('./dist/client')) + app.use(express.static(distClientDir)) app.get('/{*splat}', (req, res) => { - res.sendFile(path.resolve('./dist/client/index.html')) + res.sendFile(path.resolve(distClientDir, 'index.html')) }) return { app } diff --git a/e2e/react-start/basic-test-suite/src/client-only.spec.ts b/e2e/react-start/basic/tests/client-only.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/client-only.spec.ts rename to e2e/react-start/basic/tests/client-only.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/navigation.spec.ts b/e2e/react-start/basic/tests/navigation.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/navigation.spec.ts rename to e2e/react-start/basic/tests/navigation.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/not-found.spec.ts b/e2e/react-start/basic/tests/not-found.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/not-found.spec.ts rename to e2e/react-start/basic/tests/not-found.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/open-redirect-prevention.spec.ts b/e2e/react-start/basic/tests/open-redirect-prevention.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/open-redirect-prevention.spec.ts rename to e2e/react-start/basic/tests/open-redirect-prevention.spec.ts diff --git a/e2e/solid-start/basic-test-suite/src/prerendering.spec.ts b/e2e/react-start/basic/tests/prerendering.spec.ts similarity index 90% rename from e2e/solid-start/basic-test-suite/src/prerendering.spec.ts rename to e2e/react-start/basic/tests/prerendering.spec.ts index 66cc1f68ff..ddcf720a4c 100644 --- a/e2e/solid-start/basic-test-suite/src/prerendering.spec.ts +++ b/e2e/react-start/basic/tests/prerendering.spec.ts @@ -3,15 +3,18 @@ import { join } from 'node:path' import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' import { isPrerender } from './utils/isPrerender' -import { getBasicAppRoot } from './utils/getBasicAppRoot' + +const distDir = join( + process.cwd(), + process.env.E2E_DIST_DIR ?? 'dist', + 'client', +) test.describe('Prerender Static Path Discovery', () => { test.skip(!isPrerender, 'Skipping since not in prerender mode') test.describe('Build Output Verification', () => { test('should automatically discover and prerender static routes', () => { // Check that static routes were automatically discovered and prerendered - const distDir = join(getBasicAppRoot(), 'dist', 'client') - // These static routes should be automatically discovered and prerendered expect(existsSync(join(distDir, 'index.html'))).toBe(true) expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true) @@ -35,7 +38,6 @@ test.describe('Prerender Static Path Discovery', () => { test.describe('Static Files Verification', () => { test('should contain prerendered content in posts.html', () => { - const distDir = join(getBasicAppRoot(), 'dist', 'client') expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true) // "Select a post." should be in the prerendered HTML diff --git a/e2e/react-start/basic-test-suite/src/raw-stream.spec.ts b/e2e/react-start/basic/tests/raw-stream.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/raw-stream.spec.ts rename to e2e/react-start/basic/tests/raw-stream.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/redirect.spec.ts b/e2e/react-start/basic/tests/redirect.spec.ts similarity index 97% rename from e2e/react-start/basic-test-suite/src/redirect.spec.ts rename to e2e/react-start/basic/tests/redirect.spec.ts index 0763a901a2..1915388b0d 100644 --- a/e2e/react-start/basic-test-suite/src/redirect.spec.ts +++ b/e2e/react-start/basic/tests/redirect.spec.ts @@ -6,15 +6,15 @@ import { getTestServerPort, test, } from '@tanstack/router-e2e-utils' -import { getPackageName } from './utils/getPackageName.ts' +import { getE2EPortKey } from './utils/getE2EPortKey.ts' // somehow playwright does not correctly import default exports const combinate = (combinateImport as any).default as typeof combinateImport -const packageName = getPackageName() +const e2ePortKey = getE2EPortKey() -const PORT = await getTestServerPort(packageName) +const PORT = await getTestServerPort(e2ePortKey) -const EXTERNAL_HOST_PORT = await getDummyServerPort(packageName) +const EXTERNAL_HOST_PORT = await getDummyServerPort(e2ePortKey) test.describe('redirects', () => { test.describe('internal', () => { diff --git a/e2e/react-start/basic-test-suite/src/root-scripts.spec.ts b/e2e/react-start/basic/tests/root-scripts.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/root-scripts.spec.ts rename to e2e/react-start/basic/tests/root-scripts.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/script-duplication.spec.ts b/e2e/react-start/basic/tests/script-duplication.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/script-duplication.spec.ts rename to e2e/react-start/basic/tests/script-duplication.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/search-params.spec.ts b/e2e/react-start/basic/tests/search-params.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/search-params.spec.ts rename to e2e/react-start/basic/tests/search-params.spec.ts diff --git a/e2e/solid-start/basic-test-suite/src/setup/global.setup.ts b/e2e/react-start/basic/tests/setup/global.setup.ts similarity index 50% rename from e2e/solid-start/basic-test-suite/src/setup/global.setup.ts rename to e2e/react-start/basic/tests/setup/global.setup.ts index fc06a65591..cd38c35885 100644 --- a/e2e/solid-start/basic-test-suite/src/setup/global.setup.ts +++ b/e2e/react-start/basic/tests/setup/global.setup.ts @@ -1,6 +1,6 @@ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' export default async function setup() { - await e2eStartDummyServer(getPackageName()) + await e2eStartDummyServer(getE2EPortKey()) } diff --git a/e2e/vue-start/basic-test-suite/src/setup/global.teardown.ts b/e2e/react-start/basic/tests/setup/global.teardown.ts similarity index 55% rename from e2e/vue-start/basic-test-suite/src/setup/global.teardown.ts rename to e2e/react-start/basic/tests/setup/global.teardown.ts index a8a6899700..36e5c92858 100644 --- a/e2e/vue-start/basic-test-suite/src/setup/global.teardown.ts +++ b/e2e/react-start/basic/tests/setup/global.teardown.ts @@ -1,8 +1,8 @@ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' export default async function teardown() { try { - await e2eStopDummyServer(getPackageName()) + await e2eStopDummyServer(getE2EPortKey()) } catch {} } diff --git a/e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts b/e2e/react-start/basic/tests/setup/waitForDummyServer.ts similarity index 82% rename from e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts rename to e2e/react-start/basic/tests/setup/waitForDummyServer.ts index f27dc86d5b..88c18fd79e 100644 --- a/e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts +++ b/e2e/react-start/basic/tests/setup/waitForDummyServer.ts @@ -1,11 +1,11 @@ import { getDummyServerPort } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' const timeoutMs = 10_000 const retryIntervalMs = 100 export default async function waitForDummyServer() { - const port = await getDummyServerPort(getPackageName()) + const port = await getDummyServerPort(getE2EPortKey()) const deadline = Date.now() + timeoutMs while (Date.now() < deadline) { diff --git a/e2e/react-start/basic-test-suite/src/special-characters.spec.ts b/e2e/react-start/basic/tests/special-characters.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/special-characters.spec.ts rename to e2e/react-start/basic/tests/special-characters.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/streaming.spec.ts b/e2e/react-start/basic/tests/streaming.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/streaming.spec.ts rename to e2e/react-start/basic/tests/streaming.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/type-only-reexport.spec.ts b/e2e/react-start/basic/tests/type-only-reexport.spec.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/type-only-reexport.spec.ts rename to e2e/react-start/basic/tests/type-only-reexport.spec.ts diff --git a/e2e/react-start/basic/tests/utils/getE2EPortKey.ts b/e2e/react-start/basic/tests/utils/getE2EPortKey.ts new file mode 100644 index 0000000000..48be6a9027 --- /dev/null +++ b/e2e/react-start/basic/tests/utils/getE2EPortKey.ts @@ -0,0 +1,5 @@ +import { getPackageName } from './getPackageName.ts' + +export function getE2EPortKey() { + return process.env.E2E_PORT_KEY ?? getPackageName() +} diff --git a/e2e/react-start/basic-test-suite/src/utils/getPackageName.ts b/e2e/react-start/basic/tests/utils/getPackageName.ts similarity index 100% rename from e2e/react-start/basic-test-suite/src/utils/getPackageName.ts rename to e2e/react-start/basic/tests/utils/getPackageName.ts diff --git a/e2e/react-start/basic/tsconfig.json b/e2e/react-start/basic/tsconfig.json index af81492efb..e486543b96 100644 --- a/e2e/react-start/basic/tsconfig.json +++ b/e2e/react-start/basic/tsconfig.json @@ -8,9 +8,11 @@ "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, + "allowImportingTsExtensions": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", + "types": ["node"], "allowJs": true, "forceConsistentCasingInFileNames": true, "paths": { diff --git a/e2e/react-start/basic/vite.config.ts b/e2e/react-start/basic/vite.config.ts index 8ac18ef504..f88efe304f 100644 --- a/e2e/react-start/basic/vite.config.ts +++ b/e2e/react-start/basic/vite.config.ts @@ -28,8 +28,13 @@ const prerenderConfiguration = { maxRedirects: 100, } +const outDir = process.env.E2E_DIST_DIR ?? 'dist' + export default defineConfig({ resolve: { tsconfigPaths: true }, + build: { + outDir, + }, server: { port: 3000, }, diff --git a/e2e/solid-start/basic-prerender/.gitignore b/e2e/solid-start/basic-prerender/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/solid-start/basic-prerender/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/solid-start/basic-prerender/package.json b/e2e/solid-start/basic-prerender/package.json deleted file mode 100644 index eda156f8e3..0000000000 --- a/e2e/solid-start/basic-prerender/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-solid-start-e2e-basic-prerender", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=prerender playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-solid-start-e2e-basic": "workspace:*", - "tanstack-solid-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/solid-start/basic-prerender/playwright.config.ts b/e2e/solid-start/basic-prerender/playwright.config.ts deleted file mode 100644 index cf2cf5d396..0000000000 --- a/e2e/solid-start/basic-prerender/playwright.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: - 'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start', - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'prerender', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/solid-start/basic-preview/.gitignore b/e2e/solid-start/basic-preview/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/solid-start/basic-preview/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/solid-start/basic-preview/package.json b/e2e/solid-start/basic-preview/package.json deleted file mode 100644 index 233d6af609..0000000000 --- a/e2e/solid-start/basic-preview/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-solid-start-e2e-basic-preview", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=preview playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-solid-start-e2e-basic": "workspace:*", - "tanstack-solid-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/solid-start/basic-preview/playwright.config.ts b/e2e/solid-start/basic-preview/playwright.config.ts deleted file mode 100644 index 071f336f9f..0000000000 --- a/e2e/solid-start/basic-preview/playwright.config.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: `pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build && pnpm --dir ../basic preview --port ${PORT}`, - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'preview', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/solid-start/basic-spa/.gitignore b/e2e/solid-start/basic-spa/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/solid-start/basic-spa/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/solid-start/basic-spa/package.json b/e2e/solid-start/basic-spa/package.json deleted file mode 100644 index c9f0283736..0000000000 --- a/e2e/solid-start/basic-spa/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-solid-start-e2e-basic-spa", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=spa playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-solid-start-e2e-basic": "workspace:*", - "tanstack-solid-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/solid-start/basic-spa/playwright.config.ts b/e2e/solid-start/basic-spa/playwright.config.ts deleted file mode 100644 index 27e85e1f1a..0000000000 --- a/e2e/solid-start/basic-spa/playwright.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: - 'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:spa && pnpm --dir ../basic start', - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'spa', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/solid-start/basic-test-suite/.gitignore b/e2e/solid-start/basic-test-suite/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/solid-start/basic-test-suite/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/solid-start/basic-test-suite/package.json b/e2e/solid-start/basic-test-suite/package.json deleted file mode 100644 index 775f9fc598..0000000000 --- a/e2e/solid-start/basic-test-suite/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "tanstack-solid-start-e2e-basic-test-suite", - "private": true, - "sideEffects": false, - "type": "module", - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "@types/node": "^22.10.2", - "combinate": "^1.1.11" - } -} diff --git a/e2e/solid-start/basic-test-suite/src/utils/getBasicAppRoot.ts b/e2e/solid-start/basic-test-suite/src/utils/getBasicAppRoot.ts deleted file mode 100644 index 12a411989a..0000000000 --- a/e2e/solid-start/basic-test-suite/src/utils/getBasicAppRoot.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { join } from 'node:path' -import { getPackageName } from './getPackageName.ts' - -export function getBasicAppRoot() { - return getPackageName().endsWith('-basic') - ? process.cwd() - : join(process.cwd(), '../basic') -} diff --git a/e2e/solid-start/basic-test-suite/src/utils/isPrerender.ts b/e2e/solid-start/basic-test-suite/src/utils/isPrerender.ts deleted file mode 100644 index d5d991d454..0000000000 --- a/e2e/solid-start/basic-test-suite/src/utils/isPrerender.ts +++ /dev/null @@ -1 +0,0 @@ -export const isPrerender: boolean = process.env.MODE === 'prerender' diff --git a/e2e/solid-start/basic-test-suite/src/utils/isPreview.ts b/e2e/solid-start/basic-test-suite/src/utils/isPreview.ts deleted file mode 100644 index 7ea362a83e..0000000000 --- a/e2e/solid-start/basic-test-suite/src/utils/isPreview.ts +++ /dev/null @@ -1 +0,0 @@ -export const isPreview: boolean = process.env.MODE === 'preview' diff --git a/e2e/solid-start/basic-test-suite/src/utils/isSpaMode.ts b/e2e/solid-start/basic-test-suite/src/utils/isSpaMode.ts deleted file mode 100644 index b4edb829a8..0000000000 --- a/e2e/solid-start/basic-test-suite/src/utils/isSpaMode.ts +++ /dev/null @@ -1 +0,0 @@ -export const isSpaMode: boolean = process.env.MODE === 'spa' diff --git a/e2e/solid-start/basic-test-suite/tsconfig.json b/e2e/solid-start/basic-test-suite/tsconfig.json deleted file mode 100644 index 3f3dbe6bcd..0000000000 --- a/e2e/solid-start/basic-test-suite/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "include": ["src/**/*.ts"], - "compilerOptions": { - "strict": true, - "esModuleInterop": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "isolatedModules": true, - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "target": "ES2022", - "types": ["node"], - "forceConsistentCasingInFileNames": true, - "noEmit": true - } -} diff --git a/e2e/solid-start/basic/.gitignore b/e2e/solid-start/basic/.gitignore index a79d5cf129..b9c2c7e5f6 100644 --- a/e2e/solid-start/basic/.gitignore +++ b/e2e/solid-start/basic/.gitignore @@ -18,3 +18,4 @@ yarn.lock /playwright-report/ /blob-report/ /playwright/.cache/ +/dist-vite-*/ diff --git a/e2e/solid-start/basic/package.json b/e2e/solid-start/basic/package.json index 09e4b11f6b..e166a886ba 100644 --- a/e2e/solid-start/basic/package.json +++ b/e2e/solid-start/basic/package.json @@ -7,13 +7,11 @@ "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", - "build:spa": "MODE=spa vite build && tsc --noEmit", - "build:prerender": "MODE=prerender vite build && tsc --noEmit", "preview": "vite preview", "start": "node server.js", - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf dist; rm -rf port*.txt; playwright test --project=chromium" + "test:e2e:startDummyServer": "node -e 'import(\"./tests/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"./tests/setup/waitForDummyServer.ts\").then(m => m.default())'", + "test:e2e:stopDummyServer": "node -e 'import(\"./tests/setup/global.teardown.ts\").then(m => m.default())'", + "test:e2e:local": "playwright test --project=chromium" }, "dependencies": { "@tanstack/solid-router": "workspace:^", @@ -37,15 +35,30 @@ "combinate": "^1.1.11", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", - "tanstack-solid-start-e2e-basic-test-suite": "workspace:*", "typescript": "^6.0.2", "vite-plugin-solid": "^2.11.11" }, "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } + "metadata": { + "playwrightModes": [ + { + "toolchain": "vite", + "mode": "ssr", + "shards": 4 + }, + { + "toolchain": "vite", + "mode": "spa" + }, + { + "toolchain": "vite", + "mode": "prerender" + }, + { + "toolchain": "vite", + "mode": "preview" + } + ] } } } diff --git a/e2e/solid-start/basic/playwright.config.ts b/e2e/solid-start/basic/playwright.config.ts index 0bc99a1ec4..6924134cd1 100644 --- a/e2e/solid-start/basic/playwright.config.ts +++ b/e2e/solid-start/basic/playwright.config.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs' import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, @@ -5,35 +6,54 @@ import { } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) +const mode = process.env.MODE ?? 'ssr' +const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name +const distDir = process.env.E2E_DIST_DIR ?? 'dist' + +if (process.env.TEST_WORKER_INDEX === undefined) { + for (const portFile of [ + `port-${e2ePortKey}.txt`, + `port-${e2ePortKey}_start.txt`, + `port-${e2ePortKey}-external.txt`, + ]) { + fs.rmSync(portFile, { force: true }) + } +} + +const PORT = await getTestServerPort(e2ePortKey) +const START_PORT = await getTestServerPort(`${e2ePortKey}_start`) +const EXTERNAL_PORT = await getDummyServerPort(e2ePortKey) const baseURL = `http://localhost:${PORT}` -const ssrModeCommand = `pnpm build && pnpm start` +const commandByMode = + mode === 'preview' + ? `pnpm run test:e2e:startDummyServer && pnpm preview --outDir ${distDir} --port ${PORT}` + : `pnpm run test:e2e:startDummyServer && pnpm start` export default defineConfig({ - testDir: '../basic-test-suite/src', + testDir: './tests', workers: 1, reporter: [['line']], - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', + globalTeardown: './tests/setup/global.teardown.ts', use: { baseURL, }, webServer: { - command: `pnpm run test:e2e:startDummyServer && ${ssrModeCommand}`, + command: commandByMode, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', env: { - MODE: '', + MODE: mode, VITE_NODE_ENV: 'test', VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), VITE_SERVER_PORT: String(PORT), START_PORT: String(START_PORT), PORT: String(PORT), + E2E_DIST_DIR: distDir, + E2E_PORT_KEY: e2ePortKey, }, }, diff --git a/e2e/solid-start/basic/server.js b/e2e/solid-start/basic/server.js index 83f5ff0079..9db7619ae1 100644 --- a/e2e/solid-start/basic/server.js +++ b/e2e/solid-start/basic/server.js @@ -9,16 +9,19 @@ const startPort = process.env.START_PORT || 3001 const isSpaMode = process.env.MODE === 'spa' const isPrerender = process.env.MODE === 'prerender' +const distDir = process.env.E2E_DIST_DIR || 'dist' +const distClientDir = path.resolve(distDir, 'client') +const distServerEntryPath = path.resolve(distDir, 'server', 'server.js') export async function createStartServer() { - const server = (await import('./dist/server/server.js')).default + const server = (await import(distServerEntryPath)).default const nodeHandler = toNodeHandler(server.fetch) const app = express() // to keep testing uniform stop express from redirecting /posts to /posts/ // when serving pre-rendered pages - app.use(express.static('./dist/client', { redirect: !isPrerender })) + app.use(express.static(distClientDir, { redirect: !isPrerender })) app.use(async (req, res, next) => { try { @@ -50,10 +53,10 @@ export async function createSpaServer() { }), ) - app.use(express.static('./dist/client')) + app.use(express.static(distClientDir)) app.get('/{*splat}', (req, res) => { - res.sendFile(path.resolve('./dist/client/index.html')) + res.sendFile(path.resolve(distClientDir, 'index.html')) }) return { app } diff --git a/e2e/solid-start/basic-test-suite/src/navigation.spec.ts b/e2e/solid-start/basic/tests/navigation.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/navigation.spec.ts rename to e2e/solid-start/basic/tests/navigation.spec.ts diff --git a/e2e/solid-start/basic-test-suite/src/not-found.spec.ts b/e2e/solid-start/basic/tests/not-found.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/not-found.spec.ts rename to e2e/solid-start/basic/tests/not-found.spec.ts diff --git a/e2e/vue-start/basic-test-suite/src/prerendering.spec.ts b/e2e/solid-start/basic/tests/prerendering.spec.ts similarity index 90% rename from e2e/vue-start/basic-test-suite/src/prerendering.spec.ts rename to e2e/solid-start/basic/tests/prerendering.spec.ts index 66cc1f68ff..ddcf720a4c 100644 --- a/e2e/vue-start/basic-test-suite/src/prerendering.spec.ts +++ b/e2e/solid-start/basic/tests/prerendering.spec.ts @@ -3,15 +3,18 @@ import { join } from 'node:path' import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' import { isPrerender } from './utils/isPrerender' -import { getBasicAppRoot } from './utils/getBasicAppRoot' + +const distDir = join( + process.cwd(), + process.env.E2E_DIST_DIR ?? 'dist', + 'client', +) test.describe('Prerender Static Path Discovery', () => { test.skip(!isPrerender, 'Skipping since not in prerender mode') test.describe('Build Output Verification', () => { test('should automatically discover and prerender static routes', () => { // Check that static routes were automatically discovered and prerendered - const distDir = join(getBasicAppRoot(), 'dist', 'client') - // These static routes should be automatically discovered and prerendered expect(existsSync(join(distDir, 'index.html'))).toBe(true) expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true) @@ -35,7 +38,6 @@ test.describe('Prerender Static Path Discovery', () => { test.describe('Static Files Verification', () => { test('should contain prerendered content in posts.html', () => { - const distDir = join(getBasicAppRoot(), 'dist', 'client') expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true) // "Select a post." should be in the prerendered HTML diff --git a/e2e/solid-start/basic-test-suite/src/redirect.spec.ts b/e2e/solid-start/basic/tests/redirect.spec.ts similarity index 97% rename from e2e/solid-start/basic-test-suite/src/redirect.spec.ts rename to e2e/solid-start/basic/tests/redirect.spec.ts index c25a6b0dbf..2cad7d8f9d 100644 --- a/e2e/solid-start/basic-test-suite/src/redirect.spec.ts +++ b/e2e/solid-start/basic/tests/redirect.spec.ts @@ -6,14 +6,14 @@ import { getTestServerPort, test, } from '@tanstack/router-e2e-utils' -import { getPackageName } from './utils/getPackageName.ts' +import { getE2EPortKey } from './utils/getE2EPortKey.ts' // somehow playwright does not correctly import default exports const combinate = (combinateImport as any).default as typeof combinateImport -const packageName = getPackageName() -const PORT = await getTestServerPort(packageName) -const EXTERNAL_HOST_PORT = await getDummyServerPort(packageName) +const e2ePortKey = getE2EPortKey() +const PORT = await getTestServerPort(e2ePortKey) +const EXTERNAL_HOST_PORT = await getDummyServerPort(e2ePortKey) test.describe('redirects', () => { const internalNavigationTestMatrix = combinate({ diff --git a/e2e/solid-start/basic-test-suite/src/script-duplication.spec.ts b/e2e/solid-start/basic/tests/script-duplication.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/script-duplication.spec.ts rename to e2e/solid-start/basic/tests/script-duplication.spec.ts diff --git a/e2e/solid-start/basic-test-suite/src/search-params.spec.ts b/e2e/solid-start/basic/tests/search-params.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/search-params.spec.ts rename to e2e/solid-start/basic/tests/search-params.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/setup/global.setup.ts b/e2e/solid-start/basic/tests/setup/global.setup.ts similarity index 50% rename from e2e/react-start/basic-test-suite/src/setup/global.setup.ts rename to e2e/solid-start/basic/tests/setup/global.setup.ts index fc06a65591..cd38c35885 100644 --- a/e2e/react-start/basic-test-suite/src/setup/global.setup.ts +++ b/e2e/solid-start/basic/tests/setup/global.setup.ts @@ -1,6 +1,6 @@ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' export default async function setup() { - await e2eStartDummyServer(getPackageName()) + await e2eStartDummyServer(getE2EPortKey()) } diff --git a/e2e/react-start/basic-test-suite/src/setup/global.teardown.ts b/e2e/solid-start/basic/tests/setup/global.teardown.ts similarity index 55% rename from e2e/react-start/basic-test-suite/src/setup/global.teardown.ts rename to e2e/solid-start/basic/tests/setup/global.teardown.ts index a8a6899700..36e5c92858 100644 --- a/e2e/react-start/basic-test-suite/src/setup/global.teardown.ts +++ b/e2e/solid-start/basic/tests/setup/global.teardown.ts @@ -1,8 +1,8 @@ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' export default async function teardown() { try { - await e2eStopDummyServer(getPackageName()) + await e2eStopDummyServer(getE2EPortKey()) } catch {} } diff --git a/e2e/vue-start/basic-test-suite/src/setup/waitForDummyServer.ts b/e2e/solid-start/basic/tests/setup/waitForDummyServer.ts similarity index 82% rename from e2e/vue-start/basic-test-suite/src/setup/waitForDummyServer.ts rename to e2e/solid-start/basic/tests/setup/waitForDummyServer.ts index f27dc86d5b..88c18fd79e 100644 --- a/e2e/vue-start/basic-test-suite/src/setup/waitForDummyServer.ts +++ b/e2e/solid-start/basic/tests/setup/waitForDummyServer.ts @@ -1,11 +1,11 @@ import { getDummyServerPort } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' const timeoutMs = 10_000 const retryIntervalMs = 100 export default async function waitForDummyServer() { - const port = await getDummyServerPort(getPackageName()) + const port = await getDummyServerPort(getE2EPortKey()) const deadline = Date.now() + timeoutMs while (Date.now() < deadline) { diff --git a/e2e/solid-start/basic-test-suite/src/special-characters.spec.ts b/e2e/solid-start/basic/tests/special-characters.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/special-characters.spec.ts rename to e2e/solid-start/basic/tests/special-characters.spec.ts diff --git a/e2e/solid-start/basic-test-suite/src/streaming.spec.ts b/e2e/solid-start/basic/tests/streaming.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/streaming.spec.ts rename to e2e/solid-start/basic/tests/streaming.spec.ts diff --git a/e2e/solid-start/basic-test-suite/src/transition.spec.ts b/e2e/solid-start/basic/tests/transition.spec.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/transition.spec.ts rename to e2e/solid-start/basic/tests/transition.spec.ts diff --git a/e2e/solid-start/basic/tests/utils/getE2EPortKey.ts b/e2e/solid-start/basic/tests/utils/getE2EPortKey.ts new file mode 100644 index 0000000000..48be6a9027 --- /dev/null +++ b/e2e/solid-start/basic/tests/utils/getE2EPortKey.ts @@ -0,0 +1,5 @@ +import { getPackageName } from './getPackageName.ts' + +export function getE2EPortKey() { + return process.env.E2E_PORT_KEY ?? getPackageName() +} diff --git a/e2e/solid-start/basic-test-suite/src/utils/getPackageName.ts b/e2e/solid-start/basic/tests/utils/getPackageName.ts similarity index 100% rename from e2e/solid-start/basic-test-suite/src/utils/getPackageName.ts rename to e2e/solid-start/basic/tests/utils/getPackageName.ts diff --git a/e2e/solid-start/basic/tsconfig.json b/e2e/solid-start/basic/tsconfig.json index cb1a0b80dd..0543683c33 100644 --- a/e2e/solid-start/basic/tsconfig.json +++ b/e2e/solid-start/basic/tsconfig.json @@ -9,6 +9,7 @@ "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, + "allowImportingTsExtensions": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", diff --git a/e2e/solid-start/basic/vite.config.ts b/e2e/solid-start/basic/vite.config.ts index 3cca892d57..9e26483d04 100644 --- a/e2e/solid-start/basic/vite.config.ts +++ b/e2e/solid-start/basic/vite.config.ts @@ -30,8 +30,13 @@ const prerenderConfiguration = { maxRedirects: 100, } +const outDir = process.env.E2E_DIST_DIR ?? 'dist' + export default defineConfig({ resolve: { tsconfigPaths: true }, + build: { + outDir, + }, server: { port: 3000, }, diff --git a/e2e/vue-start/basic-prerender/.gitignore b/e2e/vue-start/basic-prerender/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/vue-start/basic-prerender/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/vue-start/basic-prerender/package.json b/e2e/vue-start/basic-prerender/package.json deleted file mode 100644 index 5b05d8f771..0000000000 --- a/e2e/vue-start/basic-prerender/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-vue-start-e2e-basic-prerender", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=prerender playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-vue-start-e2e-basic": "workspace:*", - "tanstack-vue-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/vue-start/basic-prerender/playwright.config.ts b/e2e/vue-start/basic-prerender/playwright.config.ts deleted file mode 100644 index cf2cf5d396..0000000000 --- a/e2e/vue-start/basic-prerender/playwright.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: - 'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start', - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'prerender', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/vue-start/basic-preview/.gitignore b/e2e/vue-start/basic-preview/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/vue-start/basic-preview/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/vue-start/basic-preview/package.json b/e2e/vue-start/basic-preview/package.json deleted file mode 100644 index 663b1365f1..0000000000 --- a/e2e/vue-start/basic-preview/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-vue-start-e2e-basic-preview", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=preview playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-vue-start-e2e-basic": "workspace:*", - "tanstack-vue-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/vue-start/basic-preview/playwright.config.ts b/e2e/vue-start/basic-preview/playwright.config.ts deleted file mode 100644 index 071f336f9f..0000000000 --- a/e2e/vue-start/basic-preview/playwright.config.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: `pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build && pnpm --dir ../basic preview --port ${PORT}`, - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'preview', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/vue-start/basic-spa/.gitignore b/e2e/vue-start/basic-spa/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/vue-start/basic-spa/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/vue-start/basic-spa/package.json b/e2e/vue-start/basic-spa/package.json deleted file mode 100644 index 2186636549..0000000000 --- a/e2e/vue-start/basic-spa/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tanstack-vue-start-e2e-basic-spa", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf port*.txt; MODE=spa playwright test --project=chromium" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "tanstack-vue-start-e2e-basic": "workspace:*", - "tanstack-vue-start-e2e-basic-test-suite": "workspace:*" - }, - "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } - } - } -} diff --git a/e2e/vue-start/basic-spa/playwright.config.ts b/e2e/vue-start/basic-spa/playwright.config.ts deleted file mode 100644 index 27e85e1f1a..0000000000 --- a/e2e/vue-start/basic-spa/playwright.config.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defineConfig, devices } from '@playwright/test' -import { - getDummyServerPort, - getTestServerPort, -} from '@tanstack/router-e2e-utils' -import packageJson from './package.json' with { type: 'json' } - -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - testDir: '../basic-test-suite/src', - workers: 1, - reporter: [['line']], - - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', - - use: { - baseURL, - }, - - webServer: { - command: - 'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:spa && pnpm --dir ../basic start', - url: baseURL, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - env: { - MODE: 'spa', - VITE_NODE_ENV: 'test', - VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), - VITE_SERVER_PORT: String(PORT), - START_PORT: String(START_PORT), - PORT: String(PORT), - }, - }, - - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}) diff --git a/e2e/vue-start/basic-test-suite/.gitignore b/e2e/vue-start/basic-test-suite/.gitignore deleted file mode 100644 index 6381bf3860..0000000000 --- a/e2e/vue-start/basic-test-suite/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules - -/test-results/ -/playwright-report/ -/blob-report/ diff --git a/e2e/vue-start/basic-test-suite/package.json b/e2e/vue-start/basic-test-suite/package.json deleted file mode 100644 index 7d5ea7c002..0000000000 --- a/e2e/vue-start/basic-test-suite/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "tanstack-vue-start-e2e-basic-test-suite", - "private": true, - "sideEffects": false, - "type": "module", - "devDependencies": { - "@playwright/test": "^1.50.1", - "@tanstack/router-e2e-utils": "workspace:^", - "@types/node": "^22.10.2", - "combinate": "^1.1.11" - } -} diff --git a/e2e/vue-start/basic-test-suite/src/utils/getBasicAppRoot.ts b/e2e/vue-start/basic-test-suite/src/utils/getBasicAppRoot.ts deleted file mode 100644 index 12a411989a..0000000000 --- a/e2e/vue-start/basic-test-suite/src/utils/getBasicAppRoot.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { join } from 'node:path' -import { getPackageName } from './getPackageName.ts' - -export function getBasicAppRoot() { - return getPackageName().endsWith('-basic') - ? process.cwd() - : join(process.cwd(), '../basic') -} diff --git a/e2e/vue-start/basic-test-suite/src/utils/isPrerender.ts b/e2e/vue-start/basic-test-suite/src/utils/isPrerender.ts deleted file mode 100644 index d5d991d454..0000000000 --- a/e2e/vue-start/basic-test-suite/src/utils/isPrerender.ts +++ /dev/null @@ -1 +0,0 @@ -export const isPrerender: boolean = process.env.MODE === 'prerender' diff --git a/e2e/vue-start/basic-test-suite/src/utils/isPreview.ts b/e2e/vue-start/basic-test-suite/src/utils/isPreview.ts deleted file mode 100644 index 7ea362a83e..0000000000 --- a/e2e/vue-start/basic-test-suite/src/utils/isPreview.ts +++ /dev/null @@ -1 +0,0 @@ -export const isPreview: boolean = process.env.MODE === 'preview' diff --git a/e2e/vue-start/basic-test-suite/src/utils/isSpaMode.ts b/e2e/vue-start/basic-test-suite/src/utils/isSpaMode.ts deleted file mode 100644 index b4edb829a8..0000000000 --- a/e2e/vue-start/basic-test-suite/src/utils/isSpaMode.ts +++ /dev/null @@ -1 +0,0 @@ -export const isSpaMode: boolean = process.env.MODE === 'spa' diff --git a/e2e/vue-start/basic-test-suite/tsconfig.json b/e2e/vue-start/basic-test-suite/tsconfig.json deleted file mode 100644 index 3f3dbe6bcd..0000000000 --- a/e2e/vue-start/basic-test-suite/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "include": ["src/**/*.ts"], - "compilerOptions": { - "strict": true, - "esModuleInterop": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "isolatedModules": true, - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "target": "ES2022", - "types": ["node"], - "forceConsistentCasingInFileNames": true, - "noEmit": true - } -} diff --git a/e2e/vue-start/basic/.gitignore b/e2e/vue-start/basic/.gitignore index a79d5cf129..b9c2c7e5f6 100644 --- a/e2e/vue-start/basic/.gitignore +++ b/e2e/vue-start/basic/.gitignore @@ -18,3 +18,4 @@ yarn.lock /playwright-report/ /blob-report/ /playwright/.cache/ +/dist-vite-*/ diff --git a/e2e/vue-start/basic/package.json b/e2e/vue-start/basic/package.json index ab56c475ef..6950d738ea 100644 --- a/e2e/vue-start/basic/package.json +++ b/e2e/vue-start/basic/package.json @@ -7,13 +7,11 @@ "dev": "vite dev --port 3000", "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", - "build:spa": "MODE=spa vite build && tsc --noEmit", - "build:prerender": "MODE=prerender vite build && tsc --noEmit", "preview": "vite preview", "start": "node server.js", - "test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'", - "test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'", - "test:e2e": "rm -rf dist; rm -rf port*.txt; playwright test --project=chromium" + "test:e2e:startDummyServer": "node -e 'import(\"./tests/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"./tests/setup/waitForDummyServer.ts\").then(m => m.default())'", + "test:e2e:stopDummyServer": "node -e 'import(\"./tests/setup/global.teardown.ts\").then(m => m.default())'", + "test:e2e:local": "playwright test --project=chromium" }, "dependencies": { "@tanstack/vue-router": "workspace:^", @@ -39,14 +37,29 @@ "combinate": "^1.1.11", "srvx": "^0.11.9", "tailwindcss": "^4.2.2", - "tanstack-vue-start-e2e-basic-test-suite": "workspace:*", "typescript": "^6.0.2" }, "nx": { - "targets": { - "test:e2e": { - "parallelism": false - } + "metadata": { + "playwrightModes": [ + { + "toolchain": "vite", + "mode": "ssr", + "shards": 4 + }, + { + "toolchain": "vite", + "mode": "spa" + }, + { + "toolchain": "vite", + "mode": "prerender" + }, + { + "toolchain": "vite", + "mode": "preview" + } + ] } } } diff --git a/e2e/vue-start/basic/playwright.config.ts b/e2e/vue-start/basic/playwright.config.ts index 0bc99a1ec4..6924134cd1 100644 --- a/e2e/vue-start/basic/playwright.config.ts +++ b/e2e/vue-start/basic/playwright.config.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs' import { defineConfig, devices } from '@playwright/test' import { getDummyServerPort, @@ -5,35 +6,54 @@ import { } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } -const PORT = await getTestServerPort(packageJson.name) -const START_PORT = await getTestServerPort(`${packageJson.name}_start`) -const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) +const mode = process.env.MODE ?? 'ssr' +const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name +const distDir = process.env.E2E_DIST_DIR ?? 'dist' + +if (process.env.TEST_WORKER_INDEX === undefined) { + for (const portFile of [ + `port-${e2ePortKey}.txt`, + `port-${e2ePortKey}_start.txt`, + `port-${e2ePortKey}-external.txt`, + ]) { + fs.rmSync(portFile, { force: true }) + } +} + +const PORT = await getTestServerPort(e2ePortKey) +const START_PORT = await getTestServerPort(`${e2ePortKey}_start`) +const EXTERNAL_PORT = await getDummyServerPort(e2ePortKey) const baseURL = `http://localhost:${PORT}` -const ssrModeCommand = `pnpm build && pnpm start` +const commandByMode = + mode === 'preview' + ? `pnpm run test:e2e:startDummyServer && pnpm preview --outDir ${distDir} --port ${PORT}` + : `pnpm run test:e2e:startDummyServer && pnpm start` export default defineConfig({ - testDir: '../basic-test-suite/src', + testDir: './tests', workers: 1, reporter: [['line']], - globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts', + globalTeardown: './tests/setup/global.teardown.ts', use: { baseURL, }, webServer: { - command: `pnpm run test:e2e:startDummyServer && ${ssrModeCommand}`, + command: commandByMode, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', env: { - MODE: '', + MODE: mode, VITE_NODE_ENV: 'test', VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), VITE_SERVER_PORT: String(PORT), START_PORT: String(START_PORT), PORT: String(PORT), + E2E_DIST_DIR: distDir, + E2E_PORT_KEY: e2ePortKey, }, }, diff --git a/e2e/vue-start/basic/server.js b/e2e/vue-start/basic/server.js index 83f5ff0079..9db7619ae1 100644 --- a/e2e/vue-start/basic/server.js +++ b/e2e/vue-start/basic/server.js @@ -9,16 +9,19 @@ const startPort = process.env.START_PORT || 3001 const isSpaMode = process.env.MODE === 'spa' const isPrerender = process.env.MODE === 'prerender' +const distDir = process.env.E2E_DIST_DIR || 'dist' +const distClientDir = path.resolve(distDir, 'client') +const distServerEntryPath = path.resolve(distDir, 'server', 'server.js') export async function createStartServer() { - const server = (await import('./dist/server/server.js')).default + const server = (await import(distServerEntryPath)).default const nodeHandler = toNodeHandler(server.fetch) const app = express() // to keep testing uniform stop express from redirecting /posts to /posts/ // when serving pre-rendered pages - app.use(express.static('./dist/client', { redirect: !isPrerender })) + app.use(express.static(distClientDir, { redirect: !isPrerender })) app.use(async (req, res, next) => { try { @@ -50,10 +53,10 @@ export async function createSpaServer() { }), ) - app.use(express.static('./dist/client')) + app.use(express.static(distClientDir)) app.get('/{*splat}', (req, res) => { - res.sendFile(path.resolve('./dist/client/index.html')) + res.sendFile(path.resolve(distClientDir, 'index.html')) }) return { app } diff --git a/e2e/vue-start/basic-test-suite/src/navigation.spec.ts b/e2e/vue-start/basic/tests/navigation.spec.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/navigation.spec.ts rename to e2e/vue-start/basic/tests/navigation.spec.ts diff --git a/e2e/vue-start/basic-test-suite/src/not-found.spec.ts b/e2e/vue-start/basic/tests/not-found.spec.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/not-found.spec.ts rename to e2e/vue-start/basic/tests/not-found.spec.ts diff --git a/e2e/react-start/basic-test-suite/src/prerendering.spec.ts b/e2e/vue-start/basic/tests/prerendering.spec.ts similarity index 90% rename from e2e/react-start/basic-test-suite/src/prerendering.spec.ts rename to e2e/vue-start/basic/tests/prerendering.spec.ts index 66cc1f68ff..ddcf720a4c 100644 --- a/e2e/react-start/basic-test-suite/src/prerendering.spec.ts +++ b/e2e/vue-start/basic/tests/prerendering.spec.ts @@ -3,15 +3,18 @@ import { join } from 'node:path' import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' import { isPrerender } from './utils/isPrerender' -import { getBasicAppRoot } from './utils/getBasicAppRoot' + +const distDir = join( + process.cwd(), + process.env.E2E_DIST_DIR ?? 'dist', + 'client', +) test.describe('Prerender Static Path Discovery', () => { test.skip(!isPrerender, 'Skipping since not in prerender mode') test.describe('Build Output Verification', () => { test('should automatically discover and prerender static routes', () => { // Check that static routes were automatically discovered and prerendered - const distDir = join(getBasicAppRoot(), 'dist', 'client') - // These static routes should be automatically discovered and prerendered expect(existsSync(join(distDir, 'index.html'))).toBe(true) expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true) @@ -35,7 +38,6 @@ test.describe('Prerender Static Path Discovery', () => { test.describe('Static Files Verification', () => { test('should contain prerendered content in posts.html', () => { - const distDir = join(getBasicAppRoot(), 'dist', 'client') expect(existsSync(join(distDir, 'posts/index.html'))).toBe(true) // "Select a post." should be in the prerendered HTML diff --git a/e2e/vue-start/basic-test-suite/src/redirect.spec.ts b/e2e/vue-start/basic/tests/redirect.spec.ts similarity index 97% rename from e2e/vue-start/basic-test-suite/src/redirect.spec.ts rename to e2e/vue-start/basic/tests/redirect.spec.ts index c25a6b0dbf..2cad7d8f9d 100644 --- a/e2e/vue-start/basic-test-suite/src/redirect.spec.ts +++ b/e2e/vue-start/basic/tests/redirect.spec.ts @@ -6,14 +6,14 @@ import { getTestServerPort, test, } from '@tanstack/router-e2e-utils' -import { getPackageName } from './utils/getPackageName.ts' +import { getE2EPortKey } from './utils/getE2EPortKey.ts' // somehow playwright does not correctly import default exports const combinate = (combinateImport as any).default as typeof combinateImport -const packageName = getPackageName() -const PORT = await getTestServerPort(packageName) -const EXTERNAL_HOST_PORT = await getDummyServerPort(packageName) +const e2ePortKey = getE2EPortKey() +const PORT = await getTestServerPort(e2ePortKey) +const EXTERNAL_HOST_PORT = await getDummyServerPort(e2ePortKey) test.describe('redirects', () => { const internalNavigationTestMatrix = combinate({ diff --git a/e2e/vue-start/basic-test-suite/src/script-duplication.spec.ts b/e2e/vue-start/basic/tests/script-duplication.spec.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/script-duplication.spec.ts rename to e2e/vue-start/basic/tests/script-duplication.spec.ts diff --git a/e2e/vue-start/basic-test-suite/src/search-params.spec.ts b/e2e/vue-start/basic/tests/search-params.spec.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/search-params.spec.ts rename to e2e/vue-start/basic/tests/search-params.spec.ts diff --git a/e2e/vue-start/basic-test-suite/src/setup/global.setup.ts b/e2e/vue-start/basic/tests/setup/global.setup.ts similarity index 50% rename from e2e/vue-start/basic-test-suite/src/setup/global.setup.ts rename to e2e/vue-start/basic/tests/setup/global.setup.ts index fc06a65591..cd38c35885 100644 --- a/e2e/vue-start/basic-test-suite/src/setup/global.setup.ts +++ b/e2e/vue-start/basic/tests/setup/global.setup.ts @@ -1,6 +1,6 @@ import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' export default async function setup() { - await e2eStartDummyServer(getPackageName()) + await e2eStartDummyServer(getE2EPortKey()) } diff --git a/e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts b/e2e/vue-start/basic/tests/setup/global.teardown.ts similarity index 55% rename from e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts rename to e2e/vue-start/basic/tests/setup/global.teardown.ts index a8a6899700..36e5c92858 100644 --- a/e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts +++ b/e2e/vue-start/basic/tests/setup/global.teardown.ts @@ -1,8 +1,8 @@ import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' export default async function teardown() { try { - await e2eStopDummyServer(getPackageName()) + await e2eStopDummyServer(getE2EPortKey()) } catch {} } diff --git a/e2e/react-start/basic-test-suite/src/setup/waitForDummyServer.ts b/e2e/vue-start/basic/tests/setup/waitForDummyServer.ts similarity index 82% rename from e2e/react-start/basic-test-suite/src/setup/waitForDummyServer.ts rename to e2e/vue-start/basic/tests/setup/waitForDummyServer.ts index f27dc86d5b..88c18fd79e 100644 --- a/e2e/react-start/basic-test-suite/src/setup/waitForDummyServer.ts +++ b/e2e/vue-start/basic/tests/setup/waitForDummyServer.ts @@ -1,11 +1,11 @@ import { getDummyServerPort } from '@tanstack/router-e2e-utils' -import { getPackageName } from '../utils/getPackageName.ts' +import { getE2EPortKey } from '../utils/getE2EPortKey.ts' const timeoutMs = 10_000 const retryIntervalMs = 100 export default async function waitForDummyServer() { - const port = await getDummyServerPort(getPackageName()) + const port = await getDummyServerPort(getE2EPortKey()) const deadline = Date.now() + timeoutMs while (Date.now() < deadline) { diff --git a/e2e/vue-start/basic-test-suite/src/special-characters.spec.ts b/e2e/vue-start/basic/tests/special-characters.spec.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/special-characters.spec.ts rename to e2e/vue-start/basic/tests/special-characters.spec.ts diff --git a/e2e/vue-start/basic-test-suite/src/streaming.spec.ts b/e2e/vue-start/basic/tests/streaming.spec.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/streaming.spec.ts rename to e2e/vue-start/basic/tests/streaming.spec.ts diff --git a/e2e/vue-start/basic/tests/utils/getE2EPortKey.ts b/e2e/vue-start/basic/tests/utils/getE2EPortKey.ts new file mode 100644 index 0000000000..48be6a9027 --- /dev/null +++ b/e2e/vue-start/basic/tests/utils/getE2EPortKey.ts @@ -0,0 +1,5 @@ +import { getPackageName } from './getPackageName.ts' + +export function getE2EPortKey() { + return process.env.E2E_PORT_KEY ?? getPackageName() +} diff --git a/e2e/vue-start/basic-test-suite/src/utils/getPackageName.ts b/e2e/vue-start/basic/tests/utils/getPackageName.ts similarity index 100% rename from e2e/vue-start/basic-test-suite/src/utils/getPackageName.ts rename to e2e/vue-start/basic/tests/utils/getPackageName.ts diff --git a/e2e/vue-start/basic/tsconfig.json b/e2e/vue-start/basic/tsconfig.json index 6f99c0f49a..1b2ac7b943 100644 --- a/e2e/vue-start/basic/tsconfig.json +++ b/e2e/vue-start/basic/tsconfig.json @@ -9,6 +9,7 @@ "moduleResolution": "Bundler", "lib": ["DOM", "DOM.Iterable", "ES2022"], "isolatedModules": true, + "allowImportingTsExtensions": true, "resolveJsonModule": true, "skipLibCheck": true, "target": "ES2022", diff --git a/e2e/vue-start/basic/vite.config.ts b/e2e/vue-start/basic/vite.config.ts index e638e37f36..6fe8d5d194 100644 --- a/e2e/vue-start/basic/vite.config.ts +++ b/e2e/vue-start/basic/vite.config.ts @@ -30,8 +30,13 @@ const prerenderConfiguration = { maxRedirects: 100, } +const outDir = process.env.E2E_DIST_DIR ?? 'dist' + export default defineConfig({ resolve: { tsconfigPaths: true }, + build: { + outDir, + }, server: { port: 3000, }, diff --git a/package.json b/package.json index 453492cbaf..89a0dcf333 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "test:docs": "node scripts/verify-links.ts", "vite-ecosystem-ci:build": "nx run-many --targets=build --projects=@tanstack/router-plugin,@tanstack/start-plugin-core,@tanstack/react-start,@tanstack/react-start-client,@tanstack/react-start-server --skipRemoteCache", "vite-ecosystem-ci:before-test": "pnpm exec playwright install chromium", - "vite-ecosystem-ci:test": "nx run-many --targets=test:unit --projects=@tanstack/router-plugin,@tanstack/start-plugin-core,@tanstack/react-start-client --skipRemoteCache && nx run-many --target=test:e2e --projects=tanstack-router-e2e-react-basic-file-based,tanstack-router-e2e-react-basic-file-based-code-splitting,tanstack-react-start-e2e-basic,tanstack-react-start-e2e-basic-spa,tanstack-react-start-e2e-basic-prerender,tanstack-react-start-e2e-basic-preview,tanstack-vue-start-e2e-basic,tanstack-vue-start-e2e-basic-spa,tanstack-vue-start-e2e-basic-prerender,tanstack-vue-start-e2e-basic-preview,tanstack-solid-start-e2e-basic,tanstack-solid-start-e2e-basic-spa,tanstack-solid-start-e2e-basic-prerender,tanstack-solid-start-e2e-basic-preview --skipRemoteCache" + "vite-ecosystem-ci:test": "nx run-many --targets=test:unit --projects=@tanstack/router-plugin,@tanstack/start-plugin-core,@tanstack/react-start-client --skipRemoteCache && nx run-many --target=test:e2e --projects=tanstack-router-e2e-react-basic-file-based,tanstack-router-e2e-react-basic-file-based-code-splitting,tanstack-react-start-e2e-basic,tanstack-vue-start-e2e-basic,tanstack-solid-start-e2e-basic --skipRemoteCache" }, "nx": { "includedScripts": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2aaa9bd032..81494ebb58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1389,9 +1389,6 @@ importers: tailwindcss: specifier: ^4.2.2 version: 4.2.2 - tanstack-react-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite typescript: specifier: ^6.0.2 version: 6.0.2 @@ -1527,36 +1524,6 @@ importers: specifier: ^4.74.0 version: 4.75.0 - e2e/react-start/basic-prerender: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-react-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-react-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/react-start/basic-preview: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-react-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-react-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - e2e/react-start/basic-react-query: dependencies: '@tanstack/react-query': @@ -1627,36 +1594,6 @@ importers: specifier: ^6.0.2 version: 6.0.2 - e2e/react-start/basic-spa: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-react-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-react-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/react-start/basic-test-suite: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:* - version: link:../../e2e-utils - '@types/node': - specifier: 25.0.9 - version: 25.0.9 - combinate: - specifier: ^1.1.11 - version: 1.1.11 - e2e/react-start/basic-tsr-config: dependencies: '@tanstack/react-router': @@ -3935,9 +3872,6 @@ importers: tailwindcss: specifier: ^4.2.2 version: 4.2.2 - tanstack-solid-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite typescript: specifier: ^6.0.2 version: 6.0.2 @@ -4055,36 +3989,6 @@ importers: specifier: ^4.74.0 version: 4.75.0 - e2e/solid-start/basic-prerender: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-solid-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-solid-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/solid-start/basic-preview: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-solid-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-solid-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - e2e/solid-start/basic-solid-query: dependencies: '@tanstack/solid-query': @@ -4146,36 +4050,6 @@ importers: specifier: ^2.11.11 version: 2.11.11(@testing-library/jest-dom@6.6.3)(solid-js@1.9.12)(vite@8.0.0(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.6.1)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) - e2e/solid-start/basic-spa: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-solid-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-solid-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/solid-start/basic-test-suite: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - '@types/node': - specifier: 25.0.9 - version: 25.0.9 - combinate: - specifier: ^1.1.11 - version: 1.1.11 - e2e/solid-start/basic-tsr-config: dependencies: '@tanstack/solid-router': @@ -5834,9 +5708,6 @@ importers: tailwindcss: specifier: ^4.2.2 version: 4.2.2 - tanstack-vue-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite typescript: specifier: ^6.0.2 version: 6.0.2 @@ -5957,66 +5828,6 @@ importers: specifier: ^4.74.0 version: 4.75.0 - e2e/vue-start/basic-prerender: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-vue-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-vue-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/vue-start/basic-preview: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-vue-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-vue-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/vue-start/basic-spa: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - tanstack-vue-start-e2e-basic: - specifier: workspace:* - version: link:../basic - tanstack-vue-start-e2e-basic-test-suite: - specifier: workspace:* - version: link:../basic-test-suite - - e2e/vue-start/basic-test-suite: - devDependencies: - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.0 - '@tanstack/router-e2e-utils': - specifier: workspace:^ - version: link:../../e2e-utils - '@types/node': - specifier: 25.0.9 - version: 25.0.9 - combinate: - specifier: ^1.1.11 - version: 1.1.11 - e2e/vue-start/basic-tsr-config: dependencies: '@tanstack/vue-router': diff --git a/scripts/nx/playwright-plugin.md b/scripts/nx/playwright-plugin.md index 8fdf391141..a9f33bb4c8 100644 --- a/scripts/nx/playwright-plugin.md +++ b/scripts/nx/playwright-plugin.md @@ -1,40 +1,98 @@ -# Playwright E2E Sharding +# Playwright E2E Inference (Modes + Shards) -Use `scripts/nx/playwright-plugin.ts` to turn a Playwright e2e project into Nx shard targets. +Use `scripts/nx/playwright-plugin.ts` to infer Playwright e2e targets from +`package.json` metadata. -The plugin looks for `nx.metadata.playwrightShards` in a project's `package.json`. When present, it generates: +The plugin supports two metadata styles: -- `test:e2e--shard-1-of-N` through `test:e2e--shard-N-of-N` -- a parent `test:e2e` target that depends on all shard targets +- `nx.metadata.playwrightModes` (recommended) +- `nx.metadata.playwrightShards` (legacy, still supported) -Each shard target runs Playwright with: +When `playwrightModes` is present, it takes precedence. -- `--shard=/` -- a shard-specific `E2E_PORT_KEY` -- a dependency on `build` and `^build` - -## 1. Enable sharding in `package.json` +## 1. Configure `playwrightModes` in `package.json` ```json { - "name": "tanstack-react-start-e2e-rsc", + "name": "tanstack-react-start-e2e-basic", "nx": { "metadata": { - "playwrightShards": 6 + "playwrightModes": [ + { "toolchain": "vite", "mode": "ssr" }, + { "toolchain": "vite", "mode": "spa", "shards": 4 }, + { "toolchain": "vite", "mode": "prerender", "shards": 4 }, + { "toolchain": "vite", "mode": "preview" } + ] } } } ``` -The package name is used to build the shard port key: +Supported modes: + +- `ssr` +- `spa` +- `prerender` +- `preview` + +`shards` is optional and defaults to `1`. + +Supported toolchains: + +- `vite` + +## 2. What targets are generated + +For each `{ toolchain, mode }` entry, the plugin generates: + +- `build:e2e---` +- `test:e2e---` + +If `shards > 1`, it also generates: + +- `test:e2e-----shard-1-of-N` ... `--shard-N-of-N` +- `test:e2e---` as a parent noop target depending on all shards + +Finally, it generates: + +- `test:e2e` as a parent noop target depending on every mode target -```txt --shard--of- +## 3. Environment variables injected by inferred targets + +Each inferred `build:e2e:*` and `test:e2e:*` target sets: + +- `MODE=` +- `TOOLCHAIN=` +- `E2E_TOOLCHAIN=` +- `E2E_DIST=dist--` +- `E2E_DIST_DIR=dist--` + +Each inferred `test:e2e:*` target also sets: + +- `E2E_PORT_KEY=--` +- For shard targets: `E2E_PORT_KEY=---shard--of-` + +## 4. Build behavior and webServer command + +Each inferred e2e target depends on the inferred `build:e2e---` +target, which runs: + +```sh +vite build && tsc --noEmit ``` -## 2. Do not build inside `playwright.config.ts` +with the mode/toolchain env above. + +This command is intentionally fixed (it does not call `pnpm build`). This avoids +accidentally selecting the wrong build path in projects that include multiple +toolchains. -The plugin already makes each shard depend on `build`, so the Playwright `webServer` command should only start the built app. +The inferred build target uses standard production inputs and explicit mode- +specific outputs (`dist--`). Mode env values are passed through +the target command env, so they do not need to be duplicated as target input +env hashes. + +Because of this, Playwright `webServer.command` should only start the app. Good: @@ -45,22 +103,31 @@ webServer: { } ``` -Avoid: +For preview mode, pass the inferred dist folder: ```ts +const distDir = process.env.E2E_DIST_DIR ?? 'dist' + webServer: { - command: `PORT=${PORT} pnpm build && PORT=${PORT} pnpm start`, + command: `pnpm preview --outDir ${distDir} --port ${PORT}`, url: baseURL, } ``` -Running the build inside every shard causes unnecessary work and can create races when multiple shards share the same project directory. +Avoid: -## 3. Use `E2E_PORT_KEY` for every derived port +```ts +webServer: { + command: `MODE=spa pnpm build && PORT=${PORT} pnpm start`, + url: baseURL, +} +``` -If the project uses `getTestServerPort`, `getDummyServerPort`, `e2eStartDummyServer`, or `e2eStopDummyServer`, key them off `process.env.E2E_PORT_KEY` first. +## 5. Use `E2E_PORT_KEY` for all server ports -Example: +If your setup uses `getTestServerPort`, `getDummyServerPort`, +`e2eStartDummyServer`, or `e2eStopDummyServer`, use +`process.env.E2E_PORT_KEY` first. ```ts import { getTestServerPort } from '@tanstack/router-e2e-utils' @@ -77,15 +144,14 @@ await e2eStartDummyServer(process.env.E2E_PORT_KEY ?? packageJson.name) await e2eStopDummyServer(process.env.E2E_PORT_KEY ?? packageJson.name) ``` -Without this, multiple shards on the same runner will reuse the same `port-*.txt` files and tear each other down. +## 6. Clean stale port files once per Playwright run -## 4. Clean stale port files once per Playwright run +If the project allocates ports through `@tanstack/router-e2e-utils`, clean stale +`port-*.txt` files before resolving the port. -If the project allocates ports through `@tanstack/router-e2e-utils`, clean stale `port-*.txt` files before resolving the port. - -Important: do this only in the main Playwright config load. Playwright loads the config more than once, and unconditional cleanup can remap the port after the web server has already started. - -Example: +Important: do this only in the main Playwright config load. Playwright loads the +config more than once, and unconditional cleanup can remap the port after the +web server has already started. ```ts import fs from 'node:fs' @@ -96,6 +162,7 @@ const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name if (process.env.TEST_WORKER_INDEX === undefined) { for (const portFile of [ `port-${e2ePortKey}.txt`, + `port-${e2ePortKey}_start.txt`, `port-${e2ePortKey}-external.txt`, ]) { fs.rmSync(portFile, { force: true }) @@ -103,49 +170,34 @@ if (process.env.TEST_WORKER_INDEX === undefined) { } ``` -Avoid broad cleanup such as `rm -rf port*.txt` in shared shard runs. - -## 5. Keep the base URL derived from the shard key +Include the `*_start` file only if your test setup allocates a separate +`START_PORT` key. -```ts -const PORT = await getTestServerPort(e2ePortKey) -const baseURL = `http://localhost:${PORT}` - -export default defineConfig({ - use: { - baseURL, - }, - webServer: { - command: `PORT=${PORT} pnpm start`, - url: baseURL, - }, -}) -``` +Avoid broad cleanup such as `rm -rf port*.txt` in shared shard runs. -## 6. Run the generated targets +## 7. Run inferred targets Examples: ```sh -pnpm nx run tanstack-react-start-e2e-rsc:test:e2e--shard-1-of-6 -pnpm nx run tanstack-react-start-e2e-rsc:test:e2e +pnpm nx run tanstack-react-start-e2e-basic:test:e2e--vite-ssr +pnpm nx run tanstack-react-start-e2e-basic:test:e2e--vite-spa--shard-1-of-4 +pnpm nx run tanstack-react-start-e2e-basic:test:e2e ``` -## Checklist - -- add `nx.metadata.playwrightShards` to `package.json` -- make sure the project has a working `build` target -- remove `build` from `webServer.command` -- read ports from `process.env.E2E_PORT_KEY ?? packageJson.name` -- use the same key for helper servers in setup and teardown -- clean shard-specific `port-*.txt` files once, guarded by `TEST_WORKER_INDEX === undefined` -- avoid wildcard port cleanup in shared shard runs +## Legacy fallback: `playwrightShards` -## Reference +If `playwrightModes` is not configured, the plugin still supports: -Working example: +```json +{ + "nx": { + "metadata": { + "playwrightShards": 6 + } + } +} +``` -- `e2e/react-start/rsc/package.json` -- `e2e/react-start/rsc/playwright.config.ts` -- `e2e/react-start/rsc/tests/setup/global.setup.ts` -- `e2e/react-start/rsc/tests/setup/global.teardown.ts` +This generates legacy shard targets under `test:e2e--shard-...` plus a parent +`test:e2e` target. diff --git a/scripts/nx/playwright-plugin.ts b/scripts/nx/playwright-plugin.ts index 9e8c9618ad..464ff3ac00 100644 --- a/scripts/nx/playwright-plugin.ts +++ b/scripts/nx/playwright-plugin.ts @@ -10,7 +10,7 @@ export const createNodesV2: CreateNodesV2 = [ '**/package.json', async (configFiles, options, context) => { return await createNodesFromFiles( - (configFile, options, context) => + (configFile, _options, context) => createNodesInternal(configFile, context), configFiles, options, @@ -19,15 +19,45 @@ export const createNodesV2: CreateNodesV2 = [ }, ] -async function createNodesInternal( +function createNodesInternal( configFilePath: string, - context: CreateNodesContextV2, + _context: CreateNodesContextV2, ) { const projectConfiguration = readJsonFile<{ name?: string - nx?: { metadata?: { playwrightShards?: number } } + nx?: { + metadata?: { + playwrightShards?: number + playwrightModes?: Array + } + } }>(configFilePath) const root = dirname(configFilePath) + const packageName = projectConfiguration.name ?? root + const playwrightModes = + projectConfiguration.nx?.metadata?.playwrightModes ?? [] + + if (playwrightModes.length > 0) { + const { targets, targetGroupEntries } = buildModeTargets( + root, + packageName, + playwrightModes, + ) + + return { + projects: { + [root]: { + targets, + metadata: { + targetGroups: { + playwright: targetGroupEntries, + }, + }, + tags: ['playwright:sharded'], + }, + }, + } + } if (!projectConfiguration.nx?.metadata?.playwrightShards) { return { @@ -39,9 +69,8 @@ async function createNodesInternal( const { targets, targetGroupEntries } = buildShardedTargets( root, - projectConfiguration.name ?? root, + packageName, projectConfiguration.nx.metadata.playwrightShards, - ['default', '^production'], ) // Project configuration to be merged into the rest of the Nx configuration return { @@ -53,19 +82,240 @@ async function createNodesInternal( playwright: targetGroupEntries, }, }, - tags: ['playwright:shareded'], + tags: ['playwright:sharded'], }, }, } } const CI_TARGET_NAME = 'test:e2e' +const MODE_TARGET_SEPARATOR = '--' +const TEST_INPUTS: TargetConfiguration['inputs'] = ['default', '^production'] +const BUILD_INPUTS: TargetConfiguration['inputs'] = [ + 'production', + '^production', +] + +const PLAYWRIGHT_TOOLCHAINS = ['vite'] as const +const PLAYWRIGHT_MODES = ['ssr', 'spa', 'prerender', 'preview'] as const + +type PlaywrightToolchain = (typeof PLAYWRIGHT_TOOLCHAINS)[number] +type PlaywrightMode = (typeof PLAYWRIGHT_MODES)[number] + +type PlaywrightModeMetadata = { + toolchain: PlaywrightToolchain + mode: PlaywrightMode + shards?: number +} + +type RawPlaywrightModeMetadata = { + toolchain?: string + mode?: string + shards?: number +} + +function validateModeMetadata( + modeMetadata: RawPlaywrightModeMetadata, + index: number, +): PlaywrightModeMetadata { + const targetName = `${modeMetadata.toolchain ?? ''}:${modeMetadata.mode ?? ''}` + + if (!modeMetadata.toolchain || !modeMetadata.mode) { + throw new Error( + `[Playwright Sharding Plugin] Invalid playwrightModes[${index}] entry: ` + + `${JSON.stringify(modeMetadata)}. Both "toolchain" and "mode" are required.`, + ) + } + + if ( + !PLAYWRIGHT_TOOLCHAINS.includes( + modeMetadata.toolchain as PlaywrightToolchain, + ) + ) { + throw new Error( + `[Playwright Sharding Plugin] Invalid toolchain for playwrightModes[${index}]: ` + + `${modeMetadata.toolchain}. Supported toolchains: ${PLAYWRIGHT_TOOLCHAINS.join(', ')}.`, + ) + } + + if (!PLAYWRIGHT_MODES.includes(modeMetadata.mode as PlaywrightMode)) { + throw new Error( + `[Playwright Sharding Plugin] Invalid mode for playwrightModes[${index}]: ` + + `${modeMetadata.mode}. Supported modes: ${PLAYWRIGHT_MODES.join(', ')}.`, + ) + } + + if (modeMetadata.shards !== undefined) { + const shardCount = Number(modeMetadata.shards) + + if (!Number.isInteger(shardCount) || shardCount < 1) { + throw new Error( + `[Playwright Sharding Plugin] Invalid shard count for ${targetName}: ${modeMetadata.shards}. ` + + `Expected a positive integer.`, + ) + } + } + + return { + toolchain: modeMetadata.toolchain as PlaywrightToolchain, + mode: modeMetadata.mode as PlaywrightMode, + shards: modeMetadata.shards, + } +} + +function buildModeTargets( + projectRoot: string, + packageName: string, + modes: Array, +): { + targets: Record + targetGroupEntries: Array +} { + const targets: Record = {} + const targetGroup: Array = [] + const ciDependsOnTargets: Array<{ + target: string + projects: 'self' + params: 'forward' + }> = [] + + for (const [index, rawModeMetadata] of modes.entries()) { + const modeMetadata = validateModeMetadata(rawModeMetadata, index) + const modeTargetName = `${CI_TARGET_NAME}--${modeMetadata.toolchain}-${modeMetadata.mode}` + const buildTargetName = `build:e2e--${modeMetadata.toolchain}-${modeMetadata.mode}` + const shardCount = modeMetadata.shards ?? 1 + const distDir = `dist-${modeMetadata.toolchain}-${modeMetadata.mode}` + const modeEnv = { + MODE: modeMetadata.mode, + TOOLCHAIN: modeMetadata.toolchain, + E2E_TOOLCHAIN: modeMetadata.toolchain, + E2E_DIST: distDir, + E2E_DIST_DIR: distDir, + } + const modePortKey = `${packageName}-${modeMetadata.toolchain}-${modeMetadata.mode}` + const modeDescription = `${modeMetadata.toolchain}/${modeMetadata.mode}` + const modeShardTargets: Array = [] + + targets[buildTargetName] = { + executor: 'nx:run-commands', + options: { + command: 'vite build && tsc --noEmit', + cwd: projectRoot, + env: modeEnv, + }, + parallelism: false, + cache: true, + inputs: BUILD_INPUTS, + dependsOn: ['^build'], + outputs: [`{projectRoot}/${distDir}`], + metadata: { + technologies: ['playwright'], + description: `Build artifacts for ${modeDescription} e2e tests`, + }, + } + targetGroup.push(buildTargetName) + + if (shardCount === 1) { + targets[modeTargetName] = { + executor: 'nx:run-commands', + options: { + command: 'playwright test --project=chromium', + cwd: projectRoot, + env: { + ...modeEnv, + E2E_PORT_KEY: modePortKey, + }, + }, + cache: true, + inputs: TEST_INPUTS, + dependsOn: [buildTargetName], + metadata: { + technologies: ['playwright'], + description: `Run Playwright tests for ${modeDescription}`, + }, + } + targetGroup.push(modeTargetName) + } else { + for (let shardIndex = 1; shardIndex <= shardCount; shardIndex++) { + const shardTargetName = `${modeTargetName}${MODE_TARGET_SEPARATOR}shard-${shardIndex}-of-${shardCount}` + const shardPortKey = `${modePortKey}-shard-${shardIndex}-of-${shardCount}` + + targets[shardTargetName] = { + executor: 'nx:run-commands', + options: { + command: `playwright test --project=chromium --shard=${shardIndex}/${shardCount}`, + cwd: projectRoot, + env: { + ...modeEnv, + E2E_PORT_KEY: shardPortKey, + }, + }, + cache: true, + inputs: TEST_INPUTS, + dependsOn: [buildTargetName], + metadata: { + technologies: ['playwright'], + description: `Run Playwright ${modeDescription} shard ${shardIndex} of ${shardCount}`, + help: { + command: `npx playwright run --help`, + example: { + options: { + shard: `${shardIndex}/${shardCount}`, + }, + }, + }, + }, + } + + modeShardTargets.push(shardTargetName) + targetGroup.push(shardTargetName) + } + + targets[modeTargetName] = { + executor: 'nx:noop', + cache: true, + inputs: TEST_INPUTS, + dependsOn: modeShardTargets.map((shardTargetName) => ({ + target: shardTargetName, + projects: 'self' as const, + params: 'forward' as const, + })), + metadata: { + technologies: ['playwright'], + description: `Run all Playwright ${modeDescription} shards (${shardCount} shards)`, + }, + } + targetGroup.push(modeTargetName) + } + + ciDependsOnTargets.push({ + target: modeTargetName, + projects: 'self', + params: 'forward', + }) + } + + targets[CI_TARGET_NAME] = { + executor: 'nx:noop', + cache: true, + inputs: TEST_INPUTS, + dependsOn: ciDependsOnTargets, + metadata: { + technologies: ['playwright'], + description: `Run all Playwright modes (${modes.length} modes)`, + }, + } + + return { + targets, + targetGroupEntries: [...targetGroup, CI_TARGET_NAME], + } +} function buildShardedTargets( projectRoot: string, packageName: string, shardCount: number, - testInputs: TargetConfiguration['inputs'], ): { targets: Record targetGroupEntries: Array @@ -88,7 +338,7 @@ function buildShardedTargets( }, }, cache: true, - inputs: testInputs, + inputs: TEST_INPUTS, dependsOn: [`^build`, 'build'], metadata: { technologies: ['playwright'], @@ -111,7 +361,7 @@ function buildShardedTargets( targets[CI_TARGET_NAME] = { executor: 'nx:noop', cache: true, - inputs: testInputs, + inputs: TEST_INPUTS, dependsOn: targetGroup.map((shardTargetName) => ({ target: shardTargetName, projects: 'self' as const,