mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-15 20:52:01 +03:00
168 lines
6.4 KiB
TypeScript
168 lines
6.4 KiB
TypeScript
import { expect, test } from "@playwright/test"
|
|
import { project, provider, sessionHref } from "../utils/app"
|
|
import { mockOpenCodeServer } from "../utils/mock-server"
|
|
import { expectAppVisible } from "../utils/waits"
|
|
import { openSession } from "../utils/workspace"
|
|
|
|
const directory = "C:/OpenCode/NewProject"
|
|
|
|
test("creates a session in a new project and selects its model", async ({ page }) => {
|
|
// An empty draft must remain usable when the file viewer is unavailable.
|
|
await page.route(/(?:\/_assets\/file-(?!icon-)[^/]+\.js|\/session-ui\/src\/components\/file\.tsx)(?:\?|$)/, (route) =>
|
|
route.abort(),
|
|
)
|
|
const created = project({ id: "proj_model_selection_flow", directory, name: "NewProject" })
|
|
// The server knows no project until the user adds this one.
|
|
const projects: unknown[] = []
|
|
await mockOpenCodeServer(page, {
|
|
directory,
|
|
project: created,
|
|
projects: () => projects,
|
|
// The picker answers with the platform's separators; the server resolves them to its own.
|
|
onLocationGet: (requested) => {
|
|
if (requested.replaceAll("\\", "/") === directory && !projects.includes(created)) projects.push(created)
|
|
},
|
|
provider: () => ({
|
|
all: [
|
|
{
|
|
id: "opencode",
|
|
name: "OpenCode",
|
|
models: {
|
|
"free-model": {
|
|
id: "free-model",
|
|
name: "Free Model",
|
|
cost: { input: 0, output: 0 },
|
|
limit: { context: 200_000 },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: "opencode-go",
|
|
name: "OpenCode Go",
|
|
models: {
|
|
"go-model-1": {
|
|
id: "go-model-1",
|
|
name: "Go Model 1",
|
|
cost: { input: 1, output: 1 },
|
|
limit: { context: 200_000 },
|
|
},
|
|
},
|
|
},
|
|
],
|
|
connected: ["opencode", "opencode-go"],
|
|
default: { providerID: "opencode", modelID: "free-model" },
|
|
}),
|
|
sessions: [],
|
|
pageMessages: () => ({ items: [] }),
|
|
// Listings are requested by absolute path and returned relative to the stable Location.
|
|
fileList: (path) => (path === "C:/OpenCode" ? [{ path: "./", type: "directory", ignored: false }] : []),
|
|
})
|
|
await page.addInitScript(() => {
|
|
localStorage.setItem(
|
|
"opencode.global.dat:model",
|
|
JSON.stringify({
|
|
user: [
|
|
{ providerID: "opencode", modelID: "free-model", visibility: "show" },
|
|
{ providerID: "opencode-go", modelID: "go-model-1", visibility: "show" },
|
|
],
|
|
recent: [{ providerID: "opencode-go", modelID: "go-model-1" }],
|
|
variant: {},
|
|
}),
|
|
)
|
|
})
|
|
|
|
await page.goto("/")
|
|
const addProject = page.locator('[data-action="home-add-project-row"]')
|
|
await expectAppVisible(addProject)
|
|
await addProject.click()
|
|
const picker = page.getByRole("dialog", { name: "Open project", exact: true })
|
|
await expect(picker.getByRole("combobox")).toHaveValue("")
|
|
|
|
const listing = page.waitForRequest((request) => {
|
|
const url = new URL(request.url())
|
|
|
|
return url.pathname === "/api/fs/list" && url.searchParams.get("path") === "C:/OpenCode"
|
|
})
|
|
|
|
await picker.getByRole("button", { name: "Parent", exact: true }).click()
|
|
expect(new URL((await listing).url()).searchParams.get("location[directory]")).toBe(directory)
|
|
const directoryItem = picker.getByRole("treeitem", { name: "NewProject", exact: true })
|
|
await expect(directoryItem).toBeVisible()
|
|
await directoryItem.click()
|
|
await expect(directoryItem).toHaveAttribute("aria-selected", "true")
|
|
await expect(picker.getByText("C:\\OpenCode\\NewProject", { exact: true })).toBeVisible()
|
|
const selectFolder = picker.getByRole("button", { name: "Select folder", exact: true })
|
|
await expect(selectFolder).toBeEnabled()
|
|
await selectFolder.click()
|
|
await expect(picker).toBeHidden()
|
|
|
|
await page.locator('[data-action="home-new-session"]').click()
|
|
await expectAppVisible(page.locator('[data-component="composer"]'))
|
|
|
|
const modelControl = page.locator('[data-action="composer-model"]')
|
|
await expect(modelControl).toContainText("Go Model 1")
|
|
await modelControl.click()
|
|
const modelSearch = page.getByPlaceholder("Search models", { exact: true })
|
|
await expect(modelSearch).toBeFocused()
|
|
await modelSearch.press("ArrowDown")
|
|
await modelSearch.press("Enter")
|
|
await expect(modelControl).toContainText("Free Model")
|
|
|
|
await modelControl.click()
|
|
await expect(modelSearch).toBeFocused()
|
|
await modelSearch.press("ArrowUp")
|
|
await modelSearch.press("Enter")
|
|
|
|
await expect(modelControl).toContainText("Go Model 1")
|
|
})
|
|
|
|
test("restores each existing session's model and variant when switching tabs", async ({ page }) => {
|
|
const sessions = ["A", "B"].map((name) => ({
|
|
id: `ses_model_${name}`,
|
|
title: `Model ${name}`,
|
|
model: { id: `model-${name}`, providerID: "opencode", variant: "balanced" },
|
|
}))
|
|
|
|
await openSession(page, {
|
|
name: "ModelSelection",
|
|
sessions,
|
|
provider: provider(
|
|
...sessions.map((session) => ({
|
|
id: session.model.id,
|
|
name: session.title,
|
|
variants: { balanced: {}, high: {} },
|
|
})),
|
|
),
|
|
})
|
|
|
|
const hrefA = sessionHref(sessions[0]!.id)
|
|
const hrefB = sessionHref(sessions[1]!.id)
|
|
const composer = page.locator('[data-component="composer"]')
|
|
const modelControl = composer.locator('[data-action="composer-model"]')
|
|
const variant = composer.getByRole("button", { name: "Choose model variant", exact: true })
|
|
await expect(modelControl).toHaveText("Model A")
|
|
await expect(variant).toHaveText("balanced")
|
|
await variant.click()
|
|
await page.getByRole("menuitemradio", { name: "high", exact: true }).click()
|
|
await expect(variant).toHaveText("high")
|
|
|
|
await page.locator(`[data-titlebar-tab-link][href="${hrefB}"]`).click()
|
|
await expect(page).toHaveURL(hrefB)
|
|
await expect(modelControl).toHaveText("Model B")
|
|
await expect(variant).toHaveText("balanced")
|
|
await variant.click()
|
|
await page.getByRole("menuitemradio", { name: "high", exact: true }).click()
|
|
await expect(variant).toHaveText("high")
|
|
|
|
// A new draft starts from the current session's non-default model and chosen variant.
|
|
await page.getByRole("button", { name: "New session", exact: true }).click()
|
|
await expect(page).toHaveURL(/\/new-session\?draftId=/)
|
|
await expect(modelControl).toHaveText("Model B")
|
|
await expect(variant).toHaveText("high")
|
|
|
|
await page.locator(`[data-titlebar-tab-link][href="${hrefA}"]`).click()
|
|
await expect(page).toHaveURL(hrefA)
|
|
await expect(modelControl).toHaveText("Model A")
|
|
await expect(variant).toHaveText("high")
|
|
})
|