Cypress - Write a file everytime the url changes - cypress

I am running a cypress test, in which I visit a url that has many redirects.
I wish to get all redirects and write them to a file.
Doing this code works somehow
describe(`Test Offer URL`, () => {
let urls = [];
it(`Visits the offer url ${Cypress.env(
"offer_url"
)} with a pre-defined user agent of ${userAgent}`, () => {
cy.visit(Cypress.env("offer_url"), {
headers: { "user-agent": userAgent },
});
cy.on("url:changed", (url) => {
urls.push(url);
});
cy.log(urls);
});
after(() => {
cy.writeFile("cypress/results/offers.json", {
baseUrl: Cypress.env("offer_url"),
userAgent,
redirectUrls: urls,
});
});
});
so with this code cy.on("url:changed", (url) => { urls.push(url);});
I am able to get all the needed urls, I can check this with cy.log(urls)
But when i write the file I wont have them all
I thought to run the cy.writeFile() function inside of the url onChange trigger, but I get an error :
I tried chaining with a .then(() => cy.writeFile()) but it will only write the file one time
Any clues of how I could resolve this.

Related

How to change the domain in cypress?

Suppose i have one test case in that i have visit two different url can it is possible in cypress!
google.com
youtube.com
describe('validate the functionlity of change the domain',()=>{
it('verify the functionlity of change domain',()=>{
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})
cy.origin('http://www.webdriveruniversity.com',()=>{
cy.visit('/Contact-Us/contactus.html')
})
})
})
i have tried with this one it is working but my problem is that i want to use two different url in one test case
If you need to visit two separate domains, such as the two you provided, you can do that by having separate cy.origin() calls.
it('tests two separate domains', () => {
cy.origin('https://www.google.com', () => {
cy.visit('/');
// code
});
cy.origin('https://www.youtube.com', () => {
cy.visit('/');
// code
});
});
If you run the above, with cy.wait() after the cy.visit() commands, you'll see that Google and Youtube are navigated to.

Set cookies and return user on Cypress request

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.

Cypress with Auth0.com login and redirects

We have changed our login page so it's now redirecting to auth0.com and then back to our domain after you login to auth0. The issue is now when I login I get redirected to our QA environment which requires authentication so once the test submits the form I get a 401.
Before auth0 I was getting around the 401 by overwritting the visit function passing in a auth header.
If I try to visit our QA environment first before going to our login page I get
You may only cy.visit() same-origin URLs within a single test.
I've seen other questions asked about auth0 but not with also requiring authentication in the redirect, is it possible to still run tests on our QA environment?
While the Cypress team works to resolve max 1 site... support visiting multiple superdomains in one test on work around I have used in the past is this: detailed again below.
In commands.js
// -- Save localStorage between tests
let LOCAL_STORAGE_MEMORY = {};
Cypress.Commands.add('saveLocalStorage', () => {
Object.keys(localStorage).forEach(key => {
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
});
});
Cypress.Commands.add('restoreLocalStorage', () => {
Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
});
});
// -- Visit multiple domains in one test
Cypress.Commands.add('forceVisit', url => {
cy.window().then(win => {
return win.open(url, '_self');
});
});
In test.spec.js
/// <reference types="cypress" />
context('Force Visit', () => {
it('should be able to visit and assert on two domains', () => {
cy.forceVisit('https://example.cypress.io');
cy.title().should('eq', 'Cypress.io: Kitchen Sink');
cy.forceVisit('https://www.google.com');
cy.title().should('eq', 'Google');
});
});
context('Auth Flow', () => {
before(() => {
cy.forceVisit('<auth url>');
cy.get('<username input>').type('<username>');
cy.get('<password input>').type('<password>');
cy.intercept('POST', '<auth login request>').as('auth');
cy.get('<submit button>').click();
cy.wait('#auth');
});
afterEach(() => {
cy.saveLocalStorage();
});
beforeEach(() => {
cy.restoreLocalStorage();
// Preserve Cookies between tests
Cypress.Cookies.defaults({
preserve: /[\s\S]*/,
});
});
it('should be able to start in authorized state', () => {
cy.visit('<site url>');
});
});

Problem with CSRF protected Login in Cypress

I try to test a PHP Symfony application via Cypress.io but got problems with a custom login command which uses CSRF protection.
My command looks like this:
Cypress.Commands.add('login', () => {
cy.request('/login')
.its('body')
.then((body) => {
const $html = Cypress.$(body);
const csrf = $html.find('input[name=_csrf_token]').val();
cy.request({
method: 'POST',
url: '/login',
failOnStatusCode: false,
form: true,
body: {
username: 'user',
password: 'password',
_csrf_token: csrf,
},
})
});
});
This is the test spec:
describe('Masterdata Test', () => {
beforeEach(() => {
cy.login();
cy.visit('/masterdata/autoselector');
});
it('highlights the correct register', () => {
cy.get('.registerTab.bg-white').contains('Masterdata');
});
});
When I run the tests it seems that the login somehow works (it returns a 200 status code) but when it visits the url which should be checked it redirects to login again. (See picture below)
I already checked that the CSRF token is correct and I also removed the whole CSRF protection from the login and only used the second cy.request call in my login command and it worked just fine.
Can someone help me out with this?
The below code worked for me,
PS: Only for UI based login.
before(() => {
cy.visit("/");
cy.get(/*<get the webelement for csrf token>*/).invoke('attr', 'value').then(($value) => {
cy.setCookie('csrftoken', $value);
});
});
beforeEach(() => {
Cypress.Cookies.preserveOnce('csrftoken');
});
Refer to the CSRF Tokens demo in the Cypress Recipes repo.
https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/logging-in__csrf-tokens
I had exactly the same problem: The form would just reload without any error.
Turns out for me this happened because php sessions in Symfony where not working with APP_ENV=test and therefore the csrf token would change on every request ;-) I had to switch to dev or prod and then it worked just fine.
I discovered this by testing if the csfr token would be the same between reloads:
describe('Check csrf', () => {
it('Grabs Token and reloads', () => {
// get csrf token and output to command log
cy.request('/login')
.its('body')
.then((body) => {
const $html = Cypress.$(body)
const csrf = $html.find('input[name=_csrf_token]').val()
cy.log(csrf)
})
// do the same again
cy.request('/login')
.its('body')
.then((body) => {
const $html = Cypress.$(body)
const csrf = $html.find('input[name=_csrf_token]').val()
cy.log(csrf)
})
})
})

Cypress: changing the code while running crashes my tests (request aborted)

I'm testing an Angular App with Cypress.
I'm running my test with the Cypress dashboard, that I open using this command:
$(npm bin)/cypress open
I'm calling an API with my test: it works.
But when I change my code, Cypress will rerun the code which will cause my first (and only my first test) to fail. The request calling the API is aborted.
The only way to make it work again is to manually end the process, then start it again.
Has anyone got an idea what is causing this strange behaviour?
Here is my test code:
beforeEach(() => {
cy.visit('/');
cy.server();
cy.route('POST', `myUrl`).as('apiCall');
});
it('should find a doctor when user searches doctor with firstName', () => {
cy.get('#myInput').type('foo');
cy.get('#submitButton]').click();
cy.wait('#apiCall').then((xhr) => {
expect(xhr.status).to.eq(200);
});
});
You can prepare XHR stub like this:
describe('', () => {
let requests = {}; // for store sent request
beforeEach(() => {
cy.server({ delay: 500 }); // cypress will answer for mocked xhr after 0.5s
cy.route({
url: '<URL>',
method: 'POST',
response: 'fixture:response',
onRequest: ({ request }) => {
Object.assign(requests, { someRequest: request.body }); // it has to be mutated
},
});
});
And then in test:
it('', () => {
cy
.doSomeSteps()
.assertEqual(requests, 'someRequest', { data: 42 })
});
There is 2 advantages of this solution: first 0.5s delay make test more realistic because real backend doesn't answer immediately. Second is you can verify if application will send proper payload after doSomeActions() step.
assertEqual is just util to make assertion more readable
Cypress.Commands.add('assertEqual', (obj, key, value) =>
cy
.wrap(obj)
.its(key)
.should('deep.equal', value)
);

Resources