如何在柏树中停止循环
How to stop loop in cypress
我有一个循环检查 40 个项目。
当我找到第一个 > 0 的元素时,我想停止循环
这是我的代码
var genArr = Array.from({ length: 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
});
});
});
但是我有一个语法错误。请帮助我,我怎样才能打破我的循环
break
仅适用于本机 for
/while
循环。
要提前退出 .each
循环(正如评论中所建议的那样),false
必须从您传递给它的同一回调中返回,因此返回 false
来自嵌套的 then
回调不会产生影响。
你甚至不能在 then
回调中设置一个标志并在 each
回调中检查它,因为 .each()
命令在深处只是一个 jQuery.fn.each
--- 它是同步的,当您设置标志时,所有迭代都将具有 运行(并将嵌套命令排入队列)。
因此,唯一的选择就是不使用.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
进行测试。
包含 40 个项目的列表本身会给出一个 'array-like' 主题,您可以将 filter()
和函数参数应用于该主题。
Cypress 仅使用 jquery,因此请参阅 here 了解语法(未在 Cypress .filter()
文档中给出)。
过滤器函数应该 return 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()
,只是一个静态 table.
<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 => console.log(x))
来帮忙测试
我有一个循环检查 40 个项目。 当我找到第一个 > 0 的元素时,我想停止循环 这是我的代码
var genArr = Array.from({ length: 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
});
});
});
但是我有一个语法错误。请帮助我,我怎样才能打破我的循环
break
仅适用于本机 for
/while
循环。
要提前退出 .each
循环(正如评论中所建议的那样),false
必须从您传递给它的同一回调中返回,因此返回 false
来自嵌套的 then
回调不会产生影响。
你甚至不能在 then
回调中设置一个标志并在 each
回调中检查它,因为 .each()
命令在深处只是一个 jQuery.fn.each
--- 它是同步的,当您设置标志时,所有迭代都将具有 运行(并将嵌套命令排入队列)。
因此,唯一的选择就是不使用.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
进行测试。
包含 40 个项目的列表本身会给出一个 'array-like' 主题,您可以将 filter()
和函数参数应用于该主题。
Cypress 仅使用 jquery,因此请参阅 here 了解语法(未在 Cypress .filter()
文档中给出)。
过滤器函数应该 return 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()
,只是一个静态 table.
<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 => console.log(x))
来帮忙测试