Quellcode durchsuchen

test(app): session actions (#11386)

Filip vor 2 Monaten
Ursprung
Commit
511c7abaca

+ 111 - 0
packages/app/e2e/actions.ts

@@ -4,6 +4,17 @@ import os from "node:os"
 import path from "node:path"
 import { execSync } from "node:child_process"
 import { modKey, serverUrl } from "./utils"
+import {
+  sessionItemSelector,
+  dropdownMenuTriggerSelector,
+  dropdownMenuContentSelector,
+  titlebarRightSelector,
+  popoverBodySelector,
+  listItemSelector,
+  listItemKeySelector,
+  listItemKeyStartsWithSelector,
+} from "./selectors"
+import type { createSdk } from "./utils"
 
 export async function defocus(page: Page) {
   await page.mouse.click(5, 5)
@@ -158,3 +169,103 @@ export function sessionIDFromUrl(url: string) {
   const match = /\/session\/([^/?#]+)/.exec(url)
   return match?.[1]
 }
+
+export async function hoverSessionItem(page: Page, sessionID: string) {
+  const sessionEl = page.locator(sessionItemSelector(sessionID)).first()
+  await expect(sessionEl).toBeVisible()
+  await sessionEl.hover()
+  return sessionEl
+}
+
+export async function openSessionMoreMenu(page: Page, sessionID: string) {
+  const sessionEl = await hoverSessionItem(page, sessionID)
+
+  const menuTrigger = sessionEl.locator(dropdownMenuTriggerSelector).first()
+  await expect(menuTrigger).toBeVisible()
+  await menuTrigger.click()
+
+  const menu = page.locator(dropdownMenuContentSelector).first()
+  await expect(menu).toBeVisible()
+  return menu
+}
+
+export async function clickMenuItem(menu: Locator, itemName: string | RegExp, options?: { force?: boolean }) {
+  const item = menu.getByRole("menuitem").filter({ hasText: itemName }).first()
+  await expect(item).toBeVisible()
+  await item.click({ force: options?.force })
+}
+
+export async function confirmDialog(page: Page, buttonName: string | RegExp) {
+  const dialog = page.getByRole("dialog").first()
+  await expect(dialog).toBeVisible()
+
+  const button = dialog.getByRole("button").filter({ hasText: buttonName }).first()
+  await expect(button).toBeVisible()
+  await button.click()
+}
+
+export async function openSharePopover(page: Page) {
+  const rightSection = page.locator(titlebarRightSelector)
+  const shareButton = rightSection.getByRole("button", { name: "Share" }).first()
+  await expect(shareButton).toBeVisible()
+
+  const popoverBody = page
+    .locator(popoverBodySelector)
+    .filter({ has: page.getByRole("button", { name: /^(Publish|Unpublish)$/ }) })
+    .first()
+
+  const opened = await popoverBody
+    .isVisible()
+    .then((x) => x)
+    .catch(() => false)
+
+  if (!opened) {
+    await shareButton.click()
+    await expect(popoverBody).toBeVisible()
+  }
+  return { rightSection, popoverBody }
+}
+
+export async function clickPopoverButton(page: Page, buttonName: string | RegExp) {
+  const button = page.getByRole("button").filter({ hasText: buttonName }).first()
+  await expect(button).toBeVisible()
+  await button.click()
+}
+
+export async function clickListItem(
+  container: Locator | Page,
+  filter: string | RegExp | { key?: string; text?: string | RegExp; keyStartsWith?: string },
+): Promise<Locator> {
+  let item: Locator
+
+  if (typeof filter === "string" || filter instanceof RegExp) {
+    item = container.locator(listItemSelector).filter({ hasText: filter }).first()
+  } else if (filter.keyStartsWith) {
+    item = container.locator(listItemKeyStartsWithSelector(filter.keyStartsWith)).first()
+  } else if (filter.key) {
+    item = container.locator(listItemKeySelector(filter.key)).first()
+  } else if (filter.text) {
+    item = container.locator(listItemSelector).filter({ hasText: filter.text }).first()
+  } else {
+    throw new Error("Invalid filter provided to clickListItem")
+  }
+
+  await expect(item).toBeVisible()
+  await item.click()
+  return item
+}
+
+export async function withSession<T>(
+  sdk: ReturnType<typeof createSdk>,
+  title: string,
+  callback: (session: { id: string; title: string }) => Promise<T>,
+): 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")
+
+  try {
+    return await callback(session)
+  } finally {
+    await sdk.session.delete({ sessionID: session.id }).catch(() => undefined)
+  }
+}

+ 9 - 21
packages/app/e2e/app/server-default.spec.ts

@@ -1,5 +1,6 @@
 import { test, expect } from "../fixtures"
 import { serverName, serverUrl } from "../utils"
+import { clickListItem, closeDialog, clickMenuItem } from "../actions"
 
 const DEFAULT_SERVER_URL_KEY = "opencode.settings.dat:defaultServerUrl"
 
@@ -33,31 +34,18 @@ test("can set a default server on web", async ({ page, gotoSession }) => {
   const row = dialog.locator('[data-slot="list-item"]').filter({ hasText: serverName }).first()
   await expect(row).toBeVisible()
 
-  const menu = row.locator('[data-component="icon-button"]').last()
-  await menu.click()
-  await page.getByRole("menuitem", { name: "Set as default" }).click()
+  const menuTrigger = row.locator('[data-slot="dropdown-menu-trigger"]').first()
+  await expect(menuTrigger).toBeVisible()
+  await menuTrigger.click({ force: true })
+
+  const menu = page.locator('[data-component="dropdown-menu-content"]').first()
+  await expect(menu).toBeVisible()
+  await clickMenuItem(menu, /set as default/i)
 
   await expect.poll(() => page.evaluate((key) => localStorage.getItem(key), DEFAULT_SERVER_URL_KEY)).toBe(serverUrl)
   await expect(row.getByText("Default", { exact: true })).toBeVisible()
 
-  await page.keyboard.press("Escape")
-  const closed = await dialog
-    .waitFor({ state: "detached", timeout: 1500 })
-    .then(() => true)
-    .catch(() => false)
-
-  if (!closed) {
-    await page.keyboard.press("Escape")
-    const closedSecond = await dialog
-      .waitFor({ state: "detached", timeout: 1500 })
-      .then(() => true)
-      .catch(() => false)
-
-    if (!closedSecond) {
-      await page.locator('[data-component="dialog-overlay"]').click({ position: { x: 5, y: 5 } })
-      await expect(dialog).toHaveCount(0)
-    }
-  }
+  await closeDialog(page, dialog)
 
   await ensurePopoverOpen()
 

+ 4 - 9
packages/app/e2e/app/session.spec.ts

@@ -1,21 +1,16 @@
 import { test, expect } from "../fixtures"
 import { promptSelector } from "../selectors"
+import { withSession } from "../actions"
 
 test("can open an existing session and type into the prompt", async ({ page, sdk, gotoSession }) => {
   const title = `e2e smoke ${Date.now()}`
-  const created = await sdk.session.create({ title }).then((r) => r.data)
 
-  if (!created?.id) throw new Error("Session create did not return an id")
-  const sessionID = created.id
-
-  try {
-    await gotoSession(sessionID)
+  await withSession(sdk, title, async (session) => {
+    await gotoSession(session.id)
 
     const prompt = page.locator(promptSelector)
     await prompt.click()
     await page.keyboard.type("hello from e2e")
     await expect(prompt).toContainText("hello from e2e")
-  } finally {
-    await sdk.session.delete({ sessionID }).catch(() => undefined)
-  }
+  })
 })

+ 25 - 31
packages/app/e2e/app/titlebar-history.spec.ts

@@ -1,48 +1,42 @@
 import { test, expect } from "../fixtures"
-import { openSidebar } from "../actions"
+import { openSidebar, withSession } from "../actions"
 import { promptSelector } from "../selectors"
 
 test("titlebar back/forward navigates between sessions", async ({ page, slug, sdk, gotoSession }) => {
   await page.setViewportSize({ width: 1400, height: 800 })
 
   const stamp = Date.now()
-  const one = await sdk.session.create({ title: `e2e titlebar history 1 ${stamp}` }).then((r) => r.data)
-  const two = await sdk.session.create({ title: `e2e titlebar history 2 ${stamp}` }).then((r) => r.data)
 
-  if (!one?.id) throw new Error("Session create did not return an id")
-  if (!two?.id) throw new Error("Session create did not return an id")
+  await withSession(sdk, `e2e titlebar history 1 ${stamp}`, async (one) => {
+    await withSession(sdk, `e2e titlebar history 2 ${stamp}`, async (two) => {
+      await gotoSession(one.id)
 
-  try {
-    await gotoSession(one.id)
+      await openSidebar(page)
 
-    await openSidebar(page)
+      const link = page.locator(`[data-session-id="${two.id}"] a`).first()
+      await expect(link).toBeVisible()
+      await link.scrollIntoViewIfNeeded()
+      await link.click()
 
-    const link = page.locator(`[data-session-id="${two.id}"] a`).first()
-    await expect(link).toBeVisible()
-    await link.scrollIntoViewIfNeeded()
-    await link.click()
+      await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))
+      await expect(page.locator(promptSelector)).toBeVisible()
 
-    await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))
-    await expect(page.locator(promptSelector)).toBeVisible()
+      const back = page.getByRole("button", { name: "Back" })
+      const forward = page.getByRole("button", { name: "Forward" })
 
-    const back = page.getByRole("button", { name: "Back" })
-    const forward = page.getByRole("button", { name: "Forward" })
+      await expect(back).toBeVisible()
+      await expect(back).toBeEnabled()
+      await back.click()
 
-    await expect(back).toBeVisible()
-    await expect(back).toBeEnabled()
-    await back.click()
+      await expect(page).toHaveURL(new RegExp(`/${slug}/session/${one.id}(?:\\?|#|$)`))
+      await expect(page.locator(promptSelector)).toBeVisible()
 
-    await expect(page).toHaveURL(new RegExp(`/${slug}/session/${one.id}(?:\\?|#|$)`))
-    await expect(page.locator(promptSelector)).toBeVisible()
+      await expect(forward).toBeVisible()
+      await expect(forward).toBeEnabled()
+      await forward.click()
 
-    await expect(forward).toBeVisible()
-    await expect(forward).toBeEnabled()
-    await forward.click()
-
-    await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))
-    await expect(page.locator(promptSelector)).toBeVisible()
-  } finally {
-    await sdk.session.delete({ sessionID: one.id }).catch(() => undefined)
-    await sdk.session.delete({ sessionID: two.id }).catch(() => undefined)
-  }
+      await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))
+      await expect(page.locator(promptSelector)).toBeVisible()
+    })
+  })
 })

+ 2 - 4
packages/app/e2e/files/file-open.spec.ts

@@ -1,5 +1,5 @@
 import { test, expect } from "../fixtures"
-import { openPalette } from "../actions"
+import { openPalette, clickListItem } from "../actions"
 
 test("can open a file tab from the search palette", async ({ page, gotoSession }) => {
   await gotoSession()
@@ -9,9 +9,7 @@ test("can open a file tab from the search palette", async ({ page, gotoSession }
   const input = dialog.getByRole("textbox").first()
   await input.fill("package.json")
 
-  const fileItem = dialog.locator('[data-slot="list-item"][data-key^="file:"]').first()
-  await expect(fileItem).toBeVisible()
-  await fileItem.click()
+  await clickListItem(dialog, { keyStartsWith: "file:" })
 
   await expect(dialog).toHaveCount(0)
 

+ 2 - 8
packages/app/e2e/files/file-viewer.spec.ts

@@ -1,5 +1,5 @@
 import { test, expect } from "../fixtures"
-import { openPalette } from "../actions"
+import { openPalette, clickListItem } from "../actions"
 
 test("smoke file viewer renders real file content", async ({ page, gotoSession }) => {
   await gotoSession()
@@ -12,13 +12,7 @@ test("smoke file viewer renders real file content", async ({ page, gotoSession }
   const input = dialog.getByRole("textbox").first()
   await input.fill(file)
 
-  const fileItem = dialog
-    .locator(
-      '[data-slot="list-item"][data-key^="file:"][data-key*="packages"][data-key*="app"][data-key$="package.json"]',
-    )
-    .first()
-  await expect(fileItem).toBeVisible()
-  await fileItem.click()
+  await clickListItem(dialog, { text: /packages.*app.*package.json/ })
 
   await expect(dialog).toHaveCount(0)
 

+ 2 - 3
packages/app/e2e/models/model-picker.spec.ts

@@ -1,5 +1,6 @@
 import { test, expect } from "../fixtures"
 import { promptSelector } from "../selectors"
+import { clickListItem } from "../actions"
 
 test("smoke model selection updates prompt footer", async ({ page, gotoSession }) => {
   await gotoSession()
@@ -32,9 +33,7 @@ test("smoke model selection updates prompt footer", async ({ page, gotoSession }
 
   await input.fill(model)
 
-  const item = dialog.locator(`[data-slot="list-item"][data-key="${key}"]`)
-  await expect(item).toBeVisible()
-  await item.click()
+  await clickListItem(dialog, { key })
 
   await expect(dialog).toHaveCount(0)
 

+ 1 - 1
packages/app/e2e/models/models-visibility.spec.ts

@@ -1,6 +1,6 @@
 import { test, expect } from "../fixtures"
 import { promptSelector } from "../selectors"
-import { closeDialog, openSettings } from "../actions"
+import { closeDialog, openSettings, clickListItem } from "../actions"
 
 test("hiding a model removes it from the model picker", async ({ page, gotoSession }) => {
   await gotoSession()

+ 6 - 1
packages/app/e2e/projects/project-edit.spec.ts

@@ -14,7 +14,12 @@ test("dialog edit project updates name and startup script", async ({ page, gotoS
     await expect(trigger).toBeVisible()
     await trigger.click({ force: true })
 
-    await page.getByRole("menuitem", { name: "Edit" }).click()
+    const menu = page.locator('[data-component="dropdown-menu-content"]').first()
+    await expect(menu).toBeVisible()
+
+    const editItem = menu.getByRole("menuitem", { name: "Edit" }).first()
+    await expect(editItem).toBeVisible()
+    await editItem.click({ force: true })
 
     const dialog = page.getByRole("dialog")
     await expect(dialog).toBeVisible()

+ 6 - 13
packages/app/e2e/projects/projects-close.spec.ts

@@ -1,5 +1,5 @@
 import { test, expect } from "../fixtures"
-import { createTestProject, seedProjects, cleanupTestProject, openSidebar } from "../actions"
+import { createTestProject, seedProjects, cleanupTestProject, openSidebar, clickMenuItem } from "../actions"
 import { projectCloseHoverSelector, projectCloseMenuSelector, projectSwitchSelector } from "../selectors"
 import { dirSlug } from "../utils"
 
@@ -33,7 +33,7 @@ test("can close a project via project header more options menu", async ({ page,
   await page.setViewportSize({ width: 1400, height: 800 })
 
   const other = await createTestProject()
-  const otherName = other.split("/").pop()
+  const otherName = other.split("/").pop() ?? other
   const otherSlug = dirSlug(other)
   await seedProjects(page, { directory, extra: [other] })
 
@@ -59,17 +59,10 @@ test("can close a project via project header more options menu", async ({ page,
     await trigger.focus()
     await page.keyboard.press("Enter")
 
-    const close = page
-      .locator(projectCloseMenuSelector(otherSlug))
-      .or(page.getByRole("menuitem", { name: "Close" }))
-      .or(
-        page
-          .locator('[data-component="dropdown-menu-content"] [data-slot="dropdown-menu-item"]')
-          .filter({ hasText: "Close" }),
-      )
-      .first()
-    await expect(close).toBeVisible({ timeout: 10_000 })
-    await close.click({ force: true })
+    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)
   } finally {
     await cleanupTestProject(other)

+ 6 - 11
packages/app/e2e/prompt/context.spec.ts

@@ -1,16 +1,13 @@
 import { test, expect } from "../fixtures"
 import { promptSelector } from "../selectors"
+import { withSession } from "../actions"
 
 test("context panel can be opened from the prompt", async ({ page, sdk, gotoSession }) => {
   const title = `e2e smoke context ${Date.now()}`
-  const created = await sdk.session.create({ title }).then((r) => r.data)
 
-  if (!created?.id) throw new Error("Session create did not return an id")
-  const sessionID = created.id
-
-  try {
+  await withSession(sdk, title, async (session) => {
     await sdk.session.promptAsync({
-      sessionID,
+      sessionID: session.id,
       noReply: true,
       parts: [
         {
@@ -22,12 +19,12 @@ test("context panel can be opened from the prompt", async ({ page, sdk, gotoSess
 
     await expect
       .poll(async () => {
-        const messages = await sdk.session.messages({ sessionID, limit: 1 }).then((r) => r.data ?? [])
+        const messages = await sdk.session.messages({ sessionID: session.id, limit: 1 }).then((r) => r.data ?? [])
         return messages.length
       })
       .toBeGreaterThan(0)
 
-    await gotoSession(sessionID)
+    await gotoSession(session.id)
 
     const contextButton = page
       .locator('[data-component="button"]')
@@ -39,7 +36,5 @@ test("context panel can be opened from the prompt", async ({ page, sdk, gotoSess
 
     const tabs = page.locator('[data-component="tabs"][data-variant="normal"]')
     await expect(tabs.getByRole("tab", { name: "Context" })).toBeVisible()
-  } finally {
-    await sdk.session.delete({ sessionID }).catch(() => undefined)
-  }
+  })
 })

+ 1 - 1
packages/app/e2e/prompt/prompt.spec.ts

@@ -1,6 +1,6 @@
 import { test, expect } from "../fixtures"
 import { promptSelector } from "../selectors"
-import { sessionIDFromUrl } from "../actions"
+import { sessionIDFromUrl, withSession } from "../actions"
 
 test("can send a prompt and receive a reply", async ({ page, sdk, gotoSession }) => {
   test.setTimeout(120_000)

+ 18 - 0
packages/app/e2e/selectors.ts

@@ -15,3 +15,21 @@ export const projectMenuTriggerSelector = (slug: string) =>
   `${sidebarNavSelector} [data-action="project-menu"][data-project="${slug}"]`
 
 export const projectCloseMenuSelector = (slug: string) => `[data-action="project-close-menu"][data-project="${slug}"]`
+
+export const titlebarRightSelector = "#opencode-titlebar-right"
+
+export const popoverBodySelector = '[data-slot="popover-body"]'
+
+export const dropdownMenuTriggerSelector = '[data-slot="dropdown-menu-trigger"]'
+
+export const dropdownMenuContentSelector = '[data-component="dropdown-menu-content"]'
+
+export const inlineInputSelector = '[data-component="inline-input"]'
+
+export const sessionItemSelector = (sessionID: string) => `${sidebarNavSelector} [data-session-id="${sessionID}"]`
+
+export const listItemSelector = '[data-slot="list-item"]'
+
+export const listItemKeyStartsWithSelector = (prefix: string) => `${listItemSelector}[data-key^="${prefix}"]`
+
+export const listItemKeySelector = (key: string) => `${listItemSelector}[data-key="${key}"]`

+ 115 - 0
packages/app/e2e/session/session.spec.ts

@@ -0,0 +1,115 @@
+import { test, expect } from "../fixtures"
+import {
+  openSidebar,
+  openSessionMoreMenu,
+  clickMenuItem,
+  confirmDialog,
+  openSharePopover,
+  withSession,
+} from "../actions"
+import { sessionItemSelector, inlineInputSelector } from "../selectors"
+
+const shareDisabled = process.env.OPENCODE_DISABLE_SHARE === "true" || process.env.OPENCODE_DISABLE_SHARE === "1"
+
+test("sidebar session can be renamed", async ({ page, sdk, gotoSession }) => {
+  const stamp = Date.now()
+  const originalTitle = `e2e rename test ${stamp}`
+  const newTitle = `e2e renamed ${stamp}`
+
+  await withSession(sdk, originalTitle, async (session) => {
+    await gotoSession(session.id)
+    await openSidebar(page)
+
+    const menu = await openSessionMoreMenu(page, session.id)
+    await clickMenuItem(menu, /rename/i)
+
+    const input = page.locator(sessionItemSelector(session.id)).locator(inlineInputSelector).first()
+    await expect(input).toBeVisible()
+    await input.fill(newTitle)
+    await input.press("Enter")
+
+    await expect(page.locator(sessionItemSelector(session.id)).locator("a").first()).toContainText(newTitle)
+  })
+})
+
+test("sidebar session can be archived", async ({ page, sdk, gotoSession }) => {
+  const stamp = Date.now()
+  const title = `e2e archive test ${stamp}`
+
+  await withSession(sdk, title, async (session) => {
+    await gotoSession(session.id)
+    await openSidebar(page)
+
+    const sessionEl = page.locator(sessionItemSelector(session.id))
+    const menu = await openSessionMoreMenu(page, session.id)
+    await clickMenuItem(menu, /archive/i)
+
+    await expect(sessionEl).not.toBeVisible()
+  })
+})
+
+test("sidebar session can be deleted", async ({ page, sdk, gotoSession }) => {
+  const stamp = Date.now()
+  const title = `e2e delete test ${stamp}`
+
+  await withSession(sdk, title, async (session) => {
+    await gotoSession(session.id)
+    await openSidebar(page)
+
+    const sessionEl = page.locator(sessionItemSelector(session.id))
+    const menu = await openSessionMoreMenu(page, session.id)
+    await clickMenuItem(menu, /delete/i)
+    await confirmDialog(page, /delete/i)
+
+    await expect(sessionEl).not.toBeVisible()
+  })
+})
+
+test("session can be shared and unshared via header button", async ({ page, sdk, gotoSession }) => {
+  test.skip(shareDisabled, "Share is disabled in this environment (OPENCODE_DISABLE_SHARE).")
+
+  const stamp = Date.now()
+  const title = `e2e share test ${stamp}`
+
+  await withSession(sdk, title, async (session) => {
+    await gotoSession(session.id)
+
+    const { rightSection, popoverBody } = await openSharePopover(page)
+    await popoverBody.getByRole("button", { name: "Publish" }).first().click()
+
+    await expect
+      .poll(
+        async () => {
+          const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data)
+          return data?.share?.url || undefined
+        },
+        { timeout: 30_000 },
+      )
+      .not.toBeUndefined()
+
+    const copyButton = rightSection.locator('button[aria-label="Copy link"]').first()
+    await expect(copyButton).toBeVisible({ timeout: 30_000 })
+
+    const sharedPopover = await openSharePopover(page)
+    const unpublish = sharedPopover.popoverBody.getByRole("button", { name: "Unpublish" }).first()
+    await expect(unpublish).toBeVisible({ timeout: 30_000 })
+    await unpublish.click()
+
+    await expect
+      .poll(
+        async () => {
+          const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data)
+          return data?.share?.url || undefined
+        },
+        { timeout: 30_000 },
+      )
+      .toBeUndefined()
+
+    await expect(copyButton).not.toBeVisible({ timeout: 30_000 })
+
+    const unsharedPopover = await openSharePopover(page)
+    await expect(unsharedPopover.popoverBody.getByRole("button", { name: "Publish" }).first()).toBeVisible({
+      timeout: 30_000,
+    })
+  })
+})

+ 1 - 1
packages/app/e2e/settings/settings-providers.spec.ts

@@ -1,6 +1,6 @@
 import { test, expect } from "../fixtures"
 import { promptSelector } from "../selectors"
-import { closeDialog, openSettings } from "../actions"
+import { closeDialog, openSettings, clickListItem } from "../actions"
 
 test("smoke providers settings opens provider selector", async ({ page, gotoSession }) => {
   await gotoSession()

+ 1 - 1
packages/app/e2e/sidebar/sidebar-session-links.spec.ts

@@ -1,5 +1,5 @@
 import { test, expect } from "../fixtures"
-import { openSidebar } from "../actions"
+import { openSidebar, withSession } from "../actions"
 import { promptSelector } from "../selectors"
 
 test("sidebar session links navigate to the selected session", async ({ page, slug, sdk, gotoSession }) => {

+ 1 - 1
packages/app/script/e2e-local.ts

@@ -58,7 +58,7 @@ const sandbox = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-e2e-"))
 
 const serverEnv = {
   ...process.env,
-  OPENCODE_DISABLE_SHARE: "true",
+  OPENCODE_DISABLE_SHARE: process.env.OPENCODE_DISABLE_SHARE ?? "true",
   OPENCODE_DISABLE_LSP_DOWNLOAD: "true",
   OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
   OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: "true",

+ 2 - 0
turbo.json

@@ -1,5 +1,7 @@
 {
   "$schema": "https://turborepo.com/schema.json",
+  "globalEnv": ["CI", "OPENCODE_DISABLE_SHARE"],
+  "globalPassThroughEnv": ["CI", "OPENCODE_DISABLE_SHARE"],
   "tasks": {
     "typecheck": {},
     "build": {