Skip to content
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

Allow dynamically matching requests with a function #387

davidnorth opened this issue Jan 23, 2017 · 14 comments · Fixed by #8974

Allow dynamically matching requests with a function #387

davidnorth opened this issue Jan 23, 2017 · 14 comments · Fixed by #8974


Copy link

davidnorth commented Jan 23, 2017

To allow highly flexible matching of which requests you want to stub, we could add the option to accept a function instead of a fixed method/URL. This would recieve the request object (or some wrapper around it). The motivation is that I have and SPA that uses a GraphQL backend where all requests have the same URL and I want to filter them by the query name which is sent as a parameter in the POST body.

  matcher: (request) => !! request.body.match(//)
@brian-mann brian-mann added type: question type: feature New feature that does not currently exist and removed type: question labels Jan 23, 2017
@jennifer-shehane jennifer-shehane added the stage: ready for work The issue is reproducible and in scope label Apr 17, 2018
mgurov added a commit to mgurov/kibanator that referenced this issue Jul 16, 2018
a workaround for missing dynamic routing capabilities
see cypress-io/cypress#521
and cypress-io/cypress#387
Copy link

TLadd commented Sep 26, 2018

I have the same use case (using GraphQL). At the moment, rather than being able to wait on requests, having to hardcode timeouts wherever there are requests that can take a couple seconds. Being able to match on operation name and waiting on it to return would let us write tests in a way more similar to how the Cypress docs recommend.

Copy link

nazar commented Dec 6, 2018

Hey @davidnorth - I'm researching for solutions for the same issue you reported. I'd be extremely grateful if you could share what solutions, if any, you have used to enable defining multiple graphQL routes in a test and waiting for specific ones to complete.

Were you able to do this in Cypress (or any other e2e testing framework)?

Hey @jennifer-shehane - any progress or rough ETAs on this feature 🙏 ?

We are increasingly working on applications that utilise graphQL and Cypress' lack of isolating specific requests to wait via cy.server().route() on is becoming a blocker for us, especially for applications that exclusively use graphQL.

Copy link

agoldis commented Jan 17, 2019

+1 here
We use the same URL and HTTP methods for performing different tasks (unfortunately that's how Parse is designed). So it'd be useful to be able to customize routes matching with more granularity than HTTP verb and URL.

Copy link

@nazar There has been no work currently done on this feature.

Copy link

nazar commented Jan 30, 2019

Thank you for the update @jennifer-shehane

I've been able to find a work-around that provides me with the required functionality. I feel it's a bit hacky but it works for now.

My support/index.js contains:

beforeEach(() => {
      method: 'POST',
      url: '/api/graphql' <---- this is our graphQL endpoint

And the following in my support/commands.js

Cypress.Commands.add('waitForQuery', operationName => {
  cy.wait('@graphqlRequest').then(({ request }) => {
    if (request.body.operationName !== operationName) {
      return cy.waitForQuery(operationName); <---- this is the hacky bit

The above can be used as follows:

      it('Should Foo Bar', () => {

           // can also be used to check XHR payloads
          .then(({ body: { variables: { search: { foo } } } }) => expect(foo).to.equal(1))

HTH anybody with the same requirements.

Copy link

stanbluijs commented Feb 14, 2019

I tried to solve the issue a different way where not to actually wait for the request to happen, but to execute a function that contains the tests when the request got a response. stackoverflow The reason I want to solve it this way is because I also think the solution that nazar is using is a bit hacky.

Now the only issue I get is that the test passes while one of the expects fails (see screenshot). I think this is because route onResponse does not trigger the error like I expected. There are no console errors and also the graphql request get server response 200.

Is there a wait to tell Cypress that the test should fail?



export const onGraphQLResponse = (resolvers, args) => {
    resolvers.forEach((n) => {
        const operationName = Object.keys(n).shift();
        const nextFn = n[operationName];

        if (args.request.body.operationName === operationName) {

const handleGraphQLResponse = (next) => {
    return (response) => {

        const responseBody = Cypress._.get(response, "body");

        return async (alias) => {
            await Cypress.Blob.blobToBase64String(responseBody)
                .then((blobResponse) => atob(blobResponse))
                .then((jsonString) => JSON.parse(jsonString))
                .then((jsonResponse) => {
                        name: "wait blob",
                        displayName: `Wait ${alias}`,
                        consoleProps: () => {

                .then((data) => {
                }).catch((error) => {
                    return error;

In a test file

Bind an array with objects where the key is the operationName and the value is the resolve function.

import { onGraphQLResponse } from "./util/graphQLResponse";

describe("Foo and Bar", function() {
    it("Should be able to test GraphQL response data", () => {

            method: "POST",
            url: "**/graphql",
            onResponse: onGraphQLResponse.bind(null, [
                {"some operationName": testResponse},
                {"some other operationName": testOtherResponse}


        function testResponse(result) {
            const foo =;
            expect(foo.label).to.equal("Foo label");

        function testOtherResponse(result) {
            const bar =;
            expect(bar.label).to.equal("Bar label");


Used the blob command from

Copy link

godspeedelbow commented Apr 17, 2019

Opened a PR for this feature request after spending way to much time trying to hack it in in userland. This should be supported out of the box IMO.


@cypress-bot cypress-bot bot added stage: work in progress There is an open PR for this issue [WIP] and removed stage: ready for work The issue is reproducible and in scope labels Apr 17, 2019
Copy link

erezrokah commented Sep 8, 2019

I'm using this approach at the moment:

const stubFetch = (win, routes) => {
  const fetch = win.fetch;
  cy.stub(win, 'fetch').callsFake((...args) => {
    const routeIndex = routes.findIndex(r => matchRoute(r, args));
    if (routeIndex >= 0) {
      const route = routes.splice(routeIndex, 1)[0];
      const response = {
        status: route.status,
        headers: new Headers(route.headers),
        text: () => Promise.resolve(route.response),
        json: () => Promise.resolve(JSON.parse(route.response)),
        ok: route.status >= 200 && route.status <= 299,
      return Promise.resolve(response);
    } else {
      console.log('No route match for:', args[0]);
      return fetch(...args);

Cypress.Commands.add('stubFetch', ({ fixture }) => {
  return cy.fixture(fixture, { log: false }).then(routes => {
    cy.on('window:before:load', win => stubFetch(win, routes));

matchRoute is a custom implementation and fixture is a json file with an array of the the routes to mock:

const escapeRegExp = string => {
  return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');

const matchRoute = (route, fetchArgs) => {
  const url = fetchArgs[0];
  const options = fetchArgs[1];

  const method = options && options.method ? options.method : 'GET';
  const body = options && options.body;

  // use pattern matching of the timestamp parameter
  const urlRegex = escapeRegExp(route.url);

  if (method === route.method && decodeURIComponent(url).match(new RegExp(urlRegex))) {
    if (body && route.body) {
      // some custom logic to match the bodies     
    } else {
      return route;

Copy link

I've run into the same issue and have created a command that supports 2 different methods which should be a good jumping off point for those that need it.

Copy link

thinkerelwin commented Apr 29, 2020

for those who got the same question, the official doc has provide a solution:

you can use

onResponse: (xhr) => {
    // do something with the
    // raw XHR object when the
    // response comes back

to change the xhr.response, an example willl looks like this:

onResponse: xhr => {
        const index = xhr.request.body.targetUrl.replace(
          (match, number) => number

        if (index) {
          xhr.response.body = mockDataList[index];

Copy link

vicatcu commented Oct 29, 2020

@thinkerelwin How can you use onResponse to wait for a response body that satisfies some application specific criteria? How do you prevent cy.wait('@namedRoute') from completing on "just any" request that matches the location and method declared in cy.route?

Copy link

flotwig commented Nov 2, 2020

Once #8974 is merged, you will be able to dynamically alias cy.route2 requests from the function callback:

cy.route2('POST', '/graphql', (req) => {
  if (req.body.includes('mutation')) {
    req.alias = 'gqlMutation'

// assert that a matching request has been made

Copy link

cypress-bot bot commented Nov 3, 2020

The code for this is done in cypress-io/cypress#8974, but has yet to be released.
We'll update this issue and reference the changelog when it's released.

@cypress-bot cypress-bot bot added stage: pending release There is a closed PR for this issue and removed stage: work in progress There is an open PR for this issue [WIP] labels Nov 3, 2020
Copy link

cypress-bot bot commented Nov 9, 2020

Released in 5.6.0.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v5.6.0, please open a new issue.

@cypress-bot cypress-bot bot removed the stage: pending release There is a closed PR for this issue label Nov 9, 2020
@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Nov 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
None yet

Successfully merging a pull request may close this issue.