From 7644520cee08d3b885740ef5ed14326b90668c70 Mon Sep 17 00:00:00 2001 From: b25027 Date: Mon, 12 Oct 2026 22:54:15 +0900 Subject: [PATCH] fix(org-chart): improve auto connector contrast and test portability --- .../org-chart-react/verify-boundaries.mjs | 5 ++- examples/org-chart-react/verify.mjs | 8 ++-- examples/org-chart/verify-card-content.mjs | 7 ++-- examples/org-chart/verify-card-design.mjs | 5 ++- .../org-chart/verify-final-card-check.mjs | 6 +-- .../org-chart/verify-final-logo-check.mjs | 6 +-- .../org-chart/verify-final-status-check.mjs | 6 +-- examples/org-chart/verify-p1.mjs | 10 ++--- examples/org-chart/verify-p2.mjs | 6 +-- examples/org-chart/verify-p3.1.mjs | 5 ++- examples/org-chart/verify-p3.2.mjs | 5 ++- examples/org-chart/verify-p3.mjs | 6 +-- examples/org-chart/verify-p5-1a.mjs | 8 ++-- examples/org-chart/verify-p5-1b.mjs | 8 ++-- examples/org-chart/verify-p5-23.mjs | 8 ++-- examples/org-chart/verify-p5-4.mjs | 8 ++-- .../org-chart/verify-quality-boundaries.mjs | 6 +-- examples/org-chart/verify-quality-p1.mjs | 7 ++-- examples/org-chart/verify.mjs | 6 +-- .../src/styles/org-chart-expanded.css | 7 +++- scripts/orgchart-browser-runtime.mjs | 37 +++++++++++++++++++ 21 files changed, 109 insertions(+), 61 deletions(-) create mode 100644 scripts/orgchart-browser-runtime.mjs diff --git a/examples/org-chart-react/verify-boundaries.mjs b/examples/org-chart-react/verify-boundaries.mjs index 2af120b..a1e2876 100644 --- a/examples/org-chart-react/verify-boundaries.mjs +++ b/examples/org-chart-react/verify-boundaries.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import assert from 'node:assert/strict'; import http from 'node:http'; import path from 'node:path'; @@ -5,7 +6,7 @@ import { readFile, writeFile } from 'node:fs/promises'; import { fileURLToPath, pathToFileURL } from 'node:url'; const root = fileURLToPath(new URL('../../', import.meta.url)); -const { chromium } = await import(process.env.ORGCHART_PLAYWRIGHT_MODULE ? pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href : 'playwright'); +await loadPlaywright(); const server = http.createServer(async (request, response) => { try { const url = new URL(request.url, 'http://localhost'); @@ -18,7 +19,7 @@ const server = http.createServer(async (request, response) => { } catch { response.writeHead(404).end(); } }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const results = [], errors = []; const check = (condition, name, detail) => { results.push({ name, status: condition ? 'PASS' : 'FAIL', detail }); assert.ok(condition, name); }; try { diff --git a/examples/org-chart-react/verify.mjs b/examples/org-chart-react/verify.mjs index 0a3f046..63d0ddd 100644 --- a/examples/org-chart-react/verify.mjs +++ b/examples/org-chart-react/verify.mjs @@ -1,14 +1,14 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import assert from 'node:assert/strict'; import http from 'node:http'; import path from 'node:path'; import { readFile, mkdir, writeFile } from 'node:fs/promises'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-react'); await mkdir(output, { recursive: true }); -const modulePath = process.env.ORGCHART_PLAYWRIGHT_MODULE; -const { chromium } = await import(modulePath ? pathToFileURL(modulePath).href : 'playwright'); +await loadPlaywright(); const mime = { '.html': 'text/html', '.js': 'text/javascript', '.json': 'application/json', '.css': 'text/css', '.ttf': 'font/ttf', '.woff2': 'font/woff2', '.svg': 'image/svg+xml' }; async function serve(directory) { const server = http.createServer(async (request, response) => { @@ -27,7 +27,7 @@ async function serve(directory) { const consumer = await serve(path.join(root, 'examples/org-chart-react/dist')); const vanilla = await serve(path.join(root, 'examples/org-chart/dist')); const storybook = await serve(path.join(root, 'storybook-static')); -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const results = [], errors = [], metrics = [], snapshots = {}; function check(condition, name, detail) { results.push({ name, status: condition ? 'PASS' : 'FAIL', detail }); diff --git a/examples/org-chart/verify-card-content.mjs b/examples/org-chart/verify-card-content.mjs index 4d273ce..eefc964 100644 --- a/examples/org-chart/verify-card-content.mjs +++ b/examples/org-chart/verify-card-content.mjs @@ -1,14 +1,15 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, writeFile, mkdir } from 'node:fs/promises'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; // Stage 2 only: typography, data placement, overflow and accessible tooltip. // Do not repeat Stage 1's gradient/data/Summary/engine suites. const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-card-content'); await mkdir(output, { recursive: true }); -const { chromium } = await import(process.env.ORGCHART_PLAYWRIGHT_MODULE ? pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href : 'playwright'); +await loadPlaywright(); const results = [], errors = [], measurements = [], servers = []; const check = (value, name, detail) => results.push({ name, status: value ? 'PASS' : 'FAIL', ...(detail === undefined ? {} : { detail }) }); const mime = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.ttf': 'font/ttf' }; @@ -135,7 +136,7 @@ async function tooltipChecks(page, label) { let browser; try { const origins = await Promise.all(['storybook-static', 'examples/org-chart/dist', 'examples/org-chart-react/dist'].map(directory => serve(path.join(root, directory)))); - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1000 }, reducedMotion: 'reduce' }); const routes = [['department-colors', `${origins[0]}/iframe.html?id=orgchart--department-colors&viewMode=story`], ['full-composition', `${origins[0]}/iframe.html?id=orgchart--full-composition&viewMode=story`], ['html', `${origins[1]}/p1.html`], ['react', `${origins[2]}/p1.html`]]; diff --git a/examples/org-chart/verify-card-design.mjs b/examples/org-chart/verify-card-design.mjs index cee3f74..199db3e 100644 --- a/examples/org-chart/verify-card-design.mjs +++ b/examples/org-chart/verify-card-design.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'; @@ -8,7 +9,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-card-design'); await mkdir(output, { recursive: true }); -const { chromium } = await import(process.env.ORGCHART_PLAYWRIGHT_MODULE ? pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href : 'playwright'); +await loadPlaywright(); const results = [], measurements = [], errors = [], servers = []; const check = (value, name, detail) => results.push({ name, status: value ? 'PASS' : 'FAIL', ...(detail === undefined ? {} : { detail }) }); const mime = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.ttf': 'font/ttf', '.svg': 'image/svg+xml' }; @@ -91,7 +92,7 @@ function verify(value, label) { let browser; try { const origins = await Promise.all(['storybook-static', 'examples/org-chart/dist', 'examples/org-chart-react/dist'].map(directory => serve(path.join(root, directory)))); - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1000 }, deviceScaleFactor: 1, reducedMotion: 'reduce' }); const index = JSON.parse(await readFile(path.join(root, 'storybook-static/index.json'), 'utf8')); const entries = Object.values(index.entries).filter(entry => entry.type === 'story' && entry.title.startsWith('OrgChart')); diff --git a/examples/org-chart/verify-final-card-check.mjs b/examples/org-chart/verify-final-card-check.mjs index 6fd4254..617517b 100644 --- a/examples/org-chart/verify-final-card-check.mjs +++ b/examples/org-chart/verify-final-card-check.mjs @@ -1,15 +1,15 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import { readFile, writeFile, mkdir } from 'node:fs/promises'; -import { pathToFileURL } from 'node:url'; import { createHash } from 'node:crypto'; // Only the eight final card requirements; existing 368-check evidence is untouched. -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href); +await loadPlaywright(); const output = 'test-results/org-chart-final-check'; await mkdir(output, { recursive: true }); const fixture = JSON.parse(await readFile('examples/org-chart/data-p1.json', 'utf8')); const results = [], measurements = [], errors = []; const check = (ok, name, actual) => results.push({ name, status: ok ? 'PASS' : 'FAIL', actual }); -const browser = await chromium.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const origin = process.env.ORGCHART_STORY_ORIGIN ?? 'http://127.0.0.1:6031'; const collisionRepair = process.argv.includes('--collision-repair'); async function capture(story, scenario, data) { diff --git a/examples/org-chart/verify-final-logo-check.mjs b/examples/org-chart/verify-final-logo-check.mjs index 9fe49b8..3d861b3 100644 --- a/examples/org-chart/verify-final-logo-check.mjs +++ b/examples/org-chart/verify-final-logo-check.mjs @@ -1,10 +1,10 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import { readFile, writeFile } from 'node:fs/promises'; -import { pathToFileURL } from 'node:url'; -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href); +await loadPlaywright(); 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 browser = await launchChromium({ 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')); diff --git a/examples/org-chart/verify-final-status-check.mjs b/examples/org-chart/verify-final-status-check.mjs index d36245c..2297faa 100644 --- a/examples/org-chart/verify-final-status-check.mjs +++ b/examples/org-chart/verify-final-status-check.mjs @@ -1,12 +1,12 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, writeFile } from 'node:fs/promises'; -import { pathToFileURL } from 'node:url'; -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href); +await loadPlaywright(); const output = 'test-results/org-chart-final-check'; const baselineMode = process.argv.includes('--baseline'); const colorsRepair = process.argv.includes('--colors-repair'); -const browser = await chromium.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const results = [], measurements = [], errors = []; const check = (ok, name, actual) => results.push({ name, status: ok ? 'PASS' : 'FAIL', actual }); async function measure(url, label) { diff --git a/examples/org-chart/verify-p1.mjs b/examples/org-chart/verify-p1.mjs index 364b441..6ca768a 100644 --- a/examples/org-chart/verify-p1.mjs +++ b/examples/org-chart/verify-p1.mjs @@ -1,15 +1,15 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, mkdir, writeFile } from 'node:fs/promises'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; // Verification tooling is supplied by the environment, never shipped in Core. const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-p1'); await mkdir(output, { recursive: true }); const fixture = JSON.parse(await readFile(path.join(root, 'examples/org-chart/data-p1.json'), 'utf8')); -const modulePath = process.env.ORGCHART_PLAYWRIGHT_MODULE; -const { chromium } = await import(modulePath ? pathToFileURL(modulePath).href : 'playwright'); +await loadPlaywright(); const mime = { '.html': 'text/html', '.js': 'text/javascript', '.json': 'application/json', '.css': 'text/css', '.ttf': 'font/ttf', '.woff2': 'font/woff2', '.png': 'image/png', '.svg': 'image/svg+xml' }; async function serve(directory) { const server = http.createServer(async (request, response) => { @@ -306,7 +306,7 @@ async function recheckStorybookConsole() { let retryBrowser; let sidebar; try { - retryBrowser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + retryBrowser = await launchChromium({ headless: true }); const context = await retryBrowser.newContext({ viewport: { width: 1440, height: 1080 } }); const iframe = await context.newPage(); iframe.on('pageerror', error => retryErrors.push({ surface: 'iframe', type: 'pageerror', message: error.message })); @@ -361,7 +361,7 @@ try { const vanilla = await serve(path.join(root, 'examples/org-chart/dist')); servers.push(vanilla); const react = await serve(path.join(root, 'examples/org-chart-react/dist')); servers.push(react); const storybook = await serve(path.join(root, 'storybook-static')); servers.push(storybook); - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1080 } }); const pages = new Map(); for (const [transport, origin] of [['vanilla', vanilla.origin], ['react', react.origin]]) { diff --git a/examples/org-chart/verify-p2.mjs b/examples/org-chart/verify-p2.mjs index 8710613..5af73c5 100644 --- a/examples/org-chart/verify-p2.mjs +++ b/examples/org-chart/verify-p2.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, mkdir, writeFile } from 'node:fs/promises'; @@ -14,8 +15,7 @@ const fixturePath = path.join(root, 'examples/org-chart/data-p1.json'); const fixtureBytes = await readFile(fixturePath); const fixtureHash = createHash('sha256').update(fixtureBytes).digest('hex'); const fixture = JSON.parse(fixtureBytes.toString('utf8')); -const bundledPlaywright = 'C:\\Users\\user\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\node\\node_modules\\playwright\\index.mjs'; -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE ?? bundledPlaywright).href); +await loadPlaywright(); const viewports = [{ width: 1920, height: 1080 }, { width: 1440, height: 900 }, { width: 1280, height: 800 }, { width: 768, height: 1024 }, { width: 390, height: 844 }]; const expectedArrows = [[194.15, 311.75, 66.25, 19.8], [430.4, 311.75, 66.25, 19.8], [666.65, 302, 183.75, 42], [1044, 302, 258.92, 42], [1520.55, 305, 135.35, 36]]; const results = [], measurements = [], errors = [], screenshots = [], servers = []; @@ -275,7 +275,7 @@ let browser; try { const [htmlOrigin, reactOrigin] = await Promise.all([serve(path.join(root, 'examples/org-chart/dist')), serve(path.join(root, 'examples/org-chart-react/dist'))]); const storybookOrigin = process.env.ORGCHART_STORYBOOK_URL?.replace(/\/$/, '') ?? await serve(path.join(root, 'storybook-static')); - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext({ deviceScaleFactor: 1, reducedMotion: 'reduce' }); const routes = [ { label: 'html', transport: 'html', url: `${htmlOrigin}/p1.html?mode=readOnly` }, diff --git a/examples/org-chart/verify-p3.1.mjs b/examples/org-chart/verify-p3.1.mjs index a61c7db..6106504 100644 --- a/examples/org-chart/verify-p3.1.mjs +++ b/examples/org-chart/verify-p3.1.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import fs from 'node:fs/promises'; @@ -9,7 +10,7 @@ const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-p3.1'); const fixtureBytes = await fs.readFile(path.join(root, 'examples/org-chart/data-p1.json')); const fixture = JSON.parse(fixtureBytes); -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE ?? 'C:/Users/user/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs').href); +await loadPlaywright(); const viewports = [{ width: 1920, height: 1080 }, { width: 1440, height: 900 }, { width: 1280, height: 800 }, { width: 768, height: 1024 }, { width: 390, height: 844 }]; const results = [], measurements = [], fontEvidence = [], errors = [], servers = []; const check = (pass, name, detail) => results.push({ name, status: pass ? 'PASS' : 'FAIL', ...(detail === undefined ? {} : { detail }) }); @@ -139,7 +140,7 @@ let browser; try { const htmlOrigin = await serve(path.join(root,'examples/org-chart/dist')); const reactOrigin = await serve(path.join(root,'examples/org-chart-react/dist')); - browser = await chromium.launch({executablePath:process.env.ORGCHART_CHROME ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe',headless:true}); + browser = await launchChromium({ headless: true }); const context = await browser.newContext(); const routes = [{name:'html',url:`${htmlOrigin}/p1.html?mode=readOnly`},{name:'file-classic',url:pathToFileURL(path.join(root,'examples/org-chart/dist/p1-classic.html')).href+'?mode=readOnly'},{name:'react',url:`${reactOrigin}/p1.html?mode=readOnly`},{name:'storybook',url:'http://127.0.0.1:6041/iframe.html?id=orgchart--full-composition&viewMode=story'}]; for(const route of routes) { diff --git a/examples/org-chart/verify-p3.2.mjs b/examples/org-chart/verify-p3.2.mjs index f1e972d..6e12309 100644 --- a/examples/org-chart/verify-p3.2.mjs +++ b/examples/org-chart/verify-p3.2.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import fs from 'node:fs/promises'; @@ -11,7 +12,7 @@ const baselineMode = process.argv.includes('--baseline'); const policiesOnly = process.argv.includes('--policies-only'); const fixtureBytes = await fs.readFile(path.join(root, 'examples/org-chart/data-p1.json')); const fixture = JSON.parse(fixtureBytes); -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE ?? 'C:/Users/user/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs').href); +await loadPlaywright(); const viewports = [{ width: 1920, height: 1080 }, { width: 1440, height: 900 }, { width: 1280, height: 800 }, { width: 768, height: 1024 }, { width: 390, height: 844 }]; const storyOrigin = process.env.ORGCHART_STORY_ORIGIN ?? 'http://127.0.0.1:6041'; const results = [], measurements = [], errors = [], servers = []; @@ -200,7 +201,7 @@ let browser; try { const htmlOrigin = await serve(path.join(root, 'examples/org-chart/dist')); const reactOrigin = await serve(path.join(root, 'examples/org-chart-react/dist')); - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext(); const routes = [{ name: 'html', url: `${htmlOrigin}/p1.html?mode=readOnly`, origin: htmlOrigin }, { name: 'file-classic', url: pathToFileURL(path.join(root, 'examples/org-chart/dist/p1-classic.html')).href + '?mode=readOnly' }, { name: 'react', url: `${reactOrigin}/p1.html?mode=readOnly`, origin: reactOrigin }, { name: 'storybook', url: `${storyOrigin}/iframe.html?id=orgchart--full-composition&viewMode=story` }]; for (const route of routes) { diff --git a/examples/org-chart/verify-p3.mjs b/examples/org-chart/verify-p3.mjs index 553663b..17ecfef 100644 --- a/examples/org-chart/verify-p3.mjs +++ b/examples/org-chart/verify-p3.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, mkdir, writeFile } from 'node:fs/promises'; @@ -13,8 +14,7 @@ const fixturePath = path.join(root, 'examples/org-chart/data-p1.json'); const fixtureBytes = await readFile(fixturePath); const fixtureHash = createHash('sha256').update(fixtureBytes).digest('hex'); const fixture = JSON.parse(fixtureBytes.toString('utf8')); -const playwrightPath = process.env.ORGCHART_PLAYWRIGHT_MODULE ?? 'C:\\Users\\user\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\node\\node_modules\\playwright\\index.mjs'; -const { chromium } = await import(pathToFileURL(playwrightPath).href); +await loadPlaywright(); const viewports = [{ width: 1920, height: 1080 }, { width: 1440, height: 900 }, { width: 1280, height: 800 }, { width: 768, height: 1024 }, { width: 390, height: 844 }]; const results = [], measurements = [], interactions = [], errors = [], screenshots = [], servers = []; const check = (condition, name, detail) => results.push({ name, status: condition ? 'PASS' : 'FAIL', ...(detail === undefined ? {} : { detail }) }); @@ -333,7 +333,7 @@ try { // The user identified the live 6041 Storybook; never silently verify a stale // storybook-static build from P2 instead of that requested running server. const storybookOrigin = process.env.ORGCHART_STORYBOOK_URL?.replace(/\/$/, '') ?? 'http://127.0.0.1:6041'; - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext({ deviceScaleFactor: 1, reducedMotion: 'reduce' }); const routes = [ { label: 'html', transport: 'html', url: `${htmlOrigin}/p1.html?mode=readOnly` }, diff --git a/examples/org-chart/verify-p5-1a.mjs b/examples/org-chart/verify-p5-1a.mjs index 389d269..c6b22a6 100644 --- a/examples/org-chart/verify-p5-1a.mjs +++ b/examples/org-chart/verify-p5-1a.mjs @@ -1,15 +1,15 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import fs from 'node:fs/promises'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; // P5-1A only: preserve previous suites and the baseline-files.json snapshot. // Copy the pre-change public Core dist to baseline-dist before rebuilding. const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-p5-1a'); const storyOrigin = process.env.ORGCHART_STORY_ORIGIN ?? 'http://127.0.0.1:6052'; -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE - ?? 'C:/Users/user/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs').href); +const { chromium } = await loadPlaywright(); const fixedFixture = JSON.parse(await fs.readFile(path.join(root, 'examples/org-chart/data-p1.json'), 'utf8')); const expandedFixture = JSON.parse(await fs.readFile(path.join(root, 'examples/org-chart/data-expanded.json'), 'utf8')); const presets = [ @@ -44,7 +44,7 @@ const server = http.createServer(async (request, response) => { }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const origin = `http://127.0.0.1:${server.address().port}`; -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); async function newPage(name, viewport = { width: 1440, height: 900 }) { const page = await browser.newPage({ viewport }); diff --git a/examples/org-chart/verify-p5-1b.mjs b/examples/org-chart/verify-p5-1b.mjs index 732d91e..941b406 100644 --- a/examples/org-chart/verify-p5-1b.mjs +++ b/examples/org-chart/verify-p5-1b.mjs @@ -1,15 +1,15 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import fs from 'node:fs/promises'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; // P5-1B focused integration suite. Preserve historical verification evidence. // Copy the pre-change public Core dist to baseline-dist before rebuilding. const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-p5-1b'); const storyOrigin = process.env.ORGCHART_STORY_ORIGIN ?? 'http://127.0.0.1:6053'; -const playwright = process.env.ORGCHART_PLAYWRIGHT_MODULE; -const { chromium } = await import(playwright ? pathToFileURL(playwright).href : 'playwright'); +await loadPlaywright(); const fixedFixture = JSON.parse(await fs.readFile(path.join(root, 'examples/org-chart/data-p1.json'), 'utf8')); const expandedFixture = JSON.parse(await fs.readFile(path.join(root, 'examples/org-chart/data-expanded.json'), 'utf8')); const presets = [ @@ -44,7 +44,7 @@ const server = http.createServer(async (request, response) => { }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const origin = `http://127.0.0.1:${server.address().port}`; -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); async function newPage(name, viewport = { width: 1440, height: 900 }) { const page = await browser.newPage({ viewport }); diff --git a/examples/org-chart/verify-p5-23.mjs b/examples/org-chart/verify-p5-23.mjs index 532dcfe..747bea5 100644 --- a/examples/org-chart/verify-p5-23.mjs +++ b/examples/org-chart/verify-p5-23.mjs @@ -1,11 +1,11 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import fs from 'node:fs/promises'; import http from 'node:http'; import path from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-p5-23'); -const playwright = process.env.ORGCHART_PLAYWRIGHT_MODULE; -const { chromium } = await import(playwright ? pathToFileURL(playwright).href : 'playwright'); +await loadPlaywright(); const story = process.env.ORGCHART_STORY_ORIGIN ?? 'http://127.0.0.1:6054'; const html = process.env.ORGCHART_HTML_ORIGIN ?? 'http://127.0.0.1:4189'; const fixtures = Object.fromEntries(await Promise.all(['small', 'medium', 'large'].map(async size => [size, JSON.parse(await fs.readFile(path.join(root, `examples/org-chart/data-expanded-${size}.json`), 'utf8'))]))); @@ -25,7 +25,7 @@ const server = http.createServer(async (request, response) => { await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const origin = `http://127.0.0.1:${server.address().port}`; await fs.mkdir(output, { recursive: true }); -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); page.on('pageerror', error => errors.push(error.message)); page.on('console', message => { if (message.type() === 'error') errors.push(message.text()); }); diff --git a/examples/org-chart/verify-p5-4.mjs b/examples/org-chart/verify-p5-4.mjs index fa9d39c..317bf22 100644 --- a/examples/org-chart/verify-p5-4.mjs +++ b/examples/org-chart/verify-p5-4.mjs @@ -1,13 +1,13 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; // Focused P5-4 checks. Does not replay P5-1B/P5-23 suites or performance timings. import fs from 'node:fs/promises'; import { writeFileSync } from 'node:fs'; import http from 'node:http'; import path from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-p5-4'); -const playwright = process.env.ORGCHART_PLAYWRIGHT_MODULE; -const { chromium } = await import(playwright ? pathToFileURL(playwright).href : 'playwright'); +await loadPlaywright(); const fixtures = Object.fromEntries(await Promise.all(['small', 'medium', 'large'].map(async size => [size, JSON.parse(await fs.readFile(path.join(root, `examples/org-chart/data-expanded-${size}.json`), 'utf8'))]))); const fixed = JSON.parse(await fs.readFile(path.join(root, 'examples/org-chart/data-p1.json'), 'utf8')); const results = [], errors = [], measurements = []; @@ -37,7 +37,7 @@ const server = http.createServer(async (request, response) => { await fs.mkdir(output, { recursive: true }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const origin = `http://127.0.0.1:${server.address().port}`; -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); const track = p => { p.on('pageerror', e => errors.push(e.message)); p.on('console', m => { if (m.type() === 'error') errors.push({message:m.text(),location:m.location()}); }); }; track(page); diff --git a/examples/org-chart/verify-quality-boundaries.mjs b/examples/org-chart/verify-quality-boundaries.mjs index ba19aca..7f20bd3 100644 --- a/examples/org-chart/verify-quality-boundaries.mjs +++ b/examples/org-chart/verify-quality-boundaries.mjs @@ -1,7 +1,7 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import { writeFile } from 'node:fs/promises'; -import { pathToFileURL } from 'node:url'; -const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href); -const browser = await chromium.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe', headless: true }); +await loadPlaywright(); +const browser = await launchChromium({ headless: true }); try { const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); await page.goto('http://127.0.0.1:6027/iframe.html?id=orgchart--full-composition&viewMode=story'); diff --git a/examples/org-chart/verify-quality-p1.mjs b/examples/org-chart/verify-quality-p1.mjs index 08acb56..025068f 100644 --- a/examples/org-chart/verify-quality-p1.mjs +++ b/examples/org-chart/verify-quality-p1.mjs @@ -1,14 +1,15 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import http from 'node:http'; import path from 'node:path'; import { readFile, writeFile, mkdir } from 'node:fs/promises'; import { createHash } from 'node:crypto'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'node:url'; // Quality-only verification. No data/source/lifecycle regression suite is run. const root = fileURLToPath(new URL('../../', import.meta.url)); const output = path.join(root, 'test-results/org-chart-quality'); await mkdir(output, { recursive: true }); -const { chromium } = await import(process.env.ORGCHART_PLAYWRIGHT_MODULE ? pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href : 'playwright'); +await loadPlaywright(); const results = [], errors = [], measurements = [], screenshots = []; const check = (value, name, detail) => results.push({ name, status: value ? 'PASS' : 'FAIL', ...(detail === undefined ? {} : { detail }) }); const mime = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.png': 'image/png', '.ttf': 'font/ttf' }; @@ -108,7 +109,7 @@ let browser; const servers = []; try { for (const directory of ['storybook-static', 'examples/org-chart/dist', 'examples/org-chart-react/dist']) servers.push(await serve(path.join(root, directory))); - browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); + browser = await launchChromium({ headless: true }); const context = await browser.newContext({ viewport: { width: 1440, height: 1000 }, deviceScaleFactor: 1, reducedMotion: 'reduce' }); const canonical = ['logo', 'department-colors', 'position-summary', 'affiliation-summary', 'full-composition']; const index = JSON.parse(await readFile(path.join(root, 'storybook-static/index.json'), 'utf8')); diff --git a/examples/org-chart/verify.mjs b/examples/org-chart/verify.mjs index 5ef3330..3282d83 100644 --- a/examples/org-chart/verify.mjs +++ b/examples/org-chart/verify.mjs @@ -1,3 +1,4 @@ +import { loadPlaywright, launchChromium } from '../../scripts/orgchart-browser-runtime.mjs'; import assert from 'node:assert/strict'; import http from 'node:http'; import { readFile, mkdir, writeFile } from 'node:fs/promises'; @@ -5,8 +6,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; import path from 'node:path'; // Browser tooling belongs to the verification environment, not the production package. -const playwright = process.env.ORGCHART_PLAYWRIGHT_MODULE; -const { chromium } = await import(playwright ? pathToFileURL(playwright).href : 'playwright'); +await loadPlaywright(); const example = fileURLToPath(new URL('./dist/', import.meta.url)); const output = fileURLToPath(new URL('../../test-results/org-chart-browser/', import.meta.url)); await mkdir(output, { recursive: true }); @@ -24,7 +24,7 @@ const server = http.createServer(async (request, response) => { }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const origin = `http://127.0.0.1:${server.address().port}`; -const browser = await chromium.launch({ executablePath: process.env.ORGCHART_CHROME || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', headless: true }); +const browser = await launchChromium({ headless: true }); const results = [], errors = [], metrics = []; const check = (condition, name, detail) => { results.push({ name, status: condition ? 'PASS' : 'FAIL', detail }); assert.ok(condition, `${name}: ${JSON.stringify(detail)}`); }; try { diff --git a/packages/org-chart/src/styles/org-chart-expanded.css b/packages/org-chart/src/styles/org-chart-expanded.css index 139c732..69dbeb2 100644 --- a/packages/org-chart/src/styles/org-chart-expanded.css +++ b/packages/org-chart/src/styles/org-chart-expanded.css @@ -6,6 +6,8 @@ --hm-orgchart-expanded-title-gap: var(--hm-orgchart-resolved-spacing-md); --hm-orgchart-expanded-employee-gap: var(--hm-orgchart-resolved-spacing-md); --hm-orgchart-expanded-line-width: var(--hm-orgchart-resolved-border-width); + /* Meaningful Auto relationships: separate from neutral card/Fixed borders. */ + --hm-orgchart-expanded-line-color: #738399; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--hm-orgchart-resolved-spacing-lg); @@ -101,6 +103,7 @@ width: 100%; height: 0; border-width: var(--hm-orgchart-expanded-line-width) 0 0; + border-color: var(--hm-orgchart-expanded-line-color); } .hm-orgchart-expanded .hm-orgchart-engine.orgchart .hierarchy:has(> .node[data-depth]) > .nodes > .hierarchy:first-child::before { left: calc(50% - 1px); width: calc(50% + 1px); } .hm-orgchart-expanded .hm-orgchart-engine.orgchart .hierarchy:has(> .node[data-depth]) > .nodes > .hierarchy:last-child::before { width: calc(50% + 1px); } @@ -109,11 +112,13 @@ top: calc(-1 * var(--hm-orgchart-expanded-level-gap) / 2); height: calc(var(--hm-orgchart-expanded-level-gap) / 2); width: var(--hm-orgchart-expanded-line-width); + background-color: var(--hm-orgchart-expanded-line-color); } .hm-orgchart-expanded .hm-orgchart-engine.orgchart .hierarchy:not(.couple) > .node:has(+ .nodes)::after { bottom: calc(-1 * var(--hm-orgchart-expanded-level-gap) / 2); height: calc(100% - var(--hm-orgchart-expanded-content-height) + var(--hm-orgchart-expanded-level-gap) / 2); width: var(--hm-orgchart-expanded-line-width); + background-color: var(--hm-orgchart-expanded-line-color); } /* Standalone content variables, independent of Fixed and HM React controls. */ @@ -162,7 +167,7 @@ padding-inline: var(--hm-orgchart-resolved-spacing-xxl); } .hm-orgchart-expanded .hm-orgchart-employee-connectors { position: absolute; inset: 0; overflow: visible; pointer-events: none; } -.hm-orgchart-expanded .hm-orgchart-employee-connectors path { fill: none; stroke: var(--hm-orgchart-resolved-stroke-default); stroke-width: var(--hm-orgchart-expanded-line-width); } +.hm-orgchart-expanded .hm-orgchart-employee-connectors path { fill: none; stroke: var(--hm-orgchart-expanded-line-color); stroke-width: var(--hm-orgchart-expanded-line-width); } .hm-orgchart-expanded .hm-orgchart-engine.orgchart .node[data-depth] .hm-orgchart-employee[data-card-presentation="expanded"] { padding: var(--hm-orgchart-expanded-card-padding); border-radius: var(--hm-orgchart-expanded-card-radius); diff --git a/scripts/orgchart-browser-runtime.mjs b/scripts/orgchart-browser-runtime.mjs new file mode 100644 index 0000000..32a6290 --- /dev/null +++ b/scripts/orgchart-browser-runtime.mjs @@ -0,0 +1,37 @@ +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; + +let playwrightPromise; + +// Resolve project dependencies normally; an external installation is optional. +export async function loadPlaywright() { + playwrightPromise ??= (async () => { + const modulePath = process.env.ORGCHART_PLAYWRIGHT_MODULE?.trim(); + try { + const runtime = await import(modulePath ? pathToFileURL(path.resolve(modulePath)).href : 'playwright'); + if (typeof runtime.chromium?.launch !== 'function') throw new Error('Module does not export chromium.launch'); + return runtime; + } catch (cause) { + throw new Error( + `OrgChart browser verification cannot load Playwright (${modulePath || 'project module: playwright'}). ` + + 'Use an existing project Playwright installation, or set ORGCHART_PLAYWRIGHT_MODULE to an existing Playwright entry file.', + { cause }, + ); + } + })(); + return playwrightPromise; +} + +export async function launchChromium(options = {}) { + const { chromium } = await loadPlaywright(); + const executablePath = process.env.ORGCHART_CHROME?.trim(); + try { + return await chromium.launch({ ...options, ...(executablePath ? { executablePath: path.resolve(executablePath) } : {}) }); + } catch (cause) { + throw new Error( + `OrgChart browser verification cannot launch Chromium (${executablePath || 'Playwright-managed browser'}). ` + + 'Ensure this Playwright installation has its Chromium browser, or set ORGCHART_CHROME to an existing browser executable.', + { cause }, + ); + } +}