Skip to content

DO NOT REVIEW - chore(cypress): Try to analyze failures#61985

Open
DerDreschner wants to merge 5 commits into
masterfrom
chore/analyze-cypress-failures
Open

DO NOT REVIEW - chore(cypress): Try to analyze failures#61985
DerDreschner wants to merge 5 commits into
masterfrom
chore/analyze-cypress-failures

Conversation

@DerDreschner

Copy link
Copy Markdown
Contributor

Summary

This PR is only for pushing some possible fixes and see if they actually change the flaky behavior. DO NOT REVIEW RIGHT NOW, the to review label is only to let the cypress tests actually run!

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@DerDreschner DerDreschner requested a review from a team as a code owner July 11, 2026 00:08
@DerDreschner DerDreschner added the 3. to review Waiting for reviews label Jul 11, 2026
@DerDreschner DerDreschner requested review from nfebe, sorbaugh and susnux and removed request for a team July 11, 2026 00:08
@DerDreschner DerDreschner requested a review from skjnldsv as a code owner July 11, 2026 01:34
@DerDreschner DerDreschner force-pushed the chore/analyze-cypress-failures branch from d6d9efb to f44077c Compare July 11, 2026 01:44
Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
@DerDreschner DerDreschner force-pushed the chore/analyze-cypress-failures branch from 1417f2e to 4e89b8e Compare July 11, 2026 03:50
@DerDreschner

Copy link
Copy Markdown
Contributor Author

Solved. Here's the complete story and where everything stands:

Root cause (proven, not theorized)

The instrumented floating-vue call log settled it: the actions menu was never "stuck" — it was just slow, and every recovery strategy attacked it mid-opening.

  1. The files app's actions menu runs on floating-vue 1.0.0-beta.19 (via @nextcloud/vue v8 in the legacy webpack workspace). Its open sequence — position computation plus frame-by-frame transition classes — takes over a second under CPU starvation (the log showed isShown still false 1.2s after the click). During that whole window the toggle already reports aria-expanded="true" while the popper still wears --hidden.
  2. Cypress's default 4s visibility timeout sometimes expired right there → the original CI failures.
  3. Worse, every "fix" so far (the committed helpers, including mine) re-clicked when it saw "expanded but hidden" — which floating-vue's own click trigger, NcActions' controlled prop, and the files store each interpret independently. The interleaved show/hide transitions wedge the popper into the permanently hidden --hidden --show-to --hide-to state we kept photographing. The recovery clicks were manufacturing the unrecoverable state.

The fix (in your working tree, cypress/e2e/files/FilesUtils.ts)

openActionsMenu() now does the boring, correct thing: click once, only if aria-expanded != "true", then wait on the aria-controls menu with cy.get(..., { timeout: 20000 }).should('be.visible'). Never click while expanded (a genuine auto-close syncs aria-expanded back to false, so the conditional click still handles that case).

Validation: 3 full runs of version_naming + version_deletion under CPU throttle 4× + server pinned to 1 CPU — conditions that previously failed 100% of runs with all retries exhausted — now pass 18/18 tests with zero retries, and run 3-4× faster (2:30 vs 8-10 min per run).

Housekeeping for you

  • Commit: the FilesUtils.ts rewrite. The cypress/support/e2e.ts throttle hook (CYPRESS_CPU_THROTTLE=4 npx cypress run ...) is worth keeping for future flake hunts — your call.
  • Don't commit the dist/ and core/css/ churn from my local builds — CI builds its own; git checkout -- dist core/css before committing.
  • The committed container="main.app-content" change (1f906f668e1) turned out evidence-neutral — droppable from the final PR, though the argument against teleporting into the virtual list still stands on its own.
  • The floating-vue instrumentation patches in node_modules are reverted; repro tooling was: renderer throttle + docker update --cpus=1 nextcloud-e2e-test-server_server.
  • Product-side observation worth an issue (your words, per policy): a popover that needs multiple seconds to appear and wedges permanently if toggled mid-transition is a real UX defect on loaded servers/devices — the evidence (call log, stuck classes, beta.19 pin in @nextcloud/vue v8) is all in this session; modern floating-vue 5.x reworked those transitions, so a dependency bump in nextcloud-vue is the likely durable fix.

Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
@DerDreschner

Copy link
Copy Markdown
Contributor Author

Here's the summary of the progressbar issue in files_sharing/public-share/view_file-drop.cy.ts ("Can upload files"):

Symptom

The test flakes on CI (seen on runner 0 of the PR #61984 cypress run, and reproduced locally at a 100% rate with the browser CPU-throttled 6× and the test server pinned to 1 CPU). Two different error signatures appear depending on timing:

  • AssertionError: expected findByRole('progressbar') to be visible — while the screenshots clearly show progress bars rendered on the page, or
  • CypressError: A response handler timed out after returning a Promise that took more than the defaultCommandTimeout of 4000ms to resolve on the PUT **/public.php/dav/files/** intercept.

Root cause

The test uploads two files and deliberately holds the second upload's response inside a cy.intercept handler (await promise) until a progress-bar assertion has passed, then releases it.

The gating assertion was cy.findByRole('progressbar').should('be.visible') — a singular query. The file-drop page renders multiple elements with the progressbar role during an upload (the upload-picker in the toolbar and the file-drop view's own picker), and some of them are hidden. The singular query resolves to one of the hidden instances, so should('be.visible') can never pass, regardless of timeout.

The second error signature is downstream of the first: because the assertion dead-ends, resolve() is never called, so the intercept's response handler is guaranteed to hit Cypress's rule that intercept handlers must settle within defaultCommandTimeout (4s). Whichever limit is reached first decides which error is reported. On fast machines the timing usually lands so the test passes; under CI load it reliably doesn't.

Fix

Replace the singular query with cy.findAllByRole('progressbar') and assert that at least one visible progress bar reports ≥ 50% — releasing the held response as before. The assertion's failure message enumerates every matched element with its tag, value, and visibility, so any future disagreement is self-diagnosing. No timeout changes needed: with a gate that can actually pass, the 4s intercept deadline holds comfortably (verified — a defaultCommandTimeout: 20000 override was tried and then proven unnecessary).

Evidence

  • Reproduced: 100% failure rate across multiple runs under CYPRESS_CPU_THROTTLE=6 + docker update --cpus=1 on the test server; original CI failure screenshot shows the identical signature.
  • Fixed: 4 consecutive throttled runs, 5/5 tests each, zero retries, ~44s per run (previously 3+ minutes of exhausted retries).
  • This was the only failing spec in a full 28-spec suite sweep under those conditions — everything else passed with zero retries.

The change is uncommitted in cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts (+17/−3, lint-clean), ready for your sign-off with Assisted-by: ClaudeCode:claude-fable-5.

@DerDreschner

Copy link
Copy Markdown
Contributor Author

To summarize where we are while the instrumented runs go: I reproduced the flake locally (renderer ×10 + server 0.5 CPU — version_naming retried 2-3× in run 3), and the CI logs are clean of any server-side cause, confirming it's the client-side menu-open race. The instrumented helper now records every aria-expanded/visible transition and dumps them on a hard failure. That transition trace will tell us definitively whether the menu gets stuck positioning (expanded stays true) or is closed by a re-render (expanded flips back to false) — which decides the fix (a longer wait vs. a re-click-on-collapse recovery). I'm running it 5× to catch a hard failure with the trace; report follows.

Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews AI assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants