如何对document.execCommand进行单元测试?jsdom环境下遇阻求助
解决jsdom不支持document.execCommand的富文本测试问题
嘿,我完全懂你现在的困扰——想给类文字处理器的功能(比如插入有序列表)写单元测试,结果发现jsdom压根不支持document.execCommand,这确实让人头疼。不过别担心,这里有几个实用的思路帮你搞定测试:
1. 手动模拟execCommand的行为
既然jsdom不支持这个方法,我们可以在测试里给它打个补丁,模拟它实际会产生的DOM变化。这样既不用改业务代码,又能验证功能是否符合预期。
举个Jest测试的例子:
test('点击按钮后将文本转换为有序列表', () => { // 初始化测试用的DOM结构 document.body.innerHTML = ` <div contenteditable="true">Foo</div> <button id="run">Select "Foo" then Click</button> `; // 引入你的业务脚本(就是绑定点击事件的那段代码) require('./your-script-file.js'); // 模拟用户选中文本的操作 const editableElement = document.querySelector('[contenteditable="true"]'); const range = document.createRange(); range.selectNodeContents(editableElement); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); // 给document.execCommand打补丁,模拟insertorderedlist的效果 document.execCommand = jest.fn((command) => { if (command === 'insertorderedlist') { // 手动创建有序列表结构并替换原有内容 const ol = document.createElement('ol'); const li = document.createElement('li'); li.textContent = editableElement.textContent; ol.appendChild(li); editableElement.innerHTML = ''; editableElement.appendChild(ol); return true; } return false; }); // 触发按钮点击 document.getElementById('run').click(); // 验证结果是否符合预期 expect(editableElement.querySelector('ol')).not.toBeNull(); expect(editableElement.querySelector('ol li').textContent).toBe('Foo'); expect(document.execCommand).toHaveBeenCalledWith('insertorderedlist'); });
2. 改用现代富文本API替代execCommand
其实document.execCommand已经被MDN标记为废弃API了,官方推荐用Selection、Range这类更现代的DOM API来实现富文本操作。如果你的项目允许重构代码,换成这些API后,不仅功能更可控,在jsdom里也能直接测试,不需要任何模拟。
比如把原来的点击事件代码改成这样:
document.getElementById('run').addEventListener('click', function() { const editableElement = document.querySelector('[contenteditable="true"]'); const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); // 创建有序列表和列表项 const ol = document.createElement('ol'); const li = document.createElement('li'); // 把选中的内容移到列表项里 li.appendChild(range.extractContents()); ol.appendChild(li); // 把有序列表插入到原来的位置 range.insertNode(ol); // 调整选区到列表项内,保持编辑状态 selection.removeAllRanges(); const newRange = document.createRange(); newRange.selectNodeContents(li); selection.addRange(newRange); } });
这时候测试就简单多了,直接触发点击事件,然后检查DOM结构就行,jsdom完全支持这些操作。
3. 使用真实浏览器环境测试
如果你的测试需要完全模拟真实用户的操作(比如选中文本、点击按钮的真实交互),那可以用Cypress、Playwright或者Jest + Puppeteer这类工具,它们会启动真实的浏览器,天然支持document.execCommand。
举个Playwright的测试例子:
test('点击按钮后插入有序列表', async ({ page }) => { await page.setContent(` <div contenteditable="true">Foo</div> <button id="run">Select "Foo" then Click</button> <script> document.getElementById('run').addEventListener('click', function() { document.execCommand("insertorderedlist"); }); </script> `); // 模拟用户三次点击选中文本 const editableElement = page.locator('[contenteditable="true"]'); await editableElement.click({ clickCount: 3 }); // 点击按钮 await page.click('#run'); // 验证有序列表是否正确生成 await expect(editableElement.locator('ol li')).toHaveText('Foo'); });
你可以根据项目的实际情况选择最适合的方案:如果不想改动现有代码,就用模拟补丁;如果想优化技术栈,就换成现代API;如果需要端到端的真实场景测试,就用浏览器自动化工具。
内容的提问来源于stack exchange,提问作者rebelliard
相关产品推荐
相关产品推荐

