I wrote a custom command to get authentication token from the window like below
Cypress.Commands.add("getToken", AUTH => {
return cy.window().then(window => window.localStorage.getItem(AUTH));
});
const authToken = JSON.parse(window.localStorage.getItem("AUTH"));
authToken = returned the authtoken. I want to know how to make`enter code here` this as
function/custom command so that the other t`enter code here`est could use this.
I suggest something like this:
describe('', () => {
let tokens = {};
it('', () => {
cy
.getToken('AUTH', ({ token }) => {
Object.assign(tokens, { auth: token });
})
.request({
headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.auth}`, }
})
});
});
Also you have to change little bit getToken command:
Cypress.Commands.add("getToken", (AUTH, cb) => {
return cy.window().then(window => cb(window.localStorage.getItem(AUTH)));
});
Related
///<reference types = "cypress"/>
describe('API Test Suite', ()=>{
it('Send GET Request',()=>{
cy.request('http://ptsv2.com/t/fu807-1554722621/post').then((res)=>{
expect(res.body).has.property('username', 'automate')
})
})
})
I'm trying to validate username field but its throwing error
expected '{\r\n "username": "automate",\r\n "password": "everything",\r\n "targetUrl": "http://ptsv2.com/t/7ty82-1554722743/post"\r\n}' to have property 'username'
SAMPLE RESULT on POSTMAN:
{
"username": "automate",
"password": "everything",
"targetUrl": "http://ptsv2.com/t/7ty82-1554722743/post"
}
The body property needs parsing - true, but there are less hacky ways of doing it
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post')
.its('body')
.then(JSON.parse)
.should('have.property', 'username', 'automate')
or
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post')
.then(res => JSON.parse(res.body))
.should('have.property', 'username', 'automate')
You can do something like this:
///<reference types = "cypress"/>
describe('API Test Suite', () => {
it('Send GET Request', () => {
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post').then(
(res) => {
const user = 'automate'
cy.wrap(res.body).should('contain', `"username": "${user}"`)
}
)
})
})
On, digging deeper into your response body, I could see that there are empty spaces and because of which it was failing. So before asserting you have to remove the empty spacing and then apply the has.property assertion.
///<reference types = "cypress"/>
describe('API Test Suite', () => {
it('Send GET Request', () => {
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post').then(
(res) => {
expect(JSON.parse(JSON.stringify(JSON.parse(res.body)))).has.property(
'username',
'automate'
)
}
)
})
})
cy.request yields the response as an object so you can access it using .its()
cy.request('your-request-url')
.its('response.body')
.should('have.property', 'username','automate')
Alternatively, you can use cy-spok to check for properties and values in a more readable code format.
const 'spoke = require('cy-spok')
// in your test
cy.request('your-request-url')
.its('response.body')
.should(spok({
username: 'automated',
password: 'everything',
//other props
})
)
Problem
I have a Cypress command where I can login with a random user. The API will return the following response:
{
user: { ... }
token: { ... }
}
What I would like to do is to:
Create user using cy.request
Set the cookie in the browser
Return the response out of the command so that I can work with it outside of the command
What I have tried
return cy.request({
method: 'POST',
url: getApiUrl('__cypress__/login'),
body: requestBody,
log: false,
})
.then(({ body }) => {
cy
.setCookie('_token', body.token.plainTextToken)
.then(() => {
Cypress.log({
name: 'login',
message: JSON.stringify(body),
consoleProps: () => ({ user: body }),
});
});
})
.its('body', { log: false }) 👈 times out here
What I'm looking for is to do something like:
cy.login().then(({ user }) => {
// use logged in user
})
Question
Cypress times out on .its(...) line. Is this possible to do it? Looking at the docs I couldn't find any example on what I'm trying to achieve
(from the comments)
It happens because previously chained subject, does not return anything. An explicit return for the body property will fix it.
I am facing a timeout issue with nestJs Httpservice.
The error number is -60 and error code is 'ETIMEDOUT'.
I am basically trying to call one api after the previous one is successfully.
Here is the first api
getUaaToken(): Observable<any> {
//uaaUrlForClient is defined
return this.httpService
.post(
uaaUrlForClient,
{ withCredentials: true },
{
auth: {
username: this.configService.get('AUTH_USERNAME'),
password: this.configService.get('AUTH_PASSWORD'),
},
},
)
.pipe(
map((axiosResponse: AxiosResponse) => {
console.log(axiosResponse);
return this.getJwtToken(axiosResponse.data.access_token).subscribe();
}),
catchError((err) => {
throw new UnauthorizedException('failed to login to uaa');
}),
);
}
Here is the second api
getJwtToken(uaaToken: string): Observable<any> {
console.log('inside jwt method', uaaToken);
const jwtSignInUrl = `${awsBaseUrl}/api/v1/auth`;
return this.httpService
.post(
jwtSignInUrl,
{ token: uaaToken },
{
headers: {
'Access-Control-Allow-Origin': '*',
'Content-type': 'Application/json',
},
},
)
.pipe(
map((axiosResponse: AxiosResponse) => {
console.log('SUCUSUCSCUSS', axiosResponse);
return axiosResponse.data;
}),
catchError((err) => {
console.log('ERRRORRRORROR', err);
// return err;
throw new UnauthorizedException('failed to login for');
}),
);
}
Both files are in the same service file. Strangely, when i call the second api through the controller like below. It works fine
#Post('/signin')
#Grafana('Get JWT', '[POST] /v1/api/auth')
signin(#Body() tokenBody: { token: string }) {
return this.authService.getJwtToken(tokenBody.token);
}
When the two api's are called, however, the first one works, the second one that is chained is giving me the timeout issue.
Any ideas?
Two things that made it work: changed the http proxy settings and used switchMap.
I'm trying to make a cypress api call and get the value to be use on a next stage api call and when i make a return it just send me a big obj of commands
the call im making is
Cypress.Commands.add('getSession', () => {
cy.request({
method: 'POST',
url: `${Cypress.env('apiURL')}/New`,
headers: {
'Client-Type': 'backend'
}
})
.its('body')
.then(json => {
return {
id: json.value.props.id,
name: json.value.props.name
}
})
})
Cypress.Commands.add('createNew', (email = Cypress.env('userEmail'), password = Cypress.env('userPassword')) => {
const session = cy.getSession()
cy.log('api respond', session.id)
cy.createMember(email, password, session.id)
})
and the response im getting is
[$Chainer] with a big obj
I'm not sure if the return on .then put it on a container but i can't find the value any when if someone can suggest what i have made wrong on the request call, that will be helpful
From the cypress docs:
So the createNew command must be rewritten to respect the async nature of cy commands.
Using then
Cypress.Commands.add('createNew', (email = Cypress.env('userEmail'), password = Cypress.env('userPassword')) => {
cy.getSession().then( session => {
cy.log('api respond', session.id)
cy.createMember(email, password, session.id)
})
})
Using aliases
Cypress.Commands.add('createNew', (email = Cypress.env('userEmail'), password = Cypress.env('userPassword')) => {
cy.getSession().as("session")
cy.get("#session").then(session => {
cy.log('api respond', session.id)
})
cy.get("#session").then(session => {
cy.createMember(email, password, session.id)
})
So I am trying to use a custom command to reduce the need to write the same thing in multiple files. Specifically this is for logging in and setting a token via JWT.
Here is the current working code (borrowed from JWT login example from cypress examples):
let user;
before(function() {
cy.request("POST", Cypress.env("auth_url"), {
username: Cypress.env("auth_username"),
password: Cypress.env("auth_password")
})
.its("body")
.then(res => {
user = res;
});
});
beforeEach(function() {
console.log(cy.get_auth_token)
cy.visit("/", {
onBeforeLoad(win) {
// set the user object in local storage
win.localStorage.setItem("token", user.token);
}
});
});
So i tried to do something similar via:
Cypress.Commands.add("get_auth_token", () => {
let user;
cy.request("POST", Cypress.env("auth_url"), {
username: Cypress.env("auth_username"),
password: Cypress.env("auth_password")
})
.its("body")
.then(res => {
user = res;
});
return user;
})
However when I try to call it within my beforeEach function as let user = cy.get_auth_token I get errors about user being undefined. Am I doing something wrong with returning the value? Im not an expert at promises...but this feels like it should be working?
Thanks!
Commands are not like functions, the return value is not assignable to a local variable. Instead they 'yield' it to the next command in the chain, which can be a then(). Also, the value is a 'subject' which is a jquery-wrapped version of the return value.
In short, this should be how you use your custom command:
beforeEach(function() {
cy.get_auth_token().then($user => {
console.log($user[0]);
cy.visit("/", {
onBeforeLoad(win) {
// set the user object in local storage
win.localStorage.setItem("token", $user[0].token);
}
});
});
});
Try to put your code inside a Promise and resolve 'user'. Using Cypress.Promise, it will wait until user is returned:
Cypress.Commands.add("get_auth_token", () => {
return new Cypress.Promise((resolve, reject) => {
cy.request("POST", Cypress.env("auth_url"), {
username: Cypress.env("auth_username"),
password: Cypress.env("auth_password")
})
.its("body")
.then(user => {
resolve(user);
});
})
})