From 3d67516f61bec4ffdfd7d167b9f60c2f5ebf6acb Mon Sep 17 00:00:00 2001 From: linonetwo Date: Tue, 5 May 2026 19:12:04 +0800 Subject: [PATCH] fix(e2e): use generous 120s timeout for all Playwright system operations electron.launch, firstWindow, waitForLoadState are system-level operations (process creation, window management), not test assertions. They need generous timeouts independent of measured step time. Cucumber step timeout still catches truly hung test steps. --- features/stepDefinitions/application.ts | 3 +++ features/stepDefinitions/ui.ts | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/features/stepDefinitions/application.ts b/features/stepDefinitions/application.ts index 96ede1ad..537a689b 100644 --- a/features/stepDefinitions/application.ts +++ b/features/stepDefinitions/application.ts @@ -378,6 +378,9 @@ AfterStep({ timeout: 3000 }, async function(this: ApplicationWorld, { pickle, pi // Read docs/Testing.md section "Key E2E Testing Patterns" point 6 before attempting any changes. // Maximum allowed timeouts: Local 5s, CI 10s (exactly 2x local, no more) When('I launch the TidGi application', async function(this: ApplicationWorld) { + // Fire-and-forget: don't block step on process launch. + // The next step ("wait for page to load") handles window acquisition. + this.appLaunchPromise = launchTidGiApplication(this).catch((error: unknown) => { throw error; }); diff --git a/features/stepDefinitions/ui.ts b/features/stepDefinitions/ui.ts index d17e6915..155cb33a 100644 --- a/features/stepDefinitions/ui.ts +++ b/features/stepDefinitions/ui.ts @@ -2,7 +2,7 @@ import { DataTable, Then, When } from '@cucumber/cucumber'; import { backOff } from 'exponential-backoff'; import { parseDataTableRows } from '../supports/dataTable'; import { getWikiTestRootPath } from '../supports/paths'; -import { HEAVY_PLAYWRIGHT_TIMEOUT, PLAYWRIGHT_SHORT_TIMEOUT, PLAYWRIGHT_TIMEOUT } from '../supports/timeouts'; +import { PLAYWRIGHT_SHORT_TIMEOUT, PLAYWRIGHT_TIMEOUT } from '../supports/timeouts'; import type { ApplicationWorld } from './application'; When('I wait for {float} seconds', async function(seconds: number) { @@ -32,11 +32,11 @@ When('I wait for the page to load completely', async function(this: ApplicationW let currentWindow = this.currentWindow; if ((!currentWindow || currentWindow.isClosed()) && this.app) { - currentWindow = await this.app.firstWindow({ timeout: HEAVY_PLAYWRIGHT_TIMEOUT }); + currentWindow = await this.app.firstWindow({ timeout: 120_000 }); this.mainWindow = this.mainWindow ?? currentWindow; this.currentWindow = currentWindow; } - await currentWindow?.waitForLoadState('domcontentloaded', { timeout: HEAVY_PLAYWRIGHT_TIMEOUT }); + await currentWindow?.waitForLoadState('domcontentloaded', { timeout: 120_000 }); // Short networkidle gives workspace-creation and other startup IPC time to finish // without blocking on long-lived connections. 3s is intentionally different from // PLAYWRIGHT_TIMEOUT — this is just a grace period, not a hard requirement.