|
12 | 12 | import { describe, expect, test } from "bun:test"; |
13 | 13 | import { MarkdownRenderable, RGBA, type CapturedSpan } from "@opentui/core"; |
14 | 14 | import { withTestRenderer, type Harness } from "./harness"; |
| 15 | +import { appendStreamRow } from "./shell/chrome.js"; |
| 16 | +import { createAppShell } from "./shell/index.js"; |
15 | 17 | import { transcriptSyntaxStyle } from "./stream"; |
16 | 18 | import { highlightCode } from "./syntax-highlight.js"; |
17 | 19 | import { UI } from "./theme"; |
@@ -69,6 +71,68 @@ function coloured(spans: CapturedSpan[], text: string, fg: RGBA): boolean { |
69 | 71 | return spans.some((s) => s.text.includes(text) && s.fg.equals(fg)); |
70 | 72 | } |
71 | 73 |
|
| 74 | +function hasSyntaxColour(spans: CapturedSpan[], text: string): boolean { |
| 75 | + return spans.some( |
| 76 | + (span) => |
| 77 | + span.text.includes(text) && SYNTAX_FGS.some((fg) => span.fg.equals(fg)), |
| 78 | + ); |
| 79 | +} |
| 80 | + |
| 81 | +async function expectTranscriptHighlighting( |
| 82 | + h: Harness, |
| 83 | + supportedLanguage: "js" | "ts", |
| 84 | + streaming: boolean, |
| 85 | +): Promise<void> { |
| 86 | + const shell = createAppShell(h.renderer, { |
| 87 | + terminal: { columns: 80, rows: 40 }, |
| 88 | + wireKeys: false, |
| 89 | + }); |
| 90 | + try { |
| 91 | + appendStreamRow(shell, { |
| 92 | + role: "assistant", |
| 93 | + streaming, |
| 94 | + text: [ |
| 95 | + `\`\`\`${supportedLanguage}`, |
| 96 | + "const supported_route = 42;", |
| 97 | + "```", |
| 98 | + "", |
| 99 | + "```python", |
| 100 | + "def python_plain():", |
| 101 | + ' return "visible"', |
| 102 | + "```", |
| 103 | + ].join("\n"), |
| 104 | + }); |
| 105 | + const spans = await settleSpans( |
| 106 | + h, |
| 107 | + (current) => |
| 108 | + coloured(current, "const", KEYWORD_FG) && |
| 109 | + current.some((span) => span.text.includes("python_plain")), |
| 110 | + ); |
| 111 | + |
| 112 | + expect(coloured(spans, "const", KEYWORD_FG)).toBe(true); |
| 113 | + expect(h.captureCharFrame()).toContain("python_plain"); |
| 114 | + expect(hasSyntaxColour(spans, "python_plain")).toBe(false); |
| 115 | + } finally { |
| 116 | + shell.dispose(); |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +describe("production transcript fenced rendering", () => { |
| 121 | + test("settled assistant fences use production syntax ownership", async () => { |
| 122 | + await withTestRenderer( |
| 123 | + (h) => expectTranscriptHighlighting(h, "js", false), |
| 124 | + { width: 80, height: 40 }, |
| 125 | + ); |
| 126 | + }, 30000); |
| 127 | + |
| 128 | + test("streaming assistant fences use production syntax ownership", async () => { |
| 129 | + await withTestRenderer((h) => expectTranscriptHighlighting(h, "ts", true), { |
| 130 | + width: 80, |
| 131 | + height: 40, |
| 132 | + }); |
| 133 | + }, 30000); |
| 134 | +}); |
| 135 | + |
72 | 136 | describe("native fenced rendering", () => { |
73 | 137 | test("a js fence colours keywords, strings, comments, and numbers", async () => { |
74 | 138 | await withTestRenderer( |
|
0 commit comments