diff --git a/.npmignore b/.npmignore index d522ca2b..4f6a71f7 100644 --- a/.npmignore +++ b/.npmignore @@ -10,6 +10,4 @@ dist/ .npmignore **/.gitkeep bower.json -ember-cli-build.js -Brocfile.js testem.json diff --git a/.travis.yml b/.travis.yml index d2d78875..c4732630 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ --- language: node_js - -sudo: false - node_js: - "0.10" - "0.12" - - "iojs" + - "4.2" + - "5" + +sudo: false cache: directories: @@ -17,18 +17,17 @@ env: - NPM_SCRIPT=client-test matrix: + fast_finish: true allow_failures: - - node_js: "0.10" + - env: EMBER_TRY_SCENARIO=ember-canary before_install: - - mkdir travis-phantomjs - - wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 - - tar -xvf $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -C $PWD/travis-phantomjs - - export PATH=$PWD/travis-phantomjs:$PATH + - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH - "npm config set spin false" - "npm install -g npm@^2" install: + - node --version - npm install -g bower - npm install - bower install diff --git a/.watchmanconfig b/.watchmanconfig index 5e9462c2..e7834e3e 100644 --- a/.watchmanconfig +++ b/.watchmanconfig @@ -1,3 +1,3 @@ { - "ignore_dirs": ["tmp"] + "ignore_dirs": ["tmp", "dist"] } diff --git a/LICENSE.md b/LICENSE.md index 00e9fbbf..02000b56 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 +Copyright (c) 2016 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index a06b9e24..568d1e1b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ember-try [![Build Status](https://travis-ci.org/kategengler/ember-try.svg?branch=master)](https://travis-ci.org/kategengler/ember-try) -An ember-cli addon to test against multiple bower dependencies, such as `ember` and `ember-data`. +An ember-cli addon to test against multiple bower and npm dependencies, such as `ember` and `ember-data`. ### Installation @@ -57,37 +57,53 @@ module.exports = { scenarios: [ { name: 'Ember 1.10 with ember-data', - dependencies: { - 'ember': '1.10.0', - 'ember-data': '1.0.0-beta.15' + bower: { + dependencies: { + 'ember': '1.10.0', + 'ember-data': '1.0.0-beta.15' + } } }, { name: 'Ember 1.11.0-beta.5', - dependencies: { - 'ember': '1.11.0-beta.5' + bower: { + dependencies: { + 'ember': '1.11.0-beta.5' + } } }, { - name: 'Ember canary', - dependencies: { - 'ember': 'canary' + name: 'Ember canary with Ember-Data 2.3.0', + npm: { + devDependencies: { + 'ember-data': '2.3.0' + } + }, + bower: { + dependencies: { + 'ember': 'components/ember#canary' + }, + resolutions: { + 'ember': 'canary' + } } }, { name: 'Ember beta', - dependencies: { - 'ember': 'components/ember#beta' - }, - resolutions: { // Resolutions are only necessary when they do not match the version specified in `dependencies` - 'ember': 'canary' + bower: { + dependencies: { + 'ember': 'components/ember#beta' + }, + resolutions: { // Resolutions are only necessary when they do not match the version specified in `dependencies` + 'ember': 'beta' + } } } ] }; ``` -Scenarios are sets of dependencies (`bower` only). They can be specified exactly as in the `bower.json` +Scenarios are sets of dependencies (`bower` and `npm` only). They can be specified exactly as in the `bower.json` or `package.json` The `name` can be used to try just one scenario using the `ember try` command. If no `config/ember-try.js` file is present, the default config will be used. This is the current default config: @@ -96,27 +112,35 @@ If no `config/ember-try.js` file is present, the default config will be used. Th { scenarios: [ { - name: "default", - dependencies: { } // no dependencies needed as the - // default is already specified in - // the consuming app's bower.json + name: 'default', + bower: { + dependencies: { } /* No dependencies needed as the + default is already specified in + the consuming app's bower.json */ + } }, { - name: "ember-release", - dependencies: { - "ember": "release" + name: 'ember-release', + bower: { + dependencies: { + ember: 'release' + } } }, { - name: "ember-beta", - dependencies: { - "ember": "beta" + name: 'ember-beta', + bower: { + dependencies: { + ember: 'beta' + } } }, { - name: "ember-canary", - dependencies: { - "ember": "canary" + name: 'ember-canary', + bower: { + dependencies: { + ember: 'canary' + } } } ] @@ -128,8 +152,3 @@ See an example of using `ember-try` for CI [here](https://github.com/kategengler ### Special Thanks - Much credit is due to [Edward Faulkner](https://github.com/ef4) The scripts in [liquid-fire](https://github.com/ef4/liquid-fire) that test against multiple ember versions were the inspriation for this project. - -### TODO -- [ ] Add tests -- [ ] Add a blueprint for the config -- [ ] Look into `SilentError` as seen on other `ember-cli` addons to see if its preferable to `throw new Error` for preconditions. diff --git a/bower.json b/bower.json index 264a9a06..80e3b613 100644 --- a/bower.json +++ b/bower.json @@ -1,15 +1,15 @@ { "name": "ember-try", "dependencies": { - "ember": "1.13.7", - "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3", - "ember-cli-test-loader": "ember-cli-test-loader#0.1.3", - "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5", - "ember-qunit": "0.4.9", - "ember-qunit-notifications": "0.0.7", - "ember-resolver": "~0.1.18", - "jquery": "^1.11.3", - "loader.js": "ember-cli/loader.js#3.2.1", - "qunit": "~1.18.0" + "ember": "beta", + "ember-cli-shims": "0.1.0", + "ember-cli-test-loader": "0.2.2", + "ember-load-initializers": "0.1.7", + "ember-qunit-notifications": "0.1.0", + "jquery": "1.11.3", + "loader.js": "^3.5.0" + }, + "resolutions": { + "ember": "beta" } } diff --git a/config/environment.js b/config/environment.js index 0dfaed47..28a787b6 100644 --- a/config/environment.js +++ b/config/environment.js @@ -1,3 +1,4 @@ +/*jshint node:true*/ 'use strict'; module.exports = function(/* environment, appConfig */) { diff --git a/ember-cli-build.js b/ember-cli-build.js index d37d64cd..4ac39137 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -1,13 +1,14 @@ +/*jshint node:true*/ /* global require, module */ -var EmberApp = require('ember-cli/lib/broccoli/ember-addon'); +var EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); module.exports = function(defaults) { - var app = new EmberApp(defaults, { + var app = new EmberAddon(defaults, { // Add options here }); /* - This build file specifes the options for the dummy test app of this + This build file specifies the options for the dummy test app of this addon, located in `/tests/dummy` This build file does *not* influence how the addon or the app using it behave. You most likely want to be modifying `./index.js` or app's build file diff --git a/lib/commands/reset.js b/lib/commands/reset.js index a3ec1004..029c3a07 100644 --- a/lib/commands/reset.js +++ b/lib/commands/reset.js @@ -8,10 +8,12 @@ module.exports = { works: 'insideProject', run: function(commandOptions, rawArgs) { + var config = require('../utils/config')({ project: this.project }); var ResetTask = require('../tasks/reset'); var resetTask = new ResetTask({ ui: this.ui, - project: this.project + project: this.project, + config: config }); return resetTask.run(); diff --git a/lib/commands/testall.js b/lib/commands/testall.js index 7941afa9..a228d60e 100644 --- a/lib/commands/testall.js +++ b/lib/commands/testall.js @@ -13,14 +13,14 @@ module.exports = { var config = require('../utils/config')({ project: this.project }); - var TestallTask = require('../tasks/testall'); + var TryEach = require('../tasks/try-each'); - var testallTask = new TestallTask({ + var tryEachTask = new TryEach({ ui: this.ui, project: this.project, config: config }); - return testallTask.run(commandOptions); + return tryEachTask.run(config.scenarios, commandOptions); } }; diff --git a/lib/commands/try.js b/lib/commands/try.js index 9e8d9957..f4cba832 100644 --- a/lib/commands/try.js +++ b/lib/commands/try.js @@ -49,14 +49,15 @@ module.exports = { 'specified in the ember-try.js config.'); } - var TryTask = require('../tasks/try'); - var tryTask = new TryTask({ + var TryEachTask = require('../tasks/try-each'); + var tryEachTask = new TryEachTask({ ui: this.ui, project: this.project, - config: config + config: config, + commandArgs: commandArgs }); - return tryTask.run(scenario, commandArgs, commandOptions); + return tryEachTask.run([scenario], commandOptions); } }; diff --git a/lib/tasks/reset.js b/lib/tasks/reset.js index 7a3c7b26..93f5a426 100644 --- a/lib/tasks/reset.js +++ b/lib/tasks/reset.js @@ -1,9 +1,9 @@ 'use strict'; var CoreObject = require('core-object'); -var BowerHelpers = require('../utils/bower-helpers'); +var ScenarioManager = require('../utils/scenario-manager'); module.exports = CoreObject.extend({ run: function() { - return BowerHelpers.cleanup(this.project.root); + return new ScenarioManager({project: this.project, config: this.config}).cleanup(); } }); diff --git a/lib/tasks/testall.js b/lib/tasks/testall.js deleted file mode 100644 index 72a83b03..00000000 --- a/lib/tasks/testall.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict'; -var CoreObject = require('core-object'); -var RSVP = require('rsvp'); -var mapSeries = require('promise-map-series'); -var chalk = require('chalk'); -var ScenarioManager = require('../utils/scenario-manager'); -var BowerHelpers = require('../utils/bower-helpers'); -var run = require('../utils/run'); -var findEmberPath = require('./../utils/find-ember-path'); - -module.exports = CoreObject.extend({ - run: function(options) { - var task = this; - var scenarios = this.config.scenarios; - this.ScenarioManager = new ScenarioManager({ui: this.ui, project: this.project}); - - return BowerHelpers.backupBowerFile(task.project.root).then(function() { - return mapSeries(scenarios, task._testVersion, task).then(function(results) { - var promise; - if (options.skipCleanup) { - // Create a fake promise for consistency - promise = RSVP.Promise.resolve(); - } else { - promise = BowerHelpers.cleanup(task.project.root); - } - return promise.then(function() { - task._printResults(scenarios, results); - if (results.indexOf(false) > -1) { - process.exit(1); - } else { - process.exit(0); - } - }); - }).catch(function(err) { - task.ui.writeLine(err); - task.ui.writeLine(err.stack); - process.exit(1); - }); - }); - }, - - _testVersion: function(scenario) { - var task = this; - return this.ScenarioManager.changeTo(scenario) - .then(function() { - return task._runTests(); - }); - }, - - _runTests: function() { - var task = this; - - return findEmberPath(task.project.root) - .then(function(emberPath) { - return run('node', [emberPath, 'test'], {cwd: task.project.root}); - }) - .then(function() { - return RSVP.resolve(true); - }) - .catch(function(err) { - return RSVP.resolve(false); - }); - }, - - _printResults: function(scenarios, results) { - var task = this; - task.ui.writeLine(''); - task.ui.writeLine('------ RESULTS ------'); - task.ui.writeLine(''); - scenarios.forEach(function(scenario, index) { - if (results[index]) { - task.ui.writeLine(chalk.green(scenario.name + ': PASS')); - } else { - task.ui.writeLine(chalk.red(scenario.name + ': FAIL')); - } - }); - } -}); diff --git a/lib/tasks/try-each.js b/lib/tasks/try-each.js new file mode 100644 index 00000000..f1ee5cf7 --- /dev/null +++ b/lib/tasks/try-each.js @@ -0,0 +1,102 @@ +'use strict'; +var CoreObject = require('core-object'); +var RSVP = require('rsvp'); +var mapSeries = require('promise-map-series'); +var chalk = require('chalk'); +var ScenarioManager = require('./../utils/scenario-manager'); +var runCommand = require('./../utils/run-command'); +var ResultSummary = require('./../utils/result-summary'); + +module.exports = CoreObject.extend({ + run: function(scenarios, options) { + var task = this; + + task.ScenarioManager = new ScenarioManager({ui: task.ui, + project: task.project, + config: task.config, + dependencyManagerAdapters: task.dependencyManagerAdapters + }); + + process.on('SIGINT', function() { + task.ui.writeLine('\nGracefully shutting down from SIGINT (Ctrl-C)'); + return task.ScenarioManager.cleanup().then(function() { + task._exit(); + }); + }); + + return task.ScenarioManager.setup().then(function() { + return mapSeries(scenarios, task._runCommandForThisScenario, task); + }).then(function(results) { + return task._optionallyCleanup(options).then(function() { + task._printResults(results); + task._exitAsAppropriate(results); + }); + }).catch(function(err) { + task.ui.writeLine(chalk.red('Error!')); + if (err) { + task.ui.writeLine(chalk.red(err)); + task.ui.writeLine(chalk.red(err.stack)); + } + task._exit(1); + }); + }, + + _runCommandForThisScenario: function(scenario) { + var task = this; + return task.ScenarioManager.changeTo(scenario) + .then(function(scenarioDependencyState) { + var runResults = { + scenario: scenario.name, + dependencyState: scenarioDependencyState + }; + return task._runCommand().then(function(result) { + runResults.result = result; + return RSVP.resolve(runResults); + }); + }); + }, + + _runCommand: function() { + var task = this; + return runCommand(task.project.root, this._commandArgs()); + }, + + _commandArgs: function() { + return this.commandArgs || ['test']; + }, + + _printResults: function(results) { + new ResultSummary({ui: this.ui, results: results, command: this._commandArgs().join(' ')}).print(); + }, + + _exitAsAppropriate: function(results) { + var outcomes = results.map(function(result) { + return result.result; + }); + this._exitBasedOnCondition(outcomes.indexOf(false) > -1); + }, + + _optionallyCleanup: function(options) { + var task = this; + var promise; + if (options.skipCleanup) { + // Create a fake promise for consistency + promise = RSVP.Promise.resolve(); + } else { + promise = task.ScenarioManager.cleanup(); + } + return promise; + }, + + _exitBasedOnCondition: function(condition) { + if (condition) { + this._exit(1); + } else { + this._exit(0); + } + }, + + _exit: function(code) { + process.exit(code); + } +}); diff --git a/lib/tasks/try.js b/lib/tasks/try.js deleted file mode 100644 index b54b488e..00000000 --- a/lib/tasks/try.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; -var CoreObject = require('core-object'); -var RSVP = require('rsvp'); -var ScenarioManager = require('../utils/scenario-manager'); -var run = require('../utils/run'); -var BowerHelpers = require('../utils/bower-helpers'); -var findEmberPath = require('./../utils/find-ember-path'); - -module.exports = CoreObject.extend({ - run: function(scenario, commandArgs, commandOptions) { - var task = this; - var commandName = commandOptions[1] || 'test'; - process.on('SIGINT', function() { - task.ui.writeLine('\nGracefully shutting down from SIGINT (Ctrl-C)'); - BowerHelpers.cleanup(task.project.root).then(function() { - process.exit(); - }); - }); - - this.ScenarioManager = new ScenarioManager({ui: this.ui, project: this.project}); - return BowerHelpers.backupBowerFile(task.project.root).then(function() { - return task.ScenarioManager.changeTo(scenario) - .then(function() { - return findEmberPath(task.project.root); - }) - .then(function(emberPath) { - var args = [].concat(emberPath, commandArgs); - - return run('node', args, {cwd: task.project.root}) - .then(function() { - return RSVP.resolve(true); - }) - .catch(function() { - return RSVP.resolve(false); - }); - }) - .then(function(result) { - var promise; - if (commandOptions.skipCleanup) { - // Create a fake promise for consistency - promise = RSVP.Promise.resolve(); - } else { - promise = BowerHelpers.cleanup(task.project.root); - } - return promise.then(function() { - if (!result) { - task.ui.writeLine(''); - task.ui.writeLine('ember ' + commandName + ' with scenario ' + scenario.name + ' exited nonzero'); - process.exit(1); - } else { - process.exit(0); - } - }); - }) - .catch(function(err) { - task.ui.writeLine(err); - task.ui.writeLine(err.stack); - process.exit(1); - }); - }); - } -}); diff --git a/lib/utils/bower-adapter.js b/lib/utils/bower-adapter.js new file mode 100644 index 00000000..26dd5a11 --- /dev/null +++ b/lib/utils/bower-adapter.js @@ -0,0 +1,129 @@ +var CoreObject = require('core-object'); +var fs = require('fs-extra'); +var RSVP = require('rsvp'); +var path = require('path'); +var findEmberPath = require('./find-ember-path'); +var extend = require('extend'); +var rimraf = RSVP.denodeify(require('rimraf')); +var resolve = RSVP.denodeify(require('resolve')); + + +module.exports = CoreObject.extend({ + init: function() { + this._super.apply(this, arguments); + this.run = this.run || require('./run'); + }, + bowerJSONFileName: 'bower.json', + bowerJSONBackupFileName: 'bower.json.ember-try', + configKey: 'bower', + setup: function() { + return this._backupBowerFile(); + }, + _getDependencySetAccountingForDeprecatedTopLevelKeys: function(depSet) { + if (depSet[this.configKey]) { + return depSet[this.configKey]; + } + return {dependencies: depSet.dependencies, devDependencies: depSet.devDependencies, resolutions: depSet.resolutions}; + }, + changeToDependencySet: function(depSet) { + var adapter = this; + depSet = this._getDependencySetAccountingForDeprecatedTopLevelKeys(depSet); + if (!depSet) { return RSVP.resolve([]); } + var backupBowerFile = path.join(adapter.cwd, adapter.bowerJSONBackupFileName); + var bowerFile = path.join(adapter.cwd, adapter.bowerJSONFileName); + var bowerJSON = JSON.parse(fs.readFileSync(backupBowerFile)); + var newBowerJSON = adapter._bowerJSONForDependencySet(bowerJSON, depSet); + + fs.writeFileSync(bowerFile, JSON.stringify(newBowerJSON, null, 2)); + return adapter._install().then(function() { + var deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {}); + var currentDeps = Object.keys(deps).map(function(dep) { + return { + name: dep, + versionExpected: deps[dep], + versionSeen: adapter._findCurrentVersionOf(dep), + packageManager: 'bower' + }; + }); + return RSVP.Promise.resolve(currentDeps); + }); + }, + cleanup: function() { + var adapter = this; + return adapter._restoreOriginalBowerFile().then(function() { + return rimraf(path.join(adapter.cwd, adapter.bowerJSONBackupFileName)); + }).catch(function(e) { + console.log('Error cleaning up bower scenario:', e); + }) + .then(function() { + return adapter._install(); + }); + }, + _findCurrentVersionOf: function(packageName) { + var filename = path.join(this.cwd, 'bower_components', packageName, 'bower.json'); + if (fs.existsSync(filename)) { + return JSON.parse(fs.readFileSync(filename)).version; + } else { + throw 'File ' + filename + ' does not exist'; + } + }, + _install: function() { + var adapter = this; + + return rimraf(path.join(adapter.cwd, 'bower_components')) + .then(function() { + return adapter._findBowerPath(adapter.cwd); + }) + .then(function(bowerPath) { + return adapter.run('node', [bowerPath, 'install', '--config.interactive=false'], {cwd: adapter.cwd}); + }); + }, + _bowerJSONForDependencySet: function(bowerJSON, depSet) { + if (!bowerJSON.resolutions) { + bowerJSON.resolutions = {}; + } + + this._overrideBowerJSONDependencies(bowerJSON, depSet, 'dependencies'); + this._overrideBowerJSONDependencies(bowerJSON, depSet, 'devDependencies'); + + return bowerJSON; + }, + _overrideBowerJSONDependencies: function(bowerJSON, depSet, kindOfDependency) { + if (!depSet[kindOfDependency]) { return; } + var pkgs = Object.keys(depSet[kindOfDependency]); + + pkgs.forEach(function(pkg) { + if (!bowerJSON[kindOfDependency]) { + bowerJSON[kindOfDependency] = {}; + } + bowerJSON[kindOfDependency][pkg] = depSet[kindOfDependency][pkg]; + + if (depSet.resolutions && depSet.resolutions[pkg]) { + bowerJSON.resolutions[pkg] = depSet.resolutions[pkg]; + } else { + bowerJSON.resolutions[pkg] = depSet[kindOfDependency][pkg]; + } + }); + }, + _restoreOriginalBowerFile: function() { + var copy = RSVP.denodeify(fs.copy); + return copy(path.join(this.cwd, this.bowerJSONBackupFileName), + path.join(this.cwd, this.bowerJSONFileName)); + }, + _backupBowerFile: function() { + var copy = RSVP.denodeify(fs.copy); + return copy(path.join(this.cwd, this.bowerJSONFileName), + path.join(this.cwd, this.bowerJSONBackupFileName)); + }, + _findBowerPath: function() { + return findEmberPath(this.cwd) + .then(function(emberPath) { + /* Find bower's entry point module relative to + ember-cli's entry point script */ + return resolve('bower', { basedir: path.dirname(emberPath) }); + }) + .then(function(bowerPath) { + return path.join(bowerPath, '..', '..', 'bin', 'bower'); + }); + } +}); diff --git a/lib/utils/bower-helpers.js b/lib/utils/bower-helpers.js deleted file mode 100644 index 93ad9572..00000000 --- a/lib/utils/bower-helpers.js +++ /dev/null @@ -1,63 +0,0 @@ -var path = require('path'); -var fs = require('fs-extra'); -var RSVP = require('rsvp'); -var run = require('./run'); -var rimraf = RSVP.denodeify(require('rimraf')); -var resolve = RSVP.denodeify(require('resolve')); -var findEmberPath = require('./find-ember-path'); - -module.exports = { - findBowerPath: function(root) { - return findEmberPath(root) - .then(function(emberPath) { - /* Find bower's entry point module relative to - ember-cli's entry point script */ - return resolve('bower', { basedir: path.dirname(emberPath) }); - }) - .then(function(bowerPath) { - return path.join(bowerPath, '..', '..', 'bin', 'bower'); - }); - }, - - install: function(root) { - var helpers = this; - - return rimraf(path.join(root, 'bower_components')) - .then(function() { - return helpers.findBowerPath(root); - }) - .then(function(bowerPath) { - return run('node', [bowerPath, 'install', '--config.interactive=false'], {cwd: root}); - }); - }, - resetBowerFile: function(root) { - var copy = RSVP.denodeify(fs.copy); - return copy(path.join(root, 'bower.json.ember-try'), - path.join(root, 'bower.json')); - }, - backupBowerFile: function(root) { - var copy = RSVP.denodeify(fs.copy); - return copy(path.join(root, 'bower.json'), - path.join(root, 'bower.json.ember-try')); - }, - cleanup: function(root) { - var helpers = this; - return helpers.resetBowerFile(root).then(function() { - return rimraf(path.join(root, 'bower.json.ember-try')); - }) - .catch(function(e) { - console.log('Error cleaning up bower scenario:', e); - }) - .then(function() { - return helpers.install(root); - }); - }, - findVersion: function(packageName, root) { - var filename = path.join(root, 'bower_components', packageName, 'bower.json'); - if (fs.existsSync(filename)) { - return JSON.parse(fs.readFileSync(filename)).version; - } else { - throw 'File ' + filename + ' does not exist'; - } - } -}; diff --git a/lib/utils/config.js b/lib/utils/config.js index c9788f93..093466b8 100644 --- a/lib/utils/config.js +++ b/lib/utils/config.js @@ -17,26 +17,34 @@ function defaultConfig() { scenarios: [ { name: 'default', - dependencies: { } /* No dependencies needed as the - default is already specified in - the consuming app's bower.json */ + bower: { + dependencies: { } /* No dependencies needed as the + default is already specified in + the consuming app's bower.json */ + } }, { name: 'ember-release', - dependencies: { - ember: 'release' + bower: { + dependencies: { + ember: 'release' + } } }, { name: 'ember-beta', - dependencies: { - ember: 'beta' + bower: { + dependencies: { + ember: 'beta' + } } }, { name: 'ember-canary', - dependencies: { - ember: 'canary' + bower: { + dependencies: { + ember: 'canary' + } } } ] diff --git a/lib/utils/find-ember-path.js b/lib/utils/find-ember-path.js index 430fe951..d5e109ef 100644 --- a/lib/utils/find-ember-path.js +++ b/lib/utils/find-ember-path.js @@ -4,7 +4,7 @@ var resolve = RSVP.denodeify(require('resolve')); module.exports = function(root) { /* Find ember-cli's entry point module relative to - the current projects root */ + the current projects root */ return resolve('ember-cli', { basedir: root }) .then(function(emberPath) { // Return the path to the ember command script diff --git a/lib/utils/npm-adapter.js b/lib/utils/npm-adapter.js new file mode 100644 index 00000000..4aadd908 --- /dev/null +++ b/lib/utils/npm-adapter.js @@ -0,0 +1,104 @@ +var CoreObject = require('core-object'); +var fs = require('fs-extra'); +var RSVP = require('rsvp'); +var path = require('path'); +var extend = require('extend'); +var rimraf = RSVP.denodeify(require('rimraf')); + +module.exports = CoreObject.extend({ + init: function() { + this._super.apply(this, arguments); + this.run = this.run || require('./run'); + }, + configKey: 'npm', + packageJSON: 'package.json', + packageJSONBackupFileName: 'package.json.ember-try', + nodeModules: 'node_modules', + nodeModulesBackupLocation: '.node_modules.ember-try', + setup: function() { + return this._backupOriginalDependencies(); + }, + changeToDependencySet: function(depSet) { + var adapter = this; + depSet = depSet[adapter.configKey]; + if (!depSet) { return RSVP.resolve([]); } + var backupPackageJSON = path.join(adapter.cwd, adapter.packageJSONBackupFileName); + var packageJSONFile = path.join(adapter.cwd, adapter.packageJSON); + var packageJSON = JSON.parse(fs.readFileSync(backupPackageJSON)); + var newPackageJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + fs.writeFileSync(packageJSONFile, JSON.stringify(newPackageJSON, null, 2)); + return adapter._install().then(function() { + var deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {}); + var currentDeps = Object.keys(deps).map(function(dep) { + return { + name: dep, + versionExpected: deps[dep], + versionSeen: adapter._findCurrentVersionOf(dep), + packageManager: 'npm' + }; + }); + return RSVP.Promise.resolve(currentDeps); + }); + }, + cleanup: function() { + var adapter = this; + return adapter._restoreOriginalDependencies().then(function() { + return RSVP.all([rimraf(path.join(adapter.cwd, adapter.packageJSONBackupFileName)), + rimraf(path.join(adapter.cwd, adapter.nodeModulesBackupLocation))]); + }).catch(function(e) { + console.log('Error cleaning up npm scenario:', e); + }) + .then(function() { + return adapter._install(); + }); + }, + _findCurrentVersionOf: function(packageName) { + var filename = path.join(this.cwd, this.nodeModules, packageName, this.packageJSON); + if (fs.existsSync(filename)) { + return JSON.parse(fs.readFileSync(filename)).version; + } else { + throw 'File ' + filename + ' does not exist'; + } + }, + _install: function() { + var adapter = this; + return adapter.run('npm', ['install'], {cwd: adapter.cwd}).then(function() { + return adapter.run('npm', ['prune'], {cwd: adapter.cwd}); + }); + }, + _packageJSONForDependencySet: function(packageJSON, depSet) { + + this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies'); + this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies'); + + return packageJSON; + }, + _overridePackageJSONDependencies: function(packageJSON, depSet, kindOfDependency) { + if (!depSet[kindOfDependency]) { return; } + var pkgs = Object.keys(depSet[kindOfDependency]); + + pkgs.forEach(function(pkg) { + if (!packageJSON[kindOfDependency]) { + packageJSON[kindOfDependency] = {}; + } + packageJSON[kindOfDependency][pkg] = depSet[kindOfDependency][pkg]; + }); + }, + _restoreOriginalDependencies: function() { + var copy = RSVP.denodeify(fs.copy); + return RSVP.all([ + copy(path.join(this.cwd, this.packageJSONBackupFileName), + path.join(this.cwd, this.packageJSON)), + copy(path.join(this.cwd, this.nodeModulesBackupLocation), + path.join(this.cwd, this.nodeModules), {clobber: true})]); + }, + _backupOriginalDependencies: function() { + var copy = RSVP.denodeify(fs.copy); + return RSVP.all([ + copy(path.join(this.cwd, this.packageJSON), + path.join(this.cwd, this.packageJSONBackupFileName)), + copy(path.join(this.cwd, this.nodeModules), + path.join(this.cwd, this.nodeModulesBackupLocation), {clobber: true})]); + } +}); diff --git a/lib/utils/result-summary.js b/lib/utils/result-summary.js new file mode 100644 index 00000000..328b25f3 --- /dev/null +++ b/lib/utils/result-summary.js @@ -0,0 +1,69 @@ +var CoreObject = require('core-object'); +var chalk = require('chalk'); +var Table = require('cli-table'); + +module.exports = CoreObject.extend({ + print: function() { + var task = this; + var colorAndMessage; + var countPassed = 0; + var countFailed = 0; + task._printResultHeader(); + + this.results.forEach(function(scenario, index) { + if (scenario.result) { + colorAndMessage = chalk.green('Scenario ' + scenario.scenario + ': SUCCESS'); + countPassed++; + } else { + colorAndMessage = chalk.red('Scenario ' + scenario.scenario + ': FAIL'); + countFailed++; + } + task.ui.writeLine(colorAndMessage); + task._printDependencyTable(scenario.dependencyState); + }); + + task.ui.writeLine(''); + task._printResultsSummary(countFailed, countPassed, this.results.length); + }, + _printResultHeader: function() { + var task = this; + task.ui.writeLine(''); + task.ui.writeLine('------ RESULTS ------'); + task.ui.writeLine(''); + task.ui.writeLine('Running command `' + task.command + '`'); + task.ui.writeLine(''); + }, + _printDependencyTable: function(dependencyStatus) { + if (!dependencyStatus.length) { return; } + var task = this; + var colorForDepFn; + var tableRow; + var table = new Table({ + head: [chalk.gray('Dependency'), chalk.gray('Expected'), chalk.gray('Used'), chalk.gray('Type')], + colWidths: [20, 20, 30, 10] + }); + dependencyStatus.forEach(function(dep, index) { + if (dep.versionExpected == dep.versionSeen) { + colorForDepFn = chalk.green; + } else { + colorForDepFn = chalk.yellow; + } + tableRow = [dep.name, dep.versionExpected, dep.versionSeen, dep.packageManager].map(function(column) { + return colorForDepFn(column); + }); + table.push(tableRow); + }); + task.ui.writeLine(table); + task.ui.writeLine(''); + }, + _printResultsSummary: function(countFailed, countPassed, total) { + var task = this; + if (countFailed) { + task.ui.writeLine(chalk.red(countFailed + ' scenarios failed')); + task.ui.writeLine(chalk.green(countPassed + ' scenarios succeeded')); + task.ui.writeLine(chalk.gray(total + ' scenarios run')); + } else { + task.ui.writeLine(chalk.green('All ' + countPassed + ' scenarios succeeded')); + } + } +}); diff --git a/lib/utils/run-command.js b/lib/utils/run-command.js new file mode 100644 index 00000000..c23945aa --- /dev/null +++ b/lib/utils/run-command.js @@ -0,0 +1,20 @@ +var RSVP = require('rsvp'); +var findEmberPath = require('./find-ember-path'); +var run = require('./run'); + +module.exports = function(root, commandArgs) { + return findEmberPath(root) + .then(function(emberPath) { + return run('node', [emberPath, commandArgs], {cwd: root}); + }) + .then(function() { + return RSVP.resolve(true); + }) + .catch(function(errorCode) { + if (errorCode != 1) { + return RSVP.reject('The command ' + commandArgs.join(' ') + ' exited ' + errorCode); + } else { + return RSVP.resolve(false); + } + }); +}; diff --git a/lib/utils/run.js b/lib/utils/run.js index 91dfac92..d892f98b 100644 --- a/lib/utils/run.js +++ b/lib/utils/run.js @@ -8,7 +8,7 @@ function run(command, args, opts) { var p = spawn(command, args, opts); p.on('close', function(code) { if (code !== 0) { - reject(command + ' exited with nonzero status'); + reject(code); } else { resolve(); } diff --git a/lib/utils/scenario-manager.js b/lib/utils/scenario-manager.js index 2ad3a4f1..8111d4fa 100644 --- a/lib/utils/scenario-manager.js +++ b/lib/utils/scenario-manager.js @@ -1,74 +1,57 @@ var CoreObject = require('core-object'); -var fs = require('fs'); -var path = require('path'); -var run = require('./run'); -var BowerHelpers = require('../utils/bower-helpers'); -var Chalk = require('chalk'); +var RSVP = require('rsvp'); +var mapSeries = require('promise-map-series'); +var BowerAdapter = require('./bower-adapter'); +var NpmAdapter = require('./npm-adapter'); module.exports = CoreObject.extend({ - changeTo: function(scenario) { - var manager = this; - var bowerFile = path.join(manager.project.root, 'bower.json'); - return BowerHelpers.resetBowerFile(manager.project.root).then(function() { - var bowerJSON = JSON.parse(fs.readFileSync(bowerFile)); + init: function() { + this._super.apply(this, arguments); + this._initDependencyManagerAdapters(); + }, - if (!bowerJSON.resolutions) { - bowerJSON.resolutions = {}; + _initDependencyManagerAdapters: function() { + if (this.dependencyManagerAdapters && this.dependencyManagerAdapters.length > 0) { return; } + var hasNpm = false; + var hasBower = false; + this.config.scenarios.forEach(function(scenario) { + if (scenario.npm) { + hasNpm = true; + } + if (scenario.bower || scenario.dependencies || scenario.devDependencies) { + hasBower = true; } - - fs.writeFileSync(bowerFile, JSON.stringify(manager._bowerJSONForScenario(bowerJSON, scenario), null, 2)); - - return BowerHelpers.install(manager.project.root); - }).then(function() { - manager._checkVersions(scenario); }); + this.dependencyManagerAdapters = []; + if (hasNpm) { + this.dependencyManagerAdapters.push(new NpmAdapter({cwd: this.project.root})); + } + if (hasBower) { + this.dependencyManagerAdapters.push(new BowerAdapter({cwd: this.project.root})); + } + if (this.dependencyManagerAdapters.length === 0) { + throw new Error('No dependency manager adapter created'); + } }, - _checkVersions: function(scenario) { - var manager = this; - var actualVersion, expectedVersion; - var pkgs = Object.keys(scenario.dependencies); - - manager.ui.writeLine('For scenario ' + scenario.name + ', using:'); - - pkgs.map(function(dep) { - - actualVersion = BowerHelpers.findVersion(dep, manager.project.root); - expectedVersion = scenario.dependencies[dep]; - if (actualVersion !== expectedVersion) { - manager.ui.writeLine(Chalk.yellow('Versions do not match: Expected: ' + - expectedVersion + ' but saw ' + actualVersion + - ' This might be ok, depending on the scenario')); - } - manager.ui.writeLine(' ' + dep + ' ' + actualVersion); + setup: function() { + return mapSeries(this.dependencyManagerAdapters, function(depManager) { + return depManager.setup(); }); }, - _bowerJSONForScenario: function(bowerJSON, scenario) { - if (!scenario.dependencies && !scenario.devDependencies) { - throw new Error('No dependencies specified for scenario ' + scenario.name); - } - - var self = this; - ['dependencies', 'devDependencies'].forEach(function(kindOfDependency) { - self._overrideBowerJSONDependencies(bowerJSON, scenario, kindOfDependency); + changeTo: function(scenario) { + var manager = this; + return mapSeries(this.dependencyManagerAdapters, function(depManager) { + return depManager.changeToDependencySet(scenario); + }).then(function(results) { + return RSVP.resolve([].concat.apply([], results)); }); - - return bowerJSON; }, - _overrideBowerJSONDependencies: function(bowerJSON, scenario, kindOfDependency) { - if (!scenario[kindOfDependency]) { return; } - var pkgs = Object.keys(scenario[kindOfDependency]); - - pkgs.forEach(function(pkg) { - bowerJSON[kindOfDependency][pkg] = scenario[kindOfDependency][pkg]; - - if (scenario.resolutions && scenario.resolutions[pkg]) { - bowerJSON.resolutions[pkg] = scenario.resolutions[pkg]; - } else { - bowerJSON.resolutions[pkg] = scenario[kindOfDependency][pkg]; - } + cleanup: function() { + return mapSeries(this.dependencyManagerAdapters, function(depManager) { + return depManager.cleanup(); }); } }); diff --git a/package.json b/package.json index 4e089bcb..4377a087 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "test": "tests" }, "scripts": { - "start": "ember server", "build": "ember build", - "client-test": "ember test", + "start": "ember server", + "client-test": "ember try:testall", "node-test": "node_modules/.bin/mocha .", "lint": "node_modules/.bin/jshint lib test", "jscs": "node_modules/.bin/jscs lib test", @@ -22,35 +22,38 @@ "author": "Katie Gengler", "license": "MIT", "devDependencies": { - "broccoli-asset-rev": "^2.1.2", - "ember-cli": "1.13.8", - "ember-cli-app-version": "1.0.0", - "ember-cli-content-security-policy": "0.4.0", - "ember-cli-dependency-checker": "^1.0.1", - "ember-cli-htmlbars": "1.0.1", + "broccoli-asset-rev": "^2.2.0", + "ember-cli": "2.3.0-beta.1", + "ember-cli-app-version": "^1.0.0", + "ember-cli-dependency-checker": "^1.2.0", + "ember-cli-htmlbars": "^1.0.1", "ember-cli-htmlbars-inline-precompile": "^0.3.1", - "ember-cli-ic-ajax": "0.2.1", "ember-cli-inject-live-reload": "^1.3.1", - "ember-cli-qunit": "^1.0.0", - "ember-cli-release": "0.2.7", - "ember-cli-sri": "^1.0.3", + "ember-cli-qunit": "^1.2.1", + "ember-cli-release": "0.2.8", + "ember-cli-sri": "^2.0.0", "ember-cli-uglify": "^1.2.0", - "ember-disable-proxy-controllers": "^1.0.0", - "ember-export-application-global": "^1.0.3", "ember-disable-prototype-extensions": "^1.0.0", + "ember-disable-proxy-controllers": "^1.0.1", + "ember-export-application-global": "^1.0.4", + "ember-resolver": "^2.0.3", "jscs": "^2.0.0", "jshint": "^2.8.0", "mocha": "^2.2.5", - "should": "^7.0.1" + "mockery": "^1.4.0", + "should": "^7.0.1", + "tmp-sync": "^1.1.0" }, "keywords": [ "ember-addon", "testing" ], "dependencies": { - "ember-cli-babel": "^5.1.3", "chalk": "^1.0.0", + "cli-table": "^0.3.1", "core-object": "^1.1.0", + "ember-cli-babel": "^5.1.3", + "extend": "^3.0.0", "fs-extra": "^0.24.0", "promise-map-series": "^0.2.1", "resolve": "^1.1.6", diff --git a/test/bower-adapter-test.js b/test/bower-adapter-test.js new file mode 100644 index 00000000..4e305453 --- /dev/null +++ b/test/bower-adapter-test.js @@ -0,0 +1,209 @@ +var should = require('should'); +var RSVP = require('rsvp'); +var fs = require('fs-extra'); +var path = require('path'); +var tmp = require('tmp-sync'); +var fixtureBower = require('./fixtures/bower.json'); +var BowerAdapter = require('../lib/utils/bower-adapter'); +var writeJSONFile = require('./helpers/write-json-file'); + +var remove = RSVP.denodeify(fs.remove); +var stat = RSVP.denodeify(fs.stat); +var root = process.cwd(); +var tmproot = path.join(root, 'tmp'); +var tmpdir; + +describe('bowerAdapter', function() { + beforeEach(function() { + tmpdir = tmp.in(tmproot); + process.chdir(tmpdir); + }); + + afterEach(function() { + process.chdir(root); + return remove(tmproot); + }); + + describe('#setup', function() { + it('backs up the bower file', function() { + writeJSONFile('bower.json', {originalBowerJSON: true}); + return new BowerAdapter({cwd: tmpdir}).setup().then(function() { + assertFileContainsJSON('bower.json.ember-try', {originalBowerJSON: true}); + }); + }); + }); + + describe('#_getDependencySetAccountingForDeprecatedTopLevelKeys', function() { + + it('accounts for legacy format', function() { + var scenarioDepSet = { + dependencies: { + "ember": "components/ember#beta" + }, + devDependencies: { + "ember-data": "~2.2.0" + }, + resolutions: { + "ember": "beta" + } + }; + var results = new BowerAdapter({cwd: tmpdir})._getDependencySetAccountingForDeprecatedTopLevelKeys(scenarioDepSet); + results.should.deepEqual(scenarioDepSet); + }); + + it('uses dep set from bower key if present', function() { + var scenarioDepSet = { + bower: { + dependencies: { + "ember": "components/ember#release" + }, + devDependencies: { + "ember-data": "~2.1.0" + }, + resolutions: { + "ember": "release" + } + }, + dependencies: { + "ember": "components/ember#beta" + }, + devDependencies: { + "ember-data": "~2.2.0" + }, + resolutions: { + "ember": "beta" + } + }; + + var results = new BowerAdapter({cwd: tmpdir})._getDependencySetAccountingForDeprecatedTopLevelKeys(scenarioDepSet); + results.should.deepEqual(scenarioDepSet.bower); + }); + }); + + describe('#_install', function() { + it('removes bower_components', function() { + var stubbedRun = function() { + return new RSVP.Promise(function(resolve) { + resolve(); + }); + }; + + fs.mkdirSync('bower_components'); + writeJSONFile('bower.json', fixtureBower); + writeJSONFile('bower_components/this-should-be-obliterated.json', {removed: false}); + return new BowerAdapter({cwd: tmpdir, run: stubbedRun})._install().then(function() { + return stat('bower_components/this-should-be-obliterated.json').then(function(stats) { + true.should.equal(false, 'File should not exist'); + }, function(err) { + err.code.should.equal('ENOENT', 'File should not exist'); + }); + }); + }); + + it('runs bower install', function() { + writeJSONFile('bower.json', fixtureBower); + var stubbedRun = function(command, args, opts) { + command.should.equal('node'); + args[0].should.match(/bower/); + args[1].should.equal('install'); + args[2].should.equal('--config.interactive=false'); + opts.should.have.property('cwd', tmpdir); + return new RSVP.Promise(function(resolve, reject) { + resolve(); + }); + }; + return new BowerAdapter({cwd: tmpdir, run: stubbedRun})._install(); + }); + }); + + describe('#_restoreOriginalBowerFile', function() { + it('replaces the bower.json with the backed up version', function() { + writeJSONFile('bower.json.ember-try', {originalBowerJSON: true}); + writeJSONFile('bower.json', {originalBowerJSON: false}); + return new BowerAdapter({cwd: tmpdir})._restoreOriginalBowerFile().then(function() { + assertFileContainsJSON('bower.json', {originalBowerJSON: true}); + }); + }); + }); + + describe('#_bowerJSONForDependencySet', function() { + it('changes specified bower dependency versions', function() { + var bowerAdapter = new BowerAdapter({cwd: tmpdir}); + var bowerJSON = { dependencies: { jquery: '1.11.1' }, resolutions: {} }; + var depSet = { dependencies: { jquery: '2.1.3' } }; + + var resultJSON = bowerAdapter._bowerJSONForDependencySet(bowerJSON, depSet); + + resultJSON.dependencies.jquery.should.equal('2.1.3'); + }); + + it('changes specified bower dev dependency versions', function() { + var bowerAdapter = new BowerAdapter({cwd: tmpdir}); + var bowerJSON = { devDependencies: { jquery: '1.11.1' }, resolutions: {} }; + var depSet = { devDependencies: { jquery: '2.1.3' } }; + + var resultJSON = bowerAdapter._bowerJSONForDependencySet(bowerJSON, depSet); + + resultJSON.devDependencies.jquery.should.equal('2.1.3'); + }); + + it('adds to resolutions', function() { + var bowerAdapter = new BowerAdapter({cwd: tmpdir}); + var bowerJSON = { dependencies: { jquery: '1.11.1' }, resolutions: {} }; + var depSet = { dependencies: { jquery: '2.1.3' } }; + + var resultJSON = bowerAdapter._bowerJSONForDependencySet(bowerJSON, depSet); + + resultJSON.resolutions.jquery.should.equal('2.1.3'); + }); + + it('sets custom resolutions', function() { + var bowerAdapter = new BowerAdapter({cwd: tmpdir}); + var bowerJSON = { dependencies: { ember: '1.13.5' }, resolutions: {} }; + var depSet = { + dependencies: { ember: 'components/ember#canary' }, + resolutions: { ember: 'canary' } + }; + + var resultJSON = bowerAdapter._bowerJSONForDependencySet(bowerJSON, depSet); + + resultJSON.resolutions.ember.should.equal('canary'); + }); + + it('handles lack of resolutions in original bower.json', function() { + var bowerAdapter = new BowerAdapter({cwd: tmpdir}); + var bowerJSON = { dependencies: { jquery: '1.11.1' } }; + var depSet = { dependencies: { jquery: '2.1.3' } }; + + var resultJSON = bowerAdapter._bowerJSONForDependencySet(bowerJSON, depSet); + + resultJSON.resolutions.jquery.should.equal('2.1.3'); + }); + }); + + describe('#_findBowerPath()', function() { + it('should return the correct bower path', function() { + return new BowerAdapter({cwd: tmpdir})._findBowerPath().then(function(path) { + path.should.containEql('node_modules/bower/bin/bower'); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Error should not happen'); + }); + }); + }); +}); + +function assertFileContainsJSON(filename, expectedObj) { + return assertFileContains(filename, JSON.stringify(expectedObj, null, 2)); +} + +function assertFileContains(filename, expectedContents) { + var regex = new RegExp(escapeForRegex(expectedContents) + '($|\\W)', 'gm'); + var actualContents = fs.readFileSync(path.join(tmpdir, filename), { encoding: 'utf-8' }); + var result = regex.test(actualContents); + result.should.equal(true, 'File ' + filename + ' is expected to contain ' + expectedContents); +} + +function escapeForRegex(str) { + return str.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); +} diff --git a/test/bower-helpers-test.js b/test/bower-helpers-test.js deleted file mode 100644 index 1ca82086..00000000 --- a/test/bower-helpers-test.js +++ /dev/null @@ -1,71 +0,0 @@ -var bowerHelpers = require('../lib/utils/bower-helpers'); -var should = require('should'); - -var MockProject = require('./helpers/mock-project'); - -describe('bowerHelpers', function() { - describe('#findBowerPath()', function() { - it('should return the correct bower path', function(done) { - bowerHelpers.findBowerPath('.').then(function(path) { - path.indexOf('node_modules/ember-cli/node_modules/bower/bin/bower').should.be.above(0); - done(); - }, function(err) {console.log('err', err); }); - }); - }); - - describe('#resetBowerFile()', function() { - // Bower install could take a while - this.timeout(10000); - - var proj = new MockProject('.scratch'); - beforeEach(function() { - return proj.setup(); - }); - - afterEach(function() { - return proj.destroy(); - }); - - it('should replace a bower.json with the bower.json.ember-try', function(done) { - var proj = new MockProject('.scratch'); - - proj.createBowerBackup({ - jquery: '2.1.3' - }).then(function() { - bowerHelpers.resetBowerFile(process.cwd() + '/.scratch').then(function() { - proj.bowerData().then(function(data) { - data.dependencies.jquery.should.equal('2.1.3'); - done(); - }); - }); - }); - }); - }); - - describe('#backupBowerFile()', function() { - // Bower install could take a while - this.timeout(10000); - - var proj = new MockProject('.scratch'); - beforeEach(function() { - return proj.setup(); - }); - - afterEach(function() { - return proj.destroy(); - }); - - it('should copy bower.json to bower.json.ember-try', function(done) { - proj.createBowerBackup({ - jquery: '2.1.3' - }).then(function() { - bowerHelpers.backupBowerFile(process.cwd() + '/.scratch').then(function() { - proj.backupBowerData().then(function(bowerData) { - bowerData.dependencies.jquery.should.equal('^1.11.1'); - done(); - }); - }); - }); - }); - }); -}); diff --git a/test/fixtures/package.json b/test/fixtures/package.json new file mode 100644 index 00000000..8a214e44 --- /dev/null +++ b/test/fixtures/package.json @@ -0,0 +1,6 @@ +{ + "name": "a-test-project", + "devDependencies": { + "ember-data": "2.3.3" + } +} diff --git a/test/helpers/mock-project.js b/test/helpers/mock-project.js deleted file mode 100644 index 01242089..00000000 --- a/test/helpers/mock-project.js +++ /dev/null @@ -1,64 +0,0 @@ -var assert = require('assert'); -var fs = require('fs'); -var RSVP = require('rsvp'); -var _rimraf = require('rimraf'); -var spawnSync = require('child_process').spawnSync; - -var fixtureBower = require('../fixtures/bower.json'); - -var rimraf = RSVP.denodeify(_rimraf); -var mkdir = RSVP.denodeify(fs.mkdir); -var writeFile = RSVP.denodeify(fs.writeFile); -var readFile = RSVP.denodeify(fs.readFile); - - -function MockProject(root) { - assert.equal(!!root, true, 'Must provide a root'); - this.projectRoot = root; -} - -MockProject.prototype = { - setup: function() { - var mockProj = this; - return rimraf(mockProj.projectRoot).then(function() { - return mkdir(mockProj.projectRoot).then(function() { - return writeFile(mockProj.projectRoot + '/bower.json', JSON.stringify(fixtureBower)) - .then(function() { - return spawnSync('bower', ['install'], { - cwd: mockProj.projectRoot - }); - } - ); - }); - }); - }, - - destroy: function() { - return rimraf(this.projectRoot); - }, - - bowerData: function() { - return readFile(this.projectRoot + '/bower.json').then(function(fileData) { - return JSON.parse(fileData); - }); - }, - - backupBowerData: function() { - return readFile(this.projectRoot + '/bower.json.ember-try').then(function(fileData) { - return JSON.parse(fileData); - }); - }, - - createBowerBackup: function(packageVersions) { - var vers = packageVersions || {}; - var mockProj = this; - return this.bowerData().then(function(bowerJson) { - for (var k in vers) { - bowerJson.dependencies[k] = vers[k]; - } - return writeFile(mockProj.projectRoot + '/bower.json.ember-try', JSON.stringify(bowerJson)); - }); - } -}; - -module.exports = MockProject; diff --git a/test/helpers/stub-dependency-manager-adapter.js b/test/helpers/stub-dependency-manager-adapter.js new file mode 100644 index 00000000..93b06f92 --- /dev/null +++ b/test/helpers/stub-dependency-manager-adapter.js @@ -0,0 +1,18 @@ +var CoreObject = require('core-object'); +var RSVP = require('rsvp'); + +module.exports = CoreObject.extend({ + setup: function() { + return RSVP.resolve(); + }, + changeToDependencySet: function() { + return RSVP.resolve([{ + name: 'testDep', + versionExpected: '2.0.0', + versionSeen: '2.1.0' + }]); + }, + cleanup: function() { + return RSVP.resolve(); + } +}); diff --git a/test/helpers/write-json-file.js b/test/helpers/write-json-file.js new file mode 100644 index 00000000..a814693c --- /dev/null +++ b/test/helpers/write-json-file.js @@ -0,0 +1,5 @@ +var fs = require('fs-extra'); + +module.exports = function writeJSONFile(filename, contents) { + fs.writeFileSync(filename, JSON.stringify(contents, null, 2)); +}; diff --git a/test/mocha.opts b/test/mocha.opts index 59aee150..5f9655a5 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,4 @@ test/**/*.js --require should --ui bdd +--timeout 15000 diff --git a/test/npm-adapter-test.js b/test/npm-adapter-test.js new file mode 100644 index 00000000..e989fdba --- /dev/null +++ b/test/npm-adapter-test.js @@ -0,0 +1,118 @@ +var should = require('should'); +var RSVP = require('rsvp'); +var fs = require('fs-extra'); +var path = require('path'); +var tmp = require('tmp-sync'); +var fixturePackage = require('./fixtures/package.json'); +var NpmAdapter = require('../lib/utils/npm-adapter'); +var writeJSONFile = require('./helpers/write-json-file'); + +var remove = RSVP.denodeify(fs.remove); +var stat = RSVP.denodeify(fs.stat); +var root = process.cwd(); +var tmproot = path.join(root, 'tmp'); +var tmpdir; + +describe('npmAdapter', function() { + beforeEach(function() { + tmpdir = tmp.in(tmproot); + process.chdir(tmpdir); + }); + + afterEach(function() { + process.chdir(root); + return remove(tmproot); + }); + + describe('#setup', function() { + it('backs up the package.json file and node_modules', function() { + fs.mkdirSync('node_modules'); + writeJSONFile('node_modules/prove-it.json', {originalNodeModules: true}); + writeJSONFile('package.json', {originalPackageJSON: true}); + return new NpmAdapter({cwd: tmpdir}).setup().then(function() { + assertFileContainsJSON('package.json.ember-try', {originalPackageJSON: true}); + assertFileContainsJSON('.node_modules.ember-try/prove-it.json', {originalNodeModules: true}); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Error should not happen'); + }); + }); + }); + + describe('#_install', function() { + it('runs npm prune and npm install', function() { + writeJSONFile('package.json', fixturePackage); + var runCount = 1; + var stubbedRun = function(command, args, opts) { + command.should.equal('npm'); + if (runCount == 1) { + args[0].should.equal('install'); + } else { + args[0].should.equal('prune'); + } + opts.should.have.property('cwd', tmpdir); + runCount++; + return new RSVP.Promise(function(resolve, reject) { + resolve(); + }); + }; + return new NpmAdapter({cwd: tmpdir, run: stubbedRun})._install().catch(function(err) { + console.log(err); + true.should.equal(false, 'Error should not happen'); + }); + }); + }); + + describe('#_restoreOriginalDependencies', function() { + it('replaces the package.json with the backed up version', function() { + writeJSONFile('package.json.ember-try', {originalPackageJSON: true}); + writeJSONFile('package.json', {originalPackageJSON: false}); + fs.mkdirSync('.node_modules.ember-try'); + writeJSONFile('.node_modules.ember-try/prove-it.json', {originalNodeModules: true}); + return new NpmAdapter({cwd: tmpdir})._restoreOriginalDependencies().then(function() { + assertFileContainsJSON('package.json', {originalPackageJSON: true}); + assertFileContainsJSON('node_modules/prove-it.json', {originalNodeModules: true}); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Error should not happen'); + }); + }); + }); + + describe('#_packageJSONForDependencySet', function() { + it('changes specified dependency versions', function() { + var npmAdapter = new NpmAdapter({cwd: tmpdir}); + var packageJSON = { devDependencies: { 'ember-data': '2.2.1' }, dependencies: { 'ember-cli-babel': '5.0.0'} }; + var depSet = { dependencies: { 'ember-cli-babel': '6.0.0' } }; + + var resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + + resultJSON.dependencies['ember-cli-babel'].should.equal('6.0.0'); + }); + + it('changes specified bower dev dependency versions', function() { + var npmAdapter = new NpmAdapter({cwd: tmpdir}); + var packageJSON = { devDependencies: { 'ember-data': '2.2.1' }, dependencies: { 'ember-cli-babel': '5.0.0'} }; + var depSet = { devDependencies: { 'ember-data': '2.3.0' } }; + + var resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + + resultJSON.devDependencies['ember-data'].should.equal('2.3.0'); + }); + }); +}); + +function assertFileContainsJSON(filename, expectedObj) { + return assertFileContains(filename, JSON.stringify(expectedObj, null, 2)); +} + +function assertFileContains(filename, expectedContents) { + var regex = new RegExp(escapeForRegex(expectedContents) + '($|\\W)', 'gm'); + var actualContents = fs.readFileSync(path.join(tmpdir, filename), { encoding: 'utf-8' }); + var result = regex.test(actualContents); + result.should.equal(true, 'File ' + filename + ' is expected to contain ' + expectedContents); +} + +function escapeForRegex(str) { + return str.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); +} diff --git a/test/scenario-manager-test.js b/test/scenario-manager-test.js index 47201e46..4de82f6f 100644 --- a/test/scenario-manager-test.js +++ b/test/scenario-manager-test.js @@ -1,49 +1,111 @@ var ScenarioManager = require('../lib/utils/scenario-manager'); +var CoreObject = require('core-object'); +var RSVP = require('rsvp'); var should = require('should'); describe('scenarioManager', function() { - describe('#_bowerJSONForScenario', function() { - it('should change specified bower dependency versions', function() { - var scenarioManager = new ScenarioManager(); - var bowerJSON = { dependencies: { jquery: '1.11.1' }, resolutions: {} }; - var scenario = { dependencies: { jquery: '2.1.3' } }; + describe('new', function() { + describe('dependency manager adapter creation', function() { + it('creates npm adapter when config has npm key', function() { + var manager = new ScenarioManager({ config: { scenarios: [{ npm: {} }]}, project: {root: 'here'}}); + manager.dependencyManagerAdapters[0].configKey.should.equal('npm'); + manager.dependencyManagerAdapters.length.should.equal(1); + }); - bowerJSON = scenarioManager._bowerJSONForScenario(bowerJSON, scenario); + it('creates bower adapter when config has bower key', function() { + var manager = new ScenarioManager({ config: { scenarios: [{ bower: {} }]}, project: {root: 'here'}}); + manager.dependencyManagerAdapters[0].configKey.should.equal('bower'); + manager.dependencyManagerAdapters.length.should.equal(1); + }); - bowerJSON.dependencies.jquery.should.equal('2.1.3'); - }); - - it('should change specified bower dev dependency versions', function() { - var scenarioManager = new ScenarioManager(); - var bowerJSON = { devDependencies: { jquery: '1.11.1' }, resolutions: {} }; - var scenario = { devDependencies: { jquery: '2.1.3' } }; + it('creates both adapters when it has both keys', function() { + var manager = new ScenarioManager({ config: { scenarios: [{ bower: {}, npm: {}}]}, project: {root: 'here'}}); + manager.dependencyManagerAdapters[0].configKey.should.equal('npm'); + manager.dependencyManagerAdapters[1].configKey.should.equal('bower'); + manager.dependencyManagerAdapters.length.should.equal(2); + }); - bowerJSON = scenarioManager._bowerJSONForScenario(bowerJSON, scenario); + it('creates bower adapter when legacy dependenies key', function() { + var manager = new ScenarioManager({ config: { scenarios: [{ dependencies: {}}]}, project: {root: 'here'}}); + manager.dependencyManagerAdapters[0].configKey.should.equal('bower'); + manager.dependencyManagerAdapters.length.should.equal(1); + }); - bowerJSON.devDependencies.jquery.should.equal('2.1.3'); + it('creates bower adapter when legacy devDependencies key', function() { + var manager = new ScenarioManager({ config: { scenarios: [{ devDependencies: {}}]}, project: {root: 'here'}}); + manager.dependencyManagerAdapters[0].configKey.should.equal('bower'); + manager.dependencyManagerAdapters.length.should.equal(1); + }); }); + }); - it('should add to resolutions', function() { - var scenarioManager = new ScenarioManager(); - var bowerJSON = { dependencies: { jquery: '1.11.1' }, resolutions: {} }; - var scenario = { dependencies: { jquery: '2.1.3' } }; - - bowerJSON = scenarioManager._bowerJSONForScenario(bowerJSON, scenario); + describe('#setup', function() { + it('sets up each of the dependency managers', function() { + var calledFirstAdapter = false; + var calledSecondAdapter = false; + var fakeAdapters = [ + new CoreObject({ + setup: function() { + calledFirstAdapter = true; + } + }), + new CoreObject({ + setup: function() { + calledSecondAdapter = true; + } + }) + ]; - bowerJSON.resolutions.jquery.should.equal('2.1.3'); + return new ScenarioManager({dependencyManagerAdapters: fakeAdapters}).setup().then(function() { + calledFirstAdapter.should.equal(true); + calledSecondAdapter.should.equal(true); + }); }); + }); + + describe('#changeTo', function() { + it('changes dependency sets on each of the managers, and concats results', function() { + var fakeAdapters = [ + new CoreObject({ + changeToDependencySet: function() { + return RSVP.resolve(['a', 'b', 'r']); + } + }), + new CoreObject({ + changeToDependencySet: function() { + return RSVP.resolve(['u', 'q', 'a']); + } + }) + ]; - it('should set custom resolutions', function() { - var scenarioManager = new ScenarioManager(); - var bowerJSON = { dependencies: { ember: '1.13.5' }, resolutions: {} }; - var scenario = { - dependencies: { ember: 'components/ember#canary' }, - resolutions: { ember: 'canary' } - }; + var manager = new ScenarioManager({dependencyManagerAdapters: fakeAdapters}); + return manager.changeTo({}).then(function(results) { + results.should.containDeepOrdered(['a', 'b', 'r', 'u', 'q', 'a']); + }); + }); + }); - bowerJSON = scenarioManager._bowerJSONForScenario(bowerJSON, scenario); + describe('#cleanup', function() { + it('cleans up each dependency manager', function() { + var calledFirstAdapter = false; + var calledSecondAdapter = false; + var fakeAdapters = [ + new CoreObject({ + cleanup: function() { + calledFirstAdapter = true; + } + }), + new CoreObject({ + cleanup: function() { + calledSecondAdapter = true; + } + }) + ]; - bowerJSON.resolutions.ember.should.equal('canary'); + return new ScenarioManager({dependencyManagerAdapters: fakeAdapters}).cleanup().then(function() { + calledFirstAdapter.should.equal(true); + calledSecondAdapter.should.equal(true); + }); }); }); }); diff --git a/test/tasks/reset-test.js b/test/tasks/reset-test.js new file mode 100644 index 00000000..077a3837 --- /dev/null +++ b/test/tasks/reset-test.js @@ -0,0 +1,47 @@ +var tmp = require('tmp-sync'); +var path = require('path'); +var RSVP = require('rsvp'); +var fs = require('fs-extra'); +var fixtureBower = require('../fixtures/bower.json'); +var writeJSONFile = require('../helpers/write-json-file'); + +var remove = RSVP.denodeify(fs.remove); +var root = process.cwd(); +var tmproot = path.join(root, 'tmp'); +var tmpdir; + +describe('reset', function() { + beforeEach(function() { + tmpdir = tmp.in(tmproot); + process.chdir(tmpdir); + }); + + afterEach(function() { + process.chdir(root); + return remove(tmproot); + }); + + it('runs without blowing up', function() { + this.timeout(15000); + var config = { + scenarios: [{ + name: 'first', + dependencies: { + ember: '1.13.0' + } + }] + }; + + var ResetTask = require('../../lib/tasks/reset'); + var resetTask = new ResetTask({ + project: {root: tmpdir}, + config: config + }); + + writeJSONFile('bower.json', fixtureBower); + writeJSONFile('bower.json.ember-try', fixtureBower); + + return resetTask.run(); + }); + +}); diff --git a/test/tasks/try-each-test.js b/test/tasks/try-each-test.js new file mode 100644 index 00000000..b72715b0 --- /dev/null +++ b/test/tasks/try-each-test.js @@ -0,0 +1,380 @@ +var tmp = require('tmp-sync'); +var path = require('path'); +var RSVP = require('rsvp'); +var fs = require('fs-extra'); +var fixtureBower = require('../fixtures/bower.json'); +var fixturePackage = require('../fixtures/package.json'); +var writeJSONFile = require('../helpers/write-json-file'); +var mockery = require('mockery'); + +/* The first two of the tests in this file intentionally DO NOT stub dependency manager adapter*/ +var StubDependencyAdapter = require('../helpers/stub-dependency-manager-adapter'); + +var remove = RSVP.denodeify(fs.remove); +var root = process.cwd(); +var tmproot = path.join(root, 'tmp'); +var tmpdir; +var legacyConfig = { + scenarios: [{ + name: 'default', + dependencies: {} + }, + { + name: 'first', + dependencies: { + ember: '1.13.0' + } + }, + { + name: 'second', + dependencies: { + ember: '2.0.0' + } + }, + { + name: 'with-dev-deps', + dependencies: { + ember: '2.0.0' + }, + devDependencies: { + jquery: '1.11.3' + } + }, + { + name: 'with-resolutions', + dependencies: { + ember: 'components/ember#beta' + }, + resolutions: { + ember: 'beta' + } + }] +}; + +var config = { + scenarios: [ + { + name: 'first', + bower: { + dependencies: { + ember: '1.13.0' + } + }, + npm: { + dependencies: { + 'ember-cli-deploy': '0.5.0' + } + } + },{ + name: 'second', + bower: { + dependencies: { + ember: '2.0.0' + }, + devDependencies: { + jquery: '1.11.3' + } + }, + npm: { + devDependencies: { + 'ember-cli-deploy': '0.5.1' + } + } + }, + { + name: 'with-bower-resolutions', + bower: { + dependencies: { + ember: 'components/ember#beta' + }, + resolutions: { + ember: 'beta' + } + }, + npm: { + dependencies: { + 'ember-cli-deploy': '0.5.1' + } + } + }] +}; + +describe.only('tryEach', function() { + beforeEach(function() { + tmpdir = tmp.in(tmproot); + process.chdir(tmpdir); + mockery.enable({ + warnOnUnregistered: false, + useCleanCache: true + }); + require('chalk').enabled = false; + }); + + afterEach(function() { + mockery.deregisterAll(); + mockery.disable(); + process.chdir(root); + return remove(tmproot); + }); + + describe('with legacy config', function() { + it('succeeds when scenario\'s tests succeed', function() { + this.timeout(15000); + + var mockedRun = function(_, args) { + if (args[1].indexOf('test') > -1) { + return RSVP.resolve(0); + } else { + var regularRun = require('../../lib/utils/run'); + return regularRun.apply(this, arguments); + } + }; + + mockery.registerMock('./run', mockedRun); + + var output = []; + var outputFn = function(log) { + output.push(log); + }; + + var mockedExit = function(code) { + code.should.equal(0, 'exits 0 when all scenarios succeed'); + }; + + var TryEachTask = require('../../lib/tasks/try-each'); + var tryEachTask = new TryEachTask({ + ui: {writeLine: outputFn}, + project: {root: tmpdir}, + config: legacyConfig, + _exit: mockedExit + }); + + writeJSONFile('bower.json', fixtureBower); + return tryEachTask.run(legacyConfig.scenarios, {}).then(function() { + output.should.containEql('Scenario default: SUCCESS'); + output.should.containEql('Scenario first: SUCCESS'); + output.should.containEql('Scenario second: SUCCESS'); + output.should.containEql('Scenario with-dev-deps: SUCCESS'); + output.should.containEql('Scenario with-resolutions: SUCCESS'); + + output.should.containEql('All 5 scenarios succeeded'); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Assertions should run'); + }); + }); + + + it('fails scenarios when scenario\'s tests fail', function() { + this.timeout(15000); + + var runTestCount = 0; + var mockedRun = function(_, args) { + if (args[1].indexOf('test') > -1) { + runTestCount++; + if (runTestCount == 1) { + return RSVP.reject(1); + } else { + return RSVP.resolve(0); + } + } else { + var regularRun = require('../../lib/utils/run'); + return regularRun.apply(this, arguments); + } + }; + + mockery.registerMock('./run', mockedRun); + + var output = []; + var outputFn = function(log) { + output.push(log); + }; + + var mockedExit = function(code) { + code.should.equal(1); + }; + + var TryEachTask = require('../../lib/tasks/try-each'); + var tryEachTask = new TryEachTask({ + ui: {writeLine: outputFn}, + project: {root: tmpdir}, + config: legacyConfig, + _exit: mockedExit + }); + + writeJSONFile('bower.json', fixtureBower); + return tryEachTask.run(legacyConfig.scenarios, {}).then(function() { + output.should.containEql('Scenario default: FAIL'); + output.should.containEql('Scenario first: SUCCESS'); + output.should.containEql('Scenario second: SUCCESS'); + output.should.containEql('Scenario with-dev-deps: SUCCESS'); + output.should.containEql('Scenario with-resolutions: SUCCESS'); + output.should.containEql('1 scenarios failed'); + output.should.containEql('4 scenarios succeeded'); + output.should.containEql('5 scenarios run'); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Assertions should run'); + }); + }); + + }); + describe('with both npm and bower', function() { + it('succeeds when scenario\'s tests succeed', function() { + this.timeout(300000); + + var mockedRun = function(cmd, args, opts) { + if (args && args.length > 1 && args[1].indexOf('test') > -1) { + return RSVP.resolve(0); + } else { + var regularRun = require('../../lib/utils/run'); + return regularRun.apply(this, arguments); + } + }; + + mockery.registerMock('./run', mockedRun); + + var output = []; + var outputFn = function(log) { + output.push(log); + }; + + var mockedExit = function(code) { + code.should.equal(0, 'exits 0 when all scenarios succeed'); + }; + + var TryEachTask = require('../../lib/tasks/try-each'); + var tryEachTask = new TryEachTask({ + ui: {writeLine: outputFn}, + project: {root: tmpdir}, + config: config, + _exit: mockedExit + }); + + writeJSONFile('package.json', fixturePackage); + fs.mkdirSync('node_modules'); + writeJSONFile('bower.json', fixtureBower); + return tryEachTask.run(config.scenarios, {}).then(function() { + output.should.containEql('Scenario first: SUCCESS'); + output.should.containEql('Scenario second: SUCCESS'); + output.should.containEql('Scenario with-bower-resolutions: SUCCESS'); + output.should.containEql('All 3 scenarios succeeded'); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Assertions should run'); + }); + }); + + + it('fails scenarios when scenario\'s tests fail', function() { + this.timeout(300000); + + var runTestCount = 0; + var mockedRun = function(_, args) { + if (args && args.length > 1 && args[1].indexOf('test') > -1) { + runTestCount++; + if (runTestCount == 1) { + return RSVP.reject(1); + } else { + return RSVP.resolve(0); + } + } else { + var regularRun = require('../../lib/utils/run'); + return regularRun.apply(this, arguments); + } + }; + + mockery.registerMock('./run', mockedRun); + + var output = []; + var outputFn = function(log) { + output.push(log); + }; + + var mockedExit = function(code) { + code.should.equal(1); + }; + + var TryEachTask = require('../../lib/tasks/try-each'); + var tryEachTask = new TryEachTask({ + ui: {writeLine: outputFn}, + project: {root: tmpdir}, + config: config, + _exit: mockedExit + }); + + writeJSONFile('package.json', fixturePackage); + fs.mkdirSync('node_modules'); + writeJSONFile('bower.json', fixtureBower); + return tryEachTask.run(config.scenarios, {}).then(function() { + output.should.containEql('Scenario first: FAIL'); + output.should.containEql('Scenario second: SUCCESS'); + output.should.containEql('Scenario with-bower-resolutions: SUCCESS'); + output.should.containEql('1 scenarios failed'); + output.should.containEql('2 scenarios succeeded'); + output.should.containEql('3 scenarios run'); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Assertions should run'); + }); + }); + + }); + + describe('with stubbed dependency manager', function() { + it('allows passing in of the command to run', function() { + // With stubbed dependency manager, timing out is warning for accidentally not using the stub + this.timeout(100); + + var config = { + scenarios: [{ + name: 'first', + dependencies: { + ember: '1.13.0' + } + }] + }; + var ranPassedInCommand = false; + var mockedRun = function(_, args) { + if (args[1].indexOf('serve') > -1) { + ranPassedInCommand = true; + return RSVP.resolve(0); + } else { + var regularRun = require('../../lib/utils/run'); + return regularRun.apply(this, arguments); + } + }; + + mockery.registerMock('./run', mockedRun); + + var output = []; + var outputFn = function(log) { + output.push(log); + }; + + var mockedExit = function(code) { + code.should.equal(0, 'exits 0 when all scenarios succeed'); + }; + + var TryEachTask = require('../../lib/tasks/try-each'); + var tryEachTask = new TryEachTask({ + ui: {writeLine: outputFn}, + project: {root: tmpdir}, + config: config, + commandArgs: ['serve'], + dependencyManagerAdapters: [new StubDependencyAdapter()], + _exit: mockedExit + }); + + writeJSONFile('bower.json', fixtureBower); + return tryEachTask.run(config.scenarios, {}).then(function() { + output.should.containEql('Scenario first: SUCCESS'); + ranPassedInCommand.should.equal(true, 'Should run the passed in command'); + }).catch(function(err) { + console.log(err); + true.should.equal(false, 'Assertions should run'); + }); + }); + }); + +}); diff --git a/tests/dummy/app/app.js b/tests/dummy/app/app.js index 8d66b958..d8e5c713 100644 --- a/tests/dummy/app/app.js +++ b/tests/dummy/app/app.js @@ -1,16 +1,16 @@ import Ember from 'ember'; -import Resolver from 'ember/resolver'; +import Resolver from './resolver'; import loadInitializers from 'ember/load-initializers'; import config from './config/environment'; -var App; +let App; Ember.MODEL_FACTORY_INJECTIONS = true; App = Ember.Application.extend({ modulePrefix: config.modulePrefix, podModulePrefix: config.podModulePrefix, - Resolver: Resolver + Resolver }); loadInitializers(App, config.modulePrefix); diff --git a/tests/dummy/app/index.html b/tests/dummy/app/index.html index 1c49d36d..c9b43270 100644 --- a/tests/dummy/app/index.html +++ b/tests/dummy/app/index.html @@ -7,19 +7,19 @@ - {{content-for 'head'}} + {{content-for "head"}} - {{content-for 'head-footer'}} + {{content-for "head-footer"}} - {{content-for 'body'}} + {{content-for "body"}} - {{content-for 'body-footer'}} + {{content-for "body-footer"}} diff --git a/tests/dummy/app/resolver.js b/tests/dummy/app/resolver.js new file mode 100644 index 00000000..2fb563d6 --- /dev/null +++ b/tests/dummy/app/resolver.js @@ -0,0 +1,3 @@ +import Resolver from 'ember-resolver'; + +export default Resolver; diff --git a/tests/dummy/app/router.js b/tests/dummy/app/router.js index cef554b3..3bba78eb 100644 --- a/tests/dummy/app/router.js +++ b/tests/dummy/app/router.js @@ -1,7 +1,7 @@ import Ember from 'ember'; import config from './config/environment'; -var Router = Ember.Router.extend({ +const Router = Ember.Router.extend({ location: config.locationType }); diff --git a/tests/helpers/destroy-app.js b/tests/helpers/destroy-app.js new file mode 100644 index 00000000..c3d4d1ab --- /dev/null +++ b/tests/helpers/destroy-app.js @@ -0,0 +1,5 @@ +import Ember from 'ember'; + +export default function destroyApp(application) { + Ember.run(application, 'destroy'); +} diff --git a/tests/helpers/module-for-acceptance.js b/tests/helpers/module-for-acceptance.js new file mode 100644 index 00000000..ed23003d --- /dev/null +++ b/tests/helpers/module-for-acceptance.js @@ -0,0 +1,23 @@ +import { module } from 'qunit'; +import startApp from '../helpers/start-app'; +import destroyApp from '../helpers/destroy-app'; + +export default function(name, options = {}) { + module(name, { + beforeEach() { + this.application = startApp(); + + if (options.beforeEach) { + options.beforeEach.apply(this, arguments); + } + }, + + afterEach() { + destroyApp(this.application); + + if (options.afterEach) { + options.afterEach.apply(this, arguments); + } + } + }); +} diff --git a/tests/helpers/resolver.js b/tests/helpers/resolver.js index 28f4ece4..b208d38d 100644 --- a/tests/helpers/resolver.js +++ b/tests/helpers/resolver.js @@ -1,7 +1,7 @@ -import Resolver from 'ember/resolver'; +import Resolver from '../../resolver'; import config from '../../config/environment'; -var resolver = Resolver.create(); +const resolver = Resolver.create(); resolver.namespace = { modulePrefix: config.modulePrefix, diff --git a/tests/helpers/start-app.js b/tests/helpers/start-app.js index 0f7aab1a..e098f1d5 100644 --- a/tests/helpers/start-app.js +++ b/tests/helpers/start-app.js @@ -3,12 +3,12 @@ import Application from '../../app'; import config from '../../config/environment'; export default function startApp(attrs) { - var application; + let application; - var attributes = Ember.merge({}, config.APP); + let attributes = Ember.merge({}, config.APP); attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; - Ember.run(function() { + Ember.run(() => { application = Application.create(attributes); application.setupForTesting(); application.injectTestHelpers(); diff --git a/tests/index.html b/tests/index.html index 8fea6fe7..64cb47e3 100644 --- a/tests/index.html +++ b/tests/index.html @@ -7,27 +7,28 @@ - {{content-for 'head'}} - {{content-for 'test-head'}} + {{content-for "head"}} + {{content-for "test-head"}} - {{content-for 'head-footer'}} - {{content-for 'test-head-footer'}} + {{content-for "head-footer"}} + {{content-for "test-head-footer"}} + {{content-for "body"}} + {{content-for "test-body"}} - {{content-for 'body'}} - {{content-for 'test-body'}} + - + - {{content-for 'body-footer'}} - {{content-for 'test-body-footer'}} + {{content-for "body-footer"}} + {{content-for "test-body-footer"}}