diff --git a/packages/app/e2e/regression/session-list-path-loading.spec.ts b/packages/app/e2e/regression/session-list-path-loading.spec.ts new file mode 100644 index 0000000000..1dbc0575f1 --- /dev/null +++ b/packages/app/e2e/regression/session-list-path-loading.spec.ts @@ -0,0 +1,40 @@ +import { expect, test } from "@playwright/test" +import { fixture, pageMessages } from "../smoke/session-timeline.fixture" +import { mockOpenCodeServer } from "../utils/mock-server" + +test("shows loaded sessions before the directory path request resolves", async ({ page }) => { + await mockOpenCodeServer(page, { + sessions: fixture.sessions, + provider: fixture.provider, + directory: fixture.directory, + project: fixture.project, + pageMessages, + }) + + let releasePath!: () => void + const pathBlocked = new Promise((resolve) => { + releasePath = resolve + }) + await page.route("**/path?*", async (route) => { + if (!new URL(route.request().url()).searchParams.has("directory")) return route.fallback() + await pathBlocked + return route.fallback() + }) + + await page.addInitScript((directory) => { + localStorage.setItem( + "opencode.global.dat:server", + JSON.stringify({ + projects: { local: [{ worktree: directory, expanded: true }] }, + lastProject: { local: directory }, + }), + ) + }, fixture.directory) + + await page.goto("/") + try { + await expect(page.getByText(fixture.expected.sourceTitle).first()).toBeVisible({ timeout: 5_000 }) + } finally { + releasePath() + } +}) diff --git a/packages/app/src/context/global-sync/child-store.test.ts b/packages/app/src/context/global-sync/child-store.test.ts index 7c4adb5216..19d43746b1 100644 --- a/packages/app/src/context/global-sync/child-store.test.ts +++ b/packages/app/src/context/global-sync/child-store.test.ts @@ -52,7 +52,7 @@ beforeAll(async () => { useQueries: (options: () => { queries: Array<{ enabled?: boolean }> }) => { queryGroups.push(options) return [ - { isLoading: false, data: { state: "", config: "", worktree: "", directory: "", home: "" } }, + { isLoading: true, data: undefined }, { isLoading: false, data: {} }, { isLoading: false, data: [] }, { isLoading: false, data: provider }, @@ -128,6 +128,35 @@ describe("createChildStoreManager", () => { } }) + test("provides the requested directory while the path query is pending", () => { + let manager: ReturnType | undefined + + const dispose = createOwner((owner) => { + manager = createChildStoreManager({ + owner, + isBooting: () => false, + isLoadingSessions: () => false, + onBootstrap() {}, + onMcp() {}, + onDispose() {}, + translate: (key) => key, + queryOptions: queryOptionsApi, + global: { provider }, + }) + }) + + try { + if (!manager) throw new Error("manager required") + + const [store] = manager.child("/project", { bootstrap: false }) + + expect(store.path.directory).toBe("/project") + expect(store.path.worktree).toBe("") + } finally { + dispose() + } + }) + test("enables MCP only when requested for the directory", () => { let manager: ReturnType | undefined const offset = queryGroups.length diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 99da39ebb0..b7b1d72ac0 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -204,9 +204,8 @@ export function createChildStoreManager(input: { }, config: {}, get path() { - if (pathQuery.isLoading || !pathQuery.data) - return { state: "", config: "", worktree: "", directory: "", home: "" } - return pathQuery.data + if (pathQuery.data) return pathQuery.data + return { state: "", config: "", worktree: "", directory, home: "" } }, status: "loading" as const, agent: [],