Cypressでユーザーエージェント(以降、UAと記載)による条件分岐のテストを行う場合、
E2EテストであればuserAgentの設定がありますが、
Componentテストの場合は上記の設定は効かず、
どのようにテストを書くのか非常に困ったのでまとめます。
いきなり結論
__defineGetter__
でwindow.navigator
のuserAgent
を直接書き換えることで解決できます。
import Target from './Component'
before(() => {
cy.mount(
<Target>
<div data-cy="children">test</div>
</Target>
)
const chromeUA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
// UAをchrome用の値に変更
window.navigator.__defineGetter__(
'userAgent',
() => chromeUA,
)
})
it('chromeの場合、childrenのみ表示すること', () => {
// エラーメッセージが表示されないこと
cy.get('[data-cy="エラーメッセージ"]')
.should('not.be.exist')
// childrenが表示されること
cy.get('[data-cy="children"]').should('be.visible')
})
以降はそれなりに細かく解説を書いてきます。
対象読者
- CypressでComponentテストを書いている人
- UAの分岐処理をテストしたい人
コンポーネントの実装はReactで書きますが、
テスト自体はCypressで動かすため、テストの作成以降はVueなどでも問題ないはず。
実行環境
本記事は、以下の環境で実行を行いました。
- macOS - Ventura
- Node - 18.9.0
- Vite - 4.3.8
- React - 18.2.0
- Cypress - 12.12.0
- Safari - 16.5
- Chrome - 114.0.5735.90
事前準備
Cypress等のセットアップはすでに完了している前提で進めます。
UAからブラウザを判断する処理をイチから書くのは骨が折れるので、
今回はUAParser.jsを使います。
UAParser.jsのインストール
npm i ua-parser-js
Safariの場合エラーを出すコンポーネントの作成
UAParser.jsが返すブラウザ名はここに定義されています。
今回はSafariの利用を制限する想定で、
browser.name
にSafari
を含む場合に分岐するよう処理を書いています。
import { useState, useEffect } from 'react'
import { UAParser } from 'ua-parser-js'
const SafariError = () => (
<p data-cy='エラーメッセージ'>
Safariでのアクセスは禁止されています。
</p>
)
export default ({ children }) => {
const [isSafari, setIsSafari] = useState()
useEffect(() => {
// ブラウザのUAを取得
const ua = window.navigator.userAgent
// UAParseに渡してUAを扱いやすいように整形する
const { browser } = UAParser(ua)
// Safari、Mobile Safariのときtrueにする
const isSafariBrowser = browser.name.includes('Safari')
setIsSafari(isSafariBrowser)
}, [])
// まだ定義が無いの場合は何も返さない
if (isSafari === undefined) return
// trueになった場合は、エラーメッセージを表示する
if (isSafari) return <SafariError />
// falseになった場合は、そのままchildrenを表示する
else return children
}
テストの作成
ブラウザからUAを取得する
UA変更は文字列で全文を指定する必要があるため、ブラウザからUAを取得します。
Chromeの場合
DevTools(F12)のConsoleタブでwindow.navigator.userAgent
を入力してEnter
今回、私の環境で取得したUAは以下となります。
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Safariの場合
手順はChromeの場合と殆同じです。
Webインスペクター(⌥ + ⌘ + c)のConsoleタブでwindow.navigator.userAgent
を入力してEnter
今回、私の環境で取得したUAは以下となります。
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15
テストにUA書き換え処理を追加する
冒頭にも書いた通り、UAの書き換えは__defineGetter__
でwindow.navigator
のuserAgent
を変更することで実現できます。
beforeにUA書き換え処理を記載する
itの中に書くとUA書き換え前の状態でテストが行われるようなので、
beforeやbeforeEachの中にUA書き換え処理を書いていきます。
before(() => {
// 実際のブラウザから取得したUA
const chromeUA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
// UAをchrome用の値に変更する
window.navigator.__defineGetter__(
'userAgent',
() => chromeUA,
)
})
mountとかitとか書いてく
mountのあとは、React以外のComponentテストで共通だと思います。
describe('Chromeの場合', () => {
before(() => {
// 実際のブラウザから取得したUA
const chromeUA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
// UAをChrome用の値に変更する
window.navigator.__defineGetter__(
'userAgent',
() => chromeUA,
)
})
it('childrenのみ表示されること', () => {
// エラーメッセージが表示されないこと
cy.get('[data-cy="エラーメッセージ"]').should('not.be.exist')
// childrenが表示されること
cy.get('[data-cy="children"]').should('be.visible')
})
})
describe('Safariの場合', () => {
before(() => {
// 実際のブラウザから取得したUA
const safariUA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15'
// UAをSafari用の値に変更する
window.navigator.__defineGetter__(
'userAgent',
() => safariUA,
)
})
it('エラーメッセージのみ表示されること', () => {
// エラーメッセージが表示されること
cy.get('[data-cy="エラーメッセージ"]').should('be.visible')
// childrenが表示されないこと
cy.get('[data-cy="children"]').should('not.be.exist')
})
})
実行結果
早すぎて見えませんが、UAごとの表示分岐をテストすることができました。
トレタでは一緒に働く自動化エンジニア募集しています!!
トレタでは、テスト自動化エンジニア/SETの方を募集しています。
カジュアル面談もしていますので、興味のあるかたは是非ご応募ください!
他の業種も募集してます!