From b11ad10270d076729a0cb6f9567ed1a86f10bc43 Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Thu, 6 Feb 2025 12:56:55 -0500 Subject: [PATCH 1/7] Upload test output if job is cancelled Should improve debugging hangups --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 77bf59bb8..7a570da36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -206,7 +206,7 @@ jobs: TEST_WALLET_JSON_PASSWORD: ${{ secrets.TEST_WALLET_JSON_PASSWORD }} run: xvfb-run npx playwright test --grep @expensive - uses: actions/upload-artifact@v4 - if: failure() + if: ${{ failure() || cancelled() }} with: name: debug-output path: | @@ -269,7 +269,7 @@ jobs: USE_MAINNET_FORK: true run: xvfb-run npx playwright test - uses: actions/upload-artifact@v4 - if: failure() + if: ${{ failure() || cancelled() }} with: name: fork-debug-output path: | From c23af650cbfc27c49738a9ac4031fb3085609116 Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:06:09 -0500 Subject: [PATCH 2/7] Fix invalid setTimeout reference --- background/services/indexing/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/background/services/indexing/index.ts b/background/services/indexing/index.ts index ab3bf40fe..e2aab2425 100644 --- a/background/services/indexing/index.ts +++ b/background/services/indexing/index.ts @@ -360,7 +360,7 @@ export default class IndexingService extends BaseService { ******************* */ private acceleratedTokenRefresh: { - timeout: number | undefined + timeout: ReturnType | undefined assetLookups: { asset: SmartContractFungibleAsset addressOnNetwork: AddressOnNetwork @@ -430,7 +430,7 @@ export default class IndexingService extends BaseService { ) this.acceleratedTokenRefresh.assetLookups.push(...assetLookups) - this.acceleratedTokenRefresh.timeout ??= window.setTimeout( + this.acceleratedTokenRefresh.timeout ??= setTimeout( this.handleAcceleratedTokenRefresh.bind(this), ACCELERATED_TOKEN_REFRESH_TIMEOUT, ) From 637385f0d81062f7728410e5166b215fcf3f4e7e Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:08:50 -0500 Subject: [PATCH 3/7] Ensure `redux` is awaited properly before calling `connectPort` --- src/background.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/background.ts b/src/background.ts index ee4dac87f..8a9504988 100644 --- a/src/background.ts +++ b/src/background.ts @@ -4,7 +4,6 @@ import { isEnabled, RuntimeFlag, } from "@tallyho/tally-background/features" -import ReduxService from "@tallyho/tally-background/services/redux" import { ONBOARDING_ROOT } from "@tallyho/tally-ui/pages/Onboarding/Tabbed/Routes" browser.runtime.onInstalled.addListener((obj) => { @@ -28,10 +27,9 @@ browser.runtime.onInstalled.addListener((obj) => { } }) -let redux: Promise +const redux = startRedux() browser.runtime.onConnect.addListener(async (port) => { - ;(await redux).connectPort(port) + const service = await redux + service.connectPort(port) }) - -redux ??= startRedux() From ad692d0ee442d870598a1e90249043408b1057c8 Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:36:41 -0500 Subject: [PATCH 4/7] Update e2e tests These tests were mostly failing due to minor updates in the external content --- e2e-tests/regular/dapp-connect.spec.ts | 6 +++--- e2e-tests/regular/nfts.spec.ts | 4 +++- e2e-tests/regular/signing.spec.ts | 2 +- e2e-tests/utils/walletPageHelper.ts | 6 ++++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/e2e-tests/regular/dapp-connect.spec.ts b/e2e-tests/regular/dapp-connect.spec.ts index f194b918a..c522f520b 100644 --- a/e2e-tests/regular/dapp-connect.spec.ts +++ b/e2e-tests/regular/dapp-connect.spec.ts @@ -10,8 +10,8 @@ test.describe("dApp Connections", () => { const dappPage = await context.newPage() await dappPage.goto("https://swap.cow.fi/") await dappPage - .locator("#swap-button") .getByRole("button", { name: "Connect Wallet" }) + .first() .click() // Get page after a specific action (e.g. clicking a link) @@ -44,8 +44,8 @@ test.describe("dApp Connections", () => { const dappPage2 = await context.newPage() await dappPage2.goto("https://swap.cow.fi/") await dappPage2 - .locator("#swap-button") .getByRole("button", { name: "Connect Wallet" }) + .first() .click() // Get page after a specific action (e.g. clicking a link) @@ -73,8 +73,8 @@ test.describe("dApp Connections", () => { const dappPage = await context.newPage() await dappPage.goto("https://swap.cow.fi/") await dappPage - .locator("#swap-button") .getByRole("button", { name: "Connect Wallet" }) + .first() .click() // Get page after a specific action (e.g. clicking a link) diff --git a/e2e-tests/regular/nfts.spec.ts b/e2e-tests/regular/nfts.spec.ts index f06b6ce9b..1bfeebaea 100644 --- a/e2e-tests/regular/nfts.spec.ts +++ b/e2e-tests/regular/nfts.spec.ts @@ -141,6 +141,7 @@ test.describe("NFTs", () => { .getByTestId("nft_collection_filters") .getByTestId("toggle_item") .filter({ hasText: "POAP" }) + .first() .getByRole("checkbox") .click() @@ -161,6 +162,7 @@ test.describe("NFTs", () => { .getByTestId("nft_collection_filters") .getByTestId("toggle_item") .filter({ hasText: "POAP" }) + .first() .getByRole("checkbox") .click() @@ -188,7 +190,7 @@ test.describe("NFTs", () => { const nftCollection = page .getByTestId("nft_list_item") .filter({ has: page.getByTestId("nft_list_item_collection") }) - .filter({ hasText: "Notable Punks" }) + .filter({ hasText: "Notable Crypto Punks" }) .first() await nftCollection.hover() diff --git a/e2e-tests/regular/signing.spec.ts b/e2e-tests/regular/signing.spec.ts index b785bb7be..caa3e7ac1 100644 --- a/e2e-tests/regular/signing.spec.ts +++ b/e2e-tests/regular/signing.spec.ts @@ -14,7 +14,7 @@ test.describe("Signing", () => { await siwe.getByRole("button", { name: "SIGN-IN WITH ETHEREUM" }).click() const connectPopupOpens = context.waitForEvent("page") - await siwe.getByText("Connect to your Web3 Wallet").click() + await siwe.getByTestId("component-wallet-button-taho").click() await connectPopupOpens.then(async (popup) => { await popup.waitForLoadState() diff --git a/e2e-tests/utils/walletPageHelper.ts b/e2e-tests/utils/walletPageHelper.ts index 82a749528..13f3cc9b0 100644 --- a/e2e-tests/utils/walletPageHelper.ts +++ b/e2e-tests/utils/walletPageHelper.ts @@ -309,8 +309,8 @@ export default class WalletPageHelper { const dappPage = await this.context.newPage() await dappPage.goto("https://swap.cow.fi/") await dappPage - .locator("#swap-button") .getByRole("button", { name: "Connect Wallet" }) + .first() .click() const [popupPage] = await Promise.all([ @@ -325,7 +325,9 @@ export default class WalletPageHelper { }) if ((await connectingPopupTitle.count()) > 0) { await expect(connectingPopupTitle).toBeVisible() - const bgLocator = popupPage.locator(".bg") + const bgLocator = popupPage + .locator(".bg") + .getByRole("button", { name: "Close" }) await bgLocator.click() await bgLocator.waitFor({ state: "detached", timeout: 1000 }) From 8e5fb7da3c7da889fd7ba419a7c6a1594b7f9f62 Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:26:02 -0500 Subject: [PATCH 5/7] Remove fixed request delay on nfts test --- .github/workflows/build.yml | 3 +++ e2e-tests/regular/nfts.spec.ts | 24 ++++++++++++++++++------ e2e-tests/utils/walletPageHelper.ts | 1 + ui/components/NFTs/NFTListPortfolio.tsx | 14 +++++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a570da36..7b9fa1abe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -221,6 +221,9 @@ jobs: needs: [build, detect-if-flag-changed] timeout-minutes: 60 runs-on: ubuntu-latest + env: + # https://playwright.dev/docs/service-workers-experimental + PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS: 1 steps: - uses: actions/checkout@v3 - name: Read .nvmrc diff --git a/e2e-tests/regular/nfts.spec.ts b/e2e-tests/regular/nfts.spec.ts index 1bfeebaea..86a93fa47 100644 --- a/e2e-tests/regular/nfts.spec.ts +++ b/e2e-tests/regular/nfts.spec.ts @@ -1,7 +1,20 @@ -import { wait } from "@tallyho/tally-background/lib/utils" import { test, expect } from "../utils" import { account1 } from "../utils/onboarding" +const makeStall = (label?: string) => { + let end: () => void = () => { + throw new Error(`Unable to set stall "${label}"`) + } + + const stall = new Promise((resolve) => { + end = resolve + }) as Promise & { end: (label?: string) => void } + + stall.end = end + + return stall +} + test.describe("NFTs", () => { test("User can view nft collections, poaps and badges", async ({ page, @@ -10,11 +23,11 @@ test.describe("NFTs", () => { walletPageHelper, }) => { await test.step("Shows loading state", async () => { - let shouldInterceptRequests = true + const stall = makeStall("Stall simplehash requests") // Set a delay so we don't miss loading states await context.route(/api\.simplehash\.com/i, async (route, request) => { - if (!shouldInterceptRequests || !isExtensionRequest(request)) { + if (!isExtensionRequest(request)) { route.continue() return } @@ -39,7 +52,7 @@ test.describe("NFTs", () => { .locator(".active") .waitFor({ state: "visible" }) - await wait(5_000) + await stall await route.fulfill({ response }) } }) @@ -59,9 +72,8 @@ test.describe("NFTs", () => { // Wait until load finishes await expect(page.getByTestId("loading_doggo")).toBeVisible() + stall.end("Stop stalling simplehash") await expect(page.getByTestId("loading_doggo")).not.toBeVisible() - - shouldInterceptRequests = false }) // Header stats locators diff --git a/e2e-tests/utils/walletPageHelper.ts b/e2e-tests/utils/walletPageHelper.ts index 13f3cc9b0..db5c68963 100644 --- a/e2e-tests/utils/walletPageHelper.ts +++ b/e2e-tests/utils/walletPageHelper.ts @@ -35,6 +35,7 @@ export default class WalletPageHelper { await this.popup .getByRole("navigation", { name: "Main" }) .getByRole("link", { name: tab }) + .first() .click() } diff --git a/ui/components/NFTs/NFTListPortfolio.tsx b/ui/components/NFTs/NFTListPortfolio.tsx index 39d8943fc..12809df61 100644 --- a/ui/components/NFTs/NFTListPortfolio.tsx +++ b/ui/components/NFTs/NFTListPortfolio.tsx @@ -12,6 +12,7 @@ import SharedBanner from "../Shared/SharedBanner" import NFTList from "./NFTList" import NFTsExploreBanner from "./NFTsExploreBanner" import NoMatchingNFTs from "./NoMatchingNFTs" +import SharedLoadingDoggo from "../Shared/SharedLoadingDoggo" export default function NFTListPortfolio(props: { type: "badge" | "nfts" @@ -34,7 +35,18 @@ export default function NFTListPortfolio(props: { const isLoading = useBackgroundSelector(selectIsReloadingNFTs) if (isEmptyPortfolio) { - return + return ( + <> + {isLoading && ( + + )} + + + ) } return ( From 8729383cbb0f14ea8f91f1300964f32e812aaf8f Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Fri, 7 Feb 2025 23:21:04 -0500 Subject: [PATCH 6/7] Close onboarding tab once done Should free up some of the limited resources in CI --- e2e-tests/utils/onboarding.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/e2e-tests/utils/onboarding.ts b/e2e-tests/utils/onboarding.ts index 61ee6a075..1ee434bdc 100644 --- a/e2e-tests/utils/onboarding.ts +++ b/e2e-tests/utils/onboarding.ts @@ -69,6 +69,7 @@ export default class OnboardingHelper { await expect( page.getByRole("heading", { name: "Welcome to Taho" }), ).toBeVisible() + await page.close() }) } @@ -104,6 +105,7 @@ export default class OnboardingHelper { await expect( page.getByRole("heading", { name: "Welcome to Taho" }), ).toBeVisible() + await page.close() }) } @@ -163,6 +165,7 @@ export default class OnboardingHelper { await expect( page.getByRole("heading", { name: "Welcome to Taho" }), ).toBeVisible() + await page.close() }) } @@ -227,5 +230,6 @@ export default class OnboardingHelper { await expect( page.getByRole("heading", { name: "Welcome to Taho" }), ).toBeVisible() + await page.close() } } From d71f7d9b18660acc4dacee018a118a73ddbbee04 Mon Sep 17 00:00:00 2001 From: Jorge Luis <28708889+hyphenized@users.noreply.github.com> Date: Fri, 7 Feb 2025 23:49:36 -0500 Subject: [PATCH 7/7] Add wallet popup helpers Added a utility class for connection popup interactions and a helper function to strictly watch for extension popups. --- e2e-tests/regular/dapp-connect.spec.ts | 51 +++++++------------- e2e-tests/regular/signing.spec.ts | 21 ++++----- e2e-tests/utils.ts | 8 +++- e2e-tests/utils/connect-popup.ts | 65 ++++++++++++++++++++++++++ e2e-tests/utils/walletPageHelper.ts | 42 ++--------------- 5 files changed, 104 insertions(+), 83 deletions(-) create mode 100644 e2e-tests/utils/connect-popup.ts diff --git a/e2e-tests/regular/dapp-connect.spec.ts b/e2e-tests/regular/dapp-connect.spec.ts index c522f520b..e624c61f7 100644 --- a/e2e-tests/regular/dapp-connect.spec.ts +++ b/e2e-tests/regular/dapp-connect.spec.ts @@ -14,30 +14,15 @@ test.describe("dApp Connections", () => { .first() .click() + const popupPage = walletPageHelper.getConnectPopup() // Get page after a specific action (e.g. clicking a link) - const [popupPage] = await Promise.all([ - context.waitForEvent("page"), - await dappPage.locator("text=Injected").click(), // Opens a new tab - ]) - await popupPage.waitForLoadState() - - // Clear the one-time informational popup, if present. - const connectingPopupTitle = popupPage.locator("h3", { - hasText: "Connecting with Taho", - }) + await dappPage.locator("text=Injected").click() // Opens a new tab - expect(await connectingPopupTitle.count()).toBe(1) - await expect(connectingPopupTitle).toBeVisible() - - // Clear the popover. - const popupCloseLocator = popupPage.getByRole("button", { - name: "Background close", - }) + await popupPage.ready() - await popupCloseLocator.click() - await popupCloseLocator.waitFor({ state: "detached", timeout: 1000 }) + await popupPage.hideDappConnectPopup() - await popupPage.locator("button", { hasText: "Reject" }).click() + await popupPage.rejectConnection() await dappPage.close() @@ -48,15 +33,15 @@ test.describe("dApp Connections", () => { .first() .click() + const popup2 = walletPageHelper.getConnectPopup() + + await dappPage2.locator("text=Injected").click() // Opens a new tab // Get page after a specific action (e.g. clicking a link) - const [popupPage2] = await Promise.all([ - context.waitForEvent("page"), - await dappPage2.locator("text=Injected").click(), // Opens a new tab - ]) - await popupPage2.waitForLoadState() + + await popup2.ready() // Check that the popup is no longer displayed. - const connectingPopupTitle2 = popupPage2.locator("h3", { + const connectingPopupTitle2 = popup2.page.locator("h3", { hasText: "Connecting with Taho", }) expect(await connectingPopupTitle2.count()).toBe(0) @@ -68,23 +53,23 @@ test.describe("dApp Connections", () => { walletPageHelper, }) => { await walletPageHelper.onboarding.addReadOnlyAccount("testertesting.eth") - await walletPageHelper.hideDappConnectPopup() const dappPage = await context.newPage() await dappPage.goto("https://swap.cow.fi/") + + const popup = walletPageHelper.getConnectPopup() + await dappPage .getByRole("button", { name: "Connect Wallet" }) .first() .click() // Get page after a specific action (e.g. clicking a link) - const [popupPage] = await Promise.all([ - context.waitForEvent("page"), - await dappPage.locator("text=Injected").click(), // Opens a new tab - ]) - await popupPage.waitForLoadState() + await dappPage.locator("text=Injected").click() // Opens a new tab - await popupPage.locator("button", { hasText: "Connect" }).click() + await popup.ready() + await popup.hideDappConnectPopup() + await popup.acceptConnection() await walletPageHelper.goToStartPage() diff --git a/e2e-tests/regular/signing.spec.ts b/e2e-tests/regular/signing.spec.ts index caa3e7ac1..4b7b7fa78 100644 --- a/e2e-tests/regular/signing.spec.ts +++ b/e2e-tests/regular/signing.spec.ts @@ -4,33 +4,30 @@ test.describe("Signing", () => { test("User can sign in with Ethereum", async ({ context, walletPageHelper, + waitForExtensionPage, }) => { await walletPageHelper.onboarding.addNewWallet() - await walletPageHelper.hideDappConnectPopup() const siwe = await context.newPage() await siwe.goto("https://login.xyz") await siwe.getByRole("button", { name: "SIGN-IN WITH ETHEREUM" }).click() - const connectPopupOpens = context.waitForEvent("page") + const connectPopup = walletPageHelper.getConnectPopup() await siwe.getByTestId("component-wallet-button-taho").click() - await connectPopupOpens.then(async (popup) => { - await popup.waitForLoadState() - await popup.getByRole("button", { name: "Connect", exact: true }).click() - }) + await connectPopup.ready() + await connectPopup.hideDappConnectPopup() + + await connectPopup.acceptConnection() - const signDataPromptOpens = context.waitForEvent("page") + const signDataPromptOpens = waitForExtensionPage() await signDataPromptOpens.then(async (prompt) => { + await prompt.waitForLoadState() await prompt.getByRole("button", { name: "Sign" }).click() }) - // There's a bug on login.xyz that makes the test fail - // (https://discord.com/channels/862419652286218251/886997073650655232/1173226370776694794). - // We're adding the expectation of failure. Playwright will throw `Expected - // to fail, but passed.` when bug is fixed. - test.fail() + await expect(siwe.getByText("Vote for your favorite emoji")).toBeVisible() }) diff --git a/e2e-tests/utils.ts b/e2e-tests/utils.ts index 6d59b6b9a..afa6b1fd9 100644 --- a/e2e-tests/utils.ts +++ b/e2e-tests/utils.ts @@ -1,5 +1,5 @@ /* eslint-disable no-empty-pattern */ -import { test as base, chromium, Request, Worker } from "@playwright/test" +import { test as base, chromium, Page, Request, Worker } from "@playwright/test" import path from "path" import WalletPageHelper from "./utils/walletPageHelper" import AssetsHelper from "./utils/assets" @@ -15,6 +15,7 @@ type WalletTestFixtures = { transactionsHelper: TransactionsHelper backgroundPage: Worker isExtensionRequest: (request: Request) => boolean + waitForExtensionPage: () => Promise } /** @@ -52,6 +53,11 @@ export const test = base.extend({ const extensionId = backgroundPage.url().split("/")[2] await use(extensionId) }, + waitForExtensionPage: async ({ context, extensionId }, use) => { + await use(async () => + context.waitForEvent("page", (page) => page.url().includes(extensionId)), + ) + }, isExtensionRequest: async ({}, use) => { const hasExtensionOrigin = (url: string) => /^chrome-extension:\/\//.test(url) diff --git a/e2e-tests/utils/connect-popup.ts b/e2e-tests/utils/connect-popup.ts new file mode 100644 index 000000000..b619e09ee --- /dev/null +++ b/e2e-tests/utils/connect-popup.ts @@ -0,0 +1,65 @@ +import { BrowserContext, Page, expect } from "@playwright/test" + +export default class ConnectPopupHelper { + get page() { + if (this.#targetPage === null) { + throw new Error("Connection popup hasn't loaded yet") + } + return this.#targetPage + } + + #targetPage: Page | null = null + + #pagePromise: Promise + + constructor( + private readonly context: BrowserContext, + extensionId: string, + ) { + this.#pagePromise = this.context.waitForEvent("page", (page) => + page.url().includes(extensionId), + ) + } + + async ready() { + const popup = await this.#pagePromise + await popup.waitForLoadState() + this.#targetPage = popup + } + + /** + * Hides the dApp Connection "use Taho as default" informational popup so + * tests can proceed assuming dApp connection will be available without + * additional interactions. + */ + async hideDappConnectPopup(): Promise { + // Clear the one-time informational popup, if present. + const connectingPopupTitle = this.page.locator("h3", { + hasText: "Connecting with Taho", + }) + + expect(await connectingPopupTitle.count()).toBe(1) + + await expect(connectingPopupTitle).toBeVisible() + + // Clear the popover. + const bgLocator = this.page + .locator(".bg") + .getByRole("button", { name: "Close" }) + + await bgLocator.click() + await bgLocator.waitFor({ state: "detached", timeout: 1000 }) + } + + async rejectConnection() { + await this.page.locator("button", { hasText: "Reject" }).click() + } + + async acceptConnection() { + await this.page.getByRole("button", { name: "Connect" }).click() + } + + async closeWindow() { + await this.page.close() + } +} diff --git a/e2e-tests/utils/walletPageHelper.ts b/e2e-tests/utils/walletPageHelper.ts index db5c68963..7e2f37c5e 100644 --- a/e2e-tests/utils/walletPageHelper.ts +++ b/e2e-tests/utils/walletPageHelper.ts @@ -1,6 +1,7 @@ import fs from "fs" import { Page, BrowserContext, expect } from "@playwright/test" import OnboardingHelper, { Account, getOnboardingPage } from "./onboarding" +import ConnectPopupHelper from "./connect-popup" export default class WalletPageHelper { readonly url: string @@ -11,6 +12,10 @@ export default class WalletPageHelper { return this.#onboardingHelper } + getConnectPopup(): ConnectPopupHelper { + return new ConnectPopupHelper(this.context, this.extensionId) + } + constructor( public readonly popup: Page, public readonly context: BrowserContext, @@ -300,41 +305,4 @@ export default class WalletPageHelper { this.popup.getByTestId("top_menu_profile_button").last(), ).toHaveText(accountName) } - - /** - * Hides the dApp Connection "use Taho as default" informational popup so - * tests can proceed assuming dApp connection will be available without - * additional interactions. - */ - async hideDappConnectPopup(): Promise { - const dappPage = await this.context.newPage() - await dappPage.goto("https://swap.cow.fi/") - await dappPage - .getByRole("button", { name: "Connect Wallet" }) - .first() - .click() - - const [popupPage] = await Promise.all([ - this.context.waitForEvent("page"), - await dappPage.locator("text=Injected").click(), // Opens a new tab - ]) - await popupPage.waitForLoadState() - - // Clear the one-time informational popup, if present. - const connectingPopupTitle = popupPage.locator("h3", { - hasText: "Connecting with Taho", - }) - if ((await connectingPopupTitle.count()) > 0) { - await expect(connectingPopupTitle).toBeVisible() - const bgLocator = popupPage - .locator(".bg") - .getByRole("button", { name: "Close" }) - - await bgLocator.click() - await bgLocator.waitFor({ state: "detached", timeout: 1000 }) - } - - await popupPage.close() - await dappPage.close() - } }