From 79bb170cecc483552e085c0fd772b6f5d816a6da Mon Sep 17 00:00:00 2001 From: tomas Date: Sat, 18 Jul 2026 07:58:56 +0000 Subject: [PATCH] test(e2e): fix the project-rename flake at its root and run the suite once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The E2E job reran all 16 spec files on any failure (a shell `until` loop), which was expensive, and the projectRename suite failed reproducibly deep in the shared VS Code run while passing in isolation. Root cause: leaveUnsavedCellEdit located the first code cell's view-line then clicked it as a separate step, so the cell re-rendering between locate and click threw a StaleElementReferenceError — far likelier when the shared instance is sluggish late in the run, where undismissed notification toasts had piled up. (My earlier retryable-setup wrapper masked this behind a misleading open-folder error; it is dropped in favour of the root fix.) - projectRename: locate and click the cell line in one retried step (from #375). - rootHooks.ts + .mocharc.js: a Mocha root afterEach dismisses notification toasts between tests so they do not accumulate across the shared instance. - e2e.yml: run the suite once (drop the until-loop whole-suite retry). Validated locally against the packaged VSIX under Xvfb, exactly as CI runs: full suite 54 passing, 0 failing, 1 pending in a single run; projectRename passes in-place (it failed 2/2 full runs before this fix). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01ELgnvYGB68gTtHncpWN588 --- .github/workflows/e2e.yml | 16 +++------------ test/e2e/.mocharc.js | 7 ++++++- test/e2e/rootHooks.ts | 9 +++++++++ test/e2e/suite/projectRename.e2e.test.ts | 25 ++++++++++++++++++------ 4 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 test/e2e/rootHooks.ts diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index cf7193431..2912addc0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -86,19 +86,9 @@ jobs: pip-${{ runner.os }}-py312- - name: Run E2E - # ExTester launches VS Code with --no-sandbox by default, so no AppArmor sysctl is needed. - # A small retry absorbs transient UI/launch flakiness (mocha already retries the test once). - run: | - attempt=1 - max=2 - until xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24' npm run test:e2e:prebuilt; do - if [ "$attempt" -ge "$max" ]; then - echo "E2E failed after $attempt attempt(s)" - exit 1 - fi - echo "E2E attempt $attempt failed — retrying…" - attempt=$((attempt + 1)) - done + # VS Code launches with --no-sandbox (no AppArmor sysctl needed). Runs once; Mocha's retries:1 + # and rootHooks.ts (dismiss toasts between tests) handle flakiness in the single shared instance. + run: xvfb-run --auto-servernum --server-args='-screen 0 1920x1080x24' npm run test:e2e:prebuilt - name: Upload failure screenshots if: failure() diff --git a/test/e2e/.mocharc.js b/test/e2e/.mocharc.js index c2a25f09a..14040de24 100644 --- a/test/e2e/.mocharc.js +++ b/test/e2e/.mocharc.js @@ -1,9 +1,14 @@ // Mocha configuration for the ExTester (vscode-extension-tester) E2E suite. // UI tests are slow: the 2s Mocha default is unusable. Individual waits inside the // tests are the real guard rails; this is a generous suite-level safety net. +const path = require('path'); + module.exports = { timeout: 1500000, // 25 min — env creation + first kernel start (venv + toolkit) can be slow retries: 1, // absorb transient UI flakiness with a single retry reporter: 'spec', - color: true + color: true, + // Dismiss notification toasts between tests (rootHooks) so they don't accumulate across the one + // shared VS Code instance. Points at compiled output, so compile-e2e must run first. + require: [path.resolve(__dirname, '..', '..', 'out', 'e2e', 'rootHooks.js')] }; diff --git a/test/e2e/rootHooks.ts b/test/e2e/rootHooks.ts new file mode 100644 index 000000000..c91408300 --- /dev/null +++ b/test/e2e/rootHooks.ts @@ -0,0 +1,9 @@ +import { dismissAllNotifications } from './helpers/notifications'; + +// Mocha root hooks (wired via .mocharc.js `require`). ExTester runs every spec in ONE shared VS Code +// instance; dismiss notification toasts between tests so they don't pile up and slow/overlap later specs. +export const mochaHooks = { + async afterEach(): Promise { + await dismissAllNotifications().catch(() => undefined); + } +}; diff --git a/test/e2e/suite/projectRename.e2e.test.ts b/test/e2e/suite/projectRename.e2e.test.ts index 4d123285c..0aba4869a 100644 --- a/test/e2e/suite/projectRename.e2e.test.ts +++ b/test/e2e/suite/projectRename.e2e.test.ts @@ -40,14 +40,27 @@ async function leaveUnsavedCellEdit(): Promise { await openWorkspaceFile(DIRTIED_FILE); - // Focus the first CODE cell's Monaco editor by clicking its visible source line (markdown cells - // render without an editor, so scope to code rows), then type a marker into the focused input. - const line = await driver.wait( - async () => (await driver.findElements(By.css('.notebookOverlay .code-cell-row .view-line')))[0], + // Click the first CODE cell's source line to focus its editor (markdown cells have none); locate + // and click in one retried step, since the cell re-renders on open and can stale a prior reference. + await driver.wait( + async () => { + const line = (await driver.findElements(By.css('.notebookOverlay .code-cell-row .view-line')))[0]; + + if (!line) { + return false; + } + + try { + await line.click(); + + return true; + } catch { + return false; + } + }, WORKBENCH_TIMEOUT, - 'the notebook code cell did not render' + 'the notebook code cell did not render or settle enough to focus' ); - await line.click(); await driver.sleep(400); await driver.switchTo().activeElement().sendKeys(DIRTY_MARKER);