Sinon fakeServer with mocha and axios - mocha.js

I'm trying to get sinon.fakeServer to make axios return a faked response. Instead of returning the mocked payload, I can see the network request 404s or does a timeout trying to go to the actual URL.
My setup:
describe('test call', () => {
var server;
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondWith(
"https://my.domain.com/myresource",
[200, { "Content-Type": "application/json" }, "[]"]
);
server.autoRespond = true
});
it('returns empty array', done => {
axios
.get('https://my.domain.com/myresource')
.then(res => {
expect(true).to.equal(true);
done()
})
.catch(err=>{
console.log(err.message);
expect(false).to.equal(true);
done();
});
});
afterEach(() => {
server.restore();
});
})

It seems that your execution environment is NodeJS, even though it's not mentioned. Others had the same issue - have a look here.
Also the Sinon team mentions that it's outside their scope since XHR are supposed to work correctly in the browser, where their fake server works as expected as it stubs the XHR object.
Axios is using a different library for making requests when running on the server, so this scenario cannot work by default. There are specific mocking libs for axios like moxios as an alternative.

Related

How do I log a specific field in API requests within Cypress

I want to cypress.log() out a specific field in the request header whenever my webapp makes requests that way when it fails and adds screenshots/logs I can grab that that requestId that failed.
Is there a way to setup cypress so that for all network requests it checks for this field and log it?
I can add a cy.intercept within each individual file but I want a more generic way to handle this.
Cypress.log is the synchronous version of cy.log().
Add middleware: true to the intercept to pass request to other intercepts.
cy.intercept({ url: '*', middleware: true }, (req) => {
const headerValue = req.headers?['x-custom-headers']
if (headerValue) {
Cypress.log({
name: 'x-custom-header',
message: headerValue
})
}
})
You'll get an Cypress promise error if you try to use cy.log() to log out every request header in an cy.intercept() within a routeHandler callback. This would also make it kind of tough to log to a CI terminal as well.
Instead you can console.log to dev tools. To make it apply to all tests, you can wrap it in a beforeEach() and place it in the support/index.js file.
// support/index.js
beforeEach(() => {
cy.intercept('*', (req) => {
req.continue((res) => {
console.log(JSON.stringify(req.headers))
})
})
})

Mock Graphql server with multiple stubs in Cypress

Problem:
I’m using cypress with angular and apollo graphQl. I’m trying to mock the graph server so I write my tests using custom responses. The issue here is that all graph calls go on a single endpoint and that cypress doesn’t have default full network support yet to distinguish between these calls.
An example scenario would be:
access /accounts/account123
when the api is hit two graph calls are sent out - a query getAccountDetails and another one with getVehicles
Tried:
Using one stub of the graph endpoint per test. Not working as it stubs with the same stub all calls.
Changing the app such that the query is appended 'on the go' to the url where I can intercept it in cypress and therefore have a unique url for each query. Not possible to change the app.
My only bet seems to be intercepting the XHR call and using this, but I don't seem to be able to get it working Tried all options using XHR outlined here but to no luck (it picks only the stub declared last and uses that for all calls) https://github.com/cypress-io/cypress-documentation/issues/122.
The answer from this question uses Fetch and therefore doesn't apply:
Mock specific graphql request in cypress when running e2e tests
Anyone got any ideas?
With cypress 6.0 route and route2 are deprecated, suggesting the use of intercept. As written in the docs (https://docs.cypress.io/api/commands/intercept.html#Aliasing-individual-GraphQL-requests) you can mock the GraphQL requests in this way:
cy.intercept('POST', '/api', (req) => {
if (req.body.operationName === 'operationName') {
req.reply({ fixture: 'mockData.json'});
}
For anyone else hitting this issue, there is a working solution with the new cypress release using cy.route2()
The requests are sent to the server but the responses are stubbed/ altered on return.
Later Edit:
Noticed that the code version below doesn't alter the status code. If you need this, I'd recommend the version I left as a comment below.
Example code:
describe('account details', () => {
it('should display the account details correctly', () => {
cy.route2(graphEndpoint, (req) => {
let body = req.body;
if (body == getAccountDetailsQuery) {
req.reply((res) => {
res.body = getAccountDetailsResponse,
res.status = 200
});
} else if (body == getVehiclesQuery) {
req.reply((res) => {
res.body = getVehiclesResponse,
res.status = 200
});
}
}).as('accountStub');
cy.visit('/accounts/account123').wait('#accountStub');
});
});
Both your query and response should be in string format.
This is the cy command I'm using:
import * as hash from 'object-hash';
Cypress.Commands.add('stubRequest', ({ request, response, alias }) => {
const previousInteceptions = Cypress.config('interceptions');
const expectedKey = hash(
JSON.parse(
JSON.stringify({
query: request.query,
variables: request.variables,
}),
),
);
if (!(previousInteceptions || {})[expectedKey]) {
Cypress.config('interceptions', {
...(previousInteceptions || {}),
[expectedKey]: { alias, response },
});
}
cy.intercept('POST', '/api', (req) => {
const interceptions = Cypress.config('interceptions');
const receivedKey = hash(
JSON.parse(
JSON.stringify({
query: req.body.query,
variables: { ...req.body.variables },
}),
),
);
const match = interceptions[receivedKey];
if (match) {
req.alias = match.alias;
req.reply({ body: match.response });
}
});
});
With that is posible to stub exact request queries and variables:
import { MUTATION_LOGIN } from 'src/services/Auth';
...
cy.stubRequest({
request: {
query: MUTATION_LOGIN,
variables: {
loginInput: { email: 'test#user.com', password: 'test#user.com' },
},
},
response: {
data: {
login: {
accessToken: 'Bearer FakeToken',
user: {
username: 'Fake Username',
email: 'test#user.com',
},
},
},
});
...
Cypress.config is what make it possible, it is kind of a global key/val getter/setter in tests which I'm using to store interceptions with expected requests hash and fake responses
This helped me https://www.autoscripts.net/stubbing-in-cypress/
But I'm not sure where the original source is
A "fix" that I use is to create multiple aliases, with different names, on the same route, with wait on the alias between the different names, as many as requests you have.
I guess you can use aliases as already suggested in Answer by #Luis above like this. This is given in documentation too. Only thing you need to use here is multiple aliases as you have multiple calls and have to manage the sequence between them . Please correct me if i understood you question in other way ??
cy.route({
method: 'POST',
url: 'abc/*',
status: 200.
response: {whatever response is needed in mock }
}).as('mockAPI')
// HERE YOU SHOULD WAIT till the mockAPI is resolved.
cy.wait('#mockAPI')

How to get response headers from RxJS's ajax?

I am creating new frontend for an interview system. Some its API endpoints is updated, so getting pagination info is not a problem, but old ones still have pagination data inside response headers.
P.S. we are using react, redux and redux-observable
RxJS has the following call:
ajax({ ...params }).pipe(
map(response => {
// here I need to somehow get headers from ajax response
}),
catchError(errorResponse => {
// return error
})
)
I've been looking for the same answer, looks like there is a way (See: https://stackblitz.com/edit/typescript-k2ggm2?file=index.ts):
ajax({ ...params }).pipe(
map(response => {
// here I need to somehow get headers from ajax response
console.log(response.xhr.getAllResponseHeaders())
console.log(response.xhr.getResponseHeader('pragma'))
}),
catchError(errorResponse => {
// return error
})
)

Mocha chai request and express-session

When using two nested chai requests, session get lost.
chai.request(server)
.post('/api/v1/account/login')
.send({_email: 'test#test.com', _password: 'testtest'})
.end(function(err, res){
chai.request(server)
.get('/api/v1/user/me')
.end(function(err2, res2){
//here i should get the session, but its empty
res2.should.have.status(200);
done();
});
});
And i'm pretty sure that it's an error in my mocha test, because i tried it (the login and then retrieving the session) outside the test and the session is being setted.
express itself does not have any native session support. I guess you are using some session middleware such as https://github.com/expressjs/session.
Meanwhile, I guess you are using chai-http plugin to send HTTP request. In chai-http, in order to retain cookies between different HTTP requests (so that req.session can be available in express side), you need to use chai.request.agent rather than chai.
Here is a simple example for your code:
var agent = chai.request.agent(app);
agent.post('/api/v1/account/login')
.send({_email: 'test#test.com', _password: 'testtest'})
.then(function(res){
agent.get('/api/v1/user/me')
.then(function(res2){
// should get status 200, which indicates req.session existence.
res2.should.have.status(200);
done();
});
});
For chai.request.agent, you can refer to http://chaijs.com/plugins/chai-http/#retaining-cookies-with-each-request
In case anyone else comes across this issue, this approach worked for me using Mocha:
it("should...", () => {
return agent.post('/api/v1/account/login')
.send({_email: 'test#test.com', _password: 'testtest'})
.then(async res => {
const res2 = await agent.get('/api/v1/user/me')
res2.should.have.status(200);
})
.catch(error => {
throw error;
});
});

How to make Ajax request through NodeJS to an endpoint

I am using NodeJS. One of my function (lets call it funcOne) receives some input which I pass to another function (lets call it funcTwo) which produces some output.
Before I pass the input to funcTwo I need to make an Ajax call to an endpoint passing the input and then I must pass the output produced by the AJAX call to funcTwo. funcTwo should be called only when the AJAX call is successful.
How can I achieve this in NodeJS. I wonder if Q Library can be utilized in this case
Using request
function funcOne(input) {
var request = require('request');
request.post(someUrl, {json: true, body: input}, function(err, res, body) {
if (!err && res.statusCode === 200) {
funcTwo(body, function(err, output) {
console.log(err, output);
});
}
});
}
function funcTwo(input, callback) {
// process input
callback(null, input);
}
Edit: Since request is now deprecated you can find alternatives here
Since request is deprecated. I recommend working with axios.
npm install axios#0.16.2
const axios = require('axios');
axios.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
.then(response => {
console.log(response.data.url);
console.log(response.data.explanation);
})
.catch(error => {
console.log(error);
});
Using the standard http library to make requests will require more effort to parse/get data. For someone who was used to making AJAX request purely in Java/JavaScript I found axios to be easy to pick up.
https://www.twilio.com/blog/2017/08/http-requests-in-node-js.html

Resources