@camunda/e2e-test-suite 0.0.1270 → 0.0.1271
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/playwright.config.ts +80 -0
package/package.json
CHANGED
package/playwright.config.ts
CHANGED
|
@@ -224,6 +224,83 @@ const changedFolders =
|
|
|
224
224
|
? args[changedFoldersArgIndex + 1].split(',')
|
|
225
225
|
: [];
|
|
226
226
|
|
|
227
|
+
// Run-budget guards. See the "A hung test is not an infrastructure failure"
|
|
228
|
+
// note in docs/ci-failure-categories.md, and issue #3115.
|
|
229
|
+
const isSmoke = process.env.IS_SMOKE === 'true';
|
|
230
|
+
|
|
231
|
+
// Reads a numeric env override. An unset, empty, negative or unparseable value
|
|
232
|
+
// returns undefined so the guard falls back to its default rather than being
|
|
233
|
+
// silently switched off by a typo. An explicit 0 is honoured -- Playwright
|
|
234
|
+
// reads 0 as "no limit" for both globalTimeout and maxFailures.
|
|
235
|
+
const numericEnv = (name: string): number | undefined => {
|
|
236
|
+
const raw = process.env[name];
|
|
237
|
+
if (raw === undefined || raw.trim() === '') return undefined;
|
|
238
|
+
const value = Number(raw);
|
|
239
|
+
return Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Without globalTimeout Playwright never stops itself, so one hung test costs
|
|
243
|
+
// `timeout x (retries + 1)` -- 24 minutes at the 12-minute default -- and
|
|
244
|
+
// GitHub SIGKILLs the job at its `timeout-minutes` before any report is
|
|
245
|
+
// written. A run with no report has total_tests == 0, which
|
|
246
|
+
// docs/ci-failure-categories.md classifies as
|
|
247
|
+
// `infrastructure` -- a hung test gets filed as a platform problem and never
|
|
248
|
+
// reaches anyone who can fix it. When Playwright owns the deadline instead it
|
|
249
|
+
// runs its reporters first (runner/tasks.js finishTaskRun calls onEnd on every
|
|
250
|
+
// status), so the run fails with a report naming the stuck test.
|
|
251
|
+
//
|
|
252
|
+
// The budget belongs to the caller: globalTimeout is only correct below the
|
|
253
|
+
// job's `timeout-minutes`, and that differs per workflow. The smoke leg is the
|
|
254
|
+
// one context this file can infer -- the blocking gate with the tightest
|
|
255
|
+
// budget (25 minutes, camunda/camunda-platform-helm
|
|
256
|
+
// .github/workflows/test-integration-runner.yaml), which a single hang already
|
|
257
|
+
// exhausts. Every other caller opts in with C8E2E_GLOBAL_TIMEOUT_MIN. Jobs that
|
|
258
|
+
// declare no `timeout-minutes` (this repo's nightlies, the repeat-each
|
|
259
|
+
// validation loops that run for hours) have no budget to protect, so they keep
|
|
260
|
+
// the unbounded behaviour.
|
|
261
|
+
const SMOKE_GLOBAL_TIMEOUT_MINUTES = 20;
|
|
262
|
+
|
|
263
|
+
const globalTimeout = () => {
|
|
264
|
+
const override = numericEnv('C8E2E_GLOBAL_TIMEOUT_MIN');
|
|
265
|
+
if (override !== undefined) return override * 60 * 1000;
|
|
266
|
+
return isSmoke ? SMOKE_GLOBAL_TIMEOUT_MINUTES * 60 * 1000 : undefined;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// The missing fail-fast lever: once the platform is down there is no value in
|
|
270
|
+
// paying the per-test timeout for every remaining test. Smoke is a blocking
|
|
271
|
+
// gate over a single spec, so the first failure is already the answer.
|
|
272
|
+
//
|
|
273
|
+
// Everywhere else the default stays 0 (no limit) on purpose. The nightly triage
|
|
274
|
+
// dispatchers (.github/workflows/triage-nightly-failures*.yml) fan out one fix
|
|
275
|
+
// agent per failing test and docs/ci-failure-categories.md derives
|
|
276
|
+
// failure_category from the full counts, so truncating the failure list would
|
|
277
|
+
// hide work rather than save time. Callers with a real budget set
|
|
278
|
+
// C8E2E_MAX_FAILURES.
|
|
279
|
+
const maxFailures = () => {
|
|
280
|
+
const override = numericEnv('C8E2E_MAX_FAILURES');
|
|
281
|
+
if (override !== undefined) return override;
|
|
282
|
+
return isSmoke ? 1 : 0;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// @playwright/test sets the context navigation timeout to 0 -- no limit --
|
|
286
|
+
// whenever `navigationTimeout` is absent (playwright/lib/index.js
|
|
287
|
+
// `_setupContextOptions`: `navigationTimeout || 0`), and TimeoutSettings
|
|
288
|
+
// resolves that 0 before it ever falls through to `actionTimeout`. So today
|
|
289
|
+
// every `goto`, `waitForURL` and `waitForLoadState` call that does not pass its
|
|
290
|
+
// own `timeout` is bounded only by the 12-minute test timeout.
|
|
291
|
+
//
|
|
292
|
+
// That is the hang primitive: `waitForLoadState('networkidle')` waits for 500ms
|
|
293
|
+
// of network silence that never arrives while a backend restarts and clients
|
|
294
|
+
// retry, and the suite has 86 of those calls. Bounding navigation converts a
|
|
295
|
+
// 12-minute stall into a 2-minute failure with a stack trace, without touching
|
|
296
|
+
// the per-test budget that genuinely slow flows rely on.
|
|
297
|
+
//
|
|
298
|
+
// A call that passes its own `timeout` always wins, so nothing bounded today
|
|
299
|
+
// becomes stricter; 120s matches the most generous explicit navigation timeout
|
|
300
|
+
// the suite already uses.
|
|
301
|
+
const NAVIGATION_TIMEOUT_MS =
|
|
302
|
+
numericEnv('C8E2E_NAVIGATION_TIMEOUT_MS') ?? 120 * 1000;
|
|
303
|
+
|
|
227
304
|
const timeout = () => {
|
|
228
305
|
if (process.env.MINOR_VERSION?.includes('SM-8.7')) {
|
|
229
306
|
// OpenSearch indexes more slowly than Elasticsearch — give SM-8.7 the
|
|
@@ -268,6 +345,8 @@ export default defineConfig({
|
|
|
268
345
|
// API specs into a browser project that has none of their environment.
|
|
269
346
|
testIgnore: /[\\/]tests[\\/]api[\\/]/,
|
|
270
347
|
timeout: timeout(),
|
|
348
|
+
globalTimeout: globalTimeout(),
|
|
349
|
+
maxFailures: maxFailures(),
|
|
271
350
|
workers: workerCount(),
|
|
272
351
|
retries: 1,
|
|
273
352
|
fullyParallel: false,
|
|
@@ -275,6 +354,7 @@ export default defineConfig({
|
|
|
275
354
|
use: {
|
|
276
355
|
baseURL: getBaseURL(),
|
|
277
356
|
actionTimeout: 10000,
|
|
357
|
+
navigationTimeout: NAVIGATION_TIMEOUT_MS,
|
|
278
358
|
// Stage 5: trace on-first-retry. With retries=1, a passing-on-retry
|
|
279
359
|
// test (i.e., a flake) leaves a trace from the first attempt — exactly
|
|
280
360
|
// the cases worth debugging. Green runs pay no trace cost.
|