packages/meteor-jasmine/src/client/unit/ClientUnitTestFramework.js
/* globals ClientUnitTestFramework: true, __meteor_runtime_config__: false */
var path = Npm.require('path');
var fs = Npm.require('fs');
var mkdirp = Npm.require('mkdirp');
ClientUnitTestFramework = function (options) {
options = options || {}
_.defaults(options, {
name: 'jasmine-client-unit',
regex: '^tests/jasmine/client/unit/.+\\.(js|coffee|litcoffee|coffee\\.md)$',
sampleTestGenerator: function () {
return [
{
path: 'jasmine/client/unit/sample/spec/PlayerSpec.js',
contents: Assets.getText('src/client/unit/sample-tests/sample/spec/PlayerSpec.js')
},
{
path: 'jasmine/client/unit/sample/spec/SpecMatchers.js',
contents: Assets.getText('src/client/unit/sample-tests/sample/spec/SpecMatchers.js')
},
{
path: 'jasmine/client/unit/sample/src/Player.js',
contents: Assets.getText('src/client/unit/sample-tests/sample/src/Player.js')
},
{
path: 'jasmine/client/unit/sample/src/Song.js',
contents: Assets.getText('src/client/unit/sample-tests/sample/src/Song.js')
}
]
},
jasmineRequire: null
})
this.userKarmaConfig = {}
JasmineTestFramework.call(this, options)
}
ClientUnitTestFramework.prototype = Object.create(JasmineTestFramework.prototype)
_.extend(ClientUnitTestFramework.prototype, {
start: function () {
lazyStart(this.name, this.startKarma.bind(this))
},
startKarma: function () {
var self = this
self._restartKarma();
// Listen for message 'refresh:client' that signals incoming 'refreshable' autoupdate
process.on('message', Meteor.bindEnvironment(function (message) {
log.debug('client-refresh noticed, stopping Karma')
if (message && message.refresh === 'client') {
// Listen for message 'on-listening' that signals that the application has been rebuild
// and is ready to serve
// * This callback *must* be registered here in 'on-message-refresh-client'
// * because onListening is a short-lived registration that is removed after firing once
WebApp.onListening(function () {
log.debug('WebApp has been updated. Updating Karma config file and starting it up.');
self._restartKarma();
});
}
}));
},
_restartKarma: function () {
var self = this
var karmaConfig = this.getKarmaConfig();
if (Karma.isRunning(self.name)) {
Karma.reloadFileList(self.name, karmaConfig.files)
} else {
self._generateContextHtml()
self._generateDebugHtml()
Karma.start(self.name, karmaConfig)
}
},
_generateContextHtml: function () {
this._generateKarmaHtml('context')
},
_generateDebugHtml: function () {
this._generateKarmaHtml('debug')
},
_generateKarmaHtml: function (type) {
var fileName = type + '.html'
var htmlPath = this._getKarmaHtmlPath(type);
mkdirp.sync(path.dirname(htmlPath))
var headHtml = this._getHeadHtml() || ''
var contextHtml = Assets.getText('src/client/unit/assets/' + fileName)
.replace('%HEAD%', headHtml)
fs.writeFileSync(htmlPath, contextHtml, {encoding: 'utf8'})
},
_getKarmaHtmlPath: function (type) {
var fileName = type + '.html'
return path.join(
MeteorFilesHelpers.getAppPath(),
'.meteor/local/karma/',
this.name, fileName
)
},
setUserKarmaConfig: function (config) {
var blacklist = [
'autoWatch', 'autoWatchBatchDelay',
'basePath', 'browserDisconnectTimeout', 'browserDisconnectTolerance',
'browserNoActivityTimeout', 'browsers', 'captureTimeout', 'client',
'exclude', 'files', 'frameworks', 'hostname', 'port', 'proxies', 'singleRun',
'urlRoot'
]
this.userKarmaConfig = _.omit(config, blacklist)
},
getKarmaConfig: function () {
var files = [];
var proxies = {};
this._addPreAppFiles(files, proxies)
this._addPackageFiles(files, proxies)
this._addHelperFiles(files, proxies)
this._addStubFiles(files, proxies)
this._addAppFiles(files, proxies)
this._addTestFiles(files, proxies)
var launcherPlugins = {
'Chrome': 'karma-chrome-launcher',
'HiddenChrome': 'karma-chrome-launcher',
'ChromeCanary': 'karma-chrome-launcher',
'Firefox': 'karma-firefox-launcher',
'PhantomJS': 'karma-phantomjs-launcher',
'SauceLabs': 'karma-sauce-launcher'
}
var browser = process.env.JASMINE_BROWSER || 'HiddenChrome';
var launcherPlugin = launcherPlugins[browser];
var basePath = Velocity.getAppPath()
/* jshint camelcase: false */
var startOptions = _.extend({}, this.userKarmaConfig, {
port: 9876,
basePath: basePath,
frameworks: ['jasmine'],
browsers: [browser],
customLaunchers: {
HiddenChrome: {
base: 'Chrome',
flags: ['--window-size=1024,768', '--window-position=-1024,0'],
}
},
plugins: [
'karma-jasmine',
launcherPlugin,
'karma-coffee-preprocessor'
],
files: files,
proxies: proxies,
client: {
contextFile: this._getKarmaHtmlPath('context'),
debugFile: this._getKarmaHtmlPath('debug'),
args: [_.defaults({
// Make those values constant to avoid unnecessary Karma restarts
autoupdateVersion: 'unknown',
autoupdateVersionRefreshable: 'unknown',
autoupdateVersionCordova: 'none'
}, __meteor_runtime_config__)]
},
browserDisconnectTimeout: 10000,
browserNoActivityTimeout: 15000,
preprocessors: {
'**/*.{coffee,litcoffee,coffee.md}': ['coffee']
},
coffeePreprocessor: {
options: {
bare: true,
sourceMap: true
},
transformPath: function (path) {
return path.replace(/\.(coffee|litcoffee|coffee\\.md)$/, '.js');
}
}
})
/* jshint camelcase: true */
if (this.userKarmaConfig.plugins) {
startOptions.plugins = startOptions.plugins.concat(this.userKarmaConfig.plugins)
}
if (this.userKarmaConfig.preprocessors) {
_.extend(startOptions.preprocessors, this.userKarmaConfig.preprocessors)
}
return startOptions
},
_addPreAppFiles: function (files) {
files.push(
this._getAssetPath('src/client/unit/assets/__meteor_runtime_config__.js')
)
},
_addPackageFiles: function (files, proxies) {
_.chain(WebApp.clientPrograms['web.browser'].manifest)
.filter(function (file) {
return file.path.indexOf('packages/') === 0
})
.filter(function (file) {
var ignoredFiles = [
'packages/sanjo_jasmine.js',
'packages/velocity_html-reporter.js'
]
return !_.contains(ignoredFiles, file.path)
})
.forEach(function (file) {
var mockedFiles = [
'packages/autoupdate.js',
'packages/reload.js',
'packages/meteorhacks_fast-render.js'
]
if (_.contains(mockedFiles, file.path)) {
files.push(this._getAssetPath('src/client/unit/assets/mocks/' + file.path))
} else {
this._addFile(file, files, proxies)
}
}, this)
.value()
},
_addAppFiles: function (files, proxies) {
return _.chain(WebApp.clientPrograms['web.browser'].manifest)
.filter(function (file) {
return file.path.indexOf('packages/') !== 0
})
.forEach(function (file) {
this._addFile(file, files, proxies)
}, this)
.value()
},
_addFile: function (file, files, proxies) {
var basePath = '.meteor/local/build/programs/web.browser/'
files.push({
pattern: basePath + file.path,
watched: false,
included: _.contains(['js', 'css'], file.type),
served: true
});
if (file.type === 'asset') {
proxies[file.url] = '/base/' + basePath + file.path
}
},
_addHelperFiles: function (files) {
files.push(
this._getAssetPath('src/client/unit/assets/jasmine-jquery.js'),
this._getAssetPath('.npm/package/node_modules/component-mocker/index.js'),
this._getAssetPath('src/lib/mock.js'),
this._getAssetPath('src/lib/VelocityTestReporter.js'),
this._getAssetPath('src/client/unit/assets/adapter.js'),
'.meteor/local/build/programs/server/assets/packages/velocity_meteor-stubs/index.js',
this._getAssetPath('src/client/unit/assets/helpers/iron_router.js')
)
},
_addStubFiles: function (files) {
files.push(
'tests/jasmine/client/unit/**/*-+(stub|stubs|mock|mocks).+(js|coffee|litcoffee|coffee.md)'
)
},
_addTestFiles: function (files) {
// Use a match pattern directly.
// That allows Karma to detect changes and rerun the tests.
files.push(
'tests/jasmine/client/unit/**/*.+(js|coffee|litcoffee|coffee.md)'
)
},
_getAssetPath: function (fileName) {
var assetsPath = '.meteor/local/build/programs/server/assets/packages/sanjo_jasmine/'
return assetsPath + fileName;
},
_getHeadHtml: function () {
try {
return fs.readFileSync(
path.join(Velocity.getAppPath(), '.meteor/local/build/programs/web.browser/head.html'),
{encoding: 'utf8'}
);
} catch (error) {
return null;
}
}
});