前两天,我们开始为 Vue.js • TodoMVC 攥写 E2E 测试,并分别在
而今天我们就要把最後的 Case 4(改变 Todo 状态) 以及 Case 5(删除 Todo) 完成,并且还会介绍一个进阶的技巧 - 客制化命令 来优化我们的测试程序。
大家可以透过连结自由操作一下,会对於接下来的测试例子更佳有感!
由於今天的内容会使用到前两篇的程序码,所以建议还没看过前两篇的朋友先前往阅读,那我们就马上开始今天的内容!
我们先来回顾一下 Case 2 和 Case 3 的程序码,可以观察到其实有许多测试案例中都会需要新增 Todo 再接下去测试其他操作行为 (红色框的部分),不过又因为每个测试案例的情境有些许的不同,无法一口气使用 beforeEach 来新增 Todo 。
面对这样的情况 Cypress 提供了一个 API Cypress.Commands.add(name, callbackFn)
,我们可以用它来客制化一个 createTodo 的命令吧!
Cypress.Commands.add(name, callbackFn) 的使用方法很简单,name 就是命令的名称,callbackFn 则是要执行内容,通常就是那些我们不想要重复一直写的命令。
我们马上来看一下改动後的程序码吧!(是不是变得乾净许多,至少不用一直重复写一样的东西了)
const selectors = {
main: '.main',
footer: '.footer',
todoItems: '.todo-list .todo',
newTodo: '.new-todo',
lastOne: '.todo-list .todo:last-child'
}
Cypress.Commands.add('createTodo', (todo) => {
cy
.get(selectors.newTodo)
.type(`${todo}{enter}`)
cy.get(selectors.lastOne)
.find('label')
.contains(todo)
})
describe('Todo MVC', () => {
// ... case 1
context('Case 2: New Todo', () => {
it('Case 2-1: create items', () => {
// create first item
cy.createTodo(TODO_ITEM_ONE)
// create second item
cy.createTodo(TODO_ITEM_TWO)
cy.get(selectors.todoItems).should('have.length', 2)
})
})
// ... case 3 ~ 5
})
context('Case 4: Mark Todo As Completed', () => {
it('Case 4-1: mark items as completed one by one', () => {
cy.createTodo(TODO_ITEM_ONE)
cy.createTodo(TODO_ITEM_TWO)
cy.get(selectors.todoItems).eq(0).as('firstTodo')
cy.get(selectors.todoItems).eq(1).as('secondTodo')
cy.get('@firstTodo')
.should('not.have.class', 'completed')
.find('.toggle')
.check()
cy.get('@secondTodo')
.should('not.have.class', 'completed')
.find('.toggle')
.check()
cy.get('@firstTodo').should('have.class', 'completed')
cy.get('@secondTodo').should('have.class', 'completed')
})
it('Case 4-2: clear the complete state of item one by one', () => {
cy.createTodo(TODO_ITEM_ONE)
cy.createTodo(TODO_ITEM_TWO)
cy.get(selectors.todoItems).eq(0).as('firstTodo')
cy.get(selectors.todoItems).eq(1).as('secondTodo')
cy.get('@firstTodo')
.should('not.have.class', 'completed')
.find('.toggle')
.check()
cy.get('@secondTodo')
.should('not.have.class', 'completed')
.find('.toggle')
.check()
cy.get('@firstTodo')
.should('have.class', 'completed')
.find('.toggle')
.uncheck()
cy.get('@firstTodo').should('not.have.class', 'completed')
cy.get('@secondTodo').should('have.class', 'completed')
})
it('Case 4-3: mark all items as completed at once', () => {
const count = 10
for (let i = 0; i < count; i++) {
cy.createTodo(`Item ${i}`)
}
cy.get(selectors.toggleAll).check({ force: true })
cy.get(selectors.todoItems)
.filter('.completed')
.should('have.length', count)
})
it('Case 4-4: clear the complete state of all item at once', () => {
const count = 10
for (let i = 0; i < count; i++) {
cy.createTodo(`Item ${i}`)
}
cy.get(selectors.toggleAll).check({ force: true })
cy.get(selectors.toggleAll).uncheck({ force: true })
cy.get(selectors.todoItems)
.filter('.completed')
.should('have.length', 0)
})
})
语法说明:
check() & uncheck(): 选取或取消选取 checkbox 或者是 radio 的事件,而我们这里多传的 { force: true }
是因为 toggle all 的 checkbox 其实被隐藏起来了(opacity: 0) ,渲染在画面中的其实是 label icon,所以 check 或 uncheck 是无法正常发出事件的,因此在这边加上 { force: true }
便可以无视元素的可见度,直接对元素强制进行事件行为。
filter(): 筛选符合指定选择器的 DOM 元素。
context('Case 5: Delete Todo', () => {
it('Case 5-1: delete item one by one', () => {
cy.createTodo(TODO_ITEM_ONE)
cy.createTodo(TODO_ITEM_TWO)
cy.get(selectors.todoItems).eq(0).as('firstTodo')
cy.get(selectors.todoItems).eq(1).as('secondTodo')
cy.get('@firstTodo').find('.destroy').click({ force: true })
cy.get(selectors.todoItems)
.eq(0)
.find('label')
.should('contain', TODO_ITEM_TWO)
cy.get(selectors.todoItems).should('have.length', 1)
cy.get('@secondTodo').find('.destroy').click({ force: true })
cy.get(selectors.todoItems).should('have.length', 0)
})
it('Case 5-2: delete all completed items at once', () => {
const count = 10
for (let i = 0; i < count; i++) {
cy.createTodo(`Item ${i}`)
}
cy.get(selectors.toggleAll).check({ force: true })
cy.get(selectors.clearCompleted).click()
cy.get(selectors.todoItems).should('have.length', 0)
})
})
今天的分享就到这边,如果大家对我分享的内容有兴趣欢迎点击追踪 & 订阅系列文章,如果对内容有任何疑问,或是文章内容有错误,都非常欢迎留言讨论或指教的!
Vue3 E2E Testing 的主题在这边告一个段落了,明天我会分享前端部署网页的方式 (Vercel, Netlify & AWS S3),我们明天见!
>>: [Day28]程序菜鸟自学C++资料结构演算法 – 基数排序法(Radix sort)
JAVA是一个大家既熟悉又陌生的程序语言 稍微知道怎麽编写但会写错,也可能写还不知道怎麽储存,还有不...
参考资料1:MDN — the Mozilla Developer Network 参考资料2:0...
A.14 系统获取、开发及维护 A.14.1 资讯系统之安全要求事项 目标:确保资讯安全系跨越整个生...
我们用到的 API endpoint 只有一个,就是用来取得港铁机场快綫、东涌綫、屯马綫及将军澳綫最...
什麽是 Code splitting?为什麽要做 Code splitting? 如果你的网站是用 ...