/** * DOM Diff 算法单元测试 */ import { describe, test, expect, beforeEach, afterEach } from 'vitest'; import { morphNode, morphHTML, morphWithKeys } from './domDiff'; describe('DOM Diff Algorithm', () => { let container: HTMLElement; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { document.body.removeChild(container); }); describe('morphNode - 基础功能', () => { test('应该更新文本节点内容', () => { const fromNode = document.createTextNode('Hello'); const toNode = document.createTextNode('World'); container.appendChild(fromNode); morphNode(fromNode, toNode); expect(fromNode.nodeValue).toBe('World'); }); test('应该保持相同的文本节点不变', () => { const fromNode = document.createTextNode('Hello'); const toNode = document.createTextNode('Hello'); container.appendChild(fromNode); const originalNode = fromNode; morphNode(fromNode, toNode); expect(fromNode).toBe(originalNode); expect(fromNode.nodeValue).toBe('Hello'); }); test('应该替换不同类型的节点', () => { const fromNode = document.createElement('span'); fromNode.textContent = 'Hello'; const toNode = document.createElement('div'); toNode.textContent = 'World'; container.appendChild(fromNode); morphNode(fromNode, toNode); expect(container.firstChild?.nodeName).toBe('DIV'); expect(container.firstChild?.textContent).toBe('World'); }); }); describe('morphNode - 属性更新', () => { test('应该添加新属性', () => { const fromEl = document.createElement('div'); const toEl = document.createElement('div'); toEl.setAttribute('class', 'test'); toEl.setAttribute('id', 'myid'); container.appendChild(fromEl); morphNode(fromEl, toEl); expect(fromEl.getAttribute('class')).toBe('test'); expect(fromEl.getAttribute('id')).toBe('myid'); }); test('应该更新已存在的属性', () => { const fromEl = document.createElement('div'); fromEl.setAttribute('class', 'old'); const toEl = document.createElement('div'); toEl.setAttribute('class', 'new'); container.appendChild(fromEl); morphNode(fromEl, toEl); expect(fromEl.getAttribute('class')).toBe('new'); }); test('应该删除不存在的属性', () => { const fromEl = document.createElement('div'); fromEl.setAttribute('class', 'test'); fromEl.setAttribute('id', 'myid'); const toEl = document.createElement('div'); toEl.setAttribute('class', 'test'); container.appendChild(fromEl); morphNode(fromEl, toEl); expect(fromEl.getAttribute('class')).toBe('test'); expect(fromEl.hasAttribute('id')).toBe(false); }); }); describe('morphNode - 子节点更新', () => { test('应该添加新子节点', () => { const fromEl = document.createElement('ul'); fromEl.innerHTML = '
Old
'; const toEl = document.createElement('div'); toEl.innerHTML = 'New
'; container.appendChild(fromEl); const originalP = fromEl.querySelector('p'); morphNode(fromEl, toEl); // 应该保持同一个 p 元素,只更新内容 expect(fromEl.querySelector('p')).toBe(originalP); expect(fromEl.querySelector('p')?.textContent).toBe('New'); }); }); describe('morphHTML - HTML 字符串更新', () => { test('应该从 HTML 字符串更新元素', () => { const element = document.createElement('div'); element.innerHTML = 'Old
'; container.appendChild(element); morphHTML(element, 'New
'); expect(element.innerHTML).toBe('New
'); }); test('应该处理复杂的 HTML 结构', () => { const element = document.createElement('div'); element.innerHTML = 'Paragraph
'; container.appendChild(element); morphHTML(element, 'New Paragraph
Extra'); expect(element.children.length).toBe(3); expect(element.querySelector('h1')?.textContent).toBe('New Title'); expect(element.querySelector('p')?.textContent).toBe('New Paragraph'); expect(element.querySelector('span')?.textContent).toBe('Extra'); }); }); describe('morphWithKeys - 基于 key 的智能 diff', () => { test('应该保持相同 key 的节点', () => { const fromEl = document.createElement('ul'); fromEl.innerHTML = `