-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add response alias support (res.alias) #3083
Comments
Would the feature proposed in this issue help solve your problem? #521 |
Actually not. As far as i understand, this issue is about either stubbing a response or returning the real response depending on an certain conditions. It either stubs the response immediately, if these conditions are met (true) or otherwise waits for the real response. But basically it waits for one response. My request is about waiting for a real response on a single route, until it satisfies certain conditions. Responses that arrive in the meantime (before these conditions are met), are treated like they were for another route, and are thus ignored on a wait. (It keeps waiting). As i saw in #521 (comment), all this will be implemented as part of this large rewrite #687. I'd be happy if this issue could be included there as related to it. By the way. Thank you for this invaluable testing tool. |
I have this issue too. Hope this can get resolved! Being able to filter on the operation name would be pretty useful too. |
I'm having a hard time figuring out how to achieve this. I use I would like to intercept certain requests (depending on the request body) and respond with a fixture or plain object without hitting the server. |
Same problem here, by now I need to use arbitraries |
Also having this issue. I've been tracking should.exist & should.not.exist of a |
same problem here, would like to know what your workarounds are.. |
Any progress on this issue? Or another place to track this? Also needing to work with real GraphQL requests to the same endpoint, not stubbed. |
Hey @jennifer-shehane - the functionality described in #521 would actually not cover this. What would be really useful is to be able to distinguish a request based on its POST body or response body. One of the love-it/hate-it things of graphql is that all requests go over the same URL and use the same HTTP port (typically This means using the typical For reason reason (as |
Oops. Probably a mistaken mention. The last place I worked had a similar issue because we used websockets for a lot of request/response communication. We solved it by making a custom request/wait pair using the built-in Cypress commands as a baseline. It worked well, but unfortunately I don't have access to the source code. I remember the implementation had an internal mapping of requests and spied on the websockets APIs to match requests. The code wasn't sharable because we had our own protocol based on socket.io. It was doable, but not trivial. GraphQL is defined enough to come up with a generalized plugin. |
I had the same issue and solved it in a "messy but working" kind of way, this is my solution : in a javascript file add this function example : utils/networking.js export function waitForGQL(queryName, onQueryFoundFn) {
function waitOnce() {
cy.wait('@gql-call').then(xhr => {
if (xhr.requestBody && xhr.requestBody.query.includes(queryName)) {
if (onQueryFoundFn) onQueryFoundFn(xhr);
} else {
waitOnce();
}
});
}
waitOnce();
} In the test file : import { waitForGQL } from '../utils/networking';
// start by listening on the api endpoint
cy.server();
cy.route('POST', '/api/graphql').as('gql-call');
// ... some test code here
// then when you want to wait for a GraphQL query you do this :
waitForGQL('myQuery', xhr => {
const someValue= xhr.responseBody.data.myQuery.someValue;
expect(someValue).to.be.above(0);
}); |
@bmarwane, thank you for your solution. I want to say that the solution doesn't always work for us. We have four different GraphQL queries on the page that we want to test and let's say we are trying to wait for the third query. It seems We are trying to look at export function waitForGQL(operationName, onQueryFoundFn) {
function waitOnce() {
cy.wait('@gql-call').then(xhr => {
// then block doesn't always work
if (xhr.requestBody && xhr.requestBody.operationName === operationName) {
if (onQueryFoundFn) onQueryFoundFn(xhr)
} else {
waitOnce()
}
})
}
waitOnce()
} and the usage: waitForGQL('companies', xhr => console.log(xhr)) cypress v4.2.0 |
@bmarwane Perfect, this works for me for now. It would be nice to have a built-in solution though. EDIT: Actually this is not that robust in my case. I have 5 other graphql calls firing before the one I am waiting for. If one of those fails, the test doesn't complete. |
@umitkucuk the reason why it doesn't work AFAIU is because if you have two requests happening simultaneously to the same URL, Cypress is grouping them sometimes (you can see they're literally grouped in the event log when using Cypress UI). What it means for |
I tried this way, but it's also working not stable cypress/support/commands.js Cypress.Commands.add('waitForQuery', (operationName, checkStatus = true) => {
Cypress.log({
name: 'api request',
displayName: 'Wait for API request',
});
cy
.wait('@apiRequest')
.then(async ({ response, request }) => {
if (request.body.operationName !== operationName) {
return cy.waitForQuery(operationName);
}
const bodyText = await response.body.text();
const json = JSON.parse(bodyText);
Cypress.log({
name: 'api response',
displayName: 'API response',
message: operationName,
consoleProps: () => ({ Response: json.data }),
});
if (checkStatus) {
expect(json.errors, 'API response without errors').not.exist;
}
return {
...response,
json: json.data,
};
});
}); In a test file cy.waitForQuery('savePreview'); |
Hello I have the same p
I've tried to reply your suggestion but I have the same problem, this aproach not always work |
We are also experiencing the behavior @ikornienko was describing, where graphQL calls that are initiated at approximately the same time are grouped together and can't be waited on individually. The workaround we're using is to append a query string with an empty parameter to the GraphQL endpoint based on the query/mutation name. For example, if your GraphQL queries and mutation hit if (window.Cypress) {
url = `/graphql?${operationName}`
}
// make the ajax call e.g., if your query is named cy.route("POST", "**/graphql?getFoo").as("getFoo");
// ...
cy.wait("@getFoo"); instead of cy.route("POST", "**/graphql").as("graphql");
// ...
cy.wait("@graphql"); |
We have the same problems. @bahmutov @jennifer-shehane Is there a plan when support for grapQL can be implemented? |
Followed @Jamescan approach. When using Apollo you can edit the link setup and have the operationName param.
|
If your app can be made test-aware (as it should - in the elusive "app actions" fashion) then perhaps it is enough to have the app register its responses with some well-known custom event on the window, and wait for them using e.g. cypress-wait-until: (in app's response handler) if(window.Cypress) {
const evt = new Event(`graphql-response:${req.operationName}`)
evt.json = res.json
window.dispatchEvent(evt) (in test) cy.waitUntil(
() => new Promise((resolve, reject)=> { window.addEventListener('graphql-response:getUser', function(e){ resolve(e) })})
) note: untested, just off the top of my head 😅 |
N.b. I was able to solve a very similar case recently, by using cy.route's |
Hi @cbrunnkvist Can you pls post your solution using onResponse |
@danisal I figured out following way: Step 1: Step 2: Intercept fetch request and retrieve operation name. Call the original fetch by appending the operation name to the original url
Step 3: Create a route with query as |
Hello, Somebody use cy.route2() to intercept graphql queries?? |
FWIW, I was interested in waiting for a specific operation to complete. This is my solution on Cypress.Commands.add('waitGraphql', (operationName: string) => {
cy.route({
method: 'POST',
url: '**/graphql',
onResponse: ({ request }) => {
if (request.body.operationName === operationName) {
cy.emit(operationName)
}
},
})
cy.waitFor(operationName)
}) |
@jennifer-shehane when we can expect this to be closed with proper support to wait for graphql. We need the same support how we have for a rest api. |
I worked around it by extending on @umitkucuk 's answer:
The last line can also be |
Any update on this on version 6? Has anyone managed to use cy.intercept() for graphql? |
The solution that i shared last time was a bit buggy, so i tried something more robust based on some answers in this thread, i settled on this solution for the moment : in commands.js : Cypress.Commands.add('startObservingGqlQueries', () => {
cy.server().route({
method: 'POST',
url: '**/graphql',
onResponse: ({ request, response }) => {
window.Cypress.emit('gql', { request, response });
},
});
});
Cypress.Commands.add('waitForGQL', queryName => {
return new Cypress.Promise(resolve => {
cy.on('gql', ({ request, response }) => {
if (request.body && request.body.query && request.body.query.includes(queryName)) {
resolve(response);
}
});
});
}); in my test file i do this : // first line in my test file
cy.startObservingGqlQueries();
// ... some test code here
// then when you want to wait for a GraphQL query you do this :
// wait inside a cy.wrap so tell cypress to wait for the response to be resolved before going to the next step
cy.wrap(null).then(() => {
return cy.waitForGQL('someQueryName').then(response => {
expect(response.someValue).to.be.equal(1);
});
}); It's not a perfect solution, but i hope you find it helpful. |
As @leilafi mentioned, the
Where queryName and mutationName are the names of your GQL operations. You can add an additional condition for each request that you would like to alias. You can then wait for them like so:
|
@travislloyd Unfortunately, this didn't quite work for me. I think the Cypress.Commands.add('waitGraphql', (operationName: string) => {
cy.intercept('POST', '/graphql', req => {
if (req.body.operationName === operationName) {
req.reply(() => {
cy.emit(operationName)
})
}
})
cy.waitFor(operationName)
}) |
Nice, how would you configure this in a cypress command? That would help out a lot! |
ContextPerforming verification on a component that contains a search bar. Entering two character into the search bar starts to send graphQL requests. Verification of filtered components starts once the final graphQL query is resolved. ExampleTest is entering the
ObjectiveWait until the last character is entered via Solution (tested with Cypress 6.6.0)Create a specific alias via intercept targeting the final string:
Perform data entry:
Intelligently wait until the last request is resolved:
At this point the last character of the search string is entered and the response for the full string is received. Happy testing. 🧡💛🧡 |
Closing as resolved as this is now possible using cy.intercept. |
Current behavior:
It's a bit error prone to wait for specific GraphQL queries, if they run simultaneously or their order of execution is unknown or the response times cause race conditions, as they all run on the same endpoint, for example
/graphql
.My current workaraound is a recursive function:
This works often, but suffers from some flakyness, which could also be caused by #2700. Also i'm not 100% sure how this recursion works with Cypress.
Desired behavior:
A solution to define already on the route, the content i'm waiting for, and keep waiting until i have found the specified content or the timeout is reached. This could be achieved with a validation callback option with the signature
xhrResponse => Promise<boolean> | boolean
. For example:The text was updated successfully, but these errors were encountered: