Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#8428: playwright activating a quick bar mod when you dont have a quick bar shortcut configured #8429

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions end-to-end-tests/pageObjects/extensionConsole/modsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ export class ActivateModPage {
return this.page.getByRole("button", { name: "Activate" });
}

configureQuickbarShortcutLink() {
return this.page.getByRole("link", { name: "configured your Quick Bar" });
}

keyboardShortcutDocumentationLink() {
return this.page.getByRole("link", {
name: "configuring keyboard shortcuts",
});
}

/** Successfully activating the mod will navigate to the "All Mods" page. */
async clickActivateAndWaitForModsPageRedirect() {
await this.activateButton().click();
Expand Down
146 changes: 146 additions & 0 deletions end-to-end-tests/pageObjects/extensionsShortcutsPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

import { expect, type Page } from "@playwright/test";
import { getModifierKey, getModifierSymbol } from "end-to-end-tests/utils";

function getExtensionShortcutsUrl(chromiumChannel: "chrome" | "msedge") {
switch (chromiumChannel) {
case "chrome": {
return "chrome://extensions/shortcuts";
}

case "msedge": {
return "edge://extensions/shortcuts";
}

default: {
const exhaustiveCheck: never = chromiumChannel;
throw new Error(`Unexpected channel: ${exhaustiveCheck}`);
}
}
}

async function getShortcut(page: Page): Promise<string> {
const modifierKey = await getModifierKey(page);
const modifierSymbol = await getModifierSymbol(page);

return modifierKey === "Meta" ? `${modifierSymbol}M` : "Ctrl + M";
}

export class ExtensionsShortcutsPage {
private readonly pageUrl: string;

constructor(
private readonly page: Page,
private readonly chromiumChannel: "chrome" | "msedge",
) {
this.pageUrl = getExtensionShortcutsUrl(this.chromiumChannel);
}

getPageUrl() {
return this.pageUrl;
}

async goto() {
await this.page.goto(this.pageUrl);

if (this.chromiumChannel === "chrome") {
await expect(
this.page.getByRole("heading", { name: /PixieBrix/ }),
).toBeVisible();
} else {
await expect(this.page.getByText(/PixieBrix/)).toBeVisible();
}
}

async clearQuickbarShortcut() {
await this.page.bringToFront();

const shortcut = await getShortcut(this.page);

if (this.chromiumChannel === "chrome") {
await expect(this.page.getByPlaceholder(/shortcut set: /i)).toHaveValue(
shortcut,
);

// Clear the shortcut
await this.page.getByLabel("Edit shortcut Toggle Quick").click();
grahamlangford marked this conversation as resolved.
Show resolved Hide resolved
await this.page
.locator("extensions-keyboard-shortcuts #container")
.click();

await expect(
this.page.getByLabel(/Shortcut Toggle Quick Bar for PixieBrix/, {
exact: true,
}),
).toBeEmpty();
} else {
await expect(
this.page.getByLabel(
/Type a shortcut that will Toggle Quick Bar for PixieBrix/,
),
).toHaveValue(shortcut);

await this.page.getByRole("button", { name: "Clear shortcut" }).click();

await expect(
this.page.getByLabel(
/Type a shortcut that will Toggle Quick Bar for PixieBrix/,
),
).toBeEmpty();
}
}

async setQuickbarShortcut() {
await this.page.bringToFront();

const modifierKey = await getModifierKey(this.page);
const shortcut = await getShortcut(this.page);

if (this.chromiumChannel === "chrome") {
await expect(
this.page.getByLabel(/Shortcut Toggle Quick Bar for PixieBrix/),
).toBeEmpty();

await this.page.getByLabel("Edit shortcut Toggle Quick").click();
await this.page
.getByPlaceholder("Type a shortcut")
.press(`${modifierKey}+m`);

await this.page
.locator("extensions-keyboard-shortcuts #container")
.click();

await expect(this.page.getByPlaceholder(/shortcut set: /i)).toHaveValue(
shortcut,
);
} else {
const shortcutLabel = /type a shortcut that will toggle quick bar/i;
const input = this.page.getByLabel(shortcutLabel);

await expect(input).toBeEmpty();

await input.click();
await input.press(`${modifierKey}+m`);

await this.page.getByText("Keyboard ShortcutsPixieBrix").click();

await expect(input).toHaveValue(shortcut);
}
}
}
73 changes: 72 additions & 1 deletion end-to-end-tests/tests/extensionConsoleActivation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ import { test, expect } from "../fixtures/extensionBase";
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 Page, test as base, type Frame } from "@playwright/test";
import { getSidebarPage, runModViaQuickBar } from "../utils";
import {
getSidebarPage,
clickAndWaitForNewPage,
runModViaQuickBar,
getBrowserOs,
} from "../utils";
import path from "node:path";
import { VALID_UUID_REGEX } from "@/types/stringTypes";
import { type Serializable } from "playwright-core/types/structs";
import { MV } from "../env";
import { ExtensionsShortcutsPage } from "end-to-end-tests/pageObjects/extensionsShortcutsPage";

test("can activate a mod with no config options", async ({
page,
Expand Down Expand Up @@ -164,3 +170,68 @@ test("can activate a mod with a database", async ({ page, extensionId }) => {

await expect(sideBarPage.getByTestId("card").getByText(note)).toBeHidden();
});

test("activating a mod when the quickbar shortcut is not configured", async ({
context,
page: firstTab,
extensionId,
chromiumChannel,
}) => {
const shortcutsPage = new ExtensionsShortcutsPage(firstTab, chromiumChannel);
await shortcutsPage.goto();

await test.step("Clear the quickbar shortcut before activing a quickbar mod", async () => {
const os = await getBrowserOs(firstTab);
// See https://github.com/pixiebrix/pixiebrix-extension/issues/6268
// eslint-disable-next-line playwright/no-conditional-in-test -- Existing bug where shortcut isn't set on Edge in Windows/Linux
if (os === "MacOS" || chromiumChannel === "chrome") {
await shortcutsPage.clearQuickbarShortcut();
}
});

let modActivationPage: ActivateModPage;
const secondTab = await context.newPage();
await test.step("Begin activation of a mod with a quickbar shortcut", async () => {
const modId = "@e2e-testing/show-alert";
modActivationPage = new ActivateModPage(secondTab, extensionId, modId);
await modActivationPage.goto();
});

await test.step("Verify the mod activation page has links for setting the shortcut", async () => {
await expect(
modActivationPage.keyboardShortcutDocumentationLink(),
).toBeVisible();
await modActivationPage.keyboardShortcutDocumentationLink().click();

await expect(
secondTab.getByRole("heading", { name: "Changing the Quick Bar" }),
).toBeVisible();
await secondTab.goBack();

await expect(
modActivationPage.configureQuickbarShortcutLink(),
).toBeVisible();

const configureShortcutPage = await clickAndWaitForNewPage(
modActivationPage.configureQuickbarShortcutLink(),
context,
);

await expect(configureShortcutPage).toHaveURL(shortcutsPage.getPageUrl());
await configureShortcutPage.close();
});

await test.step("Restore the shortcut and activate the mod", async () => {
await shortcutsPage.setQuickbarShortcut();

await modActivationPage.clickActivateAndWaitForModsPageRedirect();
});

await test.step("Verify the mod is activated and works as expected", async () => {
await firstTab.bringToFront();
await firstTab.goto("/");

await runModViaQuickBar(firstTab, "Show Alert");
await expect(firstTab.getByText("Quick Bar Action ran")).toBeVisible();
});
});
grahamlangford marked this conversation as resolved.
Show resolved Hide resolved
67 changes: 64 additions & 3 deletions end-to-end-tests/utils.ts
Copy link
Collaborator

@mnholtz mnholtz May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opportunity to refactor runModViaQuickbar in this file, which currently works by trying both modifier keys

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
*/

import type AxeBuilder from "@axe-core/playwright";
import { type Locator, expect, type Page, type Frame } from "@playwright/test";
import {
type Locator,
expect,
type Page,
type Frame,
type BrowserContext,
} from "@playwright/test";
import { MV } from "./env";

type AxeResults = Awaited<ReturnType<typeof AxeBuilder.prototype.analyze>>;
Expand Down Expand Up @@ -71,8 +77,10 @@ export async function ensureVisibility(
export async function runModViaQuickBar(page: Page, modName: string) {
await waitForQuickBarReadiness(page);
await page.locator("html").focus(); // Ensure the page is focused before running the keyboard shortcut
await page.keyboard.press("Meta+M"); // MacOS
await page.keyboard.press("Control+M"); // Windows and Linux

const modifierKey = await getModifierKey(page);
await page.keyboard.press(`${modifierKey}+M`);

// Short delay to allow the quickbar to finish opening
// eslint-disable-next-line playwright/no-wait-for-timeout -- TODO: Find a better way to detect when the quickbar is done loading opening
await page.waitForTimeout(500);
Expand Down Expand Up @@ -182,3 +190,56 @@ export async function conditionallyHoverOverMV2Sidebar(page: Page) {
await sidebarFrame.dispatchEvent("mouseenter");
}
}

/**
* Returns a reference to the new page that was opened.
* @param locator The anchor or button that opens the new page (must be clickable)
* @param context The browser context
*/
export async function clickAndWaitForNewPage(
locator: Locator,
context: BrowserContext,
): Promise<Page> {
const pagePromise = context.waitForEvent("page");

await locator.click();

return pagePromise;
}

// 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<string> {
let OSName = "";

const response = String(await page.evaluate(() => navigator.userAgent));

if (response.includes("Win")) {
OSName = "Windows";
}

if (response.includes("Mac")) {
OSName = "MacOS";
}

if (response.includes("X11")) {
OSName = "Unix";
}

if (response.includes("Linux")) {
OSName = "Linux";
}

return OSName;
}

export async function getModifierKey(page: Page): Promise<string> {
const OSName = await getBrowserOs(page);
return OSName === "MacOS" ? "Meta" : "Control";
}

export async function getModifierSymbol(page: Page): Promise<string> {
const OSName = await getBrowserOs(page);
return OSName === "MacOS" ? "⌘" : "⌃";
}
Loading