Merge branch 'pr/126'

Conflicts:
	package.json
This commit is contained in:
Amos Haviv
2013-12-03 02:12:47 +02:00
10 changed files with 455 additions and 16 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@
.nodemonignore
.sass-cache/
node_modules/
public/lib
public/lib
test/coverage/

View File

@@ -1,12 +1,16 @@
{
"name": "mean",
"version": "0.1.0",
"dependencies": {
"bootstrap": "2.3.2",
"angular": "1.0.6",
"angular-resource": "1.0.6",
"angular-cookies": "1.0.6",
"angular-bootstrap": "0.6.0",
"angular-ui-utils": "0.0.4"
}
"name": "mean",
"version": "0.1.0",
"dependencies": {
"bootstrap": "2.3.2",
"angular": "1.0.6",
"angular-resource": "1.0.6",
"angular-cookies": "1.0.6",
"angular-bootstrap": "0.6.0",
"angular-ui-utils": "0.0.4",
"angular-mocks": "~1.0.8"
},
"resolutions": {
"angular": "1.0.6"
}
}

View File

@@ -30,7 +30,7 @@ module.exports = function(grunt) {
}
},
jshint: {
all: ['gruntfile.js', 'public/js/**/*.js', 'test/**/*.js', 'app/**/*.js']
all: ['gruntfile.js', 'public/js/**/*.js', 'test/mocha/**/*.js', 'test/karma/**/*.js', 'app/**/*.js']
},
nodemon: {
dev: {
@@ -50,7 +50,7 @@ module.exports = function(grunt) {
}
},
concurrent: {
tasks: ['nodemon', 'watch'],
tasks: ['nodemon', 'watch', 'karma:unit'],
options: {
logConcurrentOutput: true
}
@@ -59,12 +59,17 @@ module.exports = function(grunt) {
options: {
reporter: 'spec'
},
src: ['test/**/*.js']
src: ['test/mocha/**/*.js']
},
env: {
test: {
NODE_ENV: 'test'
}
},
karma: {
unit: {
configFile: 'test/karma/karma.conf.js'
}
}
});
@@ -72,6 +77,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-env');

View File

@@ -1,4 +1,5 @@
{
<<<<<<< HEAD
"name": "mean",
"description": "MEAN - A Modern Stack: MongoDB, ExpressJS, AngularJS, NodeJS. (BONUS: Passport User Support).",
"version": "0.1.0",
@@ -48,4 +49,67 @@
"grunt-concurrent": "latest",
"grunt-mocha-test": "latest"
}
=======
"name": "mean",
"description": "MEAN - A Modern Stack: MongoDB, ExpressJS, AngularJS, NodeJS. (BONUS: Passport User Support).",
"version": "0.1.0",
"private": false,
"author": "Amos Haviv",
"repository": {
"type": "git",
"url": "https://github.com/linnovate/mean.git"
},
"engines": {
"node": "0.10.x",
"npm": "1.2.x"
},
"scripts": {
"start": "node node_modules/grunt-cli/bin/grunt",
"test": "node node_modules/grunt-cli/bin/grunt test",
"postinstall": "bower install"
},
"dependencies": {
"express": "latest",
"jade": "latest",
"mongoose": "latest",
"connect-mongo": "latest",
"connect-flash": "latest",
"crypto": "latest",
"passport": "latest",
"passport-local": "latest",
"passport-facebook": "latest",
"passport-twitter": "latest",
"passport-github": "latest",
"passport-google-oauth": "latest",
"underscore": "latest",
"async": "latest",
"view-helpers": "latest",
"mean-logger": "latest",
"forever": "latest",
"bower": "latest",
"grunt": "latest",
"grunt-cli": "latest",
"grunt-env": "latest"
},
"devDependencies": {
"supertest": "latest",
"should": "latest",
"grunt-contrib-watch": "latest",
"grunt-contrib-jshint": "latest",
"grunt-nodemon": "latest",
"grunt-concurrent": "latest",
"grunt-mocha-test": "latest",
"karma-script-launcher": "~0.1.0",
"karma-chrome-launcher": "~0.1.0",
"karma-firefox-launcher": "~0.1.0",
"karma-html2js-preprocessor": "~0.1.0",
"karma-jasmine": "~0.1.3",
"karma-requirejs": "~0.1.0",
"karma-coffee-preprocessor": "~0.1.0",
"karma-phantomjs-launcher": "~0.1.0",
"karma": "~0.10.4",
"grunt-karma": "~0.6.2",
"karma-coverage": "~0.1.0"
}
>>>>>>> pr/126
}

99
test/karma/karma.conf.js Normal file
View File

@@ -0,0 +1,99 @@
// Karma configuration
// Generated on Sat Oct 05 2013 22:00:14 GMT+0700 (ICT)
module.exports = function (config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '../../',
// frameworks to use
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'public/lib/angular/angular.js',
'public/lib/angular-mocks/angular-mocks.js',
'public/lib/angular-cookies/angular-cookies.js',
'public/lib/angular-resource/angular-resource.js',
'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',
'public/lib/angular-bootstrap/ui-bootstrap.js',
'public/lib/angular-ui-utils/modules/route/route.js',
'public/js/app.js',
'public/js/config.js',
'public/js/directives.js',
'public/js/filters.js',
'public/js/services/global.js',
'public/js/services/articles.js',
'public/js/controllers/articles.js',
'public/js/controllers/index.js',
'public/js/controllers/header.js',
'public/js/init.js',
'test/karma/unit/**/*.js'
],
// list of files to exclude
exclude: [
],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
//reporters: ['progress'],
reporters: ['progress', 'coverage'],
// coverage
preprocessors: {
// source files, that you wanna generate coverage for
// do not include tests or libraries
// (these files will be instrumented by Istanbul)
'public/js/controllers/*.js': ['coverage'],
'public/js/services/*.js': ['coverage']
},
coverageReporter: {
type : 'html',
dir : 'test/coverage/'
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['PhantomJS'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};

View File

@@ -0,0 +1,201 @@
(function () {
'use strict';
// Articles Controller Spec
describe('MEAN controllers', function () {
describe('ArticlesController', function () {
// The $resource service augments the response object with methods for updating and deleting the resource.
// If we were to use the standard toEqual matcher, our tests would fail because the test values would not match
// the responses exactly. To solve the problem, we use a newly-defined toEqualData Jasmine matcher.
// When the toEqualData matcher compares two objects, it takes only object properties into
// account and ignores methods.
beforeEach(function () {
this.addMatchers({
toEqualData: function (expected) {
return angular.equals(this.actual, expected);
}
});
});
// Load the controllers module
beforeEach(module('mean'));
// Initialize the controller and a mock scope
var ArticlesController,
scope,
$httpBackend,
$routeParams,
$location;
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
// This allows us to inject a service but then attach it to a variable
// with the same name as the service.
beforeEach(inject(function ($controller, $rootScope, _$location_, _$routeParams_, _$httpBackend_) {
scope = $rootScope.$new();
ArticlesController = $controller('ArticlesController', {
$scope: scope
});
$routeParams = _$routeParams_;
$httpBackend = _$httpBackend_;
$location = _$location_;
}));
it('$scope.find() should create an array with at least one article object ' +
'fetched from XHR', function () {
// test expected GET request
$httpBackend.expectGET('articles').respond([
{title: 'An Article about MEAN', content: 'MEAN rocks!'}
]);
// run controller
scope.find();
$httpBackend.flush();
// test scope value
expect(scope.articles).toEqualData([
{title: 'An Article about MEAN', content: 'MEAN rocks!'}
]);
});
it('$scope.findOne() should create an array with one article object fetched ' +
'from XHR using a articleId URL parameter', function () {
// fixture URL parament
$routeParams.articleId = '525a8422f6d0f87f0e407a33';
// fixture response object
var testArticleData = function () {
return {
title: 'An Article about MEAN',
content: 'MEAN rocks!'
};
};
// test expected GET request with response object
$httpBackend.expectGET(/articles\/([0-9a-fA-F]{24})$/).respond(testArticleData());
// run controller
scope.findOne();
$httpBackend.flush();
// test scope value
expect(scope.article).toEqualData(testArticleData());
});
it('$scope.create() with valid form data should send a POST request ' +
'with the form input values and then ' +
'locate to new object URL', function () {
// fixture expected POST data
var postArticleData = function () {
return {
title: 'An Article about MEAN',
content: 'MEAN rocks!'
};
};
// fixture expected response data
var responseArticleData = function () {
return {
_id: '525cf20451979dea2c000001',
title: 'An Article about MEAN',
content: 'MEAN rocks!'
};
};
// fixture mock form input values
scope.title = 'An Article about MEAN';
scope.content = 'MEAN rocks!';
// test post request is sent
$httpBackend.expectPOST('articles', postArticleData()).respond(responseArticleData());
// Run controller
scope.create();
$httpBackend.flush();
// test form input(s) are reset
expect(scope.title).toEqual('');
expect(scope.content).toEqual('');
// test URL location to new object
expect($location.path()).toBe('/articles/' + responseArticleData()._id);
});
it('$scope.update() should update a valid article', inject(function (Articles) {
// fixture rideshare
var putArticleData = function () {
return {
_id: '525a8422f6d0f87f0e407a33',
title: 'An Article about MEAN',
to: 'MEAN is great!'
};
};
// mock article object from form
var article = new Articles(putArticleData());
// mock article in scope
scope.article = article;
// test PUT happens correctly
$httpBackend.expectPUT(/articles\/([0-9a-fA-F]{24})$/).respond();
// testing the body data is out for now until an idea for testing the dynamic updated array value is figured out
//$httpBackend.expectPUT(/articles\/([0-9a-fA-F]{24})$/, putArticleData()).respond();
/*
Error: Expected PUT /articles\/([0-9a-fA-F]{24})$/ with different data
EXPECTED: {"_id":"525a8422f6d0f87f0e407a33","title":"An Article about MEAN","to":"MEAN is great!"}
GOT: {"_id":"525a8422f6d0f87f0e407a33","title":"An Article about MEAN","to":"MEAN is great!","updated":[1383534772975]}
*/
// run controller
scope.update();
$httpBackend.flush();
// test URL location to new object
expect($location.path()).toBe('/articles/' + putArticleData()._id);
}));
it('$scope.remove() should send a DELETE request with a valid articleId' +
'and remove the article from the scope', inject(function (Articles) {
// fixture rideshare
var article = new Articles({
_id: '525a8422f6d0f87f0e407a33'
});
// mock rideshares in scope
scope.articles = [];
scope.articles.push(article);
// test expected rideshare DELETE request
$httpBackend.expectDELETE(/articles\/([0-9a-fA-F]{24})$/).respond(204);
// run controller
scope.remove(article);
$httpBackend.flush();
// test after successful delete URL location articles lis
//expect($location.path()).toBe('/articles');
expect(scope.articles.length).toBe(0);
}));
});
});
}());

View File

@@ -0,0 +1,32 @@
(function() {
'use strict';
describe('MEAN controllers', function() {
describe('HeaderController', function() {
// Load the controllers module
beforeEach(module('mean'));
var scope,
HeaderController;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
HeaderController = $controller('HeaderController', {
$scope: scope
});
}));
it('should expose some global scope', function() {
expect(scope.global).toBeTruthy();
});
});
});
})();

View File

@@ -0,0 +1,32 @@
(function() {
'use strict';
describe('MEAN controllers', function() {
describe('IndexController', function() {
// Load the controllers module
beforeEach(module('mean'));
var scope,
IndexController;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
IndexController = $controller('IndexController', {
$scope: scope
});
}));
it('should expose some global scope', function() {
expect(scope.global).toBeTruthy();
});
});
});
})();

View File

@@ -2,7 +2,7 @@
* Module dependencies.
*/
var should = require('should'),
app = require('../../server'),
app = require('../../../server'),
mongoose = require('mongoose'),
User = mongoose.model('User'),
Article = mongoose.model('Article');

View File

@@ -2,7 +2,7 @@
* Module dependencies.
*/
var should = require('should'),
app = require('../../server'),
app = require('../../../server'),
mongoose = require('mongoose'),
User = mongoose.model('User');