How to stub an incoming http request (response) with cypress - javascript

How can I stub a response of a HTTP request?
Let me explain it with my code I have now:
Cypress.Commands.add("FakeLoginWithMsal", (userId) => {
cy.intercept('**/oauth2/v2.0/token', (req) => {
req.reply({
token_type: "Bearer",
expires_in: 3795,
access_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJS"
})
req.continue((res) => {
})
})
With this code I am trying to stub the response for the following request:
But it still gives the following error, where I can understand the stub did not work:
We attempted to make an http request to this URL but the request
failed without a response.
I've tried already different intercept methods of cypress but I couldn't get worked.
I even can't intercept the /token endpoint with the following:
cy.intercept({
method: 'POST',
url: 'https://login.microsoftonline.com/xx-xx-xx-xx-/oauth2/v2.0/token',
}).as('apiCheck')
Update:
#Fody Thankyou vey much (again) for you respond. Actually I am trying to stub all the MSAL endpoints. It is not a testscript, but a login command.
Here it is:
Cypress.Commands.add("FakeLoginWithMsal", (userId) => {
cy.intercept('GET', '**/authorize', { fixture: 'authorizeStub.json' })
cy.intercept('GET', '**/v2.0/.well-known/openid-configuration', { fixture: 'openidConfigurationStub.json' })
cy.request({
method: 'POST',
url: 'https://login.microsoftonline.com/xxxxx/oauth2/v2.0/token',
body: {
grant_type: "password",
client_id: "xxxxxxxxxxxxx",
client_secret: "xxxxxxxxxxx",
scope: "api://xxxxxxxxxxxxxx/Cp.Use",
username: "xxx#xxxx.com",
password: "xxxxx",
},
form: true,
}).then(response => {
cy.log(JSON.stringify(response))
cy.intercept('response', { fixture: 'tokenStub.json' })
})
These are 3 endpoints, namely:
GET: /authorize (stubbed with a fixture)
GET: /openid-configuration (stubbed with a fixture)
Post: /token --> This POST has a response and there inside is the accesstoken. This response I need to stub.
And I guess that this response is a "incoming HTTP request" (see attachments). This incoming http response is exactly what I want to stub in a fixture.

I'm not sure without seeing the whole test, but are you are issuing the POST to microsoftonline from within the test using cy.request()?
If so, you can't use cy.intercept() to catch it, only requests from the app will be caught.
But you can append a .then() to the cy.request() to wait for the response.
cy.request({
method: 'POST',
url: 'https://login.microsoftonline.com/.../oauth2/v2.0/token',
})
.then(response => {
// handle response
})
Also in this code req.reply() and req.continue() you are both stubbing (with reply) and continuing to the server (with continue), which are opposite actions. You would only want to do one or the other.
cy.intercept('**/oauth2/v2.0/token', (req) => {
req.reply({
token_type: "Bearer",
expires_in: 3795,
access_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJS"
})
req.continue((res) => {
})
})

Related

Fastify inject test with jest timing out when sending text/plain body

I have the following basic fastify server:
import Fastify from 'fastify';
import expressPlugin from 'fastify-express';
import cors from 'cors';
async function build() {
const fastify = Fastify();
await fastify.register(expressPlugin);
fastify.use(cors());
fastify.post(
'/someurl',
async (request, reply) => {
console.log('PATATAPATATA');
void reply.send({message: "Hey, I'm stuck :D"})
}
);
return fastify;
}
And then I have the following test:
it.only('Should reply with a 200 status code when sending a string body', async () => {
// after this line, jest gives a timeout (5000 ms)
const response = await getResponse({
headers: {
Accept: 'application/json',
'Content-Type': 'text/plain',
'Content-Length': 1,
},
payload: 'a',
});
expect(response.statusCode).toEqual(200);
});
async function getResponse(options?: InjectOptions) {
const app = await build();
return app.inject({
method: 'POST',
url: `/someurl`,
...options,
});
}
The content-length header should not be needed, but I have added it after trying different things to fix it. If you delete it you get the timeout anyway. Any idea why this is happening? If I delete the content-type header then I get a 415 http error.
Side note: the code console.log('PATATAPATATA'); is never executed, so the problem has to be in some fastify internals or the light my request module.
Update It seems this only happens when I use payload in the inject options, like this:
async function getResponse(options?: InjectOptions) {
const app = await build();
return app.inject({
method: 'POST',
url: `/someurl`,
payload: {},
});
}
I have also discovered that:
Using payload: '' works.
Using payload: '{}' does not work. It does not give a timeout, but it does not run the post method either.
Using payload: '{}' and content-type set to application/json or text/plain, also gives the timeout.
It seems fastify-express it's not compatible with inject -> https://github.com/fastify/fastify/issues/3739

How to catch 3rd party api error response using NestJS' httpService?

Suppose i make a request using httpService like this
const response = await this.httpService
.request({
url: 'https://example.com/data',
method: 'POST',
})
.toPromise()
.catch(error => console.log(error))
Assume that example.com/data api sends a message like "Cannot send data without specifying user id." on this request (if i make this request via curl)
Now, if i use httpService like in the codeblock above i get this ambiguous message:
Error: Request failed with status code 400 at createError (/workspace/node_modules/axios/lib/core/createError.js:16:15) at settle (/workspace/node_modules/axios/lib/core/settle.js:17:12) at IncomingMessage.handleStreamEnd (/workspace/node_modules/axios/lib/adapters/http.js:236:11) at IncomingMessage.emit (events.js:203:15) at endReadableNT (_stream_readable.js:1145:12) at process._tickCallback (internal/process/next_tick.js:63:19)"
but if i write the catch statement like this
.catch(error => {
const errorLog = _.pick(error.response, [
'status',
'statusText',
'data',
]);
this.logger.error(
util.inspect(
{
...errorLog,
user: { id: user.id },
},
{ showHidden: false, depth: null },
),
);
throw new InternalServerErrorException();
});
I'll get the 3rd party api response in my logs "Cannot send data without specifying user id."
So, is there a way to make the httpService behave this way by default? like an interceptor or something?
You can try like this:
const response = await this.httpService
.request({
url: 'https://example.com/data',
method: 'POST',
}).pipe(
catchError((e) => {
throw new HttpException(e.response.data, e.response.status);
})
)

Bearer auth token stored in localstorage passed in multiple API calls (Cypress)

I'm trying to use the bearer auth token received from a Cypress command in some tests but I'm getting an error
Cypress error
Here is my command code:
Cypress.Commands.add("getToken", () => {
cy.request({
method: 'POST',
url: 'https://login.microsoftonline.com/abcc1234-abc12345-abcc1234-abc12345/oauth2/v2.0/token',
form: true,
body: {
'client_id': 'abcc1234-abc12345-abcc1234-abc12345abcc1234-abc12345/'
'client_secret' : 'J2Kq.XXXXXXXX.Xt_-XXXXXX',
'grant_type': 'client_credentials'
}
})
.then((response) =>{
const tokenAuth = response.body.access_token;
window.localStorage.setItem('auth', tokenAuth);
And here is my test:
describe('API Testing', ()=>{
beforeEach(() => {
cy.getToken()
})
it('Connector - GET', () =>{
let a = "Bearer " + localStorage.getItem('auth')
cy.log(a);
cy.request({
method:'GET',
url:'https://XXXX-XXXXX-XXXXXXXXX.azurewebsites.net/api/v1/connectors/cost-max-and-power-intervals',
auth : 'a'
})
.then((response) =>{
expect(response).to.have.property('status', 200)
expect(response.body).to.not.be.null
expect(response.body).to.have.property('costMax', 35)
})
})
I've also tried to set a header in request but without luck. :(
Thank you in advance!
Looks like I've made a confusion, auth is not the same with Autorization. I managed to make it work by replacing auth with Autorization : a

Are ongoing fetch requests cancelled after changing window.location?

I noticed that some fetch requests don't reach the server when I change window.location just after sending the request (location change is not inside the promise response handler).
I tested on Chrome browser
# util.js
function sendLog(payload) {
const urlApi = `http://api.example.com/public/send-message`;
return fetch(urlApi, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}).then(response => {
return response.json();
});
}
# main.js
sendLog({
project: 'FrontEnd-Card',
countryId: country
}).then(res => {
console.log(res);
});
window.location.replace(REDIRECT_URL);
I noticed this pattern:
If network is fast, the log is successfully sent to the server
If network is slow, the log doesn't reach the server.
Somehow redirection kills the outgoing request. Is this true ? Where can I find a detailed documentation about this behavior ?

Axios: Send variable in the body of a POST request and not params

I am working on a project where i need to send authentication variables in the body of the request and not as parameters. I see that a POST request sends the second parameter as the body in the documentation but I am getting an error in the .NET service.
_response: "{"error":"invalid_clientId","error_description":"ClientId should be sent."}"
I was getting the same error in PHP when I wasn't send the params in the body but the values are all the same that I am using in this POST request so I know the params and values are correct.
Here is what I have:
axios.post(`https://myawesomeapi.com`,
{username:this.state.email_address, password:this.state.password, grant_type:'password', client_id:'thisAppsClientID'},
{headers: { 'content-type':'application/x-www-form-urlencoded', 'accept':'application/json' }}
).then( res => {
let result = res.data;
console.log(result);
})
.catch( (e) => { console.log(e.response); });
When I console.log and inspect the error response I see this:
config:
adapter: ƒ xhrAdapter(config)
data: "{"username":"myusername","password":"mypassword!","grant_type":"password","client_id":"thisAppsClientID"}"
headers: {Accept: "application/json, text/plain, */*", Content-Type: "application/x-www-form-urlencoded"}
...
Here is what I have in guzzle that is working if that helps:
$this->client->request('post', 'https://myawesomeapi.com',
['form_params' => ['username' => $input['username'], 'password' => $input['password'], 'client_id'=>'thisAppsClientID', 'grant_type'=>'password']],
['headers' => ['accept' => 'application/json','Content-Type' => 'application/x-www-form-urlencoded']]);
Is the username, password, grant_type, and client_id being passed as the body? Did I mess up how to send the headers? Thanks and let me know!
I know I had similar problems with axios that I never quite figured out. However, I was able to get post requests to work with Form Data. Try the snippet below. I hope it works.
const dataForm = new FormData();
dataForm.append('username', this.state.email_address);
dataForm.append('password', this.state.password);
dataForm.append('grant_type', 'password');
dataForm.append('client_id', 'thisAppsClientID');
axios.post('https://myawesomeapi.com', dataForm)
.then(res => {
let result = res.data;
console.log(result);
})
.catch(e => {
console.log(e.response);
});

Categories

Resources