summaryrefslogtreecommitdiffhomepage
path: root/packages/app/e2e/session
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-19 15:02:45 -0600
committerGitHub <[email protected]>2026-02-19 15:02:45 -0600
commit49cc872c4415f081b4208d16fd0d85e425a75eed (patch)
treeed78239c2a2bb941c5008f378a00592f5b10717a /packages/app/e2e/session
parentf8dad0ae170acb9667d9402c162f7c29980373c1 (diff)
downloadopencode-49cc872c4415f081b4208d16fd0d85e425a75eed.tar.gz
opencode-49cc872c4415f081b4208d16fd0d85e425a75eed.zip
chore: refactor composer/dock components (#14328)
Diffstat (limited to 'packages/app/e2e/session')
-rw-r--r--packages/app/e2e/session/session-composer-dock.spec.ts207
1 files changed, 207 insertions, 0 deletions
diff --git a/packages/app/e2e/session/session-composer-dock.spec.ts b/packages/app/e2e/session/session-composer-dock.spec.ts
new file mode 100644
index 000000000..6bf7714a6
--- /dev/null
+++ b/packages/app/e2e/session/session-composer-dock.spec.ts
@@ -0,0 +1,207 @@
+import { test, expect } from "../fixtures"
+import { clearSessionDockSeed, seedSessionPermission, seedSessionQuestion, seedSessionTodos } from "../actions"
+import {
+ permissionDockSelector,
+ promptSelector,
+ questionDockSelector,
+ sessionComposerDockSelector,
+ sessionTodoDockSelector,
+ sessionTodoListSelector,
+ sessionTodoToggleButtonSelector,
+} from "../selectors"
+
+type Sdk = Parameters<typeof clearSessionDockSeed>[0]
+
+async function withDockSession<T>(sdk: Sdk, title: string, fn: (session: { id: string; title: string }) => Promise<T>) {
+ const session = await sdk.session.create({ title }).then((r) => r.data)
+ if (!session?.id) throw new Error("Session create did not return an id")
+ return fn(session)
+}
+
+test.setTimeout(120_000)
+
+async function withDockSeed<T>(sdk: Sdk, sessionID: string, fn: () => Promise<T>) {
+ try {
+ return await fn()
+ } finally {
+ await clearSessionDockSeed(sdk, sessionID).catch(() => undefined)
+ }
+}
+
+test("default dock shows prompt input", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock default", async (session) => {
+ await gotoSession(session.id)
+
+ await expect(page.locator(sessionComposerDockSelector)).toBeVisible()
+ await expect(page.locator(promptSelector)).toBeVisible()
+ await expect(page.locator(questionDockSelector)).toHaveCount(0)
+ await expect(page.locator(permissionDockSelector)).toHaveCount(0)
+
+ await page.locator(promptSelector).click()
+ await expect(page.locator(promptSelector)).toBeFocused()
+ })
+})
+
+test("blocked question flow unblocks after submit", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock question", async (session) => {
+ await withDockSeed(sdk, session.id, async () => {
+ await gotoSession(session.id)
+
+ await seedSessionQuestion(sdk, {
+ sessionID: session.id,
+ questions: [
+ {
+ header: "Need input",
+ question: "Pick one option",
+ options: [
+ { label: "Continue", description: "Continue now" },
+ { label: "Stop", description: "Stop here" },
+ ],
+ },
+ ],
+ })
+
+ const dock = page.locator(questionDockSelector)
+ await expect.poll(() => dock.count(), { timeout: 10_000 }).toBe(1)
+ await expect(page.locator(promptSelector)).toHaveCount(0)
+
+ await dock.locator('[data-slot="question-option"]').first().click()
+ await dock.getByRole("button", { name: /submit/i }).click()
+
+ await expect.poll(() => page.locator(questionDockSelector).count(), { timeout: 10_000 }).toBe(0)
+ await expect(page.locator(promptSelector)).toBeVisible()
+ })
+ })
+})
+
+test("blocked permission flow supports allow once", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock permission once", async (session) => {
+ await withDockSeed(sdk, session.id, async () => {
+ await gotoSession(session.id)
+
+ await seedSessionPermission(sdk, {
+ sessionID: session.id,
+ permission: "bash",
+ patterns: ["README.md"],
+ description: "Need permission for command",
+ })
+
+ await expect.poll(() => page.locator(permissionDockSelector).count(), { timeout: 10_000 }).toBe(1)
+ await expect(page.locator(promptSelector)).toHaveCount(0)
+
+ await page
+ .locator(permissionDockSelector)
+ .getByRole("button", { name: /allow once/i })
+ .click()
+ await expect.poll(() => page.locator(permissionDockSelector).count(), { timeout: 10_000 }).toBe(0)
+ await expect(page.locator(promptSelector)).toBeVisible()
+ })
+ })
+})
+
+test("blocked permission flow supports reject", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock permission reject", async (session) => {
+ await withDockSeed(sdk, session.id, async () => {
+ await gotoSession(session.id)
+
+ await seedSessionPermission(sdk, {
+ sessionID: session.id,
+ permission: "bash",
+ patterns: ["REJECT.md"],
+ })
+
+ await expect.poll(() => page.locator(permissionDockSelector).count(), { timeout: 10_000 }).toBe(1)
+ await expect(page.locator(promptSelector)).toHaveCount(0)
+
+ await page.locator(permissionDockSelector).getByRole("button", { name: /deny/i }).click()
+ await expect.poll(() => page.locator(permissionDockSelector).count(), { timeout: 10_000 }).toBe(0)
+ await expect(page.locator(promptSelector)).toBeVisible()
+ })
+ })
+})
+
+test("blocked permission flow supports allow always", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock permission always", async (session) => {
+ await withDockSeed(sdk, session.id, async () => {
+ await gotoSession(session.id)
+
+ await seedSessionPermission(sdk, {
+ sessionID: session.id,
+ permission: "bash",
+ patterns: ["README.md"],
+ description: "Need permission for command",
+ })
+
+ await expect.poll(() => page.locator(permissionDockSelector).count(), { timeout: 10_000 }).toBe(1)
+ await expect(page.locator(promptSelector)).toHaveCount(0)
+
+ await page
+ .locator(permissionDockSelector)
+ .getByRole("button", { name: /allow always/i })
+ .click()
+ await expect.poll(() => page.locator(permissionDockSelector).count(), { timeout: 10_000 }).toBe(0)
+ await expect(page.locator(promptSelector)).toBeVisible()
+ })
+ })
+})
+
+test("todo dock transitions and collapse behavior", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock todo", async (session) => {
+ await withDockSeed(sdk, session.id, async () => {
+ await gotoSession(session.id)
+
+ await seedSessionTodos(sdk, {
+ sessionID: session.id,
+ todos: [
+ { content: "first task", status: "pending", priority: "high" },
+ { content: "second task", status: "in_progress", priority: "medium" },
+ ],
+ })
+
+ await expect.poll(() => page.locator(sessionTodoDockSelector).count(), { timeout: 10_000 }).toBe(1)
+ await expect(page.locator(sessionTodoListSelector)).toBeVisible()
+
+ await page.locator(sessionTodoToggleButtonSelector).click()
+ await expect(page.locator(sessionTodoListSelector)).toBeHidden()
+
+ await page.locator(sessionTodoToggleButtonSelector).click()
+ await expect(page.locator(sessionTodoListSelector)).toBeVisible()
+
+ await seedSessionTodos(sdk, {
+ sessionID: session.id,
+ todos: [
+ { content: "first task", status: "completed", priority: "high" },
+ { content: "second task", status: "cancelled", priority: "medium" },
+ ],
+ })
+
+ await expect.poll(() => page.locator(sessionTodoDockSelector).count(), { timeout: 10_000 }).toBe(0)
+ })
+ })
+})
+
+test("keyboard focus stays off prompt while blocked", async ({ page, sdk, gotoSession }) => {
+ await withDockSession(sdk, "e2e composer dock keyboard", async (session) => {
+ await withDockSeed(sdk, session.id, async () => {
+ await gotoSession(session.id)
+
+ await seedSessionQuestion(sdk, {
+ sessionID: session.id,
+ questions: [
+ {
+ header: "Need input",
+ question: "Pick one option",
+ options: [{ label: "Continue", description: "Continue now" }],
+ },
+ ],
+ })
+
+ await expect.poll(() => page.locator(questionDockSelector).count(), { timeout: 10_000 }).toBe(1)
+ await expect(page.locator(promptSelector)).toHaveCount(0)
+
+ await page.locator("main").click({ position: { x: 5, y: 5 } })
+ await page.keyboard.type("abc")
+ await expect(page.locator(promptSelector)).toHaveCount(0)
+ })
+ })
+})