├── public
├── data
│ ├── freewind.json
│ └── config.json
├── js
│ ├── hello.js
│ ├── hello2.js
│ ├── jquery-private.js
│ ├── appConfig.js
│ ├── app.js
│ ├── controllers.js
│ └── config.js
└── index.html
├── .gitignore
├── test
├── protractor-conf.js
├── unit
│ ├── hello2_spec.js
│ ├── hello_spec.js
│ └── controller_spec.js
├── e2e
│ ├── juliemr_spec.js
│ └── controller_spec.js
└── test-main.js
├── README.md
├── bower.json
├── package.json
├── karma.conf.js
└── Gruntfile.js
/public/data/freewind.json:
--------------------------------------------------------------------------------
1 | {
2 | "age": 123
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
3 | lib
4 |
--------------------------------------------------------------------------------
/public/data/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "freewind"
3 | }
--------------------------------------------------------------------------------
/public/js/hello.js:
--------------------------------------------------------------------------------
1 | function hello() {
2 | return "hello, world";
3 | }
4 |
--------------------------------------------------------------------------------
/public/js/hello2.js:
--------------------------------------------------------------------------------
1 | define([], function() {
2 | return {
3 | name: "freewind"
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/public/js/jquery-private.js:
--------------------------------------------------------------------------------
1 | define(['jquery'], function(jq) {
2 | return jQuery.noConflict(true);
3 | });
4 |
--------------------------------------------------------------------------------
/public/js/appConfig.js:
--------------------------------------------------------------------------------
1 | define(['json!../data/config.json'], function(config) {
2 | console.dir(config);
3 | return config;
4 | });
--------------------------------------------------------------------------------
/test/protractor-conf.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | seleniumAddress: 'http://localhost:4444/wd/hub',
3 | specs: ['e2e/*_spec.js']
4 | }
5 |
--------------------------------------------------------------------------------
/test/unit/hello2_spec.js:
--------------------------------------------------------------------------------
1 | define(['hello2'], function(hello2) {
2 | describe("hello2", function() {
3 | it("will return name of freewind", function() {
4 | expect(hello2.name).toBe("freewind");
5 | });
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/test/unit/hello_spec.js:
--------------------------------------------------------------------------------
1 | define(['hello'], function(he) {
2 | describe('hello', function() {
3 |
4 | it('hello should be true', function() {
5 | expect(he()).toBe("hello, world");
6 | });
7 |
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/e2e/juliemr_spec.js:
--------------------------------------------------------------------------------
1 | describe('angular homepage', function() {
2 | it('should have a title', function() {
3 | browser.get('http://juliemr.github.io/protractor-demo/');
4 | expect(browser.getTitle()).toEqual('Super Calculator');
5 | });
6 | });
7 |
--------------------------------------------------------------------------------
/public/js/app.js:
--------------------------------------------------------------------------------
1 | define(['angular', 'app.controllers', 'appConfig'], function(angular, xxx, appConfig) {
2 |
3 | var myApp = angular.module('myApp', ['app.controllers'])
4 | .constant('appConfig', appConfig);
5 |
6 | angular.element(document).ready(function() {
7 | angular.bootstrap(document, ['myApp']);
8 | });
9 |
10 | return myApp;
11 | });
12 |
--------------------------------------------------------------------------------
/test/e2e/controller_spec.js:
--------------------------------------------------------------------------------
1 | describe('input', function() {
2 | it('should update the label aside', function() {
3 | browser.get('http://localhost:8081/public/index.html');
4 | var nameInput = element(by.model('name'));
5 | nameInput.clear();
6 | nameInput.sendKeys('test-value');
7 | expect(element(by.binding('name')).getText()).toEqual("test-value");
8 | });
9 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a simple repository to demo how to use
2 |
3 | grunt + bower + requirejs + angular
4 |
5 | together, and also how to write unit tests and e2e tests for them.
6 |
7 | ## run unit tests
8 |
9 | karma start karma.conf.js
10 |
11 | ## run e2e tests
12 |
13 | webdriver-manager start
14 | npm start
15 | protractor test/protractor-conf.js
16 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{name}}
9 | Age: {{age}}
10 |
11 |
12 | {{config}}
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/public/js/controllers.js:
--------------------------------------------------------------------------------
1 | define(['angular'], function(angular) {
2 | return angular.module('app.controllers', [])
3 | .controller('MyController', ['$rootScope', '$scope', '$http', 'appConfig',
4 | function($rootScope, $scope, $http, appConfig) {
5 | $scope.name = 'Change the name';
6 | $scope.age = 0;
7 | $http.get('/public/data/' + appConfig.name + ".json")
8 | .success(function(data) {
9 | $scope.age=data.age;
10 | })
11 | }])
12 | .controller('ConfigController', ['$scope', function($scope) {
13 |
14 | }]);
15 | });
16 |
--------------------------------------------------------------------------------
/test/unit/controller_spec.js:
--------------------------------------------------------------------------------
1 | define(['angular', 'angularMocks', 'app.controllers'], function(angular, mocks, appModule) {
2 | describe('MyController', function() {
3 |
4 | var MyController, scope;
5 |
6 | beforeEach(function() {
7 | mocks.module('app.controllers');
8 | mocks.inject(function($rootScope, $controller) {
9 | scope = $rootScope.$new();
10 | MyController = $controller('MyController', {
11 | $scope: scope
12 | });
13 | });
14 | });
15 |
16 | it('should have added name to $scope', function() {
17 | expect(scope.name).toBe("Change the name");
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grunt-bower-angular-demo",
3 | "version": "0.0.0",
4 | "homepage": "https://github.com/freewind/grunt-bower-angular-demo",
5 | "authors": [
6 | "Peng Li "
7 | ],
8 | "license": "MIT",
9 | "ignore": [
10 | "**/.*",
11 | "node_modules",
12 | "bower_components",
13 | "test",
14 | "tests"
15 | ],
16 | "dependencies": {
17 | "angularjs": "~1.2.20",
18 | "jquery": "~2.1.1",
19 | "requirejs": "~2.1.14",
20 | "angular-mocks": "~1.2.21",
21 | "requirejs-plugins": "~1.0.2"
22 | },
23 | "devDependencies": {
24 | "requirejs-text": "~2.0.12"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/public/js/config.js:
--------------------------------------------------------------------------------
1 | requirejs.config({
2 | baseUrl: '/public/js',
3 | paths: {
4 | app: 'app',
5 | hello: 'hello',
6 | hello2: 'hello2',
7 | jquery: 'lib/jquery/jquery',
8 | angular: 'lib/angularjs/angular',
9 | text: 'lib/requirejs-text/text',
10 | json: 'lib/requirejs-plugins/json',
11 | appConfig: 'appConfig',
12 | 'app.controllers': 'controllers'
13 | },
14 | shim: {
15 | angular : { exports : 'angular'},
16 | hello : { exports: 'hello' }
17 | },
18 | priority: ["angular"],
19 | map: {
20 | '*': { 'jquery': 'jquery-private'},
21 | 'jquery-private': { 'jquery': 'jquery'}
22 | }
23 | });
24 |
25 | requirejs(['app']);
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": ">= 0.10.0"
4 | },
5 | "devDependencies": {
6 | "bower": "^1.3.8",
7 | "grunt": "~0.4.5",
8 | "grunt-bower": "^0.13.4",
9 | "grunt-bower-task": "^0.4.0",
10 | "grunt-contrib-concat": "~0.4.0",
11 | "grunt-contrib-jshint": "~0.10.0",
12 | "grunt-contrib-qunit": "~0.5.2",
13 | "grunt-contrib-uglify": "~0.5.0",
14 | "grunt-contrib-watch": "~0.6.1",
15 | "grunt-http-server": "0.0.5",
16 | "grunt-karma": "^0.8.3",
17 | "grunt-protractor-runner": "^1.1.0",
18 | "grunt-protractor-webdriver": "^0.1.8",
19 | "grunt-shell": "^0.7.0",
20 | "grunt-shell-spawn": "^0.3.0",
21 | "grunt-wait-server": "^0.1.2",
22 | "http-server": "^0.6.1",
23 | "karma": "^0.12.19",
24 | "karma-chrome-launcher": "^0.1.4",
25 | "karma-ie-launcher": "^0.1.5",
26 | "karma-jasmine": "^0.1.5",
27 | "karma-requirejs": "^0.2.2",
28 | "protractor": "^1.0.0",
29 | "requirejs": "^2.1.14"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/test-main.js:
--------------------------------------------------------------------------------
1 | var allTestFiles = [];
2 | var TEST_REGEXP = /(spec|test)\.js$/i;
3 |
4 | var pathToModule = function(path) {
5 | return path.replace(/^\/base\//, '').replace(/\.js$/, '');
6 | };
7 |
8 | Object.keys(window.__karma__.files).forEach(function(file) {
9 | if (TEST_REGEXP.test(file)) {
10 | // Normalize paths to RequireJS module names.
11 | allTestFiles.push(pathToModule(file));
12 | }
13 | });
14 |
15 | require.config({
16 | // Karma serves files under /base, which is the basePath from your config file
17 | baseUrl: '/base/',
18 |
19 | paths: {
20 | app: 'public/js/app',
21 | hello: 'public/js/hello',
22 | hello2: 'public/js/hello2',
23 | jquery: 'public/js/lib/jquery/jquery',
24 | angular: 'public/js/lib/angular/angular',
25 | 'angularMocks': 'public/js/lib/angular-mocks/angular-mocks',
26 | 'app.controllers': 'public/js/controllers'
27 | },
28 | shim: {
29 | angular : { exports : 'angular'},
30 | hello : { exports: 'hello' },
31 | 'angularMocks': {
32 | deps:['angular'],
33 | 'exports':'angular.mock'
34 | }
35 | },
36 | priority: ["angular"],
37 | map: {
38 | '*': { 'jquery': 'jquery-private'},
39 | 'jquery-private': { 'jquery': 'jquery'}
40 | },
41 |
42 | // dynamically load all test files
43 | deps: allTestFiles,
44 |
45 | // we have to kickoff jasmine, as it is asynchronous
46 | callback: window.__karma__.start
47 | });
48 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Wed Jul 30 2014 13:58:11 GMT+0800 (CST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine', 'requirejs'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | 'test/test-main.js',
19 | {pattern: 'public/js/**/*.js', included: false},
20 | {pattern: 'test/unit/**/*_spec.js', included: false}
21 | ],
22 |
23 |
24 | // list of files to exclude
25 | exclude: [
26 | ],
27 |
28 |
29 | // preprocess matching files before serving them to the browser
30 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
31 | preprocessors: {
32 | },
33 |
34 |
35 | // test results reporter to use
36 | // possible values: 'dots', 'progress'
37 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
38 | reporters: ['progress'],
39 |
40 |
41 | // web server port
42 | port: 9876,
43 |
44 |
45 | // enable / disable colors in the output (reporters and logs)
46 | colors: true,
47 |
48 |
49 | // level of logging
50 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
51 | logLevel: config.LOG_INFO,
52 |
53 |
54 | // enable / disable watching file and executing tests whenever any file changes
55 | autoWatch: true,
56 |
57 |
58 | // start these browsers
59 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
60 | browsers: ['Chrome'],
61 |
62 |
63 | // Continuous Integration mode
64 | // if true, Karma captures browsers, runs the tests and exits
65 | singleRun: true
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*global module:false*/
2 | module.exports = function(grunt) {
3 |
4 | var protractorDir = 'node_modules/protractor/bin/';
5 |
6 | // Project configuration.
7 | grunt.initConfig({
8 | // Metadata.
9 | pkg: grunt.file.readJSON('package.json'),
10 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
11 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
12 | '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
13 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
14 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
15 | // Task configuration.
16 | concat: {
17 | options: {
18 | banner: '<%= banner %>',
19 | stripBanners: true
20 | },
21 | dist: {
22 | src: ['lib/<%= pkg.name %>.js'],
23 | dest: 'dist/<%= pkg.name %>.js'
24 | }
25 | },
26 | uglify: {
27 | options: {
28 | banner: '<%= banner %>'
29 | },
30 | dist: {
31 | src: '<%= concat.dist.dest %>',
32 | dest: 'dist/<%= pkg.name %>.min.js'
33 | }
34 | },
35 | jshint: {
36 | options: {
37 | curly: true,
38 | eqeqeq: true,
39 | immed: true,
40 | latedef: true,
41 | newcap: true,
42 | noarg: true,
43 | sub: true,
44 | undef: true,
45 | unused: true,
46 | boss: true,
47 | eqnull: true,
48 | browser: true,
49 | globals: {
50 | jQuery: true
51 | }
52 | },
53 | gruntfile: {
54 | src: 'Gruntfile.js'
55 | },
56 | lib_test: {
57 | src: ['lib/**/*.js', 'test/**/*.js']
58 | }
59 | },
60 | qunit: {
61 | files: ['test/**/*.html']
62 | },
63 | watch: {
64 | gruntfile: {
65 | files: '<%= jshint.gruntfile.src %>',
66 | tasks: ['jshint:gruntfile']
67 | },
68 | lib_test: {
69 | files: '<%= jshint.lib_test.src %>',
70 | tasks: ['jshint:lib_test', 'qunit']
71 | }
72 | },
73 | bower: {
74 | install: {
75 | options: {
76 | targetDir: './public/js/lib',
77 | layout: 'byComponent',
78 | install: true,
79 | verbose: true,
80 | cleanTargetDir: true,
81 | cleanBowerDir: true,
82 | bowerOptions: {}
83 | }
84 | }
85 | },
86 | 'http-server': {
87 | dev: {
88 | root: "./",
89 | port: 8282,
90 | host: "127.0.0.1",
91 | cache: 10000,
92 | showDir : true,
93 | autoIndex: true,
94 | defaultExt: "html",
95 | runInBackground: true
96 | }
97 | },
98 | protractor_webdriver: {
99 | alive: {
100 | options: {
101 | path: protractorDir,
102 | keepAlive: true
103 | }
104 | },
105 | dead: {
106 | options: {
107 | path: protractorDir
108 | }
109 | }
110 | },
111 | protractor: {
112 | options: {
113 | configFile: "node_modules/protractor/referenceConf.js", // Default config file
114 | keepAlive: false, // If false, the grunt process stops when the test fails.
115 | noColor: false, // If true, protractor will not use colors in its output.
116 | args: {
117 | // Arguments passed to the command
118 | }
119 | },
120 | test: {
121 | options: {
122 | configFile: "test/protractor-conf.js", // Target-specific config file
123 | args: {} // Target-specific arguments
124 | }
125 | },
126 | },
127 | karma: {
128 | unit: {
129 | configFile: 'karma.conf.js'
130 | }
131 | },
132 | shell: {
133 | protractor: {
134 | options: {
135 | stdout: true
136 | },
137 | command: protractorDir + 'webdriver-manager update --standalone --chrome'
138 | },
139 | serverForTesting: {
140 | options: {
141 | async: true,
142 | stdout: true,
143 | failOnError: true
144 | },
145 | command: './node_modules/http-server/bin/http-server . -p 8899'
146 | },
147 | server: {
148 | options: {
149 | stdout: true,
150 | async: false,
151 | failOnError: true,
152 | cache: 0
153 | },
154 | command: './node_modules/http-server/bin/http-server . -p 8900'
155 | },
156 | ls: {
157 | options: {
158 | stdout: true
159 | },
160 | command: 'ls'
161 | }
162 | },
163 | waitServer: {
164 | server: {
165 | options: {
166 | url: 'http://localhost:8899',
167 | fail: function () {console.error('the server had not start'); },
168 | timeout: 20 * 1000,
169 | isforce: false,
170 | interval: 1000,
171 | print: true
172 | }
173 | }
174 | }
175 | });
176 |
177 | // These plugins provide necessary tasks.
178 | grunt.loadNpmTasks('grunt-contrib-concat');
179 | grunt.loadNpmTasks('grunt-contrib-uglify');
180 | grunt.loadNpmTasks('grunt-contrib-qunit');
181 | grunt.loadNpmTasks('grunt-contrib-jshint');
182 | grunt.loadNpmTasks('grunt-contrib-watch');
183 | grunt.loadNpmTasks('grunt-bower-task');
184 | grunt.loadNpmTasks('grunt-http-server');
185 | grunt.loadNpmTasks('grunt-protractor-runner');
186 | grunt.loadNpmTasks('grunt-protractor-webdriver');
187 | grunt.loadNpmTasks('grunt-karma');
188 | grunt.loadNpmTasks('grunt-shell-spawn');
189 | grunt.loadNpmTasks('grunt-wait-server');
190 |
191 | // Default task.
192 | grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
193 | grunt.registerTask('unit-test', ['karma:unit']);
194 | grunt.registerTask('server', ['shell:server']);
195 | grunt.registerTask('e2e-test', [
196 | 'http-server',
197 | 'shell:protractor',
198 | 'protractor_webdriver:alive',
199 | 'protractor:test']);
200 | grunt.registerTask('extern-server', ['shell:serverForTesting', 'waitServer:server', 'shell:ls']);
201 | };
202 |
--------------------------------------------------------------------------------