From a04376fa3eefca49b2ce4698f10136db9ae22219 Mon Sep 17 00:00:00 2001 From: mspivak-actionengine Date: Mon, 8 Jan 2024 23:44:06 +0300 Subject: [PATCH] fix(e2e-test): random failures on navigations --- src/pages/dashboard/e2e.dashboard.spec.ts | 104 +++++++++++--------- src/pages/debug-app/e2e.debug-app.spec.ts | 4 +- src/pages/viewer-app/e2e.viewer-app.spec.ts | 4 +- src/utils/testing-utils/utils.ts | 36 +++++++ 4 files changed, 96 insertions(+), 52 deletions(-) create mode 100644 src/utils/testing-utils/utils.ts diff --git a/src/pages/dashboard/e2e.dashboard.spec.ts b/src/pages/dashboard/e2e.dashboard.spec.ts index c9116c94..3f8dbf34 100644 --- a/src/pages/dashboard/e2e.dashboard.spec.ts +++ b/src/pages/dashboard/e2e.dashboard.spec.ts @@ -1,4 +1,5 @@ import puppeteer from "puppeteer"; +import { clickAndNavigate } from "../../utils/testing-utils/utils"; describe("Dashboard Default View", () => { let browser; @@ -50,9 +51,12 @@ describe("Dashboard Default View", () => { it("Should go to the Viewer page", async () => { await page.goto("http://localhost:3000"); await page.waitForSelector("#header-links-default"); - await page.click("a[href='/viewer']"); - await page.waitForTimeout(5000); - const currentUrl = page.url(); + + const currentUrl = await clickAndNavigate( + page, + "a[href='/viewer']", + "tileset=" + ); expect(currentUrl).toBe( "http://localhost:3000/viewer?tileset=san-francisco-v1_7" ); @@ -63,9 +67,11 @@ describe("Dashboard Default View", () => { it("Should go to the Debug page", async () => { await page.goto("http://localhost:3000"); await page.waitForSelector("#header-links-default"); - await page.click("a[href='/debug']"); - await page.waitForTimeout(5000); - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate( + page, + "a[href='/debug']", + "tileset=" + ); expect(currentUrl).toBe( "http://localhost:3000/debug?tileset=san-francisco-v1_7" ); @@ -78,9 +84,10 @@ describe("Dashboard Default View", () => { await page.waitForSelector("#compare-default-button"); await page.click("#compare-default-button"); await page.hover("a[href='/compare-across-layers']"); - await page.click("a[href='/compare-across-layers']"); - - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate( + page, + "a[href='/compare-across-layers']" + ); expect(currentUrl).toBe("http://localhost:3000/compare-across-layers"); }); @@ -89,28 +96,28 @@ describe("Dashboard Default View", () => { await page.waitForSelector("#compare-default-button"); await page.click("#compare-default-button"); await page.hover("a[href='/compare-within-layer']"); - await page.click("a[href='/compare-within-layer']"); - - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate( + page, + "a[href='/compare-within-layer']" + ); expect(currentUrl).toBe("http://localhost:3000/compare-within-layer"); }); it("Should go to the project GitHub page", async () => { await page.goto("http://localhost:3000"); await page.waitForSelector("#header-links-default"); - await page.click("a[href='https://github.com/visgl/loaders.gl-showcases']"); - await page.waitForTimeout(5000); + const currentUrl = await clickAndNavigate( + page, + "a[href='https://github.com/visgl/loaders.gl-showcases']" + ); - const currentUrl = page.url(); expect(currentUrl).toBe("https://github.com/visgl/loaders.gl-showcases"); await page.goto("http://localhost:3000"); }); it("Should return from viewer page to Dashboard", async () => { await page.goto("http://localhost:3000/viewer"); - await page.waitForSelector("#header-links-default"); - await page.click("a[href='/dashboard']"); - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate(page, "a[href='/dashboard']"); expect(currentUrl).toBe("http://localhost:3000/dashboard"); const dashboardCanvas = await page.$$("#dashboard-app"); expect(dashboardCanvas).toBeDefined(); @@ -286,10 +293,8 @@ describe("Dashboard Default View", () => { }); it("Should go to viewer page from tools description", async () => { - await page.waitForSelector("#viewer-link"); - await page.click("#viewer-link"); - - const currentUrl = page.url(); + const el = await page.waitForSelector("#viewer-link"); + const currentUrl = await clickAndNavigate(page, "#viewer-link", "tileset="); expect(currentUrl).toBe( "http://localhost:3000/viewer?tileset=san-francisco-v1_7" ); @@ -298,9 +303,7 @@ describe("Dashboard Default View", () => { it("Should go to debug page from tools description", async () => { await page.goto("http://localhost:3000"); await page.waitForSelector("#debug-link"); - await page.click("#debug-link"); - await page.waitForTimeout(5000); - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate(page, "#debug-link", "tileset="); expect(currentUrl).toBe( "http://localhost:3000/debug?tileset=san-francisco-v1_7" ); @@ -309,9 +312,11 @@ describe("Dashboard Default View", () => { it("Should go to the Comparison Across Layers Page", async () => { await page.goto("http://localhost:3000"); await page.waitForSelector("#comparison-link"); - await page.click("#comparison-link"); - - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate( + page, + "#comparison-link", + "tileset=" + ); expect(currentUrl).toBe("http://localhost:3000/compare-across-layers"); }); }); @@ -412,10 +417,11 @@ describe("Dashboard Tablet or Mobile view", () => { await page.waitForSelector("#burger-menu"); await page.click("#burger-menu"); await page.waitForSelector("#header-links"); - await page.click("a[href='/viewer']"); - await page.waitForTimeout(5000); - - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate( + page, + "a[href='/viewer']", + "tileset=" + ); expect(currentUrl).toBe( "http://localhost:3000/viewer?tileset=san-francisco-v1_7" ); @@ -429,10 +435,11 @@ describe("Dashboard Tablet or Mobile view", () => { await page.waitForSelector("#burger-menu"); await page.click("#burger-menu"); await page.waitForSelector("#header-links"); - await page.click("a[href='/debug']"); - await page.waitForTimeout(5000); - - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate( + page, + "a[href='/debug']", + "tileset=" + ); expect(currentUrl).toBe( "http://localhost:3000/debug?tileset=san-francisco-v1_7" ); @@ -446,10 +453,11 @@ describe("Dashboard Tablet or Mobile view", () => { await page.waitForSelector("#burger-menu"); await page.click("#burger-menu"); await page.waitForSelector("#header-links"); - await page.click("a[href='https://github.com/visgl/loaders.gl-showcases']"); - await page.waitForTimeout(5000); + const currentUrl = await clickAndNavigate( + page, + "a[href='https://github.com/visgl/loaders.gl-showcases']" + ); - const currentUrl = page.url(); expect(currentUrl).toBe("https://github.com/visgl/loaders.gl-showcases"); expect(!(await page.$("#tablet-or-mobile-menu"))); }); @@ -459,9 +467,7 @@ describe("Dashboard Tablet or Mobile view", () => { await page.waitForSelector("#burger-menu"); await page.click("#burger-menu"); await page.waitForSelector("#header-links"); - await page.click("a[href='/dashboard']"); - - const currentUrl = page.url(); + const currentUrl = await clickAndNavigate(page, "a[href='/dashboard']"); expect(currentUrl).toBe("http://localhost:3000/dashboard"); const dashboardCanvas = await page.$$("#dashboard-app"); expect(dashboardCanvas).toBeDefined(); @@ -501,15 +507,16 @@ describe("Dashboard Tablet or Mobile view", () => { await page.click("#compare-tablet-or-mobile-button"); await page.hover("a[href='/compare-across-layers']"); - await page.click("a[href='/compare-across-layers']"); + const currentUrl = await clickAndNavigate( + page, + "a[href='/compare-across-layers']" + ); await page.waitForSelector("#left-deck-container"); await page.waitForSelector("#right-deck-container"); expect(await page.$$("#left-deck-container")).toBeDefined(); expect(await page.$$("#right-deck-container")).toBeDefined(); - - const currentUrl = page.url(); expect(currentUrl).toBe("http://localhost:3000/compare-across-layers"); }); @@ -521,15 +528,16 @@ describe("Dashboard Tablet or Mobile view", () => { await page.click("#compare-tablet-or-mobile-button"); await page.hover("a[href='/compare-within-layer']"); - await page.click("a[href='/compare-within-layer']"); + const currentUrl = await clickAndNavigate( + page, + "a[href='/compare-within-layer']" + ); await page.waitForSelector("#left-deck-container"); await page.waitForSelector("#right-deck-container"); expect(await page.$$("#left-deck-container")).toBeDefined(); expect(await page.$$("#right-deck-container")).toBeDefined(); - - const currentUrl = page.url(); expect(currentUrl).toBe("http://localhost:3000/compare-within-layer"); }); }); diff --git a/src/pages/debug-app/e2e.debug-app.spec.ts b/src/pages/debug-app/e2e.debug-app.spec.ts index 22f916bd..79b22117 100644 --- a/src/pages/debug-app/e2e.debug-app.spec.ts +++ b/src/pages/debug-app/e2e.debug-app.spec.ts @@ -31,7 +31,7 @@ describe("Debug", () => { expect( await page.$eval( "#header-links-default>a[active='1']", - (node) => node.innerText + (node) => node.textContent ) ).toEqual("Debug"); }); @@ -134,7 +134,7 @@ describe("Debug - Layers panel", () => { expect( await page.$eval( "#debug--layers-panel #san-francisco-v1_7>input", - (node) => node.checked + (node) => (node as HTMLInputElement).checked ) ).toBeTruthy(); }); diff --git a/src/pages/viewer-app/e2e.viewer-app.spec.ts b/src/pages/viewer-app/e2e.viewer-app.spec.ts index f34bf9a0..fc59db2b 100644 --- a/src/pages/viewer-app/e2e.viewer-app.spec.ts +++ b/src/pages/viewer-app/e2e.viewer-app.spec.ts @@ -31,7 +31,7 @@ describe("Viewer", () => { expect( await page.$eval( "#header-links-default>a[active='1']", - (node) => node.innerText + (node) => node.textContent ) ).toEqual("Viewer"); }); @@ -118,7 +118,7 @@ describe("Viewer - Layers panel", () => { expect( await page.$eval( "#viewer--layers-panel #san-francisco-v1_7>input", - (node) => node.checked + (node) => (node as HTMLInputElement).checked ) ).toBeTruthy(); }); diff --git a/src/utils/testing-utils/utils.ts b/src/utils/testing-utils/utils.ts new file mode 100644 index 00000000..8605c92e --- /dev/null +++ b/src/utils/testing-utils/utils.ts @@ -0,0 +1,36 @@ +import { Page } from "puppeteer"; + +const URL_CHECKING_INTERVAL = 500; +const URL_CHECKING_TIMEOUT = 10000; +/** + * Clicks on an element and waits until indirect navigations complete. + * In case of multiple navigations performed by the application with unpredictable timings + * you might want to wait for the current URL to match some string before returning. + * @param page - Page instance. + * @param selector - Selector of the element to click on. + * @param waitFor - substring that is expected to be a part of the URL. + * @returns current URL. + */ +export const clickAndNavigate = async ( + page: Page, + selector: string, + waitFor?: string +) => { + await Promise.all([ + page.waitForNavigation({ + waitUntil: ["load", "domcontentloaded", "networkidle0"], + }), + page.click(selector), + ]); + let currentUrl = page.url(); + if (waitFor) { + for (let i = 0; i < URL_CHECKING_TIMEOUT / URL_CHECKING_INTERVAL; i++) { + await page.waitForTimeout(URL_CHECKING_INTERVAL); + currentUrl = page.url(); + if (currentUrl.indexOf(waitFor) !== -1) { + break; + } + } + } + return currentUrl; +};