From 45b1a8acb6045e739c664859098f55df50b2ec47 Mon Sep 17 00:00:00 2001 From: Eduardo Fungairino Date: Mon, 1 Jul 2024 14:09:18 -0400 Subject: [PATCH] #8581 refactoring POMs to use the BasePageObject (#8727) * refactoring POMs to use the BasePageObject * fix root locator * fix lint error * update comment --- .../pageObjects/basePageObject.ts | 9 +- .../extensionConsole/localIntegrationsPage.ts | 35 +++---- .../pageObjects/extensionConsole/modsPage.ts | 97 ++++++++----------- .../workshop/createWorkshopModPage.ts | 17 ++-- .../workshop/editWorkshopModPage.ts | 15 ++- .../extensionConsole/workshop/modEditor.ts | 26 ++--- .../extensionConsole/workshop/workshopPage.ts | 39 ++++---- .../pageObjects/pageEditorPage.ts | 88 +++++++---------- .../tests/extensionConsole/activation.spec.ts | 14 +-- end-to-end-tests/tests/modLifecycle.spec.ts | 6 +- .../tests/pageEditor/saveMod.spec.ts | 2 +- .../tests/runtime/localIntegrations.spec.ts | 22 +++-- end-to-end-tests/tests/smoke/modsPage.spec.ts | 5 +- .../tests/smoke/pageEditor.spec.ts | 2 +- end-to-end-tests/tests/smoke/sidebar.spec.ts | 2 +- .../tests/workshop/createMod.spec.ts | 2 +- 16 files changed, 166 insertions(+), 215 deletions(-) diff --git a/end-to-end-tests/pageObjects/basePageObject.ts b/end-to-end-tests/pageObjects/basePageObject.ts index 386d6cc7f8..3c11f61e83 100644 --- a/end-to-end-tests/pageObjects/basePageObject.ts +++ b/end-to-end-tests/pageObjects/basePageObject.ts @@ -24,8 +24,9 @@ import { type Locator, type Page } from "playwright"; * * @example * class LoginPage extends BasePageObject { - * const usernameInput = this.getByPlaceholder('Username'); - * const passwordInput = this.getByPlaceholder('Password'); + * // define common locators at the top. + * usernameInput = this.getByPlaceholder('Username'); + * passwordInput = this.getByPlaceholder('Password'); * * async login(username, password) { * await usernameInput.fill(username); @@ -56,7 +57,7 @@ import { type Locator, type Page } from "playwright"; * const username = await dashboardPage.userProfile.getUsername(); * * @param {Locator|Page} rootLocatorOrPage The root locator scoping this page object. - * If a Page is provided, the root locator will be the body. + * If a Page is provided, the root locator will be `locator("html")`. */ export class BasePageObject { readonly root: Locator; @@ -76,7 +77,7 @@ export class BasePageObject { this.root = rootLocatorOrPage; this.page = rootLocatorOrPage.page(); } else { - this.root = rootLocatorOrPage.locator("body"); + this.root = rootLocatorOrPage.locator("html"); this.page = rootLocatorOrPage; } diff --git a/end-to-end-tests/pageObjects/extensionConsole/localIntegrationsPage.ts b/end-to-end-tests/pageObjects/extensionConsole/localIntegrationsPage.ts index 7453de63f7..1b9b0d69a6 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/localIntegrationsPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/localIntegrationsPage.ts @@ -17,14 +17,13 @@ import { expect, type Page } from "@playwright/test"; import { getBaseExtensionConsoleUrl } from "../constants"; +import { BasePageObject } from "../basePageObject"; -export class LocalIntegrationsPage { +export class LocalIntegrationsPage extends BasePageObject { private readonly extensionConsoleUrl?: string; - constructor( - private readonly page: Page, - extensionId?: string, - ) { + constructor(page: Page, extensionId?: string) { + super(page); this.extensionConsoleUrl = extensionId && getBaseExtensionConsoleUrl(extensionId); } @@ -34,30 +33,26 @@ export class LocalIntegrationsPage { await this.page.goto(this.extensionConsoleUrl); } - await this.page - .getByRole("link", { - name: "Local Integrations", - }) - .click(); + await this.getByRole("link", { + name: "Local Integrations", + }).click(); await expect( - this.page.getByRole("heading", { name: "Local Integrations" }), + this.getByRole("heading", { name: "Local Integrations" }), ).toBeVisible(); - await expect(this.page.getByTestId("loader")).toBeHidden(); + await expect(this.getByTestId("loader")).toBeHidden(); } async createNewIntegration(integrationName: string) { - await this.page - .getByRole("button", { name: "Add Local Integration" }) - .click(); + await this.getByRole("button", { name: "Add Local Integration" }).click(); - await this.page - .getByPlaceholder("Start typing to find results") - .fill(integrationName); + await this.getByPlaceholder("Start typing to find results").fill( + integrationName, + ); - await this.page.getByText(integrationName).first().click(); + await this.getByText(integrationName).first().click(); - await this.page.getByTestId(`${integrationName} detail button`).click(); + await this.getByTestId(`${integrationName} detail button`).click(); } } diff --git a/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts b/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts index 2a01269bf1..20c4ea48a0 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts @@ -18,14 +18,16 @@ import { expect, type Page } from "@playwright/test"; import { getBaseExtensionConsoleUrl } from "../constants"; import { ensureVisibility } from "../../utils"; +import { BasePageObject } from "../basePageObject"; -export class ModsPage { +export class ModsPage extends BasePageObject { private readonly extensionConsoleUrl: string; - constructor( - private readonly page: Page, - extensionId: string, - ) { + modTableItems = this.getByRole("table").locator(".list-group-item"); + searchModsInput = this.getByTestId("blueprints-search-input"); + + constructor(page: Page, extensionId: string) { + super(page); this.extensionConsoleUrl = getBaseExtensionConsoleUrl(extensionId); } @@ -40,39 +42,30 @@ export class ModsPage { request.url().includes("/api/registry/bricks/"), ); await this.page.goto(this.extensionConsoleUrl); - await expect(this.page.getByText("Extension Console")).toBeVisible(); + await expect(this.getByText("Extension Console")).toBeVisible(); await registryPromise; // Check that the page is stable, and that the content has finished loading - const activeModsHeading = this.page.getByRole("heading", { + const activeModsHeading = this.getByRole("heading", { name: "Active Mods", }); await ensureVisibility(activeModsHeading, { timeout: 10_000 }); - const modTableItems = this.modTableItems(); - const contentLoadedLocator = this.page - .getByText("Welcome to PixieBrix!") - .or(modTableItems.nth(0)); + const contentLoadedLocator = this.getByText("Welcome to PixieBrix!").or( + this.modTableItems.nth(0), + ); await expect(contentLoadedLocator).toBeVisible(); } async viewAllMods() { - await this.page.getByTestId("all-mods-mod-tab").click(); + await this.getByTestId("all-mods-mod-tab").click(); } async viewActiveMods() { - await this.page.getByTestId("active-mod-tab").click(); - } - - modTableItems() { - return this.page.getByRole("table").locator(".list-group-item"); + await this.getByTestId("active-mod-tab").click(); } modTableItemById(modId: string) { - return this.modTableItems().filter({ hasText: modId }); - } - - searchModsInput() { - return this.page.getByTestId("blueprints-search-input"); + return this.modTableItems.filter({ hasText: modId }); } /** @@ -83,10 +76,10 @@ export class ModsPage { */ async actionForModByName(modName: string, actionName: string): Promise { await this.page.bringToFront(); - await this.searchModsInput().fill(modName); - await expect(this.page.getByText(`results for "${modName}`)).toBeVisible(); + await this.searchModsInput.fill(modName); + await expect(this.getByText(`results for "${modName}`)).toBeVisible(); - const modSearchResult = this.page.locator(".list-group-item", { + const modSearchResult = this.locator(".list-group-item", { hasText: modName, }); await expect(modSearchResult).toBeVisible(); @@ -95,7 +88,7 @@ export class ModsPage { await modSearchResult.locator(".dropdown").click(); // Click the delete button in the delete confirmation modal - await this.page.getByRole("button", { name: actionName }).click(); + await this.getByRole("button", { name: actionName }).click(); } /** @@ -106,9 +99,9 @@ export class ModsPage { */ async deleteModByName(modName: string) { await this.page.bringToFront(); - await this.searchModsInput().fill(modName); - await expect(this.page.getByText(`results for "${modName}`)).toBeVisible(); - const modToDelete = this.page.locator(".list-group-item", { + await this.searchModsInput.fill(modName); + await expect(this.getByText(`results for "${modName}`)).toBeVisible(); + const modToDelete = this.locator(".list-group-item", { hasText: modName, }); await expect(modToDelete).toBeVisible(); @@ -124,9 +117,7 @@ export class ModsPage { await deactivateOption.click({ timeout: 3000, }); - await expect( - this.page.getByText(`Deactivated mod: ${modName}`), - ).toBeVisible(); + await expect(this.getByText(`Deactivated mod: ${modName}`)).toBeVisible(); // Re-open the dropdown action menu to stay in the same state await modToDelete.locator(".dropdown").click(); } @@ -137,23 +128,33 @@ export class ModsPage { }); // Click the delete button in the delete confirmation modal - await this.page.getByRole("button", { name: "Delete" }).click(); + await this.getByRole("button", { name: "Delete" }).click(); await expect( // Exact text varies by standalone mod vs. mod package - this.page.getByText(`Deleted mod ${modName}`), + this.getByText(`Deleted mod ${modName}`), ).toBeVisible(); } } -export class ActivateModPage { +export class ActivateModPage extends BasePageObject { private readonly baseConsoleUrl: string; private readonly activateModUrl: string; + activateButton = this.getByRole("button", { name: "Activate" }); + keyboardShortcutDocumentationLink = this.getByRole("link", { + name: "configuring keyboard shortcuts", + }); + + configureQuickbarShortcutLink = this.getByRole("link", { + name: "configured your Quick Bar", + }); + constructor( - private readonly page: Page, + page: Page, private readonly extensionId: string, private readonly modId: string, ) { + super(page); this.baseConsoleUrl = getBaseExtensionConsoleUrl(extensionId); this.activateModUrl = `${ this.baseConsoleUrl @@ -163,35 +164,21 @@ export class ActivateModPage { async goto() { await this.page.goto(this.activateModUrl); - await expect(this.page.getByText("Activate Mod")).toBeVisible(); + await expect(this.getByText("Activate Mod")).toBeVisible(); // Loading the mod details may take more than 5 seconds - await expect(this.page.getByText(this.modId)).toBeVisible({ + await expect(this.getByText(this.modId)).toBeVisible({ timeout: 10_000, }); } - activateButton() { - 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(); + await this.activateButton.click(); await this.page.waitForURL(`${this.baseConsoleUrl}#/mods`); const modsPage = new ModsPage(this.page, this.extensionId); await modsPage.viewActiveMods(); - // Loading mods sometimes takes upwards of 5s - await expect(modsPage.modTableItems().getByText(this.modId)).toBeVisible({ + // Loading mods sometimes takes upwards of 10s + await expect(modsPage.modTableItems.getByText(this.modId)).toBeVisible({ timeout: 10_000, }); return modsPage; diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/createWorkshopModPage.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/createWorkshopModPage.ts index 2f1bde174d..c7e102660b 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/createWorkshopModPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/createWorkshopModPage.ts @@ -15,17 +15,12 @@ * along with this program. If not, see . */ -import { type Locator, type Page } from "@playwright/test"; import { WorkshopModEditor } from "./modEditor"; +import { BasePageObject } from "../../basePageObject"; -export class CreateWorkshopModPage { - readonly editor: WorkshopModEditor; - readonly createBrickButton: Locator; - - constructor(private readonly page: Page) { - this.editor = new WorkshopModEditor(this.page); - this.createBrickButton = this.page.getByRole("button", { - name: "Create Brick", - }); - } +export class CreateWorkshopModPage extends BasePageObject { + editor = new WorkshopModEditor(this.getByLabel("Editor")); + createBrickButton = this.getByRole("button", { + name: "Create Brick", + }); } diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts index 8a902e328b..6d4dd4a9f6 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts @@ -15,22 +15,19 @@ * along with this program. If not, see . */ -import { type Page } from "@playwright/test"; import { WorkshopModEditor } from "./modEditor"; +import { BasePageObject } from "end-to-end-tests/pageObjects/basePageObject"; -export class EditWorkshopModPage { - readonly editor: WorkshopModEditor; - constructor(private readonly page: Page) { - this.editor = new WorkshopModEditor(this.page); - } +export class EditWorkshopModPage extends BasePageObject { + editor = new WorkshopModEditor(this.getByLabel("Editor")); async updateBrick() { - await this.page.getByRole("button", { name: "Update Brick" }).click(); + await this.getByRole("button", { name: "Update Brick" }).click(); } async deleteBrick() { - await this.page.getByRole("button", { name: "Delete Brick" }).click(); - await this.page.getByRole("button", { name: "Permanently Delete" }).click(); + await this.getByRole("button", { name: "Delete Brick" }).click(); + await this.getByRole("button", { name: "Permanently Delete" }).click(); // eslint-disable-next-line playwright/no-networkidle -- for some reason, can't assert on the "Brick deleted" notice await this.page.waitForLoadState("networkidle"); } diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts index dda8a05524..d30a1739f3 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts @@ -15,25 +15,19 @@ * along with this program. If not, see . */ -import { type Locator, type Page, expect } from "@playwright/test"; +import { expect } from "@playwright/test"; import fs from "node:fs/promises"; import path from "node:path"; import { uuidv4 } from "@/types/helpers"; +import { BasePageObject } from "../../basePageObject"; -export class WorkshopModEditor { - readonly textArea: Locator; - readonly content: Locator; - readonly baseLocator: Locator; - - constructor(private readonly page: Page) { - this.baseLocator = this.page.getByLabel("Editor"); - this.content = this.baseLocator.locator(".ace_content"); - this.textArea = this.baseLocator.getByRole("textbox"); - } +export class WorkshopModEditor extends BasePageObject { + content = this.locator(".ace_content"); + textArea = this.getByRole("textbox"); async waitForLoad() { await expect(this.textArea).toBeVisible(); - await expect(this.baseLocator.getByText("Loading editor...")).toBeHidden(); + await expect(this.getByText("Loading editor...")).toBeHidden(); } async getValue() { @@ -50,14 +44,14 @@ export class WorkshopModEditor { async findText(text: string) { await this.content.click(); // Focus on the visible editor await this.page.keyboard.press("ControlOrMeta+f"); - await this.page.getByPlaceholder("Search for").fill(text); + await this.getByPlaceholder("Search for").fill(text); } async findAndReplaceText(findText: string, replaceText: string) { await this.findText(findText); - await this.page.getByText("+", { exact: true }).click(); - await this.page.getByPlaceholder("Replace with").fill(replaceText); - await this.page.getByText("Replace").click(); + await this.getByText("+", { exact: true }).click(); + await this.getByPlaceholder("Replace with").fill(replaceText); + await this.getByText("Replace").click(); } // Loads the corresponding yaml from the fixtures/modDefinitions directory diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts index 04ecb7d0b2..1c3baf9951 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts @@ -15,39 +15,34 @@ * along with this program. If not, see . */ -import { type Locator, type Page, expect } from "@playwright/test"; +import { type Page, expect } from "@playwright/test"; import { getBaseExtensionConsoleUrl } from "../../constants"; import { EditWorkshopModPage } from "end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage"; import { CreateWorkshopModPage } from "./createWorkshopModPage"; +import { BasePageObject } from "../../basePageObject"; -export class WorkshopPage { +export class WorkshopPage extends BasePageObject { private readonly extensionConsoleUrl: string; - private readonly createNewBrickButton: Locator; - constructor( - private readonly page: Page, - extensionId: string, - ) { + createNewBrickButton = this.getByRole("button", { + name: "Create New Brick", + }); + + constructor(page: Page, extensionId: string) { + super(page); this.extensionConsoleUrl = getBaseExtensionConsoleUrl(extensionId); - this.createNewBrickButton = this.page.getByRole("button", { - name: "Create New Brick", - }); } async goto() { await this.page.goto(this.extensionConsoleUrl); - await this.page - .getByRole("link", { - name: "Workshop", - }) - .click(); + await this.getByRole("link", { + name: "Workshop", + }).click(); } async findAndSelectMod(modId: string) { - await this.page - .getByPlaceholder("Start typing to find results") - .fill(modId); - await this.page.getByRole("cell", { name: modId }).click(); + await this.getByPlaceholder("Start typing to find results").fill(modId); + await this.getByRole("cell", { name: modId }).click(); const editPage = new EditWorkshopModPage(this.page); await editPage.editor.waitForLoad(); @@ -61,9 +56,9 @@ export class WorkshopPage { const modId = await createPage.editor.replaceWithModDefinition(modDefinitionName); await createPage.createBrickButton.click(); - await expect( - this.page.getByRole("status").getByText("Created "), - ).toBeVisible({ timeout: 8000 }); + await expect(this.getByRole("status").getByText("Created ")).toBeVisible({ + timeout: 8000, + }); return modId; } diff --git a/end-to-end-tests/pageObjects/pageEditorPage.ts b/end-to-end-tests/pageObjects/pageEditorPage.ts index 8dd9117709..4639893646 100644 --- a/end-to-end-tests/pageObjects/pageEditorPage.ts +++ b/end-to-end-tests/pageObjects/pageEditorPage.ts @@ -21,6 +21,7 @@ import { uuidv4 } from "@/types/helpers"; import { ModsPage } from "./extensionConsole/modsPage"; import { WorkshopPage } from "end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage"; import { type UUID } from "@/types/stringTypes"; +import { BasePageObject } from "./basePageObject"; // Starter brick names as shown in the Page Editor UI export type StarterBrickName = @@ -38,16 +39,21 @@ export type StarterBrickName = * @knip usage of PageEditorPage indirectly via the newPageEditorPage fixture in testBase.ts causes a * false-positive */ -export class PageEditorPage { +export class PageEditorPage extends BasePageObject { private readonly pageEditorUrl: string; private readonly savedStandaloneModNames: string[] = []; private readonly savedPackageModIds: string[] = []; + templateGalleryButton = this.getByRole("button", { + name: "Launch Template Gallery", + }); + constructor( - private readonly page: Page, + page: Page, private readonly urlToConnectTo: string, private readonly extensionId: string, ) { + super(page); this.pageEditorUrl = getBasePageEditorUrl(extensionId); } @@ -55,8 +61,8 @@ export class PageEditorPage { await this.page.goto(this.pageEditorUrl); // Set the viewport size to the expected in horizontal layout size of the devconsole when docked on the bottom. await this.page.setViewportSize({ width: 1280, height: 300 }); - await this.page.getByTestId(`tab-${this.urlToConnectTo}`).click(); - const heading = this.page.getByRole("heading", { + await this.getByTestId(`tab-${this.urlToConnectTo}`).click(); + const heading = this.getByRole("heading", { name: "Welcome to the Page Editor!", }); await expect(heading).toBeVisible(); @@ -72,10 +78,6 @@ export class PageEditorPage { await this.page.waitForTimeout(500); } - getTemplateGalleryButton() { - return this.page.getByRole("button", { name: "Launch Template Gallery" }); - } - /** * Adds a starter brick in the Page Editor. Generates a unique mod name to prevent * test collision. @@ -87,12 +89,10 @@ export class PageEditorPage { async addStarterBrick(starterBrickName: StarterBrickName) { const modUuid = uuidv4(); const modComponentName = `Test ${starterBrickName} ${modUuid}`; - await this.page.getByRole("button", { name: "Add", exact: true }).click(); - await this.page - .locator("[role=button].dropdown-item", { - hasText: starterBrickName, - }) - .click(); + await this.getByRole("button", { name: "Add", exact: true }).click(); + await this.locator("[role=button].dropdown-item", { + hasText: starterBrickName, + }).click(); return { modComponentName, modUuid }; } @@ -103,7 +103,7 @@ export class PageEditorPage { } async fillInBrickField(fieldLabel: string, value: string) { - await this.page.getByLabel(fieldLabel).fill(value); + await this.getByLabel(fieldLabel).fill(value); await this.waitForReduxUpdate(); } @@ -111,21 +111,20 @@ export class PageEditorPage { brickName: string, { index = 0 }: { index?: number } = {}, ) { - await this.page - .getByTestId(/icon-button-.*-add-brick/) + await this.getByTestId(/icon-button-.*-add-brick/) .nth(index) .click(); - await this.page.getByTestId("tag-search-input").fill(brickName); - await this.page.getByRole("button", { name: brickName }).click(); + await this.getByTestId("tag-search-input").fill(brickName); + await this.getByRole("button", { name: brickName }).click(); - await this.page.getByRole("button", { name: "Add brick" }).click(); + await this.getByRole("button", { name: "Add brick" }).click(); } async selectConnectedPageElement(connectedPage: Page) { // Without focusing first, the click doesn't enable selection tool ¯\_(ツ)_/¯ - await this.page.getByLabel("Select element").focus(); - await this.page.getByLabel("Select element").click(); + await this.getByLabel("Select element").focus(); + await this.getByLabel("Select element").click(); await connectedPage.bringToFront(); await expect( @@ -136,7 +135,7 @@ export class PageEditorPage { .click(); await this.page.bringToFront(); - await expect(this.page.getByPlaceholder("Select an element")).toHaveValue( + await expect(this.getByPlaceholder("Select an element")).toHaveValue( "#root h1", ); @@ -144,8 +143,7 @@ export class PageEditorPage { } getModListItemByName(modName: string) { - return this.page - .locator(".list-group-item") + return this.locator(".list-group-item") .locator("span", { hasText: modName }) .first(); } @@ -157,28 +155,16 @@ export class PageEditorPage { // TODO: this method is currently meant for packaged mods that aren't meant to be // cleaned up after the test. Future work is adding affordance to clean up saved packaged // mods, with an option to avoid cleanup for certain mods. - await this.page.locator("[data-icon=save]").click(); - await this.page.getByRole("button", { name: "Save" }).click(); - } - - getByText(text: string) { - return this.page.getByText(text); - } - - getByLabel(text: string) { - return this.page.getByLabel(text); - } - - getByPlaceholder(text: string) { - return this.page.getByPlaceholder(text); + await this.locator("[data-icon=save]").click(); + await this.getByRole("button", { name: "Save" }).click(); } getRenderPanelButton() { - return this.page.getByRole("button", { name: "Render Panel" }); + return this.getByRole("button", { name: "Render Panel" }); } getIncrementVersionErrorToast() { - return this.page.getByText( + return this.getByText( "Cannot overwrite version of a published brick. Increment the version", ); } @@ -188,12 +174,12 @@ export class PageEditorPage { // https://github.com/pixiebrix/pixiebrix-extension/blob/277eab74d2c85c2d16053bbcd27023d2612f9e31/src/pageEditor/panes/EditorPane.tsx#L48 // eslint-disable-next-line playwright/no-wait-for-timeout -- see above await this.page.waitForTimeout(600); - const modListItem = this.page.locator(".list-group-item", { + const modListItem = this.locator(".list-group-item", { hasText: modName, }); await modListItem.click(); await modListItem.locator("[data-icon=save]").click(); - await expect(this.page.getByText("Saved Mod")).toBeVisible(); + await expect(this.getByText("Saved Mod")).toBeVisible(); this.savedStandaloneModNames.push(modName); } @@ -208,18 +194,18 @@ export class PageEditorPage { }) { const modName = `${modNameRoot} ${modUuid}`; - await this.page.getByLabel(`${modComponentName} - Ellipsis`).click(); - await this.page.getByRole("button", { name: "Add to mod" }).click(); + await this.getByLabel(`${modComponentName} - Ellipsis`).click(); + await this.getByRole("button", { name: "Add to mod" }).click(); - await this.page.getByText("Select...Choose a mod").click(); - await this.page.getByRole("option", { name: /Create new mod.../ }).click(); - await this.page.getByRole("button", { name: "Move" }).click(); + await this.getByText("Select...Choose a mod").click(); + await this.getByRole("option", { name: /Create new mod.../ }).click(); + await this.getByRole("button", { name: "Move" }).click(); const modId = `${modName.split(" ").join("-").toLowerCase()}-${modUuid}`; - await this.page.getByTestId("registryId-id-id").fill(modId); + await this.getByTestId("registryId-id-id").fill(modId); - await this.page.getByLabel("Name", { exact: true }).fill(modName); - await this.page.getByRole("button", { name: "Create" }).click(); + await this.getByLabel("Name", { exact: true }).fill(modName); + await this.getByRole("button", { name: "Create" }).click(); this.savedPackageModIds.push(modId); diff --git a/end-to-end-tests/tests/extensionConsole/activation.spec.ts b/end-to-end-tests/tests/extensionConsole/activation.spec.ts index 74e32c3c67..5ea10a5ab8 100644 --- a/end-to-end-tests/tests/extensionConsole/activation.spec.ts +++ b/end-to-end-tests/tests/extensionConsole/activation.spec.ts @@ -85,7 +85,9 @@ test("can activate a mod with built-in integration", async ({ await modActivationPage.goto(); await expect( - page.locator(".form-group").filter({ hasText: /^GIPHY — ✨ Built-in$/ }), + modActivationPage + .locator(".form-group") + .filter({ hasText: /^GIPHY — ✨ Built-in$/ }), ).toBeVisible(); await modActivationPage.clickActivateAndWaitForModsPageRedirect(); await page.goto("/"); @@ -176,21 +178,19 @@ test("activating a mod when the quickbar shortcut is not configured", async ({ await test.step("Verify the mod activation page has links for setting the shortcut", async () => { await expect( - modActivationPage.keyboardShortcutDocumentationLink(), + modActivationPage.keyboardShortcutDocumentationLink, ).toBeVisible(); - await modActivationPage.keyboardShortcutDocumentationLink().click(); + await modActivationPage.keyboardShortcutDocumentationLink.click(); await expect( secondTab.getByRole("heading", { name: "Changing the Quick Bar" }), ).toBeVisible(); await secondTab.goBack(); - await expect( - modActivationPage.configureQuickbarShortcutLink(), - ).toBeVisible(); + await expect(modActivationPage.configureQuickbarShortcutLink).toBeVisible(); const configureShortcutPage = await clickAndWaitForNewPage( - modActivationPage.configureQuickbarShortcutLink(), + modActivationPage.configureQuickbarShortcutLink, context, ); diff --git a/end-to-end-tests/tests/modLifecycle.spec.ts b/end-to-end-tests/tests/modLifecycle.spec.ts index a9366306fa..db305e8e47 100644 --- a/end-to-end-tests/tests/modLifecycle.spec.ts +++ b/end-to-end-tests/tests/modLifecycle.spec.ts @@ -112,14 +112,14 @@ test("create, run, package, and update mod", async ({ ).toBeVisible(); await modListing.getByRole("button", { name: "Update" }).click(); - await expect(newPage.locator("form")).toContainText( + await expect(modsPage.locator("form")).toContainText( "Created through Playwright Automation", ); await expect( - newPage.getByRole("button", { name: "Reactivate" }), + modsPage.getByRole("button", { name: "Reactivate" }), ).toBeVisible(); - await newPage.getByRole("button", { name: "Reactivate" }).click(); + await modsPage.getByRole("button", { name: "Reactivate" }).click(); await expect(modListing).toContainText("version 1.0.1"); }); diff --git a/end-to-end-tests/tests/pageEditor/saveMod.spec.ts b/end-to-end-tests/tests/pageEditor/saveMod.spec.ts index 1dc3babc0b..f7f100552b 100644 --- a/end-to-end-tests/tests/pageEditor/saveMod.spec.ts +++ b/end-to-end-tests/tests/pageEditor/saveMod.spec.ts @@ -36,7 +36,7 @@ test("can save a standalone trigger mod", async ({ const modsPage = new ModsPage(page, extensionId); await modsPage.goto(); await expect( - page.locator(".list-group-item", { hasText: modComponentName }), + modsPage.locator(".list-group-item", { hasText: modComponentName }), ).toBeVisible(); }); diff --git a/end-to-end-tests/tests/runtime/localIntegrations.spec.ts b/end-to-end-tests/tests/runtime/localIntegrations.spec.ts index 7684b72ff6..9032b9c08c 100644 --- a/end-to-end-tests/tests/runtime/localIntegrations.spec.ts +++ b/end-to-end-tests/tests/runtime/localIntegrations.spec.ts @@ -46,35 +46,39 @@ test.describe("Local Integrations Page", () => { "Automation Anywhere Control Room", ); - await page.getByLabel("Label").fill("AA Control Room"); + await localIntegrationsPage.getByLabel("Label").fill("AA Control Room"); await page .getByTestId("toggle-config.folderId") .getByRole("button") .click(); - await page.getByLabel("Folder ID").click(); + await localIntegrationsPage.getByLabel("Folder ID").click(); - await expect(page.getByLabel("Folder ID")).toBeFocused(); + await expect(localIntegrationsPage.getByLabel("Folder ID")).toBeFocused(); await page.keyboard.press("Tab"); // Verify that the numeric text field is validated await expect( - page.getByText("String does not match pattern."), + localIntegrationsPage.getByText("String does not match pattern."), ).toBeVisible(); // Verify that the other fields are also validated - await page.getByLabel("Control Room URL").click(); - await expect(page.getByLabel("Control Room URL")).toBeFocused(); + await localIntegrationsPage.getByLabel("Control Room URL").click(); + await expect( + localIntegrationsPage.getByLabel("Control Room URL"), + ).toBeFocused(); await page.keyboard.press("Tab"); await expect( - page.getByText("controlRoomUrl is a required field"), + localIntegrationsPage.getByText("controlRoomUrl is a required field"), ).toBeVisible(); - await expect(page.getByLabel("Username")).toBeFocused(); + await expect(localIntegrationsPage.getByLabel("Username")).toBeFocused(); await page.keyboard.press("Tab"); - await expect(page.getByText("username is a required field")).toBeVisible(); + await expect( + localIntegrationsPage.getByText("username is a required field"), + ).toBeVisible(); }); }); diff --git a/end-to-end-tests/tests/smoke/modsPage.spec.ts b/end-to-end-tests/tests/smoke/modsPage.spec.ts index 58497d1546..2964945875 100644 --- a/end-to-end-tests/tests/smoke/modsPage.spec.ts +++ b/end-to-end-tests/tests/smoke/modsPage.spec.ts @@ -18,7 +18,7 @@ import { test, expect } from "../../fixtures/testBase"; import { ModsPage } from "../../pageObjects/extensionConsole/modsPage"; import AxeBuilder from "@axe-core/playwright"; -import { checkForCriticalViolations, ensureVisibility } from "../../utils"; +import { checkForCriticalViolations } from "../../utils"; // @ts-expect-error -- https://youtrack.jetbrains.com/issue/AQUA-711/Provide-a-run-configuration-for-Playwright-tests-in-specs-with-fixture-imports-only import { test as base } from "@playwright/test"; @@ -29,9 +29,6 @@ test.describe("extension console mods page smoke test", () => { const pageTitle = await page.title(); expect(pageTitle).toBe("Mods | PixieBrix"); await modsPage.viewAllMods(); - const modTableItems = modsPage.modTableItems(); - // Expect at least one mod visible - These might be initially hidden, so toBeVisible() would immediately fail - await ensureVisibility(modTableItems.nth(0), { timeout: 6000 }); const accessibilityScanResults = await new AxeBuilder({ page }).analyze(); diff --git a/end-to-end-tests/tests/smoke/pageEditor.spec.ts b/end-to-end-tests/tests/smoke/pageEditor.spec.ts index a0da335ff6..dd8e2f94b4 100644 --- a/end-to-end-tests/tests/smoke/pageEditor.spec.ts +++ b/end-to-end-tests/tests/smoke/pageEditor.spec.ts @@ -27,6 +27,6 @@ test.describe("page editor smoke test", () => { await page.goto("/bootstrap-5"); const pageEditorPage = await newPageEditorPage(page.url()); - await expect(pageEditorPage.getTemplateGalleryButton()).toBeVisible(); + await expect(pageEditorPage.templateGalleryButton).toBeVisible(); }); }); diff --git a/end-to-end-tests/tests/smoke/sidebar.spec.ts b/end-to-end-tests/tests/smoke/sidebar.spec.ts index 71e6eeafef..e7e652aacd 100644 --- a/end-to-end-tests/tests/smoke/sidebar.spec.ts +++ b/end-to-end-tests/tests/smoke/sidebar.spec.ts @@ -37,7 +37,7 @@ test.describe("sidebar page smoke test", () => { // The default integration values are not immediately loaded and are temporarily empty. // If we try activating too fast, the activation will fail due to missing configuration, so we wait for the values to load. await expect( - page.getByText("OpenAI — ✨ Built-in", { exact: true }), + modActivationPage.getByText("OpenAI — ✨ Built-in", { exact: true }), ).toBeVisible(); await modActivationPage.clickActivateAndWaitForModsPageRedirect(); diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts b/end-to-end-tests/tests/workshop/createMod.spec.ts index c96873768b..da58b02019 100644 --- a/end-to-end-tests/tests/workshop/createMod.spec.ts +++ b/end-to-end-tests/tests/workshop/createMod.spec.ts @@ -36,7 +36,7 @@ test("can create a new mod from a yaml definition", async ({ const editWorkshopModPage = await workshopPage.findAndSelectMod(simpleSidebarModId); - await expect(editWorkshopModPage.editor.baseLocator).toContainText( + await expect(editWorkshopModPage.editor.root).toContainText( simpleSidebarModId, );