From ec267acdbc8c5c3efd94fd227be3bcb0639d4560 Mon Sep 17 00:00:00 2001 From: "n8n-cat-bot[bot]" <283985454+n8n-cat-bot[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:48:50 +0000 Subject: [PATCH] test: Wait for backend readiness before the Playwright database reset (#38366) Co-authored-by: n8n-cat-bot[bot] Co-authored-by: Claude Opus 5 Co-authored-by: Declan Carroll --- .../useWorkflowPublicationStatusSync.test.ts | 1 + .../useWorkflowPublicationStatusSync.ts | 7 ++++- packages/testing/playwright/README.md | 6 ++-- .../testing/playwright/playwright.config.ts | 15 ++++++++-- .../playwright/scripts/run-local-isolated.mjs | 12 ++++---- .../dev-server-smoke/user-folder.spec.ts | 2 +- .../tests/e2e/instance-ai/README.md | 8 ++--- .../playwright/utils/url-helper.test.ts | 29 +++++++++++++++++++ .../testing/playwright/utils/url-helper.ts | 11 +++++++ packages/testing/playwright/vitest.config.ts | 1 + 10 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 packages/testing/playwright/utils/url-helper.test.ts diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.test.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.test.ts index 33cbea70432..f1ba410dc41 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.test.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.test.ts @@ -73,6 +73,7 @@ describe('useWorkflowPublicationStatusSync', () => { settingsStore = mockedStore(useSettingsStore); workflowDocumentStore = useWorkflowDocumentStore(TEST_DOCUMENT_ID); + workflowsStore.isNewWorkflow = false; settingsStore.isWorkflowPublicationServiceEnabled = true; vi.spyOn(workflowsStore, 'fetchPublicationStatus').mockResolvedValue(makeStatus('published')); diff --git a/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.ts b/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.ts index 58fd12ef103..31bd3a9241a 100644 --- a/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.ts +++ b/packages/frontend/editor-ui/src/app/composables/useWorkflowPublicationStatusSync.ts @@ -41,7 +41,12 @@ export function useWorkflowPublicationStatusSync(documentId: MaybeRefOrGetter { } }); -// Poll an actual REST route, not just /favicon.ico, so we know controllers are -// registered. We POST `/rest/e2e/reset` with no body — a registered route +// Poll the actual REST route, not just a health endpoint, so we know controllers +// are registered. We POST `/rest/e2e/reset` with no body — a registered route // returns 4xx/5xx, an unregistered one returns 404 with an HTML body. async function waitForN8n(timeoutMs = 120_000) { const deadline = Date.now() + timeoutMs; diff --git a/packages/testing/playwright/tests/dev-server-smoke/user-folder.spec.ts b/packages/testing/playwright/tests/dev-server-smoke/user-folder.spec.ts index 770d5ca469b..86516b6e00c 100644 --- a/packages/testing/playwright/tests/dev-server-smoke/user-folder.spec.ts +++ b/packages/testing/playwright/tests/dev-server-smoke/user-folder.spec.ts @@ -9,7 +9,7 @@ import { expect, test } from '../../fixtures/base'; * USER_FOLDER in playwright.config.ts), not in the default ~/.n8n. Guards the * smoke suite against silently polluting the developer's own install. * - * Playwright polls the backend's favicon and global-setup resets its database + * Playwright waits for backend readiness and global-setup resets its database * before any test runs, so the file exists by now without visiting the app. */ test.describe( diff --git a/packages/testing/playwright/tests/e2e/instance-ai/README.md b/packages/testing/playwright/tests/e2e/instance-ai/README.md index 8843570d5e9..7b45cbb8319 100644 --- a/packages/testing/playwright/tests/e2e/instance-ai/README.md +++ b/packages/testing/playwright/tests/e2e/instance-ai/README.md @@ -100,10 +100,10 @@ From the isolated runner you get: `~/.n8n/database.sqlite` is never touched. - **`PLAYWRIGHT_ALLOW_CONTAINER_ONLY=true`** so the `@capability:proxy` tag is honoured locally. -- **Self-managed n8n** with a real `/rest/e2e/reset` readiness check (not the - racy default favicon poll), `PLAYWRIGHT_SKIP_WEBSERVER=true` to stop - Playwright from spawning a duplicate, and process-group cleanup so - `node ./n8n` doesn't get orphaned. +- **Self-managed n8n** with a `/rest/e2e/reset` readiness check that waits for + the E2E controller, `PLAYWRIGHT_SKIP_WEBSERVER=true` to stop Playwright from + spawning a duplicate, and process-group cleanup so `node ./n8n` doesn't get + orphaned. The `instanceAiProxySetup` fixture (`fixtures.ts`) detects the missing `n8nContainer` and short-circuits all proxy + tool-trace setup, so every LLM diff --git a/packages/testing/playwright/utils/url-helper.test.ts b/packages/testing/playwright/utils/url-helper.test.ts new file mode 100644 index 00000000000..56adda3633b --- /dev/null +++ b/packages/testing/playwright/utils/url-helper.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, test } from 'vitest'; + +import { getReadinessUrl } from './url-helper'; + +describe('getReadinessUrl', () => { + test('uses the default health endpoint', () => { + expect(getReadinessUrl('http://localhost:5678')).toBe( + 'http://localhost:5678/healthz/readiness', + ); + }); + + test('uses a custom health endpoint', () => { + expect(getReadinessUrl('http://localhost:5678', '/health')).toBe( + 'http://localhost:5678/health/readiness', + ); + }); + + test('adds the leading slash a custom health endpoint may omit', () => { + expect(getReadinessUrl('http://localhost:5678', 'health')).toBe( + 'http://localhost:5678/health/readiness', + ); + }); + + test('drops a trailing slash of the base URL', () => { + expect(getReadinessUrl('http://localhost:5678/')).toBe( + 'http://localhost:5678/healthz/readiness', + ); + }); +}); diff --git a/packages/testing/playwright/utils/url-helper.ts b/packages/testing/playwright/utils/url-helper.ts index 010572cefec..a31e4117a51 100644 --- a/packages/testing/playwright/utils/url-helper.ts +++ b/packages/testing/playwright/utils/url-helper.ts @@ -22,3 +22,14 @@ export function getBackendUrl(): string | undefined { export function getFrontendUrl(): string | undefined { return process.env.N8N_EDITOR_URL ?? process.env.N8N_BASE_URL; } + +/** + * Build the readiness URL of a backend. + * The backend serves readiness at `/readiness` and adds a + * missing leading slash to that variable, so mirror both rules here. + */ +export function getReadinessUrl(baseUrl: string, healthEndpoint?: string): string { + const health = healthEndpoint ?? '/healthz'; + const healthPath = health.startsWith('/') ? health : `/${health}`; + return `${baseUrl.replace(/\/+$/, '')}${healthPath}/readiness`; +} diff --git a/packages/testing/playwright/vitest.config.ts b/packages/testing/playwright/vitest.config.ts index a5657c78571..e9becff99f0 100644 --- a/packages/testing/playwright/vitest.config.ts +++ b/packages/testing/playwright/vitest.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ 'scripts/**/*.test.ts', 'fixtures/**/*.test.ts', 'reporters/**/*.test.ts', + 'utils/**/*.test.ts', 'tests/framework/telemetry.test.ts', 'tests/framework/telemetry-integration.test.ts', '*.test.ts',