fix: restore inline diagram controls and synchronize portal theme
Build and publish Eldorado documentation / build (pull_request) Successful in 43s

This commit is contained in:
jarvis-at-skic committed 2026-10-05 12:18:45 +00:00
1 parent 62d2afc152
commit 9347de298f
9 files changed
+267 -6

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
const {chromium} = require('playwright');
const assert = require('node:assert/strict');
const fs = require('node:fs');
(async () => {
const base = process.argv[2] || 'http://127.0.0.1:4173';
const output = `.cache/inline-${process.argv[3] || 'local'}`;
fs.mkdirSync(output, {recursive: true});
const browser = await chromium.launch({headless: true, executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, args: ['--no-sandbox']});
const results = [];
try {
for (const width of [1440, 390, 320]) {
const context = await browser.newContext({viewport: {width, height: 1000}, colorScheme: 'light'});
const page = await context.newPage();
await page.goto(`${base}/eldorado-v1/documentation/strategy/`, {waitUntil: 'networkidle'});
const panel = page.locator('.archify-panel--story');
const iframe = panel.locator(width > 700 ? '.story-desktop' : '.story-mobile');
await iframe.scrollIntoViewIfNeeded();
const frame = await (await iframe.elementHandle()).contentFrame();
const theme = async value => {
await page.waitForFunction(t => document.documentElement.dataset.theme === t, value);
await frame.waitForFunction(t => document.documentElement.dataset.theme === t, value);
};
await theme('light');
for (const id of ['btn-theme', 'btn-preset', 'btn-export']) {
const box = await frame.locator(`#${id}`).boundingBox();
assert(box && box.width >= 40 && box.height >= 44, id);
}
const light = await frame.locator('body').evaluate(n => getComputedStyle(n).backgroundColor);
await frame.locator('#btn-theme').click(); await theme('dark');
assert.notEqual(await frame.locator('body').evaluate(n => getComputedStyle(n).backgroundColor), light);
await frame.locator('#btn-theme').click(); await theme('light');
// Mobile portal theme control lives in the navigation drawer.
const portalTheme = async target => {
if (width <= 700) await page.getByRole('button', {name: 'Toggle navigation bar'}).click();
for (let attempt = 0; attempt < 3; attempt++) {
await page.getByRole('button', {name: /Switch between dark and light mode/}).filter({visible: true}).click();
if (await page.locator('html').getAttribute('data-theme') === target) break;
}
if (width <= 700) await page.getByRole('button', {name: 'Close navigation bar'}).click();
};
await portalTheme('dark'); await theme('dark');
await portalTheme('light'); await theme('light');
for (const preset of ['signal-flow', 'blueprint', 'editorial', 'classic']) {
await frame.locator('#btn-preset').click();
await frame.locator(`[data-preset-value="${preset}"]`).click();
assert.equal(await frame.locator('html').getAttribute('data-preset'), preset);
}
// A theme change must not reload the iframe or lose the selected visual preset.
await frame.locator('#btn-preset').click();
await frame.locator('[data-preset-value="blueprint"]').click();
await portalTheme('dark'); await theme('dark');
assert.equal(await frame.locator('html').getAttribute('data-preset'), 'blueprint');
const exports = [];
for (const format of ['png', 'jpeg', 'webp', 'svg', 'svg-light', 'svg-dark', 'webm']) {
await frame.locator('#btn-export').click();
if (format === 'webm' && await frame.locator('[data-format="webm"]').isDisabled()) {
exports.push({format, unsupported: true});
await frame.locator('#btn-export').click();
continue;
}
console.log(`Export ${width} ${format}`);
const [download] = await Promise.all([
page.waitForEvent('download', {timeout: 60000}),
frame.locator(`[data-format="${format}"]`).click(),
]);
assert.equal(await download.failure(), null);
const path = `${output}/${width}-${format}-${download.suggestedFilename()}`;
await download.saveAs(path);
const bytes = fs.readFileSync(path);
assert(bytes.length > 1000);
if (format.startsWith('svg')) {
const valid = await page.evaluate(xml => {
const doc = new DOMParser().parseFromString(xml, 'image/svg+xml');
return !doc.querySelector('parsererror') && doc.documentElement.localName === 'svg' && doc.querySelectorAll('text').length > 0;
}, bytes.toString());
assert(valid, format);
} else {
const mime = {png:'image/png', jpeg:'image/jpeg', webp:'image/webp', webm:'video/webm'}[format];
const dimensions = await page.evaluate(async ({data, mime}) => {
const media = document.createElement(mime.startsWith('video') ? 'video' : 'img');
const loaded = new Promise((resolve, reject) => {media[mime.startsWith('video') ? 'onloadedmetadata' : 'onload'] = resolve; media.onerror = reject;});
media.src = `data:${mime};base64,${data}`; await loaded;
return [media.videoWidth || media.naturalWidth, media.videoHeight || media.naturalHeight];
}, {data: bytes.toString('base64'), mime});
assert(dimensions.every(n => n > 0), format);
}
exports.push({format, path, bytes:bytes.length});
}
const opener = panel.getByRole('button', {name:/^Expand /});
await opener.click();
let full = await (await panel.locator('dialog iframe').elementHandle()).contentFrame();
await full.waitForFunction(() => document.documentElement.dataset.theme === 'dark');
await full.locator('#btn-theme').click(); await theme('light');
const close = panel.getByRole('button', {name:'Close full-screen diagram'});
assert.equal(await close.innerText(), '×');
await close.click(); assert(await opener.evaluate(n => n === document.activeElement));
await opener.click();
full = await (await panel.locator('dialog iframe').elementHandle()).contentFrame();
await full.waitForFunction(() => document.documentElement.dataset.theme === 'light');
await close.focus(); await page.keyboard.press('Escape');
assert(await opener.evaluate(n => n === document.activeElement));
await iframe.scrollIntoViewIfNeeded();
await page.evaluate(() => scrollTo(0, 0));
await page.screenshot({path:`${output}/${width}.png`, fullPage:true});
await frame.locator('#btn-theme').click(); await theme('dark');
await page.locator('.strategy-content a[href$="/strategy/target-operating-model/"]').filter({visible:true}).click();
const next = page.locator(`.archify-panel--story[data-diagram="strategy-target"] > .story-${width > 700 ? 'desktop' : 'mobile'}`);
await next.scrollIntoViewIfNeeded();
await (await (await next.elementHandle()).contentFrame()).waitForFunction(() => document.documentElement.dataset.theme === 'dark');
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth));
results.push({width, themeSync:true, presets:4, exports, fullscreen:true, routeTheme:true});
fs.writeFileSync(`${output}/results.json`, JSON.stringify(results,null,2));
await context.close();
}
console.log(`PASS ${results.length} viewports: bidirectional themes, 4 presets, all supported exports decoded, fullscreen and route persistence`);
} finally {await browser.close();}
})().catch(error => {console.error(error); process.exitCode = 1;});