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

31 KiB
Raw Blame History

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 相关的所有配置选项

接口

// 获取配置选项
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 库

接口

// 检测页面是否包含 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 地址映射

$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 配置并启动渲染

接口

// 初始化 Mermaid 配置
function initMermaidConfig()

// 获取当前主题对应的 Mermaid 主题
function getMermaidTheme()

// 渲染所有 Mermaid 图表
function renderAllMermaidCharts()

// 重新渲染图表(主题切换时)
function reRenderMermaidCharts()

Mermaid 配置对象

{
    startOnLoad: false,  // 手动控制渲染时机
    theme: 'default',    // 根据页面主题动态设置
    securityLevel: 'loose',  // 允许 HTML 标签
    logLevel: 'error',   // 生产环境使用 error调试模式使用 debug
    flowchart: {
        useMaxWidth: true,
        htmlLabels: true
    }
}

4. 代码块检测器 (Code Block Detector)

职责:识别页面中的 Mermaid 代码块

接口

// 检测所有 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)

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

接口

// 应用容器样式
function applyMermaidContainerStyles(container)

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

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

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)

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

接口

// 处理渲染错误
function handleMermaidError(error, element)

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

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

错误提示格式

<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)

职责:监听主题模式切换并重新渲染图表

接口

// 监听主题切换事件
function listenThemeSwitch()

// 主题切换回调
function onThemeSwitched(isDarkMode)

// 批量重新渲染
function batchReRender(elements)

实现方式

  • 监听 Argon 主题的 argon:theme-switched 自定义事件
  • 监听 html 元素的 darkmode class 变化MutationObserver
  • 使用防抖避免频繁重新渲染

8. 插件兼容层 (Plugin Compatibility Layer)

职责:检测并兼容主流 Mermaid 插件

接口

// 检测已安装的 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 配置对象

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. 代码块元素对象

interface MermaidBlock {
    element: HTMLElement;       // DOM 元素
    code: string;               // Mermaid 代码
    type: string;               // 代码块类型: 'div' | 'pre-code' | 'custom'
    rendered: boolean;          // 是否已渲染
    error: Error | null;        // 渲染错误
}

3. 渲染结果对象

interface RenderResult {
    success: boolean;           // 是否成功
    svg: string;                // 渲染后的 SVG
    error: {
        type: string;           // 错误类型
        message: string;        // 错误信息
        line: number;           // 错误行号
    } | null;
}

4. 插件检测结果

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. 错误信息对象

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 都失败,显示全局提示信息

实现

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

<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. 阻止保存无效配置

验证函数

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. 在设置页显示插件兼容性状态

检测逻辑

function checkMermaidLoaded() {
    if (typeof window.mermaid !== 'undefined') {
        console.log('[Argon Mermaid] 检测到 Mermaid 库已由其他插件加载');
        return true;
    }
    return false;
}

5. 主题切换异常

场景:主题切换时重新渲染失败

处理策略

  1. 使用 try-catch 包裹重新渲染逻辑
  2. 如果重新渲染失败,保留原有图表
  3. 在控制台输出警告信息
  4. 不影响页面其他功能

实现

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 单元测试示例

// 测试 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 单元测试示例

// 测试主题获取
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}

属性测试示例

// 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 个正确性属性
  • 单元测试覆盖:所有核心函数和边缘情况