Hi all, we’re using chimp + mocha + chai for acceptance/integration testing of our meteor web app, and, so far, it’s working great. We have some cases where we need to use chai-as-promised, but I’m unclear on how to use promises with server. I’d love to see examples of how to:
Use server.execute, server.call, or server.apply to retrieve document(s) from a Meteor collection.
Use chai-as-promised (expect(documents).to.eventually... or documents.should.eventually) to validate the documents retrieved.
Chain multiple server call promises to test data integrity between multiple documents and collections.
I think I solved the problem on my own, with a tip from @sam (via the Xolv.io Community Slack channel). After doing some more experimentation, I figured out I was using .then() incorrectly. It appears there’s no need for a then, since expect(Promise.resolve(newDoc)).to.eventually... automagically blocks execution of subsequent statements until the promise resolves.
Here’s some example code, for future reference:
it("should get a document and do something with it", function () {
var newDoc = server.execute(function () {
var docId = MyCollection.insert({name: 'new document'});
return MyCollection.findOne({_id: docId});
});
expect(Promise.resolve(newDoc)).to.eventually.have.property('_id');
expect(newDoc.name).to.equal('new document');
var clonedDoc = server.execute(function (originalDoc) {
var docId = MyCollection.insert({name: 'copy of ' + originalDoc.name});
return MyCollection.findOne({_id: docId});
}, newDoc);
expect(Promise.resolve(clonedDoc)).to.eventually.have.property('_id');
expect(clonedDoc.name).to.equal('copy of new document');
});
it("should get a document and do something with it", function () {
var newDoc = server.execute(function () {
var docId = MyCollection.insert({name: 'new document'});
return MyCollection.findOne({_id: docId});
});
expect(newDoc).to.have.property('_id');
expect(newDoc.name).to.equal('new document');
var clonedDoc = server.execute(function (originalDoc) {
var docId = MyCollection.insert({name: 'copy of ' + originalDoc.name});
return MyCollection.findOne({_id: docId});
}, newDoc);
expect(clonedDoc).to.have.property('_id');
expect(clonedDoc.name).to.equal('copy of new document');
});
Update: I’ve identified a few ways of resolving race conditions in Chimp, without using promises.
Insert an intentional delay in publication methods using Meteor._sleepForMs() to expose tests that are failing due to race conditions. A 7-second delay seems to do the trick.
Wrap problematic asynchronous queries (even server-side ones) with browser.waitUntil.
Use browser.waitForExist or browser.waitForVisible liberally to ensure that subscriptions are complete before proceeding.
Here’s how I’d re-write the code above now:
it("should get a document and do something with it", function () {
const longDelay = 60000;
var newDocId = server.execute(function () {
return MyCollection.insert({name: 'new document'});
});
newDocId.should.be.a('string')
.and.not.be.empty;
var newDoc = browser.waitUntil(function () {
return server.execute(function (docId) {
return MyCollection.findOne({_id: docId});
}, newDocId);
}, longDelay);
newDoc.should.have.property('name')
.that.is.a('string')
.that.equals('new document');
var clonedDocId = server.execute(function (originalDoc) {
return MyCollection.insert({name: 'clone of ' + originalDoc.name});
}, newDoc);
clonedDocId.should.be.a('string')
.and.not.be.empty;
var clonedDoc = browser.waitUntil(function () {
return server.execute(function (docId) {
return MyCollection.findOne({_id: docId});
}, clonedDocId);
}, longDelay);
clonedDoc.should.have.property('name')
.that.is.a('string')
.that.equals('clone of ' + newDoc.name);
});