これだけはおさえておきたいCypressコマンド集

# はじめに
[【初心者向けワークショップ】Cloud9＋Docker Compose＋CypressでE2Eテストを書いてみよう！](https://qiita.com/oh_rusty_nail/items/a18a72a0ec7d09be9a10)の記事でCypressを使ったE2Eのテストを書くワークショップの記事を書きました。
この記事では、こういう検証をしたい場合Cypressではどう書くのかを記していきます。
コマンド集は[公式ドキュメント](https://docs.cypress.io/api/commands/clear.html#Command-Log)にもあります。
本記事ではその中からよく使いそうな**これだけはおさえておきたい**ものをピックアップして、要素の操作と検証など実践に使いやすい形のサンプルを用意してみました。
また、CypressのテストコードだけでなくHTMLも合わせて載せておりますので参考にしていただけたらと思います。

# ページ遷移
以下、前提として baseUrlが`cypress.json`に定義されているものとします。

```javascript:e2e/cypress.json
{
  "baseUrl": "http://localhost:8080",
  "reporter": "junit",
  "reporterOptions": {
    "mochaFile": "cypress/results/output.xml"
  }
}
```

```javascript:cypress/integration/spec.js
it('open page', () => {
  // ページ遷移(パスを指定)
  cy.visit('/')

  // Qiitaのページを開く(URLを指定)
  cy.visit('https://qiita.com')
}
```

# 要素の取得、要素の値を取得
## 要素の取得
`cy.get('[セレクタ]')`で要素を取得できます。

```html
<div id='header'></div>
```

```javascript
it('Get element', () => {
  cy.visit('/')

  // 要素の取得
  cy.get('#header')
})
```

## 親・子要素の取得

```html
<ul class='parent'>
  <li class='active child'>list1</li>
  <li class='child'>list2</li>
  <li class='child'>list3</li>
</ul>
```

```javascript
it('Get child and parent element', () => {
  cy.visit('/')

  // 子要素の取得
  cy.get('ul').children('.active').should('have.text', 'list1')
  cy.get('ul').children().should('have.length', 3)
  cy.task('log', '== 子要素の取得 ==')
  cy.get('ul').children().each(($li, index, $list) => {
    cy.task('log', $li.text())
  })

  // 親要素の取得
  cy.get('.child').parent().should('have.class', 'parent')
```

※ ログの出力方法は、後述の **ログの出力** をご覧ください。

```:コンソールログ(抜粋)
apache | 172.20.0.3 - - [13/Sep/2020:05:05:16 +0000] "GET / HTTP/1.1" 200 254
cypress | == 子要素の取得 ==
cypress | list1
cypress | list2
cypress | list3
cypress | 
cypress |   (Results)
cypress | 
```

## ページタイトルの取得
`cy.title()`でページタイトルを取得できます。
**要素の取得**でも説明した通り、`cy.get('title')`でも取得できます。

```html
<title>sample page title</title>
```

```javascript
it('Verify title', () => {
  cy.visit('/')

  // ページタイトルを取得して検証する
  cy.title().should('eq', 'sample page title')
  cy.get('title').should('have.text', 'sample page title')
})
```

# 要素の操作
## クリック、チェック操作
チェックボックスにチェックを入れたり、ラジオボタンを選択したりボタンをクリックすることができます。

```html
<form>
  <div>
    <input type='checkbox' name='check' value='check_1' checked='checked'>
    <input type='checkbox' name='check' value='check_2'>
  </div>
  <div>
    <input type='radio' name='feedback' value='good' checked='checked'>良い
    <input type='radio' name='feedback' value='bad'>悪い
  </div>
</form>
<button type=button onclick='alert("clicked button")'>Click here</button> 
```

```javascript
it('Click or check an element', () => {
  cy.visit('/')

  // 要素をチェックする
  cy.get('[value="check_1"]').check()       // チェックボックス
  cy.get('[type="radio"]').first().check()  // ラジオボタンの１番目

  // 要素をクリックする
  cy.get('[type="button"]').click()
})
```

## 入力操作
テキストボックスに文字の入力することができます。
また、入力されたテキスト内の文字を削除することもできます。

```html
<textarea>hoge</textarea>
```

```javascript
it('Delete text and type text', () => {
  cy.visit('/')

  // テキストエリアの文字列を検証(消去前)
  cy.get('textarea').should('have.value', 'hoge')

  // テキストエリアの文字を消去
  cy.get('textarea').clear()
  // テキストエリアの文字列を検証(消去後)
  cy.get('textarea').should('have.value', '')

  // テキストエリアに文字を入力
  cy.get('textarea').type('Hello, World')
  // テキストエリアの文字列を検証(入力後)
  cy.get('textarea').should('have.value', 'Hello, World')
})
```

# 検証
## 要素の値の検証
`cy.get('[セレクタ]').should('have.id')`などhave.xxxで指定します。
また、`cy.get('[セレクタ]').should('have.attr', '[属性名]', '[値]')`でも各属性の値は取得できます。

```html
<div class='title'>
  <div id='main_title'>Content title</div>
  <div class='sub_title'>Sub title</div>
</div>
<input type='number' name='numberbox' value='1'>
```

```javascript
it('Verify element values and attributes', () => {
  cy.visit('/')

  // 要素のテキストを検証する
  cy.get('#main_title').should('have.text', 'Content title')

  // 要素のid属性の値を検証する
  cy.get('.title div:first-child').should('have.id', 'main_title')

  // 要素のclass属性の値を検証する
  cy.get('.title div:last-child').should('have.class', 'sub_title')

  // 要素のvalue属性の値を検証する
  cy.get('[name="numberbox"]').should('have.value', '1')

  // 要素のtype属性の値を検証する
  cy.get('[name="numberbox"]').should('have.attr', 'type', 'number')

  // 要素のname属性の値を検証する
  cy.get('[type="number"]').should('have.attr', 'name', 'numberbox')
})
```

## テキストの部分一致、完全一致の検証

```html
<title>sample page title</title>
<div class='title'>
  <div id='main_title'>Content title</div>
  <div class='sub_title'>Sub title</div>
</div>
```

```javascript
it('Verify title', () => {
  cy.visit('/')

  // ページタイトルを取得して検証する
  // 完全一致
  cy.title().should('eq', 'sample page title')
  cy.get('title').should('have.text', 'sample page title')
  // 部分一致
  cy.title().should('include', 'sample')
  cy.get('title').contains('sample')

  // ページ内のどこかに文字列が存在することを検証する
  cy.contains('Sub')
})
```

## 要素が表示されている・いないことの検証

```html
<div class='title'>
  <div id='main_title'>Content title</div>
  <input type='hidden' name='userid' value='12345'>
</div>
```

```javascript
it('Verify element display', () => {
  cy.visit('/')

  // 要素が表示されていることを検証する
  cy.get('#main_title').should('be.visible')

  // 要素が表示されていないことを検証する
  cy.get('.title > input').should('not.be.visible')
})
```

# ロケーションの取得

```javascript
it('Get location', () => {
  cy.visit('https://qiita.com')

  // ロケーションの取得
  cy.location().should((loc) => {
    expect(loc.href).to.eq('https://qiita.com/')
    expect(loc.hostname).to.eq('qiita.com')
    expect(loc.pathname).to.eq('/')
    expect(loc.port).to.eq('')
    expect(loc.protocol).to.eq('https:')
  })
})
```

現在のページのURLの取得は`cy.url()`でも可能です。

```javascript
it('Get current url', () => {
  cy.visit('https://qiita.com')

  // URLの取得その１
  cy.location().should((loc) => {
    expect(loc.href).to.eq('https://qiita.com/')
  })

  // URLの取得その２
  cy.url().should('eq', 'https://qiita.com/')
})
```

# ログの出力
`cypress/plugins/index.js `を以下のように編集します。

```javascript:cypress/plugins/index.js 
module.exports = (on, config) => {
  on('task', {
    log (message) {
      console.log(message)
      return null
    }
  })
}
```

```javascript:cypress/integration/spec.js
it('Log', () => {
  cy.visit('/')

  // ログの出力
  cy.task('log', "*** ログ開始 ***")
  cy.task('log', "hoge")
  cy.task('log', "fuga")
  cy.task('log', "*** ログ終了 ***")
})
```

```:Cypressのコンソールログ(抜粋)
cypress |   Running:  spec.js                                                                         (1 of 1)
apache | 172.20.0.3 - - [13/Sep/2020:02:41:17 +0000] "GET / HTTP/1.1" 200 152
cypress | *** ログ開始 ***
cypress | hoge
cypress | fuga
cypress | *** ログ終了 ***
cypress | 
cypress |   (Results)
cypress | 
```