Files
MyGoNavi/frontend/src/components/MonacoEditor.theme.test.ts
Syngnat ffd1be8ad6 🐛 fix(query-editor): 统一主题背景与结果区布局
让 Monaco、SQL 工具栏、数据网格和嵌入日志统一使用当前主题的工作台表面色,并兼容自定义主题覆盖。

统一结果 Tab 的高度、圆角与间距,移除嵌入日志重复标题,将清空日志和隐藏结果区操作并排放到 Tab 栏右侧。

补充主题、工具栏、结果 Tab 和日志面板回归测试。
2026-07-30 17:49:58 +08:00

67 lines
2.6 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { BUILTIN_CUSTOM_THEME_PRESETS } from '../utils/customThemePresets';
import { registerGonaviMonacoThemes } from './MonacoEditor';
const readHexProperty = (css: string, property: string): string => {
const escaped = property.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const match = css.match(new RegExp(`${escaped}\\s*:\\s*(#[0-9a-f]{6})`, 'i'));
if (!match?.[1]) throw new Error(`Missing hexadecimal custom property: ${property}`);
return match[1];
};
const relativeLuminance = (hex: string): number => {
const channels = [1, 3, 5].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16) / 255);
const [red, green, blue] = channels.map((channel) => (
channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4
));
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
};
const contrastRatio = (foreground: string, background: string): number => {
const light = Math.max(relativeLuminance(foreground), relativeLuminance(background));
const dark = Math.min(relativeLuminance(foreground), relativeLuminance(background));
return (light + 0.05) / (dark + 0.05);
};
describe('GoNavi Monaco themes', () => {
it('defines bold SQL keyword colors that remain AA-readable on the shared workbench surface', () => {
const defineTheme = vi.fn();
registerGonaviMonacoThemes({ editor: { defineTheme } } as never);
const definitions = new Map(
defineTheme.mock.calls.map(([name, definition]) => [name, definition]),
);
const keywordTokens = [
'keyword.sql',
'keyword.try.sql',
'keyword.catch.sql',
'keyword.block.sql',
'keyword.choice.sql',
];
const lightRules = definitions.get('transparent-light')?.rules ?? [];
const darkRules = definitions.get('transparent-dark')?.rules ?? [];
for (const token of keywordTokens) {
expect(lightRules.find((rule: any) => rule.token === token)).toMatchObject({
foreground: '6D28D9',
fontStyle: 'bold',
});
expect(darkRules.find((rule: any) => rule.token === token)).toMatchObject({
foreground: 'C792EA',
fontStyle: 'bold',
});
}
for (const preset of BUILTIN_CUSTOM_THEME_PRESETS) {
const keyword = (preset.baseMode === 'dark' ? darkRules : lightRules)
.find((rule: any) => rule.token === 'keyword.sql');
const background = readHexProperty(preset.css, '--gn-bg-panel-2');
expect(
contrastRatio(`#${keyword.foreground}`, background),
`${preset.id} SQL keyword must contrast with its editor background`,
).toBeGreaterThanOrEqual(4.5);
}
});
});