import { describe, expect, it } from 'vitest';
import {
splitStreamingArtifact,
stripArtifact,
stripRecoveredHtmlFallbackForDisplay,
summarizeArtifactsForTranscript,
} from '../../src/artifacts/strip';
const completeHtml = '
X
X
';
describe('stripArtifact', () => {
it('removes a real artifact tag and its body from prose', () => {
const out = stripArtifact(
'Header.\n\n
x
\n\nFooter.',
);
expect(out).toBe('Header.\n\nFooter.');
});
it('preserves an artifact tag wrapped in inline backticks', () => {
const input = 'Wrap output as `demo` to ship it.';
expect(stripArtifact(input)).toBe(input);
});
it('preserves an artifact tag inside a fenced code block', () => {
const input = [
'Example:',
'```html',
'',
'
Demo
',
'',
'```',
'After.',
].join('\n');
expect(stripArtifact(input)).toBe(input);
});
it('preserves a tag wrapped in double backticks', () => {
const input = 'Use ```` here.';
expect(stripArtifact(input)).toBe(input);
});
it('returns content unchanged when no artifact open tag is present', () => {
const input = 'Just prose, no markup.';
expect(stripArtifact(input)).toBe(input);
});
it('does not truncate when an open tag has no matching close', () => {
// A bare orphan open without a close should not nuke the rest of the
// message (the previous implementation sliced to end-of-string).
const input = 'Trailing prose with no closer.';
expect(stripArtifact(input)).toBe(input);
});
it('preserves a tag inside a fenced literal whose closing fence has no trailing newline', () => {
// Renderer treats an unclosed-at-EOF fence as a code block extending to
// end of input. The stripper must do the same, otherwise a literal
// recitation tucked into a code example at the bottom of a chat reply
// gets eaten.
const input = '```js\nconst s = `demo`;\n```';
expect(stripArtifact(input)).toBe(input);
});
it('strips a real artifact that follows an indented pseudo-fence (renderer sees no fence)', () => {
// The renderer's open-fence regex disallows leading spaces; an indented
// " ```html" line is rendered as a paragraph, not a fence. The
// on the next line is therefore a real protocol tag and
// must be stripped — not preserved as fictional fenced content.
const input = [
' ```html',
'',
'
x
',
'',
'Tail.',
].join('\n');
const out = stripArtifact(input);
expect(out).not.toContain(' {
// Inline code spans are paragraph-local in the renderer; an unbalanced
// backtick in one paragraph must not bridge across a blank line to pair
// with a backtick in another paragraph and accidentally classify a real
// between them as inline code (mrcfps's 2026-05-11 repro).
const input = [
'intro `',
'',
'demo',
'',
'closing `',
].join('\n');
const out = stripArtifact(input);
expect(out).not.toContain(' {
// runtime/markdown.tsx:95-104's paragraph-accumulation loop only breaks on
// blank / fence / heading / ul / ol — it does NOT break on HR. So a buffer
// shaped `intro \`` / `---` / `…` / `---` / `closing \``
// is one paragraph in the renderer, and the two stray backticks pair to
// cover the literal artifact recitation. The skip-range walker must mirror
// that (mrcfps's 2026-05-11 05:46 follow-up).
const input = [
'intro `',
'---',
'demo',
'---',
'closing `',
].join('\n');
expect(stripArtifact(input)).toBe(input);
});
it('does not strip or other prefix-shared identifiers', () => {
// The streaming parser only treats `` gets silently truncated.
const input = 'prefix demo suffix';
expect(stripArtifact(input)).toBe(input);
});
it('preserves a tag inside a fenced block that contains a `\`\`\`html` line in its body', () => {
// The renderer closes a fence only on a bare "```\\s*$" — a "```html"
// line inside an open fence is kept as code content. The stripper must
// match that semantics or it will exit the fence early and eat a
// literal recitation that follows.
const input = [
'```js',
'const a = `
x
`;',
'```html',
'demo',
'```',
].join('\n');
expect(stripArtifact(input)).toBe(input);
});
});
describe('splitStreamingArtifact', () => {
it('surfaces an open-but-unclosed html artifact as a live box', () => {
const input = 'Building it.\n\n