Day 5: ページ遷移とフォーム操作
今日学ぶこと
- cy.visit() の詳細オプション(baseUrl, timeout, headers)
- cy.url(), cy.location() でURL検証
- cy.go(), cy.reload() でブラウザナビゲーション
- フォーム要素の操作(input, select, checkbox, radio, textarea)
- ファイルアップロード(cy.selectFile())
- フォーム送信とバリデーションテスト
ページ遷移の基本
cy.visit() の詳細オプション
cy.visit() は単にURLを開くだけでなく、さまざまなオプションを指定できます。
// 基本的な使い方
cy.visit('https://example.com')
// 相対パス(baseUrlが設定されている場合)
cy.visit('/login')
// オプション付き
cy.visit('/dashboard', {
timeout: 30000, // タイムアウト(ms)
failOnStatusCode: false, // ステータスコードでの失敗を無効化
headers: {
'Accept-Language': 'ja'
},
auth: {
username: 'admin',
password: 'secret'
},
qs: {
page: 1,
sort: 'name'
}
})
baseUrl の設定
cypress.config.js で baseUrl を設定すると、毎回フルURLを書く必要がなくなります。
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
}
})
// baseUrl が設定されていれば相対パスで OK
cy.visit('/') // http://localhost:3000/
cy.visit('/about') // http://localhost:3000/about
cy.visit('/users/1') // http://localhost:3000/users/1
flowchart LR
Config["cypress.config.js\nbaseUrl設定"] --> Visit["cy.visit('/path')"]
Visit --> Full["http://localhost:3000/path"]
style Config fill:#3b82f6,color:#fff
style Visit fill:#8b5cf6,color:#fff
style Full fill:#22c55e,color:#fff
URL検証
cy.url() でURLを確認
// 現在のURLを検証
cy.visit('/login')
cy.url().should('include', '/login')
cy.url().should('eq', 'http://localhost:3000/login')
cy.url().should('match', /\/login$/)
cy.location() で詳細なURL情報を取得
// URL: http://localhost:3000/search?q=cypress#results
cy.location().should((loc) => {
expect(loc.protocol).to.eq('http:')
expect(loc.hostname).to.eq('localhost')
expect(loc.port).to.eq('3000')
expect(loc.pathname).to.eq('/search')
expect(loc.search).to.eq('?q=cypress')
expect(loc.hash).to.eq('#results')
})
// 特定のプロパティだけを検証
cy.location('pathname').should('eq', '/search')
cy.location('search').should('include', 'q=cypress')
| コマンド | 戻り値 | 用途 |
|---|---|---|
cy.url() |
フルURL文字列 | URLの簡単な検証 |
cy.location() |
Locationオブジェクト | URLの詳細な検証 |
cy.location('pathname') |
パス文字列 | パスのみの検証 |
cy.location('search') |
クエリ文字列 | クエリパラメータの検証 |
cy.location('hash') |
ハッシュ文字列 | ハッシュフラグメントの検証 |
ブラウザナビゲーション
cy.go() で履歴を移動
// ページを遷移してから戻る
cy.visit('/page-1')
cy.visit('/page-2')
cy.visit('/page-3')
// 戻る
cy.go('back')
cy.url().should('include', '/page-2')
// さらに戻る
cy.go(-1)
cy.url().should('include', '/page-1')
// 進む
cy.go('forward')
cy.url().should('include', '/page-2')
// 2つ進む
cy.go(2)
cy.url().should('include', '/page-3') // ※ブラウザ履歴に依存
cy.reload() でページを再読み込み
// 通常のリロード
cy.reload()
// キャッシュを無視したリロード(ハードリロード)
cy.reload(true)
flowchart LR
Back["cy.go('back')\ncy.go(-1)"] --> Current["現在のページ"]
Current --> Forward["cy.go('forward')\ncy.go(1)"]
Current --> Reload["cy.reload()"]
Reload --> Current
style Back fill:#f59e0b,color:#fff
style Current fill:#3b82f6,color:#fff
style Forward fill:#22c55e,color:#fff
style Reload fill:#8b5cf6,color:#fff
フォーム要素の操作
テキスト入力(input, textarea)
// テキスト入力
cy.get('#username').type('testuser')
cy.get('#password').type('secret123')
// テキストエリア
cy.get('textarea#comment').type('これはコメントです。\n改行も入力できます。')
// 特殊キーの入力
cy.get('#search').type('Cypress{enter}') // Enterキー
cy.get('#email').type('{selectall}{backspace}') // 全選択して削除
cy.get('#field').type('{ctrl+a}') // Ctrl+A
// 入力をクリアしてから入力
cy.get('#name').clear().type('新しい名前')
// 遅延を入れてタイプ(1文字あたり100ms)
cy.get('#slow-input').type('ゆっくり入力', { delay: 100 })
type() で使える特殊キー
| キー | 記法 | 説明 |
|---|---|---|
| Enter | {enter} |
Enterキー |
| Tab | {tab} |
Tabキー |
| Escape | {esc} |
Escapeキー |
| Backspace | {backspace} |
Backspaceキー |
| Delete | {del} |
Deleteキー |
| 上矢印 | {uparrow} |
上方向キー |
| 下矢印 | {downarrow} |
下方向キー |
| 左矢印 | {leftarrow} |
左方向キー |
| 右矢印 | {rightarrow} |
右方向キー |
| 全選択 | {selectall} |
全テキスト選択 |
セレクトボックス(select)
// テキストで選択
cy.get('#country').select('Japan')
// value属性で選択
cy.get('#country').select('jp')
// インデックスで選択
cy.get('#country').select(2)
// 複数選択(multiple属性のselect)
cy.get('#skills').select(['JavaScript', 'TypeScript', 'Python'])
// 選択されている値を検証
cy.get('#country').should('have.value', 'jp')
// HTML例
// <select id="country">
// <option value="">選択してください</option>
// <option value="jp">Japan</option>
// <option value="us">United States</option>
// <option value="uk">United Kingdom</option>
// </select>
cy.get('#country').select('Japan')
cy.get('#country').should('have.value', 'jp')
チェックボックスとラジオボタン
// チェックボックスをオンにする
cy.get('#agree').check()
cy.get('#agree').should('be.checked')
// チェックボックスをオフにする
cy.get('#agree').uncheck()
cy.get('#agree').should('not.be.checked')
// 値を指定してチェック(同じname属性の複数チェックボックス)
cy.get('input[name="hobbies"]').check(['reading', 'coding'])
// ラジオボタンを選択
cy.get('input[name="gender"]').check('male')
cy.get('input[name="gender"]').should('have.value', 'male')
// force オプション(非表示要素のチェック)
cy.get('#hidden-checkbox').check({ force: true })
flowchart TB
subgraph CheckboxOps["チェックボックス操作"]
Check["cy.check()"] --> Checked["checked状態"]
Uncheck["cy.uncheck()"] --> Unchecked["unchecked状態"]
end
subgraph RadioOps["ラジオボタン操作"]
Radio["cy.check('value')"] --> Selected["選択状態"]
end
subgraph Verify["検証"]
V1["should('be.checked')"]
V2["should('not.be.checked')"]
V3["should('have.value', 'x')"]
end
style CheckboxOps fill:#3b82f6,color:#fff
style RadioOps fill:#8b5cf6,color:#fff
style Verify fill:#22c55e,color:#fff
ファイルアップロード
cy.selectFile() の基本
// ファイルパスを指定してアップロード
cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png')
// 複数ファイルのアップロード
cy.get('input[type="file"]').selectFile([
'cypress/fixtures/file1.pdf',
'cypress/fixtures/file2.pdf'
])
// ドラッグ&ドロップでアップロード
cy.get('.dropzone').selectFile('cypress/fixtures/data.csv', {
action: 'drag-drop'
})
// Blobオブジェクトからアップロード
cy.get('input[type="file"]').selectFile({
contents: Cypress.Buffer.from('file content'),
fileName: 'test.txt',
mimeType: 'text/plain',
lastModified: Date.now()
})
fixture ファイルの準備
# テスト用のファイルを配置
cypress/
fixtures/
image.png # テスト用画像
data.csv # テスト用CSV
document.pdf # テスト用PDF
sample.json # テスト用JSON
フォーム送信とバリデーションテスト
基本的なフォーム送信テスト
describe('ログインフォーム', () => {
beforeEach(() => {
cy.visit('/login')
})
it('正常にログインできる', () => {
cy.get('#email').type('user@example.com')
cy.get('#password').type('password123')
cy.get('button[type="submit"]').click()
cy.url().should('include', '/dashboard')
cy.get('.welcome-message').should('contain', 'ようこそ')
})
it('メールアドレスが空の場合エラーが表示される', () => {
cy.get('#password').type('password123')
cy.get('button[type="submit"]').click()
cy.get('#email-error')
.should('be.visible')
.and('contain', 'メールアドレスを入力してください')
})
it('無効なメールアドレスでエラーが表示される', () => {
cy.get('#email').type('invalid-email')
cy.get('#password').type('password123')
cy.get('button[type="submit"]').click()
cy.get('#email-error')
.should('be.visible')
.and('contain', '有効なメールアドレスを入力してください')
})
})
会員登録フォームの総合テスト
describe('会員登録フォーム', () => {
beforeEach(() => {
cy.visit('/register')
})
it('すべてのフィールドを入力して送信できる', () => {
// テキスト入力
cy.get('#name').type('山田太郎')
cy.get('#email').type('yamada@example.com')
cy.get('#password').type('SecurePass123!')
cy.get('#password-confirm').type('SecurePass123!')
// セレクトボックス
cy.get('#prefecture').select('東京都')
// ラジオボタン
cy.get('input[name="gender"][value="male"]').check()
// チェックボックス
cy.get('input[name="interests"]').check(['technology', 'music'])
// テキストエリア
cy.get('#bio').type('よろしくお願いします。')
// ファイルアップロード
cy.get('#avatar').selectFile('cypress/fixtures/avatar.png')
// 利用規約に同意
cy.get('#terms').check()
// フォーム送信
cy.get('form').submit()
// 成功画面の確認
cy.url().should('include', '/welcome')
cy.get('.success-message').should('contain', '登録が完了しました')
})
it('パスワードが一致しない場合エラーが表示される', () => {
cy.get('#password').type('SecurePass123!')
cy.get('#password-confirm').type('DifferentPass456!')
cy.get('form').submit()
cy.get('#password-error')
.should('be.visible')
.and('contain', 'パスワードが一致しません')
})
})
バリデーションパターン一覧
flowchart TB
subgraph Input["入力"]
I1["テキスト入力"]
I2["選択"]
I3["チェック"]
end
subgraph Validation["バリデーション"]
V1["必須チェック"]
V2["形式チェック"]
V3["一致チェック"]
V4["範囲チェック"]
end
subgraph Result["結果"]
R1["成功\n画面遷移"]
R2["エラー\nメッセージ表示"]
end
Input --> Validation
Validation --> |"OK"| R1
Validation --> |"NG"| R2
style Input fill:#3b82f6,color:#fff
style Validation fill:#f59e0b,color:#fff
style R1 fill:#22c55e,color:#fff
style R2 fill:#ef4444,color:#fff
まとめ
| カテゴリ | コマンド | 用途 |
|---|---|---|
| ページ遷移 | cy.visit() |
ページを開く |
| URL検証 | cy.url() |
現在のURLを検証 |
| URL検証 | cy.location() |
URLの詳細情報を検証 |
| ナビゲーション | cy.go('back') |
前のページに戻る |
| ナビゲーション | cy.go('forward') |
次のページに進む |
| ナビゲーション | cy.reload() |
ページを再読み込み |
| テキスト入力 | cy.type() |
テキストを入力 |
| テキスト入力 | cy.clear() |
テキストをクリア |
| セレクト | cy.select() |
オプションを選択 |
| チェック | cy.check() |
チェックボックス/ラジオをオンに |
| チェック | cy.uncheck() |
チェックボックスをオフに |
| ファイル | cy.selectFile() |
ファイルをアップロード |
| フォーム | .submit() |
フォームを送信 |
重要ポイント
- baseUrl を設定する -
cypress.config.jsで設定しておくと、テストコードがすっきりする - cy.location() を活用する - URLの各部分を個別に検証できるため、より正確なテストが書ける
- clear() + type() のパターン - 既存の入力値をクリアしてから新しい値を入力する習慣をつける
- バリデーションテストは網羅的に - 正常系だけでなく、異常系(空、不正形式、境界値)もテストする
- selectFile() を使う - ファイルアップロードは専用コマンドで簡潔にテストできる
練習問題
基本
cy.visit()にqsオプションを使って、クエリパラメータ付きでページを開いてください- ページ遷移後に
cy.url()とcy.location('pathname')でURLを検証してください - テキスト入力に
cy.clear()とcy.type()を使って値を更新してください
応用
- セレクトボックス、チェックボックス、ラジオボタンを含むフォームの操作テストを作成してください
- パスワードの一致チェックを含むバリデーションテストを作成してください
cy.go('back')とcy.go('forward')を使ったナビゲーションテストを作成してください
チャレンジ
- 会員登録フォームの全フィールド(テキスト、セレクト、ラジオ、チェックボックス、ファイル)を操作する統合テストを作成してください
- バリデーションエラーの表示・非表示を網羅的にテストするスイートを作成してください
参考リンク
次回予告
Day 6では、ネットワークリクエストの制御を学びます。cy.intercept() を使ったAPIリクエストのインターセプト、レスポンスのスタブ、エラーシミュレーションなど、モダンなWebアプリのテストに欠かせないテクニックを習得しましょう。