test(tui): await diff tree context readiness (#37501)

Co-authored-by: James Long <longster@gmail.com>
This commit is contained in:
opencode-agent[bot]
2026-07-17 10:41:00 -04:00
committed by GitHub
co-authored by James Long
parent 44f7bb71c1
commit 651751405d
@@ -3,6 +3,7 @@ import { describe, expect, test } from "bun:test"
import { RGBA } from "@opentui/core" import { RGBA } from "@opentui/core"
import { testRender } from "@opentui/solid" import { testRender } from "@opentui/solid"
import type { JSX } from "solid-js" import type { JSX } from "solid-js"
import { onMount, type ParentProps } from "solid-js"
import { createTuiResolvedConfig } from "../../fixture/tui-runtime" import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
import { ThemeProvider } from "../../../src/context/theme" import { ThemeProvider } from "../../../src/context/theme"
import { ConfigProvider } from "../../../src/config" import { ConfigProvider } from "../../../src/config"
@@ -27,42 +28,33 @@ const theme = {
describe("DiffViewerFileTree", () => { describe("DiffViewerFileTree", () => {
test.skip("renders sorted hierarchical file rows", async () => { test.skip("renders sorted hierarchical file rows", async () => {
const app = await testRender( const lines = visibleLines(
() => await renderFrame(() => (
withTheme(() => ( <DiffViewerFileTree
<DiffViewerFileTree width={32}
width={32} files={[
files={[ { file: "z-file.ts" },
{ file: "z-file.ts" }, { file: "b/file.ts" },
{ file: "b/file.ts" }, { file: "a/zeta.ts" },
{ file: "a/zeta.ts" }, { file: "b/alpha.ts" },
{ file: "b/alpha.ts" }, { file: "a/alpha.ts" },
{ file: "a/alpha.ts" }, ]}
]} loading={false}
loading={false} error={undefined}
error={undefined} theme={theme}
theme={theme} focused={true}
focused={true} />
/> )),
)),
{ width: 40, height: 20 },
) )
try { expect(lines).toEqual([
await renderOnceSettled(app) "▾ a",
const lines = visibleLines(app.captureCharFrame()) "│ ├─ alpha.ts ?",
"│ └─ zeta.ts ?",
expect(lines).toEqual([ "├─ ▾ b",
"▾ a", "│ ├─ alpha.ts ?",
"│ alpha.ts ?", "│ file.ts ?",
"│ └─ zeta.ts ?", ])
"├─ ▾ b",
"│ ├─ alpha.ts ?",
"│ └─ file.ts ?",
])
} finally {
app.renderer.destroy()
}
}) })
test("keeps loading and error quiet while rendering an empty settled state", async () => { test("keeps loading and error quiet while rendering an empty settled state", async () => {
@@ -152,41 +144,35 @@ describe("DiffViewerFileTree", () => {
}) })
async function renderFrame(component: () => JSX.Element) { async function renderFrame(component: () => JSX.Element) {
const app = await testRender(() => withTheme(component), { width: 40, height: 10 }) const mounted = Promise.withResolvers<void>()
const app = await testRender(() => withTheme(component, mounted.resolve), { width: 40, height: 10 })
try { try {
await renderOnceSettled(app) await mounted.promise
return await captureSettledFrame(app) await app.renderOnce()
await app.renderOnce()
return app.captureCharFrame()
} finally { } finally {
app.renderer.destroy() app.renderer.destroy()
} }
} }
async function renderOnceSettled(app: Awaited<ReturnType<typeof testRender>>) { function withTheme(component: () => JSX.Element, onReady = () => {}) {
await app.renderOnce()
await new Promise((resolve) => setTimeout(resolve, 25))
await app.renderOnce()
}
async function captureSettledFrame(app: Awaited<ReturnType<typeof testRender>>) {
for (let attempt = 0; attempt < 5; attempt++) {
const frame = app.captureCharFrame()
if (visibleLines(frame).length > 0) return frame
await new Promise((resolve) => setTimeout(resolve, 25))
await app.renderOnce()
}
return app.captureCharFrame()
}
function withTheme(component: () => JSX.Element) {
return ( return (
<TestTuiContexts> <TestTuiContexts>
<ConfigProvider config={createTuiResolvedConfig()}> <ConfigProvider config={createTuiResolvedConfig()}>
<ThemeProvider mode="dark">{component()}</ThemeProvider> <ThemeProvider mode="dark">
<Ready onReady={onReady}>{component()}</Ready>
</ThemeProvider>
</ConfigProvider> </ConfigProvider>
</TestTuiContexts> </TestTuiContexts>
) )
} }
function Ready(props: ParentProps<{ onReady: () => void }>) {
onMount(props.onReady)
return props.children
}
function visibleLines(frame: string) { function visibleLines(frame: string) {
return frame return frame
.split("\n") .split("\n")