import assert from 'node:assert/strict'; import http from 'node:http'; import path from 'node:path'; import { readFile, writeFile } from 'node:fs/promises'; import { fileURLToPath, pathToFileURL } from 'node:url'; // Target only the affected existing Story and the unchanged Fixed composition. const evidence = fileURLToPath(new URL('../test-results/org-chart-minimal-install/', import.meta.url)); const root = path.join(evidence, 'storybook'); const mime = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.woff2': 'font/woff2' }; const server = http.createServer(async (request, response) => { const filename = path.resolve(root, '.' + decodeURIComponent(new URL(request.url, 'http://localhost').pathname)); if (!filename.startsWith(root + path.sep)) { response.writeHead(403).end(); return; } try { const bytes = await readFile(filename); response.writeHead(200, { 'Content-Type': mime[path.extname(filename)] || 'application/octet-stream' }).end(bytes); } catch { response.writeHead(404).end(); } }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); assert.ok(process.env.ORGCHART_PLAYWRIGHT_MODULE, 'Set ORGCHART_PLAYWRIGHT_MODULE'); const { chromium } = await import(pathToFileURL(process.env.ORGCHART_PLAYWRIGHT_MODULE).href); const browser = await chromium.launch({ headless: true, executablePath: process.env.ORGCHART_CHROME || 'C:/Program Files/Google/Chrome/Application/chrome.exe' }); const errors = [], results = []; try { const page = await browser.newPage({ viewport: { width: 1440, height: 900 } }); page.on('pageerror', error => errors.push(error.message)); page.on('console', message => { if (message.type() === 'error') errors.push(message.text()); }); for (const id of ['orgchart--expanded-org-chart', 'orgchart--full-composition']) { await page.goto(`http://127.0.0.1:${server.address().port}/iframe.html?id=${id}&viewMode=story`); const card = page.locator('.hm-orgchart-employee').first(); await card.waitFor(); await card.focus(); await page.keyboard.press('Enter'); const hm = page.locator('[data-component="HMOrgChartEmployeeDialog"]'); const core = page.locator('dialog.hm-orgchart-employee-dialog'); if (id.includes('expanded')) { await hm.waitFor({ state: 'visible' }); assert.equal(await core.evaluate(e => e.open), false); await page.screenshot({ path: path.join(evidence, 'storybook-expanded-hm.png') }); await page.keyboard.press('Escape'); await hm.waitFor({ state: 'hidden' }); assert.equal(await card.evaluate(e => e.getRootNode().activeElement === e), true); const trigger = page.getByRole('combobox', { name: '직원 정보 팝업' }); await trigger.click(); await page.getByRole('option', { name: 'Core 기본 팝업', exact: true }).click(); // Wait for Radix's exiting Select portal/focus restoration before opening // another modal with the keyboard. External consumers have no Select here. await page.getByRole('listbox').waitFor({ state: 'detached' }); await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))); await card.focus(); await page.keyboard.press('Enter'); await core.waitFor({ state: 'visible' }); assert.equal(await hm.count(), 0); assert.equal(await core.evaluate(e => e.contains(e.getRootNode().activeElement)), true, 'Core modal receives focus after Select closes'); await page.keyboard.press('Escape'); await core.waitFor({ state: 'hidden' }); results.push({ story: id, status: 'PASS', checks: ['Existing default HM Dialog', 'Escape and focus restoration', 'Existing selector switches to Core popup'] }); } else { await core.waitFor({ state: 'visible' }); assert.equal(await hm.count(), 0); await page.screenshot({ path: path.join(evidence, 'storybook-fixed-core.png') }); await page.keyboard.press('Escape'); await core.waitFor({ state: 'hidden' }); results.push({ story: id, status: 'PASS', checks: ['Existing Fixed render and Core popup', 'Escape'] }); } } assert.deepEqual(errors, []); await writeFile(path.join(evidence, 'storybook-browser.json'), JSON.stringify({ status: 'PASS', results, errors }, null, 2)); console.log(JSON.stringify({ status: 'PASS', results, errors })); } finally { await browser.close(); await new Promise(resolve => server.close(resolve)); }