You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:25:32