open-design/apps/web/tests/artifacts/manifest.test.ts
chaoxiaoche fce444bcab
Consolidate chat comments preview on main (#2906)
* feat(web): queue chat sends

* feat(web): render code comment directives

* feat(web): add preview comments and manual edits

* fix(web): polish shared chrome controls

* fix(web): align queued send loading state

* feat(web): open primary project artifacts

* fix(web): keep queued sends and tests aligned

* fix(web): restore docked comment tools layout

* fix(web): align preview comment toolbar

* fix(web): place local cli beside handoff

* fix(web): move agent menu beside handoff

* fix(web): make project instructions a direct header action

* fix(web): compact handoff and toolbar labels

* fix(web): clarify handoff menu and annotation label

* fix(web): restore compact cursor handoff trigger

* fix(web): align agent menu trigger with handoff

* fix(web): add draw toolbar close action

* fix(web): move inspect editing into edit mode

* fix(web): avoid reserving comment sidebar in annotation mode

* fix(web): float preview comments panel

* fix(web): keep edit canvas full width

* fix(web): polish preview annotation tools

* fix(web): highlight active preview comments

* fix(web): open comments panel after annotation save

* fix(web): polish comment handoff controls

* fix(web): remove palette preview tool

* fix(web): simplify draw annotation toolbar

* fix(web): restore queued tasks into composer

* fix(web): restore queued send strip styling

* fix(web): hide internal comment target ids

* fix(web): align manual edit panel header

* test(web): cover visual interaction contracts

* fix(web): address PR feedback regressions

* fix(web): preserve artifact chrome state

* fix(daemon): restore project raw file routes

---------

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
Co-authored-by: mrcfps <mrc@powerformer.com>
2026-05-26 10:31:19 +00:00

135 lines
4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
artifactManifestNameFor,
createHtmlArtifactManifest,
inferLegacyManifest,
parseArtifactManifest,
} from '../../src/artifacts/manifest';
describe('parseArtifactManifest', () => {
it('returns null for malformed json', () => {
expect(parseArtifactManifest('{"version":1')).toBeNull();
});
it('returns null when required fields are missing', () => {
expect(parseArtifactManifest(JSON.stringify({ version: 1, kind: 'html' }))).toBeNull();
});
it('returns null for wrong version', () => {
const raw = JSON.stringify({
version: 2,
kind: 'html',
title: 'x',
entry: 'index.html',
renderer: 'html',
exports: ['html'],
});
expect(parseArtifactManifest(raw)).toBeNull();
});
it('defaults status to complete when missing', () => {
const raw = JSON.stringify({
version: 1,
kind: 'html',
title: 'x',
entry: 'index.html',
renderer: 'html',
exports: ['html'],
});
const out = parseArtifactManifest(raw);
expect(out?.status).toBe('complete');
});
it('preserves valid status when provided', () => {
const raw = JSON.stringify({
version: 1,
kind: 'html',
title: 'x',
entry: 'index.html',
renderer: 'html',
status: 'streaming',
exports: ['html'],
});
const out = parseArtifactManifest(raw);
expect(out?.status).toBe('streaming');
});
it('preserves primary file hints', () => {
const raw = JSON.stringify({
version: 1,
kind: 'html',
title: 'x',
entry: 'index.html',
renderer: 'html',
exports: ['html'],
primary: 'index.html',
});
const out = parseArtifactManifest(raw);
expect(out?.primary).toBe('index.html');
});
});
describe('inferLegacyManifest', () => {
it('infers markdown manifests for .md files', () => {
const out = inferLegacyManifest({ entry: 'README.md' });
expect(out?.kind).toBe('markdown-document');
expect(out?.renderer).toBe('markdown');
expect(out?.status).toBe('complete');
});
it('infers svg manifests for .svg files', () => {
const out = inferLegacyManifest({ entry: 'logo.svg' });
expect(out?.kind).toBe('svg');
expect(out?.renderer).toBe('svg');
expect(out?.status).toBe('complete');
});
it('returns null for non-artifact file types', () => {
expect(inferLegacyManifest({ entry: 'photo.png' })).toBeNull();
expect(inferLegacyManifest({ entry: 'archive.bin' })).toBeNull();
});
it('infers React component artifacts from JSX and TSX entries', () => {
expect(inferLegacyManifest({ entry: 'Card.jsx' })).toMatchObject({
kind: 'react-component',
renderer: 'react-component',
exports: ['jsx', 'html', 'zip'],
});
expect(inferLegacyManifest({ entry: 'Card.tsx' })).toMatchObject({
kind: 'react-component',
renderer: 'react-component',
exports: ['jsx', 'html', 'zip'],
});
});
});
describe('artifactManifestNameFor', () => {
it('handles names without extension', () => {
expect(artifactManifestNameFor('README')).toBe('README.artifact.json');
});
it('handles names with multiple dots', () => {
expect(artifactManifestNameFor('page.v2.final.html')).toBe('page.v2.final.html.artifact.json');
});
it('avoids collisions between different extensions', () => {
expect(artifactManifestNameFor('foo.html')).not.toBe(artifactManifestNameFor('foo.md'));
});
});
describe('createHtmlArtifactManifest', () => {
it('creates expected default html manifest shape', () => {
const out = createHtmlArtifactManifest({ entry: 'index.html', title: 'Landing' });
expect(out.version).toBe(1);
expect(out.kind).toBe('html');
expect(out.renderer).toBe('html');
expect(out.status).toBe('complete');
expect(out.exports).toEqual(['html', 'pdf', 'zip']);
expect(out.primary).toBe(true);
expect(out.entry).toBe('index.html');
expect(out.title).toBe('Landing');
expect(typeof out.createdAt).toBe('string');
expect(typeof out.updatedAt).toBe('string');
});
});