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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 69 additions & 38 deletions packages/extension/e2e/hub.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,33 @@ import { OptionsPage } from "./pages/OptionsPage"

const HUB_URL = "https://ujiro99.github.io/selection-command"

const tryGetCommandId = (commandData: string | null): string => {
if (!commandData) {
throw new Error("Hub button is missing required data-command attribute")
}
let parsedCommand: unknown
try {
parsedCommand = JSON.parse(commandData)
} catch (error) {
throw new Error(
`Failed to parse data-command JSON from Hub button: ${(error as Error).message}`,
)
}
const commandId =
typeof parsedCommand === "object" &&
parsedCommand !== null &&
"id" in parsedCommand &&
typeof (parsedCommand as { id: unknown }).id === "string"
? (parsedCommand as { id: string }).id
: null
if (!commandId) {
throw new Error(
`Parsed data-command JSON does not contain a valid "id": ${commandData}`,
)
}
return commandId
}

test.describe("Command Hub", () => {
/**
* E2E-90: Verify that a PageAction command can be installed from the Hub.
Expand Down Expand Up @@ -32,18 +59,17 @@ test.describe("Command Hub", () => {
.locator('button[data-command*=\'"openMode":"pageAction"\']')
.filter({ hasNot: page.locator('[data-installed="true"]') })
.first()

const isVisible = await downloadButton.isVisible()
if (!isVisible) {
test.skip(true, "No installable PageAction commands found on hub page")
return
}

await downloadButton.waitFor({ state: "visible", timeout: 5000 })
await downloadButton.click()
await page.waitForTimeout(500)

const commandsAfter = await getCommands()
expect(commandsAfter?.length ?? 0).toBeGreaterThan(countBefore)
await expect
.poll(
async () => {
const commands = await getCommands()
return (commands?.length ?? 0) > countBefore
},
{ timeout: 5000 },
)
.toBe(true)
})

/**
Expand Down Expand Up @@ -72,21 +98,22 @@ test.describe("Command Hub", () => {
.filter({ hasNot: page.locator('[data-installed="true"]') })
.first()

const isVisible = await downloadButton.isVisible()
if (!isVisible) {
test.skip(true, "No download buttons found on hub page")
return
}
await downloadButton.waitFor({ state: "visible", timeout: 5000 })

// Get the command identifier for verification
const commandId = await downloadButton.getAttribute("data-command")
const commandData = await downloadButton.getAttribute("data-command")
tryGetCommandId(commandData)

await downloadButton.click()
await page.waitForTimeout(500)

const commandsAfter = await getCommands()
expect(commandsAfter?.length ?? 0).toBeGreaterThan(countBefore)
expect(commandId).toBeTruthy()
await expect
.poll(
async () => {
const commands = await getCommands()
return (commands?.length ?? 0) > countBefore
},
{ timeout: 5000 },
)
.toBe(true)
})

/**
Expand All @@ -107,23 +134,27 @@ test.describe("Command Hub", () => {
await page.goto(HUB_URL)
await page.waitForLoadState("domcontentloaded")

const downloadButton = page.locator("button[data-command]").first()
const isVisible = await downloadButton.isVisible()
if (!isVisible) {
test.skip(true, "No download buttons found on hub page")
return
}
// Find any available download button
const downloadButton = page
.locator('button[data-command*=\'"openMode":"popup"\']')
.filter({ hasNot: page.locator('[data-installed="true"]') })
.first()

await downloadButton.waitFor({ state: "visible", timeout: 5000 })

const commandData = await downloadButton.getAttribute("data-command")
const commandId = commandData ? JSON.parse(commandData).id : null
await downloadButton.click()
await page.waitForTimeout(500)
const commandId = tryGetCommandId(commandData)

const commandsAfterInstall = await getCommands()
const installedCommand = commandsAfterInstall?.find(
(cmd) => cmd.id === commandId,
)
expect(installedCommand).toBeDefined()
await downloadButton.click()
await expect
.poll(
async () => {
const commands = await getCommands()
return commands?.find((cmd) => cmd.id === commandId) !== undefined
},
{ timeout: 5000 },
)
.toBe(true)

// Step 2: Delete the command via settings
await optionsPage.open()
Expand All @@ -140,7 +171,7 @@ test.describe("Command Hub", () => {
const restoredButton = page.locator(
`button[data-command*='"id":"${commandId}"']`,
)
const isRestoredVisible = await restoredButton.isVisible()
expect(isRestoredVisible).toBe(true)
await restoredButton.waitFor({ state: "visible", timeout: 5000 })
expect(restoredButton).toBeVisible()
})
})
2 changes: 2 additions & 0 deletions packages/extension/e2e/page-action.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ test.describe("PageAction Commands", () => {
context,
page,
}) => {
test.skip(!!process.env.CI, "Do not run tests for external services in CI.")

await page.goto("https://www.amazon.com/")
await page.waitForLoadState("domcontentloaded")
await page.locator(".a-list-item .a-link-normal").first().click()
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/e2e/pages/OptionsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,6 @@ export class OptionsPage {
const reloadPromise = this.page.waitForLoadState("domcontentloaded")
await okButton.click()
await reloadPromise
await this.page.waitForTimeout(500)
await this.page.waitForTimeout(100)
}
Comment on lines 238 to 242
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

resetSettings() の末尾が waitForTimeout(100) になっていますが、固定スリープは環境差で再度フレークしやすいです(importSettings() は expect.poll で getCommands の反映を待っています)。

  • domcontentloaded 後に「設定が既定値へ反映されたこと」(例: getCommands の内容が変化/安定したこと、または特定UI要素の表示)を条件待ちする形に寄せてください。

Copilot uses AI. Check for mistakes.
}
4 changes: 3 additions & 1 deletion packages/extension/e2e/url-status.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { COMMAND_URLS } from "./generated-command-urls"
* The %s placeholder is replaced with "test" for each request.
*/

test.describe.skip("E2E-URL: Default command URLs return HTTP 200", () => {
test.describe("E2E-URL: Default command URLs return HTTP 200", () => {
test.skip(!!process.env.CI, "Do not run tests for external services in CI.")

for (const { title, locale, searchUrl } of COMMAND_URLS) {
const url = searchUrl.replace("%s", "test")
test(`${title} (${locale}): ${url}`, async ({ request }) => {
Expand Down
Loading