From 6df840347b4037688394db77ba307daa1eef3c1e Mon Sep 17 00:00:00 2001 From: apendua Date: Sat, 24 Jan 2015 18:24:05 +0100 Subject: [PATCH 01/34] Reorganizing the project structure --- lib/{ => browser}/browser.js | 0 lib/{ => browser}/browserPromiseChain.js | 0 lib/{ => browser}/helpers.js | 0 lib/{ => ddp}/ddp.js | 0 lib/{ => ddp}/makeDDPClient.js | 0 lib/{ => meteor}/build.js | 0 lib/{ => meteor}/meteor.js | 0 lib/{instance.js => meteor/meteorProcess.js} | 0 lib/{ => meteor}/meteorPromiseChain.js | 0 lib/{ => meteor}/remote.js | 0 lib/{ => mocha}/gagarin.js | 0 lib/{ => mocha}/interface.js | 0 lib/{ => mongo}/database.js | 0 lib/{ => mongo}/makeMongoDB.js | 0 lib/{ => mongo}/mongoDBProcess.js | 0 lib/{ => tools}/closure.js | 0 lib/{ => tools}/generic.js | 0 lib/{ => tools}/genericPromiseChain.js | 0 lib/{ => tools}/tools.js | 0 backdoor.js => meteor/backdoor.js | 0 settings.js => meteor/settings.js | 0 package.js | 4 ++-- 22 files changed, 2 insertions(+), 2 deletions(-) rename lib/{ => browser}/browser.js (100%) rename lib/{ => browser}/browserPromiseChain.js (100%) rename lib/{ => browser}/helpers.js (100%) rename lib/{ => ddp}/ddp.js (100%) rename lib/{ => ddp}/makeDDPClient.js (100%) rename lib/{ => meteor}/build.js (100%) rename lib/{ => meteor}/meteor.js (100%) rename lib/{instance.js => meteor/meteorProcess.js} (100%) rename lib/{ => meteor}/meteorPromiseChain.js (100%) rename lib/{ => meteor}/remote.js (100%) rename lib/{ => mocha}/gagarin.js (100%) rename lib/{ => mocha}/interface.js (100%) rename lib/{ => mongo}/database.js (100%) rename lib/{ => mongo}/makeMongoDB.js (100%) rename lib/{ => mongo}/mongoDBProcess.js (100%) rename lib/{ => tools}/closure.js (100%) rename lib/{ => tools}/generic.js (100%) rename lib/{ => tools}/genericPromiseChain.js (100%) rename lib/{ => tools}/tools.js (100%) rename backdoor.js => meteor/backdoor.js (100%) rename settings.js => meteor/settings.js (100%) diff --git a/lib/browser.js b/lib/browser/browser.js similarity index 100% rename from lib/browser.js rename to lib/browser/browser.js diff --git a/lib/browserPromiseChain.js b/lib/browser/browserPromiseChain.js similarity index 100% rename from lib/browserPromiseChain.js rename to lib/browser/browserPromiseChain.js diff --git a/lib/helpers.js b/lib/browser/helpers.js similarity index 100% rename from lib/helpers.js rename to lib/browser/helpers.js diff --git a/lib/ddp.js b/lib/ddp/ddp.js similarity index 100% rename from lib/ddp.js rename to lib/ddp/ddp.js diff --git a/lib/makeDDPClient.js b/lib/ddp/makeDDPClient.js similarity index 100% rename from lib/makeDDPClient.js rename to lib/ddp/makeDDPClient.js diff --git a/lib/build.js b/lib/meteor/build.js similarity index 100% rename from lib/build.js rename to lib/meteor/build.js diff --git a/lib/meteor.js b/lib/meteor/meteor.js similarity index 100% rename from lib/meteor.js rename to lib/meteor/meteor.js diff --git a/lib/instance.js b/lib/meteor/meteorProcess.js similarity index 100% rename from lib/instance.js rename to lib/meteor/meteorProcess.js diff --git a/lib/meteorPromiseChain.js b/lib/meteor/meteorPromiseChain.js similarity index 100% rename from lib/meteorPromiseChain.js rename to lib/meteor/meteorPromiseChain.js diff --git a/lib/remote.js b/lib/meteor/remote.js similarity index 100% rename from lib/remote.js rename to lib/meteor/remote.js diff --git a/lib/gagarin.js b/lib/mocha/gagarin.js similarity index 100% rename from lib/gagarin.js rename to lib/mocha/gagarin.js diff --git a/lib/interface.js b/lib/mocha/interface.js similarity index 100% rename from lib/interface.js rename to lib/mocha/interface.js diff --git a/lib/database.js b/lib/mongo/database.js similarity index 100% rename from lib/database.js rename to lib/mongo/database.js diff --git a/lib/makeMongoDB.js b/lib/mongo/makeMongoDB.js similarity index 100% rename from lib/makeMongoDB.js rename to lib/mongo/makeMongoDB.js diff --git a/lib/mongoDBProcess.js b/lib/mongo/mongoDBProcess.js similarity index 100% rename from lib/mongoDBProcess.js rename to lib/mongo/mongoDBProcess.js diff --git a/lib/closure.js b/lib/tools/closure.js similarity index 100% rename from lib/closure.js rename to lib/tools/closure.js diff --git a/lib/generic.js b/lib/tools/generic.js similarity index 100% rename from lib/generic.js rename to lib/tools/generic.js diff --git a/lib/genericPromiseChain.js b/lib/tools/genericPromiseChain.js similarity index 100% rename from lib/genericPromiseChain.js rename to lib/tools/genericPromiseChain.js diff --git a/lib/tools.js b/lib/tools/tools.js similarity index 100% rename from lib/tools.js rename to lib/tools/tools.js diff --git a/backdoor.js b/meteor/backdoor.js similarity index 100% rename from backdoor.js rename to meteor/backdoor.js diff --git a/settings.js b/meteor/settings.js similarity index 100% rename from settings.js rename to meteor/settings.js diff --git a/package.js b/package.js index bee88cf..aee3553 100644 --- a/package.js +++ b/package.js @@ -15,8 +15,8 @@ Package.onUse(function (api) { api.addFiles([ - 'settings.js', - 'backdoor.js', + 'meteor/settings.js', + 'meteor/backdoor.js', ], 'server'); From 58c1f256e8a30f2ba4ba7b18e784d7f1b528b371 Mon Sep 17 00:00:00 2001 From: apendua Date: Sat, 24 Jan 2015 18:32:20 +0100 Subject: [PATCH 02/34] Fixed relative paths --- bin/gagarin | 2 +- lib/browser/browser.js | 2 +- lib/browser/browserPromiseChain.js | 4 ++-- lib/browser/helpers.js | 2 +- lib/ddp/makeDDPClient.js | 2 +- lib/meteor/build.js | 6 +++--- lib/meteor/meteor.js | 8 ++++---- lib/meteor/meteorPromiseChain.js | 2 +- lib/meteor/remote.js | 2 +- lib/mocha/gagarin.js | 4 ++-- lib/mocha/interface.js | 14 +++++++------- lib/mongo/makeMongoDB.js | 4 ++-- lib/mongo/mongoDBProcess.js | 2 +- test.js | 3 +-- tests/specs/exceptions.js | 2 +- 15 files changed, 29 insertions(+), 30 deletions(-) diff --git a/bin/gagarin b/bin/gagarin index 888c9c8..bcd40da 100755 --- a/bin/gagarin +++ b/bin/gagarin @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var Gagarin = require('../lib/gagarin'); +var Gagarin = require('../lib/mocha/gagarin'); var path = require('path'); var fs = require('fs'); diff --git a/lib/browser/browser.js b/lib/browser/browser.js index 253753d..c82900d 100644 --- a/lib/browser/browser.js +++ b/lib/browser/browser.js @@ -1,6 +1,6 @@ var BrowserPromiseChain = require('./browserPromiseChain'); var Promise = require('es6-promise').Promise; -var Closure = require('./closure'); +var Closure = require('../tools/closure'); var wd = require('wd'); module.exports = Browser; diff --git a/lib/browser/browserPromiseChain.js b/lib/browser/browserPromiseChain.js index d63a0f9..47b5a6d 100644 --- a/lib/browser/browserPromiseChain.js +++ b/lib/browser/browserPromiseChain.js @@ -1,6 +1,6 @@ -var cleanError = require('./tools').cleanError; +var cleanError = require('../tools/tools').cleanError; var Promise = require('es6-promise').Promise; -var either = require('./tools').either; +var either = require('../tools/tools').either; module.exports = BrowserPromiseChain; diff --git a/lib/browser/helpers.js b/lib/browser/helpers.js index 11cdc99..6be7fa6 100644 --- a/lib/browser/helpers.js +++ b/lib/browser/helpers.js @@ -1,5 +1,5 @@ var expect = require('chai').expect; -var either = require('./tools').either; +var either = require('../tools/tools').either; var Promise = require('es6-promise').Promise; var DEFAULT_TIMEOUT = 5000; diff --git a/lib/ddp/makeDDPClient.js b/lib/ddp/makeDDPClient.js index 63ec96b..63622d4 100644 --- a/lib/ddp/makeDDPClient.js +++ b/lib/ddp/makeDDPClient.js @@ -1,5 +1,5 @@ var Promise = require('es6-promise').Promise; -var generic = require('./generic'); +var generic = require('../tools/generic'); var ddp = require('./ddp'); var defaultGiveUpTimeout = 100; diff --git a/lib/meteor/build.js b/lib/meteor/build.js index 2b9b8f0..ee030b2 100644 --- a/lib/meteor/build.js +++ b/lib/meteor/build.js @@ -1,11 +1,11 @@ -var MongoServerAsPromise = require('./mongoDBProcess'); +var MongoServerAsPromise = require('../mongo/mongoDBProcess'); var Promise = require('es6-promise').Promise; var chalk = require('chalk'); var spawn = require('child_process').spawn; -var tools = require('./tools'); +var tools = require('../tools/tools'); var path = require('path'); var fs = require('fs'); -var version = require('../package.json').version; +var version = require('../../package.json').version; var pty = require('pty.js'); var myBuildPromises = {}; diff --git a/lib/meteor/meteor.js b/lib/meteor/meteor.js index 7e3185d..0c76aac 100644 --- a/lib/meteor/meteor.js +++ b/lib/meteor/meteor.js @@ -4,16 +4,16 @@ * Module dependencies. */ -var makeDDPClientFactory = require('./ddp'); +var makeDDPClientFactory = require('../ddp/ddp'); var MeteorPromiseChain = require('./meteorPromiseChain'); -var MongoDatabase = require('./database'); +var MongoDatabase = require('../mongo/database'); var BuildAsPromise = require('./build'); var portscanner = require('portscanner'); -var Instance = require('./instance'); +var Instance = require('./meteorProcess'); var Promise = require('es6-promise').Promise; var Remote = require('./remote'); var chalk = require('chalk'); -var tools = require('./tools'); +var tools = require('../tools/tools'); var path = require('path'); var url = require('url'); diff --git a/lib/meteor/meteorPromiseChain.js b/lib/meteor/meteorPromiseChain.js index 841913a..ca0abe0 100644 --- a/lib/meteor/meteorPromiseChain.js +++ b/lib/meteor/meteorPromiseChain.js @@ -1,5 +1,5 @@ var Promise = require('es6-promise').Promise; -var either = require('./tools').either; +var either = require('../tools/tools').either; module.exports = MeteorPromiseChain; diff --git a/lib/meteor/remote.js b/lib/meteor/remote.js index 763ca5f..9e8adc6 100644 --- a/lib/meteor/remote.js +++ b/lib/meteor/remote.js @@ -1,4 +1,4 @@ -var Closure = require('./closure'); +var Closure = require('../tools/closure'); module.exports = Remote; diff --git a/lib/mocha/gagarin.js b/lib/mocha/gagarin.js index 0b04793..8fcded7 100644 --- a/lib/mocha/gagarin.js +++ b/lib/mocha/gagarin.js @@ -3,8 +3,8 @@ * Module dependencies. */ -var Meteor = require('./meteor'); -var tools = require('./tools'); +var Meteor = require('../meteor/meteor'); +var tools = require('../tools/tools'); var Mocha = require('mocha'); var chalk = require('chalk'); var path = require('path'); diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 3baead4..ac66cf9 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -3,11 +3,11 @@ * Module dependencies. */ -var Closure = require('./closure'); -var Browser = require('./browser'); -var helpers = require('./helpers'); -var Meteor = require('./meteor'); -var tools = require('./tools'); +var Closure = require('../tools/closure'); +var Browser = require('../browser/browser'); +var helpers = require('../browser/helpers'); +var Meteor = require('../meteor/meteor'); +var tools = require('../tools/tools'); var Mocha = require('mocha'); var Fiber = require('fibers'); var Future = require('fibers/future'); @@ -161,7 +161,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.ddp = function (server, options) { - var makeDDPClient = require('./makeDDPClient'); + var makeDDPClient = require('../ddp/makeDDPClient'); var ddpSetupProvider = null; options = options || {}; @@ -187,7 +187,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.mongo = function (options) { - var makeMongoDB = require('./makeMongoDB'); + var makeMongoDB = require('../mongo/makeMongoDB'); var myHelpers = {}; diff --git a/lib/mongo/makeMongoDB.js b/lib/mongo/makeMongoDB.js index c6c75d8..3e6bab6 100644 --- a/lib/mongo/makeMongoDB.js +++ b/lib/mongo/makeMongoDB.js @@ -1,8 +1,8 @@ var MongoDatabase = require('./database'); var MongoClient = require('mongodb').MongoClient; var Promise = require('es6-promise').Promise; -var generic = require('./generic'); -var either = require('./tools').either; +var generic = require('../tools/generic'); +var either = require('../tools/tools').either; module.exports = function makeMongoDB (options, helpers) { diff --git a/lib/mongo/mongoDBProcess.js b/lib/mongo/mongoDBProcess.js index ec3ec14..719bb43 100644 --- a/lib/mongo/mongoDBProcess.js +++ b/lib/mongo/mongoDBProcess.js @@ -4,7 +4,7 @@ var Promise = require('es6-promise').Promise; var mkdirp = require('mkdirp'); var spawn = require('child_process').spawn; var path = require('path'); -var tools = require('./tools'); +var tools = require('../tools/tools'); var either = tools.either; var fs = require('fs'); diff --git a/test.js b/test.js index deee74c..4ae3aa5 100755 --- a/test.js +++ b/test.js @@ -1,11 +1,10 @@ #!/usr/bin/env node -var Gagarin = require('./lib/gagarin'); +var Gagarin = require('./lib/mocha/gagarin'); var path = require('path'); var fs = require('fs'); var pathToApp = path.resolve('./tests/example'); var program = require('commander'); -var helpers = require('./lib/helpers'); program .option('-g, --grep ', 'only run tests matching ') diff --git a/tests/specs/exceptions.js b/tests/specs/exceptions.js index 03c4289..6b62452 100644 --- a/tests/specs/exceptions.js +++ b/tests/specs/exceptions.js @@ -1,5 +1,5 @@ var expect = require('chai').expect; -var Meteor = require('../../lib/meteor'); +var Meteor = require('../../lib/meteor/meteor'); var path = require('path'); describe('Reporting Exceptions', function () { From e0c3b4fd4f1f07a42e343bb5f7c80b126d914ec9 Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 09:24:25 +0100 Subject: [PATCH 03/34] Fixed the find script --- find | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/find b/find index 244e0ca..8056228 100755 --- a/find +++ b/find @@ -1,2 +1,2 @@ #!/bin/bash -grep -R $1 lib/*.js tests/specs/*.js +grep -R $1 lib/*/*.js tests/specs/*.js From f156cc37386272fbfc737811576b420acdef6aff Mon Sep 17 00:00:00 2001 From: Tomasz Lenarcik Date: Mon, 26 Jan 2015 10:09:15 +0100 Subject: [PATCH 04/34] Added a quick note about breaking changes in 0.4.0 --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 615bf2f..7ddd8f3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ ![gagarin](https://s3.amazonaws.com/gagarinjs/assets/gagarinLogo.svg) +# Important note + +Since version `0.4.0` the `server` object created by `meteor()` helper no longer has the `location` property. To make sure the `browser` starts on the proper location, you need to pass `server` as the first argument, so +```javascript +var server = meteor(); +var client = browser(server); // before 0.4.0 you would use server.location here +``` + # gagarin [![Circle CI](https://circleci.com/gh/anticoders/gagarin/tree/devel.svg?style=svg)](https://circleci.com/gh/anticoders/gagarin/tree/devel) Gagarin is a tool you can use in your tests to run Meteor apps in a sandboxed environment. It's useful when you need more refined control over the meteor processes and test fancy things, e.g. the behavior of your app on server restarts or when you have multiple app instances writing to the same database. This is currently not achievable with the official Meteor testing framework. @@ -76,7 +84,7 @@ A test suite using both server and client may look like this: ```javascript describe('You can also use browser in your tests', function () { var server = meteor(); - var client = browser(server.location + "/path/to/some/view") + var client = browser(server); it('should just work', function () { return client.execute(function () { From 1320d82b15c126dac7c7826d475acb13cea90e97 Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 12:40:05 +0100 Subject: [PATCH 05/34] Use index.js for simplicity --- lib/browser/{browser.js => index.js} | 0 lib/ddp/{makeDDPClient.js => index.js} | 0 lib/meteor/{meteor.js => index.js} | 0 lib/mongo/{makeMongoDB.js => index.js} | 0 lib/tools/{tools.js => index.js} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename lib/browser/{browser.js => index.js} (100%) rename lib/ddp/{makeDDPClient.js => index.js} (100%) rename lib/meteor/{meteor.js => index.js} (100%) rename lib/mongo/{makeMongoDB.js => index.js} (100%) rename lib/tools/{tools.js => index.js} (100%) diff --git a/lib/browser/browser.js b/lib/browser/index.js similarity index 100% rename from lib/browser/browser.js rename to lib/browser/index.js diff --git a/lib/ddp/makeDDPClient.js b/lib/ddp/index.js similarity index 100% rename from lib/ddp/makeDDPClient.js rename to lib/ddp/index.js diff --git a/lib/meteor/meteor.js b/lib/meteor/index.js similarity index 100% rename from lib/meteor/meteor.js rename to lib/meteor/index.js diff --git a/lib/mongo/makeMongoDB.js b/lib/mongo/index.js similarity index 100% rename from lib/mongo/makeMongoDB.js rename to lib/mongo/index.js diff --git a/lib/tools/tools.js b/lib/tools/index.js similarity index 100% rename from lib/tools/tools.js rename to lib/tools/index.js From 8270a25c8b374f8f8fc8122c9502713a2dc2e834 Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 12:43:25 +0100 Subject: [PATCH 06/34] Updated relative paths --- lib/browser/browserPromiseChain.js | 4 ++-- lib/browser/helpers.js | 2 +- lib/meteor/build.js | 2 +- lib/meteor/index.js | 2 +- lib/meteor/meteorPromiseChain.js | 2 +- lib/mocha/gagarin.js | 4 ++-- lib/mocha/interface.js | 10 +++++----- lib/mongo/index.js | 2 +- lib/mongo/mongoDBProcess.js | 2 +- lib/tools/generic.js | 2 +- lib/tools/genericPromiseChain.js | 2 +- tests/specs/exceptions.js | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/browser/browserPromiseChain.js b/lib/browser/browserPromiseChain.js index 47b5a6d..dabb1a4 100644 --- a/lib/browser/browserPromiseChain.js +++ b/lib/browser/browserPromiseChain.js @@ -1,6 +1,6 @@ -var cleanError = require('../tools/tools').cleanError; +var cleanError = require('../tools').cleanError; var Promise = require('es6-promise').Promise; -var either = require('../tools/tools').either; +var either = require('../tools').either; module.exports = BrowserPromiseChain; diff --git a/lib/browser/helpers.js b/lib/browser/helpers.js index 6be7fa6..73ba8fa 100644 --- a/lib/browser/helpers.js +++ b/lib/browser/helpers.js @@ -1,5 +1,5 @@ var expect = require('chai').expect; -var either = require('../tools/tools').either; +var either = require('../tools').either; var Promise = require('es6-promise').Promise; var DEFAULT_TIMEOUT = 5000; diff --git a/lib/meteor/build.js b/lib/meteor/build.js index ee030b2..262f3a5 100644 --- a/lib/meteor/build.js +++ b/lib/meteor/build.js @@ -2,7 +2,7 @@ var MongoServerAsPromise = require('../mongo/mongoDBProcess'); var Promise = require('es6-promise').Promise; var chalk = require('chalk'); var spawn = require('child_process').spawn; -var tools = require('../tools/tools'); +var tools = require('../tools'); var path = require('path'); var fs = require('fs'); var version = require('../../package.json').version; diff --git a/lib/meteor/index.js b/lib/meteor/index.js index 0c76aac..b87e1a2 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -13,7 +13,7 @@ var Instance = require('./meteorProcess'); var Promise = require('es6-promise').Promise; var Remote = require('./remote'); var chalk = require('chalk'); -var tools = require('../tools/tools'); +var tools = require('../tools'); var path = require('path'); var url = require('url'); diff --git a/lib/meteor/meteorPromiseChain.js b/lib/meteor/meteorPromiseChain.js index ca0abe0..b8f3370 100644 --- a/lib/meteor/meteorPromiseChain.js +++ b/lib/meteor/meteorPromiseChain.js @@ -1,5 +1,5 @@ var Promise = require('es6-promise').Promise; -var either = require('../tools/tools').either; +var either = require('../tools').either; module.exports = MeteorPromiseChain; diff --git a/lib/mocha/gagarin.js b/lib/mocha/gagarin.js index 8fcded7..b267fc5 100644 --- a/lib/mocha/gagarin.js +++ b/lib/mocha/gagarin.js @@ -3,8 +3,8 @@ * Module dependencies. */ -var Meteor = require('../meteor/meteor'); -var tools = require('../tools/tools'); +var Meteor = require('../meteor'); +var tools = require('../tools'); var Mocha = require('mocha'); var chalk = require('chalk'); var path = require('path'); diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index ac66cf9..7220ffa 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -4,10 +4,10 @@ */ var Closure = require('../tools/closure'); -var Browser = require('../browser/browser'); +var Browser = require('../browser'); var helpers = require('../browser/helpers'); -var Meteor = require('../meteor/meteor'); -var tools = require('../tools/tools'); +var Meteor = require('../meteor'); +var tools = require('../tools'); var Mocha = require('mocha'); var Fiber = require('fibers'); var Future = require('fibers/future'); @@ -161,7 +161,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.ddp = function (server, options) { - var makeDDPClient = require('../ddp/makeDDPClient'); + var makeDDPClient = require('../ddp'); var ddpSetupProvider = null; options = options || {}; @@ -187,7 +187,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.mongo = function (options) { - var makeMongoDB = require('../mongo/makeMongoDB'); + var makeMongoDB = require('../mongo'); var myHelpers = {}; diff --git a/lib/mongo/index.js b/lib/mongo/index.js index 3e6bab6..981fcde 100644 --- a/lib/mongo/index.js +++ b/lib/mongo/index.js @@ -2,7 +2,7 @@ var MongoDatabase = require('./database'); var MongoClient = require('mongodb').MongoClient; var Promise = require('es6-promise').Promise; var generic = require('../tools/generic'); -var either = require('../tools/tools').either; +var either = require('../tools').either; module.exports = function makeMongoDB (options, helpers) { diff --git a/lib/mongo/mongoDBProcess.js b/lib/mongo/mongoDBProcess.js index 719bb43..7d20b4e 100644 --- a/lib/mongo/mongoDBProcess.js +++ b/lib/mongo/mongoDBProcess.js @@ -4,7 +4,7 @@ var Promise = require('es6-promise').Promise; var mkdirp = require('mkdirp'); var spawn = require('child_process').spawn; var path = require('path'); -var tools = require('../tools/tools'); +var tools = require('../tools'); var either = tools.either; var fs = require('fs'); diff --git a/lib/tools/generic.js b/lib/tools/generic.js index bb8c258..537802c 100644 --- a/lib/tools/generic.js +++ b/lib/tools/generic.js @@ -1,6 +1,6 @@ var genericPromiseChain = require('./genericPromiseChain'); var Promise = require('es6-promise').Promise; -var either = require('./tools').either; +var either = require('./index').either; module.exports = function generic(methods, myPrototype) { diff --git a/lib/tools/genericPromiseChain.js b/lib/tools/genericPromiseChain.js index d82567e..7fda618 100644 --- a/lib/tools/genericPromiseChain.js +++ b/lib/tools/genericPromiseChain.js @@ -1,5 +1,5 @@ var Promise = require('es6-promise').Promise; -var either = require('./tools').either; +var either = require('./index').either; module.exports = function genericPromiseChain(methods, myPrototype) { diff --git a/tests/specs/exceptions.js b/tests/specs/exceptions.js index 6b62452..03c4289 100644 --- a/tests/specs/exceptions.js +++ b/tests/specs/exceptions.js @@ -1,5 +1,5 @@ var expect = require('chai').expect; -var Meteor = require('../../lib/meteor/meteor'); +var Meteor = require('../../lib/meteor'); var path = require('path'); describe('Reporting Exceptions', function () { From 96144d337005406a988d1b66c474845ab70177f5 Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 17:56:38 +0100 Subject: [PATCH 07/34] Renaming another file --- lib/ddp/{ddp.js => ddpClientManager.js} | 0 lib/ddp/index.js | 2 +- lib/meteor/index.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename lib/ddp/{ddp.js => ddpClientManager.js} (100%) diff --git a/lib/ddp/ddp.js b/lib/ddp/ddpClientManager.js similarity index 100% rename from lib/ddp/ddp.js rename to lib/ddp/ddpClientManager.js diff --git a/lib/ddp/index.js b/lib/ddp/index.js index 63622d4..9560251 100644 --- a/lib/ddp/index.js +++ b/lib/ddp/index.js @@ -1,6 +1,6 @@ var Promise = require('es6-promise').Promise; var generic = require('../tools/generic'); -var ddp = require('./ddp'); +var ddp = require('./ddpClientManager'); var defaultGiveUpTimeout = 100; diff --git a/lib/meteor/index.js b/lib/meteor/index.js index b87e1a2..1348931 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var makeDDPClientFactory = require('../ddp/ddp'); +var makeDDPClientFactory = require('../ddp/ddpClientManager'); var MeteorPromiseChain = require('./meteorPromiseChain'); var MongoDatabase = require('../mongo/database'); var BuildAsPromise = require('./build'); From c2060badb72719665d1beda2b144f7e2a2c7312f Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 18:10:04 +0100 Subject: [PATCH 08/34] Simplify ddpClientManager --- lib/ddp/ddpClientManager.js | 7 ++----- lib/ddp/index.js | 14 ++++++++++++-- lib/meteor/index.js | 13 ++++++++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/ddp/ddpClientManager.js b/lib/ddp/ddpClientManager.js index befc930..a6bc08c 100644 --- a/lib/ddp/ddpClientManager.js +++ b/lib/ddp/ddpClientManager.js @@ -1,7 +1,7 @@ var DDPClient = require('ddp'); var Promise = require('es6-promise').Promise; -module.exports = function makeDDPClientFactory (ddpSetupProvider) { +module.exports = function createDDPClientManager () { "use strict"; var self = this; @@ -11,9 +11,7 @@ module.exports = function makeDDPClientFactory (ddpSetupProvider) { var port = null; var host = null; - return function ddpClientAsPromise () { - - return ddpSetupProvider().then(function (setup) { + return function getDDPClient (setup) { if (code === setup.code && port === setup.port && ddpClientPromise) { return ddpClientPromise; @@ -75,7 +73,6 @@ module.exports = function makeDDPClientFactory (ddpSetupProvider) { }); return ddpClientPromise; - }); }; }; diff --git a/lib/ddp/index.js b/lib/ddp/index.js index 9560251..95b45b1 100644 --- a/lib/ddp/index.js +++ b/lib/ddp/index.js @@ -1,6 +1,7 @@ var Promise = require('es6-promise').Promise; var generic = require('../tools/generic'); -var ddp = require('./ddpClientManager'); + +var createDDPClientManager = require('./ddpClientManager'); var defaultGiveUpTimeout = 100; @@ -93,7 +94,16 @@ module.exports = function makeDDPClient (ddpSetupProvider, helpers) { var DDPGeneric = generic(methods, myPrototype); var DDPClient = function () { - DDPGeneric.call(this, ddp(ddpSetupProvider)); + + var getDDPClient = createDDPClientManager(); + + function ddpClient () { + return ddpSetupProvider().then(function (setup) { + return getDDPClient(setup); + }); + } + + DDPGeneric.call(this, ddpClient); }; DDPClient.prototype = Object.create(new DDPGeneric(), { diff --git a/lib/meteor/index.js b/lib/meteor/index.js index 1348931..28fe2f1 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -4,7 +4,8 @@ * Module dependencies. */ -var makeDDPClientFactory = require('../ddp/ddpClientManager'); +var createDDPClientManager = require('../ddp/ddpClientManager'); + var MeteorPromiseChain = require('./meteorPromiseChain'); var MongoDatabase = require('../mongo/database'); var BuildAsPromise = require('./build'); @@ -60,7 +61,7 @@ function Meteor (options) { mongoUrlPromise = databasePromise.getMongoUrlPromise(); } - var ddpClientAsPromise = makeDDPClientFactory(ddpSetupProvider); + var getDDPClient = createDDPClientManager(); var instance = null; var meteorPromise = null; @@ -75,7 +76,13 @@ function Meteor (options) { var meteorPort = null; var meteorDatabase = options.dbName || 'gagarin_' + (new Date()).getTime(); - var remote = new Remote(ddpClientAsPromise, serverControllerProvider); + function ddpClient () { + return ddpSetupProvider().then(function (setup) { + return getDDPClient(setup); + }); + } + + var remote = new Remote(ddpClient, serverControllerProvider); var dummyController = { start : noop, From 7757e8fa070f5667e0d1f7edf073a43f5eda564f Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 18:13:16 +0100 Subject: [PATCH 09/34] Moved once() to tools --- lib/ddp/index.js | 13 ------------- lib/mongo/mongoDBProcess.js | 15 +-------------- lib/tools/index.js | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/lib/ddp/index.js b/lib/ddp/index.js index 95b45b1..b96412f 100644 --- a/lib/ddp/index.js +++ b/lib/ddp/index.js @@ -114,16 +114,3 @@ module.exports = function makeDDPClient (ddpSetupProvider, helpers) { } -/** - * Make a version of function that can only be called once. - */ -function once (cb) { - var done = false; - return function () { - if (done) { - return; - } - done = true; - return cb.apply(this, arguments); - } -} diff --git a/lib/mongo/mongoDBProcess.js b/lib/mongo/mongoDBProcess.js index 7d20b4e..583f7e8 100644 --- a/lib/mongo/mongoDBProcess.js +++ b/lib/mongo/mongoDBProcess.js @@ -72,7 +72,7 @@ module.exports = function MongoDBProcess (options) { }); }; - mongoProcess.stdout.on('data', debounce(once(connectWithRetry), 1000)); + mongoProcess.stdout.on('data', debounce(tools.once(connectWithRetry), 1000)); // TODO: get rid of this log before the next release mongoProcess.stderr.on('data', function (data) { @@ -94,16 +94,3 @@ module.exports = function MongoDBProcess (options) { return myMongoServerPromises[pathToApp]; }; -/** - * Make a version of function that can only be called once. - */ -function once (cb) { - var done = false; - return function () { - if (done) { - return; - } - done = true; - return cb.apply(this, arguments); - } -} diff --git a/lib/tools/index.js b/lib/tools/index.js index 011f8ef..c635c52 100644 --- a/lib/tools/index.js +++ b/lib/tools/index.js @@ -206,6 +206,20 @@ module.exports = { } }, + /** + * Make a version of function that can only be called once. + */ + once: function (cb) { + var done = false; + return function () { + if (done) { + return; + } + done = true; + return cb.apply(this, arguments); + } + }, + }; // since 0.9.0, the format is METEOR@x.x.x From d08545eaf3dfdaa438cbed2d244165a102a691b9 Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 18:15:19 +0100 Subject: [PATCH 10/34] Cleaning up code --- lib/ddp/ddpClientManager.js | 97 ++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/lib/ddp/ddpClientManager.js b/lib/ddp/ddpClientManager.js index a6bc08c..f876cbe 100644 --- a/lib/ddp/ddpClientManager.js +++ b/lib/ddp/ddpClientManager.js @@ -13,66 +13,61 @@ module.exports = function createDDPClientManager () { return function getDDPClient (setup) { - if (code === setup.code && port === setup.port && ddpClientPromise) { - return ddpClientPromise; - } + if (code === setup.code && port === setup.port && ddpClientPromise) { + return ddpClientPromise; + } - code = setup.code; - port = setup.port; - host = setup.host || 'localhost'; - - ddpClientPromise = new Promise(function (resolve, reject) { - - ddpClient && ddpClient.close(); + code = setup.code; + port = setup.port; + host = setup.host || 'localhost'; + + ddpClientPromise = new Promise(function (resolve, reject) { - ddpClient = new DDPClient({ - host : host, - port : port, - path : "websocket", - ssl : false, - autoReconnect : true, - autoReconnectTimer : 500, - maintainCollections : true, - ddpVersion : '1' - }); + ddpClient && ddpClient.close(); - //ddpClient.connect(function (err, wasReconnected) { - // if (err) { - // return reject(err); - // } - // resolve(ddpClient); - //}); + ddpClient = new DDPClient({ + host : host, + port : port, + path : "websocket", + ssl : false, + //------------------------- + autoReconnect : true, + autoReconnectTimer : 500, + maintainCollections : true, + ddpVersion : '1' + }); - var retryCount = 5; + var retryCount = 5; - // XXX we need this because the WebApp.httpServer may start with some delay; - // in fact, this should be handled within the app itself - (function tryConnect() { - ddpClient.connect(function (err, wasReconnected) { - if (err) { - if (retryCount <= 0) { - reject(typeof err === 'string' ? new Error(err) : err); - } else if (!wasReconnected) { - retryCount -= 1; - setTimeout(tryConnect, 500); - } - } else { - resolve(ddpClient); + // XXX we need this because the WebApp.httpServer may start with some delay; + // in fact, this should be handled within the app itself + (function tryConnect() { + ddpClient.connect(function (err, wasReconnected) { + if (err) { + if (retryCount <= 0) { + reject(typeof err === 'string' ? new Error(err) : err); + } else if (!wasReconnected) { + retryCount -= 1; + setTimeout(tryConnect, 500); } - }); - })(); + } else { + resolve(ddpClient); + } + }); + })(); - // TODO: re-enable this feature when we make timeout configurable - //setTimeout(function () { - // reject(new Error('timeout while waiting to establish ddp connection')); - //}, 2000); + // TODO: re-enable this feature when we make timeout configurable + //setTimeout(function () { + // reject(new Error('timeout while waiting to establish ddp connection')); + //}, 2000); - //------------------------------------------------------------- - //\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ - //------------------------------------------------------------- - }); + //------------------------------------------------------------- + //\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ + //------------------------------------------------------------- + }); + + return ddpClientPromise; - return ddpClientPromise; }; }; From f17a58b4a1edb32c8c6ded6c7c8a92fa21c6a508 Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 20:30:39 +0100 Subject: [PATCH 11/34] BrowserPromiseChain now uses generics --- lib/browser/browserManager.js | 134 ++++++++++ lib/browser/helpers.js | 34 +-- lib/browser/index.js | 232 +++++------------- .../{browserPromiseChain.js => methods.js} | 218 ++-------------- lib/mocha/interface.js | 5 +- lib/tools/generic.js | 4 +- lib/tools/genericPromiseChain.js | 26 +- 7 files changed, 262 insertions(+), 391 deletions(-) create mode 100644 lib/browser/browserManager.js rename lib/browser/{browserPromiseChain.js => methods.js} (56%) diff --git a/lib/browser/browserManager.js b/lib/browser/browserManager.js new file mode 100644 index 0000000..f3865d4 --- /dev/null +++ b/lib/browser/browserManager.js @@ -0,0 +1,134 @@ + +var portscanner = require('portscanner'); +var Promise = require('es6-promise').Promise; +var either = require('../tools').either; +var url = require('url'); +var wd = require('wd'); + +module.exports = function createBrowserManager (options) { + "use strict"; + + var driverLocation = options.webdriver || "http://localhost:9515"; // chromedriver default + var browser = wd.remote(driverLocation); + var dontWaitForMeteor = options.dontWaitForMeteor !== undefined ? !!options.dontWaitForMeteor : false; + var meteorLoadTimeout = options.meteorLoadTimeout !== undefined ? options.meteorLoadTimeout : 2000; + var capabilities = options.capabilities || {}; + var windowSize = options.windowSize; + var myLocation = options.location || "http://localhost:3000"; + var browserPromise = null; + var ddpSetupProvider = typeof myLocation === 'string' ? Promise.resolve(myLocation) : myLocation._ddpSetupProvider; + + if (!ddpSetupProvider) { + throw new Error('the location option must be either string or a meteor server'); + } + + return function getBrowser () { + + var isInitialized = false; + + if (browserPromise) { + return browserPromise; + } + + browserPromise = new Promise(function (resolve, reject) { + + function _reject (err) { + if (isInitialized) { + browser.quit(); + } + if (typeof err === 'string') { + return reject(new Error(err)); + } + reject(err); + } + + var driverLocationParsed = url.parse(driverLocation); + + portscanner.checkPortStatus(driverLocationParsed.port, driverLocationParsed.hostname, function (err, status) { + if (err || status !== 'open') { + return _reject(err || 'webdriver not found on ' + driverLocation); + } + browser.init(capabilities, either(_reject).or(function () { + + isInitialized = true; + + if (windowSize) { + browser.setWindowSize(windowSize.width, windowSize.height, either(_reject).or(function () { + afterResize(either(_reject).or(resolve)); + })); + } else { + afterResize(either(_reject).or(resolve)); + } + })); + }); + }); + + return browserPromise; + }; + + function afterResize (done) { + if (ddpSetupProvider) { + ddpSetupProvider().then(function (setup) { + if (setup.host) { + getLocation(setup.host, done); + } else { + getLocation('http://localhost:' + setup.port, done); + } + }).catch(done); + } else { + done(null, browser); + } + } + + function getLocation (url, done) { + browser.get(url, function (err) { + if (err) { + return done(err); + } + + if (dontWaitForMeteor) { // already done, so lets just resolve + return done(null, browser); + } + + browser.setAsyncScriptTimeout(meteorLoadTimeout, function (err) { + // XXX asyncScriptTimeout is only relevant in "onStartup" routine + if (err) { + return done(err); + } + + var handle1 = null; + var handle2 = null; + + handle2 = setTimeout(function () { + clearTimeout(handle1); + done(new Error('Meteor code is still not loaded after ' + meteorLoadTimeout + ' ms')); + }, meteorLoadTimeout); + + // wait until all meteor-related files are loaded + (function test() { + browser.execute("return !!window.Meteor && !!window.Meteor.startup", function (err, isOK) { + if (err) { + clearTimeout(handle2); + return done(err); + } + if (isOK) { + onStartup(function (err) { + clearTimeout(handle2); + done(err, !err && browser); + }); + } else { + handle1 = setTimeout(test, 100); + } + }); + })(); + }); + }); + } + + function onStartup (done) { + browser.executeAsync(function (cb) { + Meteor.startup(cb); + }, done); + } + +} diff --git a/lib/browser/helpers.js b/lib/browser/helpers.js index 73ba8fa..fc2555a 100644 --- a/lib/browser/helpers.js +++ b/lib/browser/helpers.js @@ -88,15 +88,16 @@ exports.client = { }, click: function (selector, keys) { - return this._callWebDriver('elementByCssSelectorOrNull', selector) - .then(function (element) { - if (!element) { - throw new Error('element ' + selector + ' does not exists'); - } - return new Promise(function (resolve, reject) { - element.click(either(reject).or(resolve)); - }); + return this.__custom__(function (operand, done) { + operand.browser.elementByCssSelectorOrNull(selector, done); + }).then(function (element) { + if (!element) { + throw new Error('element ' + selector + ' does not exists'); + } + return new Promise(function (resolve, reject) { + element.click(either(reject).or(resolve)); }); + }); }, // we should probably call this helper "typeKeys" @@ -109,15 +110,16 @@ exports.client = { //}, sendKeys: function (selector, keys) { - return this._callWebDriver('elementByCssSelectorOrNull', selector) - .then(function (element) { - if (!element) { - throw new Error('element ' + selector + ' does not exists'); - } - return new Promise(function (resolve, reject) { - element.sendKeys(keys, either(reject).or(resolve)); - }); + return this.__custom__(function (operand, done) { + operand.browser.elementByCssSelectorOrNull(selector, done); + }).then(function (element) { + if (!element) { + throw new Error('element ' + selector + ' does not exists'); + } + return new Promise(function (resolve, reject) { + element.sendKeys(keys, either(reject).or(resolve)); }); + }); }, signUp: function (options) { diff --git a/lib/browser/index.js b/lib/browser/index.js index c82900d..de06833 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -1,185 +1,83 @@ -var BrowserPromiseChain = require('./browserPromiseChain'); -var Promise = require('es6-promise').Promise; -var Closure = require('../tools/closure'); -var wd = require('wd'); -module.exports = Browser; +var createBrowserManager = require('./browserManager'); +var browserMethods = require('./methods'); +var Closure = require('../tools/closure'); +var generic = require('../tools/generic'); -function Browser (options) { +module.exports = function createBrowser (options) { "use strict"; - var self = this; - var closure = null; - var driverLocation = options.webdriver || "http://localhost:9515"; - var browser = wd.remote(driverLocation); // default to chromedriver - var dontWaitForMeteor = options.dontWaitForMeteor !== undefined ? !!options.dontWaitForMeteor : false; - var meteorLoadTimeout = options.meteorLoadTimeout !== undefined ? options.meteorLoadTimeout : 2000; - var browserPromise = null; - var capabilities = options.capabilities || {}; - var windowSize = options.windowSize; - var portscanner = require('portscanner'); - var URL = require('url'); - var myLocation = options.location || "http://localhost:3000"; - var ddpSetupProvider = typeof myLocation === 'string' ? Promise.resolve(myLocation) : myLocation._ddpSetupProvider; - - if (!ddpSetupProvider) { - throw new Error('the location option must be either string or a meteor server'); - } + var helpers = options.helpers || {}; - self.getBrowserPromise = function () { + var myPrototype = Object.create(helpers); - // XXX theoretically we could use promiseChainRemote here to simplify the code - // but I don't really want to mess up with wd's promises implementation, since - // we have our own here ... and all we want from them is a stable async API + myPrototype.init = function () { + return this.branch().then(function () { + // ... + }); + } - var isInitialized = false; + var methods = [ + // these methods are copy/pasted from wd + 'newWindow', + 'close', + 'quit', + 'status', + 'get', + 'refresh', + 'maximize', + 'getWindowSize', + 'setWindowSize', + 'forward', + 'back', + 'takeScreenshot', + 'saveScreenshot', + 'title', + 'allCookies', + 'setCookie', + 'deleteAllCookies', + 'deleteCookie', + 'getLocation', + ]; + + Object.keys(browserMethods).forEach(function (name) { + myPrototype[name] = browserMethods[name]; + }); - if (browserPromise) { - return browserPromise; + var BrowserGeneric = generic(methods, myPrototype, function (operand, name, args, done) { + if (!operand.browser) { + done(new Error('operand.browser is undefined')); + } else if (!operand.browser[name]) { + done(new Error('operand.browser does not implement method: ' + name)); + } else { + args.push(done); + operand.browser[name].apply(operand.browser, args); } + }); - return browserPromise = new Promise(function (resolve, reject) { - - var _reject = function (err) { - if (isInitialized) { - browser.quit(); - } - if (typeof err === 'string') { - return reject(new Error(err)); - } - reject(err); - } - - var driverLocationParsed = URL.parse(driverLocation); - - portscanner.checkPortStatus(driverLocationParsed.port, driverLocationParsed.hostname, function (error, status) { - if (status !== 'open') { - _reject('webdriver not found on ' + driverLocation); - } - browser.init(capabilities, function (err) { - if (err) { - return _reject(err); - } - isInitialized = true; - //------------------- - if (windowSize) { - browser.setWindowSize(windowSize.width, windowSize.height, function (err) { - if (err) { - _reject(err); - } - afterResize(); - }); - } else { - afterResize(); - } - - function afterResize() { - if (ddpSetupProvider) { - ddpSetupProvider().then(function (setup) { - if (setup.host) { - getLocation(setup.host); - } else { - getLocation('http://localhost:' + setup.port); - } - }).catch(_reject); - } else { - resolve({ browser: browser, closure: closure }); - } - } - - function getLocation(myLocation) { - browser.get(myLocation, function (err) { - if (err) { - return _reject(err); - } - if (dontWaitForMeteor) { // already done, so lets just resolve - return resolve({ browser: browser, closure: closure }); - } - browser.setAsyncScriptTimeout(meteorLoadTimeout, function (err) { - // XXX asyncScriptTimeout is only relevant in "onStartup" routine - if (err) { - return _reject(err); - } - - var handle1 = null; - var handle2 = null; - - handle2 = setTimeout(function () { - clearTimeout(handle1); - _reject(new Error('Meteor code is still not loaded after ' + meteorLoadTimeout + ' ms')); - }, meteorLoadTimeout); - - // wait until all meteor-related files are loaded - (function test() { - browser.execute("return !!window.Meteor && !!window.Meteor.startup", function (err, isOK) { - if (err) { - clearTimeout(handle2); - return _reject(err); - } - - if (isOK) { - onStartup(function () { - clearTimeout(handle2); - resolve({ browser: browser, closure: closure }); - }); - } else { - handle1 = setTimeout(test, 100); - } - }); - })(); - - function onStartup(callback) { - browser.executeAsync(function (cb) { - Meteor.startup(cb); - }, function (err) { - if (err) { - return _reject(err); - } - callback(); - }); - } - - }); // setAsyncScriptTimeout - }); // get - } // getLocation - }); // init - }); // checkPortStatus - }); // Promise - } - - // adds "useClosure" and "closure" methods - Closure.mixin(self); - closure = self.closure.bind(self); - - // set-up helpers - options.helpers = options.helpers || {}; + var Browser = function () { - self.helpers = {}; - self.methods = Object.keys(options.helpers).concat(BrowserPromiseChain.methods); + var getBrowser = createBrowserManager(options); + var closure = null; - Object.keys(options.helpers).forEach(function (key) { - if (self[key] !== undefined) { - console.warn('helper ' + key + ' conflicts with some Browser method'); + function operand () { + return getBrowser().then(function (browser) { + return { browser: browser, closure: closure }; + }); } - // TODO: for each helper we should create a new promise chain, - // the same way we do it for BrowserPromiseChain.methods - self[key] = self.helpers[key] = options.helpers[key]; - }); -} -BrowserPromiseChain.methods.forEach(function (name) { - "use strict"; + BrowserGeneric.call(this, operand); + + Closure.mixin(this); // adds "useClosure" and "closure" methods + closure = this.closure.bind(this); - Browser.prototype[name] = function () { - var chain = new BrowserPromiseChain(this.getBrowserPromise(), this.helpers); - return chain[name].apply(chain, arguments); }; - Browser.prototype.init = function () { - var chain = new BrowserPromiseChain(this.getBrowserPromise(), this.helpers); - return chain.then(function () { - // nothing here ... - }); - } + Browser.prototype = Object.create(new BrowserGeneric(), { + methods: { value: [].concat(Object.keys(myPrototype), Object.keys(helpers), BrowserGeneric.prototype.methods) } + }); + + return new Browser(); + +} -}); diff --git a/lib/browser/browserPromiseChain.js b/lib/browser/methods.js similarity index 56% rename from lib/browser/browserPromiseChain.js rename to lib/browser/methods.js index dabb1a4..1b8601f 100644 --- a/lib/browser/browserPromiseChain.js +++ b/lib/browser/methods.js @@ -1,180 +1,8 @@ var cleanError = require('../tools').cleanError; -var Promise = require('es6-promise').Promise; -var either = require('../tools').either; -module.exports = BrowserPromiseChain; +module.exports = {}; -//---------------------- -// BROWSER PROMISE CHAIN -//---------------------- - -function BrowserPromiseChain (operand, helpers) { - - "use strict"; - - var self = this; - - helpers = helpers || {}; - - this._operand = operand; - this._promise = operand; - this._helpers = helpers; - - // install helpers - Object.keys(helpers).forEach(function (key) { - if (self[key] !== undefined) { - console.warn('helper ' + key + ' conflicts with some BrowserPromiseChain method'); - } - self[key] = helpers[key]; - }); -} - -[ 'then', 'catch' ].forEach(function (name) { - "use strict"; - - BrowserPromiseChain.prototype[name] = function () { - - this._promise = this._promise[name].apply(this._promise, arguments); - return this; - }; - -}); - -var webdriverMethods = [ - 'newWindow', - 'close', - 'quit', - 'status', - //'execute', - //'executeAsync', - 'get', - 'refresh', - 'maximize', - 'getWindowSize', - 'setWindowSize', - 'forward', - 'back', - //'waitForConditionInBrowser', - //'setAsyncScriptTimeout', - 'takeScreenshot', - 'saveScreenshot', - 'title', - 'allCookies', - 'setCookie', - 'deleteAllCookies', - 'deleteCookie', - //'getOrientation', - //'setOrientation', - 'getLocation', -]; - -webdriverMethods.forEach(function (name) { - "use strict"; - - BrowserPromiseChain.prototype[name] = function () { - return this._applyWebDriver(name, Array.prototype.slice.call(arguments, 0)); - }; - -}); - -BrowserPromiseChain.methods = webdriverMethods.concat([ - '_applyWebDriver', - '_callWebDriver', - 'catch', - 'then', - 'always', - 'sleep', - 'expectError', - 'noWait', - 'yet', - 'execute', - 'timeout', - 'promise', - 'wait', -]); - -/** - * Update the current promise and return this to allow chaining. - */ -BrowserPromiseChain.prototype._applyWebDriver = function (name, args, custom) { - "use strict"; - - args = Array.prototype.slice.call(args, 0); // shallow copy the arguments - - var self = this; - self._promise = Promise.all([ - self._operand, self._promise - ]).then(function (all) { - return new Promise(function (resolve, reject) { - var operand = all[0]; - if (!operand || !operand.browser) { - reject(new Error('operand.browser is undefined')); - } else if (!operand.browser[name]) { - reject(new Error('operand.browser does not implement method: ' + name)); - } else { - args.push(either(reject).or(resolve)); - if (typeof custom === 'function') { - return custom(operand, name, args); - } - return operand.browser[name].apply(operand.browser, args); - } - }); - }); - return self; -} - -BrowserPromiseChain.prototype._callWebDriver = function (name) { - "use strict"; - - return this._applyWebDriver(name, Array.prototype.slice.call(arguments, 1)); -} - -BrowserPromiseChain.prototype.always = function (callback) { - "use strict"; - - return this.then(function (result) { callback(null, result) }, callback); -}; - -BrowserPromiseChain.prototype.sleep = function (timeout) { - "use strict"; - - var self = this; - return self.then(function () { - return new Promise(function (resolve) { - setTimeout(resolve, timeout); - }); - }); -}; - -BrowserPromiseChain.prototype.expectError = function (callback) { - "use strict"; - - var self = this; - return self.then(function () { - throw new Error('exception was not thrown'); - }, callback); -}; - -BrowserPromiseChain.prototype.noWait = function () { - "use strict"; - - return BrowserPromiseChain(this._operand, this._helpers); -}; - -BrowserPromiseChain.prototype.yet = function (code, args) { - "use strict"; - - var args = Array.prototype.slice.call(arguments, 0); - var self = this; - //-------------------------------- - return self.catch(function (err) { - return self.noWait().execute(code, args).then(function (errMessage) { - throw new Error(err.message + ' ' + errMessage); - }); - }); -}; - -BrowserPromiseChain.prototype.execute = function (code, args) { +module.exports.execute = function (code, args) { "use strict"; var self = this; @@ -191,23 +19,22 @@ BrowserPromiseChain.prototype.execute = function (code, args) { throw new Error('`args` has to be an array'); } - return self._applyWebDriver('execute', arguments, function (operand, name, myArgs) { + return self.__custom__(function (operand, done) { var closure = operand.closure ? operand.closure() : {}; - var cb = myArgs[myArgs.length-1]; code = wrapSourceCode(codeToString(code), args, closure); operand.browser.execute("return (" + code + ").apply(null, arguments)", - values(closure), feedbackProcessor(operand.closure.bind(operand), cb)); + values(closure), feedbackProcessor(operand.closure.bind(operand), done)); }); -} +}; -BrowserPromiseChain.prototype.timeout = function (ms) { +module.exports.timeout = function (ms) { this._timeout = ms; return this; -} +}; -BrowserPromiseChain.prototype.promise = function (code, args) { +module.exports.promise = function (code, args) { "use strict"; var self = this; @@ -225,10 +52,14 @@ BrowserPromiseChain.prototype.promise = function (code, args) { } // we could set this 5000 globally, right? - return self._callWebDriver('setAsyncScriptTimeout', self._timeout || 5000)._applyWebDriver('executeAsync', arguments, function (operand, name, myArgs) { + + return self.__custom__(function (operand, done) { + + operand.browser.setAsyncScriptTimeout(self._timeout || 5000, done); + + }).__custom__(function (operand, done) { var closure = operand.closure ? operand.closure() : {}; - var cb = myArgs[myArgs.length-1]; // the last argument is callback var chunks = []; var keys = Object.keys(closure).map(function (key) { @@ -274,12 +105,12 @@ BrowserPromiseChain.prototype.promise = function (code, args) { // TODO: how come "args" instead of values(closure) was passing as well? operand.browser.executeAsync("(" + code + ").apply(null, arguments)", - values(closure), feedbackProcessor(operand.closure.bind(operand), cb)); + values(closure), feedbackProcessor(operand.closure.bind(operand), done)); }); }; -BrowserPromiseChain.prototype.wait = function (timeout, message, code, args) { +module.exports.wait = function (timeout, message, code, args) { "use strict"; if (arguments.length < 4) { @@ -304,10 +135,13 @@ BrowserPromiseChain.prototype.wait = function (timeout, message, code, args) { var self = this; - return self._callWebDriver('setAsyncScriptTimeout', 2 * timeout)._applyWebDriver('executeAsync', arguments, function (operand, name, myArgs) { + return self.__custom__(function (operand, done) { + + operand.browser.setAsyncScriptTimeout(2 * timeout, done); + + }).__custom__(function (operand, done) { var closure = operand.closure ? operand.closure() : {}; - var cb = myArgs[myArgs.length-1]; // callback is always the last one var chunks = []; var keys = Object.keys(closure).map(function (key) { @@ -348,17 +182,9 @@ BrowserPromiseChain.prototype.wait = function (timeout, message, code, args) { code = chunks.join('\n'); operand.browser.executeAsync("(" + code + ").apply(null, arguments)", values(closure), - feedbackProcessor(operand.closure.bind(operand), cb)); + feedbackProcessor(operand.closure.bind(operand), done)); }); - - // ---------------------------------------------- - //if (args.length > Math.max(code.length - 2, 0)) { - // return Promise.reject(new Error('You passed too many arguments: ' + args.length + ' given but expected ' + (code.length - 2) + '.')); - //} - - // TODO: also check if arguments are named properly - }; function values(closure) { diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 7220ffa..5308747 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -4,7 +4,6 @@ */ var Closure = require('../tools/closure'); -var Browser = require('../browser'); var helpers = require('../browser/helpers'); var Meteor = require('../meteor'); var tools = require('../tools'); @@ -109,6 +108,8 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.browser = function (options, initialize) { + var createBrowser = require('../browser'); + var myHelpers = {}; options = options || {}; @@ -130,7 +131,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { tools.mergeHelpers(myHelpers, [ helpers.both, helpers.client ]); tools.mergeHelpers(myHelpers, gagarin_options.clientHelpers); - var browser = new Browser({ + var browser = createBrowser({ helpers : tools.mergeHelpers(myHelpers, options.helpers), location : options.location, webdriver : options.webdriver || gagarin_options.webdriver, diff --git a/lib/tools/generic.js b/lib/tools/generic.js index 537802c..3f76c42 100644 --- a/lib/tools/generic.js +++ b/lib/tools/generic.js @@ -2,9 +2,9 @@ var genericPromiseChain = require('./genericPromiseChain'); var Promise = require('es6-promise').Promise; var either = require('./index').either; -module.exports = function generic(methods, myPrototype) { +module.exports = function generic(methods, myPrototype, defaultAction) { - var GenericPromiseChain = genericPromiseChain(methods, myPrototype); + var GenericPromiseChain = genericPromiseChain(methods, myPrototype, defaultAction); function Generic (operand) { "use strict"; diff --git a/lib/tools/genericPromiseChain.js b/lib/tools/genericPromiseChain.js index 7fda618..6776138 100644 --- a/lib/tools/genericPromiseChain.js +++ b/lib/tools/genericPromiseChain.js @@ -1,7 +1,11 @@ var Promise = require('es6-promise').Promise; var either = require('./index').either; -module.exports = function genericPromiseChain(methods, myPrototype) { +module.exports = function genericPromiseChain(methods, myPrototype, defaultAction) { + + if (typeof defaultAction !== 'function') { + defaultAction = canonical; + } function GenericPromiseChain (operand) { "use strict"; @@ -103,6 +107,8 @@ module.exports = function genericPromiseChain(methods, myPrototype) { return self; }; + + methods.forEach(function (name) { "use strict"; @@ -111,18 +117,22 @@ module.exports = function genericPromiseChain(methods, myPrototype) { */ GenericPromiseChain.prototype[name] = function () { var args = Array.prototype.slice.call(arguments, 0); - return this.__custom__(function (operand) { - if (!operand[name]) { - reject(new Error('GenericPromiseChain: operand does not implement method: ' + name)); - } else { - args.push(either(cleanError(reject)).or(resolve)); - operand[name].apply(operand, args); - } + return this.__custom__(function (operand, done) { + defaultAction(operand, name, args, done); }); }; }); + function canonical (operand, name, args, done) { + if (!operand[name]) { + done(new Error('GenericPromiseChain: operand does not implement method: ' + name)); + } else { + args.push(done); + operand[name].apply(operand, args); + } + } + return GenericPromiseChain; } From b16ecd1897e55438908363c8c67fc974339ad7bc Mon Sep 17 00:00:00 2001 From: apendua Date: Sun, 25 Jan 2015 20:36:40 +0100 Subject: [PATCH 12/34] Moved findAvailablePort to tools --- lib/meteor/index.js | 28 +--------------------------- lib/tools/index.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lib/meteor/index.js b/lib/meteor/index.js index 28fe2f1..ad59fcf 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -7,9 +7,9 @@ var createDDPClientManager = require('../ddp/ddpClientManager'); var MeteorPromiseChain = require('./meteorPromiseChain'); +var findAvailablePort = require('../tools').findAvailablePort; var MongoDatabase = require('../mongo/database'); var BuildAsPromise = require('./build'); -var portscanner = require('portscanner'); var Instance = require('./meteorProcess'); var Promise = require('es6-promise').Promise; var Remote = require('./remote'); @@ -307,29 +307,3 @@ MeteorPromiseChain.methods.forEach(function (name) { }); -function findAvailablePort(meteorPort) { - - if (meteorPort) { - return Promise.resolve(meteorPort); - } - - return new Promise(function (resolve, reject) { - var numberOfRetries = 5; - - (function retry () { - var port = 4000 + Math.floor(Math.random() * 1000); - portscanner.checkPortStatus(port, 'localhost', function (err, status) { - if (err || status !== 'closed') { - if (--numberOfRetries > 0) { - setTimeout(retry); - } else { - reject('Cannot find a free port... giving up'); - } - } else { - resolve(port); - } - }); - })(); - }); -} - diff --git a/lib/tools/index.js b/lib/tools/index.js index c635c52..d674424 100644 --- a/lib/tools/index.js +++ b/lib/tools/index.js @@ -1,3 +1,4 @@ +var portscanner = require('portscanner'); var Promise = require('es6-promise').Promise; var spawn = require('child_process').spawn; var path = require('path'); @@ -220,6 +221,35 @@ module.exports = { } }, + /** + * Find a port, nobody is listening on. + */ + findAvailablePort: function (port) { + + if (port) { + return Promise.resolve(port); + } + + return new Promise(function (resolve, reject) { + var numberOfRetries = 5; + + (function retry () { + var port = 4000 + Math.floor(Math.random() * 1000); + portscanner.checkPortStatus(port, 'localhost', function (err, status) { + if (err || status !== 'closed') { + if (--numberOfRetries > 0) { + setTimeout(retry); + } else { + reject('Cannot find a free port... giving up'); + } + } else { + resolve(port); + } + }); + })(); + }); + }, + }; // since 0.9.0, the format is METEOR@x.x.x From 1eec3c36951b38795ad852d2d0b4da0ce339b840 Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:16:20 +0100 Subject: [PATCH 13/34] Require build promise directly --- lib/mocha/gagarin.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/mocha/gagarin.js b/lib/mocha/gagarin.js index b267fc5..acdf609 100644 --- a/lib/mocha/gagarin.js +++ b/lib/mocha/gagarin.js @@ -3,14 +3,13 @@ * Module dependencies. */ -var Meteor = require('../meteor'); var tools = require('../tools'); var Mocha = require('mocha'); var chalk = require('chalk'); var path = require('path'); var util = require('util'); -var BuildAsPromise = Meteor.BuildAsPromise; +var BuildAsPromise = require('../meteor/build'); module.exports = Gagarin; From 0b1e32874dc3ee67504c88f8f4ff39510737355e Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:16:30 +0100 Subject: [PATCH 14/34] Updated tests --- tests/specs/browser.js | 2 +- tests/specs/exceptions.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/specs/browser.js b/tests/specs/browser.js index 7a6e288..e0bd9a8 100644 --- a/tests/specs/browser.js +++ b/tests/specs/browser.js @@ -56,7 +56,7 @@ describe('Tests with browser', function () { return server.execute(function (id) { // TODO: wait? return Items.findOne({_id: id}); - }, id) + }, [ id ]) .then(function (value) { expect(value).not.to.be.empty; expect(value._id).to.equal(id); diff --git a/tests/specs/exceptions.js b/tests/specs/exceptions.js index 03c4289..df6705d 100644 --- a/tests/specs/exceptions.js +++ b/tests/specs/exceptions.js @@ -113,9 +113,8 @@ describe('Reporting Exceptions', function () { }, 500); }); - it('should respawn the meteor process when requested', function () { + it('should respawn the meteor process automatically', function () { return server1 - .restart() .execute(function () { return Meteor.release; }) From deab93f72fb36902a0764245777daed7a733ebbb Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:17:05 +0100 Subject: [PATCH 15/34] Isolated meteor methods --- lib/meteor/methods.js | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 lib/meteor/methods.js diff --git a/lib/meteor/methods.js b/lib/meteor/methods.js new file mode 100644 index 0000000..d88745e --- /dev/null +++ b/lib/meteor/methods.js @@ -0,0 +1,84 @@ + +module.exports = {}; + +module.exports.promise = function (code, args) { + + if (arguments.length < 2) { + args = []; + } + + if (!Array.isArray(args)) { + // throw new Error('args has to be an array'); + args = [ args ]; + } + + return this.__custom__(function (operand, cb) { + + var ddpClient = operand.ddpClient; + var closure = operand.closure; + + callDDPMethod(ddpClient, '/gagarin/promise', [ closure(), code.toString(), args ], closure, cb); + + }); +} + +module.exports.execute = function (code, args) { + + if (arguments.length < 2) { + args = []; + } + + if (!Array.isArray(args)) { + // throw new Error('args has to be an array'); + args = [ args ]; + } + + return this.__custom__(function (operand, cb) { + + var ddpClient = operand.ddpClient; + var closure = operand.closure; + + callDDPMethod(ddpClient, '/gagarin/execute', [ closure(), code.toString(), args ], closure, cb); + + }); +} + +module.exports.wait = function (timeout, message, code, args) { + + if (arguments.length < 2) { + args = []; + } + + if (!Array.isArray(args)) { + // throw new Error('args has to be an array'); + args = [ args ]; + + } + + return this.__custom__(function (operand, cb) { + + var ddpClient = operand.ddpClient; + var closure = operand.closure; + + callDDPMethod(ddpClient, '/gagarin/wait', [ closure(), timeout, message, code.toString(), args ], closure, cb); + + }); +} + +function callDDPMethod (ddpClient, name, args, closure, cb) { + if (!ddpClient) { + return cb(new Error('invalid ddpClient')); + } + ddpClient.call(name, args, function (err, feedback) { + if (feedback && feedback.closure) { + closure(feedback.closure); + } + if (err) { + return cb(err); + } + if (feedback.error) { + return cb(new Error(feedback.error)); + } + cb(null, feedback.value); + }); +} From cf3ef89609ee0b4bb36830f8738e7b5c81db0498 Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:20:44 +0100 Subject: [PATCH 16/34] Cleanup meteor process implementation --- lib/meteor/meteorProcess.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/meteor/meteorProcess.js b/lib/meteor/meteorProcess.js index 06c50bc..8d43cb9 100644 --- a/lib/meteor/meteorProcess.js +++ b/lib/meteor/meteorProcess.js @@ -13,29 +13,32 @@ var spawn = require('child_process').spawn; * - onExit {Function} * - safetyTimeout {Number} */ -module.exports = function Instance (node, main, env, options) { +module.exports = function createMeteorProcess (node, main, env, myPrototype) { "use strict"; + // TODO: let instance be an event emitter + + var instance = Object.create(myPrototype); var meteor = null; var lastError = ""; var lastErrorAt = "nowhere"; - var safetyTimeout = options.safetyTimeout || 5 * 1000; + var safetyTimeout = instance.safetyTimeout || 5 * 1000; var safetyHandle = null; setTimeout(function () { try { meteor = spawn(node, [ main ], { env: env }); } catch (err) { - return options.onStart && options.onStart(err); + return instance.onStart && instance.onStart(err); } safetyHandle = setTimeout(function () { kill(function (err) { - if (options.onStart) { + if (instance.onStart) { if (err) { - options.onStart(err); + instance.onStart(err); } else { - options.onStart(new Error('Gagarin is not there.' + + instance.onStart(new Error('Gagarin is not there.' + ' Please make sure you have added it with: meteor add anti:gagarin.')); } } @@ -47,10 +50,10 @@ module.exports = function Instance (node, main, env, options) { if (/Поехали!/.test(data.toString())) { clearTimeout(safetyHandle); //----------------------------------- - options.onStart && options.onStart(); + instance.onStart && instance.onStart(); } - options.onData && options.onData(data); + instance.onData && instance.onData(data); }); @@ -59,7 +62,7 @@ module.exports = function Instance (node, main, env, options) { meteor.stderr.on('data', function (data) { // look for errors - options.onData && options.onData(data, { isError: true }); + instance.onData && instance.onData(data, { isError: true }); data.toString().split('\n').forEach(function (line) { var hasMatch = [ @@ -94,15 +97,16 @@ module.exports = function Instance (node, main, env, options) { meteor.on('exit', function (code) { clearTimeout(safetyHandle); //------------------------- - if (options.onExit) { - options.onExit(code, lastError, lastErrorAt); + if (instance.onExit) { + instance.onExit(code, lastError, lastErrorAt); } meteor = null; }); }); - this.kill = kill; + instance.kill = kill; + /** * Kill the meteor process and cleanup. * @@ -130,4 +134,6 @@ module.exports = function Instance (node, main, env, options) { meteor = null; } + return instance; + } From b47cec7d7ee2501a20e759ab11d7aea60e4a80cc Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:21:09 +0100 Subject: [PATCH 17/34] MeteorPromiseChain now uses generics --- lib/meteor/index.js | 369 +++++++++++------------------ lib/meteor/meteorProcessManager.js | 163 +++++++++++++ lib/meteor/meteorPromiseChain.js | 138 ----------- lib/meteor/remote.js | 123 ---------- 4 files changed, 296 insertions(+), 497 deletions(-) create mode 100644 lib/meteor/meteorProcessManager.js delete mode 100644 lib/meteor/meteorPromiseChain.js delete mode 100644 lib/meteor/remote.js diff --git a/lib/meteor/index.js b/lib/meteor/index.js index ad59fcf..a7fa9dd 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -1,35 +1,18 @@ - -/** - * Module dependencies. - */ - -var createDDPClientManager = require('../ddp/ddpClientManager'); - -var MeteorPromiseChain = require('./meteorPromiseChain'); -var findAvailablePort = require('../tools').findAvailablePort; -var MongoDatabase = require('../mongo/database'); -var BuildAsPromise = require('./build'); -var Instance = require('./meteorProcess'); -var Promise = require('es6-promise').Promise; -var Remote = require('./remote'); -var chalk = require('chalk'); -var tools = require('../tools'); -var path = require('path'); -var url = require('url'); - -module.exports = Meteor; -module.exports.BuildAsPromise = BuildAsPromise; - -//------- -// METEOR -//------- - -function Meteor (options) { +var createMeteorProcessManager = require('./meteorProcessManager'); +var createDDPClientManager = require('../ddp/ddpClientManager'); +var BuildAsPromise = require('./build'); +var MongoDatabase = require('../mongo/database'); +var meteorMethods = require('./methods'); +var Promise = require('es6-promise').Promise; +var Closure = require('../tools/closure'); +var generic = require('../tools/generic'); +var tools = require('../tools'); +var url = require('url'); + +module.exports = function createMeteor (options) { "use strict"; - var self = this; - options = options || {}; if (typeof options === 'string') { @@ -37,21 +20,20 @@ function Meteor (options) { } var pathToApp = options.pathToApp || path.resolve('.'); - var remoteServer = options.remoteServer; - var verbose = !!options.verbose; var skipBuild = !!options.skipBuild; + var remoteServer = options.remoteServer ? url.parse(options.remoteServer) : null; var mongoUrlPromise = null; var databasePromise = null; - if (remoteServer) { - remoteServer = url.parse(remoteServer); - } + var uniqueCode = Math.random(); if (typeof options.mongoUrl === 'string') { mongoUrlPromise = Promise.resolve(options.mongoUrl); + } else if (options.mongoUrl && options.mongoUrl.then) { mongoUrlPromise = options.mongoUrl; + } else if (options.mongoUrl) { throw new Error('mongoUrl must be a string or a promise returning a string'); } @@ -61,249 +43,164 @@ function Meteor (options) { mongoUrlPromise = databasePromise.getMongoUrlPromise(); } - var getDDPClient = createDDPClientManager(); - - var instance = null; - var meteorPromise = null; - - var meteorHasCrashed = false; - var meteorUniqueCode = null; - var meteorSettings = tools.getSettings(options.settings); + var helpers = options.helpers || {}; - var meteorRestartDelay = 100; - var restartRequired = false; + var myPrototype = Object.create(helpers); - var meteorPort = null; - var meteorDatabase = options.dbName || 'gagarin_' + (new Date()).getTime(); + myPrototype.init = function () { + return this.branch().then(function () { + // ... + }); + } - function ddpClient () { - return ddpSetupProvider().then(function (setup) { - return getDDPClient(setup); + myPrototype.start = function () { + return this.__custom__(function (operand, done) { + done(); }); } - var remote = new Remote(ddpClient, serverControllerProvider); - - var dummyController = { - start : noop, - restart : noop, - stop : noop, - }; + myPrototype.stop = function () { + return this.__custom__(function (operand, done) { + + operand.ddpClient.close(); - var controller = { - start : function (cb) { cb() }, - restart : function (cb) { - (restartRequired = true) && serverControllerProvider() - .then(function () { - cb(); - }).catch(function (err) { - cb(err); - }); - }, - stop : function (cb) { - cleanUpThen(function (err) { + if (!operand.process) { // e.g. if using remote server + return done(); + } + + operand.process.kill(function (err) { if (err) { - return cb(err); + done(err); + } else if (databasePromise) { + databasePromise.then(function (db) { + db.cleanUp(done); + }).catch(done); } - databasePromise.then(function (db) { - db.cleanUp(cb); - }).catch(function (err) { - cb(err); - }); }); - }, - }; + }); + } - // set-up helpers - options.helpers = options.helpers || {}; + // TODO: think if this can be implemented outside the manager - self.helpers = {}; - self.methods = Object.keys(options.helpers).concat(MeteorPromiseChain.methods); + //myPrototype.restart = function (delay) { + // var self = this; + // return self.then(function () { + // uniqueCode = Math.random(); + // return this.__custom__(function (operand, done) { + // done(); + // }); + // }); + //} - Object.keys(options.helpers).forEach(function (key) { - if (self[key] !== undefined) { - console.warn('helper ' + key + ' conflicts with some Meteor method'); - } - self[key] = self.helpers[key] = options.helpers[key]; + var methods = [ + 'restart' + ]; + + Object.keys(meteorMethods).forEach(function (name) { + myPrototype[name] = meteorMethods[name]; }); - self.useClosure = function (objectOrGetter) { - remote.useClosure(objectOrGetter); - }; + var MeteorGeneric = generic(methods, myPrototype, function (operand, name, args, done) { + if (!operand.process) { + done(new Error('operand.process is undefined')); + } else if (!operand.process[name]) { + done(new Error('operand.process does not implement method: ' + name)); + } else { + args.push(done); + operand.process[name].apply(operand.process, args); + } + }); - self.meteorRemoteAsPromise = Promise.resolve(remote); + var Meteor = function () { - self._ddpSetupProvider = ddpSetupProvider; + var getMeteorProcess = createMeteorProcessManager(options); + var getDDPClient = createDDPClientManager(); + var closure = null; - function cleanUpThen(callback) { - meteorPromise = null; - //------------------- - if (instance) { - instance.kill(callback); - instance = null; - } else { - callback(); + function getPathToMain () { + return BuildAsPromise({ pathToApp: pathToApp, skipBuild: skipBuild }); } - } - function ddpSetupProvider() { - - if (remoteServer) { - return Promise.resolve({ - host: remoteServer.hostname, - port: remoteServer.port || 443, - }); + function getMongoUrl () { + return databasePromise.getMongoUrlPromise(); } - return serverControllerProvider().then(function () { - return { - port: meteorPort, - code: meteorUniqueCode, - }; - }); - } - - function serverControllerProvider () { + function getMeteor () { - if (!restartRequired && !!meteorPromise) { - if (meteorHasCrashed) { - restartRequired = true; + if (remoteServer) { + return Promise.resolve(null); } - return meteorPromise; - } - restartRequired = false; + return Promise.all([ - if (remoteServer) { - return meteorPromise = Promise.resolve(dummyController); - } + getPathToMain(), + getMongoUrl(), - meteorPromise = new Promise(function (resolve, reject) { - - var instanceOptions = { - onStart: function (err) { - if (err) { - return reject(err); - } - meteorUniqueCode = Math.random(); - //------------------------------- - resolve(controller); - }, - onExit: function (code, lastError, lastErrorAt) { - meteorHasCrashed = (code && code !== 0 && code !== 130); - //------------------------------------------------------ - if (meteorHasCrashed) { - if (lastError) { - meteorPromise = Promise.reject(new Error(chalk.red(lastError) + chalk.blue(' => ') + chalk.blue(lastErrorAt))); - } else { - meteorPromise = Promise.reject(new Error(chalk.red('Meteor server has crashed due to some unknown reason.'))); - } - } - }, - onData: function (data, options) { - if (options && options.isError) { - logServerError(data); - } else { - logServerOutput(data); - } - }, - }; - - cleanUpThen(function () { // respawn - - meteorHasCrashed = false - - Promise.all([ - - BuildAsPromise({ - pathToApp: pathToApp, - skipBuild: skipBuild, - }), - - mongoUrlPromise, - - findAvailablePort(meteorPort), - - ]).then(function (all) { - - var pathToMain = all[0]; - var mongoUrl = all[1]; - - meteorPort = all[2]; - - var env = Object.create(process.env); - - if (meteorSettings) { - env.METEOR_SETTINGS = JSON.stringify(meteorSettings); - } - - env.ROOT_URL = 'http://localhost:' + meteorPort; - env.PORT = meteorPort; - env.MONGO_URL = mongoUrl; - env.GAGARIN_SETTINGS = "{}"; // only used if METEOR_SETTINGS does not contain gagarin field - - setTimeout(function () { - instance = new Instance(tools.getNodePath(pathToApp), pathToMain, env, instanceOptions); - }, meteorRestartDelay); - - }, function (err) { - reject(err); + ]).then(function (all) { + + var pathToMain = all[0]; + var mongoUrl = all[1]; + + return getMeteorProcess({ + pathToNode : tools.getNodePath(pathToApp), + pathToMain : pathToMain, + uniqueCode : uniqueCode, + mongoUrl : mongoUrl, }); }); + } - }); + function operand () { - return meteorPromise; - } // serverControllerProvider + return Promise.all([ - function logServerOutput(data) { - if (!verbose) { - return; - } - process.stdout.write(data.toString().split('\n').map(function (line) { - if (line === '\r') { - return; - } - if (line.length === 0) { - return ""; - } - return chalk.blue('[server] ') + line; - }).join('\n')); - } + getDDPSetup(), + getMeteor(), + + ]).then(function (all) { - function logServerError(data) { - if (!verbose) { - return; + var setup = all[0]; + var process = all[1]; + + return getDDPClient(setup).then(function (ddpClient) { + return { ddpClient: ddpClient, process: process, closure: closure }; + }); + + }); } - process.stdout.write(data.toString().split('\n').map(function (line) { - if (line === '\r') { - return; - } - if (line.length === 0) { - return ""; + + function getDDPSetup () { + + if (remoteServer) { + return Promise.resolve({ + host: remoteServer.hostname, + port: remoteServer.port || 443, + }); } - return chalk.red('[server] ') + line; - }).join('\n')); - } - function deny (cb) { - cb(new Error('action not allowed')); - } + return getMeteor().then(function (process) { + return { + port: process.env.PORT, + code: process.pid, + }; + }); - function noop (cb) { - cb(); - } + } -} + this._ddpSetupProvider = getDDPSetup; -MeteorPromiseChain.methods.forEach(function (name) { - "use strict"; + MeteorGeneric.call(this, operand); + + Closure.mixin(this); // adds "useClosure" and "closure" methods + closure = this.closure.bind(this); - Meteor.prototype[name] = function () { - var chain = new MeteorPromiseChain(this.meteorRemoteAsPromise, this.helpers); - return chain[name].apply(chain, arguments); }; -}); + Meteor.prototype = Object.create(new MeteorGeneric(), { + methods: { value: [].concat(Object.keys(myPrototype), Object.keys(helpers), MeteorGeneric.prototype.methods) } + }); + + return new Meteor(); +} diff --git a/lib/meteor/meteorProcessManager.js b/lib/meteor/meteorProcessManager.js new file mode 100644 index 0000000..257d707 --- /dev/null +++ b/lib/meteor/meteorProcessManager.js @@ -0,0 +1,163 @@ + +var createMeteorProcess = require('./meteorProcess'); +var findAvailablePort = require('../tools').findAvailablePort; +var Promise = require('es6-promise').Promise; +var chalk = require('chalk'); +var tools = require('../tools'); + +module.exports = function createMeteorProcessManager (options) { + "use strict"; + + options = options || {}; + + var meteorProcessPrototype = {}; + var meteorProcess = null; + var meteorPromise = null; + + var pathToMain = ""; + var pathToNode = ""; + var mongoUrl = ""; + + var meteorHasCrashed = false; + var meteorSettings = tools.getSettings(options.settings); + + var meteorRestartDelay = 100; + var restartRequired = false; + + var meteorPort = null; + var verbose = !!options.verbose; + + meteorProcessPrototype.restart = function (delay) { + + var done = arguments[arguments.length - 1]; + + if (arguments.length >= 2) { + meteorRestartDelay = delay; + } else { + meteorRestartDelay = 100; + } + + restartRequired = true; + + console.log('restart requested'); + + getMeteorProcess({ + + pathToMain : pathToMain, + pathToNode : pathToNode, + mongoUrl : mongoUrl, + + }).then(function () { done() }).catch(done); + } + + function getMeteorProcess (setup) { + + if (pathToMain !== setup.pathToMain || pathToNode !== setup.pathToNode || mongoUrl !== setup.mongoUrl) { + restartRequired = true; + } + + pathToMain = setup.pathToMain; + pathToNode = setup.pathToNode; + mongoUrl = setup.mongoUrl; + + if (!restartRequired && !!meteorPromise) { + if (meteorHasCrashed) { + restartRequired = true; + } + return meteorPromise; + } + + restartRequired = false; + + meteorPromise = new Promise(function (resolve, reject) { + + cleanUpThen(function () { + + meteorHasCrashed = false + + findAvailablePort(meteorPort).then(function (port) { + + meteorPort = port; + + var env = Object.create(process.env); + + if (meteorSettings) { + env.METEOR_SETTINGS = JSON.stringify(meteorSettings); + } + + env.ROOT_URL = 'http://localhost:' + meteorPort; + env.PORT = meteorPort; + env.MONGO_URL = mongoUrl; + env.GAGARIN_SETTINGS = "{}"; // only used if METEOR_SETTINGS does not contain gagarin field + + setTimeout(function () { + + meteorProcess = new createMeteorProcess(pathToNode, pathToMain, env, meteorProcessPrototype); + meteorProcess.env = env; + + }, meteorRestartDelay); + + }).catch(reject); + + }); + + // callbacks to cooperate with meteor process + + meteorProcessPrototype.onStart = function (err) { + if (err) { + return reject(err); + } + this.pid = Math.random(); + resolve(this); + }; + + meteorProcessPrototype.onExit = function (code, lastError, lastErrorAt) { + meteorHasCrashed = (code && code !== 0 && code !== 130); + if (meteorHasCrashed) { + if (lastError) { + meteorPromise = Promise.reject(new Error(chalk.red(lastError) + chalk.blue(' => ') + chalk.blue(lastErrorAt))); + } else { + meteorPromise = Promise.reject(new Error(chalk.red('Meteor server has crashed due to some unknown reason.'))); + } + } + }; + + meteorProcessPrototype.onData = function (data, opts) { + logServerOutput(data, opts && opts.isError ? chalk.red : chalk.blue); + }; + + }); + + return meteorPromise; + } + + function cleanUpThen(callback) { + meteorPromise = null; + //------------------- + if (meteorProcess) { + meteorProcess.kill(callback); + meteorProcess = null; + } else { + callback(); + } + } + + function logServerOutput(data, color) { + if (!verbose) { + return; + } + process.stdout.write(data.toString().split('\n').map(function (line) { + if (line === '\r') { + return; + } + if (line.length === 0) { + return ""; + } + return color('[server] ') + line; + }).join('\n')); + } + + return getMeteorProcess; + +} + diff --git a/lib/meteor/meteorPromiseChain.js b/lib/meteor/meteorPromiseChain.js deleted file mode 100644 index b8f3370..0000000 --- a/lib/meteor/meteorPromiseChain.js +++ /dev/null @@ -1,138 +0,0 @@ -var Promise = require('es6-promise').Promise; -var either = require('../tools').either; - -module.exports = MeteorPromiseChain; - -//--------------------- -// METEOR PROMISE CHAIN -//--------------------- - -function MeteorPromiseChain (operand, helpers) { - "use strict"; - - var self = this; - - helpers = helpers || {}; - - this._operand = operand; - this._promise = operand; - this._helpers = helpers; - - Object.keys(helpers).forEach(function (key) { - if (self[key] !== undefined) { - console.warn('helper ' + key + ' conflicts with some MeteorPromiseChain method'); - } - self[key] = helpers[key]; - }); -} - -[ 'then', 'catch' ].forEach(function (name) { - - MeteorPromiseChain.prototype[name] = function () { - "use strict"; - - this._promise = this._promise[name].apply(this._promise, arguments); - return this; - }; - -}); - -var meteorRemoteMethods = [ - 'stop', - 'start', - 'restart', - 'execute', - 'promise', - 'wait', -]; - -MeteorPromiseChain.methods = meteorRemoteMethods.concat([ - 'catch', - 'then', - 'always', - 'sleep', - 'expectError', - 'noWait', - 'yet', - 'timeout', -]); - -MeteorPromiseChain.prototype.always = function (callback) { - "use strict"; - - return this.then(function (result) { callback(null, result) }, callback); -}; - -MeteorPromiseChain.prototype.sleep = function (timeout) { - "use strict"; - - var self = this; - return self.then(function () { - return new Promise(function (resolve) { - setTimeout(resolve, timeout); - }); - }); -}; - -MeteorPromiseChain.prototype.expectError = function (callback) { - "use strict"; - - var self = this; - return self.then(function () { - throw new Error('exception was not thrown'); - }, callback); -}; - -MeteorPromiseChain.prototype.noWait = function () { - "use strict"; - - return MeteorPromiseChain(this._operand, this._helpers); -}; - -MeteorPromiseChain.prototype.yet = function (code, args) { - "use strict"; - - var args = Array.prototype.slice.call(arguments, 0); - var self = this; - //-------------------------------- - return self.catch(function (err) { - return self.noWait().execute(code, args).then(function (errMessage) { - throw new Error(err.message + ' ' + errMessage); - }); - }); -}; - -MeteorPromiseChain.prototype.timeout = function () { - console.warn('timeout for server code is not implemented yet'); - return this; -} - -meteorRemoteMethods.forEach(function (name) { - "use strict"; - - /** - * Update the current promise and return this to allow chaining. - */ - MeteorPromiseChain.prototype[name] = function () { - var args = Array.prototype.slice.call(arguments, 0); - var self = this; - self._promise = Promise.all([ - self._operand, self._promise - ]).then(function (all) { - return new Promise(function (resolve, reject) { - // TODO: think how we could use value returned by self._promise - var operand = all[0]; - if (!operand || typeof operand !== 'object') { - reject(new Error('MeteorPromiseChain: invalid operand')); - } else if (!operand[name]) { - reject(new Error('MeteorPromiseChain: operand does not implement method: ' + name)); - } else { - args.push(either(reject).or(resolve)); - return operand[name].apply(operand, args); - } - }); - }); - return self; - }; - -}); diff --git a/lib/meteor/remote.js b/lib/meteor/remote.js deleted file mode 100644 index 9e8adc6..0000000 --- a/lib/meteor/remote.js +++ /dev/null @@ -1,123 +0,0 @@ -var Closure = require('../tools/closure'); - -module.exports = Remote; - -/** - * Creates a meteor remote driver, which can be used to send - * commands to meteor server. - * - * @param {Function} ddpClientProvider, should return a promise - * @apram {Function} serverControllerProvider, should return a promise - */ -function Remote(ddpClientProvider, serverControllerProvider) { - "use strict"; - - var self = this; - var closure; - - Closure.mixin(self); - closure = self.closure.bind(self); - - this.promise = function (code, args) { - var cb = arguments[arguments.length-1]; - - if (arguments.length < 3) { - args = []; - } else { - args = Array.isArray(args) ? args : [ args ]; - } - - return ddpClientProvider().then(function (ddpClient) { - callDDPMethod(ddpClient, '/gagarin/promise', - [ closure(), code.toString(), args ], cb); - }, function (err) { - cb(err); - }); - } - - this.execute = function (code, args) { - var cb = arguments[arguments.length-1]; - - if (arguments.length < 3) { - args = []; - } else { - args = Array.isArray(args) ? args : [ args ]; - } - - return ddpClientProvider().then(function (ddpClient) { - callDDPMethod(ddpClient, '/gagarin/execute', - [ closure(), code.toString(), args ], cb); - }, function (err) { - cb(err); - }); - } - - this.wait = function (timeout, message, code, args) { - var cb = arguments[arguments.length-1]; - - if (arguments.length < 5) { - args = []; - } else { - args = Array.isArray(args) ? args : [ args ]; - } - - // TODO: verify timeout - - return ddpClientProvider().then(function (ddpClient) { - callDDPMethod(ddpClient, '/gagarin/wait', - [ closure(), timeout, message, code.toString(), args ], cb); - }, function (err) { - cb(err); - }); - } - - self.start = function (cb) { - return serverControllerProvider().then(function (controller) { - controller.start(cb); - }, function (err) { - cb(err); - }); - }; - - // TODO: retry a few times on error ... - self.restart = function (restartTimeout) { - var cb = arguments[arguments.length-1]; - - if (arguments.length < 2) { - restartTimeout = undefined; - } - - return serverControllerProvider().then(function (controller) { - controller.restart(cb); - }, function (err) { - cb(err); - }); - }; - - self.stop = function (cb) { - return serverControllerProvider().then(function (controller) { - controller.stop(cb); - }, function (err) { - cb(err); - }); - }; - - function callDDPMethod (ddpClient, name, args, cb) { - if (!ddpClient) { - return cb(new Error('invalid ddpClient')); - } - ddpClient.call(name, args, function (err, feedback) { - if (feedback && feedback.closure) { - closure(feedback.closure); - } - if (err) { - return cb(err); - } - if (feedback.error) { - return cb(new Error(feedback.error)); - } - cb(null, feedback.value); - }); - } - -}; From b57d6b3c731eabe1a1566dca0a72cf7798c493ec Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:28:41 +0100 Subject: [PATCH 18/34] Renamed ddpSetupProvider => getDDPSetup --- lib/browser/browserManager.js | 8 ++++---- lib/ddp/index.js | 4 ++-- lib/meteor/index.js | 2 +- lib/mocha/interface.js | 16 ++++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/browser/browserManager.js b/lib/browser/browserManager.js index f3865d4..69b8c6e 100644 --- a/lib/browser/browserManager.js +++ b/lib/browser/browserManager.js @@ -16,9 +16,9 @@ module.exports = function createBrowserManager (options) { var windowSize = options.windowSize; var myLocation = options.location || "http://localhost:3000"; var browserPromise = null; - var ddpSetupProvider = typeof myLocation === 'string' ? Promise.resolve(myLocation) : myLocation._ddpSetupProvider; + var getDDPSetup = typeof myLocation === 'string' ? Promise.resolve(myLocation) : myLocation.getDDPSetup; - if (!ddpSetupProvider) { + if (!getDDPSetup) { throw new Error('the location option must be either string or a meteor server'); } @@ -67,8 +67,8 @@ module.exports = function createBrowserManager (options) { }; function afterResize (done) { - if (ddpSetupProvider) { - ddpSetupProvider().then(function (setup) { + if (getDDPSetup) { + getDDPSetup().then(function (setup) { if (setup.host) { getLocation(setup.host, done); } else { diff --git a/lib/ddp/index.js b/lib/ddp/index.js index b96412f..c4a972a 100644 --- a/lib/ddp/index.js +++ b/lib/ddp/index.js @@ -5,7 +5,7 @@ var createDDPClientManager = require('./ddpClientManager'); var defaultGiveUpTimeout = 100; -module.exports = function makeDDPClient (ddpSetupProvider, helpers) { +module.exports = function makeDDPClient (getDDPSetup, helpers) { // TODO: check if arguments are ok @@ -98,7 +98,7 @@ module.exports = function makeDDPClient (ddpSetupProvider, helpers) { var getDDPClient = createDDPClientManager(); function ddpClient () { - return ddpSetupProvider().then(function (setup) { + return getDDPSetup().then(function (setup) { return getDDPClient(setup); }); } diff --git a/lib/meteor/index.js b/lib/meteor/index.js index a7fa9dd..d27d889 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -188,7 +188,7 @@ module.exports = function createMeteor (options) { } - this._ddpSetupProvider = getDDPSetup; + this.getDDPSetup = getDDPSetup; MeteorGeneric.call(this, operand); diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 5308747..3e73095 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -99,7 +99,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { if (options.flavor == "fiber") { var proxy = wrapPromisesForFiber(meteor, meteor.methods); - proxy._ddpSetupProvider = meteor._ddpSetupProvider; + proxy.getDDPSetup = meteor.getDDPSetup; return proxy; } else { return meteor; @@ -122,7 +122,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { options = { location: options }; } - if (options && options._ddpSetupProvider) { + if (options && options.getDDPSetup) { options = { location: options }; } @@ -163,24 +163,24 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.ddp = function (server, options) { var makeDDPClient = require('../ddp'); - var ddpSetupProvider = null; + var getDDPSetup = null; options = options || {}; options.flavor = options.flavor || gagarin_options.flavor || "promise"; - if (server._ddpSetupProvider) { - ddpSetupProvider = server._ddpSetupProvider; + if (server.getDDPSetup) { + getDDPSetup = server.getDDPSetup; } if (typeof server === 'string') { - ddpSetupProvider = Promise.resolve(url.parse(server || '')); + getDDPSetup = function () { return Promise.resolve(url.parse(server || '')) }; } - if (!ddpSetupProvider) { + if (!getDDPSetup) { throw new Error('DDP: no server connection provided'); } - var ddp = makeDDPClient(ddpSetupProvider, options.helpers); + var ddp = makeDDPClient(getDDPSetup, options.helpers); return (options.flavor === 'fiber') ? wrapPromisesForFiber(ddp, ddp.methods) : ddp; From 8760407f16363a15ff1dd23cd2aff5dd569cf27e Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:50:20 +0100 Subject: [PATCH 19/34] Using constructors does not make sense here --- lib/meteor/index.js | 144 +++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 76 deletions(-) diff --git a/lib/meteor/index.js b/lib/meteor/index.js index d27d889..30e456b 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -19,14 +19,16 @@ module.exports = function createMeteor (options) { options = { pathToApp: options }; } - var pathToApp = options.pathToApp || path.resolve('.'); - var skipBuild = !!options.skipBuild; + var pathToApp = options.pathToApp || path.resolve('.'); + var skipBuild = !!options.skipBuild; var remoteServer = options.remoteServer ? url.parse(options.remoteServer) : null; var mongoUrlPromise = null; var databasePromise = null; - var uniqueCode = Math.random(); + var getMeteorProcess = createMeteorProcessManager(options); + var getDDPClient = createDDPClientManager(); + var closure = null; if (typeof options.mongoUrl === 'string') { mongoUrlPromise = Promise.resolve(options.mongoUrl); @@ -47,11 +49,12 @@ module.exports = function createMeteor (options) { var myPrototype = Object.create(helpers); - myPrototype.init = function () { - return this.branch().then(function () { - // ... - }); - } + // custom methods + + //myPrototype.init = function () { + // return this.branch().then(function () { + // }); + //} myPrototype.start = function () { return this.__custom__(function (operand, done) { @@ -92,7 +95,7 @@ module.exports = function createMeteor (options) { // }); //} - var methods = [ + var methods = [ // copy/pasted meteor process methods 'restart' ]; @@ -111,96 +114,85 @@ module.exports = function createMeteor (options) { } }); - var Meteor = function () { + // create an object inheriting from MeteorGeneric - var getMeteorProcess = createMeteorProcessManager(options); - var getDDPClient = createDDPClientManager(); - var closure = null; + var meteor = Object.create(new MeteorGeneric(), { + methods: { value: [].concat(Object.keys(myPrototype), Object.keys(helpers), MeteorGeneric.prototype.methods) } + }); - function getPathToMain () { - return BuildAsPromise({ pathToApp: pathToApp, skipBuild: skipBuild }); - } + MeteorGeneric.call(meteor, getOperand); - function getMongoUrl () { - return databasePromise.getMongoUrlPromise(); - } + meteor.getDDPSetup = getDDPSetup; - function getMeteor () { + // add closure mixins, i.e. "useClosure" and "closure" methods - if (remoteServer) { - return Promise.resolve(null); - } + Closure.mixin(meteor); + closure = meteor.closure.bind(meteor); - return Promise.all([ + // helper functions producing usefull promises - getPathToMain(), - getMongoUrl(), + function getPathToMain () { + return BuildAsPromise({ pathToApp: pathToApp, skipBuild: skipBuild }); + } - ]).then(function (all) { - - var pathToMain = all[0]; - var mongoUrl = all[1]; + function getMongoUrl () { + return databasePromise.getMongoUrlPromise(); + } - return getMeteorProcess({ - pathToNode : tools.getNodePath(pathToApp), - pathToMain : pathToMain, - uniqueCode : uniqueCode, - mongoUrl : mongoUrl, - }); + function getMeteor () { - }); + if (remoteServer) { + return Promise.resolve(null); } - function operand () { + return Promise.all([ - return Promise.all([ + getPathToMain(), getMongoUrl() - getDDPSetup(), - getMeteor(), - - ]).then(function (all) { - - var setup = all[0]; - var process = all[1]; - - return getDDPClient(setup).then(function (ddpClient) { - return { ddpClient: ddpClient, process: process, closure: closure }; - }); - + ]).then(function (results) { + + return getMeteorProcess({ + pathToNode : tools.getNodePath(pathToApp), + pathToMain : results[0], + mongoUrl : results[1], }); - } - function getDDPSetup () { - - if (remoteServer) { - return Promise.resolve({ - host: remoteServer.hostname, - port: remoteServer.port || 443, - }); - } + }); + } - return getMeteor().then(function (process) { - return { - port: process.env.PORT, - code: process.pid, - }; - }); + function getOperand () { - } + return Promise.all([ - this.getDDPSetup = getDDPSetup; + getDDPSetup(), getMeteor() - MeteorGeneric.call(this, operand); + ]).then(function (results) { + + return getDDPClient(results[0]).then(function (ddpClient) { + return { ddpClient: ddpClient, process: results[1], closure: closure }; + }); + + }); + } - Closure.mixin(this); // adds "useClosure" and "closure" methods - closure = this.closure.bind(this); + function getDDPSetup () { + + if (remoteServer) { + return Promise.resolve({ + host: remoteServer.hostname, + port: remoteServer.port || 443, + }); + } - }; + return getMeteor().then(function (process) { + return { + port: process.env.PORT, + code: process.pid, + }; + }); - Meteor.prototype = Object.create(new MeteorGeneric(), { - methods: { value: [].concat(Object.keys(myPrototype), Object.keys(helpers), MeteorGeneric.prototype.methods) } - }); + } - return new Meteor(); + return meteor; } From 11bd722b18448d15dc90e1a15cae3fb25af03b9b Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:54:19 +0100 Subject: [PATCH 20/34] Simplify the helpers pattern --- lib/browser/browserManager.js | 2 +- lib/browser/helpers.js | 2 +- lib/browser/index.js | 7 +++++++ lib/meteor/meteorProcessManager.js | 2 ++ lib/mocha/interface.js | 15 ++++++--------- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/browser/browserManager.js b/lib/browser/browserManager.js index 69b8c6e..cf294b0 100644 --- a/lib/browser/browserManager.js +++ b/lib/browser/browserManager.js @@ -16,7 +16,7 @@ module.exports = function createBrowserManager (options) { var windowSize = options.windowSize; var myLocation = options.location || "http://localhost:3000"; var browserPromise = null; - var getDDPSetup = typeof myLocation === 'string' ? Promise.resolve(myLocation) : myLocation.getDDPSetup; + var getDDPSetup = typeof myLocation === 'string' ? Promise.resolve(myLocation) : myLocation.getDDPSetup; if (!getDDPSetup) { throw new Error('the location option must be either string or a meteor server'); diff --git a/lib/browser/helpers.js b/lib/browser/helpers.js index fc2555a..c74268c 100644 --- a/lib/browser/helpers.js +++ b/lib/browser/helpers.js @@ -4,7 +4,7 @@ var Promise = require('es6-promise').Promise; var DEFAULT_TIMEOUT = 5000; -exports.client = { +module.exports = { waitForDOM: function (selector, timeout) { return this.wait(timeout || DEFAULT_TIMEOUT, 'until element ' + selector + ' is present', function (selector) { diff --git a/lib/browser/index.js b/lib/browser/index.js index de06833..c0f1996 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -1,6 +1,7 @@ var createBrowserManager = require('./browserManager'); var browserMethods = require('./methods'); +var browserHelpers = require('./helpers'); var Closure = require('../tools/closure'); var generic = require('../tools/generic'); @@ -40,10 +41,16 @@ module.exports = function createBrowser (options) { 'getLocation', ]; + // TODO: early detect colisions + Object.keys(browserMethods).forEach(function (name) { myPrototype[name] = browserMethods[name]; }); + Object.keys(browserHelpers).forEach(function (name) { + myPrototype[name] = browserHelpers[name]; + }); + var BrowserGeneric = generic(methods, myPrototype, function (operand, name, args, done) { if (!operand.browser) { done(new Error('operand.browser is undefined')); diff --git a/lib/meteor/meteorProcessManager.js b/lib/meteor/meteorProcessManager.js index 257d707..eb09d33 100644 --- a/lib/meteor/meteorProcessManager.js +++ b/lib/meteor/meteorProcessManager.js @@ -52,6 +52,8 @@ module.exports = function createMeteorProcessManager (options) { function getMeteorProcess (setup) { + // TODO: make sure the setup is fine + if (pathToMain !== setup.pathToMain || pathToNode !== setup.pathToNode || mongoUrl !== setup.mongoUrl) { restartRequired = true; } diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 3e73095..9a4eb7b 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -69,12 +69,11 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { options = { pathToApp: options }; } - tools.mergeHelpers(myHelpers, [ helpers.both, helpers.server ]); - tools.mergeHelpers(myHelpers, gagarin_options.serverHelpers); + tools.mergeHelpers(myHelpers, options.helpers); var meteor = new Meteor({ pathToApp : options.pathToApp || gagarin_options.pathToApp, - helpers : tools.mergeHelpers(myHelpers, options.helpers), + helpers : myHelpers, settings : tools.getSettings(options.settings) || gagarin_settings, verbose : gagarin_options.verbose, remoteServer : options.remoteServer || gagarin_options.remoteServer, @@ -128,11 +127,10 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { options.flavor = options.flavor || gagarin_options.flavor || "promise"; - tools.mergeHelpers(myHelpers, [ helpers.both, helpers.client ]); - tools.mergeHelpers(myHelpers, gagarin_options.clientHelpers); + tools.mergeHelpers(myHelpers, options.helpers); var browser = createBrowser({ - helpers : tools.mergeHelpers(myHelpers, options.helpers), + helpers : myHelpers, location : options.location, webdriver : options.webdriver || gagarin_options.webdriver, windowSize : options.windowSize, @@ -190,8 +188,6 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { var makeMongoDB = require('../mongo'); - var myHelpers = {}; - options = options || {}; options.flavor = options.flavor || gagarin_options.flavor || "promise"; @@ -207,7 +203,8 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { pathToApp : options.pathToApp || gagarin_options.pathToApp, dbPath : options.dbPath, dbName : options.dbName, - }, helpers); + + }, options.helpers); before(function () { return mongo.start(); From 2667678a3709f744c9239fae63518c81b190cfd4 Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 26 Jan 2015 13:56:25 +0100 Subject: [PATCH 21/34] Code cleanup --- lib/mocha/interface.js | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 9a4eb7b..68e4fde 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -18,14 +18,11 @@ var url = require('url'); Mocha.interfaces['gagarin'] = module.exports = function (suite) { "use strict"; - // TODO: allow integration with other interfaces - // use the original bdd intrface Mocha.interfaces.bdd.apply(this, arguments); - - var gagarin_options = this.options; - var gagarin_settings = tools.getSettings(this.options.settings) || {}; // make sure it's not undefined + var gagarinOptions = this.options; + var gagarinSettings = tools.getSettings(this.options.settings) || {}; // make sure it's not undefined suite.on('pre-require', function (context) { @@ -38,6 +35,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.Fiber = Fiber; var originalIt = context.it; + context.it = runInsideFiber(context.it); context.it.skip = runInsideFiber(originalIt.skip); context.it.only = runInsideFiber(originalIt.only); @@ -59,7 +57,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { var myHelpers = {}; options = options || {}; - options.flavor = options.flavor || gagarin_options.flavor || "promise"; + options.flavor = options.flavor || gagarinOptions.flavor || "promise"; if (typeof options === 'function') { initialize = options; options = {}; @@ -72,12 +70,12 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { tools.mergeHelpers(myHelpers, options.helpers); var meteor = new Meteor({ - pathToApp : options.pathToApp || gagarin_options.pathToApp, + pathToApp : options.pathToApp || gagarinOptions.pathToApp, helpers : myHelpers, - settings : tools.getSettings(options.settings) || gagarin_settings, - verbose : gagarin_options.verbose, - remoteServer : options.remoteServer || gagarin_options.remoteServer, - skipBuild : options.skipBuild || gagarin_options.skipBuild, + settings : tools.getSettings(options.settings) || gagarinSettings, + verbose : gagarinOptions.verbose, + remoteServer : options.remoteServer || gagarinOptions.remoteServer, + skipBuild : options.skipBuild || gagarinOptions.skipBuild, }); meteor.useClosure(function () { @@ -125,18 +123,18 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { options = { location: options }; } - options.flavor = options.flavor || gagarin_options.flavor || "promise"; + options.flavor = options.flavor || gagarinOptions.flavor || "promise"; tools.mergeHelpers(myHelpers, options.helpers); var browser = createBrowser({ helpers : myHelpers, location : options.location, - webdriver : options.webdriver || gagarin_options.webdriver, + webdriver : options.webdriver || gagarinOptions.webdriver, windowSize : options.windowSize, capabilities : options.capabilities, - dontWaitForMeteor : options.dontWaitForMeteor || gagarin_options.dontWaitForMeteor, - meteorLoadTimeout : options.meteorLoadTimeout || gagarin_options.meteorLoadTimeout, + dontWaitForMeteor : options.dontWaitForMeteor || gagarinOptions.dontWaitForMeteor, + meteorLoadTimeout : options.meteorLoadTimeout || gagarinOptions.meteorLoadTimeout, }); browser.useClosure(function () { @@ -164,7 +162,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { var getDDPSetup = null; options = options || {}; - options.flavor = options.flavor || gagarin_options.flavor || "promise"; + options.flavor = options.flavor || gagarinOptions.flavor || "promise"; if (server.getDDPSetup) { getDDPSetup = server.getDDPSetup; @@ -189,7 +187,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { var makeMongoDB = require('../mongo'); options = options || {}; - options.flavor = options.flavor || gagarin_options.flavor || "promise"; + options.flavor = options.flavor || gagarinOptions.flavor || "promise"; if (typeof options === 'function') { initialize = options; options = {}; @@ -200,10 +198,10 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { } var mongo = makeMongoDB({ - pathToApp : options.pathToApp || gagarin_options.pathToApp, + pathToApp : options.pathToApp || gagarinOptions.pathToApp, dbPath : options.dbPath, dbName : options.dbName, - + }, options.helpers); before(function () { @@ -231,7 +229,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { }); }; - context.settings = JSON.parse(JSON.stringify(gagarin_settings)); // deep copy :P + context.settings = JSON.parse(JSON.stringify(gagarinSettings)); // deep copy :P }); } From 4ca2fe86d2b8dbf277432dc0aa8aca5f1d9d30bb Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 13:33:00 +0100 Subject: [PATCH 22/34] Cleaning up meteorProcess constructor --- lib/meteor/meteorProcess.js | 37 ++++++++++++++++++------------ lib/meteor/meteorProcessManager.js | 11 ++++----- lib/tools/index.js | 6 +---- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/meteor/meteorProcess.js b/lib/meteor/meteorProcess.js index 8d43cb9..262b9f2 100644 --- a/lib/meteor/meteorProcess.js +++ b/lib/meteor/meteorProcess.js @@ -1,30 +1,38 @@ var spawn = require('child_process').spawn; +var processCounter = 0; + /** * Creates a smart meteor instance, suitable for usage with the testing framework. * - * @param {String} node - path to node executable - * @param {String} main - path to applciation main - * @param {Object} env - environment variables for the new process - * @param {Object} options + * @param {string} path to node executable + * @param {string} path to applciation main file + * @param {object} environment variables for the new process + * @param {object} prototype for the returned object + * @param {object} options; may contain safetyTimeout value * - * Available options are: - * - onStart {Function} - * - onExit {Function} - * - safetyTimeout {Number} + * The prototype may implement the following methods + * - onStart () + * - onExit () + * - onData (data, { isError: true/false }) */ -module.exports = function createMeteorProcess (node, main, env, myPrototype) { +module.exports = function createMeteorProcess (node, main, env, myPrototype, options) { "use strict"; - - // TODO: let instance be an event emitter + + options = options || {}; var instance = Object.create(myPrototype); var meteor = null; var lastError = ""; var lastErrorAt = "nowhere"; - var safetyTimeout = instance.safetyTimeout || 5 * 1000; + var safetyTimeout = options.safetyTimeout !== undefined ? options.safetyTimeout : 5 * 1000; var safetyHandle = null; + instance.env = env; + instance.pid = processCounter++; // the only requirement is that it stays unique "locally" + + instance.kill = kill; + setTimeout(function () { try { meteor = spawn(node, [ main ], { env: env }); @@ -49,7 +57,7 @@ module.exports = function createMeteorProcess (node, main, env, myPrototype) { if (/Поехали!/.test(data.toString())) { clearTimeout(safetyHandle); - //----------------------------------- + //------------------------------------- instance.onStart && instance.onStart(); } @@ -105,8 +113,6 @@ module.exports = function createMeteorProcess (node, main, env, myPrototype) { }); - instance.kill = kill; - /** * Kill the meteor process and cleanup. * @@ -137,3 +143,4 @@ module.exports = function createMeteorProcess (node, main, env, myPrototype) { return instance; } + diff --git a/lib/meteor/meteorProcessManager.js b/lib/meteor/meteorProcessManager.js index eb09d33..2d6b1bd 100644 --- a/lib/meteor/meteorProcessManager.js +++ b/lib/meteor/meteorProcessManager.js @@ -75,9 +75,9 @@ module.exports = function createMeteorProcessManager (options) { cleanUpThen(function () { - meteorHasCrashed = false + meteorHasCrashed = false; - findAvailablePort(meteorPort).then(function (port) { + (meteorPort ? Promise.resolve(meteorPort) : findAvailablePort()).then(function (port) { meteorPort = port; @@ -93,10 +93,8 @@ module.exports = function createMeteorProcessManager (options) { env.GAGARIN_SETTINGS = "{}"; // only used if METEOR_SETTINGS does not contain gagarin field setTimeout(function () { - - meteorProcess = new createMeteorProcess(pathToNode, pathToMain, env, meteorProcessPrototype); - meteorProcess.env = env; - + // TODO: we may also specify the "safetyTimeout" here + meteorProcess = new createMeteorProcess(pathToNode, pathToMain, env, meteorProcessPrototype, {}); }, meteorRestartDelay); }).catch(reject); @@ -109,7 +107,6 @@ module.exports = function createMeteorProcessManager (options) { if (err) { return reject(err); } - this.pid = Math.random(); resolve(this); }; diff --git a/lib/tools/index.js b/lib/tools/index.js index d674424..67a616a 100644 --- a/lib/tools/index.js +++ b/lib/tools/index.js @@ -224,11 +224,7 @@ module.exports = { /** * Find a port, nobody is listening on. */ - findAvailablePort: function (port) { - - if (port) { - return Promise.resolve(port); - } + findAvailablePort: function () { return new Promise(function (resolve, reject) { var numberOfRetries = 5; From d5f673309b67e6b9c188cd2bf151d1f05189c8d3 Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 13:53:11 +0100 Subject: [PATCH 23/34] Fix the logic for default options --- lib/mocha/interface.js | 6 +++--- tests/specs/exceptions.js | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 68e4fde..9242b55 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -75,7 +75,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { settings : tools.getSettings(options.settings) || gagarinSettings, verbose : gagarinOptions.verbose, remoteServer : options.remoteServer || gagarinOptions.remoteServer, - skipBuild : options.skipBuild || gagarinOptions.skipBuild, + skipBuild : options.skipBuild !== undefined ? options.skipBuild : gagarinOptions.skipBuild, }); meteor.useClosure(function () { @@ -133,8 +133,8 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { webdriver : options.webdriver || gagarinOptions.webdriver, windowSize : options.windowSize, capabilities : options.capabilities, - dontWaitForMeteor : options.dontWaitForMeteor || gagarinOptions.dontWaitForMeteor, - meteorLoadTimeout : options.meteorLoadTimeout || gagarinOptions.meteorLoadTimeout, + dontWaitForMeteor : options.dontWaitForMeteor !== undefined ? options.dontWaitForMeteor : gagarinOptions.dontWaitForMeteor, + meteorLoadTimeout : options.meteorLoadTimeout !== undefined ? options.meteorLoadTimeout : gagarinOptions.meteorLoadTimeout, }); browser.useClosure(function () { diff --git a/tests/specs/exceptions.js b/tests/specs/exceptions.js index df6705d..9b969d0 100644 --- a/tests/specs/exceptions.js +++ b/tests/specs/exceptions.js @@ -13,7 +13,8 @@ describe('Reporting Exceptions', function () { var message = ""; var server = new Meteor({ - pathToApp: path.resolve(__dirname, '..', 'build_error') + pathToApp: path.resolve(__dirname, '..', 'build_error'), + skipBuild: false, // overwrite the default setting }); it('should throw an error', function () { From 0e9545e50e9314331f34ae752ab486c3b04078b8 Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 14:53:48 +0100 Subject: [PATCH 24/34] Added an obligatory meteor.init routine which can be run manually if noAutoStart option is used --- lib/meteor/index.js | 44 ++++++++++++++++++++++----- lib/mocha/interface.js | 26 +++++++++------- tests/incompatible/.meteor/packages | 2 +- tests/incompatible/.meteor/versions | 3 +- tests/specs/exceptions.js | 24 +++++++++------ tests/specs/initialize.js | 47 +++++++++++++++++++++++++++++ 6 files changed, 114 insertions(+), 32 deletions(-) diff --git a/lib/meteor/index.js b/lib/meteor/index.js index 30e456b..5c24c05 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -30,6 +30,8 @@ module.exports = function createMeteor (options) { var getDDPClient = createDDPClientManager(); var closure = null; + var getConfig = function () { return Promise.reject(new Error('meteor must be initialized before use')) }; + if (typeof options.mongoUrl === 'string') { mongoUrlPromise = Promise.resolve(options.mongoUrl); @@ -51,18 +53,42 @@ module.exports = function createMeteor (options) { // custom methods - //myPrototype.init = function () { - // return this.branch().then(function () { - // }); - //} + myPrototype.init = function (config) { + getConfig = function () { + return Promise.resolve(config); + } + return this; + } + + myPrototype.start = function (onStart) { + return this.then(function () { + console.warn('\n meteor.start is now deprecated; use meteor.startup instead\n'); + }) + .startup(onStart); + } + + myPrototype.startup = function (onStart) { + var self = this; + + return self.promise(function (resolve) { // wait on startup first + Meteor.startup(resolve); + + }).then(function () { + + if (typeof onStart === 'function') { + return onStart.length ? self.noWait().promise(onStart) : self.noWait().execute(onStart); + + } else if (onStart !== undefined) { + throw new Error('onStart has to be a function'); + } - myPrototype.start = function () { - return this.__custom__(function (operand, done) { - done(); }); } myPrototype.stop = function () { + + // TODO: do not start if we haven't done it yet + return this.__custom__(function (operand, done) { operand.ddpClient.close(); @@ -162,9 +188,11 @@ module.exports = function createMeteor (options) { function getOperand () { + //NOTE: we can potentially use the "getConfig()" promise to provide some async configuration + return Promise.all([ - getDDPSetup(), getMeteor() + getDDPSetup(), getMeteor(), getConfig() ]).then(function (results) { diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 9242b55..6ccde32 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -52,7 +52,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { context.expect = chai.expect; - context.meteor = function (options, initialize) { + context.meteor = function (options, onStart) { var myHelpers = {}; @@ -60,7 +60,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { options.flavor = options.flavor || gagarinOptions.flavor || "promise"; if (typeof options === 'function') { - initialize = options; options = {}; + onStart = options; options = {}; } if (typeof options === 'string') { @@ -82,17 +82,21 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { return stack[stack.length-1]; }); - before(function () { - return meteor.start().then(function () { - if (typeof initialize === 'function') { - return initialize.length ? meteor.promise(initialize) : meteor.execute(initialize); - } + if (!options.noAutoStart) { + + before(function () { + return meteor.init().startup(onStart); }); - }); - after(function () { - return meteor.stop(); - }); + after(function () { + return meteor.stop(); + }); + + } else { + if (onStart) { + console.warn('onStart will not work with noAutoStart option set to true'); + } + } if (options.flavor == "fiber") { var proxy = wrapPromisesForFiber(meteor, meteor.methods); diff --git a/tests/incompatible/.meteor/packages b/tests/incompatible/.meteor/packages index 5e150d4..705ec32 100644 --- a/tests/incompatible/.meteor/packages +++ b/tests/incompatible/.meteor/packages @@ -6,5 +6,5 @@ meteor-platform autopublish insecure -anti:gagarin@0.2.2 +anti:gagarin@0.3.0 diff --git a/tests/incompatible/.meteor/versions b/tests/incompatible/.meteor/versions index 2303a1d..1d1b6c5 100644 --- a/tests/incompatible/.meteor/versions +++ b/tests/incompatible/.meteor/versions @@ -1,4 +1,4 @@ -anti:gagarin@0.2.2 +anti:gagarin@0.3.0 application-configuration@1.0.4 autopublish@1.0.2 autoupdate@1.1.5 @@ -31,7 +31,6 @@ minifiers@1.1.3 minimongo@1.0.6 mobile-status-bar@1.0.2 mongo@1.0.11 -mrt:altimeter@0.0.2 observe-sequence@1.0.4 ordered-dict@1.0.2 random@1.0.2 diff --git a/tests/specs/exceptions.js b/tests/specs/exceptions.js index 9b969d0..72b0d9a 100644 --- a/tests/specs/exceptions.js +++ b/tests/specs/exceptions.js @@ -12,14 +12,15 @@ describe('Reporting Exceptions', function () { var message = ""; - var server = new Meteor({ - pathToApp: path.resolve(__dirname, '..', 'build_error'), - skipBuild: false, // overwrite the default setting + var server = meteor({ + pathToApp : path.resolve(__dirname, '..', 'build_error'), + skipBuild : false, // overwrite the default setting + noAutoStart : true, }); it('should throw an error', function () { return server - .start() + .init() .expectError(function (err) { message = err.message; }); @@ -39,13 +40,14 @@ describe('Reporting Exceptions', function () { var message = ""; - var server = new Meteor({ - pathToApp: path.resolve(__dirname, '..', 'no_gagarin') + var server = meteor({ + pathToApp : path.resolve(__dirname, '..', 'no_gagarin'), + noAutoStart : true, }); it('should throw an error', function () { return server - .start() + .init() .expectError(function (err) { message = err.message; }); @@ -65,13 +67,15 @@ describe('Reporting Exceptions', function () { var message = ""; - var server = new Meteor({ - pathToApp: path.resolve(__dirname, '..', 'incompatible') + var server = meteor({ + pathToApp : path.resolve(__dirname, '..', 'incompatible'), + skipBuild : false, // overwrite the default setting + noAutoStart : true, }); it('should throw an error', function () { return server - .start() + .init() .expectError(function (err) { message = err.message; }); diff --git a/tests/specs/initialize.js b/tests/specs/initialize.js index 8727abc..15ce0b9 100644 --- a/tests/specs/initialize.js +++ b/tests/specs/initialize.js @@ -43,4 +43,51 @@ describe('Initialization', function () { expect(b).to.equal(2); }); + describe('Manual initialization', function () { + + var server2 = meteor({ noAutoStart: true }); + + describe('Given the server is not initialized', function () { + + it('should throw an error if the user attepmts to do something', function () { + return server2.expectError(function (err) { + expect(err.message).to.contain('initialized'); + }); + }); + + }); + + describe('Given the server is initialized', function () { + + before(function () { + server2.init(); + }); + + it('should not throw errors anymore', function () { + return server2; + }); + + it('should not throw error if no argument is passed to meteor.startup', function () { + return server2.startup(); + }); + + it('should throw error if wrong argument is passed to meteor.startup', function () { + return server2.startup("thisIsNotAFucntion").expectError(function (err) { + expect(err.message).to.contain("function"); + }); + }); + + it('should be able to run a startup function', function () { + return server2.startup(function () { + return Meteor.release; + }).then(function (value) { + expect(value).to.be.ok; + }); + }); + + }); + + }); + + }); From b38336cda7eef1827d84a1607c5a1c03a274bc58 Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 14:56:32 +0100 Subject: [PATCH 25/34] Increased timeouts for reset detection --- tests/specs/browser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/specs/browser.js b/tests/specs/browser.js index e0bd9a8..f80bd97 100644 --- a/tests/specs/browser.js +++ b/tests/specs/browser.js @@ -86,7 +86,7 @@ describe('Tests with browser', function () { it('should recognize that the server was restarted', function () { return browser2 - .wait(5000, 'until reset event is detected', "return reset > 0") + .wait(7000, 'until reset event is detected', "return reset > 0") .execute("return reset;") .then(function (numberOfResets) { expect(numberOfResets).to.equal(1); @@ -96,7 +96,7 @@ describe('Tests with browser', function () { it ('another restart shoud work as well', function () { return server.restart().then(function () { return browser2 - .wait(5000, 'until reset event is detected', "return reset > 1") + .wait(7000, 'until reset event is detected', "return reset > 1") .execute("return reset;") .then(function (numberOfResets) { expect(numberOfResets).to.equal(2); From fa512f52edcbb96636ccb204da11e371dc866bac Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 15:07:48 +0100 Subject: [PATCH 26/34] Added deprecation warnings, fixes #78 --- lib/meteor/methods.js | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/meteor/methods.js b/lib/meteor/methods.js index d88745e..1a9a7d7 100644 --- a/lib/meteor/methods.js +++ b/lib/meteor/methods.js @@ -2,18 +2,23 @@ module.exports = {}; module.exports.promise = function (code, args) { + "use strict"; + + var deprecated = false; if (arguments.length < 2) { args = []; } if (!Array.isArray(args)) { - // throw new Error('args has to be an array'); + deprecated = true; args = [ args ]; } return this.__custom__(function (operand, cb) { + deprecated && warning('promise', 'code, arg'); + var ddpClient = operand.ddpClient; var closure = operand.closure; @@ -23,18 +28,23 @@ module.exports.promise = function (code, args) { } module.exports.execute = function (code, args) { + "use strict"; + + var deprecated = false; if (arguments.length < 2) { args = []; } if (!Array.isArray(args)) { - // throw new Error('args has to be an array'); + deprecated = true; args = [ args ]; } return this.__custom__(function (operand, cb) { + deprecated && warning('execute', 'code, arg'); + var ddpClient = operand.ddpClient; var closure = operand.closure; @@ -44,19 +54,23 @@ module.exports.execute = function (code, args) { } module.exports.wait = function (timeout, message, code, args) { + "use strict"; - if (arguments.length < 2) { + var deprecated = false; + + if (arguments.length < 4) { args = []; } if (!Array.isArray(args)) { - // throw new Error('args has to be an array'); + deprecated = true; args = [ args ]; - } return this.__custom__(function (operand, cb) { + deprecated && warning('wait', 'timeout, message, code, arg'); + var ddpClient = operand.ddpClient; var closure = operand.closure; @@ -66,6 +80,8 @@ module.exports.wait = function (timeout, message, code, args) { } function callDDPMethod (ddpClient, name, args, closure, cb) { + "use strict"; + if (!ddpClient) { return cb(new Error('invalid ddpClient')); } @@ -82,3 +98,9 @@ function callDDPMethod (ddpClient, name, args, closure, cb) { cb(null, feedback.value); }); } + +function warning (name, signature) { + "use strict"; + + console.warn('\n meteor.' + name + '(' + signature + ') is now deprecated; please use a list of arguments as the last parameter\n'); +} From 9e50f827170527eb18f426f2ba3573fe80c151af Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 15:14:28 +0100 Subject: [PATCH 27/34] Refine verbose option for builds --- lib/meteor/index.js | 3 ++- lib/mocha/interface.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/meteor/index.js b/lib/meteor/index.js index 5c24c05..b39225e 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -21,6 +21,7 @@ module.exports = function createMeteor (options) { var pathToApp = options.pathToApp || path.resolve('.'); var skipBuild = !!options.skipBuild; + var verbose = !!options.verbose; var remoteServer = options.remoteServer ? url.parse(options.remoteServer) : null; var mongoUrlPromise = null; @@ -158,7 +159,7 @@ module.exports = function createMeteor (options) { // helper functions producing usefull promises function getPathToMain () { - return BuildAsPromise({ pathToApp: pathToApp, skipBuild: skipBuild }); + return BuildAsPromise({ pathToApp: pathToApp, skipBuild: skipBuild, verbose: verbose }); } function getMongoUrl () { diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 6ccde32..1283b24 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -73,7 +73,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { pathToApp : options.pathToApp || gagarinOptions.pathToApp, helpers : myHelpers, settings : tools.getSettings(options.settings) || gagarinSettings, - verbose : gagarinOptions.verbose, + verbose : options.verbose !== undefined ? options.verbose : gagarinOptions.verbose, remoteServer : options.remoteServer || gagarinOptions.remoteServer, skipBuild : options.skipBuild !== undefined ? options.skipBuild : gagarinOptions.skipBuild, }); From 45f9ee6448aa2fff0cfd5d4b54b387ab6cce5e3e Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 15:18:19 +0100 Subject: [PATCH 28/34] Fixed capabilities test suite --- tests/specs/capabilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/specs/capabilities.js b/tests/specs/capabilities.js index 636bcba..5c6bd71 100644 --- a/tests/specs/capabilities.js +++ b/tests/specs/capabilities.js @@ -9,7 +9,7 @@ describe('Browser capabilities', function () { }); var client2 = browser({ - dontWaitForMeteor: true, + dontWaitForMeteor: false, location: server, capabilities: { browserName: 'chrome', From 52a102239c351466ed0694585ed5fde0445058da Mon Sep 17 00:00:00 2001 From: apendua Date: Tue, 27 Jan 2015 15:52:54 +0100 Subject: [PATCH 29/34] Improved expectError helper --- lib/meteor/meteorProcessManager.js | 4 +- lib/tools/genericPromiseChain.js | 24 ++++++++++- tests/specs/exceptions.js | 68 ++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/lib/meteor/meteorProcessManager.js b/lib/meteor/meteorProcessManager.js index 2d6b1bd..79276e0 100644 --- a/lib/meteor/meteorProcessManager.js +++ b/lib/meteor/meteorProcessManager.js @@ -39,7 +39,9 @@ module.exports = function createMeteorProcessManager (options) { restartRequired = true; - console.log('restart requested'); + if (verbose) { + console.log(chalk.cyan('[system]') + ' restart requested'); + } getMeteorProcess({ diff --git a/lib/tools/genericPromiseChain.js b/lib/tools/genericPromiseChain.js index 6776138..f2ba6c9 100644 --- a/lib/tools/genericPromiseChain.js +++ b/lib/tools/genericPromiseChain.js @@ -1,5 +1,6 @@ var Promise = require('es6-promise').Promise; var either = require('./index').either; +var expect = require('chai').expect; module.exports = function genericPromiseChain(methods, myPrototype, defaultAction) { @@ -48,10 +49,29 @@ module.exports = function genericPromiseChain(methods, myPrototype, defaultActio GenericPromiseChain.prototype.expectError = function (callback) { "use strict"; + var pattern = ''; + + if (typeof callback === 'string') { + pattern = callback; + callback = function (err) { expect(err.message).to.contain(pattern) } + } else if (callback instanceof RegExp) { + pattern = callback; + callback = function (err) { expect(err.message).to.match(pattern) } + } else if (callback === undefined) { // noop + callback = function () {}; + + } else if (typeof callback !== 'function') { + throw new Error('argument for expectError must be a string, RegExp or a function'); + } + var self = this; + return self.then(function () { - throw new Error('exception was not thrown'); - }, callback); + throw new Error('error was not thrown'); + }, function (err) { + expect(err).to.be.instanceof(Error); + callback(err); + }); }; GenericPromiseChain.prototype.noWait = function () { diff --git a/tests/specs/exceptions.js b/tests/specs/exceptions.js index 72b0d9a..a1df12b 100644 --- a/tests/specs/exceptions.js +++ b/tests/specs/exceptions.js @@ -4,6 +4,74 @@ var path = require('path'); describe('Reporting Exceptions', function () { + describe('Using expectError helper', function () { + + var server = meteor(); + + it('should throw error if wrong parameter is used', function () { + expect(function () { + server.expectError(123); + }).throw(/must be/); + }); + + it('should throw an error if no error is thrown', function () { + return server.expectError().then(function () { + throw new Error('error was not thrown'); + }, function (err) { + expect(err.message).to.contain('was not thrown'); + }); + }); + + it('should throw if wrong type of object is thrown', function () { + return server.then(function () { + throw "this is not a valid error"; + }).expectError().then(function () { + throw new Error('error was not thorwn'); + }, function (err) { + expect(err.message).to.contain('instance of Error'); + }); + }); + + it('may be used with no arguments', function () { + return server.then(function () { + throw new Error('just a fake error'); + }).expectError(); + }); + + it('may be used with string as an argument', function () { + return server.then(function () { + throw new Error('just a fake error'); + }).expectError('just a fake error'); + }); + + it('should throw if the string is not contained in err.message', function () { + return server.then(function () { + throw new Error('just a fake error'); + }).expectError('thisTextIsNotContainedInErrorMessage').then(function () { + throw new Error('error was not thrown'); + }, function (err) { + expect(err.message).to.contain('to include'); + }); + }); + + it('may be used with RegExp as an argument', function () { + return server.then(function () { + throw new Error('just a fake error'); + }).expectError(/error$/); + }); + + it('should throw if the RegExp does not match err.message', function () { + return server.then(function () { + throw new Error('just a fake error'); + }).expectError(/^error/).then(function () { + throw new Error('error was not thrown'); + }, function (err) { + expect(err.message).to.contain('to match'); + }); + }); + + }); + describe('Given the app does not build properly,', function () { // TODO: check if the process is properly killed From 2054469c4e42881dfa4b459f220f2c8206716fe8 Mon Sep 17 00:00:00 2001 From: grzegorz Date: Tue, 27 Jan 2015 17:43:52 +0100 Subject: [PATCH 30/34] used server instead of server location in example --- tests/example/tests/gagarin/example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/example/tests/gagarin/example.js b/tests/example/tests/gagarin/example.js index 43a5878..9c5f88c 100644 --- a/tests/example/tests/gagarin/example.js +++ b/tests/example/tests/gagarin/example.js @@ -5,7 +5,7 @@ describe('An example Gagarin test suite', function () { // probably the best place for your fixtures }); - var client = browser(server.location + "/path/to/some/view", function () { + var client = browser(server, function () { // some initialization on client (if needed) }); From 4f76d01e3a6535df7f69eb531ffd60587a058bcb Mon Sep 17 00:00:00 2001 From: apendua Date: Wed, 28 Jan 2015 11:34:12 +0100 Subject: [PATCH 31/34] Fixes #31 --- lib/browser/browserManager.js | 4 +++ lib/browser/index.js | 56 ++++++++++++++++++++++++++++---- lib/meteor/index.js | 20 +++++++----- lib/mocha/interface.js | 1 + lib/tools/genericPromiseChain.js | 12 ++++--- tests/example/example.js | 8 +++-- 6 files changed, 78 insertions(+), 23 deletions(-) diff --git a/lib/browser/browserManager.js b/lib/browser/browserManager.js index cf294b0..7677775 100644 --- a/lib/browser/browserManager.js +++ b/lib/browser/browserManager.js @@ -48,6 +48,10 @@ module.exports = function createBrowserManager (options) { if (err || status !== 'open') { return _reject(err || 'webdriver not found on ' + driverLocation); } + + // NOTE: it was not easy to find in the docs, so I am leaving it here as a comment + // capabilities.loggingPrefs = { "driver": "INFO", "browser": "INFO" }; + browser.init(capabilities, either(_reject).or(function () { isInitialized = true; diff --git a/lib/browser/index.js b/lib/browser/index.js index c0f1996..8cd9fbe 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -4,10 +4,12 @@ var browserMethods = require('./methods'); var browserHelpers = require('./helpers'); var Closure = require('../tools/closure'); var generic = require('../tools/generic'); +var chalk = require('chalk'); module.exports = function createBrowser (options) { "use strict"; + var verbose = !!options.verbose; var helpers = options.helpers || {}; var myPrototype = Object.create(helpers); @@ -51,15 +53,55 @@ module.exports = function createBrowser (options) { myPrototype[name] = browserHelpers[name]; }); - var BrowserGeneric = generic(methods, myPrototype, function (operand, name, args, done) { - if (!operand.browser) { - done(new Error('operand.browser is undefined')); - } else if (!operand.browser[name]) { - done(new Error('operand.browser does not implement method: ' + name)); + var logsCache = []; + + myPrototype.getLogs = function () { + if (verbose) { + return this.then(function () { + var listOfLogs = logsCache; + logsCache = []; + return listOfLogs; + }); } else { - args.push(done); - operand.browser[name].apply(operand.browser, args); + return this.__custom__(function (operand, done) { + operand.browser.log('browser', done); + }); } + } + + function getClientLogs (action) { + return function (operand, done) { + return action(operand, function (err) { + var args = Array.prototype.slice.call(arguments, 0); + if (err) { + done(err); + } else { + operand.browser.log('browser', function (err, listOfLogs) { + if (!err) { + listOfLogs.forEach(function (logEntry) { + console.log(chalk.yellow('[client]') + ' ' + logEntry.message); + }); + logsCache.push.apply(logsCache, listOfLogs); + } + done.apply(this, args); + }); + } + }); + } + } + + var BrowserGeneric = generic(methods, myPrototype, { + action: function (operand, name, args, done) { + if (!operand.browser) { + done(new Error('operand.browser is undefined')); + } else if (!operand.browser[name]) { + done(new Error('operand.browser does not implement method: ' + name)); + } else { + args.push(done); + operand.browser[name].apply(operand.browser, args); + } + }, + transform: options.verbose && getClientLogs }); var Browser = function () { diff --git a/lib/meteor/index.js b/lib/meteor/index.js index b39225e..191270f 100644 --- a/lib/meteor/index.js +++ b/lib/meteor/index.js @@ -130,15 +130,17 @@ module.exports = function createMeteor (options) { myPrototype[name] = meteorMethods[name]; }); - var MeteorGeneric = generic(methods, myPrototype, function (operand, name, args, done) { - if (!operand.process) { - done(new Error('operand.process is undefined')); - } else if (!operand.process[name]) { - done(new Error('operand.process does not implement method: ' + name)); - } else { - args.push(done); - operand.process[name].apply(operand.process, args); - } + var MeteorGeneric = generic(methods, myPrototype, { + action: function (operand, name, args, done) { + if (!operand.process) { + done(new Error('operand.process is undefined')); + } else if (!operand.process[name]) { + done(new Error('operand.process does not implement method: ' + name)); + } else { + args.push(done); + operand.process[name].apply(operand.process, args); + } + }, }); // create an object inheriting from MeteorGeneric diff --git a/lib/mocha/interface.js b/lib/mocha/interface.js index 1283b24..931545c 100644 --- a/lib/mocha/interface.js +++ b/lib/mocha/interface.js @@ -133,6 +133,7 @@ Mocha.interfaces['gagarin'] = module.exports = function (suite) { var browser = createBrowser({ helpers : myHelpers, + verbose : options.verbose !== undefined ? options.verbose : gagarinOptions.verbose, location : options.location, webdriver : options.webdriver || gagarinOptions.webdriver, windowSize : options.windowSize, diff --git a/lib/tools/genericPromiseChain.js b/lib/tools/genericPromiseChain.js index f2ba6c9..a8f9146 100644 --- a/lib/tools/genericPromiseChain.js +++ b/lib/tools/genericPromiseChain.js @@ -2,11 +2,12 @@ var Promise = require('es6-promise').Promise; var either = require('./index').either; var expect = require('chai').expect; -module.exports = function genericPromiseChain(methods, myPrototype, defaultAction) { +module.exports = function genericPromiseChain(methods, myPrototype, options) { - if (typeof defaultAction !== 'function') { - defaultAction = canonical; - } + options = options || {}; + + var defaultAction = typeof options.action === 'function' ? options.action : canonical; + var transform = options.transform; function GenericPromiseChain (operand) { "use strict"; @@ -121,6 +122,9 @@ module.exports = function genericPromiseChain(methods, myPrototype, defaultActio if (!operand || typeof operand !== 'object') { reject(new Error('GenericPromiseChain: invalid operand')); } + if (transform) { + action = transform(action); + } action(operand, either(cleanError(reject)).or(resolve)); }); }); diff --git a/tests/example/example.js b/tests/example/example.js index aeae168..456c260 100644 --- a/tests/example/example.js +++ b/tests/example/example.js @@ -8,9 +8,11 @@ if (Meteor.isClient) { Meteor.subscribe('items'); - Template.hello.greeting = function () { - return "Welcome to example."; - }; + Template.hello.helpers({ + greeting: function () { + return "Welcome to example."; + } + }); Template.hello.helpers({ counter: function () { From 2b6ba609f745024e7283fcc4a77efeafbd3b3cb9 Mon Sep 17 00:00:00 2001 From: apendua Date: Wed, 28 Jan 2015 11:49:38 +0100 Subject: [PATCH 32/34] Making the browser test suite more robust --- tests/example/example.js | 9 +++++++++ tests/specs/browser.js | 15 ++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/example/example.js b/tests/example/example.js index 456c260..6ae718c 100644 --- a/tests/example/example.js +++ b/tests/example/example.js @@ -27,9 +27,18 @@ if (Meteor.isClient) { }); Meteor.connection._stream.on('reset', function () { + // console.warn('connection reset detected'); reset += 1; }); + Meteor.startup(function () { + console.warn('application started'); + }); + + Tracker.autorun(function () { + // console.warn(JSON.stringify(Meteor.connection.status())); + }); + } if (Meteor.isServer) { diff --git a/tests/specs/browser.js b/tests/specs/browser.js index f80bd97..bf7fe73 100644 --- a/tests/specs/browser.js +++ b/tests/specs/browser.js @@ -86,20 +86,25 @@ describe('Tests with browser', function () { it('should recognize that the server was restarted', function () { return browser2 - .wait(7000, 'until reset event is detected', "return reset > 0") + .wait(7000, 'until status.connected === true', function () { + return Meteor.connection.status().connected; + }) .execute("return reset;") .then(function (numberOfResets) { - expect(numberOfResets).to.equal(1); + // XXX the first "reset" occurs on startup, so we have two resets up to this point + expect(numberOfResets).to.equal(2); }); }); it ('another restart shoud work as well', function () { - return server.restart().then(function () { + return server.restart(2000).then(function () { return browser2 - .wait(7000, 'until reset event is detected', "return reset > 1") + .wait(7000, 'until status.connected === true', function () { + return Meteor.connection.status().connected; + }) .execute("return reset;") .then(function (numberOfResets) { - expect(numberOfResets).to.equal(2); + expect(numberOfResets).to.equal(3); }); }); }); From 897f1643fb7855274a3b24e4a96a1cc4baf819aa Mon Sep 17 00:00:00 2001 From: apendua Date: Wed, 28 Jan 2015 11:58:16 +0100 Subject: [PATCH 33/34] Another improvement to that test suite --- tests/specs/browser.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/specs/browser.js b/tests/specs/browser.js index bf7fe73..78b5608 100644 --- a/tests/specs/browser.js +++ b/tests/specs/browser.js @@ -68,6 +68,7 @@ describe('Tests with browser', function () { describe('Restarting server', function () { var browser2 = browser(server); + var value = 0; this.timeout(10000); @@ -75,6 +76,14 @@ describe('Tests with browser', function () { return server.restart(2000); }); + before(function () { + return browser2 + .execute("return reset;") + .then(function (numberOfResets) { + value = numberOfResets; + }); + }) + it ('should be all right', function () { return server.execute(function () { return Meteor.release; @@ -92,7 +101,7 @@ describe('Tests with browser', function () { .execute("return reset;") .then(function (numberOfResets) { // XXX the first "reset" occurs on startup, so we have two resets up to this point - expect(numberOfResets).to.equal(2); + expect(numberOfResets).to.equal(value + 1); }); }); @@ -104,7 +113,7 @@ describe('Tests with browser', function () { }) .execute("return reset;") .then(function (numberOfResets) { - expect(numberOfResets).to.equal(3); + expect(numberOfResets).to.equal(value + 2); }); }); }); From b1c60b23ff49f42a36d2101639aa644511d27876 Mon Sep 17 00:00:00 2001 From: apendua Date: Mon, 2 Feb 2015 10:56:18 +0100 Subject: [PATCH 34/34] Bumped package version --- .versions | 2 +- package.js | 2 +- package.json | 2 +- tests/build_error/.meteor/versions | 2 +- tests/example/.meteor/versions | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.versions b/.versions index e945d69..2b26955 100644 --- a/.versions +++ b/.versions @@ -1,4 +1,4 @@ -anti:gagarin@0.4.0 +anti:gagarin@0.4.1 base64@1.0.2 callback-hook@1.0.2 check@1.0.4 diff --git a/package.js b/package.js index 8eef33a..b0fbf79 100644 --- a/package.js +++ b/package.js @@ -2,7 +2,7 @@ Package.describe({ summary: "Gagarin, a Meteor testing framework", name: "anti:gagarin", - version: "0.4.0", + version: "0.4.1", git: "https://github.com/anticoders/gagarin.git", }); diff --git a/package.json b/package.json index 17088d6..6b991ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gagarin", - "version": "0.4.0", + "version": "0.4.1", "description": "another testing framework for your meteor apps", "main": "gagarin.js", "repository": { diff --git a/tests/build_error/.meteor/versions b/tests/build_error/.meteor/versions index c3e3885..21cb25d 100644 --- a/tests/build_error/.meteor/versions +++ b/tests/build_error/.meteor/versions @@ -1,4 +1,4 @@ -anti:gagarin@0.4.0 +anti:gagarin@0.4.1 application-configuration@1.0.4 autopublish@1.0.2 autoupdate@1.1.5 diff --git a/tests/example/.meteor/versions b/tests/example/.meteor/versions index bf3359e..550cf22 100644 --- a/tests/example/.meteor/versions +++ b/tests/example/.meteor/versions @@ -1,6 +1,6 @@ accounts-base@1.1.3 accounts-password@1.0.6 -anti:gagarin@0.4.0 +anti:gagarin@0.4.1 application-configuration@1.0.4 autoupdate@1.1.5 base64@1.0.2