summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFilip <[email protected]>2026-02-04 00:45:49 +0100
committerGitHub <[email protected]>2026-02-03 17:45:49 -0600
commitacac05f22eb3c23eebea3c13ed0a0f5495425384 (patch)
treed189ec0c52436b3ed91b68f92f1a8365d4a43b7c
parentb5a4671c648fd428425f759f9d487754b425dc75 (diff)
downloadopencode-acac05f22eb3c23eebea3c13ed0a0f5495425384.tar.gz
opencode-acac05f22eb3c23eebea3c13ed0a0f5495425384.zip
refactor(e2e): faster tests (#12021)
-rw-r--r--packages/app/e2e/fixtures.ts57
-rw-r--r--packages/app/e2e/projects/project-edit.spec.ts73
-rw-r--r--packages/app/e2e/projects/projects-close.spec.ts92
-rw-r--r--packages/app/e2e/projects/projects-switch.spec.ts33
-rw-r--r--packages/app/e2e/projects/workspaces.spec.ts270
-rw-r--r--packages/app/playwright.config.ts4
6 files changed, 253 insertions, 276 deletions
diff --git a/packages/app/e2e/fixtures.ts b/packages/app/e2e/fixtures.ts
index bace8cd59..ea41ed851 100644
--- a/packages/app/e2e/fixtures.ts
+++ b/packages/app/e2e/fixtures.ts
@@ -1,5 +1,5 @@
-import { test as base, expect } from "@playwright/test"
-import { seedProjects } from "./actions"
+import { test as base, expect, type Page } from "@playwright/test"
+import { cleanupTestProject, createTestProject, seedProjects } from "./actions"
import { promptSelector } from "./selectors"
import { createSdk, dirSlug, getWorktree, sessionPath } from "./utils"
@@ -8,6 +8,14 @@ export const settingsKey = "settings.v3"
type TestFixtures = {
sdk: ReturnType<typeof createSdk>
gotoSession: (sessionID?: string) => Promise<void>
+ withProject: <T>(
+ callback: (project: {
+ directory: string
+ slug: string
+ gotoSession: (sessionID?: string) => Promise<void>
+ }) => Promise<T>,
+ options?: { extra?: string[] },
+ ) => Promise<T>
}
type WorkerFixtures = {
@@ -33,17 +41,7 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
await use(createSdk(directory))
},
gotoSession: async ({ page, directory }, use) => {
- await seedProjects(page, { directory })
- await page.addInitScript(() => {
- localStorage.setItem(
- "opencode.global.dat:model",
- JSON.stringify({
- recent: [{ providerID: "opencode", modelID: "big-pickle" }],
- user: [],
- variant: {},
- }),
- )
- })
+ await seedStorage(page, { directory })
const gotoSession = async (sessionID?: string) => {
await page.goto(sessionPath(directory, sessionID))
@@ -51,6 +49,39 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
}
await use(gotoSession)
},
+ withProject: async ({ page }, use) => {
+ await use(async (callback, options) => {
+ const directory = await createTestProject()
+ const slug = dirSlug(directory)
+ await seedStorage(page, { directory, extra: options?.extra })
+
+ const gotoSession = async (sessionID?: string) => {
+ await page.goto(sessionPath(directory, sessionID))
+ await expect(page.locator(promptSelector)).toBeVisible()
+ }
+
+ try {
+ await gotoSession()
+ return await callback({ directory, slug, gotoSession })
+ } finally {
+ await cleanupTestProject(directory)
+ }
+ })
+ },
})
+async function seedStorage(page: Page, input: { directory: string; extra?: string[] }) {
+ await seedProjects(page, input)
+ await page.addInitScript(() => {
+ localStorage.setItem(
+ "opencode.global.dat:model",
+ JSON.stringify({
+ recent: [{ providerID: "opencode", modelID: "big-pickle" }],
+ user: [],
+ variant: {},
+ }),
+ )
+ })
+}
+
export { expect }
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)
- }
+ })
})
diff --git a/packages/app/playwright.config.ts b/packages/app/playwright.config.ts
index 57bf86b5a..10819e69f 100644
--- a/packages/app/playwright.config.ts
+++ b/packages/app/playwright.config.ts
@@ -6,7 +6,6 @@ const serverHost = process.env.PLAYWRIGHT_SERVER_HOST ?? "localhost"
const serverPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
const command = `bun run dev -- --host 0.0.0.0 --port ${port}`
const reuse = !process.env.CI
-const win = process.platform === "win32"
export default defineConfig({
testDir: "./e2e",
@@ -15,8 +14,7 @@ export default defineConfig({
expect: {
timeout: 10_000,
},
- fullyParallel: !win,
- workers: win ? 1 : undefined,
+ fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [["html", { outputFolder: "e2e/playwright-report", open: "never" }], ["line"]],