diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a580e1895..c4a92c9af 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -5,6 +5,7 @@ on: jobs: test: strategy: + fail-fast: false matrix: os: [ubuntu-latest, macos-latest] name: E2E Testing on ${{ matrix.os }} diff --git a/tests/pageobjects/volview.page.ts b/tests/pageobjects/volview.page.ts index 30a124d89..0503f2e70 100644 --- a/tests/pageobjects/volview.page.ts +++ b/tests/pageobjects/volview.page.ts @@ -53,44 +53,15 @@ class VolViewPage extends Page { async waitForViews(timeout = DOWNLOAD_TIMEOUT) { await browser.waitUntil( async () => { - try { - // Query views once per iteration to avoid multiple queries that could become stale - const currentViews = await this.views; - const viewCount = await currentViews.length; - - if (viewCount === 0) { - return false; - } - - // Check each view's dimensions in a single pass - const viewPromises = currentViews.map(async (view) => { - try { - // Get attributes directly from the element reference - const width = await view.getAttribute('width'); - const height = await view.getAttribute('height'); - - if (width && height) { - const w = parseInt(width, 10); - const h = parseInt(height, 10); - // Canvas should have real dimensions, not be a 1x1 placeholder - // Accept any size > 10 as a real view - return w > 10 && h > 10; - } - return false; - } catch { - // Element may have been removed/recreated - that's ok, we'll retry - return false; - } + return browser.execute(() => { + const canvases = document.querySelectorAll( + 'div[data-testid~="vtk-view"] canvas' + ); + return Array.from(canvases).some((c) => { + const canvas = c as HTMLCanvasElement; + return canvas.width > 10 && canvas.height > 10; }); - - const results = await Promise.all(await viewPromises); - - // At least one view must have real dimensions - return results.some((result) => result); - } catch { - // DOM may be updating, retry on next iteration - return false; - } + }); }, { timeout, @@ -232,12 +203,18 @@ class VolViewPage extends Page { ) { await browser.waitUntil( async () => { - const views2D = await this.getViews2D(); - const view3D = await this.getView3D(); - const view2DCount = await views2D.length; + const counts = await browser.execute(() => ({ + view2DCount: document.querySelectorAll( + 'div[data-testid="vtk-view vtk-two-view"]' + ).length, + view3DExists: + document.querySelector( + 'div[data-testid="vtk-view vtk-volume-view"]' + ) !== null, + })); return ( - view2DCount === expected2DCount && - (view3D !== null) === expected3DExists + counts.view2DCount === expected2DCount && + counts.view3DExists === expected3DExists ); }, { @@ -245,7 +222,6 @@ class VolViewPage extends Page { timeoutMsg: `Expected ${expected2DCount} 2D views and ${ expected3DExists ? 'a' : 'no' } 3D view`, - interval: 1000, } ); } diff --git a/tests/specs/session-state-lifecycle.e2e.ts b/tests/specs/session-state-lifecycle.e2e.ts index 38424ed76..37bad0a07 100644 --- a/tests/specs/session-state-lifecycle.e2e.ts +++ b/tests/specs/session-state-lifecycle.e2e.ts @@ -8,6 +8,16 @@ import { TEMP_DIR } from '../../wdio.shared.conf'; const SESSION_SAVE_TIMEOUT = 40000; +const waitForElementCount = async (selector: string, minCount = 1) => { + await browser.waitUntil(async () => { + const count = await browser.execute( + (sel) => document.querySelectorAll(sel).length, + selector + ); + return count >= minCount; + }); +}; + const saveSession = async () => { const sessionFileName = await volViewPage.saveSession(); const downloadedPath = path.join(TEMP_DIR, sessionFileName); @@ -59,28 +69,14 @@ describe('Session state lifecycle', () => { await measurementsTab.waitForClickable(); await measurementsTab.click(); - await browser.waitUntil(async () => { - const rectangleEntries = await $$( - '.v-list-item i.mdi-vector-square.tool-icon' - ); - return (await rectangleEntries.length) >= 1; - }); - - await browser.waitUntil(async () => { - const polygonEntries = await $$( - '.v-list-item i.mdi-pentagon-outline.tool-icon' - ); - return (await polygonEntries.length) >= 1; - }); + await waitForElementCount('.v-list-item i.mdi-vector-square.tool-icon'); + await waitForElementCount('.v-list-item i.mdi-pentagon-outline.tool-icon'); const segmentGroupsTab = await $('button.v-tab*=Segment Groups'); await segmentGroupsTab.waitForClickable(); await segmentGroupsTab.click(); - await browser.waitUntil(async () => { - const segmentGroups = await $$('.segment-group-list .v-list-item'); - return (await segmentGroups.length) >= 1; - }); + await waitForElementCount('.segment-group-list .v-list-item'); }); it('edited label strokeWidth persists through save/load cycle', async () => { @@ -96,10 +92,7 @@ describe('Session state lifecycle', () => { ); await annotationsTab.click(); - await browser.waitUntil(async () => { - const buttons = await volViewPage.editLabelButtons; - return (await buttons.length) >= 1; - }); + await waitForElementCount('button[data-testid="edit-label-button"]'); const buttons = await volViewPage.editLabelButtons; await buttons[0].click();