From 529ae03a98878fc7e35f13f5348c612f096155f1 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 00:36:27 -0500 Subject: [PATCH 01/21] create fixture for auth, joinroom/createroom to test multi-users-connection test --- .../src/components/panel/problem/index.tsx | 1 + extension/tests/auth.ts | 41 +++++++++++++ extension/tests/fixture.ts | 25 +++++--- extension/tests/room.ts | 61 +++++++++++++++++++ .../scripts/multi-users-connection.spec.ts | 20 ++++++ 5 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 extension/tests/auth.ts create mode 100644 extension/tests/room.ts create mode 100644 extension/tests/scripts/multi-users-connection.spec.ts diff --git a/extension/src/components/panel/problem/index.tsx b/extension/src/components/panel/problem/index.tsx index a41b42a9..65c65483 100644 --- a/extension/src/components/panel/problem/index.tsx +++ b/extension/src/components/panel/problem/index.tsx @@ -3,6 +3,7 @@ import { DOM, EXTENSION } from "@cb/constants"; import { useHtmlActions } from "@cb/hooks/store"; import useResource from "@cb/hooks/useResource"; import { Question } from "@cb/types"; +import { waitForElement } from "@cb/utils"; import React, { useEffect } from "react"; import { toast } from "sonner"; diff --git a/extension/tests/auth.ts b/extension/tests/auth.ts new file mode 100644 index 00000000..c7ede0e1 --- /dev/null +++ b/extension/tests/auth.ts @@ -0,0 +1,41 @@ +import type { Page } from "@playwright/test"; +import { test } from "./fixture"; + +export interface AuthenticatedUser { + email: string; + page: Page; + context: any; + extensionId: string; +} + +async function signIn(page: Page, email: string): Promise { + await page.goto("https://leetcode.com/problems/two-sum", { + waitUntil: "domcontentloaded", + }); + const emailInput = page.locator('input[type="email"]'); + await emailInput.fill(email); + const continueButton = page.getByRole("button", { name: "Continue" }); + await continueButton.click(); +} + +export const authenticatedTest = test.extend<{ + authenticatedUser: AuthenticatedUser; +}>({ + authenticatedUser: async ({ context, extensionId }, use) => { + const page = context.pages()[0] || (await context.newPage()); + const email = `test-user-${Date.now()}-${Math.random().toString(36).substring(7)}@test.com`; + + await signIn(page, email); + + const user: AuthenticatedUser = { + email, + page, + context, + extensionId, + }; + + await use(user); + }, +}); + +export { signIn }; diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 1cff490d..298a448e 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -13,19 +13,29 @@ if (!fs.existsSync(extension)) { throw new Error(`Invalid path ${extension}`); } +async function createExtensionContext(): Promise { + const context = await chromium.launchPersistentContext("", { + headless: false, + channel: "chromium", + args: [ + `--disable-extensions-except=${extension}`, + `--load-extension=${extension}`, + + "--disable-web-security", + ], + }); + return context; +} + export const test = base.extend<{ context: BrowserContext; extensionId: string; }>({ // eslint-disable-next-line no-empty-pattern context: async ({}, use) => { - const context = await chromium.launchPersistentContext("", { - headless: false, - channel: "chromium", - args: [ - `--disable-extensions-except=${extension}`, - `--load-extension=${extension}`, - ], + const context = await createExtensionContext(); + await context.grantPermissions(["clipboard-read", "clipboard-write"], { + origin: "https://leetcode.com", }); await use(context); await context.close(); @@ -54,3 +64,4 @@ export const test = base.extend<{ }); export const expect = test.expect; +export { createExtensionContext }; diff --git a/extension/tests/room.ts b/extension/tests/room.ts new file mode 100644 index 00000000..076d8424 --- /dev/null +++ b/extension/tests/room.ts @@ -0,0 +1,61 @@ +import type { Page } from "@playwright/test"; +import { authenticatedTest } from "./auth"; + +export interface RoomInfo { + id: string; +} + +export interface CreateRoomOptions { + name?: string; + isPublic: boolean; +} + +async function createRoom(page: Page): Promise { + const createRoomButton = page.locator('button:has-text("Create Room")'); + await createRoomButton.click(); + + const radioButton = page.getByRole("radio", { name: "Private" }); + await radioButton.click(); + + const createButton = page.getByRole("button", { name: "Create" }); + await createButton.click(); + + const copyButton = page.getByRole("img", { name: "Copy room ID" }); + await copyButton.click(); + + const roomId = await page.evaluate(async () => { + return await navigator.clipboard.readText(); + }); + + if (!roomId) { + throw new Error("Failed to extract room ID after creating room. "); + } + + return { id: roomId }; +} + +async function joinRoom(page: Page, roomId: string): Promise { + //joinRoomButton is hidden by beta banner + const removeBetaBanner = page.locator(".lucide.lucide-x"); + await removeBetaBanner.click(); + + const joinRoomButton = page.getByRole("button", { name: "Join room" }); + await joinRoomButton.click(); + + const roomIdInput = page.locator('input[id="roomId"]'); + await roomIdInput.fill(roomId); + + const joinButton = page.locator('button:has-text("Join")').last(); + await joinButton.click(); +} + +export const roomTest = authenticatedTest.extend<{ + room: RoomInfo; +}>({ + room: async ({ authenticatedUser }, use) => { + const room = await createRoom(authenticatedUser.page); + await use(room); + }, +}); + +export { joinRoom }; diff --git a/extension/tests/scripts/multi-users-connection.spec.ts b/extension/tests/scripts/multi-users-connection.spec.ts new file mode 100644 index 00000000..11d13ad8 --- /dev/null +++ b/extension/tests/scripts/multi-users-connection.spec.ts @@ -0,0 +1,20 @@ +import { signIn } from "../auth"; +import { createExtensionContext } from "../fixture"; +import { joinRoom, roomTest } from "../room"; + +roomTest( + "Two users can establish connection within one room", + async ({ authenticatedUser, room }) => { + const user1 = authenticatedUser; + + const user2Context = await createExtensionContext(); + const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); + const user2Email = `user2-${Date.now()}@test.com`; + + await signIn(user2Page, user2Email); + + await joinRoom(user2Page, room.id); + + await user2Context.close(); + } +); From 14f77a706e9b49526ed26ac9f67d58c5726797ca Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 00:43:00 -0500 Subject: [PATCH 02/21] fix --- extension/tests/auth.ts | 4 ++-- extension/tests/room.ts | 5 ----- extension/tests/scripts/multi-users-connection.spec.ts | 2 -- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/extension/tests/auth.ts b/extension/tests/auth.ts index c7ede0e1..24d7da27 100644 --- a/extension/tests/auth.ts +++ b/extension/tests/auth.ts @@ -1,10 +1,10 @@ -import type { Page } from "@playwright/test"; +import type { BrowserContext, Page } from "@playwright/test"; import { test } from "./fixture"; export interface AuthenticatedUser { email: string; page: Page; - context: any; + context: BrowserContext; extensionId: string; } diff --git a/extension/tests/room.ts b/extension/tests/room.ts index 076d8424..7a62f529 100644 --- a/extension/tests/room.ts +++ b/extension/tests/room.ts @@ -5,11 +5,6 @@ export interface RoomInfo { id: string; } -export interface CreateRoomOptions { - name?: string; - isPublic: boolean; -} - async function createRoom(page: Page): Promise { const createRoomButton = page.locator('button:has-text("Create Room")'); await createRoomButton.click(); diff --git a/extension/tests/scripts/multi-users-connection.spec.ts b/extension/tests/scripts/multi-users-connection.spec.ts index 11d13ad8..8877aa91 100644 --- a/extension/tests/scripts/multi-users-connection.spec.ts +++ b/extension/tests/scripts/multi-users-connection.spec.ts @@ -5,8 +5,6 @@ import { joinRoom, roomTest } from "../room"; roomTest( "Two users can establish connection within one room", async ({ authenticatedUser, room }) => { - const user1 = authenticatedUser; - const user2Context = await createExtensionContext(); const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); const user2Email = `user2-${Date.now()}@test.com`; From d8b4077ad33544ffbd5dfbb018692a1f29c795a6 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 01:05:19 -0500 Subject: [PATCH 03/21] headless for CI = true --- extension/tests/fixture.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 298a448e..f502704a 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -15,7 +15,7 @@ if (!fs.existsSync(extension)) { async function createExtensionContext(): Promise { const context = await chromium.launchPersistentContext("", { - headless: false, + headless: process.env.CI === "true", channel: "chromium", args: [ `--disable-extensions-except=${extension}`, From 7ed84a952a78082065d32ef3116db82a1279f624 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 01:14:54 -0500 Subject: [PATCH 04/21] revert --- extension/tests/fixture.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index f502704a..298a448e 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -15,7 +15,7 @@ if (!fs.existsSync(extension)) { async function createExtensionContext(): Promise { const context = await chromium.launchPersistentContext("", { - headless: process.env.CI === "true", + headless: false, channel: "chromium", args: [ `--disable-extensions-except=${extension}`, From 3eae3d03b30a5e76e5375054fc9b18577c7daa74 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 11:05:15 -0500 Subject: [PATCH 05/21] add data-dir --- extension/tests/fixture.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 298a448e..d9ac4e52 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -1,7 +1,9 @@ import { DOM } from "@cb/constants"; import { test as base, chromium, type BrowserContext } from "@playwright/test"; +import { mkdtemp } from "fs/promises"; import fs from "node:fs"; -import { dirname, resolve } from "path"; +import { tmpdir } from "os"; +import { dirname, join, resolve } from "path"; import { fileURLToPath } from "url"; const extension = resolve( @@ -14,14 +16,14 @@ if (!fs.existsSync(extension)) { } async function createExtensionContext(): Promise { - const context = await chromium.launchPersistentContext("", { + const userDataDir = await mkdtemp(join(tmpdir(), "playwright-chrome-")); + const context = await chromium.launchPersistentContext(userDataDir, { headless: false, channel: "chromium", args: [ `--disable-extensions-except=${extension}`, `--load-extension=${extension}`, - - "--disable-web-security", + `--disable-web-security`, ], }); return context; From 4148f4db301c11159dbad6442230833f090a7fc1 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 11:20:05 -0500 Subject: [PATCH 06/21] replace --disable-web-security with grandPermission(local-network-access) --- extension/tests/fixture.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index d9ac4e52..bad5179b 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -1,9 +1,7 @@ import { DOM } from "@cb/constants"; import { test as base, chromium, type BrowserContext } from "@playwright/test"; -import { mkdtemp } from "fs/promises"; import fs from "node:fs"; -import { tmpdir } from "os"; -import { dirname, join, resolve } from "path"; +import { dirname, resolve } from "path"; import { fileURLToPath } from "url"; const extension = resolve( @@ -16,14 +14,12 @@ if (!fs.existsSync(extension)) { } async function createExtensionContext(): Promise { - const userDataDir = await mkdtemp(join(tmpdir(), "playwright-chrome-")); - const context = await chromium.launchPersistentContext(userDataDir, { + const context = await chromium.launchPersistentContext("", { headless: false, channel: "chromium", args: [ `--disable-extensions-except=${extension}`, `--load-extension=${extension}`, - `--disable-web-security`, ], }); return context; @@ -36,9 +32,12 @@ export const test = base.extend<{ // eslint-disable-next-line no-empty-pattern context: async ({}, use) => { const context = await createExtensionContext(); - await context.grantPermissions(["clipboard-read", "clipboard-write"], { - origin: "https://leetcode.com", - }); + await context.grantPermissions( + ["clipboard-read", "clipboard-write", "local-network-access"], + { + origin: "https://leetcode.com", + } + ); await use(context); await context.close(); }, From c425a3998a82bf12cad5ee26b41eb711be63f480 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 14:18:11 -0500 Subject: [PATCH 07/21] fix --- extension/tests/fixture.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index bad5179b..d03a8bfa 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -22,6 +22,12 @@ async function createExtensionContext(): Promise { `--load-extension=${extension}`, ], }); + await context.grantPermissions( + ["clipboard-read", "clipboard-write", "local-network-access"], + { + origin: "https://leetcode.com", + } + ); return context; } @@ -32,12 +38,6 @@ export const test = base.extend<{ // eslint-disable-next-line no-empty-pattern context: async ({}, use) => { const context = await createExtensionContext(); - await context.grantPermissions( - ["clipboard-read", "clipboard-write", "local-network-access"], - { - origin: "https://leetcode.com", - } - ); await use(context); await context.close(); }, From 53e0e2358292017dbfacd5b9ea2489cf00ec31f4 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 14:40:31 -0500 Subject: [PATCH 08/21] add isCI --- extension/tests/fixture.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index d03a8bfa..362cdb04 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -14,9 +14,10 @@ if (!fs.existsSync(extension)) { } async function createExtensionContext(): Promise { + const isCI = !!process.env.CI; const context = await chromium.launchPersistentContext("", { - headless: false, - channel: "chromium", + headless: isCI, + ...(isCI ? {} : { channel: "chromium" }), args: [ `--disable-extensions-except=${extension}`, `--load-extension=${extension}`, From a215e9bf3216f4e18b193e69aa2e264d201397dc Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Fri, 2 Jan 2026 14:49:58 -0500 Subject: [PATCH 09/21] revert --- extension/tests/fixture.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 362cdb04..d03a8bfa 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -14,10 +14,9 @@ if (!fs.existsSync(extension)) { } async function createExtensionContext(): Promise { - const isCI = !!process.env.CI; const context = await chromium.launchPersistentContext("", { - headless: isCI, - ...(isCI ? {} : { channel: "chromium" }), + headless: false, + channel: "chromium", args: [ `--disable-extensions-except=${extension}`, `--load-extension=${extension}`, From 32bea5050cc89878086073444a759bb63514f8fe Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Sat, 3 Jan 2026 14:02:56 -0500 Subject: [PATCH 10/21] initialize permissions instead of await context.grandPermission --- extension/tests/fixture.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index d03a8bfa..bead1c05 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -21,13 +21,8 @@ async function createExtensionContext(): Promise { `--disable-extensions-except=${extension}`, `--load-extension=${extension}`, ], + permissions: ["clipboard-read", "clipboard-write", "local-network-access"], }); - await context.grantPermissions( - ["clipboard-read", "clipboard-write", "local-network-access"], - { - origin: "https://leetcode.com", - } - ); return context; } From ed0818194f98ce715d17a003d3bb6d31bfaa340c Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Sat, 3 Jan 2026 14:52:34 -0500 Subject: [PATCH 11/21] set up docker --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4bcb1202..66beb17c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,17 @@ jobs: # todo(nickbar01234): Cache playwright? - name: Install Playwright browsers run: pnpm exec playwright install --with-deps + + - name: Start Firebase emulator + run: docker compose up -d + - name: Wait for firebase emulator + run: | + echo "Waiting to check firebase to be healthy" + until [ "$(docker inspect --format='{{.State.Health.Status}}' firebase)" = "healthy" ]; do + echo "Waiting..." + sleep 2 + done + echo "Firebase is healthy" # todo(nickbar01234): Should switch to chrome-mv3 for production build or setup CI to point to docker firebase - name: Build extension in dev mode run: pnpm build:dev From 6093bd573c45612e68c2faa34c0618fbe82c53bf Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Sat, 3 Jan 2026 16:37:55 -0500 Subject: [PATCH 12/21] test copy code feature --- extension/tests/fixture.ts | 15 ++-- extension/tests/room.ts | 56 --------------- extension/tests/scripts/copy-test.spec.ts | 42 +++++++++++ .../scripts/multi-users-connection.spec.ts | 18 ----- extension/tests/{ => setup}/auth.ts | 8 +-- extension/tests/setup/room.ts | 69 +++++++++++++++++++ 6 files changed, 123 insertions(+), 85 deletions(-) delete mode 100644 extension/tests/room.ts create mode 100644 extension/tests/scripts/copy-test.spec.ts delete mode 100644 extension/tests/scripts/multi-users-connection.spec.ts rename extension/tests/{ => setup}/auth.ts (79%) create mode 100644 extension/tests/setup/room.ts diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index bead1c05..7c455481 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -26,6 +26,13 @@ async function createExtensionContext(): Promise { return context; } +async function getExtensionId(context: BrowserContext): Promise { + let [serviceWorker] = context.serviceWorkers(); + if (!serviceWorker) + serviceWorker = await context.waitForEvent("serviceworker"); + + return serviceWorker.url().split("/")[2]; +} export const test = base.extend<{ context: BrowserContext; extensionId: string; @@ -37,11 +44,7 @@ export const test = base.extend<{ await context.close(); }, extensionId: async ({ context }, use) => { - let [serviceWorker] = context.serviceWorkers(); - if (!serviceWorker) - serviceWorker = await context.waitForEvent("serviceworker"); - - const extensionId = serviceWorker.url().split("/")[2]; + const extensionId = await getExtensionId(context); await use(extensionId); }, page: async ({ page }, use) => { @@ -60,4 +63,4 @@ export const test = base.extend<{ }); export const expect = test.expect; -export { createExtensionContext }; +export { createExtensionContext, getExtensionId }; diff --git a/extension/tests/room.ts b/extension/tests/room.ts deleted file mode 100644 index 7a62f529..00000000 --- a/extension/tests/room.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { Page } from "@playwright/test"; -import { authenticatedTest } from "./auth"; - -export interface RoomInfo { - id: string; -} - -async function createRoom(page: Page): Promise { - const createRoomButton = page.locator('button:has-text("Create Room")'); - await createRoomButton.click(); - - const radioButton = page.getByRole("radio", { name: "Private" }); - await radioButton.click(); - - const createButton = page.getByRole("button", { name: "Create" }); - await createButton.click(); - - const copyButton = page.getByRole("img", { name: "Copy room ID" }); - await copyButton.click(); - - const roomId = await page.evaluate(async () => { - return await navigator.clipboard.readText(); - }); - - if (!roomId) { - throw new Error("Failed to extract room ID after creating room. "); - } - - return { id: roomId }; -} - -async function joinRoom(page: Page, roomId: string): Promise { - //joinRoomButton is hidden by beta banner - const removeBetaBanner = page.locator(".lucide.lucide-x"); - await removeBetaBanner.click(); - - const joinRoomButton = page.getByRole("button", { name: "Join room" }); - await joinRoomButton.click(); - - const roomIdInput = page.locator('input[id="roomId"]'); - await roomIdInput.fill(roomId); - - const joinButton = page.locator('button:has-text("Join")').last(); - await joinButton.click(); -} - -export const roomTest = authenticatedTest.extend<{ - room: RoomInfo; -}>({ - room: async ({ authenticatedUser }, use) => { - const room = await createRoom(authenticatedUser.page); - await use(room); - }, -}); - -export { joinRoom }; diff --git a/extension/tests/scripts/copy-test.spec.ts b/extension/tests/scripts/copy-test.spec.ts new file mode 100644 index 00000000..927eb717 --- /dev/null +++ b/extension/tests/scripts/copy-test.spec.ts @@ -0,0 +1,42 @@ +import { expect } from "../fixture"; +import { multiUserRoomTest } from "../setup/room"; + +async function getCodeFromEditor(page: any): Promise { + return await page.evaluate(() => { + const leetCodeEditor = (window as any).monaco.editor + .getEditors() + .find((editor: any) => { + const containerId = editor.getContainerDomNode()?.id; + return ( + containerId !== "codebuddy-editor" && + editor.getModel()?.getLanguageId() !== "plaintext" + ); + }); + + return leetCodeEditor?.getModel()?.getValue() ?? ""; + }); +} + +multiUserRoomTest( + "User1 can copy code from User2", + async ({ user1, user2 }) => { + await user2.page.getByRole("button", { name: "C++" }).click(); + await user2.page.getByText("Python", { exact: true }).click(); + // wait for 2 seconds to ensure the code is loaded + await user2.page.waitForTimeout(2000); + + const user2Code = await getCodeFromEditor(user2.page); + await user1.page.getByRole("tab", { name: /Code/i }).click(); + await user1.page.locator(".lucide.lucide-eye").click(); + await user1.page.locator(".lucide.lucide-copy").first().click(); + + // wait for 5 seconds to ensure the clipboard is updated + await user1.page.waitForTimeout(5000); + + const copiedCode = await user1.page.evaluate(async () => { + return await navigator.clipboard.readText(); + }); + + expect(copiedCode.trim()).toBe(user2Code.trim()); + } +); diff --git a/extension/tests/scripts/multi-users-connection.spec.ts b/extension/tests/scripts/multi-users-connection.spec.ts deleted file mode 100644 index 8877aa91..00000000 --- a/extension/tests/scripts/multi-users-connection.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { signIn } from "../auth"; -import { createExtensionContext } from "../fixture"; -import { joinRoom, roomTest } from "../room"; - -roomTest( - "Two users can establish connection within one room", - async ({ authenticatedUser, room }) => { - const user2Context = await createExtensionContext(); - const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); - const user2Email = `user2-${Date.now()}@test.com`; - - await signIn(user2Page, user2Email); - - await joinRoom(user2Page, room.id); - - await user2Context.close(); - } -); diff --git a/extension/tests/auth.ts b/extension/tests/setup/auth.ts similarity index 79% rename from extension/tests/auth.ts rename to extension/tests/setup/auth.ts index 24d7da27..bcee324f 100644 --- a/extension/tests/auth.ts +++ b/extension/tests/setup/auth.ts @@ -1,5 +1,5 @@ import type { BrowserContext, Page } from "@playwright/test"; -import { test } from "./fixture"; +import { test } from "../fixture"; export interface AuthenticatedUser { email: string; @@ -12,10 +12,8 @@ async function signIn(page: Page, email: string): Promise { await page.goto("https://leetcode.com/problems/two-sum", { waitUntil: "domcontentloaded", }); - const emailInput = page.locator('input[type="email"]'); - await emailInput.fill(email); - const continueButton = page.getByRole("button", { name: "Continue" }); - await continueButton.click(); + await page.locator('input[type="email"]').fill(email); + await page.getByRole("button", { name: "Continue" }).click(); } export const authenticatedTest = test.extend<{ diff --git a/extension/tests/setup/room.ts b/extension/tests/setup/room.ts new file mode 100644 index 00000000..e961a3c7 --- /dev/null +++ b/extension/tests/setup/room.ts @@ -0,0 +1,69 @@ +import type { Page } from "@playwright/test"; +import { createExtensionContext, getExtensionId } from "../fixture"; +import type { AuthenticatedUser } from "./auth"; +import { authenticatedTest, signIn } from "./auth"; + +export interface RoomInfo { + id: string; +} + +async function createRoom(page: Page): Promise { + await page.locator('button:has-text("Create Room")').click(); + await page.getByRole("radio", { name: "Private" }).click(); + await page.getByRole("button", { name: "Create" }).click(); + await page.getByRole("img", { name: "Copy room ID" }).click(); + + const roomId = await page.evaluate(async () => { + return await navigator.clipboard.readText(); + }); + + if (!roomId) { + throw new Error("Failed to extract room ID after creating room. "); + } + + return { id: roomId }; +} + +async function joinRoom(page: Page, roomId: string): Promise { + //joinRoomButton is hidden by beta banner + await page.locator(".lucide.lucide-x").click(); + await page.getByRole("button", { name: "Join room" }).click(); + await page.locator('input[id="roomId"]').fill(roomId); + await page.locator('button:has-text("Join")').last().click(); +} + +export const multiUserRoomTest = authenticatedTest.extend<{ + room: RoomInfo; + user1: AuthenticatedUser; + user2: AuthenticatedUser; +}>({ + user1: async ({ authenticatedUser }, use) => { + await use(authenticatedUser); + }, + room: async ({ user1 }, use) => { + const room = await createRoom(user1.page); + await use(room); + }, + user2: async ({ room }, use) => { + // Create separate context for user2 + const user2Context = await createExtensionContext(); + const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); + const user2Email = `user2-${Date.now()}@test.com`; + + await signIn(user2Page, user2Email); + await joinRoom(user2Page, room.id); + const extensionId = await getExtensionId(user2Context); + + const user2: AuthenticatedUser = { + email: user2Email, + page: user2Page, + context: user2Context, + extensionId, + }; + + await use(user2); + await user2Context.close(); + }, +}); + +export { joinRoom }; From cf58acfbf0a98639436398dd9247e168b341096f Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Sun, 4 Jan 2026 18:31:30 -0500 Subject: [PATCH 13/21] organize code --- .../components/panel/editor/EditorPanel.tsx | 1 + .../components/panel/editor/tab/CodeTab.tsx | 1 + extension/tests/fixture.ts | 64 ++++++++++++++++- extension/tests/pages/copy-test.spec.ts | 21 ++++++ extension/tests/scripts/copy-test.spec.ts | 42 ----------- extension/tests/setup/auth.ts | 39 ----------- extension/tests/setup/room.ts | 69 ------------------- extension/tests/utils/auth.ts | 9 +++ extension/tests/utils/room.ts | 30 ++++++++ 9 files changed, 123 insertions(+), 153 deletions(-) create mode 100644 extension/tests/pages/copy-test.spec.ts delete mode 100644 extension/tests/scripts/copy-test.spec.ts delete mode 100644 extension/tests/setup/auth.ts delete mode 100644 extension/tests/setup/room.ts create mode 100644 extension/tests/utils/auth.ts create mode 100644 extension/tests/utils/room.ts diff --git a/extension/src/components/panel/editor/EditorPanel.tsx b/extension/src/components/panel/editor/EditorPanel.tsx index 9534d206..ab9308cf 100644 --- a/extension/src/components/panel/editor/EditorPanel.tsx +++ b/extension/src/components/panel/editor/EditorPanel.tsx @@ -113,6 +113,7 @@ const EditorPanel = () => { trigger={{ node: (
diff --git a/extension/src/components/panel/editor/tab/CodeTab.tsx b/extension/src/components/panel/editor/tab/CodeTab.tsx index 9ac263cd..7cc45c9c 100644 --- a/extension/src/components/panel/editor/tab/CodeTab.tsx +++ b/extension/src/components/panel/editor/tab/CodeTab.tsx @@ -14,6 +14,7 @@ export const CodeTab: React.FC = () => { trigger={{ node: (
diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 7c455481..1ebaefb7 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -1,8 +1,15 @@ import { DOM } from "@cb/constants"; -import { test as base, chromium, type BrowserContext } from "@playwright/test"; +import { + test as base, + chromium, + type BrowserContext, + type Page, +} from "@playwright/test"; import fs from "node:fs"; import { dirname, resolve } from "path"; import { fileURLToPath } from "url"; +import { signIn } from "./utils/auth"; +import { createRoom, joinRoom, type RoomInfo } from "./utils/room"; const extension = resolve( dirname(fileURLToPath(import.meta.url)), @@ -30,9 +37,9 @@ async function getExtensionId(context: BrowserContext): Promise { let [serviceWorker] = context.serviceWorkers(); if (!serviceWorker) serviceWorker = await context.waitForEvent("serviceworker"); - return serviceWorker.url().split("/")[2]; } + export const test = base.extend<{ context: BrowserContext; extensionId: string; @@ -62,5 +69,56 @@ export const test = base.extend<{ }, }); +export interface User { + page: Page; + email: string; + extensionId: string; + context: BrowserContext; +} + +export const twoUserRoomTest = test.extend<{ + room: RoomInfo; + user1: User; + user2: User; +}>({ + user1: async ({ context, extensionId }, use) => { + const page = context.pages()[0] || (await context.newPage()); + const email = `user1-${Date.now()}@test.com`; + + await signIn(page, email); + + const user1: User = { + email, + page, + context, + extensionId, + }; + + await use(user1); + }, + room: async ({ user1 }, use) => { + const room = await createRoom(user1.page); + await use(room); + }, + user2: async ({ room }, use) => { + const user2Context = await createExtensionContext(); + const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); + const user2Email = `user2-${Date.now()}@test.com`; + + await signIn(user2Page, user2Email); + await joinRoom(user2Page, room.id); + + const user2ExtensionId = await getExtensionId(user2Context); + + const user2: User = { + email: user2Email, + page: user2Page, + context: user2Context, + extensionId: user2ExtensionId, + }; + + await use(user2); + }, +}); + export const expect = test.expect; -export { createExtensionContext, getExtensionId }; diff --git a/extension/tests/pages/copy-test.spec.ts b/extension/tests/pages/copy-test.spec.ts new file mode 100644 index 00000000..85d0fae9 --- /dev/null +++ b/extension/tests/pages/copy-test.spec.ts @@ -0,0 +1,21 @@ +import { expect, twoUserRoomTest } from "../fixture"; + +const EXPECTED_CPP_CODE = `class Solution { +public: + vector twoSum(vector& nums, int target) { + + } +};`; + +twoUserRoomTest("User1 can copy code from User2", async ({ user1, user2 }) => { + await user1.page.getByRole("tab", { name: /Code/i }).click(); + await user1.page.getByTestId("toggle-code-visibility").click(); + await user1.page.getByTestId("copy-code").click({ force: true }); + + await expect(async () => { + const copiedCode = await user1.page.evaluate(async () => { + return await navigator.clipboard.readText(); + }); + expect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); + }).toPass(); +}); diff --git a/extension/tests/scripts/copy-test.spec.ts b/extension/tests/scripts/copy-test.spec.ts deleted file mode 100644 index 927eb717..00000000 --- a/extension/tests/scripts/copy-test.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { expect } from "../fixture"; -import { multiUserRoomTest } from "../setup/room"; - -async function getCodeFromEditor(page: any): Promise { - return await page.evaluate(() => { - const leetCodeEditor = (window as any).monaco.editor - .getEditors() - .find((editor: any) => { - const containerId = editor.getContainerDomNode()?.id; - return ( - containerId !== "codebuddy-editor" && - editor.getModel()?.getLanguageId() !== "plaintext" - ); - }); - - return leetCodeEditor?.getModel()?.getValue() ?? ""; - }); -} - -multiUserRoomTest( - "User1 can copy code from User2", - async ({ user1, user2 }) => { - await user2.page.getByRole("button", { name: "C++" }).click(); - await user2.page.getByText("Python", { exact: true }).click(); - // wait for 2 seconds to ensure the code is loaded - await user2.page.waitForTimeout(2000); - - const user2Code = await getCodeFromEditor(user2.page); - await user1.page.getByRole("tab", { name: /Code/i }).click(); - await user1.page.locator(".lucide.lucide-eye").click(); - await user1.page.locator(".lucide.lucide-copy").first().click(); - - // wait for 5 seconds to ensure the clipboard is updated - await user1.page.waitForTimeout(5000); - - const copiedCode = await user1.page.evaluate(async () => { - return await navigator.clipboard.readText(); - }); - - expect(copiedCode.trim()).toBe(user2Code.trim()); - } -); diff --git a/extension/tests/setup/auth.ts b/extension/tests/setup/auth.ts deleted file mode 100644 index bcee324f..00000000 --- a/extension/tests/setup/auth.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { BrowserContext, Page } from "@playwright/test"; -import { test } from "../fixture"; - -export interface AuthenticatedUser { - email: string; - page: Page; - context: BrowserContext; - extensionId: string; -} - -async function signIn(page: Page, email: string): Promise { - await page.goto("https://leetcode.com/problems/two-sum", { - waitUntil: "domcontentloaded", - }); - await page.locator('input[type="email"]').fill(email); - await page.getByRole("button", { name: "Continue" }).click(); -} - -export const authenticatedTest = test.extend<{ - authenticatedUser: AuthenticatedUser; -}>({ - authenticatedUser: async ({ context, extensionId }, use) => { - const page = context.pages()[0] || (await context.newPage()); - const email = `test-user-${Date.now()}-${Math.random().toString(36).substring(7)}@test.com`; - - await signIn(page, email); - - const user: AuthenticatedUser = { - email, - page, - context, - extensionId, - }; - - await use(user); - }, -}); - -export { signIn }; diff --git a/extension/tests/setup/room.ts b/extension/tests/setup/room.ts deleted file mode 100644 index e961a3c7..00000000 --- a/extension/tests/setup/room.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { Page } from "@playwright/test"; -import { createExtensionContext, getExtensionId } from "../fixture"; -import type { AuthenticatedUser } from "./auth"; -import { authenticatedTest, signIn } from "./auth"; - -export interface RoomInfo { - id: string; -} - -async function createRoom(page: Page): Promise { - await page.locator('button:has-text("Create Room")').click(); - await page.getByRole("radio", { name: "Private" }).click(); - await page.getByRole("button", { name: "Create" }).click(); - await page.getByRole("img", { name: "Copy room ID" }).click(); - - const roomId = await page.evaluate(async () => { - return await navigator.clipboard.readText(); - }); - - if (!roomId) { - throw new Error("Failed to extract room ID after creating room. "); - } - - return { id: roomId }; -} - -async function joinRoom(page: Page, roomId: string): Promise { - //joinRoomButton is hidden by beta banner - await page.locator(".lucide.lucide-x").click(); - await page.getByRole("button", { name: "Join room" }).click(); - await page.locator('input[id="roomId"]').fill(roomId); - await page.locator('button:has-text("Join")').last().click(); -} - -export const multiUserRoomTest = authenticatedTest.extend<{ - room: RoomInfo; - user1: AuthenticatedUser; - user2: AuthenticatedUser; -}>({ - user1: async ({ authenticatedUser }, use) => { - await use(authenticatedUser); - }, - room: async ({ user1 }, use) => { - const room = await createRoom(user1.page); - await use(room); - }, - user2: async ({ room }, use) => { - // Create separate context for user2 - const user2Context = await createExtensionContext(); - const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); - const user2Email = `user2-${Date.now()}@test.com`; - - await signIn(user2Page, user2Email); - await joinRoom(user2Page, room.id); - const extensionId = await getExtensionId(user2Context); - - const user2: AuthenticatedUser = { - email: user2Email, - page: user2Page, - context: user2Context, - extensionId, - }; - - await use(user2); - await user2Context.close(); - }, -}); - -export { joinRoom }; diff --git a/extension/tests/utils/auth.ts b/extension/tests/utils/auth.ts new file mode 100644 index 00000000..f83f05b4 --- /dev/null +++ b/extension/tests/utils/auth.ts @@ -0,0 +1,9 @@ +import type { Page } from "@playwright/test"; + +export async function signIn(page: Page, email: string): Promise { + await page.goto("https://leetcode.com/problems/two-sum", { + waitUntil: "domcontentloaded", + }); + await page.getByRole("textbox", { name: "Enter your email" }).fill(email); + await page.getByRole("button", { name: "Continue" }).click(); +} diff --git a/extension/tests/utils/room.ts b/extension/tests/utils/room.ts new file mode 100644 index 00000000..2426e83b --- /dev/null +++ b/extension/tests/utils/room.ts @@ -0,0 +1,30 @@ +import type { Page } from "@playwright/test"; + +export interface RoomInfo { + id: string; +} + +export async function createRoom(page: Page): Promise { + await page.getByRole("button", { name: "Create Room" }).click(); + await page.getByRole("radio", { name: "Private" }).click(); + await page.getByRole("button", { name: "Create" }).click(); + await page.getByRole("img", { name: "Copy room ID" }).click(); + + const roomId = await page.evaluate(async () => { + return await navigator.clipboard.readText(); + }); + + if (!roomId) { + throw new Error("Failed to extract room ID after creating room. "); + } + + return { id: roomId }; +} + +export async function joinRoom(page: Page, roomId: string): Promise { + //joinRoomButton is hidden by beta banner + await page.locator(".lucide.lucide-x").click(); + await page.getByRole("button", { name: "Join room" }).click(); + await page.getByRole("textbox", { name: "Enter room ID" }).fill(roomId); + await page.getByRole("button", { name: "Join" }).click(); +} From bd35c83faf44c34326d180c28f17ff54f30e8ed5 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Sun, 4 Jan 2026 18:35:44 -0500 Subject: [PATCH 14/21] remove force and close context --- extension/tests/fixture.ts | 1 + extension/tests/pages/copy-test.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 1ebaefb7..17104513 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -118,6 +118,7 @@ export const twoUserRoomTest = test.extend<{ }; await use(user2); + await user2Context.close(); }, }); diff --git a/extension/tests/pages/copy-test.spec.ts b/extension/tests/pages/copy-test.spec.ts index 85d0fae9..bd15faec 100644 --- a/extension/tests/pages/copy-test.spec.ts +++ b/extension/tests/pages/copy-test.spec.ts @@ -10,7 +10,7 @@ public: twoUserRoomTest("User1 can copy code from User2", async ({ user1, user2 }) => { await user1.page.getByRole("tab", { name: /Code/i }).click(); await user1.page.getByTestId("toggle-code-visibility").click(); - await user1.page.getByTestId("copy-code").click({ force: true }); + await user1.page.getByTestId("copy-code").click(); await expect(async () => { const copiedCode = await user1.page.evaluate(async () => { From b750e9d66c6994728ebf47d1ef52cbee0ec1ac41 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Sun, 4 Jan 2026 18:43:56 -0500 Subject: [PATCH 15/21] remove beta-version banner --- extension/src/components/root/ContentScript.tsx | 5 +---- extension/tests/utils/room.ts | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/extension/src/components/root/ContentScript.tsx b/extension/src/components/root/ContentScript.tsx index 6ca71940..f553dece 100644 --- a/extension/src/components/root/ContentScript.tsx +++ b/extension/src/components/root/ContentScript.tsx @@ -1,7 +1,6 @@ import { AppNavigator } from "@cb/components/navigator/AppNavigator"; import { ContainerNavigator } from "@cb/components/navigator/ContainerNavigator"; import { AppControlMenu, RoomControlMenu } from "@cb/components/navigator/menu"; -import { BottomBannerPanel } from "@cb/components/panel/BottomBannerPanel"; import { LoadingPanel } from "@cb/components/panel/LoadingPanel"; import { ResizablePanel } from "@cb/components/panel/ResizablePanel"; import SignInPanel from "@cb/components/panel/SignInPanel"; @@ -22,9 +21,7 @@ export const ContentScript = () => { case AppStatus.AUTHENTICATED: return ( }> - - - + ); case AppStatus.UNAUTHENTICATED: diff --git a/extension/tests/utils/room.ts b/extension/tests/utils/room.ts index 2426e83b..22cb417e 100644 --- a/extension/tests/utils/room.ts +++ b/extension/tests/utils/room.ts @@ -22,8 +22,6 @@ export async function createRoom(page: Page): Promise { } export async function joinRoom(page: Page, roomId: string): Promise { - //joinRoomButton is hidden by beta banner - await page.locator(".lucide.lucide-x").click(); await page.getByRole("button", { name: "Join room" }).click(); await page.getByRole("textbox", { name: "Enter room ID" }).fill(roomId); await page.getByRole("button", { name: "Join" }).click(); From 9e72d616669b4b1da3d90309195284f0c2464a9c Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Wed, 7 Jan 2026 11:04:31 -0500 Subject: [PATCH 16/21] fix --- extension/tests/fixture.ts | 48 +++++++++++++------------ extension/tests/pages/content.spec.ts | 21 ++++++++++- extension/tests/pages/copy-test.spec.ts | 21 ----------- extension/tests/utils/auth.ts | 3 -- extension/tests/utils/room.ts | 8 +---- 5 files changed, 47 insertions(+), 54 deletions(-) delete mode 100644 extension/tests/pages/copy-test.spec.ts diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 17104513..35025e1a 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -40,6 +40,19 @@ async function getExtensionId(context: BrowserContext): Promise { return serviceWorker.url().split("/")[2]; } +async function setupPage(page: Page): Promise { + page.on("console", (msg) => { + console.log("Received message from page", msg.text(), msg.type()); + }); + await page.goto("https://leetcode.com/problems/two-sum", { + waitUntil: "domcontentloaded", + }); + await page.waitForSelector(DOM.LEETCODE_ROOT_ID, { + state: "visible", + timeout: 30_000, + }); +} + export const test = base.extend<{ context: BrowserContext; extensionId: string; @@ -55,16 +68,7 @@ export const test = base.extend<{ await use(extensionId); }, page: async ({ page }, use) => { - page.on("console", (msg) => { - console.log("Received message from page", msg.text(), msg.type()); - }); - await page.goto("https://leetcode.com/problems/two-sum", { - waitUntil: "domcontentloaded", - }); - await page.waitForSelector(DOM.LEETCODE_ROOT_ID, { - state: "visible", - timeout: 30_000, - }); + await setupPage(page); await use(page); }, }); @@ -74,39 +78,38 @@ export interface User { email: string; extensionId: string; context: BrowserContext; + room?: RoomInfo; } export const twoUserRoomTest = test.extend<{ - room: RoomInfo; user1: User; user2: User; }>({ - user1: async ({ context, extensionId }, use) => { - const page = context.pages()[0] || (await context.newPage()); + user1: async ({ page, context, extensionId }, use) => { const email = `user1-${Date.now()}@test.com`; - await signIn(page, email); - + const room = await createRoom(page); const user1: User = { email, page, context, extensionId, + room, }; - await use(user1); }, - room: async ({ user1 }, use) => { - const room = await createRoom(user1.page); - await use(room); - }, - user2: async ({ room }, use) => { + user2: async ({ user1 }, use) => { + if (!user1.room) { + throw new Error("user1 must have a room for user2 to join"); + } + const user2Context = await createExtensionContext(); const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); + await setupPage(user2Page); const user2Email = `user2-${Date.now()}@test.com`; await signIn(user2Page, user2Email); - await joinRoom(user2Page, room.id); + await joinRoom(user2Page, user1.room.id); const user2ExtensionId = await getExtensionId(user2Context); @@ -115,6 +118,7 @@ export const twoUserRoomTest = test.extend<{ page: user2Page, context: user2Context, extensionId: user2ExtensionId, + room: user1.room, }; await use(user2); diff --git a/extension/tests/pages/content.spec.ts b/extension/tests/pages/content.spec.ts index 791f9e4b..41130dde 100644 --- a/extension/tests/pages/content.spec.ts +++ b/extension/tests/pages/content.spec.ts @@ -1,7 +1,26 @@ -import { expect, test } from "@tests/fixture"; +import { expect, test, twoUserRoomTest } from "@tests/fixture"; test("Content script is mounted", async ({ page }) => { await expect(page.getByText("CodeBuddy").first()).toBeVisible({ timeout: 30_000, }); }); + +const EXPECTED_CPP_CODE = `class Solution { +public: + vector twoSum(vector& nums, int target) { + + } +};`; + +twoUserRoomTest("User1 can copy code from User2", async ({ user1, user2 }) => { + await user1.page.getByRole("tab", { name: /Code/i }).click(); + await user1.page.getByTestId("toggle-code-visibility").click(); + await user1.page.getByTestId("copy-code").click(); + await expect(async () => { + const copiedCode = await user1.page.evaluate(() => + navigator.clipboard.readText() + ); + expect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); + }).toPass(); +}); diff --git a/extension/tests/pages/copy-test.spec.ts b/extension/tests/pages/copy-test.spec.ts deleted file mode 100644 index bd15faec..00000000 --- a/extension/tests/pages/copy-test.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect, twoUserRoomTest } from "../fixture"; - -const EXPECTED_CPP_CODE = `class Solution { -public: - vector twoSum(vector& nums, int target) { - - } -};`; - -twoUserRoomTest("User1 can copy code from User2", async ({ user1, user2 }) => { - await user1.page.getByRole("tab", { name: /Code/i }).click(); - await user1.page.getByTestId("toggle-code-visibility").click(); - await user1.page.getByTestId("copy-code").click(); - - await expect(async () => { - const copiedCode = await user1.page.evaluate(async () => { - return await navigator.clipboard.readText(); - }); - expect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); - }).toPass(); -}); diff --git a/extension/tests/utils/auth.ts b/extension/tests/utils/auth.ts index f83f05b4..8bce17fe 100644 --- a/extension/tests/utils/auth.ts +++ b/extension/tests/utils/auth.ts @@ -1,9 +1,6 @@ import type { Page } from "@playwright/test"; export async function signIn(page: Page, email: string): Promise { - await page.goto("https://leetcode.com/problems/two-sum", { - waitUntil: "domcontentloaded", - }); await page.getByRole("textbox", { name: "Enter your email" }).fill(email); await page.getByRole("button", { name: "Continue" }).click(); } diff --git a/extension/tests/utils/room.ts b/extension/tests/utils/room.ts index 22cb417e..46372d43 100644 --- a/extension/tests/utils/room.ts +++ b/extension/tests/utils/room.ts @@ -10,13 +10,7 @@ export async function createRoom(page: Page): Promise { await page.getByRole("button", { name: "Create" }).click(); await page.getByRole("img", { name: "Copy room ID" }).click(); - const roomId = await page.evaluate(async () => { - return await navigator.clipboard.readText(); - }); - - if (!roomId) { - throw new Error("Failed to extract room ID after creating room. "); - } + const roomId = await page.evaluate(() => navigator.clipboard.readText()); return { id: roomId }; } From 91ce42d392234c38e167d67214ea169398c43fa6 Mon Sep 17 00:00:00 2001 From: nickbar01234 Date: Thu, 8 Jan 2026 22:49:27 -0500 Subject: [PATCH 17/21] Add room creator factory --- extension/tests/fixture/factory.ts | 78 +++++++++++++++++++++++++++ extension/tests/fixture/in-room.ts | 25 +++++++++ extension/tests/pages/content.spec.ts | 9 ++-- 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 extension/tests/fixture/factory.ts create mode 100644 extension/tests/fixture/in-room.ts diff --git a/extension/tests/fixture/factory.ts b/extension/tests/fixture/factory.ts new file mode 100644 index 00000000..12fa7f20 --- /dev/null +++ b/extension/tests/fixture/factory.ts @@ -0,0 +1,78 @@ +import { DOM } from "@cb/constants"; +import { test as base, BrowserContext, chromium, Page } from "@playwright/test"; +import { signIn } from "@tests/utils/auth"; +import fs from "node:fs"; +import { dirname, resolve } from "path"; +import { fileURLToPath } from "url"; + +const extension = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../dist/chrome-mv3-dev" +); + +if (!fs.existsSync(extension)) { + throw new Error(`Invalid path ${extension}`); +} + +export interface UserPage { + page: Page; + email: string; + extensionId: string; + context: BrowserContext; +} + +interface PlayWrightPageFactory { + instantiate: (email: string) => Promise; +} + +const setupPage = async (page: Page) => { + page.on("console", (msg) => { + console.log("Received message from page", msg.text(), msg.type()); + }); + await page.goto("https://leetcode.com/problems/two-sum", { + waitUntil: "domcontentloaded", + }); + await page.waitForSelector(DOM.LEETCODE_ROOT_ID, { + state: "visible", + timeout: 30_000, + }); +}; + +export const factory = base.extend<{ pageCreator: PlayWrightPageFactory }>({ + // eslint-disable-next-line no-empty-pattern + pageCreator: async ({}, use) => { + let context: BrowserContext | undefined; + const instantiate: PlayWrightPageFactory["instantiate"] = async (email) => { + context = await chromium.launchPersistentContext("", { + headless: false, + channel: "chromium", + args: [ + `--disable-extensions-except=${extension}`, + `--load-extension=${extension}`, + ], + permissions: [ + "clipboard-read", + "clipboard-write", + "local-network-access", + ], + }); + + let [serviceWorker] = context.serviceWorkers(); + if (!serviceWorker) + serviceWorker = await context.waitForEvent("serviceworker"); + const extensionId = serviceWorker.url().split("/")[2]; + + const page = context.pages()[0] ?? (await context.newPage()); + await setupPage(page); + await signIn(page, email); + + return { page, context, email, extensionId }; + }; + + await use({ instantiate }); + + if (context != undefined) { + context.close(); + } + }, +}); diff --git a/extension/tests/fixture/in-room.ts b/extension/tests/fixture/in-room.ts new file mode 100644 index 00000000..4b5247db --- /dev/null +++ b/extension/tests/fixture/in-room.ts @@ -0,0 +1,25 @@ +import { createRoom, joinRoom, RoomInfo } from "@tests/utils/room"; +import { factory, UserPage } from "./factory"; + +interface UserInRoomPage extends UserPage { + room: RoomInfo; +} + +export const inRoomTest = factory.extend<{ + user1: UserInRoomPage; + user2: UserInRoomPage; +}>({ + user1: async ({ pageCreator }, use) => { + const user = await pageCreator.instantiate("user1@test.com"); + const room = await createRoom(user.page); + await use({ ...user, room }); + }, + + user2: async ({ pageCreator, user1 }, use) => { + const user = await pageCreator.instantiate("user2@test.com"); + await joinRoom(user.page, user1.room.id); + await use({ ...user, room: user1.room }); + }, +}); + +export const inRoomExpect = inRoomTest.expect; diff --git a/extension/tests/pages/content.spec.ts b/extension/tests/pages/content.spec.ts index 41130dde..38044c2f 100644 --- a/extension/tests/pages/content.spec.ts +++ b/extension/tests/pages/content.spec.ts @@ -1,4 +1,5 @@ -import { expect, test, twoUserRoomTest } from "@tests/fixture"; +import { expect, test } from "@tests/fixture"; +import { inRoomExpect, inRoomTest } from "@tests/fixture/in-room"; test("Content script is mounted", async ({ page }) => { await expect(page.getByText("CodeBuddy").first()).toBeVisible({ @@ -13,14 +14,14 @@ public: } };`; -twoUserRoomTest("User1 can copy code from User2", async ({ user1, user2 }) => { +inRoomTest("User1 can copy code from User2", async ({ user1 }) => { await user1.page.getByRole("tab", { name: /Code/i }).click(); await user1.page.getByTestId("toggle-code-visibility").click(); await user1.page.getByTestId("copy-code").click(); - await expect(async () => { + await inRoomExpect(async () => { const copiedCode = await user1.page.evaluate(() => navigator.clipboard.readText() ); - expect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); + inRoomExpect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); }).toPass(); }); From 507e0fda5d99f0b17238af6ab9942c80d92bac1a Mon Sep 17 00:00:00 2001 From: nickbar01234 Date: Thu, 8 Jan 2026 22:59:03 -0500 Subject: [PATCH 18/21] Fix tsconfig --- extension/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/tsconfig.json b/extension/tsconfig.json index 5bddaa48..a400bd5c 100644 --- a/extension/tsconfig.json +++ b/extension/tsconfig.json @@ -6,6 +6,6 @@ "@cb/*": ["./src/*"] } }, - "exclude": ["./**/*.spec.ts", ".output"], + "exclude": ["./**/*.spec.ts", ".output", "tests"], "extends": ["./.wxt/tsconfig.json"] } From 5c1798e699e0216935d2e39e1717aaabb40cf619 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Mon, 12 Jan 2026 12:37:13 -0500 Subject: [PATCH 19/21] create factory --- .../components/panel/editor/tab/CodeTab.tsx | 3 +- extension/tests/fixture.ts | 111 +----------------- extension/tests/fixture/factory.ts | 44 ++----- extension/tests/fixture/in-room.ts | 15 ++- extension/tests/pages/content.spec.ts | 11 +- extension/tests/utils/page.ts | 51 ++++++++ 6 files changed, 83 insertions(+), 152 deletions(-) create mode 100644 extension/tests/utils/page.ts diff --git a/extension/src/components/panel/editor/tab/CodeTab.tsx b/extension/src/components/panel/editor/tab/CodeTab.tsx index 7cc45c9c..ee88d822 100644 --- a/extension/src/components/panel/editor/tab/CodeTab.tsx +++ b/extension/src/components/panel/editor/tab/CodeTab.tsx @@ -14,11 +14,10 @@ export const CodeTab: React.FC = () => { trigger={{ node: (
- +
), }} diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts index 35025e1a..21f1acd5 100644 --- a/extension/tests/fixture.ts +++ b/extension/tests/fixture.ts @@ -1,57 +1,9 @@ -import { DOM } from "@cb/constants"; +import { test as base, type BrowserContext } from "@playwright/test"; import { - test as base, - chromium, - type BrowserContext, - type Page, -} from "@playwright/test"; -import fs from "node:fs"; -import { dirname, resolve } from "path"; -import { fileURLToPath } from "url"; -import { signIn } from "./utils/auth"; -import { createRoom, joinRoom, type RoomInfo } from "./utils/room"; - -const extension = resolve( - dirname(fileURLToPath(import.meta.url)), - "../dist/chrome-mv3-dev" -); - -if (!fs.existsSync(extension)) { - throw new Error(`Invalid path ${extension}`); -} - -async function createExtensionContext(): Promise { - const context = await chromium.launchPersistentContext("", { - headless: false, - channel: "chromium", - args: [ - `--disable-extensions-except=${extension}`, - `--load-extension=${extension}`, - ], - permissions: ["clipboard-read", "clipboard-write", "local-network-access"], - }); - return context; -} - -async function getExtensionId(context: BrowserContext): Promise { - let [serviceWorker] = context.serviceWorkers(); - if (!serviceWorker) - serviceWorker = await context.waitForEvent("serviceworker"); - return serviceWorker.url().split("/")[2]; -} - -async function setupPage(page: Page): Promise { - page.on("console", (msg) => { - console.log("Received message from page", msg.text(), msg.type()); - }); - await page.goto("https://leetcode.com/problems/two-sum", { - waitUntil: "domcontentloaded", - }); - await page.waitForSelector(DOM.LEETCODE_ROOT_ID, { - state: "visible", - timeout: 30_000, - }); -} + createExtensionContext, + getExtensionId, + setupPage, +} from "./utils/page"; export const test = base.extend<{ context: BrowserContext; @@ -73,57 +25,4 @@ export const test = base.extend<{ }, }); -export interface User { - page: Page; - email: string; - extensionId: string; - context: BrowserContext; - room?: RoomInfo; -} - -export const twoUserRoomTest = test.extend<{ - user1: User; - user2: User; -}>({ - user1: async ({ page, context, extensionId }, use) => { - const email = `user1-${Date.now()}@test.com`; - await signIn(page, email); - const room = await createRoom(page); - const user1: User = { - email, - page, - context, - extensionId, - room, - }; - await use(user1); - }, - user2: async ({ user1 }, use) => { - if (!user1.room) { - throw new Error("user1 must have a room for user2 to join"); - } - - const user2Context = await createExtensionContext(); - const user2Page = user2Context.pages()[0] || (await user2Context.newPage()); - await setupPage(user2Page); - const user2Email = `user2-${Date.now()}@test.com`; - - await signIn(user2Page, user2Email); - await joinRoom(user2Page, user1.room.id); - - const user2ExtensionId = await getExtensionId(user2Context); - - const user2: User = { - email: user2Email, - page: user2Page, - context: user2Context, - extensionId: user2ExtensionId, - room: user1.room, - }; - - await use(user2); - await user2Context.close(); - }, -}); - export const expect = test.expect; diff --git a/extension/tests/fixture/factory.ts b/extension/tests/fixture/factory.ts index 12fa7f20..7207911b 100644 --- a/extension/tests/fixture/factory.ts +++ b/extension/tests/fixture/factory.ts @@ -1,19 +1,6 @@ -import { DOM } from "@cb/constants"; import { test as base, BrowserContext, chromium, Page } from "@playwright/test"; import { signIn } from "@tests/utils/auth"; -import fs from "node:fs"; -import { dirname, resolve } from "path"; -import { fileURLToPath } from "url"; - -const extension = resolve( - dirname(fileURLToPath(import.meta.url)), - "../../dist/chrome-mv3-dev" -); - -if (!fs.existsSync(extension)) { - throw new Error(`Invalid path ${extension}`); -} - +import { getExtensionId, getExtensionPath, setupPage } from "../utils/page"; export interface UserPage { page: Page; email: string; @@ -24,26 +11,14 @@ export interface UserPage { interface PlayWrightPageFactory { instantiate: (email: string) => Promise; } - -const setupPage = async (page: Page) => { - page.on("console", (msg) => { - console.log("Received message from page", msg.text(), msg.type()); - }); - await page.goto("https://leetcode.com/problems/two-sum", { - waitUntil: "domcontentloaded", - }); - await page.waitForSelector(DOM.LEETCODE_ROOT_ID, { - state: "visible", - timeout: 30_000, - }); -}; +const extension = getExtensionPath(); export const factory = base.extend<{ pageCreator: PlayWrightPageFactory }>({ // eslint-disable-next-line no-empty-pattern pageCreator: async ({}, use) => { - let context: BrowserContext | undefined; + const contexts: BrowserContext[] = []; const instantiate: PlayWrightPageFactory["instantiate"] = async (email) => { - context = await chromium.launchPersistentContext("", { + const context = await chromium.launchPersistentContext("", { headless: false, channel: "chromium", args: [ @@ -56,12 +31,9 @@ export const factory = base.extend<{ pageCreator: PlayWrightPageFactory }>({ "local-network-access", ], }); + contexts.push(context); - let [serviceWorker] = context.serviceWorkers(); - if (!serviceWorker) - serviceWorker = await context.waitForEvent("serviceworker"); - const extensionId = serviceWorker.url().split("/")[2]; - + const extensionId = await getExtensionId(context); const page = context.pages()[0] ?? (await context.newPage()); await setupPage(page); await signIn(page, email); @@ -71,8 +43,6 @@ export const factory = base.extend<{ pageCreator: PlayWrightPageFactory }>({ await use({ instantiate }); - if (context != undefined) { - context.close(); - } + await Promise.all(contexts.map((ctx) => ctx.close())); }, }); diff --git a/extension/tests/fixture/in-room.ts b/extension/tests/fixture/in-room.ts index 4b5247db..31567068 100644 --- a/extension/tests/fixture/in-room.ts +++ b/extension/tests/fixture/in-room.ts @@ -5,7 +5,17 @@ interface UserInRoomPage extends UserPage { room: RoomInfo; } -export const inRoomTest = factory.extend<{ +export const singleUserTest = factory.extend<{ + user: UserInRoomPage; +}>({ + user: async ({ pageCreator }, use) => { + const user = await pageCreator.instantiate("user@test.com"); + const room = await createRoom(user.page); + await use({ ...user, room }); + }, +}); + +export const twoUserTest = factory.extend<{ user1: UserInRoomPage; user2: UserInRoomPage; }>({ @@ -22,4 +32,5 @@ export const inRoomTest = factory.extend<{ }, }); -export const inRoomExpect = inRoomTest.expect; +export const twoUserExpect = twoUserTest.expect; +export const singleUserExpect = singleUserTest.expect; diff --git a/extension/tests/pages/content.spec.ts b/extension/tests/pages/content.spec.ts index 38044c2f..66edfdd6 100644 --- a/extension/tests/pages/content.spec.ts +++ b/extension/tests/pages/content.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from "@tests/fixture"; -import { inRoomExpect, inRoomTest } from "@tests/fixture/in-room"; +import { twoUserExpect, twoUserTest } from "@tests/fixture/in-room"; test("Content script is mounted", async ({ page }) => { await expect(page.getByText("CodeBuddy").first()).toBeVisible({ @@ -14,14 +14,15 @@ public: } };`; -inRoomTest("User1 can copy code from User2", async ({ user1 }) => { +twoUserTest("User1 can copy code from User2", async ({ user1, user2 }) => { await user1.page.getByRole("tab", { name: /Code/i }).click(); await user1.page.getByTestId("toggle-code-visibility").click(); - await user1.page.getByTestId("copy-code").click(); - await inRoomExpect(async () => { + await user1.page.getByTestId("copy-code").click({ timeout: 30000 }); + + await twoUserExpect(async () => { const copiedCode = await user1.page.evaluate(() => navigator.clipboard.readText() ); - inRoomExpect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); + twoUserExpect(copiedCode.trim()).toBe(EXPECTED_CPP_CODE.trim()); }).toPass(); }); diff --git a/extension/tests/utils/page.ts b/extension/tests/utils/page.ts new file mode 100644 index 00000000..9ec58a73 --- /dev/null +++ b/extension/tests/utils/page.ts @@ -0,0 +1,51 @@ +import { DOM } from "@cb/constants"; +import { chromium, type BrowserContext, type Page } from "@playwright/test"; +import fs from "node:fs"; +import { dirname, resolve } from "path"; +import { fileURLToPath } from "url"; + +const extension = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../dist/chrome-mv3-dev" +); + +if (!fs.existsSync(extension)) { + throw new Error(`Invalid path ${extension}`); +} + +export function getExtensionPath(): string { + return extension; +} + +export async function createExtensionContext(): Promise { + const context = await chromium.launchPersistentContext("", { + headless: false, + channel: "chromium", + args: [ + `--disable-extensions-except=${extension}`, + `--load-extension=${extension}`, + ], + permissions: ["clipboard-read", "clipboard-write", "local-network-access"], + }); + return context; +} + +export async function getExtensionId(context: BrowserContext): Promise { + let [serviceWorker] = context.serviceWorkers(); + if (!serviceWorker) + serviceWorker = await context.waitForEvent("serviceworker"); + return serviceWorker.url().split("/")[2]; +} + +export async function setupPage(page: Page): Promise { + page.on("console", (msg) => { + console.log("Received message from page", msg.text(), msg.type()); + }); + await page.goto("https://leetcode.com/problems/two-sum", { + waitUntil: "domcontentloaded", + }); + await page.waitForSelector(DOM.LEETCODE_ROOT_ID, { + state: "visible", + timeout: 30_000, + }); +} From d228758a79fcff8c31284a1b6ce3782db42bf354 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Tue, 13 Jan 2026 00:27:46 -0500 Subject: [PATCH 20/21] remove fixture.ts --- extension/tests/fixture.ts | 28 ------------------ extension/tests/fixture/factory.ts | 3 +- extension/tests/pages/content.spec.ts | 4 +-- .../tests/scripts/append-test-case.spec.ts | 2 +- extension/tests/scripts/router.spec.ts | 2 +- extension/tests/utils/page.ts | 29 ++++++++++++++++++- 6 files changed, 34 insertions(+), 34 deletions(-) delete mode 100644 extension/tests/fixture.ts diff --git a/extension/tests/fixture.ts b/extension/tests/fixture.ts deleted file mode 100644 index 21f1acd5..00000000 --- a/extension/tests/fixture.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { test as base, type BrowserContext } from "@playwright/test"; -import { - createExtensionContext, - getExtensionId, - setupPage, -} from "./utils/page"; - -export const test = base.extend<{ - context: BrowserContext; - extensionId: string; -}>({ - // eslint-disable-next-line no-empty-pattern - context: async ({}, use) => { - const context = await createExtensionContext(); - await use(context); - await context.close(); - }, - extensionId: async ({ context }, use) => { - const extensionId = await getExtensionId(context); - await use(extensionId); - }, - page: async ({ page }, use) => { - await setupPage(page); - await use(page); - }, -}); - -export const expect = test.expect; diff --git a/extension/tests/fixture/factory.ts b/extension/tests/fixture/factory.ts index 7207911b..4d1d92dc 100644 --- a/extension/tests/fixture/factory.ts +++ b/extension/tests/fixture/factory.ts @@ -1,6 +1,7 @@ import { test as base, BrowserContext, chromium, Page } from "@playwright/test"; import { signIn } from "@tests/utils/auth"; -import { getExtensionId, getExtensionPath, setupPage } from "../utils/page"; +import { getExtensionId, getExtensionPath, setupPage } from "@tests/utils/page"; + export interface UserPage { page: Page; email: string; diff --git a/extension/tests/pages/content.spec.ts b/extension/tests/pages/content.spec.ts index 66edfdd6..edbafb68 100644 --- a/extension/tests/pages/content.spec.ts +++ b/extension/tests/pages/content.spec.ts @@ -1,5 +1,5 @@ -import { expect, test } from "@tests/fixture"; import { twoUserExpect, twoUserTest } from "@tests/fixture/in-room"; +import { expect, test } from "@tests/utils/page"; test("Content script is mounted", async ({ page }) => { await expect(page.getByText("CodeBuddy").first()).toBeVisible({ @@ -17,7 +17,7 @@ public: twoUserTest("User1 can copy code from User2", async ({ user1, user2 }) => { await user1.page.getByRole("tab", { name: /Code/i }).click(); await user1.page.getByTestId("toggle-code-visibility").click(); - await user1.page.getByTestId("copy-code").click({ timeout: 30000 }); + await user1.page.getByTestId("copy-code").click({ timeout: 30_000 }); await twoUserExpect(async () => { const copiedCode = await user1.page.evaluate(() => diff --git a/extension/tests/scripts/append-test-case.spec.ts b/extension/tests/scripts/append-test-case.spec.ts index f8be9508..6e1a48a7 100644 --- a/extension/tests/scripts/append-test-case.spec.ts +++ b/extension/tests/scripts/append-test-case.spec.ts @@ -1,5 +1,5 @@ import { WindowMessage } from "@cb/types"; -import { expect, test } from "@tests/fixture"; +import { expect, test } from "@tests/utils/page"; test("Copy test", async ({ page }) => { const testValues = ["[3,2,4]", "9"]; diff --git a/extension/tests/scripts/router.spec.ts b/extension/tests/scripts/router.spec.ts index 6985c51c..b6cc31c6 100644 --- a/extension/tests/scripts/router.spec.ts +++ b/extension/tests/scripts/router.spec.ts @@ -1,6 +1,6 @@ import { WindowMessage } from "@cb/types"; import { getNormalizedUrl } from "@cb/utils"; -import { expect, test } from "@tests/fixture"; +import { expect, test } from "@tests/utils/page"; test("Navigate to different problem", async ({ page }) => { const navigateTo = "https://leetcode.com/problems/add-two-numbers"; diff --git a/extension/tests/utils/page.ts b/extension/tests/utils/page.ts index 9ec58a73..41aa38e0 100644 --- a/extension/tests/utils/page.ts +++ b/extension/tests/utils/page.ts @@ -1,5 +1,10 @@ import { DOM } from "@cb/constants"; -import { chromium, type BrowserContext, type Page } from "@playwright/test"; +import { + test as base, + chromium, + type BrowserContext, + type Page, +} from "@playwright/test"; import fs from "node:fs"; import { dirname, resolve } from "path"; import { fileURLToPath } from "url"; @@ -49,3 +54,25 @@ export async function setupPage(page: Page): Promise { timeout: 30_000, }); } + +export const test = base.extend<{ + context: BrowserContext; + extensionId: string; +}>({ + // eslint-disable-next-line no-empty-pattern + context: async ({}, use) => { + const context = await createExtensionContext(); + await use(context); + await context.close(); + }, + extensionId: async ({ context }, use) => { + const extensionId = await getExtensionId(context); + await use(extensionId); + }, + page: async ({ page }, use) => { + await setupPage(page); + await use(page); + }, +}); + +export const expect = test.expect; From 70ea4875a72edce03f7ab304d9fe66ba9b8195b0 Mon Sep 17 00:00:00 2001 From: Daho Mita Date: Tue, 13 Jan 2026 19:26:25 -0500 Subject: [PATCH 21/21] remove test and test.expect --- extension/tests/pages/content.spec.ts | 7 --- .../tests/scripts/append-test-case.spec.ts | 44 ------------------- extension/tests/scripts/router.spec.ts | 20 --------- extension/tests/utils/page.ts | 29 +----------- 4 files changed, 1 insertion(+), 99 deletions(-) delete mode 100644 extension/tests/scripts/append-test-case.spec.ts delete mode 100644 extension/tests/scripts/router.spec.ts diff --git a/extension/tests/pages/content.spec.ts b/extension/tests/pages/content.spec.ts index edbafb68..0f264c86 100644 --- a/extension/tests/pages/content.spec.ts +++ b/extension/tests/pages/content.spec.ts @@ -1,11 +1,4 @@ import { twoUserExpect, twoUserTest } from "@tests/fixture/in-room"; -import { expect, test } from "@tests/utils/page"; - -test("Content script is mounted", async ({ page }) => { - await expect(page.getByText("CodeBuddy").first()).toBeVisible({ - timeout: 30_000, - }); -}); const EXPECTED_CPP_CODE = `class Solution { public: diff --git a/extension/tests/scripts/append-test-case.spec.ts b/extension/tests/scripts/append-test-case.spec.ts deleted file mode 100644 index 6e1a48a7..00000000 --- a/extension/tests/scripts/append-test-case.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { WindowMessage } from "@cb/types"; -import { expect, test } from "@tests/utils/page"; - -test("Copy test", async ({ page }) => { - const testValues = ["[3,2,4]", "9"]; - - await page.waitForSelector('button[data-e2e-locator="console-testcase-tag"]'); - - const initialCount = await page.$$eval( - 'button[data-e2e-locator="console-testcase-tag"]', - (buttons) => buttons.length - ); - - await expect(async () => { - await page.evaluate((values) => { - const message: WindowMessage = { - action: "appendTestCaseToLeetCode", - testValues: values, - }; - window.postMessage(message, "*"); - }, testValues); - const buttons = await page.$$( - 'button[data-e2e-locator="console-testcase-tag"]' - ); - expect(buttons.length).toBe(initialCount + 1); - }).toPass(); - - const buttons = page - .locator('button[data-e2e-locator="console-testcase-tag"]') - .last() - .click({ force: true }); - - await expect(async () => { - const inputs = await page.$$( - 'div[data-e2e-locator="console-testcase-input"][contenteditable="true"]' - ); - expect(inputs.length).toBe(testValues.length); - - for (let i = 0; i < inputs.length; i++) { - const text = await inputs[i].textContent(); - expect(text?.trim()).toBe(testValues[i]); - } - }).toPass(); -}); diff --git a/extension/tests/scripts/router.spec.ts b/extension/tests/scripts/router.spec.ts deleted file mode 100644 index b6cc31c6..00000000 --- a/extension/tests/scripts/router.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { WindowMessage } from "@cb/types"; -import { getNormalizedUrl } from "@cb/utils"; -import { expect, test } from "@tests/utils/page"; - -test("Navigate to different problem", async ({ page }) => { - const navigateTo = "https://leetcode.com/problems/add-two-numbers"; - expect(getNormalizedUrl(page.url())).toMatch( - "https://leetcode.com/problems/two-sum" - ); - await expect(async () => { - await page.evaluate((url) => { - const navigate: WindowMessage = { - action: "navigate", - url, - }; - window.postMessage(navigate); - }, navigateTo); - expect(getNormalizedUrl(page.url())).toBe(navigateTo); - }).toPass(); -}); diff --git a/extension/tests/utils/page.ts b/extension/tests/utils/page.ts index 41aa38e0..9ec58a73 100644 --- a/extension/tests/utils/page.ts +++ b/extension/tests/utils/page.ts @@ -1,10 +1,5 @@ import { DOM } from "@cb/constants"; -import { - test as base, - chromium, - type BrowserContext, - type Page, -} from "@playwright/test"; +import { chromium, type BrowserContext, type Page } from "@playwright/test"; import fs from "node:fs"; import { dirname, resolve } from "path"; import { fileURLToPath } from "url"; @@ -54,25 +49,3 @@ export async function setupPage(page: Page): Promise { timeout: 30_000, }); } - -export const test = base.extend<{ - context: BrowserContext; - extensionId: string; -}>({ - // eslint-disable-next-line no-empty-pattern - context: async ({}, use) => { - const context = await createExtensionContext(); - await use(context); - await context.close(); - }, - extensionId: async ({ context }, use) => { - const extensionId = await getExtensionId(context); - await use(extensionId); - }, - page: async ({ page }, use) => { - await setupPage(page); - await use(page); - }, -}); - -export const expect = test.expect;