Skip to content

Commit

Permalink
feat(gen): unify testing framework
Browse files Browse the repository at this point in the history
Changes:
- add prompt for Jasmine or Mocha
- if Mocha choosen, prompt for Expect or Should
- use `<%= does() %>` to dynamically insert assertions (expect|should)
- add mocha variants for protractor tests
- add mocha options to protractor.conf
- remove `test/fixtures/(bower|package).json` from repo
- move runE2E functionality to runTest and simplify switch
- comment generator test functions
- use node `0.11.13` in travis due to issues with `0.11.14`+

Note:
Server-side jasmine test are needed to fully unify testing frameworks.
Once Jasmine tests are included for server, mocha dep can be removed
fully when selecting Jasmine.
  • Loading branch information
kingcody committed Sep 26, 2014
1 parent d2bd1f5 commit 654de87
Show file tree
Hide file tree
Showing 20 changed files with 438 additions and 216 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: node_js
node_js:
- '0.10'
- '0.11'
- '0.11.13'
env:
global:
- SAUCE_USERNAME=fullstack_ci
Expand Down
133 changes: 94 additions & 39 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
this.pkg = require('../package.json');

this.filters = {};

// dynamic assertion statement
this.does = this.is = function(foo) {
if (this.filters.should) {
return foo + '.should';
} else {
return 'expect(' + foo + ').to';
}
}.bind(this);
},

info: function () {
Expand All @@ -36,9 +45,9 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({

if(this.config.get('filters')) {
this.prompt([{
type: "confirm",
name: "skipConfig",
message: "Existing .yo-rc configuration found, would you like to use it?",
type: 'confirm',
name: 'skipConfig',
message: 'Existing .yo-rc configuration found, would you like to use it?',
default: true,
}], function (answers) {
this.skipConfig = answers.skipConfig;
Expand Down Expand Up @@ -66,10 +75,10 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
this.log('# Client\n');

this.prompt([{
type: "list",
name: "script",
message: "What would you like to write scripts with?",
choices: [ "JavaScript", "CoffeeScript"],
type: 'list',
name: 'script',
message: 'What would you like to write scripts with?',
choices: [ 'JavaScript', 'CoffeeScript'],
filter: function( val ) {
var filterMap = {
'JavaScript': 'js',
Expand All @@ -79,33 +88,33 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
return filterMap[val];
}
}, {
type: "list",
name: "markup",
message: "What would you like to write markup with?",
choices: [ "HTML", "Jade"],
type: 'list',
name: 'markup',
message: 'What would you like to write markup with?',
choices: [ 'HTML', 'Jade'],
filter: function( val ) { return val.toLowerCase(); }
}, {
type: "list",
name: "stylesheet",
type: 'list',
name: 'stylesheet',
default: 1,
message: "What would you like to write stylesheets with?",
choices: [ "CSS", "Sass", "Stylus", "Less"],
message: 'What would you like to write stylesheets with?',
choices: [ 'CSS', 'Sass', 'Stylus', 'Less'],
filter: function( val ) { return val.toLowerCase(); }
}, {
type: "list",
name: "router",
type: 'list',
name: 'router',
default: 1,
message: "What Angular router would you like to use?",
choices: [ "ngRoute", "uiRouter"],
message: 'What Angular router would you like to use?',
choices: [ 'ngRoute', 'uiRouter'],
filter: function( val ) { return val.toLowerCase(); }
}, {
type: "confirm",
name: "bootstrap",
message: "Would you like to include Bootstrap?"
type: 'confirm',
name: 'bootstrap',
message: 'Would you like to include Bootstrap?'
}, {
type: "confirm",
name: "uibootstrap",
message: "Would you like to include UI Bootstrap?",
type: 'confirm',
name: 'uibootstrap',
message: 'Would you like to include UI Bootstrap?',
when: function (answers) {
return answers.bootstrap;
}
Expand All @@ -116,7 +125,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
this.filters[answers.router] = true;
this.filters.bootstrap = !!answers.bootstrap;
this.filters.uibootstrap = !!answers.uibootstrap;
cb();
cb();
}.bind(this));
},

Expand All @@ -128,13 +137,13 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
this.log('\n# Server\n');

this.prompt([{
type: "confirm",
name: "mongoose",
message: "Would you like to use mongoDB with Mongoose for data modeling?"
type: 'confirm',
name: 'mongoose',
message: 'Would you like to use mongoDB with Mongoose for data modeling?'
}, {
type: "confirm",
name: "auth",
message: "Would you scaffold out an authentication boilerplate?",
type: 'confirm',
name: 'auth',
message: 'Would you scaffold out an authentication boilerplate?',
when: function (answers) {
return answers.mongoose;
}
Expand Down Expand Up @@ -163,9 +172,9 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
}
]
}, {
type: "confirm",
name: "socketio",
message: "Would you like to use socket.io?",
type: 'confirm',
name: 'socketio',
message: 'Would you like to use socket.io?',
// to-do: should not be dependent on mongoose
when: function (answers) {
return answers.mongoose;
Expand All @@ -186,6 +195,47 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
}.bind(this));
},

projectPrompts: function() {
if(this.skipConfig) return;
var cb = this.async();
var self = this;

this.log('\n# Project\n');

this.prompt([{
type: 'list',
name: 'testing',
message: 'What would you like to write tests with?',
choices: [ 'Jasmine', 'Mocha + Chai + Sinon'],
filter: function( val ) {
var filterMap = {
'Jasmine': 'jasmine',
'Mocha + Chai + Sinon': 'mocha'
};

return filterMap[val];
}
}, {
type: 'list',
name: 'chai',
message: 'What would you like to write Chai assertions with?',
choices: ['Expect', 'Should'],
filter: function( val ) {
return val.toLowerCase();
},
when: function( answers ) {
return answers.testing === 'mocha';
}
}], function (answers) {
this.filters[answers.testing] = true;
if (this.filters.mocha) {
this.filters[answers.chai] = true;
}

cb();
}.bind(this));
},

saveSettings: function() {
if(this.skipConfig) return;
this.config.set('insertRoutes', true);
Expand All @@ -207,10 +257,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
if(this.skipConfig) return;
var appPath = 'client/app/';
var extensions = [];
var filters = [];
var filters = [
'ngroute',
'uirouter',
'jasmine',
'mocha',
'expect',
'should'
].filter(function(v) {return this.filters[v];}, this);

if(this.filters.ngroute) filters.push('ngroute');
if(this.filters.uirouter) filters.push('uirouter');
if(this.filters.coffee) extensions.push('coffee');
if(this.filters.js) extensions.push('js');
if(this.filters.html) extensions.push('html');
Expand Down Expand Up @@ -249,7 +304,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
if(this.filters.uirouter) angModules.push("'ui.router'");
if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'");

this.angularModules = "\n " + angModules.join(",\n ") +"\n";
this.angularModules = '\n ' + angModules.join(',\n ') +'\n';
},

generate: function() {
Expand Down
18 changes: 11 additions & 7 deletions app/templates/_package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"socketio-jwt": "^2.0.2"<% } %>
},
"devDependencies": {
"chai-as-promised": "^4.1.1",
"grunt": "~0.4.4",
"grunt-autoprefixer": "~0.7.2",
"grunt-wiredep": "~1.8.0",
Expand Down Expand Up @@ -62,9 +61,7 @@
"grunt-protractor-runner": "^1.1.0",
"grunt-asset-injector": "^0.1.0",
"grunt-karma": "~0.8.2",
"grunt-build-control": "DaftMonk/grunt-build-control",
"grunt-mocha-test": "~0.10.2",
"grunt-mocha-istanbul": "^2.0.0",<% if(filters.sass) { %>
"grunt-build-control": "DaftMonk/grunt-build-control",<% if(filters.sass) { %>
"grunt-contrib-sass": "^0.7.3",<% } %><% if(filters.stylus) { %>
"grunt-contrib-stylus": "latest",<% } %>
"jit-grunt": "^0.5.0",
Expand All @@ -74,12 +71,19 @@
"open": "~0.0.4",
"jshint-stylish": "~0.1.5",
"connect-livereload": "~0.4.0",
"grunt-mocha-test": "~0.10.2",
"grunt-mocha-istanbul": "^2.0.0",
"chai-as-promised": "^4.1.1",
"chai-things": "^0.2.0",
"sinon-chai": "^2.5.0",<% if(filters.mocha) { %>
"karma-mocha": "^0.1.9",
"karma-chai-plugins": "^0.2.3",<% } if(filters.jasmine) { %>
"karma-jasmine": "~0.1.5",<% } %>
"karma-ng-scenario": "~0.1.0",
"karma-firefox-launcher": "~0.1.3",
"karma-script-launcher": "~0.1.0",
"karma-html2js-preprocessor": "~0.1.0",
"karma-ng-jade2js-preprocessor": "^0.1.2",
"karma-jasmine": "~0.1.5",
"karma-chrome-launcher": "~0.1.3",
"requirejs": "~2.1.11",
"karma-requirejs": "~0.2.1",
Expand All @@ -88,9 +92,9 @@
"karma-phantomjs-launcher": "~0.1.4",
"karma": "~0.12.9",
"karma-ng-html2js-preprocessor": "~0.1.0",
"karma-spec-reporter": "0.0.13",
"proxyquire": "^1.0.1",
"supertest": "~0.11.0",
"sinon-chai": "^2.5.0"
"supertest": "~0.11.0"
},
"engines": {
"node": ">=0.10.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ describe 'Controller: MainCtrl', ->
$scope: scope

it 'should attach a list of things to the scope', ->
$httpBackend.flush()
expect(scope.awesomeThings.length).toBe 4
$httpBackend.flush()<% if (filters.jasmine) { %>
expect(scope.awesomeThings.length).toBe 4 <% } if (filters.mocha) { %>
<%= does("scope.awesomeThings.length") %>.equal 4<% } %>
5 changes: 3 additions & 2 deletions app/templates/client/app/main/main.controller.spec(js).js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ describe('Controller: MainCtrl', function () {
}));

it('should attach a list of things to the scope', function () {
$httpBackend.flush();
expect(scope.awesomeThings.length).toBe(4);
$httpBackend.flush();<% if (filters.jasmine) { %>
expect(scope.awesomeThings.length).toBe(4);<% } if (filters.mocha) { %>
<%= does("scope.awesomeThings.length") %>.equal(4);<% } %>
});
});
73 changes: 73 additions & 0 deletions app/templates/e2e/account(auth)/login/login.spec(mocha).js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use strict';

var config = protractor.getInstance().params;
var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');

describe('Login View', function() {
var page;

var loadPage = function() {
browser.get('/login');
page = require('./login.po');
};

var testUser = {
name: 'Test User',
email: '[email protected]',
password: 'test'
};

before(function() {
return UserModel
.removeAsync()
.then(function() {
return UserModel.createAsync(testUser);
})
.then(loadPage);
});

after(function() {
return UserModel.removeAsync();
});

it('should include login form with correct inputs and submit button', function() {
<%= does("page.form.email.getAttribute('type')") %>.eventually.equal('email');
<%= does("page.form.email.getAttribute('name')") %>.eventually.equal('email');
<%= does("page.form.password.getAttribute('type')") %>.eventually.equal('password');
<%= does("page.form.password.getAttribute('name')") %>.eventually.equal('password');
<%= does("page.form.submit.getAttribute('type')") %>.eventually.equal('submit');
<%= does("page.form.submit.getText()") %>.eventually.equal('Login');
});

describe('with local auth', function() {

it('should login a user and redirecting to "/"', function() {
page.login(testUser);

var navbar = require('../../components/navbar/navbar.po');

<%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/');
<%= does("navbar.navbarAccountGreeting.getText()") %>.eventually.equal('Hello ' + testUser.name);
});

describe('and invalid credentials', function() {
before(function() {
return loadPage();
})

it('should indicate login failures', function() {
page.login({
email: testUser.email,
password: 'badPassword'
});

<%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/login');

var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding'));
<%= does("helpBlock.getText()") %>.eventually.equal('This password is not correct.');
});

});

});
});
Loading

0 comments on commit 654de87

Please sign in to comment.