From 936f4c76f9bd854d5b575b7df3982265657574f5 Mon Sep 17 00:00:00 2001 From: Todd Schiller Date: Sat, 14 Sep 2024 14:45:40 -0700 Subject: [PATCH] #9154: add E2E runtime test --- .../runtime/modVariables/variableSync.spec.ts | 95 +++++++++++++++++++ end-to-end-tests/utils.ts | 14 ++- 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 end-to-end-tests/tests/runtime/modVariables/variableSync.spec.ts diff --git a/end-to-end-tests/tests/runtime/modVariables/variableSync.spec.ts b/end-to-end-tests/tests/runtime/modVariables/variableSync.spec.ts new file mode 100644 index 0000000000..0a53708273 --- /dev/null +++ b/end-to-end-tests/tests/runtime/modVariables/variableSync.spec.ts @@ -0,0 +1,95 @@ +/* + * 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/testBase"; +import { ActivateModPage } from "../../../pageObjects/extensionConsole/modsPage"; +// @ts-expect-error -- https://youtrack.jetbrains.com/issue/AQUA-711/Provide-a-run-configuration-for-Playwright-tests-in-specs-with-fixture-imports-only +import { type Frame, type Page, test as base } from "@playwright/test"; +import { getSidebarPage } from "../../../utils"; + +test.describe("Mod Variable Sync", () => { + test("session variable cross-tab sync", async ({ page, extensionId }) => { + await test.step("activate mod", async () => { + const modId = "@e2e-testing/state-sync"; + const modActivationPage = new ActivateModPage(page, extensionId, modId); + await modActivationPage.goto(); + await modActivationPage.clickActivateAndWaitForModsPageRedirect(); + + await page.goto("/frames-builder.html"); + }); + + // Waiting for the mod to be ready before opening sidebar + await expect(page.getByText("Local: 0")).toBeVisible(); + + // The mod contains a trigger to open the sidebar on h1 + await page.click("h1"); + const sideBarPage = await getSidebarPage(page, extensionId); + await expect( + sideBarPage.getByRole("heading", { name: "State Sync Demo" }), + ).toBeVisible(); + + await test.step("verify same tab increment", async () => { + await sideBarPage.getByRole("button", { name: "Increment" }).click(); + + await expect(sideBarPage.getByText("Sync: 1")).toBeVisible(); + await expect(sideBarPage.getByText("Local: 1")).toBeVisible(); + + await expect(page.getByText("Sync: 1")).toBeVisible(); + await expect(page.getByText("Local: 1")).toBeVisible(); + + const frameLocator = page.frameLocator("iframe"); + await expect(frameLocator.getByText("Sync: 1")).toBeVisible(); + await expect(frameLocator.getByText("Local: 0")).toBeVisible(); + }); + + // Close the sidebar, because getSidebarPage currently can't distinguish between multiple sidebars + await sideBarPage.getByRole("button", { name: "Close" }).click(); + await sideBarPage.getByRole("button", { name: "Close" }).click(); + + const otherPage = await page.context().newPage(); + await otherPage.goto(page.url()); + + // Waiting for the mod to be ready before opening sidebar + await expect(otherPage.getByText("Local: 0")).toBeVisible(); + + await otherPage.click("h1"); + const otherSideBarPage = await getSidebarPage(otherPage, extensionId); + await expect( + otherSideBarPage.getByRole("heading", { name: "State Sync Demo" }), + ).toBeVisible(); + + await test.step("verify cross tab increment", async () => { + // Should be available on first run of the panel + await expect(otherSideBarPage.getByText("Sync: 1")).toBeVisible(); + await expect(otherSideBarPage.getByText("Local: ")).toBeVisible(); + + await otherSideBarPage.getByRole("button", { name: "Increment" }).click(); + + await expect(otherSideBarPage.getByText("Sync: 2")).toBeVisible(); + await expect(otherSideBarPage.getByText("Local: 1")).toBeVisible(); + + // Should automatically sync to the original tab + await expect(page.getByText("Sync: 2")).toBeVisible(); + await expect(page.getByText("Local: 1")).toBeVisible(); + + const frameLocator = page.frameLocator("iframe"); + await expect(frameLocator.getByText("Sync: 2")).toBeVisible(); + // Local variable doesn't exist in the frame + await expect(frameLocator.getByText("Local: 0")).toBeVisible(); + }); + }); +}); diff --git a/end-to-end-tests/utils.ts b/end-to-end-tests/utils.ts index 6a94c36739..5a0873f4c2 100644 --- a/end-to-end-tests/utils.ts +++ b/end-to-end-tests/utils.ts @@ -90,12 +90,18 @@ export async function runModViaQuickBar(page: Page, modName: string) { } function findSidebarPage(page: Page, extensionId: string): Page | undefined { - return page + const matches = page .context() .pages() - .find((value) => + .filter((value) => value.url().startsWith(`chrome-extension://${extensionId}/sidebar.html`), ); + + if (matches.length > 1) { + throw new Error("Multiple sidebar pages found"); + } + + return matches[0]; } /** @@ -111,10 +117,12 @@ export function isSidebarOpen(page: Page, extensionId: string): boolean { /** * Finds the Pixiebrix sidebar page/frame. * + * NOTE: returns the sidebar found, not necessarily the one for the provided page. + * * Automatically clicks "OK" on the dialog that appears if the sidebar requires a user gesture to open * This is a Page contained in the browser sidepanel window. * - * @throws {Error} if the sidebar is not available + * @throws {Error} if the sidebar is not available or multiple pages have the sidebar open */ export async function getSidebarPage( page: Page,