CypressでCognitoにログインするテストケースを通すのに苦労したポイント


概要

  • cypressを初めて使う筆者が
  • cognitoを認証基盤に利用するwebアプリに
  • ログインするテストケースを作った際にハマったポイントまとめ

結論

ここのコードに解決策が全部入ってるからこれ見ればok
https://gist.github.com/vittorio-nardone/a9b388ff7388a4ae7343cd6a2758bc4b

背景

最初、cypressをcognitoに使った事例探したらクラメソさんの記事があって、
https://dev.classmethod.jp/articles/amplify-console-cypress/

助かったー、と思いつつ参考にして実践してみたがだめだったので、色々と試行錯誤した軌跡

ハマったポイント

セレクタで入力フォームを取得できない

ログイン画面を表示させて、inputタグに入力すべくセレクタで指定したが、見つからないエラー

describe("Login Test", () => {
  it('Shows Login', () => 
    cy.visit('https://xxxx/login')
    cy.get('[data-test="sign-in-username-input"]')
  })
}

これを実行すると、見つかりません、と言われます。

Timed out retrying after 4000ms: Expected to find element: [data-test="sign-in-username-input"], but never found it.

入力フォームのタグを確認したら data-test="sign-in-username-input" 要素は間違いなくある

<input id="username" type="text" placeholder="xxx" class="input" data-test="sign-in-username-input">

なのになぜ?

iframeの影響か?いや使ってないな、とか
タグが深くなったら届かない?とか
amplify-sign-in タグまで辿れてその先の amplify-form-section が辿れないのなんでやとか
色々調べましたが、解決には至らない

キレてる。怖い。

解決策

cypressのgetにオプション { includeShadowDom: true } をつけて解決しました。

cy.get('[data-test="sign-in-username-input"]', { includeShadowDom: true })

参考) https://docs.cypress.io/api/commands/get.html#Arguments

原因

コンソールログを見ると #shadow-root(open) って表記があります。

なんそれ

shadow DOM

コンポーネントっぽくDOMを利用できるっぽいなにからしい(かなり浅い理解
まあ、なんせshadowって言ってるぐらいなんで、隠れてるんでしょうね。なので、セレクタが見つけれなかったと。
で、それでも見つけてくれるオプション includeShadowDom ができたと。

デフォルト true じゃあかん?
と思ったら、configurationにあるので変えれそう

パスワード入力フォームにtypeできない

ログインIDはセレクタで取得しtypeで入力できたので、次パスワード、と意気込むとこのエラーがでました。

cy.type() failed because it targeted a disabled element.
  > <input id="password" type="password" placeholder="パスワード" class="input" data-test="sign-in-password-input">
Ensure the element does not have an attribute named disabled before typing into it.

なんか途中のinputタグの表現がキレてる。今度は私じゃなくてタグがキレてる。怖い。

エラー文にあるような、 disabled な要素になってないのは確認した。
で、ググってみると同様の現象はあるようで、以下のように解決してた。

    cy.get('[slot="sign-in"]').find('#password', { includeShadowDom: true }).click({force: true})
    cy.get('[slot="sign-in"]').find('#password', { includeShadowDom: true }).type('password')

一回 .click({force: true}) で強制クリックでフォーカスしておいて type() すると。なるほど。
これで動作した。

が、結論にのせたgistにもっとスマートな方法があった

    cy.get('[slot="sign-in"]').find('#password',{ includeShadowDom: true } ).type('password', {log: false, force: true})

type() に force:true optionあるやん。1行ですむやん。素敵やん。

ローカル環境でAPIリクエストが502 Bad Gateway

これらをクリアし、いざログインしてみると、
なぜかAPIリクエストのレスポンスが502 Bad Gateway で返ってくる。ちなみに以下のような構成。

  • AWS上の開発環境だと問題なかった
  • Cognitoから返ってきてるIdTokenの中身をdecodeしてみたが、開発環境とほぼ変わらず問題なさそうに見えた

もちろん、素のchromeだと期待通り動作する。
なので、違うのはブラウザの違いぐらいかな、とか思いながらまだ未解決

追記)解決した

原因

認可処理で、アプリケーション(lambda) で受け取る変数 event.header.Authorization が、
cypress上のブラウザからのリクエストだと event.header.authorization になってた。

cypressが投げるリクエストヘッダは Authorization なので、何故、誰が小文字にしたのかは謎。

ServerlessFrameworkをローカルで動かすプラグインservlerless framework offlineのせいかな・・・?