978 lines
31 KiB
Markdown
978 lines
31 KiB
Markdown
|
|
# 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. 尝试降级到备用 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
|
|||
|
|
<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
|
|||
|
|
- 属性测试验证一般正确性
|
|||
|
|
|
|||
|
|
### 单元测试策略
|
|||
|
|
|
|||
|
|
单元测试应专注于:
|
|||
|
|
- **具体示例**:演示正确行为的特定案例
|
|||
|
|
- **集成点**:组件之间的交互
|
|||
|
|
- **边缘情况和错误条件**:特殊场景处理
|
|||
|
|
|
|||
|
|
**测试框架**:使用 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 = '<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-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 + '<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 个正确性属性
|
|||
|
|
- **单元测试覆盖**:所有核心函数和边缘情况
|
|||
|
|
|