diff --git a/.travis.yml b/.travis.yml index b1c5d239..938713c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ matrix: - node_js: iojs before_install: + - export CHROME_BIN=chromium-browser + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + - gem install sass - npm install -g npm@latest - npm install -g bower - npm install -g gulp diff --git a/app/files.json b/app/files.json index 711f7b4a..fda517ed 100644 --- a/app/files.json +++ b/app/files.json @@ -2,7 +2,7 @@ "staticFiles": [ ".bowerrc", ".editorconfig", - ".jshintrc", + "karma.conf.js", "protractor.conf.js", @@ -20,6 +20,7 @@ ], "templates": [ ".gitignore", + ".jshintrc", "package.json", "bower.json", diff --git a/app/src/preprocessors.js b/app/src/preprocessors.js index b2bfdf5a..95e229ce 100644 --- a/app/src/preprocessors.js +++ b/app/src/preprocessors.js @@ -73,14 +73,6 @@ module.exports = function(GulpAngularGenerator) { * Copy additional lint files if needed */ GulpAngularGenerator.prototype.lintCopies = function lintCopies() { - if(this.props.jsPreprocessor.srcExtension === 'es6') { - this.files.push({ - src: 'src/.jshintrc', - dest: 'src/.jshintrc', - template: false - }); - } - if(this.props.jsPreprocessor.key === 'coffee') { this.files.push({ src: 'coffeelint.json', diff --git a/app/templates/.jshintrc b/app/templates/_.jshintrc similarity index 88% rename from app/templates/.jshintrc rename to app/templates/_.jshintrc index 36d78b0f..09825ee3 100644 --- a/app/templates/.jshintrc +++ b/app/templates/_.jshintrc @@ -1,4 +1,7 @@ { +<% if (props.jsPreprocessor.srcExtension === 'es6') { %> + "esnext": true, +<% } %> "globalstrict": true, "bitwise": true, "camelcase": true, diff --git a/app/templates/_karma.conf.js b/app/templates/_karma.conf.js index c0c338a2..7422b091 100644 --- a/app/templates/_karma.conf.js +++ b/app/templates/_karma.conf.js @@ -2,25 +2,49 @@ module.exports = function(config) { - config.set({ + var configuration = { autoWatch : false, frameworks: ['jasmine'], + ngHtml2JsPreprocessor: { + stripPrefix: 'src/', + moduleName: 'gulpAngular' + }, + <% if(props.jsPreprocessor.key === 'traceur') { %> browsers : ['Chrome'], plugins : [ - 'karma-chrome-launcher', - 'karma-jasmine' - ] -<% } else {%> + 'karma-chrome-launcher', +<% } else { %> browsers : ['PhantomJS'], plugins : [ - 'karma-phantomjs-launcher', - 'karma-jasmine' - ] + 'karma-phantomjs-launcher', <% } %> - }); + 'karma-jasmine', + 'karma-ng-html2js-preprocessor' + ], + + preprocessors: { + 'src/**/*.html': ['ng-html2js'] + } + }; + + // This block is needed to execute Chrome on Travis + // If you ever plan to use Chrome and Travis, you can keep it + // If not, you can safely remove it + // https://github.com/karma-runner/karma/issues/1144#issuecomment-53633076 + if(configuration.browsers[0] === 'Chrome' && process.env.TRAVIS) { + configuration.customLaunchers = { + 'chrome-travis-ci': { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }; + configuration.browsers = ['chrome-travis-ci']; + } + + config.set(configuration); }; diff --git a/app/templates/_package.json b/app/templates/_package.json index 2b84f0ce..30ad851b 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -31,7 +31,6 @@ "gulp-minify-html": "~0.1.7", "gulp-inject": "~1.1.1", "gulp-protractor": "~0.0.12", - "gulp-karma": "~0.0.4", "gulp-sourcemaps": "~1.3.0", <% if (props.cssPreprocessor.key === 'node-sass') { %> "gulp-sass": "~1.3.0", @@ -73,12 +72,15 @@ "merge-stream": "~0.1.7", "jshint-stylish": "~1.0.0", "wiredep": "~2.2.0", + "karma": "~0.12.31", "karma-jasmine": "~0.3.1", <% if(props.jsPreprocessor.key === 'traceur') { %> "karma-chrome-launcher": "~0.1.7", -<% } else {%> +<% } else { %> "karma-phantomjs-launcher": "~0.1.4", <% } %> + "karma-ng-html2js-preprocessor": "~0.1.2", + "concat-stream": "~1.4.7", "require-dir": "~0.1.0", "browser-sync": "~2.1.4", "browser-sync-spa": "~1.0.1", diff --git a/app/templates/gulp/_inject.js b/app/templates/gulp/_inject.js index 61261138..9ab4285e 100644 --- a/app/templates/gulp/_inject.js +++ b/app/templates/gulp/_inject.js @@ -20,7 +20,7 @@ module.exports = function(options) { ], { read: false }); <% } %> -<% if (props.jsPreprocessor.srcExtension === 'ts') { %> +<% if (props.jsPreprocessor.key === 'typescript') { %> var sortOutput = require('../' + options.tmp + '/sortOutput.json'); <% } %> diff --git a/app/templates/gulp/_unit-tests.js b/app/templates/gulp/_unit-tests.js index 59048a3a..13a090bd 100644 --- a/app/templates/gulp/_unit-tests.js +++ b/app/templates/gulp/_unit-tests.js @@ -5,42 +5,80 @@ var gulp = require('gulp'); var $ = require('gulp-load-plugins')(); var wiredep = require('wiredep'); +var karma = require('karma'); +var concat = require('concat-stream'); +var _ = require('lodash'); module.exports = function(options) { - function runTests (singleRun) { + function listFiles(callback) { var bowerDeps = wiredep({ directory: 'bower_components', - exclude: ['bootstrap-sass-official'], +<% if(wiredepExclusions.length > 0) { %> + exclude: [<%= wiredepExclusions.join(', ') %>], +<% } %> dependencies: true, devDependencies: true }); - var testFiles = bowerDeps.js.concat([ + var specFiles = [ + options.src + '/**/*.spec.js', + options.src + '/**/*.mock.js' + ]; + + var htmlFiles = [ + options.src + '/**/*.html' + ]; + + var srcFiles = [ <% if (props.jsPreprocessor.key === 'none') { %> options.src + '/{app,components}/**/*.js' <% } else if (props.jsPreprocessor.extension === 'js') { %> - options.tmp + '/serve/app/index.js', - options.src + '/{app,components}/**/*.spec.js', - options.src + '/{app,components}/**/*.mock.js' + options.tmp + '/serve/app/index.js' <% } else if (props.jsPreprocessor.key === 'typescript') { %> options.tmp + '/serve/{app,components}/**/!(index).js', - options.tmp + '/serve/{app,components}/**/index.js', - options.src + '/{app,components}/**/*.spec.js', - options.src + '/{app,components}/**/*.mock.js' + options.tmp + '/serve/{app,components}/**/index.js' <% } else { %> - options.tmp + '/serve/{app,components}/**/*.js', - options.src + '/{app,components}/**/*.spec.js', - options.src + '/{app,components}/**/*.mock.js' + options.tmp + '/serve/{app,components}/**/*.js' +<% } %> + ].concat(specFiles.map(function(file) { + return '!' + file; + })); + +<% if (props.jsPreprocessor.key === 'typescript') { %> + var sortOutput = require('../' + options.tmp + '/sortOutput.json'); <% } %> - ]); - return gulp.src(testFiles) - .pipe($.karma({ - configFile: 'karma.conf.js', - action: (singleRun)? 'run': 'watch' +<% if (props.jsPreprocessor.key === 'typescript') { %> + gulp.src(srcFiles, { read: false }) + .pipe($.order(sortOutput, {base: options.tmp + '/serve'})) +<% } else if (props.jsPreprocessor.extension !== 'js') { %> + gulp.src(srcFiles) + .pipe($.angularFilesort()).on('error', options.errorHandler('AngularFilesort')) +<% } else { %> + gulp.src(srcFiles) +<% } %> + .pipe(concat(function(files) { + callback(bowerDeps.js + .concat(_.pluck(files, 'path')) + .concat(htmlFiles) + .concat(specFiles)); })); } - gulp.task('test', ['scripts'], runTests.bind(this, true)); - gulp.task('test:auto', ['scripts'], runTests.bind(this, false)); + function runTests (singleRun, done) { + listFiles(function(files) { + karma.server.start({ + configFile: __dirname + '/../karma.conf.js', + files: files, + singleRun: singleRun + }, done); + }); + } + + gulp.task('test', ['scripts'], function(done) { + runTests(true, done); + }); + gulp.task('test:auto', ['watch'], function(done) { + runTests(false, done); + }); }; diff --git a/app/templates/src/.jshintrc b/app/templates/src/.jshintrc deleted file mode 100644 index 0eda6716..00000000 --- a/app/templates/src/.jshintrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../.jshintrc", - "esnext": true -} diff --git a/test/inception/test-inception.js b/test/inception/test-inception.js index fc959482..59c50b20 100644 --- a/test/inception/test-inception.js +++ b/test/inception/test-inception.js @@ -76,7 +76,7 @@ describe('gulp-angular generator inception tests', function () { ui: prompts.ui.values.bootstrap, bootstrapComponents: prompts.bootstrapComponents.values['ui-bootstrap'], cssPreprocessor: prompts.cssPreprocessor.values.less, - jsPreprocessor: prompts.jsPreprocessor.values['babel'], + jsPreprocessor: prompts.jsPreprocessor.values.babel, htmlPreprocessor: prompts.htmlPreprocessor.values.haml }).then(function(generator) { gulpAngular = generator; @@ -124,6 +124,32 @@ describe('gulp-angular generator inception tests', function () { }); }); + describe('with [no jquery, $http, Bootstrap, AngularStrap, ruby-sass, Traceur]', function () { + before(function() { + return inception.prepare({}, { + ui: prompts.ui.values.bootstrap, + bootstrapComponents: prompts.bootstrapComponents.values['angular-strap'], + cssPreprocessor: prompts.cssPreprocessor.values['ruby-sass'], + jsPreprocessor: prompts.jsPreprocessor.values.traceur + }).then(function(generator) { + gulpAngular = generator; + }); + }); + + it('should pass build', function () { + return inception.run(gulpAngular, 'build').should.be.fulfilled; + }); + it('should pass test', function () { + return inception.run(gulpAngular, 'test').should.be.fulfilled; + }); + it('should pass protractor', function () { + return inception.run(gulpAngular, 'protractor').should.be.fulfilled; + }); + it('should pass protractor:dist', function () { + return inception.run(gulpAngular, 'protractor:dist').should.be.fulfilled; + }); + }); + describe('with [src:src/angular/app e2e:tests/e2e dist:target/build/folder tmp:.tmp/folder] and default promps', function () { before(function() { return inception.prepare({ diff --git a/test/node/test-preprocessors.js b/test/node/test-preprocessors.js index 53be73d9..eb56e199 100644 --- a/test/node/test-preprocessors.js +++ b/test/node/test-preprocessors.js @@ -93,14 +93,6 @@ describe('gulp-angular generator preprocessors script', function () { }); describe('add lint configuration files for preprocessors different from es6', function() { - it('should add additional .jshintrc for es6 preprocessors', function() { - generator.props = { - jsPreprocessor: { srcExtension: 'es6' } - }; - generator.lintCopies(); - generator.files[5].src.should.match(/src\/\.jshintrc/); - }); - it('should add coffeelint for coffee preprocessor', function() { generator.props = { jsPreprocessor: { key: 'coffee' } diff --git a/test/template/test-inject.js b/test/template/test-inject.js index 841110c2..8fb48396 100644 --- a/test/template/test-inject.js +++ b/test/template/test-inject.js @@ -36,11 +36,11 @@ describe('gulp-angular inject template', function () { }); it('should create sortOutput.json for typescript', function() { - model.props.jsPreprocessor.srcExtension = null; + model.props.jsPreprocessor.key = null; var result = inject(model); result.should.not.match(/sortOutput\.json/); - model.props.jsPreprocessor.srcExtension = 'ts'; + model.props.jsPreprocessor.key = 'typescript'; result = inject(model); result.should.match(/var sortOutput = require/); }); diff --git a/test/template/test-jshintrc.js b/test/template/test-jshintrc.js new file mode 100644 index 00000000..c60842aa --- /dev/null +++ b/test/template/test-jshintrc.js @@ -0,0 +1,36 @@ +'use strict'; +/* jshint expr:true */ + +var chai = require('chai'); +var sinonChai = require('sinon-chai'); +chai.should(); +chai.use(sinonChai); + +var templateTools = require('../template-tools'); +var mockModel = require('./mock-model'); + +describe('gulp-angular jshint template', function () { + var jshint, model; + + before(function() { + return templateTools.load('_.jshintrc') + .then(function(templateModule) { + jshint = templateModule; + }); + }); + + beforeEach(function() { + model = mockModel(); + }); + + it('should add esnext for es6 js prepro', function() { + model.props.jsPreprocessor.srcExtension = 'notes6'; + var result = jshint(model); + result.should.not.match(/esnext/); + + model.props.jsPreprocessor.srcExtension = 'es6'; + result = jshint(model); + result.should.match(/"esnext": true/); + }); + +}); diff --git a/test/template/test-unit-tests.js b/test/template/test-unit-tests.js index 0c0464f5..1095d49e 100644 --- a/test/template/test-unit-tests.js +++ b/test/template/test-unit-tests.js @@ -23,6 +23,16 @@ describe('gulp-angular unit tests template', function () { model = mockModel(); }); + it('should configure wiredep with wiredep exclusions', function() { + model.wiredepExclusions = []; + var result = unitTests(model); + result.should.not.match(/exclude:/); + + model.wiredepExclusions = ['\'a\'', '\'b\'']; + result = unitTests(model); + result.should.match(/exclude: \['a', 'b'\]/); + }); + it('should add options for each css preprocessors', function() { model.props.jsPreprocessor.key = 'none'; var result = unitTests(model); @@ -32,50 +42,45 @@ describe('gulp-angular unit tests template', function () { model.props.jsPreprocessor.extension = 'js'; result = unitTests(model); result.should.match(/options\.tmp \+ '\/serve\/app\/index\.js/); - result.should.match(/options\.src \+ '[^\s]*spec\.js/); - result.should.match(/options\.src \+ '[^\s]*mock\.js/); model.props.jsPreprocessor.key = 'typescript'; model.props.jsPreprocessor.extension = 'ts'; result = unitTests(model); result.should.match(/options\.tmp \+ '\/serve[^\s]*!\(index\)\.js/); result.should.match(/options\.tmp \+ '\/serve[^\s]*index\.js/); - result.should.match(/options\.src \+ '[^\s]*spec\.js/); - result.should.match(/options\.src \+ '[^\s]*mock\.js/); model.props.jsPreprocessor.key = 'coffee'; model.props.jsPreprocessor.extension = 'coffee'; result = unitTests(model); result.should.match(/options\.tmp \+ '\/serve[^\s]*\.js/); - result.should.match(/options\.src \+ '[^\s]*spec\.js/); - result.should.match(/options\.src \+ '[^\s]*mock\.js/); }); - it('should select the right deps for the test tasks', function() { - model.props.jsPreprocessor.key = 'none'; + it('should create sortOutput.json for typescript', function() { + model.props.jsPreprocessor.key = null; var result = unitTests(model); - result.should.match(/task\('test', \['scripts'\], runTests\./); - result.should.match(/task\('test:auto', \['scripts'\], runTests\./); + result.should.not.match(/sortOutput\.json/); - model.props.jsPreprocessor.key = 'babel'; + model.props.jsPreprocessor.key = 'typescript'; result = unitTests(model); - result.should.match(/task\('test', \['scripts'\], runTests\./); - result.should.match(/task\('test:auto', \['scripts'\], runTests\./); + result.should.match(/var sortOutput = require/); + }); - model.props.jsPreprocessor.key = 'traceur'; - result = unitTests(model); - result.should.match(/task\('test', \['scripts'\], runTests\./); - result.should.match(/task\('test:auto', \['scripts'\], runTests\./); + it('should choose the right way to sort inject files', function() { + model.props.jsPreprocessor.key = 'typescript'; + var result = unitTests(model); + result.should.match(/\{ read: false \}\)\n.*\$\.order/); + result.should.not.match(/angularFilesort/); model.props.jsPreprocessor.key = 'coffee'; + model.props.jsPreprocessor.extension = 'coffee'; result = unitTests(model); - result.should.match(/task\('test', \['scripts'\], runTests\./); - result.should.match(/task\('test:auto', \['scripts'\], runTests\./); + result.should.match(/\$\.angularFilesort\(\)/); + result.should.not.match(/order/); - model.props.jsPreprocessor.key = 'typescript'; + model.props.jsPreprocessor.extension = 'js'; result = unitTests(model); - result.should.match(/task\('test', \['scripts'\], runTests\./); - result.should.match(/task\('test:auto', \['scripts'\], runTests\./); + result.should.not.match(/angularFilesort/); + result.should.not.match(/order/); }); });