Skip to content

Commit a510525

Browse files
committed
Stabilize sidebar file tree rendering
- Keep the workspace file tree mounted when collapsed to avoid flicker - Make browser test runner resolve Vitest from the package config location - Seed onboarding state in browser tests to match the app flow
1 parent 4d2535f commit a510525

4 files changed

Lines changed: 43 additions & 12 deletions

File tree

apps/web/src/components/ChatView.browser.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const PROJECT_ID = "project-1" as ProjectId;
3737
const NOW_ISO = "2026-03-04T12:00:00.000Z";
3838
const BASE_TIME_MS = Date.parse(NOW_ISO);
3939
const ATTACHMENT_SVG = "<svg xmlns='http://www.w3.org/2000/svg' width='120' height='300'></svg>";
40+
const ONBOARDING_STORAGE_KEY = "okcode:onboarding-completed:v1";
4041

4142
interface WsRequestEnvelope {
4243
id: string;
@@ -826,6 +827,7 @@ describe("ChatView timeline estimator parity (full app)", () => {
826827
beforeEach(async () => {
827828
await setViewport(DEFAULT_VIEWPORT);
828829
localStorage.clear();
830+
localStorage.setItem(ONBOARDING_STORAGE_KEY, "true");
829831
document.body.innerHTML = "";
830832
wsRequests.length = 0;
831833
useComposerDraftStore.setState({
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { readFileSync } from "node:fs";
2+
import { resolve } from "node:path";
3+
import { describe, expect, it } from "vitest";
4+
5+
describe("Sidebar file tree mounting", () => {
6+
it("keeps the workspace file tree mounted when the files section is collapsed", () => {
7+
const src = readFileSync(resolve(import.meta.dirname, "./Sidebar.tsx"), "utf8");
8+
9+
expect(src).toContain("<WorkspaceFileTree");
10+
expect(src).toContain('className={cn(filesCollapsedByProject.has(project.id) && "hidden")}');
11+
expect(src).not.toContain("!filesCollapsedByProject.has(project.id) && (");
12+
});
13+
});

apps/web/src/components/Sidebar.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,13 +1680,12 @@ export default function Sidebar() {
16801680
<FolderIcon className="size-3 shrink-0" />
16811681
<span>Files</span>
16821682
</button>
1683-
{!filesCollapsedByProject.has(project.id) && (
1684-
<WorkspaceFileTree
1685-
key={project.id}
1686-
cwd={activeWorkspaceCwd}
1687-
resolvedTheme={resolvedTheme}
1688-
/>
1689-
)}
1683+
<WorkspaceFileTree
1684+
key={project.id}
1685+
cwd={activeWorkspaceCwd}
1686+
resolvedTheme={resolvedTheme}
1687+
className={cn(filesCollapsedByProject.has(project.id) && "hidden")}
1688+
/>
16901689
</div>
16911690
) : null}
16921691
</CollapsibleContent>

scripts/run-browser-tests.mjs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { readdirSync, statSync } from "node:fs";
44
import { spawn } from "node:child_process";
5+
import { createRequire } from "node:module";
56
import path from "node:path";
67
import process from "node:process";
78

@@ -28,16 +29,30 @@ function listBrowserTests(rootDir) {
2829
return files.toSorted();
2930
}
3031

31-
function runTestFile({ configPath, filePath, cwd, timeoutMs }) {
32+
function resolveVitestBin(cwd, configPath) {
33+
const packageDir = path.dirname(configPath);
34+
const requireFromPackage = createRequire(path.join(packageDir, "package.json"));
35+
const vitestPackageJsonPath = requireFromPackage.resolve("vitest/package.json");
36+
const vitestPackageDir = path.dirname(vitestPackageJsonPath);
37+
const vitestPackageJson = requireFromPackage(vitestPackageJsonPath);
38+
const vitestBinRelative =
39+
typeof vitestPackageJson.bin === "string"
40+
? vitestPackageJson.bin
41+
: (vitestPackageJson.bin?.vitest ?? "./vitest.mjs");
42+
43+
return path.resolve(vitestPackageDir, vitestBinRelative);
44+
}
45+
46+
function runTestFile({ configPath, filePath, cwd, timeoutMs, vitestBin }) {
3247
return new Promise((resolve) => {
33-
const vitestBin = path.join(cwd, "node_modules", "vitest", "vitest.mjs");
34-
const args = [vitestBin, "run", "--config", configPath, filePath];
35-
const relativeFile = path.relative(cwd, filePath);
48+
const configDir = path.dirname(configPath);
49+
const args = [vitestBin, "run", "--config", configPath, path.relative(configDir, filePath)];
50+
const relativeFile = path.relative(configDir, filePath);
3651

3752
console.log(`\n[browser] Running ${relativeFile}`);
3853

3954
const child = spawn(process.execPath, args, {
40-
cwd,
55+
cwd: configDir,
4156
stdio: "inherit",
4257
env: process.env,
4358
});
@@ -76,6 +91,7 @@ async function main() {
7691
timeoutArg && Number.isFinite(Number(timeoutArg)) ? Number(timeoutArg) : DEFAULT_TIMEOUT_MS;
7792

7893
const configPath = path.resolve(cwd, configArg);
94+
const vitestBin = resolveVitestBin(cwd, configPath);
7995
const browserTestRoot = path.resolve(path.dirname(configPath), "src", "components");
8096
const browserTests = listBrowserTests(browserTestRoot);
8197

@@ -92,6 +108,7 @@ async function main() {
92108
filePath,
93109
cwd,
94110
timeoutMs,
111+
vitestBin,
95112
});
96113

97114
if (!result.ok) {

0 commit comments

Comments
 (0)