chore: fix flaky test
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
|||||||
sessionIDFromUrl,
|
sessionIDFromUrl,
|
||||||
} from "../actions"
|
} from "../actions"
|
||||||
import { projectSwitchSelector, promptSelector, workspaceItemSelector, workspaceNewSessionSelector } from "../selectors"
|
import { projectSwitchSelector, promptSelector, workspaceItemSelector, workspaceNewSessionSelector } from "../selectors"
|
||||||
import { createSdk, dirSlug } from "../utils"
|
import { createSdk, dirSlug, sessionPath } from "../utils"
|
||||||
|
|
||||||
function slugFromUrl(url: string) {
|
function slugFromUrl(url: string) {
|
||||||
return /\/([^/]+)\/session(?:\/|$)/.exec(url)?.[1] ?? ""
|
return /\/([^/]+)\/session(?:\/|$)/.exec(url)?.[1] ?? ""
|
||||||
@@ -51,7 +51,6 @@ test("switching back to a project opens the latest workspace session", async ({
|
|||||||
|
|
||||||
const other = await createTestProject()
|
const other = await createTestProject()
|
||||||
const otherSlug = dirSlug(other)
|
const otherSlug = dirSlug(other)
|
||||||
const stamp = Date.now()
|
|
||||||
let rootDir: string | undefined
|
let rootDir: string | undefined
|
||||||
let workspaceDir: string | undefined
|
let workspaceDir: string | undefined
|
||||||
let sessionID: string | undefined
|
let sessionID: string | undefined
|
||||||
@@ -80,6 +79,7 @@ test("switching back to a project opens the latest workspace session", async ({
|
|||||||
|
|
||||||
const workspaceSlug = slugFromUrl(page.url())
|
const workspaceSlug = slugFromUrl(page.url())
|
||||||
workspaceDir = base64Decode(workspaceSlug)
|
workspaceDir = base64Decode(workspaceSlug)
|
||||||
|
if (!workspaceDir) throw new Error(`Failed to decode workspace slug: ${workspaceSlug}`)
|
||||||
await openSidebar(page)
|
await openSidebar(page)
|
||||||
|
|
||||||
const workspace = page.locator(workspaceItemSelector(workspaceSlug)).first()
|
const workspace = page.locator(workspaceItemSelector(workspaceSlug)).first()
|
||||||
@@ -92,15 +92,14 @@ test("switching back to a project opens the latest workspace session", async ({
|
|||||||
|
|
||||||
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session(?:[/?#]|$)`))
|
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session(?:[/?#]|$)`))
|
||||||
|
|
||||||
const prompt = page.locator(promptSelector)
|
const created = await createSdk(workspaceDir)
|
||||||
await expect(prompt).toBeVisible()
|
.session.create()
|
||||||
await prompt.fill(`project switch remembers workspace ${stamp}`)
|
.then((x) => x.data?.id)
|
||||||
await prompt.press("Enter")
|
if (!created) throw new Error(`Failed to create session for workspace: ${workspaceDir}`)
|
||||||
|
|
||||||
await expect.poll(() => sessionIDFromUrl(page.url()) ?? "", { timeout: 30_000 }).not.toBe("")
|
|
||||||
const created = sessionIDFromUrl(page.url())
|
|
||||||
if (!created) throw new Error(`Failed to parse session id from URL: ${page.url()}`)
|
|
||||||
sessionID = created
|
sessionID = created
|
||||||
|
|
||||||
|
await page.goto(sessionPath(workspaceDir, created))
|
||||||
|
await expect(page.locator(promptSelector)).toBeVisible()
|
||||||
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session/${created}(?:[/?#]|$)`))
|
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session/${created}(?:[/?#]|$)`))
|
||||||
|
|
||||||
await openSidebar(page)
|
await openSidebar(page)
|
||||||
@@ -114,7 +113,8 @@ test("switching back to a project opens the latest workspace session", async ({
|
|||||||
await expect(rootButton).toBeVisible()
|
await expect(rootButton).toBeVisible()
|
||||||
await rootButton.click()
|
await rootButton.click()
|
||||||
|
|
||||||
await expect(page).toHaveURL(new RegExp(`/${workspaceSlug}/session/${created}(?:[/?#]|$)`))
|
await expect.poll(() => sessionIDFromUrl(page.url()) ?? "").toBe(created)
|
||||||
|
await expect(page).toHaveURL(new RegExp(`/session/${created}(?:[/?#]|$)`))
|
||||||
},
|
},
|
||||||
{ extra: [other] },
|
{ extra: [other] },
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import {
|
|||||||
displayName,
|
displayName,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
getDraggableId,
|
getDraggableId,
|
||||||
|
latestRootSession,
|
||||||
sortedRootSessions,
|
sortedRootSessions,
|
||||||
syncWorkspaceOrder,
|
syncWorkspaceOrder,
|
||||||
workspaceKey,
|
workspaceKey,
|
||||||
@@ -1093,14 +1094,51 @@ export default function Layout(props: ParentProps) {
|
|||||||
return meta?.worktree ?? directory
|
return meta?.worktree ?? directory
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateToProject(directory: string | undefined) {
|
async function navigateToProject(directory: string | undefined) {
|
||||||
if (!directory) return
|
if (!directory) return
|
||||||
const root = projectRoot(directory)
|
const root = projectRoot(directory)
|
||||||
server.projects.touch(root)
|
server.projects.touch(root)
|
||||||
|
const project = layout.projects.list().find((item) => item.worktree === root)
|
||||||
|
const dirs = Array.from(new Set([root, ...(store.workspaceOrder[root] ?? []), ...(project?.sandboxes ?? [])]))
|
||||||
|
const openSession = async (target: { directory: string; id: string }) => {
|
||||||
|
const resolved = await globalSDK.client.session
|
||||||
|
.get({ sessionID: target.id })
|
||||||
|
.then((x) => x.data)
|
||||||
|
.catch(() => undefined)
|
||||||
|
const next = resolved?.directory ? resolved : target
|
||||||
|
setStore("lastProjectSession", root, { directory: next.directory, id: next.id, at: Date.now() })
|
||||||
|
navigateWithSidebarReset(`/${base64Encode(next.directory)}/session/${next.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
const projectSession = store.lastProjectSession[root]
|
const projectSession = store.lastProjectSession[root]
|
||||||
if (projectSession?.id) {
|
if (projectSession?.id) {
|
||||||
navigateWithSidebarReset(`/${base64Encode(projectSession.directory)}/session/${projectSession.id}`)
|
await openSession(projectSession)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const latest = latestRootSession(
|
||||||
|
dirs.map((item) => globalSync.child(item, { bootstrap: false })[0]),
|
||||||
|
Date.now(),
|
||||||
|
)
|
||||||
|
if (latest) {
|
||||||
|
await openSession(latest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetched = latestRootSession(
|
||||||
|
await Promise.all(
|
||||||
|
dirs.map(async (item) => ({
|
||||||
|
path: { directory: item },
|
||||||
|
session: await globalSDK.client.session
|
||||||
|
.list({ directory: item })
|
||||||
|
.then((x) => x.data ?? [])
|
||||||
|
.catch(() => []),
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
Date.now(),
|
||||||
|
)
|
||||||
|
if (fetched) {
|
||||||
|
await openSession(fetched)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,25 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { type Session } from "@opencode-ai/sdk/v2/client"
|
||||||
import { collectOpenProjectDeepLinks, drainPendingDeepLinks, parseDeepLink } from "./deep-links"
|
import { collectOpenProjectDeepLinks, drainPendingDeepLinks, parseDeepLink } from "./deep-links"
|
||||||
import { displayName, errorMessage, getDraggableId, syncWorkspaceOrder, workspaceKey } from "./helpers"
|
import {
|
||||||
|
displayName,
|
||||||
|
errorMessage,
|
||||||
|
getDraggableId,
|
||||||
|
latestRootSession,
|
||||||
|
syncWorkspaceOrder,
|
||||||
|
workspaceKey,
|
||||||
|
} from "./helpers"
|
||||||
|
|
||||||
|
const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
|
||||||
|
({
|
||||||
|
title: "",
|
||||||
|
version: "v2",
|
||||||
|
parentID: undefined,
|
||||||
|
messageCount: 0,
|
||||||
|
permissions: { session: {}, share: {} },
|
||||||
|
time: { created: 0, updated: 0, archived: undefined },
|
||||||
|
...input,
|
||||||
|
}) as Session
|
||||||
|
|
||||||
describe("layout deep links", () => {
|
describe("layout deep links", () => {
|
||||||
test("parses open-project deep links", () => {
|
test("parses open-project deep links", () => {
|
||||||
@@ -73,6 +92,61 @@ describe("layout workspace helpers", () => {
|
|||||||
expect(result).toEqual(["/root", "/c", "/b"])
|
expect(result).toEqual(["/root", "/c", "/b"])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("finds the latest root session across workspaces", () => {
|
||||||
|
const result = latestRootSession(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: { directory: "/root" },
|
||||||
|
session: [session({ id: "root", directory: "/root", time: { created: 1, updated: 1, archived: undefined } })],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: { directory: "/workspace" },
|
||||||
|
session: [
|
||||||
|
session({
|
||||||
|
id: "workspace",
|
||||||
|
directory: "/workspace",
|
||||||
|
time: { created: 2, updated: 2, archived: undefined },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
120_000,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result?.id).toBe("workspace")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("ignores archived and child sessions when finding latest root session", () => {
|
||||||
|
const result = latestRootSession(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: { directory: "/workspace" },
|
||||||
|
session: [
|
||||||
|
session({
|
||||||
|
id: "archived",
|
||||||
|
directory: "/workspace",
|
||||||
|
time: { created: 10, updated: 10, archived: 10 },
|
||||||
|
}),
|
||||||
|
session({
|
||||||
|
id: "child",
|
||||||
|
directory: "/workspace",
|
||||||
|
parentID: "parent",
|
||||||
|
time: { created: 20, updated: 20, archived: undefined },
|
||||||
|
}),
|
||||||
|
session({
|
||||||
|
id: "root",
|
||||||
|
directory: "/workspace",
|
||||||
|
time: { created: 30, updated: 30, archived: undefined },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
120_000,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result?.id).toBe("root")
|
||||||
|
})
|
||||||
|
|
||||||
test("extracts draggable id safely", () => {
|
test("extracts draggable id safely", () => {
|
||||||
expect(getDraggableId({ draggable: { id: "x" } })).toBe("x")
|
expect(getDraggableId({ draggable: { id: "x" } })).toBe("x")
|
||||||
expect(getDraggableId({ draggable: { id: 42 } })).toBeUndefined()
|
expect(getDraggableId({ draggable: { id: 42 } })).toBeUndefined()
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ export const isRootVisibleSession = (session: Session, directory: string) =>
|
|||||||
export const sortedRootSessions = (store: { session: Session[]; path: { directory: string } }, now: number) =>
|
export const sortedRootSessions = (store: { session: Session[]; path: { directory: string } }, now: number) =>
|
||||||
store.session.filter((session) => isRootVisibleSession(session, store.path.directory)).sort(sortSessions(now))
|
store.session.filter((session) => isRootVisibleSession(session, store.path.directory)).sort(sortSessions(now))
|
||||||
|
|
||||||
|
export const latestRootSession = (stores: { session: Session[]; path: { directory: string } }[], now: number) =>
|
||||||
|
stores
|
||||||
|
.flatMap((store) => store.session.filter((session) => isRootVisibleSession(session, store.path.directory)))
|
||||||
|
.sort(sortSessions(now))[0]
|
||||||
|
|
||||||
export const childMapByParent = (sessions: Session[]) => {
|
export const childMapByParent = (sessions: Session[]) => {
|
||||||
const map = new Map<string, string[]>()
|
const map = new Map<string, string[]>()
|
||||||
for (const session of sessions) {
|
for (const session of sessions) {
|
||||||
|
|||||||
Reference in New Issue
Block a user