I was trying to cy.wait() some specific GraphQL requests that were made using fetch and using cy.route2() but I don't see an option to add a matcher for the request body. According to the type definition there's not body param
The closest thing is query but it doesn't consider the request body. Is this by design or there are plans to add it?
All GraphQL queries go to the same endpoint "server.com/graphql" using the POST method. The query itself is sent in the request payload and it's a JSON-like string as follows:
{
"query": "query ($limit: Int) {
searchShippingTemplate(limit: $limit) {
shippingTemplates {
id
}
}
}",
"variables": {
"limit": 25
}
}
My plan was add a match for searchShippingTemplate like:
cy.route2({
url: "http://localhost:3000/graphql",
method: "POST",
body: "*searchShippingTemplate*",
}).as("getQuery");
But there's no way to do that.
Add body option to RouteMatcherOptions in order to match a string in the request body.
It would be even better to be able to pass a function (for debugging purposes) since there's no easy way to know what exactly is .route2() making the comparison against for each RouteMatcherOptions option.
cy.route2({
url: "http://localhost:3000/graphql",
method: "POST",
body: (bodyString) => {
console.log(bodyString); // In theory we could even mutate the request
return bodyString;
},
}).as("getQuery");
This is not a bug and more like a feature request.
"cypress": "^5.1.0"
@asumaran yes, this would be nice to add, it is currently not implemented but as you say it would help with some implementations.
You can modify the request body and response body already.
// modify an outgoing request
cy.route2({
url: "http://localhost:3000/graphql",
method: "POST"
}, (req) => {
console.log(req.body)
req.body.replace('foo, 'bar') // modifies outgoing request
})
You can also use a deferred promise to .wait on a request that you've "matched" via the request handler.
// wait on a dynamically-matched request using a deferred Promise
const p = Cypress.Promise.defer()
cy.route2({
url: "http://localhost:3000/graphql",
method: "POST"
}, (req) => {
if (req.body.includes('searchShippingTemplate')) {
p.resolve() // resolve the deferred promise
}
})
// ... do some more Cypress stuff here ...
// now, wait on that deferred Promise to resolve:
cy.wrap(p)
I hope this helps in the meantime while we add a more formal API around this. :)
Thank you @flotwig. I鈥檓 going to try your suggestions.
I'm interested in this as well.
+1 It would be great to add this feature for the same reasons commented here.
It would be a great feature to match GraphQl queries.
@flotwig given Promise.defer() is deprecated, is there any way to do this with aliases? Does returning a promise that is resolved/rejected based on the request work?
cy.route2({
url: "http://localhost:3000/graphql",
method: "POST"
}, (req) => {
return new Promise((resolve, reject) => {
if (req.body.includes('searchShippingTemplate')) {
resolve() // resolve the deferred promise
} else {
reject()
}
})}).as(`searchShipping`)
cy.wait(`@searchShipping`)
(edit)
To answer my own question - no :(
馃摀 @bahmutov wrote a great post about graphQL requests and cy.route2 and matching request body's.
馃摀 @m4dc4p Additionally there apparently is a new way to alias specific requests now too.
cy.route2('POST', '/graphql', (req) => {
if (req.body.includes('mutation')) {
req.alias = 'gqlMutation'
}
})
// assert that a matching request has been made
cy.wait('@gqlMutation')
馃摀 @bahmutov wrote a great post about graphQL requests and
cy.route2and matching request body's.* https://glebbahmutov.com/blog/smart-graphql-stubbing/
I've read it. Unfortunately, it does not cover matching specific requests ...
馃摀 @m4dc4p Additionally there apparently is a new way to alias specific requests now too.
* https://docs.cypress.io/api/commands/route2.html#Aliasing-individual-requests
馃く - that looks like what I need. Thank you!
@flotwig given
Promise.defer()is deprecated, is there any way to do this with aliases? Does returning a promise that is resolved/rejected based on the request work?
@m4dc4p bah, I wish they hadn't stuck that deprecated warning on defer, defer is totally fine to use in situations like this imo.
You can create your own deferred promise:
function deferredPromise() {
let resolve, reject
const promise = new Cypress.Promise((_resolve, reject) => {
resolve = _resolve
reject = _reject
})
return { resolve, reject, promise }
}
Works the same as Promise.defer.
https://docs.cypress.io/api/commands/route2.html#Aliasing-individual-requests also works well, especially if you need to match on dynamic criteria.
Most helpful comment
It would be a great feature to match GraphQl queries.