Skip to content

test(e2e): bound OfflineWorkMetadata durable-store waits - #228

Merged
Fooftilly merged 6 commits into
masterfrom
cursor/e2e-offline-metadata-pending-bound-5e35
Sep 26, 2026
Merged

Fooftilly merged 6 commits into
masterfrom
cursor/e2e-offline-metadata-pending-bound-5e35

Conversation

@Fooftilly

@Fooftilly Fooftilly commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

Focused fix for the OfflineWorkMetadata APP_READY / body wedge that hung Full E2E (~301s watchdog) on #227 and #212. Not a #212 bug; cascade should stay held until this merges.

Root cause (pinned)

Two unbounded page.evaluate shapes in this module:

  1. pending / settled_conflicts / operations — one evaluate awaited listOperations() before checking a JS deadline. A never-settling store left the 300s watchdog as first kill.
  2. page.evaluate("… => prksNavigate(…)") — Playwright awaits the returned navigation Promise. When leave/render never settles, evaluate hangs until the watchdog. Reproduced with body-stage diags: hang stage CARD_META_NAV on card_meta's navigate evaluate (after both pending() calls had already completed). Leaving metadata edit open made leave wait on an unsaved-draft confirm the test never clicks (intermittent 30s wait_for_selector after voiding navigate alone).

Changes

  • Route pending, settled_conflicts, and operations through wait_for_async (per-await race). Predicates unchanged in strength.
  • Shared navigate(): exit metadata edit, void prksNavigate, wait on DOM (same pattern as test_offline).
  • Bound cached list/person IndexedDB helper reads via wait_for_async sentinels.
  • Blank-page regressions: never-settle helpers fail ~0.4s; void navigate returns immediately when the navigation Promise hangs.

Non-goals

Validation

  • Micro-repro: old pending deadline only observed after a 2s-delayed listOperations; wait_for_async fails near the caller timeout for never-settle.
  • Diagnostic pin: hang at CARD_META_NAV before void-navigate fix.
  • tests.e2e.test_wait_for_async PASS (helper + void-navigate regressions).
  • Stress after full fix (acknowledgement reconcile/keeps/patches + online pending page):
    • serial --jobs 1 × 10 PASS
    • parallel --jobs 2 × 3 PASS
    • test_acknowledgement_patches_every_cached_representation alone × 15 PASS
  • Not a full E2E gate.
Open in Web Open in Cursor 

pending/settled_conflicts used one page.evaluate that awaited
listOperations before checking a JS deadline, so a never-settling store
left the 300s APP_READY watchdog as first kill. Route those helpers
(and operations) through wait_for_async so each await is raced, and
cover never-settle failure with blank-page regressions.

Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: Fooftilly/PRKS/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: a0060895-38ce-4656-b454-5b819ae653c8

📥 Commits

Reviewing files that changed from the base of the PR and between 58a44c4 and c585bbe.

📒 Files selected for processing (2)
  • tests/e2e/test_wait_for_async.py
  • tests/e2e/test_work_metadata_offline.py
 ___________________________________________________________________
< This refactor is like rearranging furniture during an earthquake. >
 -------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

cursoragent and others added 5 commits September 26, 2026 12:30
unittest.TestCase requires a real method name; blank-page bound
regressions only need the helper methods.

Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
Pinned APP_READY body wedge to card_meta's page.evaluate awaiting
prksNavigate's Promise when leave/render never settles. Match
test_offline: void the call and wait on DOM. Also bound cached list/
person IndexedDB reads through wait_for_async sentinels.

Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
Blank-page regression: hung navigation Promise must not block evaluate
when the call is voided.

Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
Voiding prksNavigate exposed intermittent 30s wait_for_selector
timeouts: leave waited on an unsaved-metadata confirm while the
editor was still open. Clear edit mode before fire-and-forget nav.

Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
Keep urllib.parse with the stdlib import block.

Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
@Fooftilly
Fooftilly marked this pull request as ready for review September 26, 2026 13:39
greptile-apps[bot]

This comment was marked as off-topic.

@Fooftilly
Fooftilly merged commit 2b52366 into master Sep 26, 2026
20 of 24 checks passed
@Fooftilly
Fooftilly deleted the cursor/e2e-offline-metadata-pending-bound-5e35 branch September 26, 2026 13:39
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Bound OfflineWorkMetadata E2E waits and navigation

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Bounds durable-store polling so stalled IndexedDB promises fail at caller timeouts.
• Makes navigation fire-and-forget after leaving metadata edit, then waits for DOM readiness.
• Adds blank-page regressions for stalled stores, empty queues, and hung navigation promises.
Diagram

graph TD
  T["Offline metadata tests"] --> H["Bound helpers"] --> W["wait_for_async"] --> S[("Durable store")]
  H --> N["Navigation helper"] --> E["Exit metadata edit"] --> P["Void prksNavigate"] --> D["DOM readiness"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Inline Promise.race guards
  • ➕ Could bound each browser-side operation without changing helper usage.
  • ➕ Keeps timeout behavior colocated with each JavaScript expression.
  • ➖ Duplicates deadline and diagnostic logic across test helpers.
  • ➖ Increases the chance of inconsistent timeout behavior and future unbounded awaits.
2. Await bounded navigation promises
  • ➕ Preserves direct navigation rejection and completion reporting.
  • ➕ Could distinguish navigation failures from later DOM-readiness failures.
  • ➖ Requires reliable cancellation or timeout behavior inside application navigation.
  • ➖ Does not address leave flows blocked by an unhandled unsaved-draft confirmation.

Recommendation: Keep the PR's shared wait_for_async and fire-and-forget navigation approach. It reuses established per-await timeout behavior, preserves existing predicate strength, avoids duplicated deadline code, and uses destination DOM state as the authoritative navigation completion signal.

Files changed (2) +227 / -76

Bug fix (1) +139 / -76
test_work_metadata_offline.pyBound durable-store reads and centralize safe navigation +139/-76

Bound durable-store reads and centralize safe navigation

• Routes operation polling and cached list/entity reads through 'wait_for_async', using truthy wrappers where null or empty results are valid. Introduces a navigation helper that exits metadata edit mode and voids 'prksNavigate', replaces direct navigation evaluations throughout the suite, and URL-encodes dynamic route values in Python.

tests/e2e/test_work_metadata_offline.py

Tests (1) +88 / -0
test_wait_for_async.pyAdd regressions for bounded offline metadata helpers +88/-0

Add regressions for bounded offline metadata helpers

• Adds blank-page Chromium tests proving 'pending', 'settled_conflicts', and 'operations' fail near their caller timeout when 'listOperations()' never settles. Also verifies empty operation lists remain valid and voided navigation does not await a hung Promise.

tests/e2e/test_wait_for_async.py

page.evaluate("""() => {
if (typeof prksSetWorkDetailsMode === 'function') void prksSetWorkDetailsMode('view');
}""")
page.evaluate("r => { void prksNavigate(r); }", route)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Route assertions can inspect stale cards 🐞 Bug ☼ Reliability

navigate() discards the prksNavigate() Promise and returns before the workspace’s asynchronous
leave-and-render chain completes, while callers such as progress_ids() wait on generic selectors
that may still match the departing route. When switching between Progress status routes, the
existing non-loading .prks-tile--main can satisfy the predicate before the requested status
renders, causing progress_ids() to read the previous group’s work identifiers and exposing other
converted callers with shared card selectors to the same stale-render race.
Agent Prompt
## Issue description
`navigate()` starts `prksNavigate()` with `void`, allowing callers to continue before the requested route has committed and rendered. Generic card and tile selectors may already exist on the departing route, so same-page changes such as switching Progress status query parameters can inspect stale content and produce timing-dependent results.

## Fix Focus Areas
- tests/e2e/test_work_metadata_offline.py[430-442]
- tests/e2e/test_work_metadata_offline.py[884-894]

## Recommended Fix
Keep evaluation non-blocking so a hung navigation Promise cannot block indefinitely, but make `navigate()` or each route-specific caller wait for a bounded, unambiguous condition proving that the requested route has been applied and its new render has completed. Use a condition such as the expected canonical `location.hash` together with completion of the destination route’s loading state; in `progress_ids()`, specifically verify that the requested encoded status is active instead of accepting any already-rendered `.prks-tile--main` before reading identifiers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Fooftilly added a commit that referenced this pull request Sep 26, 2026
Void prksNavigate is correct, but progress_ids could accept the
departing Progress group's non-loading tile. Commit the hash in
navigate, require the requested status title before sampling ids,
and drop the fixed 120ms sleep.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Nikola Perović <Fooftilly@users.noreply.github.com>
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.

2 participants