Skip to content

fix(stakes): fix dialog races causing e2e false positives - #434

Open
yhabib wants to merge 3 commits into
mainfrom
e2e-test-false-positives-23f084
Open

fix(stakes): fix dialog races causing e2e false positives#434
yhabib wants to merge 3 commits into
mainfrom
e2e-test-false-positives-23f084

Conversation

@yhabib

@yhabib yhabib commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Most of the E2E failures in CI came from two timing bugs in the app, not from the tests. Both are user facing: a neuron action dialog could stay open after a successful mutation, and the staking wizard could reopen stuck on the previous success screen. Decoding the traces from failing runs showed the ledger transfer returned Ok and claimOrRefresh succeeded, so the on-chain part always worked. While in there, also cut the E2E runtime.

Changes

  • Fixed the navigation blocker race in the neuron detail views. They returned to the summary with setTimeout(onSuccess), but a zero-delay timer does not guarantee React has flushed the effect that disarms NavigationBlockerDialog, so under load the navigation was swallowed. The parent now flags success and navigates from an effect, which runs after the blocker is disarmed.
  • Fixed the staking wizard reset. It ran on a 300ms timer after close, but the effect cleanup clears that timer when the modal reopens first, so the reset never happened. Now it resets on open too, and uses useEffectEvent so the mutation object no longer re-arms the timer on every render.
  • Replaced the 500ms sleep in the stakeNeuron e2e helper with a wait for the wizard to actually close.
  • Sharded E2E into parallel chromium and mobile jobs. Before, both projects shared one runner and ran nearly back to back.
  • Added npm, cargo and Playwright browser caching, using workspace-local dirs so they work both from actions/cache and inside the mounted container.
  • Added a concurrency group with cancel-in-progress for pull_request only. merge_group is excluded on purpose, cancelling those would drop a merge.
  • Dropped e2e retries from 2 to 1. Every spec is a describe.serial block, so a retry re-runs the whole block including its slow beforeAll.

yhabib added 2 commits July 25, 2026 17:32
Two timing-dependent bugs were producing most of the E2E failures in CI.
Both are user-facing, not test artifacts.

Navigation blocker race: the four neuron detail action views returned to the
summary via setTimeout(onSuccess). onSuccess triggers a router navigation,
which NavigationBlockerDialog intercepts while isProcessing is still true. A
zero-delay timer does not guarantee React has committed and flushed the effect
that disarms the blocker, so under load the navigation was silently swallowed
and the dialog stayed open after a successful mutation. The parent now flags
success and navigates from an effect, relying on React running child effects
before parent effects.

Staking wizard reset: state was reset on a 300ms timer after close, but the
effect cleanup clears that timer when the modal reopens first, so the reset
never ran and the wizard reopened stuck on the previous success screen with no
amount input. Reset on open as well, and use useEffectEvent so the mutation
object no longer re-arms the timer on every render.

Trace decoding confirmed the ledger transfer returned Ok and claimOrRefresh
succeeded in the failing runs, so the on-chain work was never the problem.
The E2E job did all the work on one runner with no caching: npm ci (~70s), a
full Vite build and a cargo release build inside dfx deploy (~47s), and a
Playwright browser install (~25s) on every run. npm ci ran in five separate
jobs per PR, and the backend compiled twice.

- Shard E2E into parallel chromium and mobile jobs. Previously both projects
  shared one runner and ran nearly back to back.
- Cache npm, cargo and Playwright browsers via workspace-local dirs, so they
  are reachable both from actions/cache and from inside the mounted container.
- Add a concurrency group with cancel-in-progress for pull_request only.
  merge_group is deliberately excluded, since cancelling those would drop a
  merge.
- Drop e2e retries from 2 to 1. Every spec is a describe.serial block, so a
  retry re-runs the whole block including its expensive beforeAll; two retries
  meant one stuck test could burn ~15 minutes before reporting.

Snapshot artifacts are now per project, so update-snapshots.sh downloads by
pattern instead of by exact name.
@yhabib
yhabib requested a review from a team as a code owner July 25, 2026 15:33
Copilot AI review requested due to automatic review settings July 25, 2026 15:33
@zeropath-ai

zeropath-ai Bot commented Jul 25, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to e1153e8.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► .github/workflows/check.yaml
    Add npm and cargo cache restoration steps, plus cargo cache restoration and frontend dist placeholder generation steps
► .github/workflows/e2e.yml
    Add npm, cargo, and Playwright cache restoration and environment variable mappings for E2E runs
► .github/workflows/pipeline_dev.yaml
    Add concurrency cancellation for in-flight PR runs
► .github/workflows/pr_checks.yml
    Add concurrency cancellation for in-flight PR runs
► scripts/update-snapshots.sh
    Update artifact pattern to match multiple updated snapshots per project
► src/governance-app-frontend/playwright.config.ts
    Reduce retries to 1 on CI to avoid long stuck tests while keeping CI retry behavior
► src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailDissolveView.tsx
    Call onSuccess directly instead of delaying with setTimeout
► src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailIncreaseDelayView.tsx
    Call onSuccess directly instead of delaying with setTimeout
► src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailIncreaseStakeView.tsx
    Call onSuccess directly instead of delaying with setTimeout
► src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailMaturityModeView.tsx
    Call onSuccess directly instead of delaying with setTimeout
► src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailModal.tsx
    Introduce pendingSuccess state and navigation handling via effect; replace direct onSuccess usage in action views
► src/governance-app-frontend/src/features/stakes/components/stakingWizard/StakingWizardModal.tsx
    Introduce reset logic via useEffectEvent; adjust reset timing to handle open/close transitions

@github-actions

Copy link
Copy Markdown

📊 Build Bundle Stats

The latest build generated the following assets:

dist/index.html                                           2.39 kB │ gzip:   0.84 kB
dist/assets/index-DSwNl_yb.css                          131.57 kB │ gzip:  21.37 kB
dist/assets/externalServices-CK4eoRia.js                  0.19 kB │ gzip:   0.16 kB
dist/assets/address-C-4H442a.js                           0.20 kB │ gzip:   0.15 kB
dist/assets/Skeleton-D4rpq-Go.js                          0.46 kB │ gzip:   0.34 kB
dist/assets/useTvlValue-DsBXUzbX.js                       0.57 kB │ gzip:   0.39 kB
dist/assets/numbers-CPDItGAD.js                           0.58 kB │ gzip:   0.36 kB
dist/assets/service-DzScMUg0.js                           0.59 kB │ gzip:   0.37 kB
dist/assets/Spinner-m4r46xmU.js                           0.59 kB │ gzip:   0.41 kB
dist/assets/PageHeader-Czh9Jiub.js                        0.77 kB │ gzip:   0.45 kB
dist/assets/CertifiedBadge-Dti8h52M.js                    0.81 kB │ gzip:   0.48 kB
dist/assets/Separator-JF9oz7Jm.js                         0.83 kB │ gzip:   0.49 kB
dist/assets/useIcpIndex-BdqodfpU.js                       1.07 kB │ gzip:   0.63 kB
dist/assets/rolldown-runtime-CMxvf4Kt.js                  1.21 kB │ gzip:   0.67 kB
dist/assets/EmptyActionState-DAOb8XXS.js                  1.61 kB │ gzip:   0.83 kB
dist/assets/addressBook-6jSrC6rJ.js                       1.66 kB │ gzip:   0.99 kB
dist/assets/useCommandPaletteSettings-CC8VWgq_.js         1.73 kB │ gzip:   0.86 kB
dist/assets/useGovernanceAppCanister-DRFwiXbg.js          1.91 kB │ gzip:   0.96 kB
dist/assets/AnimatedNumber-BKWvj7AA.js                    1.96 kB │ gzip:   1.11 kB
dist/assets/useIcpIndexTransactionsPolling-rvmP7b6W.js    2.03 kB │ gzip:   1.09 kB
dist/assets/ToggleGroup-CZusfBCX.js                       3.09 kB │ gzip:   1.41 kB
dist/assets/tickers-CZm-4bhD.js                           3.10 kB │ gzip:   1.47 kB
dist/assets/Switch-BLkHAUIe.js                            3.22 kB │ gzip:   1.44 kB
dist/assets/SensitiveValue-Nn3idYD-.js                    7.33 kB │ gzip:   3.28 kB
dist/assets/routes-Moqg5WOX.js                            7.46 kB │ gzip:   2.47 kB
dist/assets/badge-Co1klocZ.js                             8.12 kB │ gzip:   2.36 kB
dist/assets/spamFilter-fNDFEHOn.js                        8.54 kB │ gzip:   3.81 kB
dist/assets/MutationDialog-B1FIhHKv.js                    9.53 kB │ gzip:   3.42 kB
dist/assets/events-BZJ7Y02M.js                           10.38 kB │ gzip:   4.11 kB
dist/assets/_id-CUlSktRw.js                              14.33 kB │ gzip:   4.30 kB
dist/assets/_auth-Ac_iPDxZ.js                            15.60 kB │ gzip:   5.74 kB
dist/assets/accounts-Bnt7wYr-.js                         23.72 kB │ gzip:   8.16 kB
dist/assets/GetTokens-C_yNkmR5.js                        41.82 kB │ gzip:  14.86 kB
dist/assets/settings-B7TJp0g8.js                         42.51 kB │ gzip:  12.20 kB
dist/assets/dashboard-CpkYBnnZ.js                        50.18 kB │ gzip:  15.03 kB
dist/assets/button-C5uNUNtl.js                           57.22 kB │ gzip:  19.69 kB
dist/assets/neurons-B-w_QyzU.js                          68.04 kB │ gzip:  20.26 kB
dist/assets/voting-BdZVGN8o.js                           69.21 kB │ gzip:  21.17 kB
dist/assets/vendor-md-CLh1GvDS.js                        73.29 kB │ gzip:  19.98 kB
dist/assets/neuronDetail-DDivKk6n.js                     77.12 kB │ gzip:  22.90 kB
dist/assets/index-BFULEodJ.js                            80.14 kB │ gzip:  25.02 kB
dist/assets/vendor-tanstack-CcoGnCz0.js                 161.15 kB │ gzip:  49.35 kB
dist/assets/vendor-core-react-qLCIanUQ.js               189.81 kB │ gzip:  59.76 kB
dist/assets/vendor-recharts-CC-WrRXd.js                 223.45 kB │ gzip:  64.00 kB
dist/assets/vendor-icp-DP4ARddz.js                      411.37 kB │ gzip: 104.08 kB
dist/assets/vendor-libs-C_IwxdbG.js                     554.14 kB │ gzip: 180.78 kB

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses two user-facing timing/race issues in the staking and neuron-action dialogs that were causing E2E false positives, and reduces CI E2E runtime by improving test synchronization and parallelizing/caching the E2E workflow.

Changes:

  • Fix neuron detail action “return to summary” navigation race by deferring navigation to a parent effect after the navigation blocker is disarmed.
  • Fix staking wizard state reset race by resetting on open as well as (delayed) on close, using useEffectEvent to avoid timer re-arming across renders.
  • Speed up and stabilize CI E2E runs via better waits, Playwright project sharding, dependency/browser caching, reduced retries, and PR-only concurrency cancellation.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/governance-app-frontend/tests/e2e/advanced-following.spec.ts Replaces a fixed sleep with a visibility-based wait after staking wizard completion.
src/governance-app-frontend/src/features/stakes/components/stakingWizard/StakingWizardModal.tsx Resets wizard state on open and (delayed) on close to avoid reopening on a stale success step.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailModal.tsx Removes timer-based “go back” and introduces parent-controlled success navigation sequencing.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailMaturityModeView.tsx Calls onSuccess() directly; parent now handles safe navigation timing.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailIncreaseStakeView.tsx Calls onSuccess() directly; parent now handles safe navigation timing.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailIncreaseDelayView.tsx Calls onSuccess() directly; parent now handles safe navigation timing.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailDissolveView.tsx Calls onSuccess() directly; parent now handles safe navigation timing.
src/governance-app-frontend/playwright.config.ts Lowers CI retries to 1 with rationale tied to describe.serial cost.
scripts/update-snapshots.sh Downloads multiple per-shard snapshot artifacts via --pattern and searches recursively.
.gitignore Ignores workspace-local CI cache directory .ci-cache/.
.github/workflows/pr_checks.yml Adds PR-only concurrency cancellation to supersede in-flight runs for the same PR.
.github/workflows/pipeline_dev.yaml Adds PR-only concurrency cancellation to supersede in-flight runs for the same PR.
.github/workflows/e2e.yml Shards E2E per Playwright project, adds caches, and uploads per-project artifacts/reports.
.github/workflows/check.yaml Adds workspace-local npm/cargo cache dirs and restores caches in relevant jobs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/governance-app-frontend/tests/e2e/advanced-following.spec.ts Outdated
Comment thread scripts/update-snapshots.sh Outdated
Copilot review feedback. The success screen can disappear mid-close-animation
once the wizard state resets, so it is not a reliable signal that the dialog has
closed. Wait on staking-wizard-dialog with an explicit timeout instead.

Also fixed the stale single-artifact name in the update-snapshots.sh early-exit
note now that snapshots are uploaded per project.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants