actions.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*global describe, before, it, afterEach, beforeEach */
  2. var fs = require('fs');
  3. var path = require('path');
  4. var util = require('util');
  5. var events = require('events');
  6. var assert = require('assert');
  7. var proxyquire = require('proxyquire');
  8. var generators = require('../');
  9. var EventEmitter = require('events').EventEmitter;
  10. var win32 = process.platform === 'win32';
  11. describe('yeoman.generators.Base', function () {
  12. before(generators.test.before(path.join(__dirname, 'temp')));
  13. before(function () {
  14. function Dummy() {
  15. generators.Base.apply(this, arguments);
  16. }
  17. util.inherits(Dummy, generators.Base);
  18. Dummy.prototype.test = function () {
  19. this.shouldRun = true;
  20. };
  21. var env = this.env = generators();
  22. env.register(Dummy, 'dummy');
  23. this.dummy = env.create('dummy');
  24. this.fixtures = path.join(__dirname, 'fixtures');
  25. });
  26. it('generator.prompt(defaults, prompts, cb)', function (done) {
  27. this.dummy.prompt([], function () {
  28. done();
  29. });
  30. });
  31. describe('generator.sourceRoot(root)', function () {
  32. it('should update the "_sourceRoot" property when root is given', function () {
  33. this.dummy.sourceRoot(this.fixtures);
  34. assert.equal(this.dummy._sourceRoot, this.fixtures);
  35. });
  36. it('should return the updated or current value of "_sourceRoot"', function () {
  37. assert.equal(this.dummy.sourceRoot(), this.fixtures);
  38. });
  39. });
  40. describe('generator.destinationRoot(root)', function () {
  41. it('should update the "_destinationRoot" property when root is given', function () {
  42. this.dummy.destinationRoot('.');
  43. assert.equal(this.dummy._destinationRoot, process.cwd());
  44. });
  45. it('should return the updated or current value of "_destinationRoot"', function () {
  46. assert.equal(this.dummy.destinationRoot(), process.cwd());
  47. });
  48. });
  49. describe('generator.cacheRoot()', function () {
  50. it('should show the cache root where yeoman stores all temp files, on a platform that follows XDG', function () {
  51. process.env.XDG_CACHE_HOME = '.';
  52. assert.equal(this.dummy.cacheRoot(), path.join(process.env.XDG_CACHE_HOME, 'yeoman'));
  53. });
  54. it('should show the cache root where yeoman stores all temp files, on a plateform that doesn\'t follow XDG', function () {
  55. if (process.env.XDG_CACHE_HOME) {
  56. delete process.env.XDG_CACHE_HOME;
  57. }
  58. assert.equal(this.dummy.cacheRoot(), path.join(process.env[win32 ? 'USERPROFILE' : 'HOME'], '.cache/yeoman'));
  59. });
  60. });
  61. describe('generator.copy(source, destination, process)', function () {
  62. before(function (done) {
  63. this.dummy.copy(path.join(__dirname, 'fixtures/foo.js'), 'write/to/bar.js');
  64. this.dummy.copy('foo.js', 'write/to/foo.js');
  65. this.dummy.copy('foo-copy.js');
  66. this.dummy.copy('yeoman-logo.png');
  67. this.dummy.copy(path.join(__dirname, 'fixtures/lodash-copy.js'), 'write/to/lodash.js');
  68. this.dummy.copy('foo-process.js', 'write/to/foo-process.js', function (contents, source, destination, props) {
  69. contents = contents.replace('foo', 'bar');
  70. contents = contents.replace('\r\n', '\n');
  71. return contents;
  72. });
  73. this.dummy.conflicter.resolve(done);
  74. });
  75. it('should copy source files relative to the "sourceRoot" value', function (done) {
  76. fs.stat('write/to/foo.js', done);
  77. });
  78. it('should allow absolute path, and prevent the relative paths join', function (done) {
  79. fs.stat('write/to/bar.js', done);
  80. });
  81. it('should allow to copy without using the templating (conficting with lodash/underscore)', function (done) {
  82. fs.stat('write/to/lodash.js', done);
  83. });
  84. it('should default the destination to the source filepath value', function (done) {
  85. fs.stat('foo-copy.js', done);
  86. });
  87. it('should retain executable mode on copied files', function (done) {
  88. fs.stat('write/to/bar.js', function (err, stats) {
  89. assert(stats.mode & 1 === 1, 'File should be executable.');
  90. done();
  91. });
  92. });
  93. it('should process source contents via function', function (done) {
  94. fs.readFile('write/to/foo-process.js', function (err, data) {
  95. if (err) throw err;
  96. assert.equal(data, 'var bar = \'foo\';\n');
  97. done();
  98. });
  99. });
  100. it('should not give a conflict on same binary files', function (done) {
  101. this.dummy.conflicter.force = true;
  102. this.dummy.conflicter.collision('yeoman-logo.png', fs.readFileSync(path.join(this.fixtures, 'yeoman-logo.png')), function (status) {
  103. assert.equal(status, 'identical');
  104. this.dummy.conflicter.force = false;
  105. done();
  106. }.bind(this));
  107. });
  108. });
  109. describe('generator.read(filepath, encoding)', function () {
  110. it('should read files relative to the "sourceRoot" value', function () {
  111. var body = this.dummy.read('foo.js');
  112. assert.equal(body, 'var foo = \'foo\';' + '\n');
  113. });
  114. it('should allow absolute path, and prevent the relative paths join', function () {
  115. var body = this.dummy.read(path.join(__dirname, 'fixtures/foo.js'));
  116. assert.equal(body, 'var foo = \'foo\';' + '\n');
  117. });
  118. });
  119. describe('generator.write(filepath, content)', function () {
  120. before(function (done) {
  121. this.body = 'var bar = \'bar\';' + '\n';
  122. this.dummy.write('write/to/foobar.js', this.body);
  123. this.dummy.conflicter.resolve(done);
  124. });
  125. it('should write the specified files relative to the "destinationRoot" value', function (done) {
  126. var body = this.body;
  127. fs.readFile('write/to/foobar.js', 'utf8', function (err, actual) {
  128. if (err) {
  129. return done(err);
  130. }
  131. assert.ok(actual, body);
  132. done();
  133. });
  134. });
  135. });
  136. describe('generator.template(source, destination, data)', function () {
  137. before(function (done) {
  138. this.dummy.foo = 'fooooooo';
  139. this.dummy.template('foo-template.js', 'write/to/from-template.js');
  140. this.dummy.template('foo-template.js');
  141. this.dummy.template('foo-template.js', 'write/to/from-template-bar.js', {
  142. foo: 'bar'
  143. });
  144. this.dummy.template('foo-template.js', 'write/to/from-template.js');
  145. this.dummy.conflicter.resolve(done);
  146. });
  147. it('should copy and process source file to destination', function (done) {
  148. fs.stat('write/to/from-template.js', done);
  149. });
  150. it('should defaults the destination to the source filepath value, relative to "destinationRoot" value', function () {
  151. var body = fs.readFileSync('foo-template.js', 'utf8');
  152. assert.equal(body, 'var fooooooo = \'fooooooo\';' + '\n');
  153. });
  154. it('should process underscore templates with the passed-in data', function () {
  155. var body = fs.readFileSync('write/to/from-template-bar.js', 'utf8');
  156. assert.equal(body, 'var bar = \'bar\';' + '\n');
  157. });
  158. it('should process underscore templates with the actual generator instance, when no data is given', function () {
  159. var body = fs.readFileSync('write/to/from-template.js', 'utf8');
  160. assert.equal(body, 'var fooooooo = \'fooooooo\';' + '\n');
  161. });
  162. });
  163. describe('generator.directory(source, destination, process)', function () {
  164. before(function (done) {
  165. // avoid hitting conflict state in this configuration for now
  166. if (fs.existsSync('foo.js')) {
  167. fs.unlinkSync('foo.js');
  168. }
  169. // avoid hitting conflict state in this configuration for now
  170. if (fs.existsSync('foo-template.js')) {
  171. fs.unlinkSync('foo-template.js');
  172. }
  173. // avoid hitting conflict state in this configuration for now
  174. if (fs.existsSync('yeoman-logo.png')) {
  175. fs.unlinkSync('yeoman-logo.png');
  176. }
  177. this.dummy.directory('./', 'directory');
  178. this.dummy.directory('./');
  179. this.dummy.directory('./', 'directory-processed', function (contents, source, destination, props) {
  180. if (source.indexOf('foo-process.js') !== -1) {
  181. contents = contents.replace('foo', 'bar');
  182. contents = contents.replace('\r\n', '\n');
  183. }
  184. return contents;
  185. });
  186. this.dummy.conflicter.resolve(done);
  187. });
  188. it('should copy and process source files to destination', function (done) {
  189. fs.stat('directory/foo-template.js', function (err) {
  190. if (err) {
  191. return done(err);
  192. }
  193. fs.stat('directory/foo.js', done);
  194. });
  195. });
  196. it('should defaults the destination to the source filepath value, relative to "destinationRoot" value', function (done) {
  197. fs.stat('foo-template.js', function (err) {
  198. if (err) {
  199. return done(err);
  200. }
  201. fs.stat('foo.js', done);
  202. });
  203. });
  204. it('should process underscore templates with the actual generator instance', function () {
  205. var body = fs.readFileSync('directory/foo-template.js', 'utf8');
  206. var foo = this.dummy.foo;
  207. assert.equal(body, 'var ' + foo + ' = \'' + foo + '\';\n');
  208. });
  209. it('should process source contents via function', function () {
  210. var body = fs.readFileSync('directory-processed/foo-process.js', 'utf8');
  211. assert.equal(body, 'var bar = \'foo\';\n');
  212. });
  213. });
  214. describe('actions/install', function () {
  215. var asyncStub = {
  216. on: function (key, cb) {
  217. if (key === 'exit') {
  218. cb();
  219. }
  220. return asyncStub;
  221. }
  222. };
  223. describe('generator.bowerInstall', function () {
  224. beforeEach(function () {
  225. // Keep track of all commands executed by spawnCommand.
  226. this.commandsRun = [];
  227. this.dummy.spawnCommand = function spawnCommand(cmd, args) {
  228. this.commandsRun.push([cmd, args]);
  229. return asyncStub;
  230. }.bind(this);
  231. });
  232. afterEach(function () {
  233. // Restore the original function.
  234. var spawn = require('../lib/actions/spawn_command');
  235. this.dummy._.extend(this.dummy, spawn);
  236. });
  237. it('should spawn a bower process', function (done) {
  238. this.dummy.bowerInstall(null, done);
  239. assert(this.commandsRun.length, 1);
  240. assert.deepEqual(this.commandsRun[0], ['bower', ['install']]);
  241. });
  242. it('should spawn a bower process with formatted options', function (done) {
  243. this.dummy.bowerInstall('jquery', { saveDev: true }, done);
  244. assert(this.commandsRun.length, 1);
  245. assert.deepEqual(this.commandsRun[0], ['bower', ['install', 'jquery', '--save-dev']]);
  246. });
  247. });
  248. describe('generator.installDependencies', function () {
  249. beforeEach(function () {
  250. // Keep track of all commands executed by spawnCommand.
  251. this.commandsRun = [];
  252. this.dummy.spawnCommand = function spawnCommand(cmd, args) {
  253. this.commandsRun.push(cmd);
  254. return asyncStub;
  255. }.bind(this);
  256. });
  257. afterEach(function () {
  258. // Restore the original function.
  259. var spawn = require('../lib/actions/spawn_command');
  260. this.dummy._.extend(this.dummy, spawn);
  261. });
  262. it('should spawn npm and bower', function () {
  263. this.dummy.installDependencies();
  264. assert.deepEqual(this.commandsRun, ['bower', 'npm']);
  265. });
  266. it('should not spawn anything with skipInstall', function () {
  267. this.dummy.installDependencies({ skipInstall: true });
  268. assert.deepEqual(this.commandsRun.length, 0);
  269. });
  270. it('npmInstall should run without callback', function () {
  271. this.dummy.npmInstall('yo', { save: true });
  272. assert.deepEqual(this.commandsRun.length, 1);
  273. });
  274. it('should execute a callback after installs', function () {
  275. var called = false;
  276. this.dummy.installDependencies({
  277. callback: function () {
  278. called = true;
  279. }
  280. });
  281. assert.deepEqual(this.commandsRun, ['bower', 'npm']);
  282. assert(called);
  283. });
  284. it('should accept and execute a function as its only argument', function () {
  285. var called = false;
  286. this.dummy.installDependencies(function () {
  287. called = true;
  288. });
  289. assert.deepEqual(this.commandsRun, ['bower', 'npm']);
  290. assert(called);
  291. });
  292. });
  293. });
  294. describe('generator.expandFiles', function () {
  295. before(function (done) {
  296. this.dummy.copy('foo.js', 'write/abc/abc.js');
  297. this.dummy.conflicter.resolve(done);
  298. });
  299. it('should return expand files', function () {
  300. var files = this.dummy.expandFiles('write/abc/**');
  301. assert.deepEqual(files, ['write/abc/abc.js']);
  302. });
  303. it('should return expand files', function () {
  304. var files = this.dummy.expandFiles('abc/**', {cwd: './write'});
  305. assert.deepEqual(files, ['abc/abc.js']);
  306. });
  307. });
  308. });