Repository: ember-cli/ember-try (js)

Issue:
Support running bower scenarios even if the app does not initially have a bower.json
aka make work with `ember-cli` post https://github.com/ember-cli/ember-cli/pull/6698

cc @Turbo87 

Pull request description:
Support having bower scenarios without having a bower.json initially
Fixes #110 

Patch:
diff --git a/lib/dependency-manager-adapters/bower.js b/lib/dependency-manager-adapters/bower.js
index f642eda..3864eaf 100644
--- a/lib/dependency-manager-adapters/bower.js
+++ b/lib/dependency-manager-adapters/bower.js
@@ -14,12 +14,21 @@ module.exports = CoreObject.extend({
   init: function() {
     this._super.apply(this, arguments);
     this.run = this.run || require('../utils/run');
+    this.hasBowerFileInitially = fs.existsSync(path.join(this.cwd, this.bowerJSONFileName));
   },
   bowerJSONFileName: 'bower.json',
   bowerJSONBackupFileName: 'bower.json.ember-try',
+  defaultBowerJSON: {
+    name: 'ember-try-placeholder',
+    dependencies: {}
+  },
   configKey: 'bower',
   setup: function() {
-    return this._backupBowerFile();
+    if (this.hasBowerFileInitially) {
+      return this._backupBowerFile();
+    } else {
+      return RSVP.resolve();
+    }
   },
   _getDependencySetAccountingForDeprecatedTopLevelKeys: function(depSet) {
     if (depSet[this.configKey]) {
@@ -27,6 +36,24 @@ module.exports = CoreObject.extend({
     }
     return {dependencies: depSet.dependencies, devDependencies: depSet.devDependencies, resolutions: depSet.resolutions};
   },
+  _writeBowerFileWithDepSetChanges: function(depSet) {
+    var adapter = this;
+    var baseBowerJSON;
+    var bowerFile = path.join(adapter.cwd, adapter.bowerJSONFileName);
+
+    if (this.hasBowerFileInitially) {
+      var backupBowerFile = path.join(adapter.cwd, adapter.bowerJSONBackupFileName);
+      baseBowerJSON = JSON.parse(fs.readFileSync(backupBowerFile));
+    } else {
+      baseBowerJSON = this.defaultBowerJSON;
+    }
+
+    var newBowerJSON = adapter._bowerJSONForDependencySet(baseBowerJSON, depSet);
+
+    debug('Write bower.json with: \n', JSON.stringify(newBowerJSON));
+
+    fs.writeFileSync(bowerFile, JSON.stringify(newBowerJSON, null, 2));
+  },
   changeToDependencySet: function(depSet) {
     var adapter = this;
     depSet = this._getDependencySetAccountingForDeprecatedTopLevelKeys(depSet);
@@ -34,14 +61,9 @@ module.exports = CoreObject.extend({
     debug('Changing to dependency set: %s', JSON.stringify(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);
 
-    debug('Write bower.json with: \n', JSON.stringify(newBowerJSON));
+    adapter._writeBowerFileWithDepSetChanges(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) {
@@ -60,15 +82,21 @@ module.exports = CoreObject.extend({
   },
   cleanup: function() {
     var adapter = this;
-    return adapter._restoreOriginalBowerFile().then(function() {
-      debug('Remove backup bower.json');
-      return rimraf(path.join(adapter.cwd, adapter.bowerJSONBackupFileName));
-    }).catch(function(e) {
-      console.log('Error cleaning up bower scenario:', e);
-    })
-    .then(function() {
-      return adapter._install();
-    });
+    if (this.hasBowerFileInitially) {
+      return adapter._restoreOriginalBowerFile().then(function() {
+        debug('Remove backup bower.json');
+        return rimraf(path.join(adapter.cwd, adapter.bowerJSONBackupFileName));
+      }).catch(function(e) {
+        console.log('Error cleaning up bower scenario:', e);
+      })
+      .then(function() {
+        return adapter._install();
+      });
+    } else {
+      return rimraf(path.join(adapter.cwd, adapter.bowerJSONFileName)).then(function() {
+        return rimraf(path.join(adapter.cwd, 'bower_components'));
+      });
+    }
   },
   _findCurrentVersionOf: function(packageName) {
     var filename = path.join(this.cwd, 'bower_components', packageName, 'bower.json');


Test patch:
diff --git a/test/dependency-manager-adapters/bower-adapter-test.js b/test/dependency-manager-adapters/bower-adapter-test.js
index f1646b6..f184777 100644
--- a/test/dependency-manager-adapters/bower-adapter-test.js
+++ b/test/dependency-manager-adapters/bower-adapter-test.js
@@ -33,6 +33,12 @@ describe('bowerAdapter', function() {
         assertFileContainsJSON('bower.json.ember-try', {originalBowerJSON: true});
       });
     });
+
+    it('does not error if no bower.json', function() {
+      return new BowerAdapter({cwd: tmpdir}).setup().catch(function() {
+        expect(true).to.eql(false);
+      });
+    });
   });
 
   describe('#_getDependencySetAccountingForDeprecatedTopLevelKeys', function() {
@@ -140,6 +146,40 @@ describe('bowerAdapter', function() {
     });
   });
 
+  describe('#_writeBowerFileWithDepSetChanges', function() {
+    it('writes bower.json with dep set changes', function() {
+      var bowerJSON = { dependencies: { jquery: '1.11.1' }, resolutions: {} };
+      var depSet =  { dependencies: { jquery: '2.1.3' } };
+      writeJSONFile('bower.json', bowerJSON);
+      writeJSONFile('bower.json.ember-try', bowerJSON);
+
+      new BowerAdapter({cwd: tmpdir})._writeBowerFileWithDepSetChanges(depSet);
+      assertFileContainsJSON('bower.json', {
+        dependencies: {
+          jquery: '2.1.3'
+        },
+        resolutions: {
+          jquery: '2.1.3'
+        }
+      });
+    });
+
+    it('writes bower.json with dep set changes even if no original bower.json', function() {
+      var depSet =  { dependencies: { jquery: '2.1.3' } };
+
+      new BowerAdapter({cwd: tmpdir})._writeBowerFileWithDepSetChanges(depSet);
+      assertFileContainsJSON('bower.json', {
+        name: 'ember-try-placeholder',
+        dependencies: {
+          jquery: '2.1.3'
+        },
+        resolutions: {
+          jquery: '2.1.3'
+        }
+      });
+    });
+  });
+
   describe('#_bowerJSONForDependencySet', function() {
     it('changes specified bower dependency versions', function() {
       var bowerAdapter = new BowerAdapter({cwd: tmpdir});
@@ -278,7 +318,7 @@ 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);
-  expect(result).to.equal(true, 'File ' + filename + ' is expected to contain ' + expectedContents);
+  expect(result).to.equal(true, 'File ' + filename + ' is expected to contain ' + expectedContents + ' but contained ' + actualContents);
 }
 
 function escapeForRegex(str) {
diff --git a/test/tasks/try-each-test.js b/test/tasks/try-each-test.js
index 64a399c..adc807c 100644
--- a/test/tasks/try-each-test.js
+++ b/test/tasks/try-each-test.js
@@ -163,7 +163,6 @@ describe('tryEach', function() {
       });
     });
 
-
     it('fails scenarios when scenario\'s tests fail', function() {
       this.timeout(30000);
 
@@ -208,8 +207,39 @@ describe('tryEach', function() {
         expect(true).to.equal(false, 'Assertions should run');
       });
     });
+  });
+
+  describe('with bower scenarios', function() {
+    it('works without an initial bower.json', function() {
+      this.timeout(30000);
+
+      var mockedRun = generateMockRun('ember test', function() {
+        return RSVP.resolve(0);
+      });
+      mockery.registerMock('./run', mockedRun);
+
+      var output = [];
+      var outputFn = function(log) {
+        output.push(log);
+      };
 
+      var TryEachTask = require('../../lib/tasks/try-each');
+      var tryEachTask = new TryEachTask({
+        ui: {writeLine: outputFn},
+        project: {root: tmpdir},
+        config: legacyConfig,
+        _on: function() {}
+      });
+
+      expect(fs.existsSync('bower.json')).to.eql(false);
+      return tryEachTask.run(legacyConfig.scenarios, {}).then(function() {
+        expect(output).to.include('All 5 scenarios succeeded');
+        expect(fs.existsSync('bower.json')).to.eql(false);
+        expect(fs.existsSync('bower_components')).to.eql(false);
+      });
+    });
   });
+
   describe('with both npm and bower', function() {
     it('succeeds when scenario\'s tests succeed', function() {
       this.timeout(300000);