|
|
@@ -7,6 +7,8 @@ import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/pr
|
|
|
import { SDKProvider } from "../../../src/cli/cmd/tui/context/sdk"
|
|
|
import { useEvent } from "../../../src/cli/cmd/tui/context/event"
|
|
|
|
|
|
+const projectID = "proj_test"
|
|
|
+
|
|
|
async function wait(fn: () => boolean, timeout = 2000) {
|
|
|
const start = Date.now()
|
|
|
while (!fn()) {
|
|
|
@@ -15,9 +17,10 @@ async function wait(fn: () => boolean, timeout = 2000) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function event(payload: Event, input: { directory: string; workspace?: string }): GlobalEvent {
|
|
|
+function event(payload: Event, input: { directory: string; project?: string; workspace?: string }): GlobalEvent {
|
|
|
return {
|
|
|
directory: input.directory,
|
|
|
+ project: input.project,
|
|
|
workspace: input.workspace,
|
|
|
payload,
|
|
|
}
|
|
|
@@ -65,6 +68,13 @@ function createSource() {
|
|
|
async function mount() {
|
|
|
const source = createSource()
|
|
|
const seen: Event[] = []
|
|
|
+ const workspaces: Array<string | undefined> = []
|
|
|
+ const fetch = (async (input: RequestInfo | URL) => {
|
|
|
+ const url = new URL(input instanceof Request ? input.url : String(input))
|
|
|
+ if (url.pathname === "/path") return Response.json({ home: "", state: "", config: "", directory: "/tmp/root" })
|
|
|
+ if (url.pathname === "/project/current") return Response.json({ id: projectID })
|
|
|
+ throw new Error(`unexpected request: ${url.pathname}`)
|
|
|
+ }) as typeof globalThis.fetch
|
|
|
let project!: ReturnType<typeof useProject>
|
|
|
let done!: () => void
|
|
|
const ready = new Promise<void>((resolve) => {
|
|
|
@@ -72,30 +82,42 @@ async function mount() {
|
|
|
})
|
|
|
|
|
|
const app = await testRender(() => (
|
|
|
- <SDKProvider url="http://test" directory="/tmp/root" events={source.source}>
|
|
|
+ <SDKProvider
|
|
|
+ url="http://test"
|
|
|
+ directory="/tmp/root"
|
|
|
+ events={source.source}
|
|
|
+ fetch={fetch}
|
|
|
+ >
|
|
|
<ProjectProvider>
|
|
|
<Probe
|
|
|
- onReady={(ctx) => {
|
|
|
+ onReady={async (ctx) => {
|
|
|
project = ctx.project
|
|
|
+ await project.sync()
|
|
|
done()
|
|
|
}}
|
|
|
seen={seen}
|
|
|
+ workspaces={workspaces}
|
|
|
/>
|
|
|
</ProjectProvider>
|
|
|
</SDKProvider>
|
|
|
))
|
|
|
|
|
|
await ready
|
|
|
- return { app, emit: source.emit, project, seen }
|
|
|
+ return { app, emit: source.emit, project, seen, workspaces }
|
|
|
}
|
|
|
|
|
|
-function Probe(props: { seen: Event[]; onReady: (ctx: { project: ReturnType<typeof useProject> }) => void }) {
|
|
|
+function Probe(props: {
|
|
|
+ seen: Event[]
|
|
|
+ workspaces: Array<string | undefined>
|
|
|
+ onReady: (ctx: { project: ReturnType<typeof useProject> }) => void
|
|
|
+}) {
|
|
|
const project = useProject()
|
|
|
const event = useEvent()
|
|
|
|
|
|
onMount(() => {
|
|
|
- event.subscribe((evt) => {
|
|
|
+ event.subscribe((evt, { workspace }) => {
|
|
|
props.seen.push(evt)
|
|
|
+ props.workspaces.push(workspace)
|
|
|
})
|
|
|
props.onReady({ project })
|
|
|
})
|
|
|
@@ -104,25 +126,26 @@ function Probe(props: { seen: Event[]; onReady: (ctx: { project: ReturnType<type
|
|
|
}
|
|
|
|
|
|
describe("useEvent", () => {
|
|
|
- test("delivers matching directory events without an active workspace", async () => {
|
|
|
- const { app, emit, seen } = await mount()
|
|
|
+ test("delivers events for the current project", async () => {
|
|
|
+ const { app, emit, seen, workspaces } = await mount()
|
|
|
|
|
|
try {
|
|
|
- emit(event(vcs("main"), { directory: "/tmp/root" }))
|
|
|
+ emit(event(vcs("main"), { directory: "/tmp/other", project: projectID, workspace: "ws_a" }))
|
|
|
|
|
|
await wait(() => seen.length === 1)
|
|
|
|
|
|
expect(seen).toEqual([vcs("main")])
|
|
|
+ expect(workspaces).toEqual(["ws_a"])
|
|
|
} finally {
|
|
|
app.renderer.destroy()
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- test("ignores non-matching directory events without an active workspace", async () => {
|
|
|
+ test("ignores events for other projects", async () => {
|
|
|
const { app, emit, seen } = await mount()
|
|
|
|
|
|
try {
|
|
|
- emit(event(vcs("other"), { directory: "/tmp/other" }))
|
|
|
+ emit(event(vcs("other"), { directory: "/tmp/root", project: "proj_other" }))
|
|
|
await Bun.sleep(30)
|
|
|
|
|
|
expect(seen).toHaveLength(0)
|
|
|
@@ -131,12 +154,12 @@ describe("useEvent", () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- test("delivers matching workspace events when a workspace is active", async () => {
|
|
|
+ test("delivers current project events regardless of active workspace", async () => {
|
|
|
const { app, emit, project, seen } = await mount()
|
|
|
|
|
|
try {
|
|
|
project.workspace.set("ws_a")
|
|
|
- emit(event(vcs("ws"), { directory: "/tmp/other", workspace: "ws_a" }))
|
|
|
+ emit(event(vcs("ws"), { directory: "/tmp/other", project: projectID, workspace: "ws_b" }))
|
|
|
|
|
|
await wait(() => seen.length === 1)
|
|
|
|
|
|
@@ -146,20 +169,6 @@ describe("useEvent", () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- test("ignores non-matching workspace events when a workspace is active", async () => {
|
|
|
- const { app, emit, project, seen } = await mount()
|
|
|
-
|
|
|
- try {
|
|
|
- project.workspace.set("ws_a")
|
|
|
- emit(event(vcs("ws"), { directory: "/tmp/root", workspace: "ws_b" }))
|
|
|
- await Bun.sleep(30)
|
|
|
-
|
|
|
- expect(seen).toHaveLength(0)
|
|
|
- } finally {
|
|
|
- app.renderer.destroy()
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
test("delivers truly global events even when a workspace is active", async () => {
|
|
|
const { app, emit, project, seen } = await mount()
|
|
|
|