From b3772da33f66a58bf8fb86af66783a1b3e537600 Mon Sep 17 00:00:00 2001 From: Xinyuan Lin Date: Wed, 12 Aug 2026 21:18:14 -0700 Subject: [PATCH] fix(test, frontend): raise Vitest timeouts for CI stalls The macOS leg of `build / frontend` goes red on a different unit test every few days -- always a timeout, never the same spec, always green on rerun. Three occurrences in the last four days: | Run | Test | Error | | --- | --- | --- | | 31665399757 | UserDatasetVersionCreatorComponent > onClickCreate ... | Test timed out in 5000ms | | 31630884042 | AdminUserComponent > sortByAffiliation ... | Hook timed out in 10000ms | | 31411656559 | WorkflowRuntimeStatisticsComponent > should create | Test timed out in 5000ms | The tests are not the problem: the runner stalls, and the stall lands on whichever test is executing. In run 31665399757 the offending spec file took 11727ms on macos-latest and 240ms on ubuntu-latest for the same commit; in an earlier run the same file took 219ms on macOS. Suite totals from that run show the same picture -- 252.88s wall on macOS vs 89.85s on ubuntu, with a cumulative test time of 307.69s vs 182.34s. macos-latest gives 3 cores and 7 GB against ubuntu's 4 and 16, so the jsdom + v8-coverage workers run under real memory pressure there. Raise testTimeout and hookTimeout to 30s in both Vitest configs, which absorbs a stall an order of magnitude worse than any observed so far. A spec that legitimately needs 30s is broken, and the job's own timeout still bounds a true hang. Per-test timeouts would be whack-a-mole: the next stall picks a different test. Also opt the frontend matrix out of fail-fast, as every other multi-leg matrix in build.yml already does. Today one flaky OS cancels the other two legs, which destroys exactly the evidence needed to tell a runner flake from a real break. Before: macOS stalls 5s -> that test fails -> ubuntu + windows cancelled After: macOS stalls 5s -> absorbed; a real break still fails all legs --- .github/workflows/build.yml | 6 ++++++ frontend/TESTING.md | 1 + frontend/vitest.browser.config.ts | 5 +++++ frontend/vitest.config.ts | 10 ++++++++++ 4 files changed, 22 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c28457b1986..826fd12ef7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,6 +87,12 @@ jobs: if: ${{ inputs.run_frontend }} runs-on: ${{ matrix.os }} strategy: + # An OS-specific failure should not cancel the other two legs: with the + # default fail-fast the surviving jobs report "The operation was + # canceled" and the run no longer says whether the failure reproduces + # off that OS — exactly the evidence needed to tell a runner flake from + # a real break. Every other multi-leg matrix here already opts out. + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] include: diff --git a/frontend/TESTING.md b/frontend/TESTING.md index 7372fc4b29d..9e4412d7e70 100644 --- a/frontend/TESTING.md +++ b/frontend/TESTING.md @@ -47,6 +47,7 @@ For repo-wide testing philosophy (TDD, characterization tests, "every test must | Coverage | `@vitest/coverage-v8` | | Test setup | `src/test-zone-setup.ts` wraps `it`/`test` in an Angular ProxyZone (Vitest does not provide one and Angular's `fakeAsync` requires it) | | Globals | `globals: true` in `vitest.config.ts`, so `describe / it / expect / vi / beforeEach` come from the runtime — no per-file imports | +| Timeouts | 30s per test and per hook, raised from the 5s/10s Vitest defaults because macOS CI runners stall for seconds at a time (#6073) | `src/main.test.ts` is intentionally a near-empty `export {}`. The `unit-test` builder uses `buildTarget`'s `main` to seed the bundle graph; if it pointed at the real `main.ts`, every component declared in `AppModule` would be type-checked for every spec, surfacing template errors for components no active spec touches. Keeping `main.test.ts` empty narrows the graph to what each spec actually imports. diff --git a/frontend/vitest.browser.config.ts b/frontend/vitest.browser.config.ts index 3fe2c410390..b67414aa392 100644 --- a/frontend/vitest.browser.config.ts +++ b/frontend/vitest.browser.config.ts @@ -64,6 +64,11 @@ export default defineConfig({ // browser-mode the runtime has neither, so we install the `buffer` npm // package as a shim). setupFiles: ["src/browser-buffer-polyfill.ts", "src/test-zone-setup.ts"], + // Same runner-stall headroom as the jsdom config (vitest.config.ts): + // driving a real Chromium through playwright is strictly slower than + // jsdom, so these specs need at least as much slack. See #6073. + testTimeout: 30_000, + hookTimeout: 30_000, browser: { enabled: true, provider: playwright(), diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 9cb2f82f88c..dbfc9a78295 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -34,6 +34,16 @@ export default defineConfig({ // which Angular's `fakeAsync` requires. Karma+Jasmine installed this // implicitly; the @angular/build:unit-test path doesn't. setupFiles: ["src/test-zone-setup.ts"], + // Vitest defaults (5s per test, 10s per hook) are too tight for the + // macOS runners, which stall for seconds at a time under load: the same + // spec file that takes 240ms on ubuntu-latest has been observed taking + // 11.7s on macos-latest in the same commit's matrix. The stall lands on + // whichever test happens to be running, so raising the ceiling is the + // only fix that isn't whack-a-mole — three different specs have gone + // red this way. A test that legitimately needs >30s is broken, and the + // job's own timeout still bounds a true hang. See apache/texera#6073. + testTimeout: 30_000, + hookTimeout: 30_000, // Per-spec exclusions live in `angular.json` (the unit-test builder // applies them at the discovery stage, before Vitest's own filter, // which is what the Vitest team recommends — see the Vite warning