# Design Document: PJAX 和 Lazyload 功能修复

## Overview

本设计旨在修复 Argon WordPress 主题中 PJAX 页面无刷新跳转和 Lazyload 图片懒加载功能的问题。当前实现存在以下核心问题：

1. **资源泄漏**：页面切换时 Observer 和第三方库实例未正确清理
2. **脚本执行失败**：新页面的内联脚本未执行
3. **样式错误**：动态样式未清理导致样式冲突
4. **Mermaid 初始化时序问题**：清除缓存后首次加载报语法错误

设计方案采用完整的生命周期管理模式，确保资源在正确的时机创建和销毁，同时提供降级方案保证兼容性。

## Architecture

### 核心架构原则

1. **生命周期驱动**：所有资源管理基于 PJAX 生命周期事件
2. **集中清理**：统一的资源清理函数，避免遗漏
3. **错误隔离**：每个模块独立的错误处理，互不影响
4. **降级支持**：为不支持的特性提供降级方案

### 架构图

```
PJAX Lifecycle
├── pjax:click (开始)
│   └── 设置加载状态
├── pjax:beforeReplace (清理阶段)
│   ├── cleanupPjaxResources()
│   │   ├── 清理 Lazyload Observer
│   │   ├── 销毁 Zoomify 实例
│   │   ├── 销毁 Tippy 实例
│   │   ├── 清理 Mermaid 实例
│   │   └── 移除动态 style/script
│   └── 更新 UI 状态
├── pjax:complete (初始化阶段)
│   ├── 执行内联脚本
│   ├── 初始化功能模块
│   │   ├── waterflowInit()
│   │   ├── lazyloadInit()
│   │   ├── zoomifyInit()
│   │   ├── highlightJsRender()
│   │   └── ... (其他模块)
│   └── 恢复滚动位置
└── pjax:end (收尾阶段)
    ├── resetMobileCatalog()
    └── resetGT4Captcha()
```


## Components and Interfaces

### 1. Resource Cleanup Manager (资源清理管理器)

**职责**：统一管理所有资源的清理

**接口**：
```javascript
/**
 * 清理 PJAX 页面切换前的所有资源
 * @returns {void}
 */
function cleanupPjaxResources() {
	cleanupLazyloadObserver();
	cleanupZoomifyInstances();
	cleanupTippyInstances();
	cleanupMermaidInstances();
	cleanupDynamicStyles();
	cleanupDynamicScripts();
	cleanupEventListeners();
}

/**
 * 清理 Lazyload Observer
 * @returns {void}
 */
function cleanupLazyloadObserver() {
	if (lazyloadObserver) {
		lazyloadObserver.disconnect();
		lazyloadObserver = null;
	}
}

/**
 * 清理 Zoomify 实例
 * @returns {void}
 */
function cleanupZoomifyInstances() {
	if (zoomifyInstances && zoomifyInstances.length > 0) {
		zoomifyInstances.forEach(instance => {
			try {
				if (instance && typeof instance.destroy === 'function') {
					instance.destroy();
				}
			} catch(e) {
				ArgonDebug.warn('Failed to destroy Zoomify instance:', e);
			}
		});
		zoomifyInstances = [];
	}
	$('img.zoomify-initialized').removeClass('zoomify-initialized');
}
```

### 2. Script Executor (脚本执行器)

**职责**：提取并执行新页面中的内联脚本

**接口**：
```javascript
/**
 * 执行新页面中的内联脚本
 * @param {HTMLElement} container - 新页面的容器元素
 * @returns {void}
 */
function executeInlineScripts(container) {
	const scripts = container.querySelectorAll('script');
	scripts.forEach(script => {
		if (!script.src) { // 只执行内联脚本
			try {
				executeScript(script);
			} catch(e) {
				ArgonDebug.error('Script execution failed:', e);
			}
		}
	});
}

/**
 * 执行单个脚本
 * @param {HTMLScriptElement} script - 脚本元素
 * @returns {void}
 */
function executeScript(script) {
	const newScript = document.createElement('script');
	newScript.textContent = script.textContent;
	// 复制属性
	Array.from(script.attributes).forEach(attr => {
		newScript.setAttribute(attr.name, attr.value);
	});
	document.head.appendChild(newScript);
	document.head.removeChild(newScript);
}
```


### 3. Lazyload Manager (懒加载管理器)

**职责**：管理图片懒加载的完整生命周期

**接口**：
```javascript
/**
 * 初始化懒加载
 * @returns {void}
 */
function lazyloadInit() {
	// 清理旧的 Observer
	cleanupLazyloadObserver();
	
	// 检查是否启用
	if (argonConfig.lazyload === false) {
		loadAllImagesImmediately();
		return;
	}
	
	const images = document.querySelectorAll('img.lazyload[data-src]');
	if (images.length === 0) return;
	
	// 使用 IntersectionObserver 或降级方案
	if ('IntersectionObserver' in window) {
		initWithObserver(images);
	} else {
		initWithScrollListener(images);
	}
}

/**
 * 使用 IntersectionObserver 初始化
 * @param {NodeList} images - 图片元素列表
 * @returns {void}
 */
function initWithObserver(images) {
	const threshold = parseInt(argonConfig.lazyload_threshold) || 800;
	lazyloadObserver = new IntersectionObserver(entries => {
		entries.forEach(entry => {
			if (entry.isIntersecting) {
				loadImage(entry.target);
				lazyloadObserver.unobserve(entry.target);
			}
		});
	}, { rootMargin: `${threshold}px 0px` });
	
	images.forEach(img => lazyloadObserver.observe(img));
}

/**
 * 加载单张图片
 * @param {HTMLImageElement} img - 图片元素
 * @returns {void}
 */
function loadImage(img) {
	const src = img.getAttribute('data-src');
	if (!src) return;
	
	const tempImg = new Image();
	tempImg.onload = () => {
		img.src = src;
		img.removeAttribute('data-src');
		img.classList.remove('lazyload');
		applyLoadEffect(img);
	};
	tempImg.onerror = () => {
		// 降级：直接设置 src
		img.src = src;
		img.removeAttribute('data-src');
		img.classList.remove('lazyload');
	};
	tempImg.src = src;
}
```

### 4. Mermaid Renderer (Mermaid 渲染器)

**职责**：管理 Mermaid 图表的渲染和生命周期

**接口**：
```javascript
/**
 * 初始化 Mermaid
 * @returns {Promise<boolean>} 初始化是否成功
 */
async function initMermaid() {
	// 等待 Mermaid 库加载
	if (typeof window.mermaid === 'undefined') {
		await waitForMermaid();
	}
	
	// 检查 API 可用性
	if (!checkMermaidAPI()) {
		return false;
	}
	
	// 配置 Mermaid
	const theme = getMermaidTheme();
	window.mermaid.initialize({
		startOnLoad: false,
		theme: theme,
		securityLevel: 'loose'
	});
	
	return true;
}

/**
 * 等待 Mermaid 库加载
 * @param {number} timeout - 超时时间（毫秒）
 * @returns {Promise<void>}
 */
function waitForMermaid(timeout = 5000) {
	return new Promise((resolve, reject) => {
		const startTime = Date.now();
		const checkInterval = setInterval(() => {
			if (typeof window.mermaid !== 'undefined' && 
			    typeof window.mermaid.render === 'function') {
				clearInterval(checkInterval);
				resolve();
			} else if (Date.now() - startTime > timeout) {
				clearInterval(checkInterval);
				reject(new Error('Mermaid load timeout'));
			}
		}, 100);
	});
}

/**
 * 渲染所有 Mermaid 图表
 * @returns {void}
 */
function renderAllMermaidCharts() {
	const blocks = detectMermaidBlocks();
	blocks.forEach((block, index) => {
		renderMermaidChart(block, index);
	});
}

/**
 * 渲染单个图表
 * @param {HTMLElement} element - 代码块元素
 * @param {number} index - 图表索引
 * @returns {void}
 */
async function renderMermaidChart(element, index) {
	const chartId = `mermaid-chart-${Date.now()}-${index}`;
	const code = extractMermaidCode(element);
	
	try {
		const result = await window.mermaid.render(`mermaid-svg-${chartId}`, code);
		const container = createMermaidContainer(chartId, result.svg, code);
		element.parentNode.replaceChild(container, element);
	} catch(error) {
		// 尝试降级方案
		if (await tryLegacyMermaidAPI(element, code, chartId)) {
			return;
		}
		// 显示错误
		showMermaidError(element, error, code);
	}
}
```


### 5. Style Manager (样式管理器)

**职责**：管理动态样式的添加和清理

**接口**：
```javascript
/**
 * 清理动态添加的样式
 * @returns {void}
 */
function cleanupDynamicStyles() {
	// 只清理标记为动态的样式
	document.querySelectorAll('style[data-dynamic="true"]').forEach(style => {
		style.remove();
	});
}

/**
 * 应用新页面的样式
 * @param {HTMLElement} container - 新页面容器
 * @returns {void}
 */
function applyNewPageStyles(container) {
	const styles = container.querySelectorAll('style');
	styles.forEach(style => {
		const newStyle = style.cloneNode(true);
		newStyle.setAttribute('data-dynamic', 'true');
		document.head.appendChild(newStyle);
	});
}
```

### 6. Event Manager (事件管理器)

**职责**：管理事件监听器的绑定和清理

**接口**：
```javascript
/**
 * 事件监听器注册表
 */
const eventRegistry = new Map();

/**
 * 注册事件监听器
 * @param {HTMLElement} element - 目标元素
 * @param {string} event - 事件名称
 * @param {Function} handler - 处理函数
 * @param {Object} options - 选项
 * @returns {void}
 */
function registerEventListener(element, event, handler, options = {}) {
	element.addEventListener(event, handler, options);
	
	// 记录到注册表
	if (!eventRegistry.has(element)) {
		eventRegistry.set(element, []);
	}
	eventRegistry.get(element).push({ event, handler, options });
}

/**
 * 清理所有注册的事件监听器
 * @returns {void}
 */
function cleanupEventListeners() {
	eventRegistry.forEach((listeners, element) => {
		listeners.forEach(({ event, handler, options }) => {
			element.removeEventListener(event, handler, options);
		});
	});
	eventRegistry.clear();
}

/**
 * 使用事件委托绑定
 * @param {string} selector - 选择器
 * @param {string} event - 事件名称
 * @param {Function} handler - 处理函数
 * @returns {void}
 */
function delegateEvent(selector, event, handler) {
	$(document).on(event, selector, handler);
}
```

## Data Models

### ResourceState (资源状态)

```javascript
/**
 * 资源状态枚举
 */
const ResourceState = {
	UNINITIALIZED: 'uninitialized',  // 未初始化
	INITIALIZING: 'initializing',    // 初始化中
	READY: 'ready',                  // 就绪
	CLEANING: 'cleaning',            // 清理中
	CLEANED: 'cleaned'               // 已清理
};

/**
 * 资源管理器状态
 */
class ResourceManager {
	constructor() {
		this.state = ResourceState.UNINITIALIZED;
		this.resources = {
			lazyloadObserver: null,
			zoomifyInstances: [],
			tippyInstances: [],
			mermaidInstances: [],
			dynamicStyles: [],
			dynamicScripts: [],
			eventListeners: []
		};
	}
	
	/**
	 * 清理所有资源
	 */
	cleanup() {
		this.state = ResourceState.CLEANING;
		// 清理逻辑
		this.state = ResourceState.CLEANED;
	}
	
	/**
	 * 初始化资源
	 */
	initialize() {
		this.state = ResourceState.INITIALIZING;
		// 初始化逻辑
		this.state = ResourceState.READY;
	}
}
```

### LazyloadConfig (懒加载配置)

```javascript
/**
 * 懒加载配置
 */
class LazyloadConfig {
	constructor() {
		this.enabled = true;              // 是否启用
		this.effect = 'fadeIn';           // 加载效果
		this.threshold = 800;             // 提前加载阈值（像素）
		this.useObserver = true;          // 是否使用 Observer
	}
	
	/**
 * 从全局配置加载
	 */
	loadFromGlobal() {
		this.enabled = argonConfig.lazyload !== false;
		this.effect = argonConfig.lazyload_effect || 'fadeIn';
		this.threshold = parseInt(argonConfig.lazyload_threshold) || 800;
		this.useObserver = 'IntersectionObserver' in window;
	}
}
```

### MermaidState (Mermaid 状态)

```javascript
/**
 * Mermaid 渲染状态
 */
class MermaidState {
	constructor() {
		this.initialized = false;         // 是否已初始化
		this.libraryLoaded = false;       // 库是否加载
		this.theme = 'default';           // 当前主题
		this.renderedCharts = new Set();  // 已渲染的图表
	}
	
	/**
	 * 检查是否可以渲染
	 */
	canRender() {
		return this.initialized && this.libraryLoaded;
	}
	
	/**
	 * 标记图表已渲染
	 */
	markRendered(chartId) {
		this.renderedCharts.add(chartId);
	}
	
	/**
	 * 清理状态
	 */
	cleanup() {
		this.renderedCharts.clear();
	}
}
```


## Correctness Properties

### 什么是 Correctness Properties？

属性（Property）是一个关于系统行为的形式化陈述，它应该在所有有效执行中保持为真。属性是人类可读规范和机器可验证正确性保证之间的桥梁。通过属性测试，我们可以验证系统在各种输入下的行为是否符合预期。

### Property Reflection（属性反思）

在编写属性之前，我们需要识别并消除冗余的属性：

**识别的冗余项**：
1. 属性 8.3 (Mermaid 渲染失败显示错误) 与属性 3.5 重复
2. 属性 10.1 (IntersectionObserver 降级) 与属性 8.2 重复
3. 多个清理相关的属性可以合并为一个综合的资源清理属性

**合并策略**：
- 将所有资源清理验证合并为一个综合属性
- 将所有降级方案验证合并为一个兼容性属性
- 保留独立的功能性属性（如脚本执行、图片加载等）

### Core Properties（核心属性）

#### Property 1: 资源清理完整性
*For any* PJAX 页面切换，当触发 pjax:beforeReplace 事件时，所有旧页面资源（Observer、第三方库实例、动态标签）都应该被完全清理，且相关引用应该被置为 null 或清空。

**Validates: Requirements 1.1, 1.2, 1.3, 1.4, 2.3**

#### Property 2: Observer 生命周期管理
*For any* Lazyload 初始化操作，如果存在旧的 Observer 实例，则必须先调用 disconnect() 并置空引用，然后才能创建新的 Observer 实例。

**Validates: Requirements 2.1, 2.2**

#### Property 3: 图片加载后清理
*For any* 被 Lazyload 监听的图片，当图片加载完成（成功或失败）后，Observer 应该取消对该图片的监听，避免重复处理。

**Validates: Requirements 2.4, 2.5**

#### Property 4: 功能模块错误隔离
*For any* 功能模块初始化过程，如果某个模块抛出错误，该错误应该被捕获并记录，且不应该阻止其他模块的初始化。

**Validates: Requirements 1.6, 8.1**

#### Property 5: 脚本执行顺序
*For any* 新页面包含的内联脚本集合，这些脚本应该按照它们在 DOM 中出现的顺序依次执行。

**Validates: Requirements 4.3**

#### Property 6: 脚本错误隔离
*For any* 内联脚本执行过程，如果某个脚本抛出错误，该错误应该被捕获，且不应该阻止后续脚本的执行。

**Validates: Requirements 4.4**

#### Property 7: Mermaid 库加载等待
*For any* Mermaid 初始化操作，系统应该检查 mermaid.render 方法是否存在，如果不存在则等待库加载或使用降级方案。

**Validates: Requirements 3.1, 3.2**

#### Property 8: Mermaid 渲染降级
*For any* Mermaid 图表渲染失败的情况，系统应该尝试使用旧版 API（mermaidAPI.render 或 mermaid.init），如果所有方案都失败，则显示友好的错误提示并保留原始代码。

**Validates: Requirements 3.3, 3.5**

#### Property 9: 动态样式清理选择性
*For any* 页面切换操作，系统应该只清理标记为动态的 style 标签，保留主题核心样式。

**Validates: Requirements 5.1, 5.2**

#### Property 10: 事件监听器清理
*For any* 注册到 eventRegistry 的事件监听器，在页面切换前都应该被正确移除。

**Validates: Requirements 6.1**

#### Property 11: 节流函数限制频率
*For any* 使用节流函数包装的事件处理器，在指定时间窗口内，无论触发多少次事件，处理函数的实际执行次数都不应该超过 1 次。

**Validates: Requirements 7.1**

#### Property 12: IntersectionObserver 降级
*For any* 浏览器环境，如果不支持 IntersectionObserver，Lazyload 应该自动使用滚动监听降级方案。

**Validates: Requirements 8.2, 10.1**

#### Property 13: 第三方库缺失保护
*For any* 第三方库（Zoomify、Tippy、Mermaid），如果库未加载，系统应该提供空实现或跳过相关功能，不应该抛出未捕获的错误。

**Validates: Requirements 8.5**

#### Property 14: PJAX 加载失败降级
*For any* PJAX 加载失败的情况，系统应该回退到传统的页面跳转方式。

**Validates: Requirements 8.4**

#### Property 15: 懒加载禁用时立即加载
*For any* 图片元素，当懒加载功能被禁用时，所有图片应该立即加载，不应该创建 Observer 实例。

**Validates: Requirements 2.6**


## Error Handling

### 错误处理策略

1. **模块级错误隔离**
   - 每个功能模块的初始化都包裹在 try-catch 中
   - 错误不应该传播到其他模块
   - 记录详细的错误信息用于调试

2. **降级方案**
   - IntersectionObserver 不支持 → 滚动监听
   - Mermaid.render 失败 → 旧版 API → init 方法 → 显示错误
   - PJAX 失败 → 传统页面跳转
   - 第三方库缺失 → 空实现或跳过功能

3. **用户友好的错误提示**
   - Mermaid 渲染失败显示可折叠的错误信息
   - 保留原始代码供用户查看
   - 提供错误类型分类（语法错误、渲染错误等）

### 错误处理实现

```javascript
/**
 * 安全执行函数（带错误处理）
 * @param {Function} fn - 要执行的函数
 * @param {string} moduleName - 模块名称
 * @returns {boolean} 是否执行成功
 */
function safeExecute(fn, moduleName) {
	try {
		fn();
		ArgonDebug.log(`${moduleName} initialized successfully`);
		return true;
	} catch(error) {
		ArgonDebug.error(`${moduleName} initialization failed:`, error);
		return false;
	}
}

/**
 * PJAX complete 事件处理（带错误隔离）
 */
$(document).on('pjax:complete', function() {
	pjaxLoading = false;
	NProgress.inc();
	
	// 每个模块独立的错误处理
	safeExecute(() => waterflowInit(), 'Waterflow');
	safeExecute(() => lazyloadInit(), 'Lazyload');
	safeExecute(() => zoomifyInit(), 'Zoomify');
	safeExecute(() => highlightJsRender(), 'HighlightJS');
	safeExecute(() => panguInit(), 'Pangu');
	safeExecute(() => clampInit(), 'Clamp');
	safeExecute(() => tippyInit(), 'Tippy');
	safeExecute(() => renderAllMermaidCharts(), 'Mermaid');
	
	// 恢复滚动位置
	if (pjaxScrollTop > 0) {
		$('body,html').scrollTop(pjaxScrollTop);
		pjaxScrollTop = 0;
	}
	
	NProgress.done();
});
```

### 降级方案实现

```javascript
/**
 * Lazyload 降级方案
 */
function initWithScrollListener(images) {
	const loadedImages = new Set();
	
	function checkImagesInView() {
		const viewportHeight = window.innerHeight;
		const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
		
		images.forEach(img => {
			if (loadedImages.has(img)) return;
			
			const rect = img.getBoundingClientRect();
			const threshold = parseInt(argonConfig.lazyload_threshold) || 800;
			
			if (rect.top < viewportHeight + threshold && rect.bottom > -threshold) {
				loadImage(img);
				loadedImages.add(img);
			}
		});
	}
	
	// 使用节流优化性能
	const throttledCheck = argonEventManager ? 
		argonEventManager.throttle(checkImagesInView, 100) : 
		checkImagesInView;
	
	window.addEventListener('scroll', throttledCheck, {passive: true});
	window.addEventListener('resize', throttledCheck, {passive: true});
	
	checkImagesInView();
}

/**
 * Mermaid 降级方案
 */
async function tryLegacyMermaidAPI(element, code, chartId) {
	// 尝试 Mermaid 8.x API
	if (typeof window.mermaidAPI !== 'undefined' && 
	    typeof window.mermaidAPI.render === 'function') {
		try {
			window.mermaidAPI.render(`mermaid-svg-${chartId}`, code, (svgCode) => {
				const container = createMermaidContainer(chartId, svgCode, code);
				element.parentNode.replaceChild(container, element);
			});
			return true;
		} catch(e) {
			ArgonDebug.warn('Legacy API failed:', e);
		}
	}
	
	// 尝试 init 方法
	if (typeof window.mermaid.init === 'function') {
		try {
			const container = document.createElement('div');
			container.className = 'mermaid';
			container.textContent = code;
			element.parentNode.replaceChild(container, element);
			window.mermaid.init(undefined, container);
			return true;
		} catch(e) {
			ArgonDebug.warn('Init method failed:', e);
		}
	}
	
	return false;
}
```

## Testing Strategy

### 测试方法

本项目采用**双重测试策略**：单元测试 + 属性测试

1. **单元测试**：验证具体示例、边缘情况和错误条件
2. **属性测试**：验证通用属性在各种输入下的正确性

两种测试方法互补，共同保证代码质量：
- 单元测试捕获具体的 bug
- 属性测试验证通用的正确性

### 属性测试配置

**测试库选择**：使用 `fast-check` (JavaScript 的属性测试库)

**配置要求**：
- 每个属性测试至少运行 100 次迭代
- 每个测试必须引用对应的设计文档属性
- 标签格式：`Feature: pjax-lazyload-fix, Property N: [property_text]`

### 测试用例示例

#### 单元测试示例

```javascript
describe('Resource Cleanup', () => {
	it('should disconnect lazyload observer on cleanup', () => {
		// 创建 Observer
		lazyloadInit();
		expect(lazyloadObserver).not.toBeNull();
		
		// 清理
		cleanupLazyloadObserver();
		
		// 验证
		expect(lazyloadObserver).toBeNull();
	});
	
	it('should handle missing Mermaid library gracefully', () => {
		// 移除 Mermaid
		const originalMermaid = window.mermaid;
		delete window.mermaid;
		
		// 尝试渲染
		expect(() => renderAllMermaidCharts()).not.toThrow();
		
		// 恢复
		window.mermaid = originalMermaid;
	});
});
```

#### 属性测试示例

```javascript
const fc = require('fast-check');

describe('Property Tests', () => {
	/**
	 * Feature: pjax-lazyload-fix, Property 1: 资源清理完整性
	 * For any PJAX 页面切换，所有旧页面资源都应该被完全清理
	 */
	it('Property 1: Resource cleanup completeness', () => {
		fc.assert(
			fc.property(
				fc.array(fc.string()), // 随机页面内容
				(pageContent) => {
					// 创建资源
					lazyloadInit();
					createZoomifyInstances();
					createTippyInstances();
					
					// 记录资源状态
					const hasObserver = lazyloadObserver !== null;
					const hasZoomify = zoomifyInstances.length > 0;
					const hasTippy = document.querySelectorAll('[data-tippy-root]').length > 0;
					
					// 清理
					cleanupPjaxResources();
					
					// 验证所有资源都被清理
					expect(lazyloadObserver).toBeNull();
					expect(zoomifyInstances).toHaveLength(0);
					expect(document.querySelectorAll('[data-tippy-root]')).toHaveLength(0);
					
					return true;
				}
			),
			{ numRuns: 100 }
		);
	});
	
	/**
	 * Feature: pjax-lazyload-fix, Property 4: 功能模块错误隔离
	 * For any 功能模块初始化，某个模块错误不应该阻止其他模块
	 */
	it('Property 4: Module error isolation', () => {
		fc.assert(
			fc.property(
				fc.integer(0, 10), // 随机选择失败的模块索引
				(failIndex) => {
					const modules = [
						{ name: 'waterflow', fn: waterflowInit },
						{ name: 'lazyload', fn: lazyloadInit },
						{ name: 'zoomify', fn: zoomifyInit },
						{ name: 'highlight', fn: highlightJsRender }
					];
					
					// 让指定模块抛出错误
					const originalFn = modules[failIndex % modules.length].fn;
					modules[failIndex % modules.length].fn = () => {
						throw new Error('Test error');
					};
					
					// 初始化所有模块
					const results = modules.map(m => safeExecute(m.fn, m.name));
					
					// 恢复原函数
					modules[failIndex % modules.length].fn = originalFn;
					
					// 验证：失败的模块返回 false，其他模块不受影响
					const failedCount = results.filter(r => !r).length;
					expect(failedCount).toBe(1);
					
					return true;
				}
			),
			{ numRuns: 100 }
		);
	});
	
	/**
	 * Feature: pjax-lazyload-fix, Property 5: 脚本执行顺序
	 * For any 内联脚本集合，应该按 DOM 顺序执行
	 */
	it('Property 5: Script execution order', () => {
		fc.assert(
			fc.property(
				fc.array(fc.string(), 1, 10), // 随机脚本内容
				(scriptContents) => {
					// 创建测试容器
					const container = document.createElement('div');
					const executionOrder = [];
					
					// 创建脚本标签
					scriptContents.forEach((content, index) => {
						const script = document.createElement('script');
						script.textContent = `window.testOrder.push(${index});`;
						container.appendChild(script);
					});
					
					// 执行脚本
					window.testOrder = [];
					executeInlineScripts(container);
					
					// 验证执行顺序
					const expectedOrder = scriptContents.map((_, i) => i);
					expect(window.testOrder).toEqual(expectedOrder);
					
					return true;
				}
			),
			{ numRuns: 100 }
		);
	});
});
```

### 测试覆盖目标

- **单元测试覆盖率**：> 80%
- **属性测试**：覆盖所有 15 个核心属性
- **集成测试**：覆盖完整的 PJAX 生命周期
- **边缘情况**：Mermaid 缓存清除、浏览器兼容性

### 测试执行

```bash
# 运行所有测试
npm test

# 运行单元测试
npm run test:unit

# 运行属性测试
npm run test:property

# 生成覆盖率报告
npm run test:coverage
```

