Reputation: 8531
I have command that can request token and it resolves promise on response. Once the promise is being resolved - I put the token into the sessionStorage in SomeTest.js. It works. But I have to put exactly same cy.window()...
in every test.
SomeTest.js:
before(()=>{
cy.login().then(res=>
cy.window().then((window) => {
window.sessionStorage.setItem('token', JJSON.stringify(res))
})
);
});
command in commands.js
Cypress.Commands.add('login', function () {
fetcher.emit('tokenRequest',{email:'[email protected]', password:'dummy'});
return new Cypress.Promise((resolve, reject) => {
fetcher.on('onTokenResponse',function(response) {
resolve(response);
});
});
});
I would like to set sessionStorage in command login()
too.
SomeTest.js:
before(()=>{
cy.login();
});
command in commands.js
Cypress.Commands.add('login', function () {
fetcher.emit('tokenRequest',{email:'[email protected]', password:'dummy'});
return new Cypress.Promise((resolve, reject) => {
fetcher.on('onTokenResponse',function(response) {
cy.window().then((window) => {
window.sessionStorage.setItem('token', JSON.stringify(response));
resolve(response);
})
});
});
});
But it throws error: Uncaught CypressError: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.
, The cy command you invoked inside the promise was:> cy.window()
.
I understand the problem is that I am trying to call another promise which is not being returned. But how can I achieve something like this?
I need to
tokenRequest
tokenResponse
cy.window()
and set some sessionStorage itemI've found walkaround and I've wrapped login into another function/command which seems to be working but I am not sure how pretty is this solution.
Cypress.Commands.add('loginFull', function () {
return cy.login().then((response)=>{
cy.window().then((window) => {
window.sessionStorage.setItem('token', JSON.stringify(response));
});
});
});
Upvotes: 1
Views: 3277
Reputation: 23483
One way that works is to move the cy.window()
command to the outermost level of the custom command, so you are not trying to use it within the Promise.
Test
describe('web socket event handler', () => {
let fetcher;
before(() => {
const io = require('socket.io-client');
fetcher = io.connect('http://127.0.0.1:5000');
cy.window().then(win => {
win.sessionStorage.clear(); // ensure clean before test
})
})
Cypress.Commands.add('login', function () {
cy.window().then(win => {
fetcher.emit('tokenRequest',{email:'[email protected]', password:'dummy'});
return new Cypress.Promise((resolve, reject) => {
fetcher.on('onTokenResponse',function(response) {
win.sessionStorage.setItem('token', JSON.stringify(response));
return resolve(response);
});
});
});
});
it('waits for token', () => {
cy.login().then(_ => {
cy.window().then(win => {
console.log(win.sessionStorage.getItem('token'))
// logs "Response: [email protected]"
})
})
})
})
Server
io.on('connection', function(socket){
socket.on('tokenRequest', function(msg){
setTimeout(() => {
io.emit('onTokenResponse', `Response: ${msg.email}`);
}, 2000)
});
});
Ideally would want to make the login command wait on the fetcher event (with timeout?).
I tried this approach Spying on the DOM API, but no luck so far.
Another way is to set a flag within the event listener, and add a command to the queue that waits on it.
Cypress.Commands.add('login', function () {
cy.window().then(win => {
fetcher.emit('tokenRequest',{email:'[email protected]', password:'dummy'});
let flag = { done: false };
fetcher.on('onTokenResponse',function(response) {
win.sessionStorage.setItem('token', JSON.stringify(response));
flag.done = true;
})
cy.wrap(flag).its('done').should('equal', true); // wait here until done
});
});
Now you can call login synchronously
it('waits for token', () => {
cy.login();
cy.window().then(win => {
console.log(win.sessionStorage.getItem('token'))
// logs "Response: [email protected]"
})
})
Upvotes: 3
Reputation: 879
Cypress.Commands.add('login', function () {
function waitForSocket(callback) {
fetcher.emit('tokenRequest',{email:'[email protected]', password:'dummy'});
fetcher.on('onTokenResponse',function(response) {
callback(response);
});
}
waitForSocket((res) => {
cy.window().then((window) => {
window.sessionStorage.setItem('token', JJSON.stringify(res))
})
})
});
The error points to this: Cypress commands are already promises and they will wait/resolve themselves. So I think there should not be a wrapping promise for the fetcher. Edit: I think you can wrap the socket tasks in a callback function.
Upvotes: 1