06c12f40b6
新增画板 Portal 主题桥接并同步亮暗主题 让编辑器根节点与 Portal 菜单共享完整品牌 Token 补充静音开关、角色动画与样式契约回归测试 同步图片画布技术方案和 Portal 排障记忆
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
function readIndexCss() {
|
|
return fs.readFileSync(path.resolve(process.cwd(), 'src/index.css'), 'utf8');
|
|
}
|
|
|
|
function getCssBlock(source: string, selector: string) {
|
|
const selectorIndex = source.indexOf(selector);
|
|
expect(selectorIndex, `${selector} should exist`).toBeGreaterThanOrEqual(0);
|
|
|
|
const openBraceIndex = source.indexOf('{', selectorIndex);
|
|
expect(
|
|
openBraceIndex,
|
|
`${selector} should open a CSS block`,
|
|
).toBeGreaterThanOrEqual(0);
|
|
|
|
let depth = 0;
|
|
for (let index = openBraceIndex; index < source.length; index += 1) {
|
|
const char = source[index];
|
|
if (char === '{') {
|
|
depth += 1;
|
|
} else if (char === '}') {
|
|
depth -= 1;
|
|
if (depth === 0) {
|
|
return source.slice(openBraceIndex + 1, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new Error(`${selector} block is not closed`);
|
|
}
|
|
|
|
describe('image canvas portal theme contract', () => {
|
|
it('exposes every image canvas brand token to body-level portal menus', () => {
|
|
const css = readIndexCss();
|
|
const sharedTokenBlock = getCssBlock(
|
|
css,
|
|
'.image-canvas-editor,\n.image-canvas-editor__portal-theme,\n.image-canvas-editor__portal-menu',
|
|
);
|
|
const editorBlock = getCssBlock(css, '.image-canvas-editor {');
|
|
const pressedToggleTrackBlock = getCssBlock(
|
|
css,
|
|
".image-canvas-editor__video-toggle[aria-pressed='true'] span",
|
|
);
|
|
|
|
for (const token of [
|
|
'--image-canvas-brand-accent:',
|
|
'--image-canvas-brand-accent-strong:',
|
|
'--image-canvas-brand-fill:',
|
|
'--image-canvas-brand-text-on-fill:',
|
|
'--image-canvas-brand-border:',
|
|
'--image-canvas-brand-border-strong:',
|
|
'--image-canvas-brand-border-soft:',
|
|
'--image-canvas-brand-soft:',
|
|
'--image-canvas-brand-soft-strong:',
|
|
'--image-canvas-brand-shadow:',
|
|
'--image-canvas-brand-focus-ring:',
|
|
]) {
|
|
expect(sharedTokenBlock).toContain(token);
|
|
}
|
|
|
|
for (const editorOnlyDeclaration of [
|
|
'position:',
|
|
'display:',
|
|
'height:',
|
|
'overflow:',
|
|
'background:',
|
|
'color:',
|
|
'user-select:',
|
|
]) {
|
|
expect(sharedTokenBlock).not.toContain(editorOnlyDeclaration);
|
|
}
|
|
|
|
expect(editorBlock).not.toContain('--image-canvas-brand-accent:');
|
|
expect(pressedToggleTrackBlock).toContain(
|
|
'background: var(--image-canvas-brand-accent);',
|
|
);
|
|
expect(pressedToggleTrackBlock).not.toContain('#00ff00');
|
|
});
|
|
});
|