fix: restore inline diagram controls and synchronize portal theme
Build and publish Eldorado documentation / build (pull_request) Successful in 43s
Build and publish Eldorado documentation / build (pull_request) Successful in 43s
This commit is contained in:
1 parent
62d2afc152
commit
9347de298f
9 files changed
+267
-6
No files matched your search
@@ -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;});
|
||||
Reference in new issue
Block a user