| 1 | import assert from "node:assert/strict"; |
| 2 | import { readFileSync } from "node:fs"; |
| 3 | import { dirname, resolve } from "node:path"; |
| 4 | import { fileURLToPath } from "node:url"; |
| 5 | |
| 6 | const testDir = dirname(fileURLToPath(import.meta.url)); |
| 7 | const panel = readFileSync(resolve(testDir, "../components/WorkspacePanel.tsx"), "utf8"); |
| 8 | const stabilityCSS = readFileSync(resolve(testDir, "../components/WorkspacePanelStability.css"), "utf8"); |
| 9 | const workspaceChangesResource = readFileSync(resolve(testDir, "../lib/useWorkspaceChangesResource.ts"), "utf8"); |
| 10 | const workspaceTreeScrollPersistence = readFileSync(resolve(testDir, "../lib/useWorkspaceTreeScrollPersistence.ts"), "utf8"); |
| 11 | |
| 12 | assert.match( |
| 13 | workspaceChangesResource, |
| 14 | /emptyKeyedResource<WorkspaceChangesView>/, |
| 15 | "working-tree changes use the same keyed stale-while-revalidate resource as preview and history", |
| 16 | ); |
| 17 | assert.match( |
| 18 | workspaceChangesResource, |
| 19 | /setResource\(\(current\) => rejectKeyedResourceRequest/, |
| 20 | "a failed working-tree refresh preserves the last painted data", |
| 21 | ); |
| 22 | assert.doesNotMatch( |
| 23 | workspaceChangesResource, |
| 24 | /setResource\(\{\s*files:\s*\[\]/, |
| 25 | "a failed working-tree refresh never clears the visible list", |
| 26 | ); |
| 27 | assert.match( |
| 28 | panel, |
| 29 | /loadingHistory\s*&&\s*gitHistory\.length\s*===\s*0/, |
| 30 | "history uses a loading placeholder only when there is no stale data to paint", |
| 31 | ); |
| 32 | assert.doesNotMatch( |
| 33 | panel, |
| 34 | /\{loadingHistory\s*\?\s*\(/, |
| 35 | "history refresh does not replace an already-painted list with a loading branch", |
| 36 | ); |
| 37 | |
| 38 | const cwdReset = panel.match(/useEffect\(\(\) => \{[\s\S]*?\}, \[cwd, fileNavigation, fileScope, loadDir, open, workspaceMemoryKey\]\);/)?.[0] ?? ""; |
| 39 | assert.ok(cwdReset, "workspace cwd reset effect is present"); |
| 40 | assert.doesNotMatch(cwdReset, /setSelected(?:File|Change)Path\(null\)/, "cwd reset does not erase restored per-project selections"); |
| 41 | // The preview key already carries its scope, path, mode and access context, so |
| 42 | // clearing it here would only discard the read another effect just started when |
| 43 | // this effect reconnects. |
| 44 | assert.doesNotMatch(cwdReset, /setPreviewResource\(emptyKeyedResource\(\)\)/, "cwd reset does not discard the preview a read just started"); |
| 45 | |
| 46 | const scopeReset = panel.match(/useEffect\(\(\) => \{[\s\S]*?\}, \[fileNavigation, fileScope, open, resetWorkspaceChanges, viewMode, workspaceScopeKey\]\);/)?.[0] ?? ""; |
| 47 | assert.ok(scopeReset, "workspace scope reset effect is present"); |
| 48 | assert.doesNotMatch(scopeReset, /setSelectedChangePath\(null\)/, "scope reset does not erase the restored change selection"); |
| 49 | |
| 50 | assert.match( |
| 51 | stabilityCSS, |
| 52 | /\.workspace-resource-status\s*\{[\s\S]*?position:\s*absolute;/, |
| 53 | "refresh and error badges overlay stale content without changing its layout", |
| 54 | ); |
| 55 | assert.match( |
| 56 | panel, |
| 57 | /onScroll=\{onWorkspaceTreeScroll\}/, |
| 58 | "tree scrolling updates memory without synchronously serializing localStorage", |
| 59 | ); |
| 60 | assert.match( |
| 61 | workspaceTreeScrollPersistence, |
| 62 | /addEventListener\("scrollend", flush\)/, |
| 63 | "tree scroll persistence flushes at the native scroll boundary", |
| 64 | ); |
| 65 | assert.match( |
| 66 | workspaceTreeScrollPersistence, |
| 67 | /addEventListener\("pagehide", flush\)/, |
| 68 | "pending tree scroll state flushes before page suspension", |
| 69 | ); |
| 70 | |
| 71 | console.log(" PASS workspace panel SWR and per-project restoration contract"); |
| 72 |