【问题标题】:How to stop loop in cypress如何在柏树中停止循环
【发布时间】:2019-09-05 13:11:53
【问题描述】:

我有一个循环检查 40 个项目。 当我找到> 0 的第一个元素时,我想停止我的循环 这是我的代码

var genArr = Array.from({ 长度: 40 }, (v, k) => k + 1);

cy.wrap(genArr).each((index) => {
    cy.get('.list-item').eq(index - 1).find('.number')
        .invoke('text')
        .then(text => {
            const pendingCount = text;
            cy.get('.list-item').eq(index - 1).click();
            cy.get('.list-table').find('.list-table-list-item')
                .should('have.length', pendingCount);
            if (text > 0) break
         });
    });

});

但是我有一个语法错误。请帮助我,我怎样才能打破我的循环

【问题讨论】:

  • 我试过了,但是if(text > 0) return false;错误:CypressError: cy.then() 失败,因为您混淆了异步和同步代码。在您的回调函数中,您调用了 1 个或多个 cy 命令,但随后返回了一个同步值。赛普拉斯命令是异步的,将 cy 命令排队并返回同步值是没有意义的。您可能忘记使用另一个 cy.then() 正确链接 cy 命令。你同步返回的值是:'false'

标签: loops automated-tests break cypress


【解决方案1】:

break 仅适用于原生 for/while 循环。

要提前退出 .each 循环(如 cmets 中所建议的那样),false 必须从您传递给它的同一个回调中返回,因此从嵌套的 then 回调中返回 false 获胜'没有效果。

你甚至不能在then 回调中设置一个标志并在each 回调中检查它,因为.each() 命令在深处只是一个jQuery.fn.each --- 它是同步的,当你'd 设置标志,所有迭代都将运行(并将嵌套命令排入队列)。

因此,唯一的选择是不使用.each(),而使用某种递归命令。让我们建立一个。

function walk ( arr, cb, index = 0 ) {
    if ( !arr.length ) return;
    arr = arr.slice();
    const ret = cb(index, arr.shift());
    ((ret && ret.chainerId) ? ret : cy.wrap(ret))
        .then( ret => {
            if ( ret === false ) return;
            return walk(arr, cb, index + 1);
        });
}

/**
 * Your callback should return/yield `false` if the loop is to exit early.
 */
Cypress.Commands.add('eachSeries', { prevSubject: 'optional' }, (subject, arr, cb) => {

    return subject
        // assume `arr` to be `cb`
        ? walk(subject, arr)
        : walk(arr, cb);
});

用法:

describe('test', () => {
    it('test', () => {
        cy.document().then(doc => {
            doc.body.innerHTML = `
                <div class="list-item">0</div>
                <div class="list-item">1</div>
                <div class="list-item">2</div>
                <div class="list-item">3</div>
            `;
        });

        var genArr = Array.from({ length: 40 }, (v, k) => k + 1);

        // the command can be chained on an existing subject
        cy.wrap(genArr).eachSeries((index) => {
            return cy.get('.list-item').eq(index)
                .invoke('text')
                .then(text => {
                    if (text > 1) return false;
                });
        });

        // or it can start the chain (the array is passed as 1st arg)
        cy.eachSeries(genArr, (index) => {
            return cy.get('.list-item').eq(index)
                .invoke('text')
                .then(text => {
                    if (text > 1) return false;
                });
        });

        // you can also return a promise
        cy.eachSeries(['a', 'b', 'c'], (index, value) => {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(value === 'b' ? false : true);
                }, 500);
            });
        });

        // non-thenable callbacks work too
        cy.eachSeries(['a', 'b', 'c'], (index, value) => {
            return value === 'b' ? false : true;
        });
    });
});

在上面的前两个示例中,只有前 3 个项目被循环通过,然后循环提前退出。

【讨论】:

  • 这太棒了。非常感谢你。当genArr 中的条目用尽时,walk 中是否还有其他条件(如if (arr.length === 0) { return; })来终止迭代。我认为如果 false 从未从回调中返回,这将无限递归。
  • @HakanBaba 非常好的一点——至少无限递归会导致堆栈溢出,而不是像迭代无限循环那样锁定 CPU :)。已编辑。
  • 进一步编辑,以便您可以从回调中返回常规的 Promise 甚至是 non-thenables。
【解决方案2】:

我认为您不需要使用 genArr 进行测试。

40 个项目的列表本身会给出一个“类似数组”的主题,您可以将 filter() 与函数参数一起应用到该主题。
赛普拉斯只使用 jquery,因此请参阅 here 的语法(赛普拉斯 .filter() 文档中未提供)。

过滤器函数应该返回true 以包含该项目,所以我们再次使用jquery (Cypress.$) 来构造表达式。

既然你想在第一个非零项之后打破,我们可以链.eq(0)

it('clicks first item with content not "0"', () => {

  cy.get('.list-item')
    .filter((i, item) => Cypress.$(item).find('.number').text() !== '0')
    .eq(0)
    .click()          // previous line yields list-item so we can now click it
    .find('.number')  // then go further into DOM to get the actual number
    .invoke('text')
    .then(pendingCount => {
      cy.get('.list-table').find('.list-table-list-item')
       .should('have.length', pendingCount);   // Now test the number of table rows
    })

});

我用这个html片段测试了它,它没有onClick(),只是一个静态表。

<ul>
    <li class="list-item">
      <span class="title">Item1</span>
      <span class="number">0</span>
    </li>
    <li class="list-item">
      <span class="title">Item2</span>
      <span class="number">4</span>
    </li>
    <li class="list-item">
      <span class="title">Item3</span>
      <span class="number">0</span>
    </li>
    <li class="list-item">
      <span class="title">Item4</span>
      <span class="number">2</span>
    </li>
  </main>
</ul>
<div class="list-table">
  <div class="list-table-list-item">Pending1</div>
  <div class="list-table-list-item">Pending2</div>
  <div class="list-table-list-item">Pending3</div>
  <div class="list-table-list-item">Pending4</div>
</div>

使用 :not 和 :contains 的替代选择器

您还可以编写 jquery :not:contains 选择器来生成替代表达式。

例如,我的测试中的以下工作

cy.get('.list-item')
  .not(':contains("0")')
  .eq(0)
  .click()
  .find('.number')
  ...etc
cy.get('.list-item:not(:contains("0"))')
  .eq(0)
  .click()
  .find('.number')
  ...etc

请注意,Cypress .contains() 命令似乎不能以这种方式使用(以排除某些值),尽管这可能只是我缺乏想象力。

更复杂的 jquery 将更难调试,因此尽可能坚持使用链式 Cypress 命令(您可以将鼠标悬停在命令日志中的步骤上)。

我经常链接.then(x =&gt; console.log(x))来帮助测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2021-10-03
    • 2012-05-20
    • 1970-01-01
    • 2018-05-13
    • 1970-01-01
    相关资源
    最近更新 更多