Set cookies and return user on Cypress request - cypress

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.

Related

NestJs Timeout issue with HttpService

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 can't return a response value on my cypress api call

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

Cypress: Returning value in promise within custom command?

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

Cypress sharing variables/alias between Hooks?

So I have a pretty `before` and `beforeEach` function that runs before all tests. It looks something like this:
describe("JWT Authentication", function() {
before(function() {
// custom command runs once to get token for JWT auth
// alias token as 'user' for further use
cy.get_auth_token().as('user')
})
beforeEach(function() {
// before each page load, set the JWT to the aliased 'user' token
cy.visit("/", {
onBeforeLoad(win) {
// set the user object in local storage
win.localStorage.setItem("token", this.user.token);
}
})
})
it("a single test...", function() {
//do stuff
});
The custom command is also pretty simple:
Cypress.Commands.add("get_auth_token", () => {
cy.request("POST", Cypress.env("auth_url"), {
username: Cypress.env("auth_username"),
password: Cypress.env("auth_password")
})
.its("body")
.then(res => {
return res;
});
})
The custom command itself works and retrieves the token as expected. However when it comes to the beforeEach it has no idea what this.user.token is. Specifically not knowing what user is.
One option is of course calling the command in every beforeEach which is what the JWT Cypress recipe/example spec does. However this feels excessive because in my case I do not NEED to grab the token every test. I only need to grab it once for this set of tests.
So how can I share the token to the beforeEach hook with a Cypress custom command.
I ran a few tests, all the bits seem to work!
The following does not give you an answer, but may help you debug.
Passing token between before() and beforeEach()
Assume we have a user in before(), does it get to the onBeforeLoad() callback?
describe("JWT Authentication", function() {
before(function() {
const mockUser = { token: 'xyz' };
cy.wrap(mockUser).as('user');
})
beforeEach(function() {
cy.visit("http://example.com", {
onBeforeLoad(win) {
console.log(this.user.title); // prints 'xyz'
}
})
})
it("a single test...", function() {
//do stuff
})
});
Is the custom command working
I can't find a generic mock for an Auth check, but any cy.request() that gets an object should be equivalent.
I'm hitting typicode.com and looking for the title property
describe("JWT Authentication", function() {
Cypress.Commands.add("get_auth_token", () => {
cy.request("GET", 'https://jsonplaceholder.typicode.com/todos/1')
.its("body")
.then(body => {
console.log('body', body); // prints {userId: 1, id: 1, title: "delectus aut autem", completed: false}
return body;
});
})
before(function() {
cy.get_auth_token()
.then(user => console.log('user', user)) // prints {userId: 1, id: 1, title: "delectus aut autem", completed: false}
.as('user')
})
beforeEach(function() {
cy.visit("http://example.com", {
onBeforeLoad(win) {
console.log(this.user.title); // prints 'delectus aut autem'
}
})
})
it("a single test...", function() {
//do stuff
})
});
Custom command
This shorter version also seems to work
Cypress.Commands.add("get_auth_token", () => {
cy.request("GET", 'https://jsonplaceholder.typicode.com/todos/1')
.its("body");
})

React/redux app function not firing

Update 2:
Another update - I'm down to this one issue. The values from redux-form are being sent to the action, but even narrowed down to this:
export function signinUser(values) {
console.log('function about to run, email: ' values.get('email'));
}
I don't even see a console log entry. However, the same function with only a simple console log works:
export function signinUser() {
console.log('function about to run');
}
Update:
The only differences between the working code and non-working code is redux-form and immutablejs. I tried back-porting these to the working app and now the behaviour is the same.
I'm submitting my form and sending the redux-form values to the function, where I'm using values.get('email') and values.get('password') to pass the values to axios.
handleFormSubmit(values) {
console.log('sending to action...', values);
this.props.signinUser(values);
}
I have a login form and onSubmit I'm passing the values to a function which dispatches actions.
The code works in a repo I've forked, but I'm transferring it to my own app. The problem I'm having is that the function doesn't seem to fire, and I'm struggling to figure out where to add console.log statements.
The only console.log that fires is on line 2.
export function signinUser(values) {
console.log('function will run');
return function(dispatch) {
axios.post(TOKEN_URL, {
email: "a#a.com",
password: "a",
method: 'post',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
})
.then(response => {
console.log('response: ', response.data.content.token );
// If request is good...
// - Update state to indicate user is authenticated
dispatch({ type: AUTH_USER });
// decode token for info on the user
var decoded_token_data = jwt_decode(response.data.content.token);
// - Save the JWT token
localStorage.setItem('token', response.data.content.token);
console.log(localStorage.getItem('token'));
// - redirect to the appropriate route
browserHistory.push(ROOT_URL);
})
.catch(() => {
// If request is bad...
// - Show an error to the user
dispatch(authError('Bad Login Info'));
});
}
}

Resources