Skip to content

Commit 4533c26

Browse files
authored
[iris] Fix flaky autoscaler-tab smoke screenshot (stale-DOM false positive) (#6287)
Anchor the readiness check on the route hash (`#/autoscaler`) **plus** the section headings that only render in the loaded (`v-else`) branch — `Waterfall Routing`, `Recent Actions`, `Autoscaler Logs` — so the predicate cannot be satisfied by any other page's DOM. This mirrors the existing `_wait_for_worker_detail_screenshot_ready` approach. The shared wait → settle → re-verify scaffold is extracted into `_await_stable_screenshot` so both detail pages share one cadence (resolving the lint-review duplicate-logic finding).
1 parent afe6a5a commit 4533c26

1 file changed

Lines changed: 42 additions & 13 deletions

File tree

lib/iris/tests/e2e/test_smoke.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,21 @@ def capture(label: str, description: str = "") -> Path:
198198
return capture
199199

200200

201+
def _await_stable_screenshot(page, check: str, *, arg=None) -> None:
202+
"""Wait for a screenshot-readiness predicate, settle briefly, then re-verify.
203+
204+
Dashboard pages render structural content only after their first RPC resolves,
205+
and an SPA route swap (lazy-imported components) can leave the previous page's
206+
DOM mounted while the next chunk loads. The settle + re-verify catches a
207+
predicate that passes on such a transient state and then flips back, so the
208+
screenshot lands on the stable loaded page. Timing lives here so both detail
209+
pages share one cadence.
210+
"""
211+
page.wait_for_function(check, arg=arg, timeout=15000)
212+
page.wait_for_timeout(250)
213+
page.wait_for_function(check, arg=arg, timeout=5000)
214+
215+
201216
def _wait_for_worker_detail_screenshot_ready(page, worker_id: str) -> None:
202217
# WorkerDetail.vue uniquely nulls `data` in its workerId watch, so a late
203218
# re-fire can flip the page back to the "Loading worker..." overlay after a
@@ -217,9 +232,7 @@ def _wait_for_worker_detail_screenshot_ready(page, worker_id: str) -> None:
217232
&& headings.includes("task history");
218233
}
219234
"""
220-
page.wait_for_function(check, arg=worker_id, timeout=15000)
221-
page.wait_for_timeout(250)
222-
page.wait_for_function(check, arg=worker_id, timeout=5000)
235+
_await_stable_screenshot(page, check, arg=worker_id)
223236

224237

225238
def _wait_for_job_detail_screenshot_ready(page, job_id: str) -> None:
@@ -496,20 +509,36 @@ def test_dashboard_worker_detail(smoke_cluster, smoke_page, smoke_screenshot, ca
496509
)
497510

498511

512+
def _wait_for_autoscaler_screenshot_ready(page) -> None:
513+
# AutoscalerTab.vue shows only a "Loading autoscaler status…" spinner until its
514+
# first RPC resolves. Route components are lazy-imported, so during the SPA swap
515+
# the previously-viewed page (e.g. worker detail, which renders "Scale Group" +
516+
# the "local-cpu" group name) is still mounted while the autoscaler chunk loads.
517+
# A body-text wait keyed on those strings false-positives on that stale DOM, so
518+
# the screenshot then catches the autoscaler spinner. Anchor on the route hash
519+
# plus the section headings that only render in the loaded (v-else) branch so the
520+
# match can't be satisfied by another page.
521+
check = """
522+
() => {
523+
const text = document.body.textContent || "";
524+
const routeReady = decodeURIComponent(window.location.hash) === "#/autoscaler";
525+
const headings = Array.from(document.querySelectorAll("h3"))
526+
.map((heading) => (heading.textContent || "").trim().toLowerCase());
527+
return routeReady
528+
&& !text.includes("Loading autoscaler status")
529+
&& headings.includes("waterfall routing")
530+
&& headings.includes("recent actions")
531+
&& headings.includes("autoscaler logs");
532+
}
533+
"""
534+
_await_stable_screenshot(page, check)
535+
536+
499537
def test_dashboard_autoscaler_tab(smoke_cluster, smoke_page, smoke_screenshot):
500538
"""Autoscaler tab shows scale groups."""
501539
dashboard_goto(smoke_page, f"{smoke_cluster.url}/autoscaler")
502540
wait_for_dashboard_ready(smoke_page)
503-
# Wait for actual scale group content. The strict !Loading-autoscaler-status check
504-
# blocks on the AutoscalerTab.vue placeholder so the screenshot isn't taken
505-
# during a refetch cycle.
506-
smoke_page.wait_for_function(
507-
"() => !document.body.textContent.includes('Loading autoscaler status') && "
508-
"(document.body.textContent.includes('Scale Group') || "
509-
"document.body.textContent.includes('scale group') || "
510-
"document.body.textContent.includes('local-cpu'))",
511-
timeout=15000,
512-
)
541+
_wait_for_autoscaler_screenshot_ready(smoke_page)
513542
smoke_screenshot("autoscaler-tab", "Autoscaler tab showing scale group configuration")
514543

515544

0 commit comments

Comments
 (0)