| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- import { expect, test, type Page, type Route } from "@playwright/test"
- import { base64Encode } from "@opencode-ai/core/util/encode"
- const server = "http://127.0.0.1:4096"
- const sessionA = session("ses_tab_a", "Tab A session")
- const sessionB = session("ses_tab_b", "Tab B session")
- test("pressing mouse down on a tab navigates before mouse up", async ({ page }) => {
- await mockServer(page)
- await page.addInitScript(
- ({ server, sessionA, sessionB }) => {
- localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: true } }))
- localStorage.setItem(
- "opencode.window.browser.dat:tabs",
- JSON.stringify([
- { type: "session", server, sessionId: sessionA },
- { type: "session", server, sessionId: sessionB },
- ]),
- )
- },
- { server, sessionA: sessionA.id, sessionB: sessionB.id },
- )
- const hrefA = `/server/${base64Encode(server)}/session/${sessionA.id}`
- const hrefB = `/server/${base64Encode(server)}/session/${sessionB.id}`
- await page.goto(hrefA)
- await expect(page.getByText(sessionA.title).first()).toBeVisible()
- const linkB = page.locator(`a[data-titlebar-tab-link][href="${hrefB}"]`)
- await expect(linkB).toBeVisible()
- const box = await linkB.boundingBox()
- if (!box) throw new Error("tab link has no bounding box")
- await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2)
- await page.mouse.down()
- // Navigation must happen on mousedown, before the button is released.
- await expect(page).toHaveURL(new RegExp(`${hrefB.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`))
- await page.mouse.up()
- await expect(page).toHaveURL(new RegExp(`${hrefB.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`))
- })
- function session(id: string, title: string) {
- return {
- id,
- slug: id,
- projectID: "project-tabs",
- directory: "C:/tab-project",
- title,
- version: "dev",
- time: { created: 1, updated: 1 },
- }
- }
- async function mockServer(page: Page) {
- const sessions = [sessionA, sessionB]
- await page.route("**/*", async (route) => {
- const url = new URL(route.request().url())
- if (url.origin !== server) return route.fallback()
- if (url.pathname === "/global/event" || url.pathname === "/event") return sse(route)
- if (url.pathname === "/global/health") return json(route, { healthy: true })
- if (url.pathname === "/session") return json(route, sessions)
- const byId = sessions.find((item) => url.pathname === `/session/${item.id}`)
- if (byId) return json(route, byId)
- if (/^\/session\/[^/]+$/.test(url.pathname)) return json(route, { name: "NotFoundError" }, 404)
- if (/^\/session\/[^/]+\/message$/.test(url.pathname)) return json(route, [])
- if (/^\/session\/[^/]+\/(children|diff)$/.test(url.pathname)) return json(route, [])
- if (["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/diff"].includes(url.pathname))
- return json(route, [])
- if (["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"].includes(url.pathname))
- return json(route, {})
- if (url.pathname === "/provider")
- return json(route, { all: [], connected: [], default: { providerID: "", modelID: "" } })
- if (url.pathname === "/agent") return json(route, [{ name: "build", mode: "primary" }])
- if (url.pathname === "/project" || url.pathname === "/project/current") {
- const project = {
- id: sessionA.projectID,
- worktree: sessionA.directory,
- vcs: "git",
- time: { created: 1, updated: 1 },
- sandboxes: [],
- }
- return json(route, url.pathname === "/project" ? [project] : project)
- }
- if (url.pathname === "/path")
- return json(route, {
- state: sessionA.directory,
- config: sessionA.directory,
- worktree: sessionA.directory,
- directory: sessionA.directory,
- home: sessionA.directory,
- })
- if (url.pathname === "/vcs") return json(route, { branch: "main", default_branch: "main" })
- return json(route, {})
- })
- }
- function json(route: Route, body: unknown, status = 200) {
- return route.fulfill({
- status,
- contentType: "application/json",
- headers: { "access-control-allow-origin": "*" },
- body: JSON.stringify(body),
- })
- }
- function sse(route: Route) {
- return route.fulfill({ status: 200, contentType: "text/event-stream", body: ": ok\n\n" })
- }
|