From 450ac2624cc781166305155b0f3c941c979dec6f Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Tue, 2 Jul 2024 21:05:35 +0200 Subject: [PATCH 1/9] feat(playwright): reuse existing server --- examples/app-playwright/playwright.config.ts | 8 +++- src/core/server.ts | 46 +++++++++++++------- src/core/setup/index.ts | 12 +++-- src/core/types.ts | 2 + 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/examples/app-playwright/playwright.config.ts b/examples/app-playwright/playwright.config.ts index a9995fa8e..c786dc72c 100644 --- a/examples/app-playwright/playwright.config.ts +++ b/examples/app-playwright/playwright.config.ts @@ -13,7 +13,7 @@ const devicesToTest = [ // Test against branded browsers. // { ...devices['Desktop Edge'], channel: 'msedge' }, // { ...devices['Desktop Chrome'], channel: 'chrome' }, -] satisfies Array +] satisfies Array /* See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ @@ -34,8 +34,12 @@ export default defineConfig({ trace: 'on-first-retry', /* Nuxt configuration options */ nuxt: { + /* Reuse a server if it's already running. Useful when developing tests. */ + reuseExistingServer: process.env.CI ? false : true, rootDir: fileURLToPath(new URL('.', import.meta.url)), }, }, - projects: devicesToTest.map(p => typeof p === 'string' ? ({ name: p, use: devices[p] }) : p), + projects: devicesToTest.map(p => + typeof p === 'string' ? { name: p, use: devices[p] } : p, + ), }) diff --git a/src/core/server.ts b/src/core/server.ts index 9db68daf2..5655d7581 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -14,11 +14,23 @@ export interface StartServerOptions { env?: Record } +export async function reuseExistingServer(options: StartServerOptions = {}) { + const ctx = useTestContext() + const host = ctx.options.host || 'localhost' // Default to localhost since it's the host used by nuxt dev server + const port = ctx.options.port || 3000 // Default to 3000 since it's the port used by nuxt dev server + + if (port === undefined) { + throw new Error('Port is required when reusing server') + } + + ctx.url = `http://${host}:${port}` +} + export async function startServer(options: StartServerOptions = {}) { const ctx = useTestContext() await stopServer() const host = '127.0.0.1' - const port = ctx.options.port || await getRandomPort(host) + const port = ctx.options.port || (await getRandomPort(host)) ctx.url = `http://${host}:${port}` if (ctx.options.dev) { const nuxiCLI = await kit.resolvePath('nuxi/cli') @@ -39,7 +51,9 @@ export async function startServer(options: StartServerOptions = {}) { for (let i = 0; i < 150; i++) { await new Promise(resolve => setTimeout(resolve, 100)) try { - const res = await $fetch(ctx.nuxt!.options.app.baseURL, { responseType: 'text' }) + const res = await $fetch(ctx.nuxt!.options.app.baseURL, { + responseType: 'text', + }) if (!res.includes('__NUXT_LOADING__')) { return } @@ -52,18 +66,20 @@ export async function startServer(options: StartServerOptions = {}) { throw lastError || new Error('Timeout waiting for dev server!') } else { - ctx.serverProcess = execa('node', [ - resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs'), - ], { - stdio: 'inherit', - env: { - ...process.env, - PORT: String(port), - HOST: host, - NODE_ENV: 'test', - ...options.env, + ctx.serverProcess = execa( + 'node', + [resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs')], + { + stdio: 'inherit', + env: { + ...process.env, + PORT: String(port), + HOST: host, + NODE_ENV: 'test', + ...options.env, + }, }, - }) + ) await waitForPort(port, { retries: 20, host }) } } @@ -79,9 +95,9 @@ export function fetch(path: string, options?: RequestInit) { return _fetch(url(path), options) } -export const $fetch = (function (path: string, options?: FetchOptions) { +export const $fetch = function (path: string, options?: FetchOptions) { return _$fetch(url(path), options) -}) as typeof globalThis['$fetch'] +} as (typeof globalThis)['$fetch'] export function url(path: string) { const ctx = useTestContext() diff --git a/src/core/setup/index.ts b/src/core/setup/index.ts index 53bf9e3a5..d5fce1ff9 100644 --- a/src/core/setup/index.ts +++ b/src/core/setup/index.ts @@ -1,6 +1,6 @@ import { createTestContext, setTestContext } from '../context' import { buildFixture, loadFixture } from '../nuxt' -import { startServer, stopServer } from '../server' +import { reuseExistingServer, startServer, stopServer } from '../server' import { createBrowser } from '../browser' import type { TestHooks, TestOptions } from '../types' import setupCucumber from './cucumber' @@ -41,20 +41,24 @@ export function createTest(options: Partial): TestHooks { } const setup = async () => { + if (ctx.options.reuseExistingServer) { + await reuseExistingServer() + } + if (ctx.options.fixture) { await loadFixture() } - if (ctx.options.build) { + if (ctx.options.build && !ctx.options.reuseExistingServer) { await buildFixture() } - if (ctx.options.server) { + if (ctx.options.server && !ctx.options.reuseExistingServer) { await startServer() } if (ctx.options.waitFor) { - await (new Promise(resolve => setTimeout(resolve, ctx.options.waitFor))) + await new Promise(resolve => setTimeout(resolve, ctx.options.waitFor)) } if (ctx.options.browser) { diff --git a/src/core/types.ts b/src/core/types.ts index c8a3a6bb2..66a2c4043 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -11,6 +11,7 @@ export interface TestOptions { rootDir: string buildDir: string nuxtConfig: NuxtConfig + reuseExistingServer?: boolean build: boolean dev: boolean setupTimeout: number @@ -23,6 +24,7 @@ export interface TestOptions { launch?: LaunchOptions } server: boolean + host?: string port?: number } From b9f02c35a1318a311d32d177fd7883c3ded870f8 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Tue, 2 Jul 2024 21:42:55 +0200 Subject: [PATCH 2/9] chore: format --- src/core/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/server.ts b/src/core/server.ts index 5655d7581..fa772b2ce 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -14,7 +14,7 @@ export interface StartServerOptions { env?: Record } -export async function reuseExistingServer(options: StartServerOptions = {}) { +export async function reuseExistingServer() { const ctx = useTestContext() const host = ctx.options.host || 'localhost' // Default to localhost since it's the host used by nuxt dev server const port = ctx.options.port || 3000 // Default to 3000 since it's the port used by nuxt dev server From 5bcbd2a0717851b1e0f89627555466e877319fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9ban?= Date: Tue, 2 Jul 2024 21:45:38 +0200 Subject: [PATCH 3/9] Apply suggestions from code review --- examples/app-playwright/playwright.config.ts | 6 ++---- src/core/server.ts | 6 ++---- src/core/setup/index.ts | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/app-playwright/playwright.config.ts b/examples/app-playwright/playwright.config.ts index c786dc72c..fe440c764 100644 --- a/examples/app-playwright/playwright.config.ts +++ b/examples/app-playwright/playwright.config.ts @@ -13,7 +13,7 @@ const devicesToTest = [ // Test against branded browsers. // { ...devices['Desktop Edge'], channel: 'msedge' }, // { ...devices['Desktop Chrome'], channel: 'chrome' }, -] satisfies Array +] satisfies Array /* See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ @@ -39,7 +39,5 @@ export default defineConfig({ rootDir: fileURLToPath(new URL('.', import.meta.url)), }, }, - projects: devicesToTest.map(p => - typeof p === 'string' ? { name: p, use: devices[p] } : p, - ), + projects: devicesToTest.map(p => typeof p === 'string' ? { name: p, use: devices[p] } : p), }) diff --git a/src/core/server.ts b/src/core/server.ts index fa772b2ce..178cff8cb 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -51,9 +51,7 @@ export async function startServer(options: StartServerOptions = {}) { for (let i = 0; i < 150; i++) { await new Promise(resolve => setTimeout(resolve, 100)) try { - const res = await $fetch(ctx.nuxt!.options.app.baseURL, { - responseType: 'text', - }) + const res = await $fetch(ctx.nuxt!.options.app.baseURL, { responseType: 'text' }) if (!res.includes('__NUXT_LOADING__')) { return } @@ -97,7 +95,7 @@ export function fetch(path: string, options?: RequestInit) { export const $fetch = function (path: string, options?: FetchOptions) { return _$fetch(url(path), options) -} as (typeof globalThis)['$fetch'] +} as typeof globalThis['$fetch'] export function url(path: string) { const ctx = useTestContext() diff --git a/src/core/setup/index.ts b/src/core/setup/index.ts index d5fce1ff9..ae3c99367 100644 --- a/src/core/setup/index.ts +++ b/src/core/setup/index.ts @@ -58,7 +58,7 @@ export function createTest(options: Partial): TestHooks { } if (ctx.options.waitFor) { - await new Promise(resolve => setTimeout(resolve, ctx.options.waitFor)) + await (new Promise(resolve => setTimeout(resolve, ctx.options.waitFor))) } if (ctx.options.browser) { From a5e092f19eeb570637701d9857c308039f947b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9ban?= Date: Tue, 2 Jul 2024 21:47:20 +0200 Subject: [PATCH 4/9] Apply suggestions from code review --- examples/app-playwright/playwright.config.ts | 2 +- src/core/server.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/app-playwright/playwright.config.ts b/examples/app-playwright/playwright.config.ts index fe440c764..8b04cfae7 100644 --- a/examples/app-playwright/playwright.config.ts +++ b/examples/app-playwright/playwright.config.ts @@ -39,5 +39,5 @@ export default defineConfig({ rootDir: fileURLToPath(new URL('.', import.meta.url)), }, }, - projects: devicesToTest.map(p => typeof p === 'string' ? { name: p, use: devices[p] } : p), + projects: devicesToTest.map(p => typeof p === 'string' ? ({ name: p, use: devices[p] }) : p), }) diff --git a/src/core/server.ts b/src/core/server.ts index 178cff8cb..284f826e9 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -64,10 +64,9 @@ export async function startServer(options: StartServerOptions = {}) { throw lastError || new Error('Timeout waiting for dev server!') } else { - ctx.serverProcess = execa( - 'node', - [resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs')], - { + ctx.serverProcess = execa('node', [ + resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs') + ], { stdio: 'inherit', env: { ...process.env, From df5735f5466cd82ecceb7f018d7d49dde5d19155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9ban?= Date: Tue, 2 Jul 2024 21:48:41 +0200 Subject: [PATCH 5/9] Apply suggestions from code review --- src/core/server.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/server.ts b/src/core/server.ts index 284f826e9..452bd4483 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -65,7 +65,7 @@ export async function startServer(options: StartServerOptions = {}) { } else { ctx.serverProcess = execa('node', [ - resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs') + resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs'), ], { stdio: 'inherit', env: { @@ -74,9 +74,8 @@ export async function startServer(options: StartServerOptions = {}) { HOST: host, NODE_ENV: 'test', ...options.env, - }, }, - ) + }) await waitForPort(port, { retries: 20, host }) } } @@ -92,9 +91,9 @@ export function fetch(path: string, options?: RequestInit) { return _fetch(url(path), options) } -export const $fetch = function (path: string, options?: FetchOptions) { +export const $fetch = (function (path: string, options?: FetchOptions) { return _$fetch(url(path), options) -} as typeof globalThis['$fetch'] +}) as typeof globalThis['$fetch'] export function url(path: string) { const ctx = useTestContext() From c33af3eccc862455e6cc8e12fa5b17b9cc7664df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9ban?= Date: Tue, 2 Jul 2024 21:49:10 +0200 Subject: [PATCH 6/9] Apply suggestions from code review --- src/core/server.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/server.ts b/src/core/server.ts index 452bd4483..27ab285ad 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -30,7 +30,7 @@ export async function startServer(options: StartServerOptions = {}) { const ctx = useTestContext() await stopServer() const host = '127.0.0.1' - const port = ctx.options.port || (await getRandomPort(host)) + const port = ctx.options.port || await getRandomPort(host) ctx.url = `http://${host}:${port}` if (ctx.options.dev) { const nuxiCLI = await kit.resolvePath('nuxi/cli') @@ -67,13 +67,13 @@ export async function startServer(options: StartServerOptions = {}) { ctx.serverProcess = execa('node', [ resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs'), ], { - stdio: 'inherit', - env: { - ...process.env, - PORT: String(port), - HOST: host, - NODE_ENV: 'test', - ...options.env, + stdio: 'inherit', + env: { + ...process.env, + PORT: String(port), + HOST: host, + NODE_ENV: 'test', + ...options.env, }, }) await waitForPort(port, { retries: 20, host }) From 495d73c61530ce1e1688ec05f2813ba5b5b1144e Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Thu, 11 Jul 2024 18:50:55 +0200 Subject: [PATCH 7/9] refactor: update comments --- examples/app-playwright/nuxt.config.ts | 7 +++++++ examples/app-playwright/playwright.config.ts | 2 +- src/core/server.ts | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/app-playwright/nuxt.config.ts b/examples/app-playwright/nuxt.config.ts index 9d825c49b..813092ba3 100644 --- a/examples/app-playwright/nuxt.config.ts +++ b/examples/app-playwright/nuxt.config.ts @@ -1,4 +1,11 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ devtools: { enabled: true }, + compatibilityDate: "2024-07-11", + // TODO: Add to default options? + // https://nuxt.com/docs/api/nuxt-config#ignore + ignore: [ + 'playwright-report', + 'test-results' + ], }) diff --git a/examples/app-playwright/playwright.config.ts b/examples/app-playwright/playwright.config.ts index 8b04cfae7..c13d2d017 100644 --- a/examples/app-playwright/playwright.config.ts +++ b/examples/app-playwright/playwright.config.ts @@ -34,7 +34,7 @@ export default defineConfig({ trace: 'on-first-retry', /* Nuxt configuration options */ nuxt: { - /* Reuse a server if it's already running. Useful when developing tests. */ + /* Reuse a server if it's already running. Useful for test first development. */ reuseExistingServer: process.env.CI ? false : true, rootDir: fileURLToPath(new URL('.', import.meta.url)), }, diff --git a/src/core/server.ts b/src/core/server.ts index 27ab285ad..f7986ef81 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -14,18 +14,31 @@ export interface StartServerOptions { env?: Record } +/** + * Reuse an existing server if it's already running. + * + * This is useful to do test first development and speed up the test execution. + */ export async function reuseExistingServer() { const ctx = useTestContext() - const host = ctx.options.host || 'localhost' // Default to localhost since it's the host used by nuxt dev server + const host = ctx.options.host || 'localhost' // Default to localhost since it's the host used by nuxt dev server (127.0.0.1 is not working) const port = ctx.options.port || 3000 // Default to 3000 since it's the port used by nuxt dev server if (port === undefined) { throw new Error('Port is required when reusing server') } + // TODO: To run against deployed server, maybe we should allow to change the protocol? ctx.url = `http://${host}:${port}` } +/** + * Start a new server. + * + * This server can be a dev server using the Nuxt CLI dev command or a production server using the Nuxt build output. + * + * During testing, it's recommended to reuse an existing server using `reuseExistingServer` setting. This will speed up the test execution and allow test first development. + */ export async function startServer(options: StartServerOptions = {}) { const ctx = useTestContext() await stopServer() From 358bbf828aba4c21a1c9ef3880ae275d2b93f819 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Thu, 11 Jul 2024 18:55:56 +0200 Subject: [PATCH 8/9] fix: does not load fixture is reusing a server --- src/core/setup/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/setup/index.ts b/src/core/setup/index.ts index ae3c99367..b3c7e78ba 100644 --- a/src/core/setup/index.ts +++ b/src/core/setup/index.ts @@ -45,7 +45,7 @@ export function createTest(options: Partial): TestHooks { await reuseExistingServer() } - if (ctx.options.fixture) { + if (ctx.options.fixture && !ctx.options.reuseExistingServer) { await loadFixture() } From e5f7c4bce2167b93c40f96c9e50838c525456491 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 19:51:12 +0000 Subject: [PATCH 9/9] [autofix.ci] apply automated fixes --- examples/app-playwright/nuxt.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/app-playwright/nuxt.config.ts b/examples/app-playwright/nuxt.config.ts index 813092ba3..2a2c1ca0a 100644 --- a/examples/app-playwright/nuxt.config.ts +++ b/examples/app-playwright/nuxt.config.ts @@ -1,11 +1,11 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ devtools: { enabled: true }, - compatibilityDate: "2024-07-11", + compatibilityDate: '2024-07-11', // TODO: Add to default options? // https://nuxt.com/docs/api/nuxt-config#ignore ignore: [ 'playwright-report', - 'test-results' + 'test-results', ], })