Not able to store cookies in all the cypress tests - testing

I am trying to automate my application with cypress. Have written few tests in my class and before visiting the site I have set up cookies (To login without entering username, password and otp). I have tried several ways to store these cookies and call before each of these tests but cookies work only for the 1st test and I am logged out for the subsequent tests.
Please find my code below. I am calling data using fixtures. It works fine only when i set sso and user agent for every test and I want to eliminate this.
describe('Watchlist Testsuite', () => {
let data;
before(() => {
cy.fixture('example').then((fdata) => {
data = fdata;
});
});
it('Setting up cookies', () => {
cy.setupCookies(data.sso, data.uid, data.userAgent, data.devId, data.url)
.then(() => cy.visit(data.url))
})
it('Enter Passcode', () => {
cy.setCookie('x-sso-token', data.sso)
cy.setCookie('x-user-agent', data.userAgent)
Cypress.config('pageLoadTimeout', 2000)
cy.enterPasscode(data.passcode)
Cypress.config('pageLoadTimeout', 20000)
cy.closeOKButton()
})
it('Create a watchlist', () => {
cy.setCookie('x-sso-token', data.sso)
cy.setCookie('x-user-agent', data.userAgent)
watchlistTest.createWatchlist(data.watchlistName)
})
Below are the ways with which I tried to preserve the cookies for all tests but none of them seem to be working.
Setting up cookies in beforeeach method
beforeEach('Setup cookies', ()=> {
cy.setupCookies(data.sso, data.uid, data.userAgent, data.devId, data.url)
.then(() => cy.visit(data.url))
})
Using cookies defaults
Cypress.Cookies.defaults
({
preserve:[data.sso, data.userAgent]
})
Using cy.session (I have set experimentalSessionAndOrigin to true)
it('setting up cookies',() => {
console.log('sso is ', data.sso)
const validate = () => {
cy.getCookie(data.sso).should('exist')
}
cy.session([window.sessionStorage.setItem('x-sso-token', data.sso),
window.sessionStorage.setItem('x-uid', data.uid),
window.sessionStorage.setItem('x-user-agent', data.userAgent),
window.sessionStorage.setItem('dev-id', data.devId)], {validate})
})
PS: I am using the Cypress 10.3.1 version.
Please guide me on the right approach to solve the issue.

Related

Cypress: login through magic link error with cy.origin()

Devs at my startup have switched login to a magic link system, in which you get inside after clicking a link on the email body.
I have set up a Mailsac email to receive mails containing magic links but I haven't been able to actually follow those links because of the following:
cy.request({
method: "GET",
url: "https://mailsac.com/api/addresses/xxxx#mailsac.com/messages",
headers: {
"Mailsac-Key": "here-goes-the-key",
},
}).then((res) => {
const magicLink = res.body[0].links[0];
cy.origin(magicLink, () => {
cy.visit('/')
});
});
I wasn't able to use cy.visit() either because the magic link URL is slightly different from the baseURL in this testing environment.
So my question is:
How could I follow this cumbersome link to find myself logged in home, or else, is there another way to deal with magic links?
Thanks
The docs say
A URL specifying the secondary origin in which the callback is to be executed. This should at the very least contain a hostname, and may also include the protocol, port number & path. Query params are not supported.
Not sure if this means the cy.visit() argument should not have query params, of just the cy.origin() parameter.
Try passing in the link
cy.request({
...
}).then((res) => {
const magicLink = res.body[0].links[0];
const magicOrigin = new URL(magicLink).origin
cy.origin(magicOrigin, { args: { magicLink } }, ({ magicLink }) => {
cy.visit(magicLink)
});
});
If that doesn't fix it, you could try using cy.request() but you'll have to observe where the token is stored after using the magicLink.
cy.request({
...
}).then((res) => {
const magicLink = res.body[0].links[0];
cy.request(magicLink).then(response =>
const token = response??? // find out where the auth token ends up
cy.setCookie(name, value) // for example
});
});
You need to pass the domain as the first parameter to origin, and do the visit within the callback function, something like this:
const magicLinkDomain = new Url(magicLink).hostname
cy.origin(magicLinkDomain, {args: magicLink}, ({ magicLink }) => {
cy.visit(magicLink);
//...
})
Reference: https://docs.cypress.io/api/commands/origin#Usage

Nuxt Auth login Cypress test true become false

i am using nuxt frame work and auth/next, the login test before has no problem but somehow i retest again in a while this happen.
the current object in the test show the loggedIn is true but at the cypress there, when i console.log it out, it become false and i am not sure why.
on cypress there
log was the state object
was the strategy
was the loggedIn ( check the red line )
it match the object info but somehow only loggedIn are true become false. i dont think is my login code problem, could cypress somehow need to refresh something or cache out to prevent mistake like this ? any one know what is this happening ?
cypress test code :
describe('Testing Login Access', () => {
const dummyUsername = 'xxxxxxxx'
const dummyPassword = 'xxxxxxxx'
beforeEach(() => {
// Backend API Login testing, if this dont pass, error came from backend.
// cy.auth()
cy.login()
})
it('Should $auth user store loggedIn be equal true, length not be empty', () => {
cy.window().then((window) => {
cy.log(window.app.store.$auth.state)
cy.log(window.app.store.$auth.state.strategy)
cy.log(window.app.store.$auth.state.loggedIn)
// expect(window.app.store.$auth.state.loggedIn).to.eq(true)
})
})
it('Should able to get user data on account page', () => {
cy.visit('/my-account').then(() => {
cy.get('h1').should('contain', 'Hello Dear xadmin cheok')
cy.get('tr td').should('contain', 'whxadmin#gmail.com')
})
})
})

How to share response data across multiple suites?

Let's say we have the following suite:
describe('Devices', () => {
describe('Master Data Set-Up', () => {
it('should create the device if necessary', () => {
cy.createDevice()
its('body.id')
.as('deviceId');
});
});
describe('Test Suite 1', () => {
it('should allow to send data to device', () => {
cy.get('#deviceId').then((deviceId) => {
cy.sendData(deviceId, 'Some Data');
});
});
});
});
So, we have a set up suite that creates master data. This is a simplified version, actually it contains a couple of it specs and I'd like to keep it like that because it's better to read in the Cypress output.
Then, there is the actual test suite that want's to use data that has previously been created. In this case a server generated id that should be used for another REST call.
This is assuming, that cy.createDevice and cy.sendData are custom commands available that internally use cy.request.
When running that, cy.get('#deviceId') fails because aliases are not shared across describe blocks AFAIK. I tried to use let deviceId but it's undefined as it is not yet available when the test specs are processed.
What is a proper way to do this?
I believe this will be better solution, as cypress is asynchronous so it's better to write it on file and read it
describe('Devices', () => {
describe('Master Data Set-Up', () => {
it('should create the device if necessary', () => {
cy.createDevice()
......
cy.writeFile('deviceId.txt', body.id)
});
});
describe('Test Suite 1', () => {
it('should allow to send data to device', () => {
cy.readFile('deviceId.txt').then((device_id) => {
cy.sendData(device_id, 'Some Data');
})
});
});
});
Upvote for #ArekKhatry's idea, but to be safe I would obtain the the id in a before(). If you ever run tests in parallel, grabbing data from one test to use in another would be flaky.
Note that running cy.createDevice().its('body.id') in the before() still gives you the same test coverage as running inside it(), i.e it tests that the request succeeds and the return value has an id.
The file should be written to cypress/fixtures, otherwise it will write to the project root causing untidy pollution of the file structure.
Also, the id is returned from cy.request() as a number, but must be stringifyed in order to write to a text file.
Here's my variant
describe('Devices', () => {
before(() => {
cy.createDevice()
.its('body.id')
.then(id => {
cy.writeFile('cypress/fixtures/deviceId.txt', id.toString());
cy.log(`Created device: ${id}`);
});
});
describe('Test Suite 1', () => {
it('should allow to send data to device', () => {
cy.fixture('deviceId') // can use simpler cy.fixture here
.then(device_id => { // returned as a string here
const id = parseInt(device_id); // may need to parse to number?
cy.sendData(id, 'Some Data');
})
});
});
});
Ok, so first thanks to Aloysius and Arek for their answers. But I had the gut feeling that there must be some easier way to do this that writing an Id to a file.
As I mentioned before, I had issues with my first attempt to use a global variable:
I tried to use let deviceId but it's undefined as it is not yet
available when the test specs are processed.
I really wanted to understand, why this did not work and did some console debugging.
I added a console log:
describe('Devices', () => {
console.log('Loading test suites...')
(...)
});
When running the tests, I saw the log output twice, once after the first describe block where the device id was stored and then a second time after the master data was written.
Actually, I found out that this issue was cause by the following known Cypress issue:
https://github.com/cypress-io/cypress/issues/2777
After setting the baseUrl, it actually works:
describe('Devices', () => {
let deviceId;
before( () => {
Cypress.config('baseUrl', Cypress.env('system_url'))
cy.visit('/');
})
describe('Master Data Set-Up', () => {
it('should create the device if necessary', () => {
cy.createDevice()
.its('body.id')
.then((id) => {
deviceId = id;
});
});
});
describe('Test Suite 1', () => {
it('should allow to send data to device', () => {
cy.sendData(deviceId, 'Some Data');
});
});
});

Is there a way for getting a list of all created tests in my emberjs app?

I need a way to get all my ember tests' nameS, since I want to run them in a separate thread by using the --filter "TEST-NAME" flag.
Right now, I am using a hardcoded array.
It's not documented, but you can use the config object on the root QUnit object to get all modules in your test suite, then the tests within each module and their names. Note that any tests not in a module will still appear, but with an empty-named module. Here's a runnable jsfiddle with the example, then the code below.
QUnit.test('no-module', (assert) => { assert.equal(0,0) })
QUnit.module('foobar', () => {
QUnit.test('foo', (assert) => { assert.equal(1, 1) })
QUnit.test('bar', (assert) => { assert.equal(2, 2) })
})
QUnit.module('batbaz', () => {
QUnit.test('bat', (assert) => { assert.equal(3, 3) })
QUnit.test('baz', (assert) => { assert.equal(4, 4) })
})
const testNames = []
QUnit.config.modules.forEach((module) => {
testNames.push(...module.tests.map(test => test.name))
})
console.log(testNames)

Testing ExpressJS endpoint with Jest

I'm trying to test an endpoint from my express application using Jest. I'm migrating from Mocha to try out Jest to improve the speed. However, my Jest test does not close? I'm at a loss...
process.env.NODE_ENV = 'test';
const app = require('../../../index');
const request = require('supertest')(app);
it('should serve the apple-app-site-association file /assetlinks.json GET', async () => {
const response = await request.get('/apple-app-site-association')
expect(response.statusCode).toBe(200);
});
So the only thing I could think for this failing is that you might be missing the package babel-preset-env.
In any case there are two other ways to use supertest:
it('should serve the apple-app-site-association file /assetlinks.json GET', () => {
return request.get('/apple-app-site-association').expect(200)
})
or
it('should serve the apple-app-site-association file /assetlinks.json GET', () => {
request.get('/apple-app-site-association').then(() => {
expect(response.statusCode).toBe(200);
done()
})
})
async is the fancy solution, but is also the one that has more requirements. If you manage to find what was the issue let me know :).
(Reference for my answer: http://www.albertgao.xyz/2017/05/24/how-to-test-expressjs-with-jest-and-supertest/)
it("should serve the apple-app-site-association file /assetlinks.json GET", async () => {
await request
.get("/apple-app-site-association")
.send()
.expect(200);
});
if your configuration setup is correct this code should work.