I'm getting trouble with Cypress asynchronous mechanism. I have a custom command that is placed in this file
class HeaderPage {
shopLink = 'a[href="/angularpractice/shop"]'
homeLink = ''
navigateToShopPage() {
cy.get(this.shopLink).click()
}
sshToServer() {
setTimeout(() => {
console.log('Connecting')
}, 5000)
console.log('Connected')
}
}
export default HeaderPage
The function sshToServer is simulated to pause 5000ms. So I want Cypress remaining test will be hold and wait for this function completed. How can I do that? Thanks in advance.
import HeaderPage from "../support/pageObjects/HeaderPage"
describe('Vefiry Alert and Confirm box', () => {
const headerPage = new HeaderPage()
it('Access home page', () => {
cy.visit(Cypress.env('url') + 'AutomationPractice/')
});
it('SSH to server', () => {
headerPage.sshToServer()
});
it('Verify content of Alert', () => {
cy.get('#alertbtn').click()
cy.on('window:alert', (alert) => {
expect(alert).to.equal('Hello , share this practice page and share your knowledge')
})
});
You can issue a new cy.wrap command with a value null and calling your async function in the .then function. Cypress will resolve that async function automatically and then move on to the next test.
First, you should convert your sshToServer method to an async(promise) function:
sshToServer() {
console.log('Connecting');
return new Promise((resolve) => {
setTimeout(() => {
console.log('Connected');
resolve();
}, 5000);
});
}
Then, in your spec:
it('SSH to server', { defaultCommandTimeout: 5000 }, () => {
cy.wrap(null).then(() => headerPage.sshToServer());
});
Note that I have also used a bigger for the spec defaultCommandTimeout since the default timeout is 4 seconds which is shorter than your sshToServer method and would cause the test to fail if not using a bigger timeout.
Sorry for late answer but i think this is the easiest way to do it
cy.wait(milliseconds)
Related
I am writing Vue application for backend which is still in development. For this reason I want add artificial delay to actions. For instance if I submit sign in, I want add delay 1 second and then redirect to main application.
this is submit method from component
onSubmit() {
this.loading = true;
this.$store.dispatch('auth/signIn', this.credentials).then(() => {
this.loading = false;
});
}
and here is action's signIn method:
async signIn({ commit }, credentials) {
const result = await authService.signIn(credentials);
await commit(AUTHENTICATE, {
authenticated: result
});
}
as you can see, I call there authService in which method I created timeout block which not work and service returns undefined
async signIn(credentials) {
setTimeout(() => {
console.log('credentials', credentials);
return true;
}, 2000);
}
Can you help me to fix it?
This is what I used for delay. Check https://alesbubblesort.netlify.app/ main.js file.
sleepFunction(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
You don't return anything from your signIn method and setTimeout doesn't block code execution.
If you want to make a blocking timeout, I suggest creating a delay() method, like this:
function delay(time = 2500) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
By default it will wait for 2.5 seconds.
You can see a working example in vanilla JS here
I've created a package for Atom called quick-fold that jumps to the next foldable line and folds it on the command quick-fold:fold-next. I wanted to start getting into Atom specs so I can run tests on this package, however I've hit this problem where the package is just never activated when running the specs.
quick-fold-spec.js:
describe('QuickFold package', () => {
let editor, editorView;
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript');
await atom.workspace.open('sample.js');
editor = atom.workspace.getActiveTextEditor();
editorView = atom.views.getView(editor);
});
describe('when the specs are run', () => {
it('opens the sample file', () => expect(!editor.isEmpty()).toBe(true));
// true
});
describe('when the quick-fold:fold-next event is triggered', () => {
beforeEach(async () => {
// Try to activate package by dispatching command:
atom.commands.dispatch(editorView, 'quick-fold:fold-next');
await atom.packages.activatePackage('quick-fold'); // Never resolves
});
it('activates the package', () => {
expect(atom.packages.isPackageActive('quick-fold')).toBe(true);
});
it('moves the cursor to a different line number', () => {
expect(editor.getCursorScreenPosition().row).not.toBe(0);
});
});
});
But atom.packages.activatePackage('quick-fold') never resolves. The package doesn't activate and instead it times out:
timeout: timed out after 5000 msec waiting for spec promise to resolve
The activation command is set in package.json:
"activationCommands": {
"atom-workspace": "quick-fold:fold-next"
},
so dispatching this should activate the package, and then the await atom.packages.activatePackage('quick-fold') should resolve. But the cursor position doesn't change and the package doesn't get activated.
(Note that atom.packages.activatePackage('quick-fold') is merely a promise - it doesn't activate the package but it resolves when the package gets activated.)
As is often the case, I figured it out in the end...
1. The beforeEach() function is missing a runs()
It should be
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript');
await atom.workspace.open('sample.js');
runs(() => {
editor = atom.workspace.getActiveTextEditor();
editorView = atom.views.getView(editor);
return activationPromise = atom.packages.activatePackage('quick-fold');
});
});
where the runs() function returns a promise on activation of the package.
From the docs:
Specs are written by defining a set of blocks with calls to runs, which usually finish with an asynchronous call.
I'm too tired right now to understand exactly what that means but it rings true here.
2. The activation commands in package.json should be based on atom-text-editor
I.e., not atom-workspace
"activationCommands": {
"atom-text-editor": "quick-fold:fold-next"
},
This is probably because we have atom.commands.dispatch(editorView, 'quick-fold:fold-next') where the commands are dispatched to editorView = atom.views.getView(editor) instead of the Atom workspace.
Refactored - the "standard" way of doing it
describe('QuickFold package', () => {
let editor, editorView, activationPromise;
const foldNext = async (callback) => {
atom.commands.dispatch(editorView, 'quick-fold:fold-next');
await activationPromise;
return callback();
};
beforeEach(async () => {
await atom.packages.activatePackage('language-javascript');
await atom.workspace.open('sample.js');
runs(() => {
editor = atom.workspace.getActiveTextEditor();
editorView = atom.views.getView(editor);
return activationPromise = atom.packages.activatePackage('quick-fold');
});
});
describe('when the specs are run', () => {
it('opens the sample file', () => expect(!editor.isEmpty()).toBe(true));
});
describe('when the quick-fold:fold-next event is triggered', () => {
it('activates the package', () => {
return foldNext(() => expect(atom.packages.isPackageActive('quick-fold')).toBe(true));
});
it('moves the cursor to a different line number', () => {
return foldNext(() => expect(editor.getCursorScreenPosition().row).not.toBe(0));
});
});
});
I am attempting to clear a former timeout before initiating a new timeout, because I want messages to display for 4 seconds and disappear UNLESS a new message pops up before the 4 seconds is up. The Problem: Old timeouts are clearing the current message, so clearTimeout() is not working in this component, in this scenario:
let t; // "t" for "timer"
const [message, updateMessage] = useState('This message is to appear for 4 seconds. Unless a new message replaces it.');
function clearLogger() {
clearTimeout(t);
t = setTimeout(() => {
console.log('wiping message');
updateMessage('');
}, 4000);
}
function initMessage(msg) {
updateMessage(msg);
clearLogger();
}
The funny thing is that this works:
function clearLogger() {
t = setTimeout(() => {
console.log('wiping message');
updateMessage('');
}, 4000);
clearTimeout(t);
}
...but obviously defeats the purpose, since it just immediately obliterates the timeout.
In practice, I should be able to trigger initMessage() every two seconds and never see, "wiping message' logged to the console.
The issue is that on every render the value of t is reset to null. Once you call updateMessage, it will trigger a re-render and will lose it's value. Any variables inside a functional react component get reset on every render (just like inside the render function of a class-based component). You need to save away the value of t using setState if you want to preserve the reference so you can call clearInterval.
However, another way to solve it is to promisify setTimeout. By making it a promise, you remove needing t because it won't resolve until setTimeout finishes. Once it's finished, you can updateMessage('') to reset message. This allows avoids the issue that you're having with your reference to t.
clearLogger = () => {
return new Promise(resolve => setTimeout(() => updateMessage(''), resolve), 5000));
};
const initMessage = async (msg) => {
updateMessage(msg);
await clearLogger();
}
I solved this with useEffect. You want to clear the timeout in the return function
const [message, updateMessage] = useState(msg);
useEffect(() => {
const t = setTimeout(() => {
console.log('wiping message');
updateMessage('');
}, 4000);
return () => {
clearTimeout(t)
}
}, [message])
function initMessage(msg) {
updateMessage(msg);
}
Try execute set timeout after clearTimeout() completes
clearTimeout(someVariable, function() {
t = setTimeout(() => {
console.log('wiping message');
updateMessage('');
}, 4000);
});
function clearTimeout(param, callback) {
//`enter code here`do stuff
}
Or you can use .then() as well.
clearTimeout(param).then(function(){
t = setTimeout(() => {
console.log('wiping message');
updateMessage('');
}, 4000);
});
I have the following test in my react native application, but the test should fail (because the action returned is not equal to the action I put in expectedActions. My guess is that it is passing because the expect test runs after the test has completed.
How can I force the test to wait until the promise is completed and the expect test runs? Is there another way of doing this?
describe('authorize actions', () => {
beforeEach(() => {
store = mockStore({});
});
it('should create an action to signify successful auth', () => {
auth.authorize.mockImplementation(() => Promise.resolve({"something": "value"}));
const expectedActions = [{"type":"AUTHORIZE_RESPONSE","payload":{"something":"sdklfjsdf"}}];
authorizeUser(store.dispatch, store.state).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
})
});
Ok, looks like I just missed some of the Jest docs - if you return the promise, i.e. return auth.authorize.mockImplementation(() => Promise.resolve... then Jest will wait until it's completed before continuing.
The are varios ways to test async code. Check the docs for examples: https://facebook.github.io/jest/docs/en/asynchronous.html
One could be returning the promise:
describe('authorize actions', () => {
beforeEach(() => {
store = mockStore({});
});
it('should create an action to signify successful auth', () => {
auth.authorize.mockImplementation(() => Promise.resolve({"something": "value"}));
const expectedActions = [{"type":"AUTHORIZE_RESPONSE","payload":{"something":"sdklfjsdf"}}];
return authorizeUser(store.dispatch, store.state).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
})
});
Background
I am made a small function that emits messages via sockets and I am trying to test it using mocha.
const myFun = socket => {
socket.emit("first", "hello World!");
setTimeout(() => {
socket.emit("second", "hello Moon!");
}, 1000);
setTimeout(() => {
socket.emit("third", "hello Mars? Venus? I dunno...");
}, 2000);
};
Because I inject the socket dependency into my function which uses it, it is fairly simple to just pass it a fake socket and test if it is called and with which parameters (using, for example, sinon.js).
Problem
The problem here is that I don't know when my test ends. Because myFun does not return a promise nor anything I don't know how to tell mocha that I have sent all the messages I wanted and that the test should end.
Test
Currently, I am using the following code:
const chai = require("chai");
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
describe("myFun", () => {
const fakeSocket = {
emit: sinon.spy()
};
it("receive first message", done => {
myFun(fakeSocket);
setTimeout(() => {
try{
expect(fakeSocket.emit).to.have.been.calledThrice;
done();
}catch(err){
done(err);
}
}, 3000);
//we wait 3 seconds because it should be more than enough for all messages to be delivered
});
});
I am using a setTimeout with a try catch to test the code, which honestly is quite horrible.
Question
How can I improve my tests so they don't take X seconds and just end when I receive the last message?
How do I remake my tests so I don't depend on timers ?
You can use fake timers to advance the clock virtually:
describe("myFun", () => {
const fakeSocket = { emit: sinon.spy() }
beforeEach(() => {
this.clock = sinon.useFakeTimers();
});
afterEach(() => {
fakeSocket.emit.reset();
this.clock.restore();
});
it('emit first message', () => {
myFun(fakeSocket);
expect(fakeSocket.emit).to.have.been.calledOnce;
});
it('emit second message after 1000ms', () => {
myFun(fakeSocket);
this.clock.tick(1000);
expect(fakeSocket.emit).to.have.been.calledTwice;
});
it('emit third message after 2000ms', () => {
myFun(fakeSocket);
this.clock.tick(2000);
expect(fakeSocket.emit).to.have.been.calledThrice;
});
});
Or without any timers at all (making setTimeout synchronously call the callback it gets passed):
describe("myFun", () => {
const fakeSocket = { emit: sinon.spy() }
const stub = sinon.stub(global, 'setTimeout').callsArg(0);
after(() => {
stub.restore();
});
it('should emit three messages', () => {
myFun(fakeSocket);
expect(fakeSocket.emit).to.have.been.calledThrice;
});
});
How can I improve my tests so they don't take 3 seconds and just end when I receive the last message?
Which message is the last message? Right now you emit three messages, but what if myFun is changed to emit four messages? Or four-hundred? Mocha can't know that myFun is finished when async is involved unless you tell it.
Perhaps you should socket.emit('done') once you have finished emitting all your messages, and then you can wait for that particular emit to mark your test as complete.
What you need to look at is Sinon fake timers.
Fake timers in Sinon allow you to control the output of inbuilt timing functions like the Date constructor, and setTimeout/setInterval.
A basic usage in mocha might be something like:
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers();
});
it ('should do a thing', () => {
//assertion
});
it ('should a thing 1000 ms later'), () => {
clock.tick(1000);
//assertion
});
afterEach(() => {
clock.restore();
});