fix: align Target governance links to shared edge midpoints
Build and publish Eldorado documentation / build (pull_request) Successful in 38s
Build and publish Eldorado documentation / build (pull_request) Successful in 38s
This commit is contained in:
1 parent
64b2d6dae8
commit
15c5dcb31d
5 files changed
+126
-10
No files matched your search
@@ -0,0 +1,85 @@
|
||||
const {chromium} = require('playwright');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
// Inspect the rendered paths, not just the authored JSON. Also runs on downloaded SVGs.
|
||||
function inspect({mobile, xml}) {
|
||||
let svg = document.querySelector('svg[role="img"]');
|
||||
if (xml) {
|
||||
svg = new DOMParser().parseFromString(xml, 'image/svg+xml').documentElement;
|
||||
document.body.appendChild(svg);
|
||||
}
|
||||
const point = (element, p) => new DOMPoint(p.x, p.y).matrixTransform(element.getCTM());
|
||||
const boxes = [...svg.querySelectorAll('[data-node-id]')].map(node => {
|
||||
const rect = node.querySelector('rect'), box = rect.getBBox();
|
||||
const a = point(rect, box), b = point(rect, {x:box.x + box.width, y:box.y + box.height});
|
||||
return {id:node.dataset.nodeId, x:a.x, y:a.y, right:b.x, bottom:b.y, cx:(a.x+b.x)/2, cy:(a.y+b.y)/2};
|
||||
});
|
||||
const agent = boxes.find(n => n.id === 'agent');
|
||||
const errors = [], endpoints = {};
|
||||
const paths = [...svg.querySelectorAll('path[data-edge-id^="govern-"]')];
|
||||
if (paths.length !== 8) errors.push(`expected eight governance paths, got ${paths.length}`);
|
||||
for (const path of paths) {
|
||||
const id = path.dataset.edgeTo;
|
||||
const side = mobile ? 'left' : ['scope','architecture','releases'].includes(id) ? 'top'
|
||||
: ['planning','delivery','uat'].includes(id) ? 'bottom' : id === 'security' ? 'left' : 'right';
|
||||
const expected = {top:[agent.cx,agent.y],bottom:[agent.cx,agent.bottom],left:[agent.x,agent.cy],right:[agent.right,agent.cy]}[side];
|
||||
const start = point(path,path.getPointAtLength(0));
|
||||
endpoints[id] = [start.x,start.y];
|
||||
if (Math.hypot(start.x-expected[0],start.y-expected[1]) > 0.01) errors.push(`${id}: wrong ${side} midpoint`);
|
||||
const length = path.getTotalLength();
|
||||
for (let distance = 1; distance < length; distance += 2) {
|
||||
const p = point(path,path.getPointAtLength(distance));
|
||||
for (const box of boxes) {
|
||||
if (box.id === id) continue;
|
||||
if (p.x > box.x+0.1 && p.x < box.right-0.1 && p.y > box.y+0.1 && p.y < box.bottom-0.1) {
|
||||
errors.push(`${id}: crosses ${box.id}`); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xml) svg.remove();
|
||||
return {errors:[...new Set(errors)],endpoints};
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const base = process.argv[2] || 'http://127.0.0.1:4173';
|
||||
const output = `.cache/midpoints-${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 page = await browser.newPage({viewport:{width,height:1000}});
|
||||
await page.goto(`${base}/eldorado-v1/documentation/strategy/target-operating-model/`,{waitUntil:'networkidle'});
|
||||
const panel = page.locator('[data-diagram="strategy-target"]');
|
||||
const iframe = panel.locator(`:scope > .story-${width > 700 ? 'desktop' : 'mobile'}`);
|
||||
await iframe.scrollIntoViewIfNeeded();
|
||||
const frame = await (await iframe.elementHandle()).contentFrame();
|
||||
await frame.locator('svg[role="img"]').waitFor();
|
||||
const inline = await frame.evaluate(inspect,{mobile:width < 700});
|
||||
assert.deepEqual(inline.errors,[]);
|
||||
await page.screenshot({path:`${output}/${width}-inline.png`,fullPage:true});
|
||||
await frame.locator('#btn-export').click();
|
||||
const [download] = await Promise.all([page.waitForEvent('download'),frame.locator('[data-format="svg"]').click()]);
|
||||
assert.equal(await download.failure(),null);
|
||||
const path = `${output}/${width}.svg`;
|
||||
await download.saveAs(path);
|
||||
const exported = await frame.evaluate(inspect,{mobile:width < 700,xml:fs.readFileSync(path,'utf8')});
|
||||
assert.deepEqual(exported.errors,[]);
|
||||
await panel.getByRole('button',{name:/^Expand /}).click();
|
||||
const full = await (await panel.locator('dialog iframe').elementHandle()).contentFrame();
|
||||
await full.locator('svg[role="img"]').waitFor();
|
||||
// The existing fullscreen viewer always opens the radial desktop source.
|
||||
const fullscreen = await full.evaluate(inspect,{mobile:false});
|
||||
assert.deepEqual(fullscreen.errors,[]);
|
||||
await page.screenshot({path:`${output}/${width}-fullscreen.png`});
|
||||
await panel.getByRole('button',{name:'Close full-screen diagram'}).click();
|
||||
results.push({width,inline,exported,fullscreen});
|
||||
fs.writeFileSync(`${output}/results.json`,JSON.stringify(results,null,2));
|
||||
await page.close();
|
||||
}
|
||||
assert.equal(results.length,3);
|
||||
console.log('PASS midpoint geometry and no node crossings: 3 viewports × inline/fullscreen/exported SVG');
|
||||
} finally {await browser.close();}
|
||||
})().catch(error => {console.error(error);process.exitCode=1;});
|
||||
Reference in new issue
Block a user