From 728db36ac3eb841595ee9414466df58ec933f2a7 Mon Sep 17 00:00:00 2001 From: Vito Date: Tue, 20 Oct 2026 19:47:37 -0700 Subject: [PATCH] ci: fix the frontend coverage timeout and the WebKit Load more race (#262) * ci: give the frontend coverage job a 40-minute limit with measured headroom The job runs every page workflow twice (chromium, then touch) on one worker with V8 coverage, so it takes about twice a single-browser job. Recent runs took 18m26s-24m57s and PR head 6359999 was cancelled at the 25-minute limit while still passing. Coverage, floors and commands are unchanged. * test(images): dispatch the production Load more press so the observer cannot pre-empt it A pointer click scrolls the continuation row into view, which lets the page's own infinite-scroll observer append the final batch and hide the button before the click lands (WebKit, run 39512528039). Dispatch the press as the concept side already does, after asserting the button is visible and enabled; the count, card, toast and loading-row assertions are unchanged. --- .github/workflows/viewerjs-check.yml | 7 ++++++- tests/browser/images-concept.spec.mjs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/viewerjs-check.yml b/.github/workflows/viewerjs-check.yml index 485c223..41ddf1b 100644 --- a/.github/workflows/viewerjs-check.yml +++ b/.github/workflows/viewerjs-check.yml @@ -26,7 +26,12 @@ jobs: container: image: mcr.microsoft.com/playwright:v1.64.1-noble options: --user 1001 - timeout-minutes: 25 + # This job runs every page workflow twice (chromium, then touch) on one worker with V8 coverage on, so it + # takes about twice a single-browser job. Measured 2026-10-20: whole job 18m26s-24m57s (main 5ded3cf + # 22m15s; PR head 6359999 was cancelled at the old 25-minute limit while still passing), coverage step + # alone 20m55s-22m17s+, against ~12-15 minutes for the firefox/webkit jobs. 40 minutes leaves headroom + # for suite growth while still stopping a hung run; re-measure if a run passes 30 minutes. + timeout-minutes: 40 steps: - uses: actions/checkout@v7 # The image's own Python 3.12 and Node 24: setup-python/setup-node install the runner's toolcache diff --git a/tests/browser/images-concept.spec.mjs b/tests/browser/images-concept.spec.mjs index 0777b0c..e88d417 100644 --- a/tests/browser/images-concept.spec.mjs +++ b/tests/browser/images-concept.spec.mjs @@ -410,7 +410,12 @@ for(const [width,height] of SIZES)for(const theme of THEMES)test(`images page ge await openProductionLoaded(page,theme,motion,total); const before=await page.locator(CARDS).count(); const loadingRow=(await inventoryOf(page,'#workspace-main')).continuation,loadingCount=await countText(page); - await page.getByRole('button',{name:'Load more images',exact:true}).click(); + // A dispatched press, as on the concept side: a pointer click scrolls the row into view first, and the page's + // own observer (rootMargin 250px) can append the final batch and hide the button before the click lands + // (WebKit, run 39512528039). The button must still be the enabled one the user would press. + const more=page.getByRole('button',{name:'Load more images',exact:true}); + await expect(more).toBeVisible();await expect(more).toHaveAttribute('aria-disabled','false'); + await more.dispatchEvent('click'); await expect(page.locator('#result-count')).toHaveText(new RegExp(`^${total} of ${total} images`)); result[motion].productionLoad={before,after:await page.locator(CARDS).count(),toast:await toastText(page),count:await countText(page)}; result[motion].loadingDiff=compareLoadingRow(result[motion].conceptLoad.loadingRow,loadingRow,loadingCount);