Files
documentation/scripts/browser/sdlc-cycle.cjs
jarvis-at-skic 29beebb379
Build and publish Eldorado documentation / build (pull_request) Successful in 51s
docs: lead Strategy with autonomous SDLC cycle
2026-10-05 16:32:51 +00:00

54 lines
3.7 KiB
JavaScript

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/cycle-${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/`, {waitUntil:'networkidle'});
if (width < 700) await page.getByRole('button', {name:'Toggle navigation bar'}).click();
const nav = page.getByRole('link', {name:'Strategy', exact:true}).filter({visible:true});
assert((await nav.getAttribute('href')).endsWith('/strategy/target-operating-model/'));
await nav.click();
await page.waitForURL('**/strategy/target-operating-model/');
for (const route of ['target-operating-model/', '']) {
await page.goto(`${base}/eldorado-v1/documentation/strategy/${route}`, {waitUntil:'networkidle'});
if (width === 1440) assert.equal(await page.locator('.theme-doc-sidebar-menu .menu__link').first().innerText(), 'Target operating model');
const iframe = page.locator(`.archify-panel--story > .story-${width > 700 ? 'desktop' : 'mobile'}`);
await iframe.scrollIntoViewIfNeeded();
const frame = await (await iframe.elementHandle()).contentFrame();
await frame.locator('svg[role="img"]').waitFor();
const metrics = await frame.evaluate(() => {
const svg = document.querySelector('svg[role="img"]');
const labels = [...svg.querySelectorAll('text[data-node-label]')].map(n => ({text:n.textContent,px:parseFloat(getComputedStyle(n).fontSize)*n.getScreenCTM().a}));
const edges = [...svg.querySelectorAll('path[data-edge-id^="stage-"]')].map(n => [n.dataset.edgeFrom,n.dataset.edgeTo,n.getAttribute('marker-end')]);
const overflow = [...svg.querySelectorAll('[data-node-id]')].flatMap(n => {
const box = n.querySelector('rect').getBoundingClientRect();
return [...n.querySelectorAll('text')].filter(t => {const b=t.getBoundingClientRect();return b.left<box.left||b.right>box.right||b.top<box.top||b.bottom>box.bottom;}).map(t=>t.textContent);
});
return {labels,edges,overflow};
});
const ids = ['scope','architecture','ui-ux','planning','delivery','uat','releases'];
assert.deepEqual(metrics.edges.map(e=>e.slice(0,2)), ids.map((id,i)=>[id,ids[(i+1)%ids.length]]));
assert(metrics.edges.every(e=>e[2]?.startsWith('url(')), 'all stages must have arrowheads');
assert(metrics.labels.every(n=>n.px>=14), JSON.stringify(metrics.labels));
assert.deepEqual(metrics.overflow, []);
assert(!await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth));
await page.evaluate(()=>scrollTo(0,0));
await page.screenshot({path:`${output}/${width}-${route ? 'target' : 'landing'}.png`,fullPage:true});
results.push({width,route,...metrics});
fs.writeFileSync(`${output}/results.json`,JSON.stringify(results,null,2));
}
await context.close();
}
assert.equal(results.length,6);
console.log('PASS 6 cycle routes/viewports: navigation, sidebar order, exact directed loop, arrowheads, >=14px labels, text bounds and no overflow');
} finally {await browser.close();}
})().catch(error=>{console.error(error);process.exitCode=1;});