Files
MetonaEditor/tests/highlight.test.ts
T
thzxx e5f99aee83 fix: 复查修复 5 处问题 — URL 双重转义/zen 泄漏/高亮环境不一致等
- parser: href/src/alt 改最小属性转义(escapeAttr 会把已实体化的 &
  再转义成 &,导致链接 URL 双重转义错误);新增 URL/alt 防回归测试
- core: _pushHistory 增加 maxLength 兜底截断(exec/Tab/Smart Enter/括号
  自动闭合等程序化路径此前绕过 maxlength 限制)
- core: destroy 时清理 Zen 模式的 document mousemove 监听(此前泄漏)
- highlight: 自写环境无关转义(仅 & < >),修复 Node 环境下字符串高亮
  失效、浏览器与 SSR 输出不一致的问题
- floating-toolbar/context-menu: toggleFloatingToolbar/registerContextMenu
  恢复链式 return this(拆分时丢失)
- plugins: 补齐 3 个硬编码文案的 i18n(regex/shortcuts/imageTooLarge),
  6 种语言同步
- 测试 768 → 780
2026-08-09 09:26:20 +08:00

117 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* highlight.ts 单元测试
* 覆盖内置轻量语法高亮器的安全性与标记输出
*/
import { highlight, normalizeLanguage, registerLanguage, getSupportedLanguages } from '../src/highlight';
describe('highlight - 基础', () => {
test('未知语言返回转义纯文本', () => {
const out = highlight('const x = 1;', 'nonexistent');
expect(out).toBe('const x = 1;');
});
test('支持的语言别名归一化', () => {
expect(normalizeLanguage('js')).toBe('javascript');
expect(normalizeLanguage('ts')).toBe('typescript');
expect(normalizeLanguage('py')).toBe('python');
expect(normalizeLanguage('sh')).toBe('bash');
expect(normalizeLanguage('yml')).toBe('yaml');
expect(normalizeLanguage('Java')).toBe('java');
expect(normalizeLanguage('unknown')).toBe('');
expect(normalizeLanguage('')).toBe('');
});
test('关键词被标记', () => {
const out = highlight('const x = 1', 'js');
expect(out).toContain('<span class="me-hl-keyword">const</span>');
});
test('字符串被标记', () => {
const out = highlight('const s = "hello"', 'js');
expect(out).toContain('<span class="me-hl-string">"hello"</span>');
});
test('注释被标记', () => {
const out = highlight('// comment\nconst x = 1', 'js');
expect(out).toContain('<span class="me-hl-comment">// comment</span>');
});
test('数字被标记', () => {
const out = highlight('const n = 42', 'js');
expect(out).toContain('<span class="me-hl-number">42</span>');
});
test('函数调用被标记', () => {
const out = highlight('foo(bar)', 'js');
expect(out).toContain('<span class="me-hl-function">foo</span>');
});
test('python 井号注释与 def 关键词', () => {
const out = highlight('# note\ndef main():', 'python');
expect(out).toContain('<span class="me-hl-comment"># note</span>');
expect(out).toContain('<span class="me-hl-keyword">def</span>');
});
test('内置全局对象被标记', () => {
const out = highlight('console.log("x")', 'js');
expect(out).toContain('<span class="me-hl-builtin">console</span>');
});
});
describe('highlight - 安全性', () => {
test('HTML 特殊字符被转义,不产生裸标签', () => {
const out = highlight('<script>alert(1)</script>', 'js');
expect(out).not.toContain('<script>');
expect(out).not.toContain('<script');
expect(out).toContain('&lt;script&gt;');
});
test('字符串中的引号安全', () => {
const out = highlight('const s = "a<b>&"', 'js');
expect(out).not.toContain('a<b');
expect(out).toContain('a&lt;b&gt;');
});
test('恶意输入不破坏 span 结构', () => {
const out = highlight('"</span><script>x</script>"', 'js');
// 所有 span 配对(每开一个 <span 就有一个 </span>
const opens = (out.match(/<span/g) || []).length;
const closes = (out.match(/<\/span>/g) || []).length;
expect(opens).toBe(closes);
expect(out).not.toContain('<script');
});
});
describe('highlight - 语言注册', () => {
test('注册自定义语言', () => {
registerLanguage('foo', { keywords: ['frobnicate'], builtins: [] });
expect(normalizeLanguage('foo')).toBe('foo');
const out = highlight('frobnicate the widget', 'foo');
expect(out).toContain('<span class="me-hl-keyword">frobnicate</span>');
});
test('getSupportedLanguages 返回语言列表', () => {
const langs = getSupportedLanguages();
expect(Array.isArray(langs)).toBe(true);
expect(langs).toContain('javascript');
expect(langs).toContain('typescript');
expect(langs).toContain('python');
});
});
describe('highlight - 环境一致性', () => {
test('字符串高亮与引号转义环境无关(jsdom 断言)', () => {
const out = highlight('const s = "hi"', 'js');
expect(out).toContain('<span class="me-hl-string">"hi"</span>');
});
test('引号不被实体化,span 文本保持原样', () => {
const out = highlight('"a" + \'b\'', 'js');
expect(out).not.toContain('&quot;');
// 双引号字符串应被完整标记
expect(out).toContain('<span class="me-hl-string">"a"</span>');
expect(out).toContain("<span class=\"me-hl-string\">'b'</span>");
});
});