From 8a67ea82c3196218129db43be5724c87c51fa7c6 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Wed, 15 May 2024 12:21:34 -0700 Subject: [PATCH] #8206: playwright tests for MS Edge sidebar links (#8216) * #8206: playwright tests for links * Open the extension console * Fix jest snapshots * Add commentary on MS Edge behavior and try to fix * #8206: add more comments on playwright issues * Work around msedge bugs * fix snapshots * wip * fix snapshots * always reopen sidebar * wip * fix e2e flakiness with deleting mods cleanup * lint fix * skip iframe link test in linux * fix os check * fix os check * fix osname check --------- Co-authored-by: Misha Holtz <36575242+mnholtz@users.noreply.github.com> Co-authored-by: Graham Langford <30706330+grahamlangford@users.noreply.github.com> Co-authored-by: Eduardo --- .../tests/regressions/sidebarLinks.spec.ts | 190 ++++++++++++++++++ end-to-end-tests/utils.ts | 6 +- src/sidebar/Header.tsx | 1 + .../__snapshots__/Header.test.tsx.snap | 2 + .../__snapshots__/SidebarBody.test.tsx.snap | 3 + 5 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 end-to-end-tests/tests/regressions/sidebarLinks.spec.ts diff --git a/end-to-end-tests/tests/regressions/sidebarLinks.spec.ts b/end-to-end-tests/tests/regressions/sidebarLinks.spec.ts new file mode 100644 index 0000000000..3b505393df --- /dev/null +++ b/end-to-end-tests/tests/regressions/sidebarLinks.spec.ts @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2024 PixieBrix, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { test, expect } from "../../fixtures/extensionBase"; +import { ActivateModPage } from "../../pageObjects/extensionConsole/modsPage"; +import { + type BrowserContext, + type Locator, + type Page, + // @ts-expect-error -- https://youtrack.jetbrains.com/issue/AQUA-711/Provide-a-run-configuration-for-Playwright-tests-in-specs-with-fixture-imports-only + test as base, +} from "@playwright/test"; +import { ensureVisibility, getBrowserOs, getSidebarPage } from "../../utils"; +import { getBaseExtensionConsoleUrl } from "../../pageObjects/constants"; +import { MV } from "../../env"; + +async function openSidebar(page: Page, extensionId: string) { + // The mod contains a trigger to open the sidebar on h1. If the sidePanel is already open, it's a NOP + await page.click("h1"); + + const sideBarPage = await getSidebarPage(page, extensionId); + + await expect( + sideBarPage.getByRole("heading", { name: "Sidebar Links" }), + ).toBeVisible(); + + return sideBarPage; +} + +async function reopenSidebar(page: Page, extensionId: string) { + await page.bringToFront(); + // eslint-disable-next-line playwright/no-wait-for-timeout -- if we try to reopen to quickly, the sidebar does not respond in time since it was just closed + await page.waitForTimeout(500); + return openSidebar(page, extensionId); +} + +async function clickLinkInSidebarAndWaitForPage( + context: BrowserContext, + locator: Locator, + chromiumChannel: string, +) { + const pagePromise = context.waitForEvent("page"); + if (chromiumChannel === "msedge") { + // On MS Edge, opening a new tab closes the sidebar. The click steps fail because MS Edge closes the sidebar when the new tab is opened + // Error: locator.click: Target page, context or browser has been closed. + // Even though it errors, the link is still opened in a new tab. + // See https://github.com/w3c/webextensions/issues/588. + await expect(async () => locator.click()).not.toPass({ + timeout: 3000, + }); + } else { + await locator.click(); + } + + return pagePromise; +} + +test("#8206: clicking links from the sidebar doesn't crash browser", async ({ + page, + context, + extensionId, + chromiumChannel, + baseURL, +}) => { + test.skip(MV === "2", "MV3 specific test"); + + const browserOSName = await getBrowserOs(page); + const modId = "@pixies/test/sidebar-links"; + const modActivationPage = new ActivateModPage(page, extensionId, modId); + await modActivationPage.goto(); + await modActivationPage.clickActivateAndWaitForModsPageRedirect(); + + await page.goto("/"); + // On MS Edge, and in Linux (both chrome and Edge) opening a new tab closes the sidebar, + // so we have to re-open it on the page after clicking each link + // See https://github.com/w3c/webextensions/issues/588. + let sideBarPage = await openSidebar(page, extensionId); + + await test.step("Clicking extension console link", async () => { + const extensionConsolePage = await clickLinkInSidebarAndWaitForPage( + context, + sideBarPage.getByRole("link", { name: "Open Extension Console" }), + chromiumChannel, + ); + + expect(extensionConsolePage.url()).toContain( + getBaseExtensionConsoleUrl(extensionId), + ); + + // eslint-disable-next-line playwright/no-conditional-in-test -- msedge bug + if (chromiumChannel === "msedge") { + // Another msedge bug causes the browser to fail to open the extension console page from the sidebar until you refresh the page. + // "Error: This script should only be loaded in a browser extension." + await extensionConsolePage.reload(); + } + + const activeModsHeading = extensionConsolePage.getByRole("heading", { + name: "Active Mods", + }); + // `activeModsHeading` may be initially be detached and hidden, so toBeVisible() would immediately fail + await ensureVisibility(activeModsHeading, { timeout: 10_000 }); + }); + + await test.step("Clicking markdown text link", async () => { + // eslint-disable-next-line playwright/no-conditional-in-test -- msedge and linux bug that causes the sidebar to close on clicking a link + if (browserOSName === "Linux" || chromiumChannel === "msedge") { + sideBarPage = await reopenSidebar(page, extensionId); + } + + const markdownTextLinkPage = await clickLinkInSidebarAndWaitForPage( + context, + sideBarPage.getByRole("link", { name: "Markdown Text Link" }), + chromiumChannel, + ); + expect(markdownTextLinkPage.url()).toBe(`${baseURL}/bootstrap-5/`); + }); + + await test.step("Clicking react bootstrap link", async () => { + // eslint-disable-next-line playwright/no-conditional-in-test -- msedge/linux bug + if (browserOSName === "Linux" || chromiumChannel === "msedge") { + sideBarPage = await reopenSidebar(page, extensionId); + } + + const reactBootstrapLinkPage = await clickLinkInSidebarAndWaitForPage( + context, + sideBarPage.getByRole("button", { name: "Open a Tab Link" }), + chromiumChannel, + ); + expect(reactBootstrapLinkPage.url()).toBe(`${baseURL}/bootstrap-5/#gamma`); + }); + + await test.step("Clicking html renderer link", async () => { + // eslint-disable-next-line playwright/no-conditional-in-test -- msedge/linux bug + if (browserOSName === "Linux" || chromiumChannel === "msedge") { + sideBarPage = await reopenSidebar(page, extensionId); + } + + const htmlRendererLinkPage = await clickLinkInSidebarAndWaitForPage( + context, + sideBarPage.getByRole("link", { name: "HTML Renderer Link" }), + chromiumChannel, + ); + expect(htmlRendererLinkPage.url()).toBe(`${baseURL}/bootstrap-5/`); + }); + + await test.step("Clicking embedded form link", async () => { + // eslint-disable-next-line playwright/no-conditional-in-test -- msedge/linux bug + if (browserOSName === "Linux" || chromiumChannel === "msedge") { + sideBarPage = await reopenSidebar(page, extensionId); + } + + const embeddedFormLinkPage = await clickLinkInSidebarAndWaitForPage( + context, + sideBarPage.getByRole("link", { name: "Embedded Form Link" }), + chromiumChannel, + ); + expect(embeddedFormLinkPage.url()).toBe(`${baseURL}/bootstrap-5/#beta`); + }); + + // Clicking link in IFrame will crash MS Edge until the issue is fixed + // https://github.com/microsoft/MicrosoftEdge-Extensions/issues/145 + // For some reason this also happens in Chrome/Linux in the CI github workflow. + // eslint-disable-next-line playwright/no-conditional-in-test -- see above comment + if (browserOSName !== "Linux" && chromiumChannel !== "msedge") { + await test.step("Clicking link in IFrame", async () => { + // PixieBrix uses 2 layers of frames to get around the host page CSP. Test page has 2 layers + const pixiebrixFrame = sideBarPage.frameLocator("iframe").first(); + const mainFrame = pixiebrixFrame.frameLocator("iframe").first(); + // eslint-disable-next-line playwright/no-conditional-expect -- see above + await expect(mainFrame.getByText("Alpha")).toBeVisible(); + + const srcdocFrame = mainFrame.frameLocator("iframe").first(); + await srcdocFrame.getByRole("link", { name: "IFrame Link" }).click(); + }); + } +}); diff --git a/end-to-end-tests/utils.ts b/end-to-end-tests/utils.ts index 10942cbb91..b88cc0e084 100644 --- a/end-to-end-tests/utils.ts +++ b/end-to-end-tests/utils.ts @@ -207,11 +207,13 @@ export async function clickAndWaitForNewPage( return pagePromise; } +type OSName = "Windows" | "MacOS" | "Unix" | "Linux" | "Unknown"; + // Temporary workaround for determining which modifiers to use for keyboard shortcuts // A permanent fix has been merged but not released // See: https://github.com/microsoft/playwright/pull/30572 -export async function getBrowserOs(page: Page): Promise { - let OSName = ""; +export async function getBrowserOs(page: Page): Promise { + let OSName: OSName = "Unknown"; const response = String(await page.evaluate(() => navigator.userAgent)); diff --git a/src/sidebar/Header.tsx b/src/sidebar/Header.tsx index e069446183..4fe4208cdd 100644 --- a/src/sidebar/Header.tsx +++ b/src/sidebar/Header.tsx @@ -103,6 +103,7 @@ const Header: React.FunctionComponent = () => { size="sm" variant="link" className={headerButtonClassName} + title="Open Extension Console" > diff --git a/src/sidebar/__snapshots__/Header.test.tsx.snap b/src/sidebar/__snapshots__/Header.test.tsx.snap index c082015691..a8569a18b7 100644 --- a/src/sidebar/__snapshots__/Header.test.tsx.snap +++ b/src/sidebar/__snapshots__/Header.test.tsx.snap @@ -38,6 +38,7 @@ exports[`Header renders 1`] = `