71 lines
5.7 KiB
JavaScript
71 lines
5.7 KiB
JavaScript
import { readFile, writeFile } from 'node:fs/promises';
|
|
import { pathToFileURL } from 'node:url';
|
|
const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href);
|
|
const output = 'test-results/org-chart-final-check';
|
|
const baselineMode = process.argv.includes('--baseline');
|
|
const repairMode = process.argv.includes('--geometry-repair');
|
|
const browser = await chromium.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true });
|
|
const measurements = [], results = [], errors = [];
|
|
const check = (ok, name, actual) => results.push({ name, status: ok ? 'PASS' : 'FAIL', actual });
|
|
const previous = baselineMode ? [] : JSON.parse(await readFile(`${output}/logo-before.json`, 'utf8'));
|
|
try {
|
|
for (const story of ['department-colors', 'full-composition']) {
|
|
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
|
let expectedImageFailure = false;
|
|
page.on('pageerror', error => errors.push(error.message));
|
|
page.on('console', item => { if (item.type() === 'error' && !(expectedImageFailure && item.text().includes('ERR_INVALID_URL'))) errors.push(item.text()); });
|
|
await page.goto(`${process.env.ORGCHART_STORY_ORIGIN}/iframe.html?id=orgchart--${story}&viewMode=story`);
|
|
await page.waitForFunction(() => {
|
|
const root = document.querySelector('.hm-orgchart-host, .hm-orgchart-component-host')?.shadowRoot;
|
|
return root?.querySelector('[data-card-asset]') && [...root.querySelectorAll('link')].every(link => link.sheet);
|
|
});
|
|
await page.evaluate(async () => {
|
|
await document.fonts.ready;
|
|
const root = document.querySelector('.hm-orgchart-host, .hm-orgchart-component-host').shadowRoot;
|
|
await Promise.all([...root.querySelectorAll('img')].map(image => image.decode().catch(() => {})));
|
|
});
|
|
const logos = await page.evaluate(() => {
|
|
const root = document.querySelector('.hm-orgchart-host, .hm-orgchart-component-host').shadowRoot;
|
|
const rect = element => { const b = element.getBoundingClientRect(); return { x: b.x, y: b.y, width: b.width, height: b.height }; };
|
|
return [...root.querySelectorAll('[data-card-kind] .hm-orgchart-affiliation-mark')].map(mark => {
|
|
const img = mark.querySelector('img'), style = getComputedStyle(mark), fallback = mark.querySelector('.hm-orgchart-affiliation-fallback');
|
|
return { id: mark.closest('[data-card-kind]').dataset.employeeId, kind: mark.closest('[data-card-kind]').dataset.cardKind,
|
|
bounds: rect(mark), image: img ? rect(img) : null, imageFit: img && getComputedStyle(img).objectFit,
|
|
background: style.backgroundColor, borders: [style.borderTopWidth, style.borderRightWidth, style.borderBottomWidth, style.borderLeftWidth],
|
|
borderStyle: style.borderTopStyle, shadow: style.boxShadow,
|
|
imageBackground: img && getComputedStyle(img).backgroundColor,
|
|
before: getComputedStyle(mark, '::before').content, after: getComputedStyle(mark, '::after').content,
|
|
loaded: !!img?.naturalWidth, fallbackVisibility: getComputedStyle(fallback).visibility };
|
|
});
|
|
});
|
|
measurements.push({ story, logos });
|
|
if (!baselineMode) {
|
|
for (const [index, logo] of logos.entries()) {
|
|
const before = previous.find(item => item.story === story).logos[index];
|
|
if (!repairMode) check(logo.background === 'rgba(0, 0, 0, 0)' && logo.borders.every(value => value === '0px') && logo.borderStyle === 'none' && logo.shadow === 'none', `${story}/${logo.id}: transparent borderless container`, logo);
|
|
check(JSON.stringify(logo.bounds) === JSON.stringify(before.bounds) && JSON.stringify(logo.image) === JSON.stringify(before.image) && logo.imageFit === 'contain', `${story}/${logo.id}: unchanged container/image geometry`, { before, after: logo });
|
|
if (!repairMode) check(logo.loaded && logo.fallbackVisibility === 'hidden' && logo.imageBackground === 'rgba(0, 0, 0, 0)' && ['none', 'normal'].includes(logo.before) && ['none', 'normal'].includes(logo.after), `${story}/${logo.id}: loaded transparent image and no fallback/decorations`, logo);
|
|
}
|
|
await page.locator('[data-employee-id="mock-leader-04"]').screenshot({ path: `${output}/${story}-logo-card.png` });
|
|
expectedImageFailure = true;
|
|
const failure = await page.evaluate(async () => {
|
|
const root = document.querySelector('.hm-orgchart-host, .hm-orgchart-component-host').shadowRoot;
|
|
const mark = root.querySelector('[data-card-asset] .hm-orgchart-affiliation-mark');
|
|
mark.querySelector('img').src = 'data:image/png;base64,invalid-image';
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
const fallback = mark.querySelector('.hm-orgchart-affiliation-fallback');
|
|
return { noImage: !mark.querySelector('img'), available: mark.dataset.logoAvailable, visibility: getComputedStyle(fallback).visibility, text: fallback.textContent };
|
|
});
|
|
if (!repairMode) check(failure.noImage && failure.available === 'false' && failure.visibility === 'visible' && !!failure.text, `${story}: failed image retains fallback`, failure);
|
|
}
|
|
await page.close();
|
|
}
|
|
} finally { await browser.close(); }
|
|
if (baselineMode) await writeFile(`${output}/logo-before.json`, JSON.stringify(measurements, null, 2));
|
|
else {
|
|
check(errors.length === 0, 'browser console/runtime errors', errors);
|
|
await writeFile(`${output}/${repairMode ? 'logo-geometry-repair' : 'logo-browser'}.json`, JSON.stringify({ results, measurements, errors }, null, 2));
|
|
console.log(JSON.stringify({ checks: results.length, pass: results.filter(item => item.status === 'PASS').length, failures: results.filter(item => item.status === 'FAIL'), errors }, null, 2));
|
|
if (results.some(item => item.status === 'FAIL')) process.exitCode = 1;
|
|
}
|