サイプレスでは、特定のネットワークリクエストにエイリアスを設定して、それを「待機」できるのは 十分に文書化された です。これは、特定のネットワーク要求が発生して終了した後にサイプレスで何かを実行したい場合に特に役立ちます。
サイプレスのドキュメントからの以下の例:
cy.server()
cy.route('POST', '**/users').as('postUser') // ALIASING OCCURS HERE
cy.visit('/users')
cy.get('#first-name').type('Julius{enter}')
cy.wait('@postUser')
ただし、アプリでGraphQLを使用しているため、エイリアシングは簡単なことではなくなりました。これは、すべてのGraphQLクエリが1つのエンドポイントを共有するためです/graphql
。
URLエンドポイントのみを使用して異なるgraphQLクエリを区別することはできませんが、operationName
(を使用してgraphQLクエリを区別することは可能です次の画像を参照してください)。
ドキュメントを掘り下げてみたところ、リクエスト本文からoperationName
を使用してgraphQLエンドポイントにエイリアスを付ける方法はないようです。また、応答ヘッダーのカスタムプロパティとしてoperationName
(黄色の矢印)を返します。ただし、特定のgraphQLクエリをエイリアスするためにそれを使用する方法を見つけることもできませんでした。
失敗した方法1:この方法では、画像に示されている紫色の矢印を使用しようとします。
cy.server();
cy.route({
method: 'POST',
url: '/graphql',
onResponse(reqObj) {
if (reqObj.request.body.operationName === 'editIpo') {
cy.wrap('editIpo').as('graphqlEditIpo');
}
},
});
cy.wait('@graphqlEditIpo');
graphqlEditIpo
エイリアスは実行時に登録されるため、このメソッドは機能しません。そのため、私が受け取るエラーは次のとおりです。
CypressError:cy.wait()は、 '@ graphqlEditIpo'の登録済みエイリアスを見つけることができませんでした。使用可能なエイリアスは次のとおりです: 'ipoInitial、graphql'。
失敗した方法2:この方法では、画像に示されている黄色の矢印を使用しようとします。
cy.server();
cy.route({
method: 'POST',
url: '/graphql',
headers: {
'operation-name': 'editIpo',
},
}).as('graphql');
cy.wait('graphql');
Cy.routeのoptionsオブジェクトのheadersプロパティは、実際には docs に従って、スタブされたルートの応答ヘッダーを受け入れるようになっているため、このメソッドは機能しません。ここでは、それを使用して特定のgraphQLクエリを識別しようとしていますが、これは明らかに機能しません。
これが私の質問につながります:サイプレスで特定のgraphQLクエリ/ミューテーションをエイリアスするにはどうすればよいですか?私は何かを逃したことがありますか?
これが、私が各GraphQLリクエストを区別する方法です。 cypress-cucumber-preprocessorを使用するため、/ cypress/Integration/common /にcommon.jsファイルがあります。 beforeおよびbeforeEachフック。これらは機能ファイルの前に呼び出されます。
ここで解決策を試しましたが、アプリケーションでは、いくつかのアクションに対して多くのGraphQLリクエストが同時にトリガーされるため、安定したものを思い付くことができませんでした。
最終的に、すべてのGraphQLリクエストをgraphql_accumulatorというグローバルオブジェクトに、発生ごとのタイムスタンプとともに保存しました。
その後、サイプレスコマンドで個々のリクエストを管理するのが簡単になりましたshould。
common.js:
beforeEach(() => {
for (const query in graphql_accumulator) {
delete graphql_accumulator[query];
}
cy.server();
cy.route({
method: 'POST',
url: '**/graphql',
onResponse(xhr) {
const queryName = xhr.requestBody.get('query').trim().split(/[({ ]/)[1];
if (!(queryName in graphql_accumulator)) graphql_accumulator[queryName] = [];
graphql_accumulator[queryName].Push({timeStamp: nowStamp('HHmmssSS'), data: xhr.responseBody.data})
}
});
});
リクエストヘッダーに(まだ)キーoperationNameがないため、FormDataからqueryNameを抽出する必要がありますが、ここでこのキーを使用します。
commands.js
Cypress.Commands.add('waitGraphQL', {prevSubject:false}, (queryName) => {
Cypress.log({
displayName: 'wait gql',
consoleProps() {
return {
'graphQL Accumulator': graphql_accumulator
}
}
});
const timeMark = nowStamp('HHmmssSS');
cy.wrap(graphql_accumulator, {log:false}).should('have.property', queryName)
.and("satisfy", responses => responses.some(response => response['timeStamp'] >= timeMark));
});
/ cypress/support/index.js:にこれらの設定を追加して、サイプレスがGraphQLリクエストを管理できるようにすることも重要です。
Cypress.on('window:before:load', win => {
// unfilters incoming GraphQL requests in cypress so we can see them in the UI
// and track them with cy.server; cy.route
win.fetch = null;
win.Blob = null; // Avoid Blob format for GraphQL responses
});
私はそれを次のように使用します:
cy.waitGraphQL('QueryChannelConfigs');
cy.get(button_edit_market).click();
cy.waitGraphQLは、呼び出し後に保存される最新のターゲットリクエストを待機します。
お役に立てれば。
私たちのユースケースでは、1ページに複数のGraphQL呼び出しが含まれていました。上記の応答の修正バージョンを使用する必要がありました。
_Cypress.Commands.add('createGql', operation => {
cy.route({
method: 'POST',
url: '**/graphql',
}).as(operation);
});
Cypress.Commands.add('waitForGql', (operation, nextOperation) => {
cy.wait(`@${operation}`).then(({ request }) => {
if (request.body.operationName !== operation) {
return cy.waitForGql(operation);
}
cy.route({
method: 'POST',
url: '**/graphql',
}).as(nextOperation || 'gqlRequest');
});
});
_
問題は、すべてのGraphQLリクエストが同じURLを共有することです。したがって、1つのGraphQLクエリに対してcy.route()
を作成すると、サイプレスは後続のすべてのGraphQLクエリをそれに一致させます。一致したら、cy.route()
をデフォルトのラベルgqlRequest
または次のクエリに設定します。
私たちのテスト:
_cy.get(someSelector)
.should('be.visible')
.type(someText)
.createGql('gqlOperation1')
.waitForGql('gqlOperation1', 'gqlOperation2') // Create next cy.route() for the next query, or it won't match
.get(someSelector2)
.should('be.visible')
.click();
cy.waitForGql('gqlOperation2')
.get(someSelector3)
.should('be.visible')
.click();
_