const {openStory, captureWithSensors} = require(process.env.HOME + '/astryx/probe-kit/lib.cjs'); const ROOT = ''; const SHA = '829da488ae4f62a0cc2aff62c4cd9dd9fbcd55f1'; const PORT = 6157; const TARGET = '#storybook-root'; // Authored BEFORE observation: the identity every frame must have. const base = { globals: {}, themeAttr: 'neutral', colorMode: 'light', direction: 'ltr', viewport: {width: 1000, height: 500, dpr: 1}, media: {forcedColors: false, reducedMotion: false, coarsePointer: false, hover: true}, targetCount: 1, }; // Semantic state: what the nav actually is, and whether a way back exists. const readState = async page => page.evaluate(() => { const nav = document.querySelector('nav[role="navigation"]'); const inert = nav.hasAttribute('inert'); const toggles = [...document.querySelectorAll('button')] .filter(b => /sidebar/i.test(b.getAttribute('aria-label') || '')); const usable = toggles.filter(b => !nav.contains(b) || !inert); return { navWidth: Math.round(nav.getBoundingClientRect().width), inert, togglesTotal: toggles.length, togglesReachable: usable.length, }; }); // `storybook dev` 404s /favicon.ico on EVERY story, including untouched ones // (verified on core-sidenav--default). It is server noise, not a page defect, // so it is excluded by exact text and recorded here rather than silently. const FAVICON_404 = 'console: Failed to load resource: the server responded with a status of 404 (Not Found)'; const realErrors = errs => errs.filter(e => e !== FAVICON_404); async function open(story) { const {browser, page, sensorErrors} = await openStory('chromium', story, { port: PORT, viewport: {width: 1000, height: 500}, ready: () => document.querySelector('nav[role="navigation"]') != null, launch: {headless: false}, }); return {browser, page, sensorErrors}; } const clickToggle = page => page.evaluate(() => { const nav = document.querySelector('nav[role="navigation"]'); [...document.querySelectorAll('button')] .find(b => /sidebar/i.test(b.getAttribute('aria-label') || '') && (!nav.contains(b) || !nav.hasAttribute('inert'))) .click(); }); (async () => { // --- The fix: the documented pairing (outside toggle). Slide + reclaim. --- { const {browser, page, sensorErrors} = await open('core-sidenav--hidden-collapse'); await captureWithSensors(page, `${ROOT}/shots/fix__expanded.png`, { label: 'documented pairing — expanded', buildRoot: ROOT, expectedSha: SHA, story: 'core-sidenav--hidden-collapse', target: TARGET, readState, sensorErrors: realErrors(sensorErrors), expected: {...base, state: {navWidth: 260, inert: false, togglesTotal: 1, togglesReachable: 1}}, }); await clickToggle(page); await page.waitForTimeout(400); await captureWithSensors(page, `${ROOT}/shots/fix__collapsed.png`, { label: 'documented pairing — collapsed', buildRoot: ROOT, expectedSha: SHA, story: 'core-sidenav--hidden-collapse', target: TARGET, readState, sensorErrors: realErrors(sensorErrors), expected: {...base, state: {navWidth: 0, inert: true, togglesTotal: 1, togglesReachable: 1}}, }); await browser.close(); } // --- The defect: collapsedWidth 0 on the DEFAULT hasButton. --- { const {browser, page, sensorErrors} = await open('scratch-sidenav3890--built-in-button'); await captureWithSensors(page, `${ROOT}/shots/trap__before.png`, { label: 'default hasButton — before collapse', buildRoot: ROOT, expectedSha: SHA, story: 'scratch-sidenav3890--built-in-button', target: TARGET, readState, sensorErrors: realErrors(sensorErrors), expected: {...base, state: {navWidth: 260, inert: false, togglesTotal: 1, togglesReachable: 1}}, }); await clickToggle(page); await page.waitForTimeout(400); // Authored expectation: the nav is gone AND no toggle is reachable. await captureWithSensors(page, `${ROOT}/shots/trap__after.png`, { label: 'default hasButton — after collapse (no way back)', buildRoot: ROOT, expectedSha: SHA, story: 'scratch-sidenav3890--built-in-button', target: TARGET, readState, sensorErrors: realErrors(sensorErrors), expected: {...base, state: {navWidth: 0, inert: true, togglesTotal: 1, togglesReachable: 0}}, }); await browser.close(); } console.log('frames done'); })().catch(e => {console.error('FRAMES FAILED', e.message); process.exit(1);});