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

Cypress自定义下拉选择命令运行时出现Promise冲突异常

环境信息

  • 操作系统:Windows 11
  • 软件版本:Cypress 13.9.0

问题背景

我在用自定义函数操作公司网站的自定义下拉输入框,这个网站稳定性很差:下拉选项只有点击输入框时才从后端加载,经常出现暂时无选项、过一秒又加载完成的情况;而且输入框还会根据其他输入框的选择自动填充内容。

之前我写了下面这个自定义命令,正常运行了好几个月:

Cypress.Commands.add(
  "selectOptionFromDropdown",
  (dropdownSelector, optionValue, config = {}) => {
    const { exactMatch = false, waitTime = 100 } = config;

    // 如果不是数字则转小写
    if (isNaN(optionValue)) {
      optionValue = optionValue.toLowerCase();
    }

    // 检查下拉框是否已包含目标选项
    cy.get(dropdownSelector).then((dropdownElement) => {
      if (dropdownElement.prop("readonly")) {
        cy.log("ELEMENT IS READONLY, SHOULD BE RETURNING");
      } else {
        // 确认下拉框非只读
        cy.get(dropdownSelector).should("not.have.attr", "readonly");

        // 获取下拉框元素并清空内容
        cy.get(dropdownSelector).clear();

        // 输入指定文本
        cy.get(dropdownSelector).type(optionValue);

        // 根据匹配规则选择mat-option
        cy.get(`mat-option`)
          .filter((index, el) => {
            const text = el.textContent.trim().toLowerCase();
            if (exactMatch) {
              return new RegExp(`^${optionValue}$`, "i").test(text);
            }
            return text.includes(optionValue);
          })
          .click();

        // 等待指定时长
        cy.wait(waitTime);
      }
    });
  }
);

调用方式如下:

cy.selectOptionFromDropdown(
  `[data-cy="input-productor"]`,
  "UNIDAD DE NEGOCIOS DE VENTA DIRECTA"
);
cy.selectOptionFromDropdown(
  `[data-cy="input-provincia"]`,
  persona.provincia
);
cy.selectOptionFromDropdown(
  `[data-cy="input-tipoPersona"]`,
  persona.tipoPersona
);
cy.selectOptionFromDropdown(
  `[data-cy="input-medioPago"]`,
  persona.medioPago
);
cy.selectOptionFromDropdown(
  `[data-cy="input-origenPago"]`,
  persona.origenPago
);
cy.selectOptionFromDropdown(
  `[data-cy="input-condicionIva"]`,
  persona.condicionIVA
);

异常情况

最近突然出现以下错误:

The following error originated from your test code, not from Cypress.

> Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.

The command that returned the promise was:

> cy.click()

The cy command you invoked inside the promise was:

> cy.task()

Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise.

Cypress will resolve your command with whatever the final Cypress command yields.

The reason this is an error instead of a warning is because Cypress internally queues commands serially whereas Promises execute as soon as they are invoked. Attempting to reconcile this would prevent Cypress from ever resolving.

When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.Learn more
cypress/support/commands.js:91:12
  89 |             return text.includes(optionValue);
  90 |           })
> 91 |           .click();
     |            ^
  92 | 
  93 |         // Wait for the specified time
  94 |         cy.wait(waitTime);

错误行号不固定,有时甚至出现在函数外部。现在不知道怎么解决,考虑过让开发团队在输入框从后端获取数据时设置为禁用状态,可行吗?

解决方案

1. 修复自定义命令的队列冲突问题

原命令在.then()回调里嵌套了多个cy.get(),打乱了Cypress的串行命令队列,加上网站不稳定,容易触发promise冲突。调整写法,去掉不必要的嵌套,改用链式逻辑:

Cypress.Commands.add(
  "selectOptionFromDropdown",
  (dropdownSelector, optionValue, config = {}) => {
    const { exactMatch = false, waitTime = 300 } = config; // 适当延长等待时间适配不稳定接口

    // 处理选项值
    const targetValue = isNaN(optionValue) ? optionValue.toLowerCase() : optionValue;

    // 链式执行操作,避免嵌套.then()
    cy.get(dropdownSelector)
      .should("not.have.attr", "readonly")
      .clear()
      .type(targetValue);

    // 等待下拉选项加载,设置更长超时时间
    cy.get(`mat-option`, { timeout: 5000 })
      .filter((index, el) => {
        const text = el.textContent.trim().toLowerCase();
        if (exactMatch) {
          return new RegExp(`^${targetValue}$`, "i").test(text);
        }
        return text.includes(targetValue);
      })
      .first() // 确保只选择第一个匹配项
      .click();

    cy.wait(waitTime);
  }
);

2. 适配网站不稳定的额外优化

  • 等待后端请求完成:如果知道下拉加载对应的API,用cy.intercept()监听并等待请求结束再操作:
    // 在测试用例开头添加拦截
    cy.intercept('GET', '/api/**/dropdown-options').as('loadDropdown');
    
    // 在输入文本后等待请求完成
    cy.get(dropdownSelector).type(targetValue);
    cy.wait('@loadDropdown');
    
  • 增加重试机制:用should()强制Cypress重试直到选项出现:
    cy.get(`mat-option`, { timeout: 5000 })
      .should('exist')
      .filter((index, el) => {
        // 匹配逻辑不变
      })
      .click();
    

3. 和开发团队协作的建议

让开发在下拉框加载数据时设置为禁用状态是非常可行的方案,这能让Cypress自动识别元素不可操作,避免在加载过程中执行无效操作,从根源减少不稳定因素。另外还可以建议:

  • 给下拉选项添加和输入框关联的专属选择器,避免用全局mat-option导致匹配错误
  • 在下拉加载完成后添加自定义状态标识或事件,方便测试脚本监听

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:40:56