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

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(...)
})

Related

PayPal: How to recieve an email when users send payments to my PayPal developer account ? (Using React.js )

I've integrated PayPal buttons in my website using react.js, and I want to get an email every time a user pays using that PayPal button on my website, How can I implement that?
P.S: I'm using PayPal sandbox
I want to do the following:
useEffect(() => {
window.paypal.Buttons({
createOrder: (data, actions, err) => {
return actions.order.create({
intent: "CAPTURE",
purchase_units: [
{
amount: { currency_code: "CAD", value: 50 },
},],
});
}, onApprove: async (data, actions) => {
setPaid(true);
setCompleted(true);
// *THEN SEND ME AN EMAIL THAT A PAYMENT HAS BEEN DONE*
},
onError: (err) => {
setCompleted(true);
console.log(err);
},
})
.render(paypal.current)
}, []);
The actual email should be sent from a backend.
Follow the PayPal Checkout integration guide and make 2 routes on your backend, one for 'Create Order' and one for 'Capture Order'. Both of these routes should return only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you can store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as sending emails or reserving product) immediately before forwarding your return JSON to the frontend caller (which will be react in your case).
For the frontend approval flow, instead of your own useEffect implementation in your question, consider the official #paypal/react-paypal-js. Give it createOrder and onApprove function parameters that call your backend (likely using fetch). For examples of such functions, see the full stack example in the integration guide or the one at https://developer.paypal.com/demo/checkout/#/pattern/server (currently this latter one is the best as it shows how to handle 3 different cases for the capture response)

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.

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

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);
});

how to stub request.get three times? with 2 same url and 1 different url?

I need to write unit testcase for following piece of code. Here urls are external api calls, so I need to mock these. For rest of the testcases I have used sinon. So would like to write these in sinon only.
function 1 {
async.waterfall() {
function 2();
function 3();
}
}
function 2(callback) {
url2_1 = "/calculatorFunctionList"
request.get(url2_1,function (err, res, body) {
return callback("with some diff response")
})}
function 3(callback) {
url3_1 = "addition/values="+somevalue1
url3_2 = "addition/values="+somevalue2
request.get(url3_1,function (err, res, body) {
if(!err) {
request.get(url3_2, function (err, res, body) {
return callback("with some response")
})
}
})
}
I need to stub these requests. How can I do this? Also how and when to call restore for stub so that it won't mess up with other request stub? Currently both stubs are not getting mocked.
Sinon - at its core (forget lolex and nise for a moment) - explicitly deals with stubbing and spying on javascript functions. That's it. It does not concern itself with module loaders or Node per se.
So it does not know how to hook into Node's module loader (require) and stub out dependencies by itself. For the general case, you have two options:
Supply your module (the System-Under-Test) with injection points (say, a function setRequestModule(module){ request = module }, which could be used to supply a Sinon stub.
Use a link-level to intercept calls to the module system to load dependencies and swap them out. See this article on Sinon's how-to section for how to do this.
That was the general case, but since you are dealing with network level stubbing, there is a third option, which is related to the second one, which involves hooking into the http layer in Node and setting up fake requests and responses there using nock.
This would look like this:
const nock = require('nock')
const scope = nock('https://api.github.com')
.get('/repos/atom/atom/license')
.reply(200, {
license: {
key: 'mit',
name: 'MIT License',
spdx_id: 'MIT',
url: 'https://api.github.com/licenses/mit',
node_id: 'MDc6TGljZW5zZTEz',
},
})
This is what I would normally do, but since you insist on only using Sinon you are left with the first option.
Elaboration of the first option: inject dependencies
Your functions 2 and 3 (invalid javascript identifiers, btw) are both dependant on the module request and its method get to have their results. To be able to control what is returned by request.get you would then need to supply your module with a different version of request for your tests. This can be done using dependency injection.
To start with, I assume your function starts with an import of the request module, meaning the first few lines includes a statement looking like this:
const request = require('request');
You would need to change this statement so that the request constant can be replaced, meaning it would now look like this:
let request = require('request');
To be able to control your dependency you can now add a new function to your exports:
module.exports.setRequestModule = function (stub){
request = stub;
}
To use this in your tests you would then need to do this in your tests:
const myModule = require('../src/my-module');
const myRequestStub = {};
myModule.setRequestModule(myRequestStub);
it('should return the expected response', () => {
const expectedOutput = "with some response";
myRequestStub.get = function (url, callback) {
callback(null, expectedOutput);
}
myModule.doSomething() ...
// do test
});
Alternatively
You could also expose the request object in your module:
module.exports._request = request;
You could then, for instance, use sinon to instrument it by stubbing the get method from your tests.
const myModule = require('../src/my-module');
sinon.stub(myModule._request, 'get').callsFake( (url, cb) => cb("hello!") );

Is it possible to make a pseudo-global promise rejection handler?

fetch() is a great improvement over the classic XMLhttpRequest() However I was wondering if I add window.addEventListener('unhandledrejection', event => ยทยทยท); to my index.android.js and index.ios.js could I theoretically remove all of my .catch() from my promise chains and make a pseudo global promise rejection handler? I am building a react native app that relies heavily on api calls, many of which are authenticated with user tokens. When a user logs in a token is stored in asynchronous storage, but each token will expire after a certain amount of time. if a user opens the app and unwittingly attempts to make a call that requires authentication with an expired token I would like the user to be routed to the login page regardless of what call was made, or what component the call is in. I currently have each individual promise chain handling unauthenticated calls on their own. Is there a better way to do it?
You could make a custom abstraction on top of fetch, which will provide a way of communicating with API and also have some business logic, which for you case is to request an auth token or make a redirect. Then you use this abstraction in the rest of the code to make API calls. Here is a small example to demonstrate the idea:
export default function request(url, options = {}) {
const req = fetch(url)
.then((response) => {
// Global response handler
// ...
// Hand response down the promise chain
return response;
})
.catch((err) => {
// Global error handler
// Check error status and (make redirect/request auth token)
// Throw error down the promise chain if needed
throw err;
});
// Returns a promise
return req;
}
Then you use it in your code like fetch(), but instead you'll be able to modify and extend it for you needs any time.
request('http://example.com/api/post/1')
.then((response) => {
// "response" has whatever you returned from global handler
})
.catch((err) => {
// "err" is whatever you've thrown from global handler
});

Resources