cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.87k stars 3.18k forks source link

Default headers for cy.request command #726

Open MaxNamazov opened 7 years ago

MaxNamazov commented 7 years ago

Current behavior:

There is no possibility to add default headers for the cy.request command. The only way to add such headers is to overwrite this command

Desired behavior:

The possibility to set default headers (and maybe some other default options) for the cy.request command. For example, we use SessionId header for authenticated requests in our app. And I would like to implement custom login command for my tests, so it would set this header as default for all subsequent requests.

wawhal commented 6 years ago

Any update about this? @jennifer-shehane?

jennifer-shehane commented 6 years ago

Let me make sure I accurately understand your feature request.

You are able to set headers within the cy.request() options object as documented here, but you would like 1 place to set the headers to affect all cy.request() done, similar to how cy.server() options affect routes and requests?

If this is the case, there is already a headers key on cy.server() that is solely used on stubbed cy.route(), we'd have to resolve this conflict somehow or implement some other way.

mattvb91 commented 6 years ago

:+1: for this. Would like to also have the option to globally set headers for all cy.visit() requests.

Qwal commented 5 years ago

As a workaround I overwrite cy.request method with my custom command:

Cypress.Commands.overwrite('request', (originalFn, ...options) => {
  const optionsObject = options[0];

  if (optionsObject === Object(optionsObject)) {
    optionsObject.headers = {
      authorization: 'Bearer YOUR_TOKEN',
      ...optionsObject.headers,
    };

    return originalFn(optionsObject);
  }

  return originalFn(...options);
});

Please note it only works if you use cy.request with options object.

GuilhermeAOliveira commented 5 years ago

I did my request like this, and it works very well(GET request):

it('myFunction()', function(){
        cy.request(
            {
                url: 'https://url-you-want-to-request',
                headers : {
                    'Service-Provider-Id' : 'name-etc',
                    'Client-Realm-Id': 'bla bla bla-',
                    'Application-Id': 'bla bla bla',
                    'Frontend-Id': 'XXX',
                    'Device-Id': 'device-id-number-etc',
                    'CID': 'co-relation-id-number-',
                }
            }
        )
    })
andywarren86 commented 5 years ago

Similar to @Qwal's comment, I created a custom request() command by overwriting the original, so I could set default options on the request (in this case basic auth headers).

This implementation supports the following usage patterns: cy.request( options ) cy.request( url ) cy.request( method, url ) cy.request( method, url, body )

import _ from 'lodash';

// Overwrite cy.request() to set default options
Cypress.Commands.overwrite('request', (originalFn, ...args) => {
  const defaults = {
    auth: {
      user: 'admin',
      pass: 'password1'
    }
  };

  let options = {};
  if (_.isObject(args[0])) {
    options = Object.assign({}, args[0]);
  } else if (args.length === 1) {
    [options.url] = args;
  } else if (args.length === 2) {
    [options.method, options.url] = args;
  } else if (args.length === 3) {
    [options.method, options.url, options.body] = args;
  }

  return originalFn(Object.assign({}, defaults, options));
});

I copied the logic from the base implementation of cy.request(): https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cy/commands/request.coffee#L61

justin808 commented 4 years ago

Similar to this feature request, is there some way to set default headers for cy.visit?

san-slysz commented 4 years ago

As I use the cucumber plugin, I did the following within a hooks.js file (next to your given.js, ... files)

import {ENV} from "../../../env";

const {
    Before,
} = require("cypress-cucumber-preprocessor/steps");

const headers = {
    'My-header-username': 'plop',
    'My-header-Id': 1
};

//Before all
Before( () => {
    Cypress.config('baseUrl', Cypress.env(`${ENV}BaseUrl`));
    cy.wrap(headers).as('defaultHeaders'); // See usage below, in the step
});

Before({tags: '@oauth2'}, () => {
    headers['My-Oauth2-Header'] = 'oauth2';
    headers['My-Needed-Key'] = Cypress.env(`${ENV}Key`);
});

Before({tags: '@jwt'}, () => {
    headers['My-Jwt-Header'] = 'jwt';
});
When(/^I do something$/, function () { // Don't use fat arrow here, as we use this. below

    cy.request({
        method: GET,
        url: /myurl,
        headers: this.defaultHeaders // Here is why I used cy.wrap() in the global before hook
    }).then( (response) => {
        cy.wrap(response).its('status').as('responseStatusCode');
        cy.wrap(response).its('headers').as('responseHeaders');
        cy.wrap(response).its('body').as('responseBody');
        cy.wrap(response).its('duration').as('responseDuration');
    })
});

Depending of the scenario tags, we'll either get the oauth2 or jwt headers. We'll always get the baseUrl one.

This is a dumb exemple of what can be done using global or tag-related befores. Hope this helps someone. This can work with cy.request(), cy.visit(), ... and does not need to redefine any of those. Which is great. But it does require cucumber.

shahiddev commented 4 years ago

We're using Cypress for some API testing and not having an easy way of setting a bearer token globally to the cy.request is a pain. I'll look at @Qwals approach as a stop gap but it feels like a missing feature to be able to specify this somewhere once rather than having to add the header to every cy.request.

hamidgh commented 3 years ago

I found a workaround for it:

cy.server({
       onAnyRequest:   function (route,  proxy) {
           proxy.xhr.setRequestHeader('CUSTOM-HEADER',  'Header value');
     }
});
pitpit commented 3 years ago

here's an updated version of https://github.com/cypress-io/cypress/issues/726#issuecomment-522378331 without lodash and deepmerging headers

// Overwrite cy.request() to set default options
Cypress.Commands.overwrite('request', (originalFn, ...args) => {
  const defaults = {
    headers: {
      'Accept': 'application/json'
    }
  };

  let options = {};
  if (typeof args[0] === 'object' && args[0] !== null) {
    options = args[0];
  } else if (args.length === 1) {
    [options.url] = args;
  } else if (args.length === 2) {
    [options.method, options.url] = args;
  } else if (args.length === 3) {
    [options.method, options.url, options.body] = args;
  }

  return originalFn({...defaults, ...options, ...{headers: {...defaults.headers, ...options.headers}}});
});
maneeshpal commented 3 years ago

Based on @pitpit 's cy.request() example, here is one for cy.visit()

 Cypress.Commands.overwrite('visit', (originalFn, ...args) => {
    const defaults = {
        headers: {
            'Accept': 'application/json'
        }
    };

    let options = {};
    if (typeof args[0] === 'object' && args[0] !== null) {
        // cy.visit(options)
        options = args[0];
    } else if (args.length === 1) {
        // cy.visit(url)
        [options.url] = args;
    } else if (args.length === 2) {
        // cy.visit(url, options)
        options = { ...args[1], url: args[0] };
    }

    return originalFn({...defaults, ...options, ...{headers: {...defaults.headers, ...options.headers}}});
});
uebayasi commented 3 years ago

Not tried yet, but according to the following, cy.intercept() might help.

https://docs.cypress.io/api/commands/intercept#Request-Response-Modification-with-routeHandler

spatwardhan-tcs commented 2 years ago

@jennifer-shehane Any update on this?

sruffing-te commented 1 year ago

Not tried yet, but according to the following, cy.intercept() might help.

https://docs.cypress.io/api/commands/intercept#Request-Response-Modification-with-routeHandler

https://docs.cypress.io/api/commands/request#cyintercept

cy.request does not go through intercept