diff options
| author | Filip <[email protected]> | 2026-02-04 00:45:49 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-03 17:45:49 -0600 |
| commit | acac05f22eb3c23eebea3c13ed0a0f5495425384 (patch) | |
| tree | d189ec0c52436b3ed91b68f92f1a8365d4a43b7c /packages/app/e2e/projects | |
| parent | b5a4671c648fd428425f759f9d487754b425dc75 (diff) | |
| download | opencode-acac05f22eb3c23eebea3c13ed0a0f5495425384.tar.gz opencode-acac05f22eb3c23eebea3c13ed0a0f5495425384.zip | |
refactor(e2e): faster tests (#12021)
Diffstat (limited to 'packages/app/e2e/projects')
| -rw-r--r-- | packages/app/e2e/projects/project-edit.spec.ts | 73 | ||||
| -rw-r--r-- | packages/app/e2e/projects/projects-close.spec.ts | 92 | ||||
| -rw-r--r-- | packages/app/e2e/projects/projects-switch.spec.ts | 33 | ||||
| -rw-r--r-- | packages/app/e2e/projects/workspaces.spec.ts | 270 |
4 files changed, 208 insertions, 260 deletions
diff --git a/packages/app/e2e/projects/project-edit.spec.ts b/packages/app/e2e/projects/project-edit.spec.ts index 772c25951..4a286fea7 100644 --- a/packages/app/e2e/projects/project-edit.spec.ts +++ b/packages/app/e2e/projects/project-edit.spec.ts @@ -1,52 +1,53 @@ import { test, expect } from "../fixtures" import { openSidebar } from "../actions" -test("dialog edit project updates name and startup script", async ({ page, gotoSession }) => { - await gotoSession() +test("dialog edit project updates name and startup script", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) - await openSidebar(page) + await withProject(async () => { + await openSidebar(page) - const open = async () => { - const header = page.locator(".group\\/project").first() - await header.hover() - const trigger = header.getByRole("button", { name: "More options" }).first() - await expect(trigger).toBeVisible() - await trigger.click({ force: true }) - - const menu = page.locator('[data-component="dropdown-menu-content"]').first() - await expect(menu).toBeVisible() + const open = async () => { + const header = page.locator(".group\\/project").first() + await header.hover() + const trigger = header.getByRole("button", { name: "More options" }).first() + await expect(trigger).toBeVisible() + await trigger.click({ force: true }) - const editItem = menu.getByRole("menuitem", { name: "Edit" }).first() - await expect(editItem).toBeVisible() - await editItem.click({ force: true }) + const menu = page.locator('[data-component="dropdown-menu-content"]').first() + await expect(menu).toBeVisible() - const dialog = page.getByRole("dialog") - await expect(dialog).toBeVisible() - await expect(dialog.getByRole("heading", { level: 2 })).toHaveText("Edit project") - return dialog - } + const editItem = menu.getByRole("menuitem", { name: "Edit" }).first() + await expect(editItem).toBeVisible() + await editItem.click({ force: true }) - const name = `e2e project ${Date.now()}` - const startup = `echo e2e_${Date.now()}` + const dialog = page.getByRole("dialog") + await expect(dialog).toBeVisible() + await expect(dialog.getByRole("heading", { level: 2 })).toHaveText("Edit project") + return dialog + } - const dialog = await open() + const name = `e2e project ${Date.now()}` + const startup = `echo e2e_${Date.now()}` - const nameInput = dialog.getByLabel("Name") - await nameInput.fill(name) + const dialog = await open() - const startupInput = dialog.getByLabel("Workspace startup script") - await startupInput.fill(startup) + const nameInput = dialog.getByLabel("Name") + await nameInput.fill(name) - await dialog.getByRole("button", { name: "Save" }).click() - await expect(dialog).toHaveCount(0) + const startupInput = dialog.getByLabel("Workspace startup script") + await startupInput.fill(startup) - const header = page.locator(".group\\/project").first() - await expect(header).toContainText(name) + await dialog.getByRole("button", { name: "Save" }).click() + await expect(dialog).toHaveCount(0) - const reopened = await open() - await expect(reopened.getByLabel("Name")).toHaveValue(name) - await expect(reopened.getByLabel("Workspace startup script")).toHaveValue(startup) - await reopened.getByRole("button", { name: "Cancel" }).click() - await expect(reopened).toHaveCount(0) + const header = page.locator(".group\\/project").first() + await expect(header).toContainText(name) + + const reopened = await open() + await expect(reopened.getByLabel("Name")).toHaveValue(name) + await expect(reopened.getByLabel("Workspace startup script")).toHaveValue(startup) + await reopened.getByRole("button", { name: "Cancel" }).click() + await expect(reopened).toHaveCount(0) + }) }) diff --git a/packages/app/e2e/projects/projects-close.spec.ts b/packages/app/e2e/projects/projects-close.spec.ts index bd323b90c..95768d21e 100644 --- a/packages/app/e2e/projects/projects-close.spec.ts +++ b/packages/app/e2e/projects/projects-close.spec.ts @@ -1,69 +1,73 @@ import { test, expect } from "../fixtures" -import { createTestProject, seedProjects, cleanupTestProject, openSidebar, clickMenuItem } from "../actions" +import { createTestProject, cleanupTestProject, openSidebar, clickMenuItem } from "../actions" import { projectCloseHoverSelector, projectCloseMenuSelector, projectSwitchSelector } from "../selectors" import { dirSlug } from "../utils" -test("can close a project via hover card close button", async ({ page, directory, gotoSession }) => { +test("can close a project via hover card close button", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) const other = await createTestProject() const otherSlug = dirSlug(other) - await seedProjects(page, { directory, extra: [other] }) try { - await gotoSession() - - await openSidebar(page) - - const otherButton = page.locator(projectSwitchSelector(otherSlug)).first() - await expect(otherButton).toBeVisible() - await otherButton.hover() - - const close = page.locator(projectCloseHoverSelector(otherSlug)).first() - await expect(close).toBeVisible() - await close.click() - - await expect(otherButton).toHaveCount(0) + await withProject( + async () => { + await openSidebar(page) + + const otherButton = page.locator(projectSwitchSelector(otherSlug)).first() + await expect(otherButton).toBeVisible() + await otherButton.hover() + + const close = page.locator(projectCloseHoverSelector(otherSlug)).first() + await expect(close).toBeVisible() + await close.click() + + await expect(otherButton).toHaveCount(0) + }, + { extra: [other] }, + ) } finally { await cleanupTestProject(other) } }) -test("can close a project via project header more options menu", async ({ page, directory, gotoSession }) => { +test("can close a project via project header more options menu", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) const other = await createTestProject() const otherName = other.split("/").pop() ?? other const otherSlug = dirSlug(other) - await seedProjects(page, { directory, extra: [other] }) try { - await gotoSession() - - await openSidebar(page) - - const otherButton = page.locator(projectSwitchSelector(otherSlug)).first() - await expect(otherButton).toBeVisible() - await otherButton.click() - - await expect(page).toHaveURL(new RegExp(`/${otherSlug}/session`)) - - const header = page - .locator(".group\\/project") - .filter({ has: page.locator(`[data-action="project-menu"][data-project="${otherSlug}"]`) }) - .first() - await expect(header).toContainText(otherName) - - const trigger = header.locator(`[data-action="project-menu"][data-project="${otherSlug}"]`).first() - await expect(trigger).toHaveCount(1) - await trigger.focus() - await page.keyboard.press("Enter") - - const menu = page.locator('[data-component="dropdown-menu-content"]').first() - await expect(menu).toBeVisible({ timeout: 10_000 }) - - await clickMenuItem(menu, /^Close$/i, { force: true }) - await expect(otherButton).toHaveCount(0) + await withProject( + async () => { + await openSidebar(page) + + const otherButton = page.locator(projectSwitchSelector(otherSlug)).first() + await expect(otherButton).toBeVisible() + await otherButton.click() + + await expect(page).toHaveURL(new RegExp(`/${otherSlug}/session`)) + + const header = page + .locator(".group\\/project") + .filter({ has: page.locator(`[data-action="project-menu"][data-project="${otherSlug}"]`) }) + .first() + await expect(header).toContainText(otherName) + + const trigger = header.locator(`[data-action="project-menu"][data-project="${otherSlug}"]`).first() + await expect(trigger).toHaveCount(1) + await trigger.focus() + await page.keyboard.press("Enter") + + const menu = page.locator('[data-component="dropdown-menu-content"]').first() + await expect(menu).toBeVisible({ timeout: 10_000 }) + + await clickMenuItem(menu, /^Close$/i, { force: true }) + await expect(otherButton).toHaveCount(0) + }, + { extra: [other] }, + ) } finally { await cleanupTestProject(other) } diff --git a/packages/app/e2e/projects/projects-switch.spec.ts b/packages/app/e2e/projects/projects-switch.spec.ts index 829ed8e57..a817412cd 100644 --- a/packages/app/e2e/projects/projects-switch.spec.ts +++ b/packages/app/e2e/projects/projects-switch.spec.ts @@ -1,33 +1,34 @@ import { test, expect } from "../fixtures" -import { defocus, createTestProject, seedProjects, cleanupTestProject } from "../actions" +import { defocus, createTestProject, cleanupTestProject } from "../actions" import { projectSwitchSelector } from "../selectors" import { dirSlug } from "../utils" -test("can switch between projects from sidebar", async ({ page, directory, gotoSession }) => { +test("can switch between projects from sidebar", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) const other = await createTestProject() const otherSlug = dirSlug(other) - await seedProjects(page, { directory, extra: [other] }) - try { - await gotoSession() - - await defocus(page) + await withProject( + async ({ directory }) => { + await defocus(page) - const currentSlug = dirSlug(directory) - const otherButton = page.locator(projectSwitchSelector(otherSlug)).first() - await expect(otherButton).toBeVisible() - await otherButton.click() + const currentSlug = dirSlug(directory) + const otherButton = page.locator(projectSwitchSelector(otherSlug)).first() + await expect(otherButton).toBeVisible() + await otherButton.click() - await expect(page).toHaveURL(new RegExp(`/${otherSlug}/session`)) + await expect(page).toHaveURL(new RegExp(`/${otherSlug}/session`)) - const currentButton = page.locator(projectSwitchSelector(currentSlug)).first() - await expect(currentButton).toBeVisible() - await currentButton.click() + const currentButton = page.locator(projectSwitchSelector(currentSlug)).first() + await expect(currentButton).toBeVisible() + await currentButton.click() - await expect(page).toHaveURL(new RegExp(`/${currentSlug}/session`)) + await expect(page).toHaveURL(new RegExp(`/${currentSlug}/session`)) + }, + { extra: [other] }, + ) } finally { await cleanupTestProject(other) } diff --git a/packages/app/e2e/projects/workspaces.spec.ts b/packages/app/e2e/projects/workspaces.spec.ts index 80cd63aa2..41a28e3e3 100644 --- a/packages/app/e2e/projects/workspaces.spec.ts +++ b/packages/app/e2e/projects/workspaces.spec.ts @@ -10,33 +10,20 @@ import { cleanupTestProject, clickMenuItem, confirmDialog, - createTestProject, openSidebar, openWorkspaceMenu, - seedProjects, setWorkspacesEnabled, } from "../actions" -import { inlineInputSelector, projectSwitchSelector, workspaceItemSelector } from "../selectors" -import { dirSlug } from "../utils" +import { inlineInputSelector, workspaceItemSelector } from "../selectors" function slugFromUrl(url: string) { return /\/([^/]+)\/session(?:\/|$)/.exec(url)?.[1] ?? "" } -async function setupWorkspaceTest(page: Page, directory: string, gotoSession: () => Promise<void>) { - const project = await createTestProject() - const rootSlug = dirSlug(project) - await seedProjects(page, { directory, extra: [project] }) - - await gotoSession() +async function setupWorkspaceTest(page: Page, project: { slug: string }) { + const rootSlug = project.slug await openSidebar(page) - const target = page.locator(projectSwitchSelector(rootSlug)).first() - await expect(target).toBeVisible() - await target.click() - await expect(page).toHaveURL(new RegExp(`/${rootSlug}/session`)) - - await openSidebar(page) await setWorkspacesEnabled(page, rootSlug, true) await page.getByRole("button", { name: "New workspace" }).first().click() @@ -70,25 +57,13 @@ async function setupWorkspaceTest(page: Page, directory: string, gotoSession: () ) .toBe(true) - return { project, rootSlug, slug, directory: dir } + return { rootSlug, slug, directory: dir } } -test("can enable and disable workspaces from project menu", async ({ page, directory, gotoSession }) => { +test("can enable and disable workspaces from project menu", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) - const project = await createTestProject() - const slug = dirSlug(project) - await seedProjects(page, { directory, extra: [project] }) - - try { - await gotoSession() - await openSidebar(page) - - const target = page.locator(projectSwitchSelector(slug)).first() - await expect(target).toBeVisible() - await target.click() - await expect(page).toHaveURL(new RegExp(`/${slug}/session`)) - + await withProject(async ({ slug }) => { await openSidebar(page) await expect(page.getByRole("button", { name: "New session" }).first()).toBeVisible() @@ -101,27 +76,13 @@ test("can enable and disable workspaces from project menu", async ({ page, direc await setWorkspacesEnabled(page, slug, false) await expect(page.getByRole("button", { name: "New session" }).first()).toBeVisible() await expect(page.locator(workspaceItemSelector(slug))).toHaveCount(0) - } finally { - await cleanupTestProject(project) - } + }) }) -test("can create a workspace", async ({ page, directory, gotoSession }) => { +test("can create a workspace", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) - const project = await createTestProject() - const slug = dirSlug(project) - await seedProjects(page, { directory, extra: [project] }) - - try { - await gotoSession() - await openSidebar(page) - - const target = page.locator(projectSwitchSelector(slug)).first() - await expect(target).toBeVisible() - await target.click() - await expect(page).toHaveURL(new RegExp(`/${slug}/session`)) - + await withProject(async ({ slug }) => { await openSidebar(page) await setWorkspacesEnabled(page, slug, true) @@ -162,17 +123,15 @@ test("can create a workspace", async ({ page, directory, gotoSession }) => { await expect(page.locator(workspaceItemSelector(workspaceSlug)).first()).toBeVisible() await cleanupTestProject(workspaceDir) - } finally { - await cleanupTestProject(project) - } + }) }) -test("can rename a workspace", async ({ page, directory, gotoSession }) => { +test("can rename a workspace", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) - const { project, slug } = await setupWorkspaceTest(page, directory, gotoSession) + await withProject(async (project) => { + const { slug } = await setupWorkspaceTest(page, project) - try { const rename = `e2e workspace ${Date.now()}` const menu = await openWorkspaceMenu(page, slug) await clickMenuItem(menu, /^Rename$/i, { force: true }) @@ -186,17 +145,15 @@ test("can rename a workspace", async ({ page, directory, gotoSession }) => { await input.fill(rename) await input.press("Enter") await expect(item).toContainText(rename) - } finally { - await cleanupTestProject(project) - } + }) }) -test("can reset a workspace", async ({ page, directory, sdk, gotoSession }) => { +test("can reset a workspace", async ({ page, sdk, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) - const { project, slug, directory: createdDir } = await setupWorkspaceTest(page, directory, gotoSession) + await withProject(async (project) => { + const { slug, directory: createdDir } = await setupWorkspaceTest(page, project) - try { const readme = path.join(createdDir, "README.md") const extra = path.join(createdDir, `e2e_reset_${Date.now()}.txt`) const original = await fs.readFile(readme, "utf8") @@ -250,17 +207,15 @@ test("can reset a workspace", async ({ page, directory, sdk, gotoSession }) => { .catch(() => false) }) .toBe(false) - } finally { - await cleanupTestProject(project) - } + }) }) -test("can delete a workspace", async ({ page, directory, gotoSession }) => { +test("can delete a workspace", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) - const { project, rootSlug, slug } = await setupWorkspaceTest(page, directory, gotoSession) + await withProject(async (project) => { + const { rootSlug, slug } = await setupWorkspaceTest(page, project) - try { const menu = await openWorkspaceMenu(page, slug) await clickMenuItem(menu, /^Delete$/i, { force: true }) await confirmDialog(page, /^Delete workspace$/i) @@ -268,124 +223,111 @@ test("can delete a workspace", async ({ page, directory, gotoSession }) => { await expect(page).toHaveURL(new RegExp(`/${rootSlug}/session`)) await expect(page.locator(workspaceItemSelector(slug))).toHaveCount(0) await expect(page.locator(workspaceItemSelector(rootSlug)).first()).toBeVisible() - } finally { - await cleanupTestProject(project) - } + }) }) -test("can reorder workspaces by drag and drop", async ({ page, directory, gotoSession }) => { +test("can reorder workspaces by drag and drop", async ({ page, withProject }) => { await page.setViewportSize({ width: 1400, height: 800 }) + await withProject(async ({ slug: rootSlug }) => { + const workspaces = [] as { directory: string; slug: string }[] - const project = await createTestProject() - const rootSlug = dirSlug(project) - await seedProjects(page, { directory, extra: [project] }) + const listSlugs = async () => { + const nodes = page.locator('[data-component="sidebar-nav-desktop"] [data-component="workspace-item"]') + const slugs = await nodes.evaluateAll((els) => { + return els.map((el) => el.getAttribute("data-workspace") ?? "").filter((x) => x.length > 0) + }) + return slugs + } - const workspaces = [] as { directory: string; slug: string }[] + const waitReady = async (slug: string) => { + await expect + .poll( + async () => { + const item = page.locator(workspaceItemSelector(slug)).first() + try { + await item.hover({ timeout: 500 }) + return true + } catch { + return false + } + }, + { timeout: 60_000 }, + ) + .toBe(true) + } - const listSlugs = async () => { - const nodes = page.locator('[data-component="sidebar-nav-desktop"] [data-component="workspace-item"]') - const slugs = await nodes.evaluateAll((els) => { - return els.map((el) => el.getAttribute("data-workspace") ?? "").filter((x) => x.length > 0) - }) - return slugs - } + const drag = async (from: string, to: string) => { + const src = page.locator(workspaceItemSelector(from)).first() + const dst = page.locator(workspaceItemSelector(to)).first() - const waitReady = async (slug: string) => { - await expect - .poll( - async () => { - const item = page.locator(workspaceItemSelector(slug)).first() - try { - await item.hover({ timeout: 500 }) - return true - } catch { - return false - } - }, - { timeout: 60_000 }, - ) - .toBe(true) - } + await src.scrollIntoViewIfNeeded() + await dst.scrollIntoViewIfNeeded() - const drag = async (from: string, to: string) => { - const src = page.locator(workspaceItemSelector(from)).first() - const dst = page.locator(workspaceItemSelector(to)).first() + const a = await src.boundingBox() + const b = await dst.boundingBox() + if (!a || !b) throw new Error("Failed to resolve workspace drag bounds") - await src.scrollIntoViewIfNeeded() - await dst.scrollIntoViewIfNeeded() + await page.mouse.move(a.x + a.width / 2, a.y + a.height / 2) + await page.mouse.down() + await page.mouse.move(b.x + b.width / 2, b.y + b.height / 2, { steps: 12 }) + await page.mouse.up() + } - const a = await src.boundingBox() - const b = await dst.boundingBox() - if (!a || !b) throw new Error("Failed to resolve workspace drag bounds") + try { + await openSidebar(page) - await page.mouse.move(a.x + a.width / 2, a.y + a.height / 2) - await page.mouse.down() - await page.mouse.move(b.x + b.width / 2, b.y + b.height / 2, { steps: 12 }) - await page.mouse.up() - } + await setWorkspacesEnabled(page, rootSlug, true) + + for (const _ of [0, 1]) { + const prev = slugFromUrl(page.url()) + await page.getByRole("button", { name: "New workspace" }).first().click() + await expect + .poll( + () => { + const slug = slugFromUrl(page.url()) + return slug.length > 0 && slug !== rootSlug && slug !== prev + }, + { timeout: 45_000 }, + ) + .toBe(true) - try { - await gotoSession() - await openSidebar(page) + const slug = slugFromUrl(page.url()) + const dir = base64Decode(slug) + workspaces.push({ slug, directory: dir }) - const target = page.locator(projectSwitchSelector(rootSlug)).first() - await expect(target).toBeVisible() - await target.click() - await expect(page).toHaveURL(new RegExp(`/${rootSlug}/session`)) + await openSidebar(page) + } - await openSidebar(page) - await setWorkspacesEnabled(page, rootSlug, true) + if (workspaces.length !== 2) throw new Error("Expected two created workspaces") - for (const _ of [0, 1]) { - const prev = slugFromUrl(page.url()) - await page.getByRole("button", { name: "New workspace" }).first().click() - await expect - .poll( - () => { - const slug = slugFromUrl(page.url()) - return slug.length > 0 && slug !== rootSlug && slug !== prev - }, - { timeout: 45_000 }, - ) - .toBe(true) + const a = workspaces[0].slug + const b = workspaces[1].slug - const slug = slugFromUrl(page.url()) - const dir = base64Decode(slug) - workspaces.push({ slug, directory: dir }) + await waitReady(a) + await waitReady(b) - await openSidebar(page) - } + const list = async () => { + const slugs = await listSlugs() + return slugs.filter((s) => s !== rootSlug && (s === a || s === b)).slice(0, 2) + } - if (workspaces.length !== 2) throw new Error("Expected two created workspaces") + await expect + .poll(async () => { + const slugs = await list() + return slugs.length === 2 + }) + .toBe(true) - const a = workspaces[0].slug - const b = workspaces[1].slug + const before = await list() + const from = before[1] + const to = before[0] + if (!from || !to) throw new Error("Failed to resolve initial workspace order") - await waitReady(a) - await waitReady(b) + await drag(from, to) - const list = async () => { - const slugs = await listSlugs() - return slugs.filter((s) => s !== rootSlug && (s === a || s === b)).slice(0, 2) + await expect.poll(async () => await list()).toEqual([from, to]) + } finally { + await Promise.all(workspaces.map((w) => cleanupTestProject(w.directory))) } - - await expect - .poll(async () => { - const slugs = await list() - return slugs.length === 2 - }) - .toBe(true) - - const before = await list() - const from = before[1] - const to = before[0] - if (!from || !to) throw new Error("Failed to resolve initial workspace order") - - await drag(from, to) - - await expect.poll(async () => await list()).toEqual([from, to]) - } finally { - await Promise.all(workspaces.map((w) => cleanupTestProject(w.directory))) - await cleanupTestProject(project) - } + }) }) |
