feat: v0.2.5 — 内置语法高亮 + exportPDF + benchmark + CI 并行
- 新增 src/highlight.ts 零依赖轻量高亮器:13 种语言,先转义后 着色保证安全,输出 me-hl-* 类,MeEditor.highlight 即插即用 - exportTool 新增 editor.exportPDF()(隐藏 iframe + window.print), exportHTML/exportPDF 共用 HTML 构建逻辑 - imagePaste 支持 maxSizeKB 限制,超限 toast 警告 - npm run bench 性能基准:parseMarkdown 1MB ≈ 51ms - CI 测试拆分为 parser/core/rest 三个并行 job + 跨版本 verify job - docs.html/README 补齐新 API、事件、高亮文档;取消跟踪 dist 产物; 版本 0.2.4 → 0.2.5,测试 725 → 768
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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('<script>');
|
||||
});
|
||||
|
||||
test('字符串中的引号安全', () => {
|
||||
const out = highlight('const s = "a<b>&"', 'js');
|
||||
expect(out).not.toContain('a<b');
|
||||
expect(out).toContain('a<b>');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -29,7 +29,7 @@ describe('index.ts - 全局 API', () => {
|
||||
|
||||
test('VERSION 是字符串', () => {
|
||||
expect(typeof VERSION).toBe('string');
|
||||
expect(VERSION).toBe('0.2.4');
|
||||
expect(VERSION).toBe('0.2.5');
|
||||
});
|
||||
|
||||
test('api.default 是 api 本身', () => {
|
||||
|
||||
Reference in New Issue
Block a user