Files
site-builder/craft/scripts/e2e-mobile-parity-entry.ts
shadowdaoandClaude Sonnet 5.5 c4b6802cab Add real-browser mobile parity test (Firefox + Chromium, 390/1280)
npm run test:browser:mobile; MOBILE_PARITY_RECORD/BASELINE compare desktop boxes against a base-commit snapshot.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:10:15 -08:00

74 lines
4.4 KiB
TypeScript

// Bundled by scripts/e2e-mobile-parity.mjs. Builds the documents the real-browser mobile test measures.
// Uses only exports that exist before and after the mobile-parity fix so the SAME harness can record a baseline.
import { exportBodyHtml, exportComposedPreviewHtml } from '../src/utils/html-export';
const node = (type: string, props: Record<string, unknown>, nodes: string[] = [], extra: Record<string, unknown> = {}) => ({
type: { resolvedName: type }, isCanvas: type === 'Container', props, displayName: type, custom: {}, hidden: false, nodes, linkedNodes: {}, ...extra,
});
const LONG = 'Honest prices, straight answers and work that is finished when we say it will be, every time.';
/** ColumnLayout with `n` Container columns each holding one TextBlock. */
function addColumns(nodes: Record<string, any>, id: string, props: Record<string, unknown>, n: number) {
const linked: Record<string, string> = {};
for (let i = 0; i < n; i++) {
const c = `${id}-c${i}`;
const t = `${id}-t${i}`;
nodes[c] = node('Container', { tag: 'div', style: { padding: '8px' } }, [t], { parent: id });
nodes[t] = node('TextBlock', { text: `${LONG} (${id} col ${i + 1})`, style: { fontSize: '16px' } }, [], { parent: c });
linked[`col-${i}`] = c;
}
nodes[id] = node('ColumnLayout', { columns: n, gap: '16px', anchorId: id, ...props }, [], { parent: 'ROOT', linkedNodes: linked });
}
export const NAV_LINKS = ['Home', 'About us', 'Services', 'Our work', 'Pricing', 'Blog', 'Contact'].map((text, i, a) => ({
text, href: '#' + text.toLowerCase().replace(/\s/g, '-'), ...(i === a.length - 1 ? { isCta: true } : {}),
}));
export function syntheticState(navShowMobileMenu: boolean | undefined): string {
const nodes: Record<string, any> = {};
const kids: string[] = ['nav'];
const navProps: Record<string, unknown> = { logoText: 'Acme Plumbing & Heating', links: NAV_LINKS, padding: '16px 24px' };
if (navShowMobileMenu !== undefined) navProps.showMobileMenu = navShowMobileMenu;
nodes.nav = node('Navbar', navProps, [], { parent: 'ROOT' });
const specs: Array<[string, Record<string, unknown>, number]> = [
['c2', { split: '50-50' }, 2],
['centered', { split: '50-50', gap: '48px', style: { alignItems: 'center' } }, 2],
['c3', { split: '33-33-33' }, 3],
['c4', { split: '25-25-25-25' }, 4],
['c6', { split: '16-16-16-16-16-16' }, 6],
['sideL', { split: '30-70' }, 2],
['sideR', { split: '70-30' }, 2],
['optout', { split: '50-50', styleMobile: { flexDirection: 'row' } }, 2],
];
for (const [id, p, n] of specs) { addColumns(nodes, id, p, n); kids.push(id); }
// An existing author-built flex row (already stacks via the global rule) must be unaffected.
nodes.row = node('Container', { tag: 'div', style: { display: 'flex', flexDirection: 'row', gap: '12px' } }, ['rt1', 'rt2'], { parent: 'ROOT' });
nodes.rt1 = node('TextBlock', { text: 'Row item one', style: {} }, [], { parent: 'row' });
nodes.rt2 = node('TextBlock', { text: 'Row item two', style: {} }, [], { parent: 'row' });
kids.push('row');
nodes.ROOT = node('Container', { tag: 'div', style: {} }, kids);
return JSON.stringify(nodes);
}
export function renderSynthetic(navShowMobileMenu: boolean | undefined): string {
const state = syntheticState(navShowMobileMenu);
const body = exportBodyHtml(state).html;
return exportComposedPreviewHtml(state, body, { title: 'Synthetic', includeFonts: false });
}
/** Stored OLS page rendered by the CURRENT frontend: regenerated body + current head. */
export function renderStored(project: any, pageIdx: number): string {
const page = project.pages_craft_state[pageIdx];
const hdr = project.header_craft_state ? exportBodyHtml(project.header_craft_state, 'header').html : '';
const ftr = project.footer_craft_state ? exportBodyHtml(project.footer_craft_state, 'footer').html : '';
const body = exportBodyHtml(page.craftState).html;
return exportComposedPreviewHtml(page.craftState, hdr + body + ftr, { title: page.name, includeFonts: true, headCode: project.design?.headCode, design: project.design });
}
/** The checked-in (OLD) stored body under the CURRENT head: "existing site, next publish". */
export function renderOldBodyNewHead(project: any, storedHtml: string): string {
const m = storedHtml.match(/<body[^>]*>([\s\S]*)<\/body>/i);
const body = m ? m[1] : storedHtml;
return exportComposedPreviewHtml(project.pages_craft_state[0].craftState, body, { title: 'old', includeFonts: true, design: project.design });
}