# 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. `
` - 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
图表渲染失败
错误类型: 语法错误
位置: 第 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 个正确性属性
- **单元测试覆盖**:所有核心函数和边缘情况