Files
argon-theme/.kiro/specs/mermaid-support/design.md
nanhaoluo 1d5899ce7e feat: 实现 Mermaid 库加载失败的降级处理机制
- 添加多 CDN 备选方案(jsdelivr、unpkg、本地镜像)
- 实现递归加载逻辑,主 CDN 失败时自动尝试备用 CDN
- 添加 onerror 事件处理,捕获库加载失败
- 所有 CDN 失败时显示友好的错误提示
- 在错误提示中保留原始代码供用户查看
- 添加详细的控制台日志输出
- 创建 PHP 和 HTML 测试文件验证功能
- 暴露 MermaidRenderer 到全局作用域供降级处理使用

Requirements: 1.4, 2.3, 7.1, 7.2, 7.3, 7.4, 7.5
2026-01-23 23:12:05 +08:00

978 lines
31 KiB
Markdown
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.
# 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. `<div class="mermaid">` - 标准格式
2. `<pre><code class="language-mermaid">` - Markdown 格式
3. `<pre data-lang="mermaid">` - 自定义属性格式
4. `<code class="mermaid">` - 简化格式
**特殊处理**
- 忽略 HTML 注释中的代码块
- 处理 WP-Markdown 生成的 `<script>document.write()</script>` 格式
- 解码转义字符(`\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
<div class="mermaid-error">
<div class="error-icon">⚠️</div>
<div class="error-title">Mermaid 图表渲染失败</div>
<div class="error-message">错误类型: 语法错误</div>
<div class="error-details">行号: 3</div>
<details>
<summary>查看原始代码</summary>
<pre><code>...</code></pre>
</details>
</div>
```
### 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. 尝试降级到备用 CDNjsdelivr → 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
<div class="mermaid-error-container">
<div class="error-header">
<span class="error-icon">⚠️</span>
<span class="error-title">图表渲染失败</span>
</div>
<div class="error-body">
<p class="error-type">错误类型: 语法错误</p>
<p class="error-message">Expecting 'NEWLINE', 'SPACE', got 'GRAPH'</p>
<p class="error-line">位置: 第 3 行</p>
</div>
<details class="error-code">
<summary>查看原始代码</summary>
<pre><code class="language-mermaid">...</code></pre>
</details>
</div>
```
### 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
- 属性测试验证一般正确性
### 单元测试策略
单元测试应专注于:
- **具体示例**:演示正确行为的特定案例
- **集成点**:组件之间的交互
- **边缘情况和错误条件**:特殊场景处理
**测试框架**:使用 PHPUnitPHP 部分)和 JestJavaScript 部分)
**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 = '<div class="mermaid">flowchart TD</div>';
$this->assertTrue(argon_has_mermaid_content($content));
}
public function test_has_mermaid_content_without_mermaid() {
$content = '<p>Regular paragraph</p>';
$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-checkJavaScript进行属性测试
**配置要求**
- 每个属性测试最少运行 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 + '<div class="mermaid">flowchart TD</div>'
: 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 个正确性属性
- **单元测试覆盖**:所有核心函数和边缘情况