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

使用Jest和jsdom测试Lit-HTML元素事件与unsafeCSS遇类未更新问题

Jest测试Lit-HTML元素点击状态切换失败

问题描述

尝试用Jest为Lit-HTML开关元素编写单元测试,验证点击后元素从active状态切换为disabled状态。测试中点击事件已触发,但元素类未按预期变化,断言失败。

组件代码(my-lit-html-toggle.ts)

// my-lit-html-toggle.ts
const TAG = 'my-lit-html-toggle';

@customElement(TAG)
export class MyLitHtmlToggle extends LitElement {
  @property({ type: Boolean })
  public active = false;

  @property({ type: Boolean })
  public disabled = false;

  public static styles = unsafeCSS(toggleSwitchStyles);

  public render(): TemplateResult {
    const classes = {
      active: this.active,
      disabled: this.disabled,
    };
    return html`
      <div class="toggle ${classMap(classes)}" @click=${this.toggleActive}>
        <div class="toggle-switch"></div>
        <div class="toggle-handle"></div>
      </div>
    `;
  }

  private toggleActive() {
    const onToggle = new CustomEvent<ToggleSwitchEvent>('onToggle', {
      detail: {
        active: (this.active = !this.active),
      },
    });
    this.dispatchEvent(onToggle);
  }
}

原测试代码(Jest)

// Test JEST
import { html, render } from 'lit'; 

describe('Given my-lit-html-toggle', () => {

    it('Should switch from active to disabled', async function () {
      const switchHandler = jest.fn();

      render(html`<my-lit-html-toggle active .click="${switchHandler}" ></my-lit-html-toggle>`, document.body);
      await Promise.resolve();
      document.body.querySelector(TAGNAME)?.click()
      await waitFor(() => expect(switchHandler).toBeCalledTimes(1));
    });
})

报错信息

expect(element).toHaveClass("disbled")

Expected the element to have class:
  disbled
Received:
  toggle-switch active

  38 |       );
  39 |       const cut = document.body.querySelector(TAGNAME)?.shadowRoot;
> 40 |       expect(cut?.childNodes.item(2)).toHaveClass('disbled');
     |                                       ^
  41 |     });
  42 |

问题分析与解决方案

1. 核心问题:组件逻辑未处理disabled属性

当前组件的toggleActive方法仅翻转active属性,完全未修改disabled值,这是状态不切换的根本原因。根据需求调整方法逻辑:

private toggleActive() {
  // 点击后将active设为false,disabled设为true
  this.active = false;
  this.disabled = true;
  
  const onToggle = new CustomEvent<ToggleSwitchEvent>('onToggle', {
    detail: {
      active: this.active,
      disabled: this.disabled
    },
  });
  this.dispatchEvent(onToggle);
}

如果需求是切换disabled状态(而非单向设置),可改为:

this.disabled = !this.disabled;
this.active = !this.disabled; // 确保active和disabled互斥

2. 测试代码的错误修正

  • 拼写错误:断言中的disbled应为disabled。
  • 错误绑定点击事件:直接给自定义元素绑定.click不会触发内部shadow DOM中div的点击事件,需获取shadow内的.toggle元素触发点击。
  • 等待Lit异步更新:Lit属性更新是异步的,需等待组件updateComplete完成后再断言。

修正后的测试代码:

import { html, render } from 'lit';
import { MyLitHtmlToggle } from './my-lit-html-toggle';

const TAG = 'my-lit-html-toggle';

describe('Given my-lit-html-toggle', () => {
  // 测试后清理DOM,避免污染
  afterEach(() => {
    document.body.innerHTML = '';
  });

  it('Should switch from active to disabled', async () => {
    // 渲染初始active为true的组件
    render(html`<${TAG} active></${TAG}>`, document.body);
    await Promise.resolve();

    const toggleElement = document.body.querySelector(TAG) as MyLitHtmlToggle;
    const toggleDiv = toggleElement.shadowRoot?.querySelector('.toggle');
    
    // 触发内部div的点击事件
    toggleDiv?.click();
    
    // 等待Lit完成更新
    await toggleElement.updateComplete;
    
    // 断言状态变化
    expect(toggleDiv).toHaveClass('disabled');
    expect(toggleDiv).not.toHaveClass('active');
  });
});

3. 额外优化

  • 组件类名修正:My-lit-htmToggle改为符合规范的驼峰命名MyLitHtmlToggle,避免语法问题。
  • 测试中使用组件实例而非直接操作DOM,更可靠。

内容的提问来源于stack exchange,提问作者rluisn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:45:36