|
| 1 | +/** @jsxImportSource @opentui/solid */ |
| 2 | +import { testRender } from "@opentui/solid" |
| 3 | +import { afterEach, expect, test } from "bun:test" |
| 4 | +import { resetSetupComplete } from "../../src/component/altimate-onboarding" |
| 5 | +import { WelcomePanel } from "../../src/component/welcome-panel" |
| 6 | +import { TuiConfigProvider } from "../../src/config" |
| 7 | +import { FULL_MIN_HEIGHT, FULL_MIN_WIDTH, MEDIUM_MIN_WIDTH } from "../../src/component/welcome-panel-utils" |
| 8 | +import { ArgsProvider } from "../../src/context/args" |
| 9 | +import { ExitProvider } from "../../src/context/exit" |
| 10 | +import { KVProvider } from "../../src/context/kv" |
| 11 | +import { ProjectProvider } from "../../src/context/project" |
| 12 | +import { RouteProvider } from "../../src/context/route" |
| 13 | +import { SDKProvider } from "../../src/context/sdk" |
| 14 | +import { SyncProvider } from "../../src/context/sync" |
| 15 | +import { ThemeProvider } from "../../src/context/theme" |
| 16 | +import { ToastProvider } from "../../src/ui/toast" |
| 17 | +import { TestTuiContexts } from "../fixture/tui-environment" |
| 18 | +import { createTuiResolvedConfig } from "../fixture/tui-runtime" |
| 19 | +import { createEventSource, createFetch, directory } from "../fixture/tui-sdk" |
| 20 | + |
| 21 | +// Render the REAL WelcomePanel through the routes' variant sizing and assert the |
| 22 | +// rendered content per variant — the piece the pure welcomePanelVariant unit test |
| 23 | +// can't cover (that the .tsx Switch actually renders the right box for a variant). |
| 24 | +// Sizing is driven by the availableWidth/availableHeight props exactly as the |
| 25 | +// routes pass them; the canvas is generous so content is captured without clipping. |
| 26 | +async function renderPanel(availableWidth: number, availableHeight: number) { |
| 27 | + resetSetupComplete() // ready() = false → deterministic (unconnected: shows the connect CTA) |
| 28 | + const calls = createFetch() |
| 29 | + const source = createEventSource() |
| 30 | + const app = await testRender( |
| 31 | + () => ( |
| 32 | + <TestTuiContexts> |
| 33 | + <ExitProvider exit={() => {}}> |
| 34 | + <TuiConfigProvider config={createTuiResolvedConfig()}> |
| 35 | + <ArgsProvider> |
| 36 | + <KVProvider> |
| 37 | + <ToastProvider> |
| 38 | + <RouteProvider> |
| 39 | + <SDKProvider url="http://test" directory={directory} fetch={calls.fetch} events={source.source}> |
| 40 | + <ProjectProvider> |
| 41 | + <SyncProvider> |
| 42 | + <ThemeProvider mode="dark"> |
| 43 | + <WelcomePanel availableWidth={availableWidth} availableHeight={availableHeight} /> |
| 44 | + </ThemeProvider> |
| 45 | + </SyncProvider> |
| 46 | + </ProjectProvider> |
| 47 | + </SDKProvider> |
| 48 | + </RouteProvider> |
| 49 | + </ToastProvider> |
| 50 | + </KVProvider> |
| 51 | + </ArgsProvider> |
| 52 | + </TuiConfigProvider> |
| 53 | + </ExitProvider> |
| 54 | + </TestTuiContexts> |
| 55 | + ), |
| 56 | + { width: 160, height: 40 }, |
| 57 | + ) |
| 58 | + await app.renderOnce() |
| 59 | + await new Promise((resolve) => setTimeout(resolve, 25)) |
| 60 | + await app.renderOnce() |
| 61 | + let frame = app.captureCharFrame() |
| 62 | + for (let attempt = 0; attempt < 5 && frame.trim().length === 0; attempt++) { |
| 63 | + await new Promise((resolve) => setTimeout(resolve, 25)) |
| 64 | + await app.renderOnce() |
| 65 | + frame = app.captureCharFrame() |
| 66 | + } |
| 67 | + return { app, frame } |
| 68 | +} |
| 69 | + |
| 70 | +let current: Awaited<ReturnType<typeof testRender>> | undefined |
| 71 | +afterEach(() => { |
| 72 | + current?.renderer.destroy() |
| 73 | + current = undefined |
| 74 | +}) |
| 75 | + |
| 76 | +test("full variant renders the wordmark + what-is section at a large available size", async () => { |
| 77 | + const { app, frame } = await renderPanel(FULL_MIN_WIDTH, FULL_MIN_HEIGHT) |
| 78 | + current = app |
| 79 | + // "What is Altimate Code" is unique to the full two-column box. |
| 80 | + expect(frame).toContain("What is Altimate Code") |
| 81 | +}) |
| 82 | + |
| 83 | +test("medium variant renders the condensed line (no what-is section) below the full width floor", async () => { |
| 84 | + const { app, frame } = await renderPanel(FULL_MIN_WIDTH - 1, FULL_MIN_HEIGHT) |
| 85 | + current = app |
| 86 | + expect(frame).toContain("Gives your AI real context") |
| 87 | + expect(frame).not.toContain("What is Altimate Code") |
| 88 | +}) |
| 89 | + |
| 90 | +test("compact variant renders a single line below the medium width floor", async () => { |
| 91 | + const { app, frame } = await renderPanel(MEDIUM_MIN_WIDTH - 1, FULL_MIN_HEIGHT) |
| 92 | + current = app |
| 93 | + // Unconnected → the connect CTA; neither the medium nor full body is present. |
| 94 | + expect(frame).toContain("Connect your AI model to start") |
| 95 | + expect(frame).not.toContain("Gives your AI real context") |
| 96 | + expect(frame).not.toContain("What is Altimate Code") |
| 97 | +}) |
0 commit comments