CypressでGoogle, Facebook, Microsoft, GitHub などのOAuth認証のログインを実施する方法を紹介します。CypressでOAuth認証を実施する方法はいくつかあるのですが、この記事ではGoogleのOAuthに対してフォーム入力する方法を試しています。
CypressでOAuth認証の実施方法
多くのOAuthプロバイダーは、ロボット判定や、リクエスト制限などにより、CypressでOAuth認証が難しい場合があります。
Cypressの Best Practice を見ると、この問題を軽減する方法が以下のように4つほど記載されております。これらの方法は、OAuthプロバイダーや利用アプリでの実装方法によりうまくいかないケースもあるので状況に応じて使い分ける必要があります。
- 方法1.
cy.origin()
でOAuth認証のフォーム入力をする - 方法2. OAuthプロバイダーからのレスポンスをスタブ化してログインする
- 方法3.
cy.request()
でOAuthプロバイダーの公開APIを直接リクエストしてログインする - 方法4. サーバーでテスト用のログインAPIを作成してOAuthプロバイダーを省略してログインする
このうち、この記事では方法1をご紹介します。
OAuth認証のサンプルアプリの用意
実際にCypressでOAuth認証を検証するためのサンプルアプリを用意します。Cypressで実際に動かして検証しない方は、この項目をスキップしても問題ありません。
今回は、Auth0のReactのサンプルアプリのauth0-samples/auth0-react-samplesを使います。Auth0のアカウント作成(無料)が必要になります。
Auth0のセットアップ
- まず、Auth0の開発用のアカウント(無料)を作成します。
- 次に、Auth0のダッシュボードから、「Applications」を選択し、「Default App」を選択します。
- Default App 内の Settingsタブ内でいくつか操作します。
- 「Domain」と「Client ID」の値をメモしておく
- Application Type を「Single Page Application」に変更
Allowed Callback URLs
、Allowed Logout URLs
,Allowd Web Origins
に「http://localhost:3000
」を追加
Auth0のReactサンプルアプリの設定
Auth0のReactサンプルアプリのauth0-samples/auth0-react-samplesをダウンロードします。
git clone https://github.com/auth0-samples/auth0-react-samples.git # サンプルアプリのルートディレクトリに移動 cd auth0-react-samples/Sample-01
次に、Auth0の設定情報を記載する設定ファイルを作成します。
touch src/auth_config.json
そして、先ほどAuth0のSettingsタブでコピーした「Domain」と「Client ID」をsrc/auth_config.json
に記述します。
{ "domain": "<Your Auth0 Domain>", "clientId": "<Your Auth0 Client ID>" }
最後に、サンプルアプリを起動します。
npm install npm run dev
サンプルのアプリが表示されることが確認できればOKです。
方法1. cy.origin()
でOAuth認証のフォーム入力をする
CypressからOAuth認証のフォーム入力を実施してOAuth認証を実現します。通常のCypressだとうまくいかないので、cy.origin
を使うことでAuth0やGoogleページにアクセスします。
ここからは、Cypressがインストールされているリポジトリで操作をします。Cypressのインストール方法や簡単な使い方を知りたい場合はCypressをさわってみるを参照ください。
# Cypressがインストールされているリポジトリに移動 cd your-installed-cypress-project
妨害するサードパーティコードの変更を有効にする
まずcypress.config.js
内でexperimentalModifyObstructiveThirdPartyCode
をtrue
にします。若干力技ですが、Cypressの操作を妨害するサードパーティのコードをCypressが変更するようになります。
const { defineConfig } = require("cypress"); module.exports = defineConfig({ // 妨害するサードパーティコードの変更を有効にする // Doc: https://docs.cypress.io/guides/guides/web-security#Modifying-Obstructive-Third-Party-Code experimentalModifyObstructiveThirdPartyCode: true, e2e: { setupNodeEvents(on, config) { // implement node event listeners here }, }, });
cypress.env.json
の設定
cypress.env.json
ファイルにAuth0のドメインとGoogleのログイン情報を設定します。もしファイルがない場合を追加してください。cypress.env.json
の環境変数は、Cypress.env
で取得できるようになります。
{ "BASE_URL": "http://localhost:3000", "AUTH0_DOMAIN": "<Your Auth0 Domain>", "GOOGLE_USERNAME": "<Your Google Account Username>", "GOOGLE_PASSWORD": "<Your Google Account Password>", }
Googleログイン用の関数を追加
cypress/support/oauth-login.js
のファイルを作成して、Googleログイン用の関数を追加します。cy.origin
を使うことでAuth0やGoogleの別ドメインのページにアクセスできるようにしています。
// Googleログイン用の関数 function loginToGoogle(username, password) { Cypress.on( 'uncaught:exception', (err) => !err.message.includes('ResizeObserver loop') && !err.message.includes('Error in protected function') ); cy.visit(Cypress.env('BASE_URL')); cy.get('#qsLoginBtn').click(); // Auth0のログインページでGoogleボタンをクリック // doc: https://docs.cypress.io/api/commands/origin cy.origin(Cypress.env('AUTH0_DOMAIN'), () => { cy.scrollTo('bottom'); cy.get('form[data-provider="google"]').submit(); }); // Googleのログインページでユーザー名とパスワードを入力 // doc: https://docs.cypress.io/api/commands/origin cy.origin( 'https://accounts.google.com', { args: { username, password, }, }, ({ username, password }) => { Cypress.on( 'uncaught:exception', (err) => !err.message.includes('ResizeObserver loop') && !err.message.includes('Error in protected function') ); cy.get('input[type="email"]').type(username, { log: false }); // NOTE: 要素はフォームに存在するが、非表示になって再レンダリングされるためwait()を追加 cy.contains('次へ').click().wait(4000); cy.get('[type="password"]').type(password, { log: false }); cy.contains('次へ').click().wait(4000); } ); // ログイン時に表示されるプロフィールドロップダウンが存在することを確認 cy.get('#profileDropDown').should('exist'); }
また、今後OAuth認証のプロバイダーを増やしやすくするために、loginViaOAuth
という関数を追加しておきます。
// OAuth経由でログインする関数 export function loginViaOAuth(provider) { switch (provider) { case 'google': loginToGoogle( // cypress.env.json に設定したGoogleのログイン情報を使う Cypress.env('GOOGLE_USERNAME'), Cypress.env('GOOGLE_PASSWORD') ); break; // NOTE: ここに他のプロバイダーの認証を追加できる default: throw new Error('no provider configured!'); } }
CypressのOAuth認証コマンドを作成する
作成したOAuth認証の関数をCypressから呼び出せるようにするためにCypress.Commands.add
にloginViaOAuth
という名前で登録します。
import { loginViaOAuth } from './oauth-login'; Cypress.Commands.add('loginViaOAuth', (provider) => { cy.log(`loginViaOAuth : ${provider}`); loginViaOAuth(provider) });
Cypressのテスト内でOAuth認証コマンドを呼び出す
では、cypress/e2e/oauth-login.cy.js
というCypressのテストファイルを作成します。そして、cy.loginViaOAuth
を呼び出して、OAuth認証を実行します。
describe('Google Login Demo', () => { beforeEach(() => { // GoogleのOAuth認証を実施 cy.loginViaOAuth('google') }) it('shows logged in user profile page', () => { cy.visit('http://localhost:3000/profile') cy.location('pathname').should('eq', '/profile') cy.contains(Cypress.env('GOOGLE_USERNAME')).should('exist') }) })
GoogleのOAuth認証のテストが書けたので、npx cypress open
でテストを実行してみます。
cy.session
でログイン情報を保持する
現状だと、テストのたびに毎回ログインフローを実行してしまい、テスト実行時間がとても長くなってしまいます。そのため、cy.session
を使ってログイン情報をブラウザコンテキスト間でキャッシュするようにします。
import { loginViaOAuth } from './oauth-login'; Cypress.Commands.add('loginViaOAuth', (provider) => { cy.log(`loginViaOAuth : ${provider}`); // cy.sessionを使ってログイン情報をキャッシュ // Doc: https://docs.cypress.io/api/commands/session cy.session(`oauth-${provider}`, () => loginViaOAuth(provider), { // ログイン時のプロフィールドロップダウンが表示されていない場合 // loginViaOAuth(provider) を実行する validate: () => { cy.visit(Cypress.env('BASE_URL')) cy.get('#profileDropDown').should('exist') }, } ); });
Cypressのブラウザ画面をリロードすると、キャッシュが再利用されてGoogleのログインフローが省略されることが確認できます。