# Design Document: Mermaid 图表支持 ## Overview 本设计文档描述了在 Argon WordPress 主题中集成 Mermaid 图表支持的技术方案。由于 WP-Markdown 编辑器的特殊渲染方式(将 Mermaid 代码块保存为单行,缺少真正的换行符),直接集成 Mermaid 存在技术障碍。 本设计采用**插件兼容方案**,通过支持主流 Mermaid WordPress 插件(如 WP Githuber MD、Markdown Block 等)来实现功能,同时提供主题级别的样式优化、夜间模式适配和性能优化。 ### 核心设计原则 1. **插件优先**:依赖成熟的 Mermaid 插件处理代码解析和渲染 2. **主题增强**:提供样式优化、主题适配和性能优化 3. **灵活配置**:支持 CDN/本地加载、多种主题、调试模式 4. **优雅降级**:加载失败时提供备用方案和友好提示 5. **性能优先**:按需加载、异步加载、缓存优化 ## Architecture ### 系统架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ WordPress 前端页面 │ └─────────────────────────────────────────────────────────────┘ │ ├─ 包含 Mermaid 代码块? │ ┌─────────┴─────────┐ │ │ 是 否 │ │ ▼ ▼ ┌──────────────────┐ ┌──────────┐ │ 加载 Mermaid 库 │ │ 不加载 │ └──────────────────┘ └──────────┘ │ ├─ CDN 模式 / 本地模式 │ ▼ ┌──────────────────┐ │ Mermaid.js 库 │ └──────────────────┘ │ ▼ ┌──────────────────┐ │ 代码块检测器 │ │ - class="mermaid"│ │ - language="mermaid"│ │ - data-lang="mermaid"│ └──────────────────┘ │ ▼ ┌──────────────────┐ │ 渲染引擎 │ │ - 初始化配置 │ │ - 主题适配 │ │ - 错误处理 │ └──────────────────┘ │ ▼ ┌──────────────────┐ │ 样式增强器 │ │ - 容器样式 │ │ - 响应式适配 │ │ - 夜间模式 │ └──────────────────┘ │ ▼ ┌──────────────────┐ │ 渲染后的 SVG │ └──────────────────┘ ``` ### 组件交互流程 ``` 用户访问页面 │ ▼ WordPress 渲染页面 │ ▼ 主题检测页面内容 │ ├─ 是否包含 Mermaid 代码块? │ ▼ (是) 加载 Mermaid 库 (CDN/本地) │ ▼ DOMContentLoaded 事件触发 │ ▼ 初始化 Mermaid 配置 │ ├─ 设置主题 (日间/夜间) ├─ 设置安全级别 └─ 设置错误处理 │ ▼ 检测所有 Mermaid 代码块 │ ▼ 批量渲染图表 │ ├─ 成功 → 应用样式增强 └─ 失败 → 显示错误提示 │ ▼ 监听主题切换事件 │ ▼ 重新渲染图表 (如需要) ``` ## Components and Interfaces ### 1. 配置管理组件 (Configuration Manager) **职责**:管理 Mermaid 相关的所有配置选项 **接口**: ```php // 获取配置选项 function argon_get_mermaid_option($option_name, $default = null) // 保存配置选项 function argon_update_mermaid_option($option_name, $value) // 验证 CDN 地址格式 function argon_validate_mermaid_cdn_url($url) // 获取当前主题模式对应的 Mermaid 主题 function argon_get_mermaid_theme() ``` **配置选项**: - `argon_enable_mermaid`: 启用/禁用 Mermaid 支持 (true/false) - `argon_mermaid_cdn_source`: CDN 来源 (jsdelivr/unpkg/custom/local) - `argon_mermaid_cdn_custom_url`: 自定义 CDN 地址 - `argon_mermaid_theme`: 图表主题 (default/dark/forest/neutral/auto) - `argon_mermaid_use_local`: 使用本地镜像 (true/false) - `argon_mermaid_debug_mode`: 调试模式 (true/false) ### 2. 库加载器 (Library Loader) **职责**:负责检测页面内容并按需加载 Mermaid 库 **接口**: ```php // 检测页面是否包含 Mermaid 代码块 function argon_has_mermaid_content($content) // 加载 Mermaid 库 function argon_enqueue_mermaid_scripts() // 获取 Mermaid 库 URL function argon_get_mermaid_library_url() ``` **实现逻辑**: 1. 在 `wp_enqueue_scripts` 钩子中检查当前页面内容 2. 使用正则表达式检测 Mermaid 代码块标记 3. 如果检测到,根据配置加载对应的库文件 4. 添加 async 或 defer 属性实现异步加载 **CDN 地址映射**: ```php $cdn_urls = [ 'jsdelivr' => 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js', 'unpkg' => 'https://unpkg.com/mermaid@10/dist/mermaid.min.js', 'local' => get_template_directory_uri() . '/assets/vendor/mermaid/mermaid.min.js' ]; ``` ### 3. 渲染引擎初始化器 (Render Engine Initializer) **职责**:初始化 Mermaid 配置并启动渲染 **接口**: ```javascript // 初始化 Mermaid 配置 function initMermaidConfig() // 获取当前主题对应的 Mermaid 主题 function getMermaidTheme() // 渲染所有 Mermaid 图表 function renderAllMermaidCharts() // 重新渲染图表(主题切换时) function reRenderMermaidCharts() ``` **Mermaid 配置对象**: ```javascript { startOnLoad: false, // 手动控制渲染时机 theme: 'default', // 根据页面主题动态设置 securityLevel: 'loose', // 允许 HTML 标签 logLevel: 'error', // 生产环境使用 error,调试模式使用 debug flowchart: { useMaxWidth: true, htmlLabels: true } } ``` ### 4. 代码块检测器 (Code Block Detector) **职责**:识别页面中的 Mermaid 代码块 **接口**: ```javascript // 检测所有 Mermaid 代码块 function detectMermaidBlocks() // 检查元素是否为 Mermaid 代码块 function isMermaidBlock(element) // 提取代码块内容 function extractMermaidCode(element) ``` **检测规则**(优先级从高到低): 1. `
` - 标准格式 2. `
` - Markdown 格式
3. `
` - 自定义属性格式
4. `` - 简化格式

**特殊处理**:
- 忽略 HTML 注释中的代码块
- 处理 WP-Markdown 生成的 `` 格式
- 解码转义字符(`\n`, `\"`, `\'`)

### 5. 样式增强器 (Style Enhancer)

**职责**:为渲染后的图表添加主题样式

**接口**:
```javascript
// 应用容器样式
function applyMermaidContainerStyles(container)

// 应用响应式样式
function applyResponsiveStyles(container)

// 应用夜间模式样式
function applyDarkModeStyles(container, isDarkMode)
```

**CSS 样式类**:
```css
.mermaid-container {
    background: var(--card-background);
    border-radius: 8px;
    padding: 20px;
    margin: 20px 0;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    overflow-x: auto;
    max-width: 100%;
}

.mermaid-container svg {
    max-width: 100%;
    height: auto;
}

html.darkmode .mermaid-container {
    background: var(--card-background-dark);
    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
```

### 6. 错误处理器 (Error Handler)

**职责**:处理渲染错误并显示友好提示

**接口**:
```javascript
// 处理渲染错误
function handleMermaidError(error, element)

// 显示错误提示
function showErrorMessage(element, errorInfo)

// 记录调试信息
function logDebugInfo(message, data)
```

**错误提示格式**:
```html
⚠️
Mermaid 图表渲染失败
错误类型: 语法错误
行号: 3
查看原始代码
...
``` ### 7. 主题切换监听器 (Theme Switch Listener) **职责**:监听主题模式切换并重新渲染图表 **接口**: ```javascript // 监听主题切换事件 function listenThemeSwitch() // 主题切换回调 function onThemeSwitched(isDarkMode) // 批量重新渲染 function batchReRender(elements) ``` **实现方式**: - 监听 Argon 主题的 `argon:theme-switched` 自定义事件 - 监听 `html` 元素的 `darkmode` class 变化(MutationObserver) - 使用防抖避免频繁重新渲染 ### 8. 插件兼容层 (Plugin Compatibility Layer) **职责**:检测并兼容主流 Mermaid 插件 **接口**: ```php // 检测已安装的 Mermaid 插件 function argon_detect_mermaid_plugins() // 检查是否已加载 Mermaid 库 function argon_is_mermaid_loaded() // 避免重复加载 function argon_prevent_duplicate_loading() ``` **支持的插件**: 1. **WP Githuber MD** - 检测 `wp-githuber-md` 插件 2. **Markdown Block** - 检测 Gutenberg Mermaid 块 3. **Code Syntax Block** - 检测代码高亮插件的 Mermaid 支持 **兼容策略**: - 如果插件已加载 Mermaid 库,主题不再重复加载 - 主题只提供样式增强和主题适配 - 通过 `window.mermaid` 对象检测库是否已加载 ## Data Models ### 1. Mermaid 配置对象 ```javascript interface MermaidConfig { enabled: boolean; // 是否启用 cdnSource: string; // CDN 来源: 'jsdelivr' | 'unpkg' | 'custom' | 'local' customCdnUrl: string; // 自定义 CDN 地址 theme: string; // 图表主题: 'default' | 'dark' | 'forest' | 'neutral' | 'auto' useLocal: boolean; // 是否使用本地镜像 debugMode: boolean; // 调试模式 autoThemeSwitch: boolean; // 自动切换主题 } ``` ### 2. 代码块元素对象 ```javascript interface MermaidBlock { element: HTMLElement; // DOM 元素 code: string; // Mermaid 代码 type: string; // 代码块类型: 'div' | 'pre-code' | 'custom' rendered: boolean; // 是否已渲染 error: Error | null; // 渲染错误 } ``` ### 3. 渲染结果对象 ```javascript interface RenderResult { success: boolean; // 是否成功 svg: string; // 渲染后的 SVG error: { type: string; // 错误类型 message: string; // 错误信息 line: number; // 错误行号 } | null; } ``` ### 4. 插件检测结果 ```php interface PluginDetectionResult { 'wp-githuber-md': boolean; // WP Githuber MD 'markdown-block': boolean; // Markdown Block 'code-syntax-block': boolean; // Code Syntax Block 'mermaid-loaded': boolean; // Mermaid 库是否已加载 } ``` ### 5. 错误信息对象 ```javascript interface ErrorInfo { type: string; // 错误类型: 'syntax' | 'render' | 'load' message: string; // 错误信息 line: number | null; // 错误行号 code: string; // 原始代码 timestamp: number; // 时间戳 } ``` ## Correctness Properties *属性是一种特征或行为,应该在系统的所有有效执行中保持为真——本质上是关于系统应该做什么的正式陈述。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。* ### Property 1: 按需加载库 *对于任意* WordPress 页面,当且仅当页面内容包含 Mermaid 代码块时,主题应该加载 Mermaid JavaScript 库。 **Validates: Requirements 1.1, 1.5, 8.1** ### Property 2: CDN 地址正确性 *对于任意* CDN 配置选项(jsdelivr、unpkg、custom、local),生成的脚本 URL 应该与配置选项对应的 CDN 地址匹配。 **Validates: Requirements 1.2, 1.3** ### Property 3: 代码块识别完整性 *对于任意* 包含 Mermaid 标记的 HTML 元素(class="mermaid"、language="mermaid"、data-lang="mermaid"),检测器应该能够识别并提取其中的 Mermaid 代码。 **Validates: Requirements 10.1, 10.2, 10.3** ### Property 4: 渲染成功后替换内容 *对于任意* 成功渲染的 Mermaid 图表,原始代码块文本应该被移除,并替换为渲染后的 SVG 图表。 **Validates: Requirements 2.4** ### Property 5: 错误时保留原始代码 *对于任意* 渲染失败的 Mermaid 代码块,系统应该显示错误提示信息,并保留原始代码块以便用户修正。 **Validates: Requirements 7.1, 7.4** ### Property 6: 主题模式自动切换 *对于任意* 页面主题模式切换(日间↔夜间),当配置为自动切换时,所有 Mermaid 图表应该重新渲染并使用对应的图表主题(浅色↔深色)。 **Validates: Requirements 4.1, 4.2, 4.3** ### Property 7: 自定义主题优先级 *对于任意* 图表,当管理员设置了自定义图表主题时,应该使用自定义主题而不是根据页面主题自动切换。 **Validates: Requirements 4.5** ### Property 8: 响应式容器宽度 *对于任意* 渲染后的 Mermaid 图表容器,其最大宽度应该设置为 100%,并且当图表宽度超过容器时应该启用横向滚动。 **Validates: Requirements 3.1, 3.3** ### Property 9: 移动端自适应 *对于任意* 屏幕宽度小于 768px 的设备,Mermaid 图表应该自动调整大小以适应屏幕宽度,不应该出现横向溢出。 **Validates: Requirements 3.2** ### Property 10: 夜间模式样式适配 *对于任意* Mermaid 图表容器,在夜间模式下应该应用深色背景和边框样式,与页面整体风格保持一致。 **Validates: Requirements 6.5** ### Property 11: 卡片内边距 *对于任意* 在卡片中显示的 Mermaid 图表,容器应该添加适当的内边距(padding),确保图表与卡片边缘有足够的间距。 **Validates: Requirements 6.3** ### Property 12: CDN 地址验证 *对于任意* 用户输入的自定义 CDN 地址,保存前应该验证其格式是否为有效的 URL,并且以 `.js` 结尾。 **Validates: Requirements 5.5** ### Property 13: 避免重复加载 *对于任意* 页面,当检测到已有 Mermaid 插件加载了 Mermaid 库时,主题不应该重复加载该库。 **Validates: Requirements 9.4** ### Property 14: 错误信息完整性 *对于任意* Mermaid 解析错误,错误提示信息应该包含错误类型和详细的错误描述,帮助用户定位问题。 **Validates: Requirements 7.3** ### Property 15: 批量渲染性能 *对于任意* 包含多个 Mermaid 图表的页面,所有图表应该在一次 DOM 遍历中批量收集,然后批量渲染,而不是逐个渲染。 **Validates: Requirements 8.4** ### Property 16: 渲染缓存 *对于任意* 已成功渲染的 Mermaid 图表,在页面生命周期内不应该重复渲染,除非主题模式发生切换。 **Validates: Requirements 8.3** ### Property 17: 代码块优先级 *对于任意* 同时包含多个 Mermaid 标记的元素(如同时有 class="mermaid" 和 data-lang="mermaid"),应该优先使用 class 属性进行识别。 **Validates: Requirements 10.4** ### Property 18: 忽略注释代码块 *对于任意* 被 HTML 注释包裹的 Mermaid 代码块,检测器应该忽略它们,不进行渲染。 **Validates: Requirements 10.5** ## Error Handling ### 1. 库加载失败 **场景**:CDN 不可用或网络问题导致 Mermaid 库加载失败 **处理策略**: 1. 监听脚本 `onerror` 事件 2. 在控制台输出详细错误信息 3. 尝试降级到备用 CDN(jsdelivr → unpkg → local) 4. 如果所有 CDN 都失败,显示全局提示信息 **实现**: ```javascript function loadMermaidWithFallback(urls, index = 0) { if (index >= urls.length) { console.error('[Argon Mermaid] 所有 CDN 加载失败'); showGlobalError('Mermaid 库加载失败,请检查网络连接'); return; } const script = document.createElement('script'); script.src = urls[index]; script.async = true; script.onerror = () => { console.warn(`[Argon Mermaid] CDN ${urls[index]} 加载失败,尝试备用 CDN`); loadMermaidWithFallback(urls, index + 1); }; script.onload = () => { console.log(`[Argon Mermaid] 成功从 ${urls[index]} 加载库`); initMermaid(); }; document.head.appendChild(script); } ``` ### 2. 代码解析错误 **场景**:Mermaid 代码语法错误导致解析失败 **处理策略**: 1. 捕获 Mermaid 渲染异常 2. 提取错误类型和行号信息 3. 在原代码块位置显示友好的错误提示 4. 保留原始代码供用户查看和修正 5. 在调试模式下输出详细堆栈信息 **错误提示 UI**: ```html
⚠️ 图表渲染失败

错误类型: 语法错误

Expecting 'NEWLINE', 'SPACE', got 'GRAPH'

位置: 第 3 行

查看原始代码
...
``` ### 3. 配置验证错误 **场景**:管理员输入无效的配置选项 **处理策略**: 1. 在保存前验证所有配置项 2. CDN URL 格式验证(必须是有效 URL 且以 .js 结尾) 3. 主题名称验证(必须是预定义的主题之一) 4. 显示具体的验证错误信息 5. 阻止保存无效配置 **验证函数**: ```php function argon_validate_mermaid_settings($settings) { $errors = []; // 验证 CDN URL if ($settings['cdn_source'] === 'custom') { $url = $settings['custom_cdn_url']; if (!filter_var($url, FILTER_VALIDATE_URL)) { $errors[] = 'CDN 地址格式无效'; } elseif (!preg_match('/\.js$/', $url)) { $errors[] = 'CDN 地址必须以 .js 结尾'; } } // 验证主题名称 $valid_themes = ['default', 'dark', 'forest', 'neutral', 'auto']; if (!in_array($settings['theme'], $valid_themes)) { $errors[] = '无效的图表主题'; } return $errors; } ``` ### 4. 插件冲突 **场景**:多个插件同时加载 Mermaid 库导致冲突 **处理策略**: 1. 在加载前检测 `window.mermaid` 是否已存在 2. 如果已存在,跳过库加载,只应用样式增强 3. 记录检测结果到控制台 4. 在设置页显示插件兼容性状态 **检测逻辑**: ```javascript function checkMermaidLoaded() { if (typeof window.mermaid !== 'undefined') { console.log('[Argon Mermaid] 检测到 Mermaid 库已由其他插件加载'); return true; } return false; } ``` ### 5. 主题切换异常 **场景**:主题切换时重新渲染失败 **处理策略**: 1. 使用 try-catch 包裹重新渲染逻辑 2. 如果重新渲染失败,保留原有图表 3. 在控制台输出警告信息 4. 不影响页面其他功能 **实现**: ```javascript function reRenderOnThemeSwitch() { const charts = document.querySelectorAll('.mermaid-rendered'); charts.forEach(chart => { try { const code = chart.dataset.mermaidCode; const newTheme = getMermaidTheme(); mermaid.initialize({ theme: newTheme }); mermaid.render('mermaid-' + Date.now(), code, (svg) => { chart.innerHTML = svg; }); } catch (error) { console.warn('[Argon Mermaid] 重新渲染失败,保留原图表', error); } }); } ``` ## Testing Strategy ### 测试方法概述 本功能采用**双重测试策略**: - **单元测试**:验证具体示例、边缘情况和错误条件 - **属性测试**:验证跨所有输入的通用属性 两者互补且都是全面覆盖所需的: - 单元测试捕获具体的 bug - 属性测试验证一般正确性 ### 单元测试策略 单元测试应专注于: - **具体示例**:演示正确行为的特定案例 - **集成点**:组件之间的交互 - **边缘情况和错误条件**:特殊场景处理 **测试框架**:使用 PHPUnit(PHP 部分)和 Jest(JavaScript 部分) **PHP 单元测试示例**: ```php // 测试 CDN URL 验证 public function test_validate_cdn_url_with_valid_url() { $url = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js'; $this->assertTrue(argon_validate_mermaid_cdn_url($url)); } public function test_validate_cdn_url_with_invalid_url() { $url = 'not-a-valid-url'; $this->assertFalse(argon_validate_mermaid_cdn_url($url)); } // 测试代码块检测 public function test_has_mermaid_content_with_div_class() { $content = '
flowchart TD
'; $this->assertTrue(argon_has_mermaid_content($content)); } public function test_has_mermaid_content_without_mermaid() { $content = '

Regular paragraph

'; $this->assertFalse(argon_has_mermaid_content($content)); } ``` **JavaScript 单元测试示例**: ```javascript // 测试主题获取 test('getMermaidTheme returns dark theme in dark mode', () => { document.documentElement.classList.add('darkmode'); expect(getMermaidTheme()).toBe('dark'); }); test('getMermaidTheme returns default theme in light mode', () => { document.documentElement.classList.remove('darkmode'); expect(getMermaidTheme()).toBe('default'); }); // 测试代码块检测 test('isMermaidBlock detects div with mermaid class', () => { const div = document.createElement('div'); div.className = 'mermaid'; expect(isMermaidBlock(div)).toBe(true); }); test('isMermaidBlock ignores regular div', () => { const div = document.createElement('div'); expect(isMermaidBlock(div)).toBe(false); }); ``` ### 属性测试策略 **测试框架**:使用 fast-check(JavaScript)进行属性测试 **配置要求**: - 每个属性测试最少运行 100 次迭代 - 每个测试必须引用设计文档中的属性 - 标签格式:`Feature: mermaid-support, Property {number}: {property_text}` **属性测试示例**: ```javascript // Feature: mermaid-support, Property 1: 按需加载库 // 对于任意 WordPress 页面,当且仅当页面内容包含 Mermaid 代码块时,主题应该加载 Mermaid JavaScript 库 fc.assert( fc.property( fc.string(), // 生成随机页面内容 fc.boolean(), // 是否包含 Mermaid 代码块 (content, hasMermaid) => { const pageContent = hasMermaid ? content + '
flowchart TD
' : content; const shouldLoad = argon_has_mermaid_content(pageContent); expect(shouldLoad).toBe(hasMermaid); } ), { numRuns: 100 } ); // Feature: mermaid-support, Property 2: CDN 地址正确性 // 对于任意 CDN 配置选项,生成的脚本 URL 应该与配置选项对应的 CDN 地址匹配 fc.assert( fc.property( fc.constantFrom('jsdelivr', 'unpkg', 'local'), (cdnSource) => { const expectedUrls = { 'jsdelivr': 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js', 'unpkg': 'https://unpkg.com/mermaid@10/dist/mermaid.min.js', 'local': '/wp-content/themes/argon/assets/vendor/mermaid/mermaid.min.js' }; const actualUrl = argon_get_mermaid_library_url(cdnSource); expect(actualUrl).toContain(expectedUrls[cdnSource]); } ), { numRuns: 100 } ); // Feature: mermaid-support, Property 3: 代码块识别完整性 // 对于任意包含 Mermaid 标记的 HTML 元素,检测器应该能够识别并提取其中的 Mermaid 代码 fc.assert( fc.property( fc.constantFrom('class', 'language', 'data-lang'), fc.string({ minLength: 10 }), (markType, code) => { let element; switch (markType) { case 'class': element = document.createElement('div'); element.className = 'mermaid'; element.textContent = code; break; case 'language': element = document.createElement('pre'); const codeEl = document.createElement('code'); codeEl.className = 'language-mermaid'; codeEl.textContent = code; element.appendChild(codeEl); break; case 'data-lang': element = document.createElement('pre'); element.setAttribute('data-lang', 'mermaid'); element.textContent = code; break; } expect(isMermaidBlock(element)).toBe(true); expect(extractMermaidCode(element)).toBe(code); } ), { numRuns: 100 } ); // Feature: mermaid-support, Property 6: 主题模式自动切换 // 对于任意页面主题模式切换,当配置为自动切换时,所有 Mermaid 图表应该重新渲染并使用对应的图表主题 fc.assert( fc.property( fc.boolean(), // 初始主题模式 fc.integer({ min: 1, max: 10 }), // 图表数量 (initialDarkMode, chartCount) => { // 设置初始主题 if (initialDarkMode) { document.documentElement.classList.add('darkmode'); } else { document.documentElement.classList.remove('darkmode'); } // 创建多个图表 const charts = []; for (let i = 0; i < chartCount; i++) { const chart = document.createElement('div'); chart.className = 'mermaid-rendered'; chart.dataset.mermaidCode = 'flowchart TD\nA-->B'; document.body.appendChild(chart); charts.push(chart); } // 切换主题 const newDarkMode = !initialDarkMode; if (newDarkMode) { document.documentElement.classList.add('darkmode'); } else { document.documentElement.classList.remove('darkmode'); } // 触发重新渲染 reRenderOnThemeSwitch(); // 验证所有图表都使用了新主题 const expectedTheme = newDarkMode ? 'dark' : 'default'; charts.forEach(chart => { expect(chart.dataset.currentTheme).toBe(expectedTheme); }); // 清理 charts.forEach(chart => chart.remove()); } ), { numRuns: 100 } ); // Feature: mermaid-support, Property 12: CDN 地址验证 // 对于任意用户输入的自定义 CDN 地址,保存前应该验证其格式是否为有效的 URL,并且以 .js 结尾 fc.assert( fc.property( fc.webUrl(), // 生成随机 URL fc.constantFrom('.js', '.css', '.json', ''), // 不同的文件扩展名 (baseUrl, extension) => { const url = baseUrl + extension; const isValid = argon_validate_mermaid_cdn_url(url); // 只有以 .js 结尾的有效 URL 才应该通过验证 const shouldBeValid = extension === '.js'; expect(isValid).toBe(shouldBeValid); } ), { numRuns: 100 } ); // Feature: mermaid-support, Property 15: 批量渲染性能 // 对于任意包含多个 Mermaid 图表的页面,所有图表应该在一次 DOM 遍历中批量收集,然后批量渲染 fc.assert( fc.property( fc.integer({ min: 1, max: 20 }), // 图表数量 (chartCount) => { // 创建多个图表 for (let i = 0; i < chartCount; i++) { const div = document.createElement('div'); div.className = 'mermaid'; div.textContent = `flowchart TD\nA${i}-->B${i}`; document.body.appendChild(div); } // 记录 DOM 查询次数 let queryCount = 0; const originalQuerySelectorAll = document.querySelectorAll; document.querySelectorAll = function(...args) { queryCount++; return originalQuerySelectorAll.apply(this, args); }; // 执行批量渲染 renderAllMermaidCharts(); // 恢复原始方法 document.querySelectorAll = originalQuerySelectorAll; // 验证只进行了一次 DOM 查询 expect(queryCount).toBe(1); // 清理 document.querySelectorAll('.mermaid').forEach(el => el.remove()); } ), { numRuns: 100 } ); ``` ### 集成测试 **测试场景**: 1. **完整渲染流程**:从页面加载到图表显示的完整流程 2. **插件兼容性**:与 WP Githuber MD、Markdown Block 等插件的集成 3. **主题切换**:日间/夜间模式切换时的图表重新渲染 4. **错误恢复**:CDN 加载失败时的降级处理 **测试工具**:使用 Playwright 或 Puppeteer 进行端到端测试 ### 手动测试清单 由于某些需求难以自动化测试,需要进行手动验证: - [ ] 图表颜色与页面背景色有足够的对比度(Requirements 4.4) - [ ] 图表容器样式与主题整体风格一致(Requirements 6.2) - [ ] 所有 Mermaid 官方图表类型都能正确渲染(Requirements 2.2) - [ ] 移动端显示效果良好(Requirements 3.2) - [ ] 错误提示信息友好易懂(Requirements 7.1) - [ ] 设置页预览功能正常工作(Requirements 5.6) ### 测试覆盖率目标 - **PHP 代码覆盖率**:≥ 80% - **JavaScript 代码覆盖率**:≥ 85% - **属性测试覆盖**:所有 18 个正确性属性 - **单元测试覆盖**:所有核心函数和边缘情况