- The render is uploaded with the new review_render_upload action (backend kind review_render, the only image a review job accepts); mocks follow. - summarizeCanvas() never lets a newline or a quote into a node line (the server validates review summaries line by line); test. - test:browser:sitesmith-cleaned (Firefox + Chromium): a page published from a cleaned ordinary Sitesmith response requests nothing outside the allow-listed hosts (own assets, fonts, Font Awesome CDN, images.pexels.com, placehold.co) and has no inline handlers or javascript:/data: URL attributes; the same response uncleaned is the control (requests example.com). All requests are answered or aborted locally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 lines
2.0 KiB
TypeScript
32 lines
2.0 KiB
TypeScript
// Bundled (esbuild) by scripts/e2e-sitesmith-cleaned.mjs: turns one ordinary Sitesmith response into a
|
|
// published-style page, with or without the apply-time cleaner, through the real exporter.
|
|
import { cleanAiResponse } from '../src/sitesmith/clean-ai-response';
|
|
import { flattenTreeForCraft } from '../src/utils/craft-tree';
|
|
import { exportToHtml } from '../src/utils/html-export';
|
|
import { DEFAULT_SITE_DESIGN } from '../src/state/SiteDesignContext';
|
|
import type { SitesmithResponse } from '../src/types/sitesmith';
|
|
|
|
const n = (type: string, props: Record<string, unknown>, nodes?: any[]) => ({ type: { resolvedName: type }, props, ...(nodes ? { nodes } : {}) });
|
|
|
|
export const RESPONSE: SitesmithResponse = {
|
|
type: 'replace', scope: 'page', message: 'Built a page.',
|
|
pages: [{ name: 'Home', tree: n('Container', { node_id: 'ROOT', tag: 'div' }, [
|
|
n('Navbar', { node_id: 'nav', logoType: 'text', logoText: 'Brand', logoUrl: '/', links: [{ text: 'Home', href: '/' }, { text: 'Old link', href: 'javascript:void(0)' }] }),
|
|
n('Heading', { node_id: 'h', text: 'Fresh bread every morning', level: 'h1' }),
|
|
n('ImageBlock', { node_id: 'i1', src: 'assets/hero.png', alt: 'Our bakery' }),
|
|
n('ImageBlock', { node_id: 'i2', src: 'https://example.com/photo.png', alt: 'Off-list host' }),
|
|
n('ImageBlock', { node_id: 'i3', src: 'https://placehold.co/600x400?text=Team', alt: 'Placeholder' }),
|
|
n('Container', { node_id: 'c', tag: 'div', style: { backgroundImage: 'url(https://example.com/bg.png)', padding: '24px' } }, [
|
|
n('ButtonLink', { node_id: 'b', text: 'Book a table', href: 'https://example.com/book' }),
|
|
]),
|
|
n('VideoBlock', { node_id: 'v', videoUrl: 'https://example.com/clip.mp4' }),
|
|
]) }],
|
|
};
|
|
|
|
export function render(clean: boolean): string {
|
|
const resp = clean ? cleanAiResponse(RESPONSE) : RESPONSE;
|
|
if (resp.type !== 'replace') throw new Error('unexpected');
|
|
const flat = flattenTreeForCraft(resp.pages[0].tree);
|
|
return exportToHtml(JSON.stringify(flat.nodes), { title: 'Home', includeFonts: true, design: DEFAULT_SITE_DESIGN }).html;
|
|
}
|