summaryrefslogtreecommitdiff
path: root/tools/acceptance
diff options
context:
space:
mode:
Diffstat (limited to 'tools/acceptance')
-rwxr-xr-xtools/acceptance/e2e_acceptance.py89
1 files changed, 88 insertions, 1 deletions
diff --git a/tools/acceptance/e2e_acceptance.py b/tools/acceptance/e2e_acceptance.py
index ee75ee6..1b3ae6c 100755
--- a/tools/acceptance/e2e_acceptance.py
+++ b/tools/acceptance/e2e_acceptance.py
@@ -253,7 +253,12 @@ def stale_load_regression(browser, base_url, track, results, evidence):
Intercepts steps GLB requests and fulfills them late, out of order, or
with errors to prove stale responses from superseded runs never repaint
- geometry or notes, while the current owner's loads keep working.
+ geometry or notes, while the current owner's loads keep working. Also
+ covers: user retry of a failed current latest-step load after run
+ completion (same owner/path, no SSE reconnect), same-run older-step
+ responses resolving after the newer step, and pending live responses
+ ignored after entering replay. All still-held responses are drained and
+ the route handler is removed before the page closes.
"""
page = browser.new_page(viewport={"width": 1280, "height": 900})
@@ -403,6 +408,88 @@ def stale_load_regression(browser, base_url, track, results, evidence):
"stale-load: current owner load unaffected by stale error"))
(evidence / "stale-scene-final.json").write_text(json.dumps(g5, indent=2, ensure_ascii=False))
page.screenshot(path=str(evidence / "stale-page-final.png"), full_page=True)
+
+ # Scenario D: current latest-step failure after run completion retries the
+ # same owner/path via the visible note affordance, without new SSE events.
+ run_k = f"run-stale-{stamp}-k"
+ start_run(run_k)
+ release(held_for(run_k, "steps/01-foundation.glb"))
+ wait_text(page, "#viewport-note", "管线几何 · steps/01-foundation.glb")
+ ek3 = held_for(run_k, "steps/03-cabin.glb")
+ wait_text(page, "#status-label", "已完成")
+ release(ek3, status=500)
+ wait_text(page, "#viewport-note", "管线几何加载失败 · steps/03-cabin.glb")
+ cursor = page.evaluate(
+ "() => getComputedStyle(document.querySelector('#viewport-note')).cursor")
+ results.append((cursor == "pointer",
+ f"stale-load: failed current load exposes retry affordance (cursor={cursor})"))
+ page.screenshot(path=str(evidence / "stale-page-retry-failed.png"), full_page=True)
+ page.click("#viewport-note")
+ ek4 = held_for(run_k, "steps/03-cabin.glb")
+ retries = [e for e in held if e["run"] == run_k and e["path"] == "steps/03-cabin.glb"]
+ results.append((len(retries) == 2 and ek4 is retries[1],
+ f"stale-load: user retry re-requests same owner/path ({len(retries)} requests)"))
+ stream = page.evaluate("() => document.querySelector('#stream-state').textContent")
+ results.append(("已连接" in stream,
+ f"stale-load: retry works without SSE reconnect (stream='{stream[:30]}')"))
+ release(ek4)
+ wait_text(page, "#viewport-note", "管线几何 · steps/03-cabin.glb")
+ page.wait_for_timeout(600)
+ gk = page.evaluate(SCENE_JS)
+ cursor_after = page.evaluate(
+ "() => getComputedStyle(document.querySelector('#viewport-note')).cursor")
+ results.append((gk["ready"] and is_glb(gk) and box_ok(gk) and cursor_after != "pointer",
+ f"stale-load: retry displays latest step and clears affordance "
+ f"(kids={gk.get('kids')})"))
+ page.screenshot(path=str(evidence / "stale-page-retry-ok.png"), full_page=True)
+
+ # Scenario E: same-run older step resolving after the newer step must be ignored.
+ run_h = f"run-stale-{stamp}-h"
+ start_run(run_h)
+ held_for(run_h, "steps/01-foundation.glb")
+ eh3 = held_for(run_h, "steps/03-cabin.glb")
+ release(eh3)
+ wait_text(page, "#viewport-note", "管线几何 · steps/03-cabin.glb")
+ page.wait_for_timeout(600)
+ gh = page.evaluate(SCENE_JS)
+ results.append((gh["ready"] and is_glb(gh) and box_ok(gh)
+ and "steps/03-cabin.glb" in gh["note"],
+ f"stale-load: newer same-run step displayed after supersede "
+ f"(kids={gh.get('kids')})"))
+ note_h = gh["note"]
+ release(held_for(run_h, "steps/01-foundation.glb"))
+ page.wait_for_timeout(600)
+ gh2 = page.evaluate(SCENE_JS)
+ results.append((gh2["ready"] and gh2["kids"] == gh["kids"] and gh2["note"] == note_h,
+ f"stale-load: late older same-run step ignored (note='{gh2['note'][:40]}')"))
+
+ # Scenario F: live GLB response still pending after entering replay must
+ # not repaint the selected replay frame view.
+ run_j = f"run-stale-{stamp}-j"
+ start_run(run_j)
+ release(held_for(run_j, "steps/01-foundation.glb"))
+ wait_text(page, "#viewport-note", "管线几何 · steps/01-foundation.glb")
+ ej3 = held_for(run_j, "steps/03-cabin.glb")
+ wait_text(page, "#status-label", "已完成")
+ page.click("#replay-button")
+ page.wait_for_timeout(300)
+ page.click("#replay-button")
+ page.wait_for_timeout(300)
+ gj = page.evaluate(SCENE_JS)
+ results.append((gj["ready"] and is_frame(gj) and box_ok(gj),
+ f"stale-load: replay entry repaints frame view while GLB pending "
+ f"(kids={gj.get('kids')})"))
+ page.screenshot(path=str(evidence / "stale-page-replay.png"), full_page=True)
+ release(ej3)
+ page.wait_for_timeout(600)
+ gj2 = page.evaluate(SCENE_JS)
+ results.append((gj2["ready"] and gj2["kids"] == gj["kids"] and gj2["note"] == gj["note"],
+ f"stale-load: live GLB after replay ignored (note='{gj2['note'][:40]}')"))
+
+ for entry in held:
+ if not entry["done"]:
+ release(entry)
+ page.unroute("**/api/artifacts/steps*")
page.close()