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

使用Cypress测试Material UI下拉框时偶发失效问题排查

问题描述

尝试了多种方法,但测试并不总是生效。测试代码能够找到元素并点击,但下拉输入框并非总能成功赋值。由于该下拉框为必填项,导致测试无法继续进行。下拉输入框不能为空。

自定义Cypress命令代码如下:

Cypress.Commands.add("selectTooltipDropdownOption", (optionText, inputSelector) => {
    cy.getCyInput(inputSelector,{timeout:10000}).click();

    cy.getCyRole("menu").children('li').contains(optionText).trigger("click", { timeout: 10000 });
  
    cy.getCyInput(inputSelector)
      .invoke("prop", "defaultValue")
      .should("contain", optionText);
  });

请问如何确保cy.getCyInput("dropdowninputField")始终有值,而非空状态?

该下拉框并非原生select类型,是基于Material UI样式封装的经典下拉组件,其HTML结构如下:

<div
  role="tooltip"
  class="jss390"
  style="
    position: absolute;
    top: 0px;
    left: 0px;
    transform: translate3d(317px, 412px, 0px);
    will-change: transform;
  "
  x-placement="bottom"
>
  <div class="MuiPaper-root MuiPaper-elevation8 MuiPaper-rounded">
    <ul
      class="MuiList-root jss389 MuiList-padding"
      role="menu"
      tabindex="-1"
      data-cy-action="select-action-root/insured/seller/0/data/address/country"
      style="min-width: 271px"
    >
      <li
        class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button"
        tabindex="0"
        role="menuitem"
        aria-disabled="false"
      >
        Austria<span class="MuiTouchRipple-root"></span>
      </li>
      <li
        class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button"
        tabindex="-1"
        role="menuitem"
        aria-disabled="false"
      >
        Belgium<span class="MuiTouchRipple-root"></span>
      </li>
      <!-- 其他选项省略 -->
    </ul>
  </div>
</div>
解决方案

针对Material UI的自定义下拉组件,调整Cypress命令的几个关键逻辑,就能确保选择操作稳定触发组件状态更新:

1. 用真实点击替代trigger("click")

trigger("click")仅触发DOM基础事件,但Material UI的MenuItem组件依赖React合成事件处理内部状态更新,直接使用.click()能更可靠地触发选择逻辑:

cy.getCyRole("menu").children('li').contains(optionText).click({ timeout: 10000 });

2. 验证正确的属性或文本内容

defaultValue是输入框的初始值,选择选项后组件通常更新的是value属性或输入框的显示文本,而非defaultValue。根据组件结构,选择以下一种方式验证:

// 验证输入框显示的文本
cy.getCyInput(inputSelector).should("have.text", optionText);
// 或者验证value属性(如果组件绑定了value)
cy.getCyInput(inputSelector).invoke("prop", "value").should("eq", optionText);

3. 等待下拉菜单完全渲染

点击输入框后,先确保下拉菜单(role="menu"的元素)完全显示再操作选项,避免因菜单未渲染完成导致的元素查找失败:

cy.getCyInput(inputSelector, {timeout:10000}).click();
// 等待菜单可见
cy.getCyRole("menu", {timeout:10000}).should("be.visible");
// 再查找并点击选项
cy.getCyRole("menu").children('li').contains(optionText).click({ timeout: 10000 });

4. 添加重试机制确保赋值成功

利用Cypress的自动重试特性,通过断言强制等待输入框状态更新完成:

cy.getCyInput(inputSelector)
  .should(($input) => {
    const value = $input.val() || $input.text();
    expect(value).to.contain(optionText);
  });

优化后的完整命令

Cypress.Commands.add("selectTooltipDropdownOption", (optionText, inputSelector) => {
  // 点击输入框打开下拉菜单,确保获得焦点
  cy.getCyInput(inputSelector, { timeout: 10000 })
    .click()
    .should("be.focused");

  // 等待菜单完全可见
  cy.getCyRole("menu", { timeout: 10000 }).should("be.visible");

  // 点击目标选项
  cy.getCyRole("menu")
    .find(`li:contains("${optionText}")`)
    .click({ timeout: 10000 });

  // 验证输入框已正确赋值
  cy.getCyInput(inputSelector)
    .should(($input) => {
      const displayValue = $input.text().trim() || $input.val();
      expect(displayValue).to.eq(optionText);
    });
});

额外建议

  • 如果选项是异步加载的,需先等待加载状态结束,比如添加.should("not.have.class", "loading")断言。
  • 用find('li')替代children('li'),能更灵活地查找嵌套的选项元素。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:12:22