[Day26] Vue3 E2E Testing: Cypress 实战之 Todo MVC (中)

前情提要

昨天,我们为了让大家更加了解 Cypress 的语法以及要如何攥写 E2E 测试,所以开始规划为 Vue.js • TodoMVC 攥写 E2E 测试,列出了我们要测试的内容,也实际地为了第一个 case 攥写测试程序。

https://i.imgur.com/w29OiBy.png

大家可以透过连结自由操作一下,会对於接下来的测试例子更佳有感!

详细完整的内容,就请大家直接查看昨天的文章吧!那我们就马上开始今天的内容!

变数宣告

在实际攥写 Case 2 之前,先带大家看一下程序码的样子以及额外宣告的变数

  • selectors - 由於我们会不断地需要透过 get() 取得目标元素并进行操作,因此将选择器 (selector) 用物件的方式储存下来,增加程序码的阅读性和维护性。
  • TODO_ITEM_ONE 和 TODO_ITEM_TWO - 待会 Case 2 测试中会使用的 Todo 变数。
describe('Todo MVC', () => {
  const selectors = {
    main: '.main',
    footer: '.footer',
    todoItems: '.todo-list .todo',
    newTodo: '.new-todo',
    lastOne: '.todo-list .todo:last-child'
  }

  const TODO_ITEM_ONE = 'Item 1'
  const TODO_ITEM_TWO = 'Item 2'

  beforeEach(() => {
    cy.visit('https://todomvc.com/examples/vue')
  })

  context('Case 1: Initial State', () => {
    it('Case 1-1: start with zero todo item', () => {
      cy.get(selectors.todoItems).should('have.length', 0)
    })

    it('Case 1-2: hide .main and .footer', () => {
      cy.get(selectors.main).should('not.be.visible')
      cy.get(selectors.footer).should('not.be.visible')
    })
  })

  context('Case 2: New Todo', () => {
    // ...
  })

  context('Case 3: Edit Todo', () => {
    // ...
  })
})

Case 2: 新增 Todo

  • Case 2-1 可以新增一至多个 Todo。
  • Case 2-2: Todo 新增完後,输入框的文字会被清除。
  • Case 2-3: Todo 会被新增至清单的最後一个。
  • Case 2-4: 当有 Todo 新增後,Todo 清单以及 Footer 将会显示。
context('Case 2: New Todo', () => {
  it('Case 2-1: create items', () => {

    // create first item
    cy.get(selectors.newTodo).type(`${TODO_ITEM_ONE}{enter}`)

    cy.get(selectors.todoItems)
      .eq(0)
      .find('label')
      .should('contain', TODO_ITEM_ONE)

    // create second item
    cy.get(selectors.newTodo).type(`${TODO_ITEM_TWO}{enter}`)

    cy.get(selectors.todoItems)
      .eq(1)
      .find('label')
      .should('contain', TODO_ITEM_TWO)

    cy.get(selectors.todoItems).should('have.length', 2)
  })

  it('Case 2-2: append new items to the bottom of the list', () => {
    const TODO_ITEM_LAST_ONE = 'Item Last One'

    for (let i = 0; i < 10; i++) {
      cy.get(selectors.newTodo).type(`Item ${i}{enter}`)
    }

    cy.get(selectors.newTodo).type(`${TODO_ITEM_LAST_ONE}{enter}`)

    cy.get(selectors.lastOne)
      .find('label')
      .should('contain', TODO_ITEM_LAST_ONE)
  })

  it('Case 2-3: clear input text after an item is added', () => {
    cy.get(selectors.newTodo).type(`${TODO_ITEM_ONE}{enter}`)
    cy.get(selectors.newTodo).should('have.text', '')
  })

  it('Case 2-4: show .main and .footer when items added', () => {
    cy.get(selectors.newTodo).type(`${TODO_ITEM_ONE}{enter}`)
    cy.get(selectors.main).should('be.visible')
    cy.get(selectors.footer).should('be.visible')
  })
})

语法说明:

  • type(): 输入文字至 DOM 中,不过传给 type() 的字串可能包含特殊字符,这些特殊字符是用来表示在输入文字期间发出的事件,例如此处的 {enter} 即表示按下 enter 键,又或者是 {selectall} 表示选取输入的文字。(更完整的列表可参考此文件)

  • eq(): 取得元素阵列中特定 index 的 DOM 元素,不过特别的是 index 可以为负值,表示从尾巴开始在元素阵列中查找元素。

    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
    </ul>
    
    cy.get('li').eq(0).should('contain', '1')
    cy.get('li').eq(1).should('contain', '2')
    cy.get('li').eq(-1).should('contain', '5')
    cy.get('li').eq(-2).should('contain', '4')
    
  • find(): 从特定选择器的元素後代开始寻找特定的元素。

    // success
    cy.get('.todo-list .todo:last-child').find('label')
    
    // error
    cy.find('.todo-list .todo:last-child label') 
    

Case 3: 编辑 Todo

  • Case 3-1: 对 Todo 连击两下後进入编辑模式,并在 blur 时完成编辑。
  • Case 3-2: 如果编辑後的 Todo 为空内容,则自动删除。
context('Case 3: Edit Todo', () => {
  it('Case 3-1: save edits on blur', () => {
    cy.get(selectors.newTodo).type(`${TODO_ITEM_ONE}{enter}`)

    cy.get(selectors.todoItems)
      .eq(0)
      .find('label')
      .should('contain', TODO_ITEM_ONE)
      .dblclick()

    cy.get(selectors.todoItems)
      .eq(0)
      .find('.edit')
      .type('{selectall}{backspace}') // same as .clear()
      .type(`${TODO_ITEM_TWO}`)
      .blur()

    cy.get(selectors.todoItems)
      .eq(0)
      .find('label')
      .should('contain', TODO_ITEM_TWO)
  })
  it('Case 3-2: remove the item if an empty text was entered', () => {
    cy.get(selectors.newTodo).type(`${TODO_ITEM_ONE}{enter}`)

    cy.get(selectors.todoItems)
      .eq(0)
      .find('label')
      .should('contain', TODO_ITEM_ONE)
      .dblclick()

    cy.get(selectors.todoItems)
      .eq(0)
      .find('.edit')
      .clear() // same as .type('{selectall}{backspace}')
      .blur()

    cy.get(selectors.todoItems).should('have.length', 0)
  })
})

语法说明:

  • type('{selectall}{backspace}')clear(): 在 Case2 的语法说明中有介绍到 type 可以传入表示事件的特殊字符,而如果我们想要清除输入框中的文字就可以使用'{selectall}{backspace}' 来达到选取後按下删除键,不过我们也可以直接使用 clear() 来达到同样的功能。

参考资料


今天的分享就到这边,如果大家对我分享的内容有兴趣欢迎点击追踪 & 订阅系列文章,如果对内容有任何疑问,或是文章内容有错误,都非常欢迎留言讨论或指教的!

我们明天见!


<<:  每日挑战,从Javascript面试题目了解一些你可能忽略的概念 - Day26

>>:  Vue.js指令(v-bind)绑定(DAY28)

轻松小单元 - 常见问题

假日就是要轻松 这个篇章整理了技服中心里的问答,包括专区的常见问题、开办研讨会的问答等,还有实作时碰...

[Day5] 第一章贴图

今日目标 载入图片,画出第一张图 stb_image.h 第三天-驱动OpenGL这篇有稍稍提到这个...

[Day 27]粗糙集特徵选择简介-5

上次说到了在特徵子集 Q 之下 被特徵子集 P 细分的程度 如果是 1 则表示 P 可以完全分类 Q...

[day-13] Python 内建的数值类函式

Python 内建的数值类函式 数值类函式 执行结果 功能 abs(-10) 10 取绝对值 min...

Day6 Html标签_1

Html的标签有许多种,我们不需要一开始就把所有的背下脑海里,而是可以透过反覆使用,将常用的标签用法...