Our latest rock-solid discovery when it comes to cypress and meteor, is to set variables on the window object. It’s not magic, and it’s recommended by Cypress in their docs, but it really made things much less flaky.
For example, we have a top level React component that uses useTracker
to get the current user and make it available through context. It looks something like this:
const CurrentUserProvider = ({ children }) => {
const currentUser = useTracker(() => Meteor.user());
window.currentuser = currentUser; // This is for cypress
return <Provider value={currentUser}>{children}</Provider>
}
This way, in your custom cypress helper you can do this:
Cypress.Commands.add(
'meteorLogin',
(email = TEST_EMAIL, password = TEST_PASSWORD) => {
cy.getMeteor().then(Meteor => {
let promise;
if (Meteor.userId()) {
// Logout first if the user is already logged in
promise = cy.meteorLogout();
} else {
promise = Promise.resolve();
}
return promise.then(
() =>
new Cypress.Promise((resolve, reject) => {
Meteor.loginWithPassword(email, password, err => {
if (err) {
return reject(err);
}
resolve();
});
}),
);
});
cy.window().should(win => {
// Make sure currentUser is truthy
expect(!!win.currentUser).to.equal(true);
});
},
);
That final line is really important and makes sure that your UI, Meteor and Cypress are in sync. And it’s just as easy to check if that value is undefined (loading) or null (logged out).
Another one we’re using, is in all before
hooks of every suite, is to wait for meteor to be ready:
// Somewhere in your frontend code
Meteor.startup(() => {
window.appIsReady = true;
})
And then in your before, call this command:
Cypress.Commands.add('startTest', (url = '/') => {
cy.visit(url);
cy.window().should('have.property', 'appReady', true);
cy.checkConnection();
});
before(() => {
cy.startTest('/first-page'); // Guarantee readiness of your app
cy.meteorLogin('test@mail.com', '12345678'); // Guaranteed to succeed and be logged in
})