When attempting to stub a request in Cypress IO (JS) against a React app using fetch, the requests still call through to the server - reactjs

I'm using Cypress to create some specs against my React app. My react app uses fetch to fetch data from an external api (isomorphic-fetch)
The fetch requests in my app are like so
import fetch from 'fetch'
...
fetch('http://www.external-server.com/ideas.json')
.then((response) => {
if (response.status >= 400) {
}
return response.json().then((result) => {
this._data = result
this._data((ele) => ele.key = ele.id)
});
})
In my Cypress specs, I want my regular specs to hit my lcoahost:3000 to get the initial page (which houses my React app). My react app in turn would normally make an external request (http://www.external-server.com/ideas.json) but in my specs I want to stub out that request and have that endpoint return fake data in my specs only.
The Cypress docs for cy.route() here, describe that I should be able to do something like
cy.server()
cy.route('http://www.external-server.com/ideas.json', [
{
id: 1,
name: 'john'
}
])
I attempted to put this into a beforeEach that runs in the context of my spec (thus running before every spec).
You will note that when I run the specs in the Cypress test running, it appears in the console output that the endpoint SHOULD be stubbed.
However, by examination, I can see that my app is in fact making the request to the real server, and calling the real endpoint (not stubbing it).
I tested several times and I am certain this is the behavior.
I found a solution online I will post below to answer my question

the solution is add to cypress/support/commands.js
this small hack will turn the window fetch into a no-op (disabling it) and will allow the native stubbing in Cypress to work without any alterations.
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
const opts = Object.assign({}, options = {}, {
onBeforeLoad: (window, ...args) => {
window.fetch = null;
if (options.onBeforeLoad) {
return options.onBeforeLoad(window, ...args);
}
},
});
return originalFn(url, opts);
});

Related

Cypress stubbing XHR response based on request

I am beginer in Cypress and looking for help with network stubbing.
My UI tiggers 3 API calls concurrently upon clicking on a button in the UI. All 3 API are of same endpoint, BUT each of them have different request and response.
I am able to stub the json response using cy.fixture, cy.server() and cy.route().
My need is to 'only stub the 3rd XHR call response', but, my test stubs all three because of the same endpoint.
Any suggessions on how could I test it using any condition ? example - Only stub the call if the parameters of 'request'XHR is 'XXX'?
I tried using before and after the .click() of submit button but that didn't work.
cy.fixture('myfixture').then(jsonresponse => {
function FixtureController(request, response) {
if (cy.url().request.body.contains("XXX")) {
cy.server()
cy.route('POST', 'URL', jsonresponse).as('myalias')
I appreciate any support.
Thanks!
You can use cy.intercept to match based on several things, including query parameters.
cy.intercept({
url: 'http://example.com/search*',
query: { q: 'expected terms' },
}, { fixture: 'myfixture' } )
If you need to match on the request body contents, you can use a route handler to specify.
cy.intercept('http://example.com/search*', (req) => {
if (req.body.contains('some string') {
req.reply({ statusCode: 200, fixture: 'myfixture' });
} else {
req.reply(); // not providing any input forwards the request as normal
}
});
Check out the cy.intercept documentation for more info.

How to make assertions on graphql query variables when using Mock Service Worker?

When we are mocking out a graphql query with a mock service worker (MSW), we want to assert that the variables passed to the query have certain values. This goes beyond the type validation with the typescript typings. We are using jest with MSW. Do you spy on MSW to make those assertions? or is there another way to expect req.variables to have a certain value.
graphql.query<SaveContent, SaveContentVariables>('SaveContent', (req, res, ctx) => {
return res(
ctx.data({
saveContent: {
success: true,
id: req.variables.id,
errors: [],
},
})
);
})
Mock Service Worker recommends basing your request assertions on the UI (read more in the Request assertions recipe). In most cases, if your request/response data is correct, then your UI would be correct in the test. The same is true for the opposite scenario. Always assert the data-driven UI, when you can.
In your case, you wish to assert the query variables in a request. Consider returning data based on those variables that later result in a corresponding UI.
When you find it absolutely necessary to perform direct request/response assertions apart from the UI, use the Life-cycle events that allow executing arbitrary logic in response to various MSW events. For example, this is how you can assert request variables in your test:
const server = setupServer(...handlers)
it('saves the content', async () => {
expect.assertions(1)
server.on('request:match', (req) => {
expect(req.variables).toEqual({ id: 'abc-123' })
})
await performQuery(...)
})

How can I access Google Fit data without logging in

I have a web page I am working on, I am wanting to use gapi to access my steps for the day. I do not want to log in every time, I want the page to log in automatically in the background, oAuth2 is required I believe but I can not get any further.
Based on code I've found online and using React with Hooks, I added ts ignore because it could not resolve gapi.
The issue I am having is that I get Login Required error
I've tried using Axios and doing it by API_KEY only. My knowledge on API's is growing but I thought just to access the data an API key would be enough providing I had registered the key in the API tools.
React.useEffect(() => {
const start = () => {
// 2. Initialize the JavaScript client library.
// #ts-ignore
gapi.client.init({
'apiKey': '<API_KEY>',
// clientId and scope are optional if auth is not required.
'clientId': '<CLIENT_ID>.apps.googleusercontent.com',
}).then(function() {
// 3. Initialize and make the API request.
// #ts-ignore
return gapi.client.request({
'path': 'https://www.googleapis.com/fitness/v1/users/<MY_GMAIL_ADDRESS>/dataset:aggregate',
'method': 'POST',
'body': {
"aggregateBy": [{
"dataTypeName": "com.google.step_count.delta",
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}],
"bucketByTime": { "durationMillis": 86400000 },
"startTimeMillis": 1567983600000,
"endTimeMillis": 1568057160150
},
})
}).then(function(response) {
console.log(response.result);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// #ts-ignore
gapi.load('client', start);
}, []);
I just want to be able to return my steps in a JSON object preferably with Axios, if not using the gapi JS library using my React Application. Nothing fancy.
I appreciate this may not be possible but the docs and other questions on stack overflow just are not working for me and no one can answer me.

How do I mock whatwg-fetch for testing in Jest?

I have a component that fetches some data in its componentDidMount function. The function that fetches the data is in fetch-data.js. I am trying to test this component, and want to mock the fetched data. I am trying to follow this link in mocking the fetch function for tests: http://facebook.github.io/jest/docs/en/manual-mocks.html
I have my __mocks__ folder adjacent to my node_modules folder:
...
node_modules
__mocks__
└── whatwg-fetch.js
...
my whatwg-fetch.js mock looks like this:
const whatwgFetch = jest.genMockFromModule('whatwg-fetch');
whatwgFetch.fetch = (url) => jest.fn().mockImplementation(() => Promise.resolve({ ok: true }));
module.exports = whatwgFetch;
However, when I run tests, I still get this error:
RUNS src/scenes/Home/Home.spec.js
myproject/node_modules/react-scripts/scripts/test.js:20
throw err;
^
TypeError: Network request failed
at XMLHttpRequest.xhr.onerror (/Local/Users/john/Documents/js/react/haiku/node_modules/whatwg-fetch/fetch.js:436:16)
The XHR request in question is not going to a third party, but is a local URL (fetch('/my/local/file')), if that makes any difference.
edit: It seems like even if there is nothing in the component tests, I get the same error. Everything is commented out in MyComponent.spec.js and it seems to still try and make network requests. Anyone know why?
So, I had installed jest-fetch-mock and added these lines to my src/setupTests.js:
global.fetch = require('jest-fetch-mock');
fetch.mockResponse(JSON.stringify({ testing: true }));
and now the tests run.
If we want to mock fetch in only one file then we could test it by spying on the window fetch and give our implementation (mock one) and test it.
//needs fetch polyfill to be loaded in ths test file
import 'whatwg-fetch';
let windowFetchSpy;
beforeEach(() => {
//spyon read only window fetch
windowFetchSpy = jest.spyOn(window, 'fetch');
});
afterEach(() => {
// store back the original implementation of window fetch
windowFetchSpy = jest.mockRestore();
})
test('window fetch',() => {
// set the behavior of mock fetch (either resolve or reject)
windowFetchSpy.mockResolvedValueOnce({
ok: true,
json: async () => ({success: true}),
});
expect(windowFetchFetch).toHaveBeenCalledTimes(1);
});

Complete E2E test of form in AngularJs 1.5.5

my goal is to create an automatic E2E test that fills a form and clicks on the "send" button. After that I need to wait the mock api response before set the test as Passed or not.
First question: Is this possible in the same E2E test?
I'm using AngularJs 1.5.5, ngMocks and Protractor\Jasmine.
Right now I'm able to fill the form using
element(by.model('xxxx')).sendKeys('xxxx');
and click on the "Send" button using
element(by.css('input[type="button"]')).click();
I created also an api mock for this test in a new js file that I included in page. The code is the following:
var testAppDEV = angular.module('testAppDEV', ['testApp', 'ngMockE2E']);
testAppDEV.run(function ($httpBackend) {
// Api to save the form
$httpBackend.whenPOST('/api/save').respond(function (method, url, data) {
var formData = angular.fromJson(data);
if (Object.keys(formData).length == 3) {
// url, data, headers
return [200, { errorCode: null }, {}];
}
else
{
return [400, { errorCode: '1' }, {}];
}
});
});
Now I need to add the code to my Jasmine test that wait for the mock api response and according to response, set the test as Passed or Failed.
Can you help me?
This is a first step because the next will be to integrate this test with Jenkins, like I did with Unit tests..
Every hint is welcome :)

Resources