|
| 1 | +import { testRender } from "@opentui/react/test-utils"; |
| 2 | +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; |
| 3 | +import { tmpdir } from "node:os"; |
| 4 | +import { join } from "node:path"; |
| 5 | +import { act } from "react"; |
| 6 | +import { loadAppBootstrap } from "../src/core/loaders"; |
| 7 | +import { AppHost } from "../src/ui/AppHost"; |
| 8 | + |
| 9 | +function runGit(cwd: string, ...args: string[]) { |
| 10 | + const proc = Bun.spawnSync(["git", ...args], { |
| 11 | + cwd, |
| 12 | + stderr: "pipe", |
| 13 | + stdin: "ignore", |
| 14 | + stdout: "pipe", |
| 15 | + }); |
| 16 | + |
| 17 | + if (proc.exitCode !== 0) { |
| 18 | + throw new Error(Buffer.from(proc.stderr).toString("utf8").trim()); |
| 19 | + } |
| 20 | +} |
| 21 | + |
| 22 | +/** Generate a large untracked file that stays small on disk for manual render checks. */ |
| 23 | +function createLargeFileBody(lineCount: number) { |
| 24 | + return ( |
| 25 | + Array.from({ length: lineCount }, (_, index) => { |
| 26 | + if (index === 0) { |
| 27 | + return "visible-first-line"; |
| 28 | + } |
| 29 | + if (index === lineCount - 1) { |
| 30 | + return "the widest generated line"; |
| 31 | + } |
| 32 | + return "x"; |
| 33 | + }).join("\n") + "\n" |
| 34 | + ); |
| 35 | +} |
| 36 | + |
| 37 | +const lineCount = Number.parseInt(process.argv[2] ?? "100000", 10); |
| 38 | +const fixtureKind = process.argv[3] === "tracked" ? "tracked" : "untracked"; |
| 39 | +if (!Number.isFinite(lineCount) || lineCount <= 0) { |
| 40 | + throw new Error("Usage: bun run scripts/test-large-untracked-render.tsx [line-count] [tracked]"); |
| 41 | +} |
| 42 | + |
| 43 | +const repo = mkdtempSync(join(tmpdir(), "hunk-large-untracked-render-")); |
| 44 | +try { |
| 45 | + runGit(repo, "init", "--initial-branch", "main"); |
| 46 | + runGit(repo, "config", "user.name", "Test User"); |
| 47 | + runGit(repo, "config", "user.email", "test@example.com"); |
| 48 | + const largePath = fixtureKind === "tracked" ? "large-tracked.txt" : "large-untracked.txt"; |
| 49 | + writeFileSync(join(repo, "tracked.txt"), "tracked\n"); |
| 50 | + if (fixtureKind === "tracked") { |
| 51 | + writeFileSync(join(repo, largePath), "original\n"); |
| 52 | + } |
| 53 | + runGit(repo, "add", "."); |
| 54 | + runGit(repo, "commit", "-m", "initial"); |
| 55 | + writeFileSync(join(repo, largePath), createLargeFileBody(lineCount)); |
| 56 | + |
| 57 | + const bootstrap = await loadAppBootstrap( |
| 58 | + { kind: "vcs", staged: false, options: { mode: "stack" } }, |
| 59 | + { cwd: repo }, |
| 60 | + ); |
| 61 | + const setup = await testRender(<AppHost bootstrap={bootstrap} />, { width: 120, height: 30 }); |
| 62 | + |
| 63 | + try { |
| 64 | + await act(async () => { |
| 65 | + await setup.renderOnce(); |
| 66 | + }); |
| 67 | + |
| 68 | + const frame = setup.captureCharFrame(); |
| 69 | + console.log( |
| 70 | + JSON.stringify( |
| 71 | + { |
| 72 | + containsHeader: frame.includes(`@@ -0,0 +1,${lineCount} @@`), |
| 73 | + containsPath: frame.includes(largePath), |
| 74 | + containsSkippedLargeMessage: frame.includes("File too large to render"), |
| 75 | + containsVisibleLine: frame.includes("visible-first-line"), |
| 76 | + fileCount: bootstrap.changeset.files.length, |
| 77 | + firstFileStats: bootstrap.changeset.files[0]?.stats, |
| 78 | + firstFileStatsTruncated: bootstrap.changeset.files[0]?.statsTruncated, |
| 79 | + fixtureKind, |
| 80 | + lineCount, |
| 81 | + renderedFrameBytes: Buffer.byteLength(frame), |
| 82 | + }, |
| 83 | + null, |
| 84 | + 2, |
| 85 | + ), |
| 86 | + ); |
| 87 | + } finally { |
| 88 | + await act(async () => { |
| 89 | + setup.renderer.destroy(); |
| 90 | + }); |
| 91 | + } |
| 92 | +} finally { |
| 93 | + rmSync(repo, { force: true, recursive: true }); |
| 94 | +} |
0 commit comments