/** Isolated DOM regression; not a browser layout/pixel or original-app acceptance test. */ import assert from 'node:assert/strict' import fs from 'node:fs' import path from 'node:path' import { createRequire } from 'node:module' import { build } from 'esbuild' const require = createRequire(import.meta.url) const { JSDOM } = require(process.env.YCODE_TEST_DOM_MODULE || process.env.YCODE_JSDOM_PATH || 'jsdom') const output = fs.mkdtempSync(path.resolve('.tmp/mode-chip-dom-')) await build({ entryPoints: ['src/renderer/src/workbuddy/composer/ModeChip.tsx'], bundle: true, platform: 'node', format: 'cjs', jsx: 'automatic', packages: 'external', loader: { '.css': 'empty' }, outfile: path.join(output, 'component.cjs'), logLevel: 'error' }) const dom = new JSDOM('
', { url: 'http://localhost' }) Object.assign(globalThis, { window: dom.window, document: dom.window.document, HTMLElement: dom.window.HTMLElement, IS_REACT_ACT_ENVIRONMENT: true }) const React = require('react'), { createRoot } = require('react-dom/client') const { ModeChip } = require(path.join(output, 'component.cjs')) const css = fs.readFileSync('src/renderer/src/workbuddy/composer/mode-chip.css', 'utf8') const style = document.createElement('style'); style.textContent = css; document.head.append(style) const root = createRoot(document.getElementById('root')); let clears = 0, checks = 0 const check = (actual, expected, message) => { assert.deepEqual(actual, expected, message); checks++ } const render = async (mode, disabled = false) => React.act(async () => root.render(React.createElement(ModeChip, { mode, disabled, onClear: () => { clears++ } }))) for (const mode of ['plan', 'ask']) { await render(mode) const chip = document.querySelector('.yc-mode-chip'), slot = chip.querySelector('.yc-mode-chip__icon-slot'), button = slot.querySelector('button'), icon = slot.querySelector('.yc-mode-chip__mode-icon') check(chip.textContent, mode === 'plan' ? '计划' : '仅问答', 'mode label has no extra X text') check(chip.getAttribute('role'), 'group', 'chip remains a group, not a menu') check(button.type, 'button', 'clear cannot submit composer') check(button.getAttribute('aria-label'), `退出${chip.textContent}模式`, 'accessible exit name') check(icon.parentElement === button.parentElement, true, 'icon and close share one slot') check(dom.window.getComputedStyle(slot).display, 'grid', 'shared grid slot') check(dom.window.getComputedStyle(button).gridArea, '1 / 1', 'button occupies original icon cell') check(dom.window.getComputedStyle(icon).gridArea, '1 / 1', 'mode occupies same original cell') check(dom.window.getComputedStyle(button).position, 'static', 'no floating absolute close') check(dom.window.getComputedStyle(chip).backgroundColor, 'rgba(0, 0, 0, 0)', 'transparent background') await React.act(async () => button.focus()) check(document.activeElement === button, true, 'native button receives keyboard focus') const before = clears await React.act(async () => button.click()) check(clears, before + 1, 'activation calls existing host clear exactly once') await render(mode, true) check(document.querySelector('button'), null, 'disabled mode exposes no clear action') check(document.querySelector('.yc-mode-chip').getAttribute('aria-disabled'), 'true', 'disabled state is announced') check(document.querySelector('.yc-mode-chip').tabIndex, -1, 'disabled mode not in keyboard tab order') } await render('craft'); check(document.querySelector('.yc-mode-chip'), null, 'craft stays hidden') // CSS contracts complement DOM checks; test-mode-chip-native.mjs is an unexecuted native hover/focus/layout probe. for (const contract of ['border-radius:var(--yc-radius-full)', 'color:var(--cr-text-secondary)', ':hover .yc-mode-chip__mode-icon', ':focus-within .yc-mode-chip__mode-icon', ':hover .yc-mode-chip__clear', ':focus-within .yc-mode-chip__clear']) check(css.includes(contract), true, contract) await React.act(async () => root.unmount()); dom.window.close() console.log(JSON.stringify({ checks, passed: true, scope: 'Isolated React DOM and CSS contracts; no native keyboard/layout/pixel acceptance' }))