Skip to content

Commit 654de87

Browse files
committed
feat(gen): unify testing framework
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.
1 parent d2bd1f5 commit 654de87

20 files changed

+438
-216
lines changed

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: node_js
22
node_js:
33
- '0.10'
4-
- '0.11'
4+
- '0.11.13'
55
env:
66
global:
77
- SAUCE_USERNAME=fullstack_ci

Diff for: app/index.js

+94-39
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
2424
this.pkg = require('../package.json');
2525

2626
this.filters = {};
27+
28+
// dynamic assertion statement
29+
this.does = this.is = function(foo) {
30+
if (this.filters.should) {
31+
return foo + '.should';
32+
} else {
33+
return 'expect(' + foo + ').to';
34+
}
35+
}.bind(this);
2736
},
2837

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

3746
if(this.config.get('filters')) {
3847
this.prompt([{
39-
type: "confirm",
40-
name: "skipConfig",
41-
message: "Existing .yo-rc configuration found, would you like to use it?",
48+
type: 'confirm',
49+
name: 'skipConfig',
50+
message: 'Existing .yo-rc configuration found, would you like to use it?',
4251
default: true,
4352
}], function (answers) {
4453
this.skipConfig = answers.skipConfig;
@@ -66,10 +75,10 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
6675
this.log('# Client\n');
6776

6877
this.prompt([{
69-
type: "list",
70-
name: "script",
71-
message: "What would you like to write scripts with?",
72-
choices: [ "JavaScript", "CoffeeScript"],
78+
type: 'list',
79+
name: 'script',
80+
message: 'What would you like to write scripts with?',
81+
choices: [ 'JavaScript', 'CoffeeScript'],
7382
filter: function( val ) {
7483
var filterMap = {
7584
'JavaScript': 'js',
@@ -79,33 +88,33 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
7988
return filterMap[val];
8089
}
8190
}, {
82-
type: "list",
83-
name: "markup",
84-
message: "What would you like to write markup with?",
85-
choices: [ "HTML", "Jade"],
91+
type: 'list',
92+
name: 'markup',
93+
message: 'What would you like to write markup with?',
94+
choices: [ 'HTML', 'Jade'],
8695
filter: function( val ) { return val.toLowerCase(); }
8796
}, {
88-
type: "list",
89-
name: "stylesheet",
97+
type: 'list',
98+
name: 'stylesheet',
9099
default: 1,
91-
message: "What would you like to write stylesheets with?",
92-
choices: [ "CSS", "Sass", "Stylus", "Less"],
100+
message: 'What would you like to write stylesheets with?',
101+
choices: [ 'CSS', 'Sass', 'Stylus', 'Less'],
93102
filter: function( val ) { return val.toLowerCase(); }
94103
}, {
95-
type: "list",
96-
name: "router",
104+
type: 'list',
105+
name: 'router',
97106
default: 1,
98-
message: "What Angular router would you like to use?",
99-
choices: [ "ngRoute", "uiRouter"],
107+
message: 'What Angular router would you like to use?',
108+
choices: [ 'ngRoute', 'uiRouter'],
100109
filter: function( val ) { return val.toLowerCase(); }
101110
}, {
102-
type: "confirm",
103-
name: "bootstrap",
104-
message: "Would you like to include Bootstrap?"
111+
type: 'confirm',
112+
name: 'bootstrap',
113+
message: 'Would you like to include Bootstrap?'
105114
}, {
106-
type: "confirm",
107-
name: "uibootstrap",
108-
message: "Would you like to include UI Bootstrap?",
115+
type: 'confirm',
116+
name: 'uibootstrap',
117+
message: 'Would you like to include UI Bootstrap?',
109118
when: function (answers) {
110119
return answers.bootstrap;
111120
}
@@ -116,7 +125,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
116125
this.filters[answers.router] = true;
117126
this.filters.bootstrap = !!answers.bootstrap;
118127
this.filters.uibootstrap = !!answers.uibootstrap;
119-
cb();
128+
cb();
120129
}.bind(this));
121130
},
122131

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

130139
this.prompt([{
131-
type: "confirm",
132-
name: "mongoose",
133-
message: "Would you like to use mongoDB with Mongoose for data modeling?"
140+
type: 'confirm',
141+
name: 'mongoose',
142+
message: 'Would you like to use mongoDB with Mongoose for data modeling?'
134143
}, {
135-
type: "confirm",
136-
name: "auth",
137-
message: "Would you scaffold out an authentication boilerplate?",
144+
type: 'confirm',
145+
name: 'auth',
146+
message: 'Would you scaffold out an authentication boilerplate?',
138147
when: function (answers) {
139148
return answers.mongoose;
140149
}
@@ -163,9 +172,9 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
163172
}
164173
]
165174
}, {
166-
type: "confirm",
167-
name: "socketio",
168-
message: "Would you like to use socket.io?",
175+
type: 'confirm',
176+
name: 'socketio',
177+
message: 'Would you like to use socket.io?',
169178
// to-do: should not be dependent on mongoose
170179
when: function (answers) {
171180
return answers.mongoose;
@@ -186,6 +195,47 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
186195
}.bind(this));
187196
},
188197

198+
projectPrompts: function() {
199+
if(this.skipConfig) return;
200+
var cb = this.async();
201+
var self = this;
202+
203+
this.log('\n# Project\n');
204+
205+
this.prompt([{
206+
type: 'list',
207+
name: 'testing',
208+
message: 'What would you like to write tests with?',
209+
choices: [ 'Jasmine', 'Mocha + Chai + Sinon'],
210+
filter: function( val ) {
211+
var filterMap = {
212+
'Jasmine': 'jasmine',
213+
'Mocha + Chai + Sinon': 'mocha'
214+
};
215+
216+
return filterMap[val];
217+
}
218+
}, {
219+
type: 'list',
220+
name: 'chai',
221+
message: 'What would you like to write Chai assertions with?',
222+
choices: ['Expect', 'Should'],
223+
filter: function( val ) {
224+
return val.toLowerCase();
225+
},
226+
when: function( answers ) {
227+
return answers.testing === 'mocha';
228+
}
229+
}], function (answers) {
230+
this.filters[answers.testing] = true;
231+
if (this.filters.mocha) {
232+
this.filters[answers.chai] = true;
233+
}
234+
235+
cb();
236+
}.bind(this));
237+
},
238+
189239
saveSettings: function() {
190240
if(this.skipConfig) return;
191241
this.config.set('insertRoutes', true);
@@ -207,10 +257,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
207257
if(this.skipConfig) return;
208258
var appPath = 'client/app/';
209259
var extensions = [];
210-
var filters = [];
260+
var filters = [
261+
'ngroute',
262+
'uirouter',
263+
'jasmine',
264+
'mocha',
265+
'expect',
266+
'should'
267+
].filter(function(v) {return this.filters[v];}, this);
211268

212-
if(this.filters.ngroute) filters.push('ngroute');
213-
if(this.filters.uirouter) filters.push('uirouter');
214269
if(this.filters.coffee) extensions.push('coffee');
215270
if(this.filters.js) extensions.push('js');
216271
if(this.filters.html) extensions.push('html');
@@ -249,7 +304,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({
249304
if(this.filters.uirouter) angModules.push("'ui.router'");
250305
if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'");
251306

252-
this.angularModules = "\n " + angModules.join(",\n ") +"\n";
307+
this.angularModules = '\n ' + angModules.join(',\n ') +'\n';
253308
},
254309

255310
generate: function() {

Diff for: app/templates/_package.json

+11-7
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
"socketio-jwt": "^2.0.2"<% } %>
3232
},
3333
"devDependencies": {
34-
"chai-as-promised": "^4.1.1",
3534
"grunt": "~0.4.4",
3635
"grunt-autoprefixer": "~0.7.2",
3736
"grunt-wiredep": "~1.8.0",
@@ -62,9 +61,7 @@
6261
"grunt-protractor-runner": "^1.1.0",
6362
"grunt-asset-injector": "^0.1.0",
6463
"grunt-karma": "~0.8.2",
65-
"grunt-build-control": "DaftMonk/grunt-build-control",
66-
"grunt-mocha-test": "~0.10.2",
67-
"grunt-mocha-istanbul": "^2.0.0",<% if(filters.sass) { %>
64+
"grunt-build-control": "DaftMonk/grunt-build-control",<% if(filters.sass) { %>
6865
"grunt-contrib-sass": "^0.7.3",<% } %><% if(filters.stylus) { %>
6966
"grunt-contrib-stylus": "latest",<% } %>
7067
"jit-grunt": "^0.5.0",
@@ -74,12 +71,19 @@
7471
"open": "~0.0.4",
7572
"jshint-stylish": "~0.1.5",
7673
"connect-livereload": "~0.4.0",
74+
"grunt-mocha-test": "~0.10.2",
75+
"grunt-mocha-istanbul": "^2.0.0",
76+
"chai-as-promised": "^4.1.1",
77+
"chai-things": "^0.2.0",
78+
"sinon-chai": "^2.5.0",<% if(filters.mocha) { %>
79+
"karma-mocha": "^0.1.9",
80+
"karma-chai-plugins": "^0.2.3",<% } if(filters.jasmine) { %>
81+
"karma-jasmine": "~0.1.5",<% } %>
7782
"karma-ng-scenario": "~0.1.0",
7883
"karma-firefox-launcher": "~0.1.3",
7984
"karma-script-launcher": "~0.1.0",
8085
"karma-html2js-preprocessor": "~0.1.0",
8186
"karma-ng-jade2js-preprocessor": "^0.1.2",
82-
"karma-jasmine": "~0.1.5",
8387
"karma-chrome-launcher": "~0.1.3",
8488
"requirejs": "~2.1.11",
8589
"karma-requirejs": "~0.2.1",
@@ -88,9 +92,9 @@
8892
"karma-phantomjs-launcher": "~0.1.4",
8993
"karma": "~0.12.9",
9094
"karma-ng-html2js-preprocessor": "~0.1.0",
95+
"karma-spec-reporter": "0.0.13",
9196
"proxyquire": "^1.0.1",
92-
"supertest": "~0.11.0",
93-
"sinon-chai": "^2.5.0"
97+
"supertest": "~0.11.0"
9498
},
9599
"engines": {
96100
"node": ">=0.10.0"

Diff for: app/templates/client/app/main/main.controller.spec(coffee).coffee

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ describe 'Controller: MainCtrl', ->
2727
$scope: scope
2828

2929
it 'should attach a list of things to the scope', ->
30-
$httpBackend.flush()
31-
expect(scope.awesomeThings.length).toBe 4
30+
$httpBackend.flush()<% if (filters.jasmine) { %>
31+
expect(scope.awesomeThings.length).toBe 4 <% } if (filters.mocha) { %>
32+
<%= does("scope.awesomeThings.length") %>.equal 4<% } %>

Diff for: app/templates/client/app/main/main.controller.spec(js).js

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ describe('Controller: MainCtrl', function () {
2626
}));
2727

2828
it('should attach a list of things to the scope', function () {
29-
$httpBackend.flush();
30-
expect(scope.awesomeThings.length).toBe(4);
29+
$httpBackend.flush();<% if (filters.jasmine) { %>
30+
expect(scope.awesomeThings.length).toBe(4);<% } if (filters.mocha) { %>
31+
<%= does("scope.awesomeThings.length") %>.equal(4);<% } %>
3132
});
3233
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use strict';
2+
3+
var config = protractor.getInstance().params;
4+
var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');
5+
6+
describe('Login View', function() {
7+
var page;
8+
9+
var loadPage = function() {
10+
browser.get('/login');
11+
page = require('./login.po');
12+
};
13+
14+
var testUser = {
15+
name: 'Test User',
16+
17+
password: 'test'
18+
};
19+
20+
before(function() {
21+
return UserModel
22+
.removeAsync()
23+
.then(function() {
24+
return UserModel.createAsync(testUser);
25+
})
26+
.then(loadPage);
27+
});
28+
29+
after(function() {
30+
return UserModel.removeAsync();
31+
});
32+
33+
it('should include login form with correct inputs and submit button', function() {
34+
<%= does("page.form.email.getAttribute('type')") %>.eventually.equal('email');
35+
<%= does("page.form.email.getAttribute('name')") %>.eventually.equal('email');
36+
<%= does("page.form.password.getAttribute('type')") %>.eventually.equal('password');
37+
<%= does("page.form.password.getAttribute('name')") %>.eventually.equal('password');
38+
<%= does("page.form.submit.getAttribute('type')") %>.eventually.equal('submit');
39+
<%= does("page.form.submit.getText()") %>.eventually.equal('Login');
40+
});
41+
42+
describe('with local auth', function() {
43+
44+
it('should login a user and redirecting to "/"', function() {
45+
page.login(testUser);
46+
47+
var navbar = require('../../components/navbar/navbar.po');
48+
49+
<%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/');
50+
<%= does("navbar.navbarAccountGreeting.getText()") %>.eventually.equal('Hello ' + testUser.name);
51+
});
52+
53+
describe('and invalid credentials', function() {
54+
before(function() {
55+
return loadPage();
56+
})
57+
58+
it('should indicate login failures', function() {
59+
page.login({
60+
email: testUser.email,
61+
password: 'badPassword'
62+
});
63+
64+
<%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/login');
65+
66+
var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding'));
67+
<%= does("helpBlock.getText()") %>.eventually.equal('This password is not correct.');
68+
});
69+
70+
});
71+
72+
});
73+
});

0 commit comments

Comments
 (0)