├── .babelrc
├── .bowerrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── app
├── app.config.js
├── app.js
├── common
│ ├── config.js
│ ├── exception.js
│ ├── index.js
│ └── logger.js
├── components
│ ├── index.js
│ └── message-list
│ │ ├── message-list.directive.html
│ │ └── message-list.directive.js
├── filters
│ ├── index.js
│ └── whitespace.filter.js
├── interceptors
│ └── seo-interceptor.js
├── layout
│ ├── app.css
│ ├── layout.controller.js
│ └── styles.js
├── messages
│ ├── index.js
│ ├── list.html
│ ├── messages.config.js
│ └── messages.controller.js
├── services
│ ├── index.js
│ └── message.service.js
└── templates.js
├── bower.json
├── e2e-tests
└── protractor.conf.js
├── gulpfile.js
├── index.js
├── jsconfig.json
├── karma.conf.js
├── package.json
├── public
├── db.json
└── index.html
├── tests
├── common
│ ├── exception.spec.js
│ └── logger.spec.js
├── components
│ └── message-list.directive.spec.js
├── filters
│ └── whitespace.filter.spec.js
├── interceptors
│ └── seo-interceptor.spec.js
├── layout
│ └── layout-controller.spec.js
├── messages
│ └── messages-controller.spec.js
├── services
│ └── message.service.spec.js
└── tests.webpack.js
├── webpack.build.js
├── webpack.config.js
├── webpack.make.js
└── webpack.test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "json": "bower.json"
4 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.html
2 | **/templates.js
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "arrowFunctions": true,
4 | "blockBindings": true,
5 | "classes": true,
6 | "defaultParams": true,
7 | "destructuring": true,
8 | "forOf": true,
9 | "generators": false,
10 | "modules": true,
11 | "objectLiteralComputedProperties": true,
12 | "objectLiteralDuplicateProperties": false,
13 | "objectLiteralShorthandMethods": true,
14 | "objectLiteralShorthandProperties": true,
15 | "restParams": true,
16 | "spread": true,
17 | "superInFunctions": true,
18 | "templateStrings": true,
19 | "jsx": false
20 | },
21 | "env": {
22 | "browser": true,
23 | "node": true,
24 | "es6": true
25 | },
26 | "rules": {
27 | "strict": 0,
28 | "no-underscore-dangle": 0,
29 | "quotes": [
30 | 2,
31 | "single"
32 | ]
33 | }
34 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | npm-debug.log
15 |
16 | .idea/
17 | node_modules/
18 | coverage/
19 | bower_components/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "5.1.0"
4 | matrix:
5 | allow_failures:
6 | - node_js: "5.1.0"
7 | fast_finish: true
8 | before_install:
9 | - "npm install -g gulp"
10 | - "npm install -g bower"
11 | before_script:
12 | - "npm install"
13 | - "bower install"
14 | - "npm install coveralls"
15 | script:
16 | - "npm run-script test-travis"
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Ziya SARIKAYA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angularjs ES6 Webpack boilerplate
2 | Angular 1.5.9 + ES6 application boilerplate with testing practices
3 | [](https://david-dm.org/ziyasal/ng-espack-boilerplate) [](https://david-dm.org/ziyasal/ng-espack-boilerplate#info=devDependencies) [](https://travis-ci.org/ziyasal/ng-espack-boilerplate) [](https://coveralls.io/github/ziyasal/ng-espack-boilerplate?branch=master)
4 |
5 | >_Inspired from [angular-webpack-workflow](https://github.com/Foxandxss/angular-webpack-workflow)_
6 |
7 | ##Features
8 | - [x] [Webpack](https://webpack.github.io/) Setup
9 | - [x] [Babel](https://babeljs.io/)
10 | - [x] [Isparta](https://github.com/douglasduteil/isparta) [Instrumenter Loader](https://github.com/ColCh/isparta-instrumenter-loader)
11 | - [x] [Bootstrap](http://getbootstrap.com/)
12 | - [x] Gulp.js Setup
13 | - [x] [Angular Template Cache](https://github.com/miickel/gulp-angular-templatecache)
14 | - [x] [Webpack](https://webpack.github.io/)
15 | - [x] [ESLint](http://eslint.org/blog/2014/11/es6-jsx-support/)
16 | - [x] Basic App Structure by following [Angular Style Guide](https://github.com/johnpapa/angular-styleguide)
17 | - [x] SEO ready configuration using [angular-seo](https://github.com/steeve/angular-seo)
18 | - [x] Full fake REST API using [json-server](https://github.com/typicode/json-server)
19 | - [x] Testing Structure by following [official docs](https://docs.angularjs.org/guide/unit-testing) and [Testing Angular](https://github.com/daniellmb/angular-test-patterns)
20 | - [x] [Karma](http://karma-runner.github.io/0.13/index.html)
21 | - [x] [Jasmine](http://jasmine.github.io/2.0/introduction.html)
22 | - [x] [PhantomJS](http://phantomjs.org/)
23 | - [x] Controller test
24 | - [x] Service test
25 | - [x] Directive test
26 | - [x] Filter Test
27 | - [x] Http interceptor Test
28 |
29 |
30 | ##Install
31 | Clone repo and install npm and bower packages;
32 |
33 | ```
34 | git clone https://github.com/ziyasal/ng-espack-boilerplate.git
35 | cd ng-espack-boilerplate
36 | npm install
37 | bower install
38 | gulp
39 | ```
40 |
41 | ## Development
42 | All scripts are run with `npm run [script]`, for example: `npm run test`.
43 |
44 | `build` - generate a minified build to `public` folder
45 | `test` - run all tests
46 | `test:live` - continuously run unit tests watching for changes
47 | `eslint:app` - lint code in `app` folder
48 | `eslint:tests` - lint code in `tests` folder
49 |
50 | See what each script does by looking at the scripts section in `package.json`.
51 |
52 | License
53 | =======
54 |
55 | Code and documentation are available according to the `MIT` License (see [LICENSE](https://github.com/ziyasal/ng-espack-boilerplate/blob/master/LICENSE)).
56 |
--------------------------------------------------------------------------------
/app/app.config.js:
--------------------------------------------------------------------------------
1 | import seoInterceptor from './interceptors/seo-interceptor';
2 |
3 | export default function config($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
4 | $locationProvider.hashPrefix('!');
5 | $urlRouterProvider.otherwise('/');
6 |
7 | $httpProvider.interceptors.push(seoInterceptor);
8 | }
9 |
10 | config.$inject = ['$stateProvider', '$urlRouterProvider', '$locationProvider', '$httpProvider'];
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | import './layout/styles'
2 |
3 | import angular from 'angular';
4 | import uirouter from 'angular-ui-router';
5 |
6 | import './templates';
7 |
8 | import 'lodash';
9 | import 'angular-animate';
10 |
11 | import '../bower_components/angular-seo/angular-seo';
12 |
13 | import config from './app.config';
14 | import LayoutController from './layout/layout.controller.js';
15 |
16 | import commonModule from './common';
17 | import messagesModule from './messages';
18 | import componentsModule from './components';
19 | import filtersModule from './filters';
20 | import servicesModule from './services';
21 |
22 | angular.module('espackApp', [
23 | uirouter,
24 | commonModule, componentsModule, filtersModule, servicesModule, messagesModule,
25 | 'templates', 'seo'
26 | ])
27 | .config(config)
28 | .controller('LayoutController', LayoutController);
--------------------------------------------------------------------------------
/app/common/config.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export default function config($provide) {
4 | $provide.decorator('$exceptionHandler', extendExceptionHandler);
5 | }
6 |
7 |
8 |
9 | function extendExceptionHandler($delegate, toaster) {
10 | return function (exception, cause) {
11 | $delegate(exception, cause);
12 | var errorData = {
13 | exception: exception,
14 | cause: cause
15 | };
16 |
17 | console.log(errorData);
18 | //log errors to remote web server
19 | //toaster.error(exception.msg, errorData);
20 | };
21 | }
22 |
23 | config.$inject = ['$provide'];
24 | extendExceptionHandler.$inject = ['$delegate'];
--------------------------------------------------------------------------------
/app/common/exception.js:
--------------------------------------------------------------------------------
1 | export default function exception(logger) {
2 | return {
3 | catcher
4 | };
5 |
6 | function catcher(message) {
7 | return function (reason) {
8 | logger.error(message, reason);
9 | };
10 | }
11 | }
12 |
13 | exception.$inject = ['logger'];
--------------------------------------------------------------------------------
/app/common/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | import config from './config';
4 | import Logger from './logger';
5 | import exception from './exception';
6 | import toaster from 'angularjs-toaster'
7 |
8 | export default angular.module('espackApp.common', [toaster])
9 | .config(config)
10 | .service('logger', Logger)
11 | .factory('exception', exception)
12 | .name;
--------------------------------------------------------------------------------
/app/common/logger.js:
--------------------------------------------------------------------------------
1 | export default class Logger {
2 |
3 | constructor(toaster){
4 | this.toaster = toaster;
5 | }
6 |
7 | info(message) {
8 | this.toaster.pop({
9 | type: 'info',
10 | body: message
11 | });
12 | }
13 |
14 | error(message) {
15 | this.toaster.pop({
16 | type: 'error',
17 | body: message
18 | });
19 | }
20 | }
21 |
22 | Logger.$inject = ['toaster'];
23 |
--------------------------------------------------------------------------------
/app/components/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | import MessageList from './message-list/message-list.directive';
4 |
5 | export default angular
6 | .module('espackApp.components', [])
7 | .directive('messageList', ()=>new MessageList)
8 | .name;
--------------------------------------------------------------------------------
/app/components/message-list/message-list.directive.html:
--------------------------------------------------------------------------------
1 |
{{::vm.header}}
2 |
3 | -
4 | {{::message.text}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/components/message-list/message-list.directive.js:
--------------------------------------------------------------------------------
1 | export default class MessageList {
2 | constructor() {
3 | this.templateUrl = 'components/message-list/message-list.directive.html';
4 | this.restrict = 'E';
5 | this.scope = {
6 | messages: '=messages',
7 | header: '=header'
8 | };
9 |
10 | this.controller = DirectiveController;
11 | this.controllerAs = 'vm';
12 | this.bindToController = true;
13 | }
14 |
15 | link(scope, attrs) {
16 | //
17 | }
18 | }
19 |
20 | class DirectiveController {
21 | constructor($scope) {
22 | //put your logic here
23 | }
24 | }
25 |
26 | DirectiveController.$inject = ['$scope'];
27 |
--------------------------------------------------------------------------------
/app/filters/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import WhiteSpaceFilter from './whitespace.filter.js';
3 |
4 | export default angular.module('espackApp.filters', [])
5 | .filter('whiteSpace',WhiteSpaceFilter)
6 | .name;
--------------------------------------------------------------------------------
/app/filters/whitespace.filter.js:
--------------------------------------------------------------------------------
1 | export default function WhiteSpaceFilter(){
2 |
3 | return (text)=>{
4 |
5 | if(!!text){
6 |
7 | let result = text.replace(/\s/g, '');
8 | return result;
9 | }
10 |
11 | }
12 | }
--------------------------------------------------------------------------------
/app/interceptors/seo-interceptor.js:
--------------------------------------------------------------------------------
1 | export default function seoInterceptor($q, $injector) {
2 |
3 | let $http;
4 |
5 | return {
6 | response: (response) => {
7 | $http = $http || $injector.get('$http');
8 | let $timeout = $injector.get('$timeout');
9 | let $rootScope = $injector.get('$rootScope');
10 |
11 | if ($http.pendingRequests.length < 1) {
12 | $timeout(()=> {
13 | if ($http.pendingRequests.length < 1) {
14 | $rootScope.htmlReady();
15 | console.log('[HTML] ready.');
16 | }
17 | }, 700);
18 | /*an 0.7 seconds safety interval, if there are no requests for 0.7 seconds, it means that the layout is through rendering*/
19 | }
20 | return response || $q.when(response);
21 | },
22 | responseError: (response) => {
23 | return $q.reject(response);
24 | }
25 | };
26 | }
27 |
28 | seoInterceptor.$inject = ['$q', '$injector'];
--------------------------------------------------------------------------------
/app/layout/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | padding-bottom: 20px;
4 | }
5 |
6 | /* Set padding to keep content from hitting the edges */
7 | .body-content {
8 | padding-left: 15px;
9 | padding-right: 15px;
10 | }
11 |
12 | /* Override the default bootstrap behavior where horizontal description lists
13 | will truncate terms that are too long to fit in the left column
14 | */
15 | .dl-horizontal dt {
16 | white-space: normal;
17 | }
18 |
19 | /* Set width on the form input elements since they're 100% wide by default */
20 | input,
21 | select,
22 | textarea {
23 | max-width: 280px;
24 | }
25 |
--------------------------------------------------------------------------------
/app/layout/layout.controller.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | export default class LayoutController {
4 | constructor($scope) {
5 | this.pageTitle = 'AngularJS + ES6 application using Webpack';
6 |
7 | $scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams)=> {
8 | if (angular.isDefined(toState.data.pageTitle)) {
9 | this.pageTitle = `${toState.data.pageTitle} | AngularJS + ES6 application using Webpack`;
10 | }
11 | });
12 | }
13 | }
14 |
15 | LayoutController.$inject = ['$scope'];
16 |
--------------------------------------------------------------------------------
/app/layout/styles.js:
--------------------------------------------------------------------------------
1 | import 'bootstrap/dist/css/bootstrap.css';
2 | import 'angularjs-toaster/toaster.css';
3 | import './app.css';
--------------------------------------------------------------------------------
/app/messages/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import uirouter from 'angular-ui-router';
3 |
4 | import config from './messages.config';
5 |
6 | import MessagesController from './messages.controller.js';
7 |
8 | export default angular.module('espackApp.messages', [uirouter])
9 | .config(config)
10 | .controller('MessagesController', MessagesController)
11 | .name;
--------------------------------------------------------------------------------
/app/messages/list.html:
--------------------------------------------------------------------------------
1 | AngularJS + ES6 boilerplate application using Webpack
2 |
3 |
--------------------------------------------------------------------------------
/app/messages/messages.config.js:
--------------------------------------------------------------------------------
1 | export default function config($stateProvider) {
2 | $stateProvider.state('messages', {
3 | url: '/',
4 | views: {
5 | main: {
6 | controller: 'MessagesController',
7 | templateUrl: 'messages/list.html',
8 | controllerAs: 'vm'
9 | }
10 | },
11 | data: {
12 | pageTitle: 'Home'
13 | }
14 | });
15 | }
16 |
17 | config.$inject = ['$stateProvider'];
--------------------------------------------------------------------------------
/app/messages/messages.controller.js:
--------------------------------------------------------------------------------
1 | export default class MessagesController {
2 | constructor($scope, logger, messageService) {
3 | this.messages = [];
4 | this.messageService = messageService;
5 | this.logger = logger;
6 |
7 | this.activate();
8 | }
9 |
10 | activate() {
11 | return this.loadMessages().then(()=> {
12 | this.logger.info('init Home View');
13 | });
14 | }
15 |
16 |
17 | loadMessages() {
18 | return this.messageService.findAll().then(response=> {
19 | this.messages = response;
20 |
21 | return this.messages;
22 | });
23 | }
24 | }
25 |
26 | MessagesController.$inject = ['$scope', 'logger', 'messageService'];
27 |
--------------------------------------------------------------------------------
/app/services/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | import MessageService from './message.service.js';
4 |
5 | export default angular.module('espackApp.services', [])
6 | .service('messageService', MessageService)
7 | .name;
--------------------------------------------------------------------------------
/app/services/message.service.js:
--------------------------------------------------------------------------------
1 | export default class MessageService {
2 |
3 | constructor($http, $q, logger) {
4 | this.$http = $http;
5 | this.$q = $q;
6 | this.logger = logger;
7 | }
8 |
9 | findAll() {
10 | var deferred = this.$q.defer();
11 |
12 | this.$http.get('http://localhost:3000/messages')
13 | .success((data, status, headers, config) => {
14 | deferred.resolve(data);
15 | })
16 | .error((data, status)=> {
17 | this.logger.error('XHR Failed for MessageService#findAll.' + data);
18 | deferred.reject(data);
19 | });
20 |
21 | return deferred.promise;
22 | }
23 |
24 | find(id) {
25 | var deferred = this.$q.defer();
26 |
27 | this.$http.get(`http://localhost:3000/messages/${id}`)
28 | .success((data, status, headers, config)=> {
29 | deferred.resolve(data);
30 | })
31 | .error((data, status) => {
32 | this.logger.error('XHR Failed for MessageService#find.' + data);
33 | deferred.reject(data);
34 | });
35 |
36 | return deferred.promise;
37 | }
38 | }
39 |
40 | MessageService.$inject = ['$http', '$q', 'logger'];
--------------------------------------------------------------------------------
/app/templates.js:
--------------------------------------------------------------------------------
1 | angular.module("templates", []).run(["$templateCache", function($templateCache) {$templateCache.put("messages/list.html","AngularJS + ES6 boilerplate application using Webpack
\n\n");
2 | $templateCache.put("components/message-list/message-list.directive.html","{{::vm.header}}
\n\n\n - \n {{::message.text}}\n
\n
\n");}]);
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-espack-boilerplate",
3 | "version": "1.0.0",
4 | "authors": [
5 | "ziyasal"
6 | ],
7 | "license": "MIT",
8 | "ignore": [
9 | "**/.*",
10 | "node_modules",
11 | "bower_components",
12 | "test",
13 | "tests"
14 | ],
15 | "dependencies": {
16 | "bootstrap": "~3.3.6",
17 | "modernizr": "~3.2.0",
18 | "respond": "~1.4.2",
19 | "angular-seo": "*"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/e2e-tests/protractor.conf.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | allScriptsTimeout: 11000,
3 |
4 | specs: [
5 | '*.js'
6 | ],
7 |
8 | capabilities: {
9 | 'browserName': 'chrome'
10 | },
11 |
12 | baseUrl: 'http://localhost:8080/#!/',
13 |
14 | framework: 'jasmine',
15 |
16 | jasmineNodeOpts: {
17 | defaultTimeoutInterval: 30000
18 | }
19 | };
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 |
2 | var gulp = require('gulp');
3 | var templateCache = require('gulp-angular-templatecache');
4 | var gutil = require('gulp-util');
5 |
6 | var webpack = require('webpack');
7 | var webpackConfig = require('./webpack.config.js');
8 | var WebpackDevServer = require('webpack-dev-server');
9 |
10 | var webpackDevCfg = Object.create(webpackConfig);
11 | webpackDevCfg.devtool = 'eval-source-map';
12 | webpackDevCfg.debug = true;
13 | webpackDevCfg.output.pathinfo = true;
14 | webpackDevCfg.cache = true;
15 | webpackDevCfg.watch = true;
16 |
17 | var devCompiler = webpack(webpackDevCfg);
18 |
19 | gulp.task('tpl-cache', function() {
20 | return gulp.src('app/**/*html')
21 | .pipe(templateCache({
22 | standalone: true
23 | }))
24 | .pipe(gulp.dest('app'));
25 | });
26 |
27 | gulp.task('watch:html', function() {
28 | return gulp.watch(['app/**/*.html'], ['tpl-cache']);
29 | });
30 |
31 |
32 | gulp.task('webpack:dev-server', function(callback) {
33 |
34 | new WebpackDevServer(devCompiler, {
35 | contentBase: 'public',
36 | hot: true,
37 | filename: 'bundle.js',
38 | watchOptions: {
39 | aggregateTimeout: 300,
40 | poll: 1000
41 | }
42 | }).listen(8080, 'localhost', function(err) {
43 | if (err) throw new gutil.PluginError('webpack-dev-server', err);
44 |
45 | gutil.log('[webpack-dev-server]', 'http://localhost:8080/webpack-dev-server/index.html');
46 |
47 | callback();
48 | });
49 | });
50 |
51 | gulp.task('json-server', function() {
52 | require('./index');
53 | });
54 |
55 | gulp.task('default', ['json-server', 'watch:html', 'webpack:dev-server']);
56 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var jsonServer = require('json-server');
2 |
3 | var server = jsonServer.create();
4 | server.use(jsonServer.defaults());
5 |
6 | var router = jsonServer.router('./public/db.json');
7 | server.use(router);
8 |
9 | server.listen(3000);
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Tue Dec 08 2015 16:01:19 GMT+0200 (Turkey Standard Time)
3 |
4 | module.exports = function (config) {
5 | config.set({
6 |
7 | // frameworks to use
8 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
9 | frameworks: ['jasmine'],
10 |
11 | // test results reporter to use
12 | // possible values: 'dots', 'progress'
13 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
14 | reporters: [
15 | 'progress',
16 | // Reference: https://github.com/mlex/karma-spec-reporter
17 | // Set reporter to print detailed results to console
18 | 'spec',
19 |
20 | // Reference: https://github.com/karma-runner/karma-coverage
21 | // Output code coverage files
22 | 'coverage'
23 | ],
24 | coverageReporter: {
25 | type: 'lcov',
26 | dir: 'coverage',
27 | subdir: '.'
28 | },
29 |
30 | // list of files / patterns to load in the browser
31 | files: [
32 | 'tests/tests.webpack.js'
33 | ],
34 |
35 | // list of files to exclude
36 | exclude: [],
37 |
38 |
39 | // preprocess matching files before serving them to the browser
40 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
41 | preprocessors: {
42 | 'tests/tests.webpack.js': ['webpack', 'sourcemap']
43 | },
44 |
45 | // web server port
46 | port: 9876,
47 |
48 |
49 | // enable / disable colors in the output (reporters and logs)
50 | colors: true,
51 |
52 |
53 | // level of logging
54 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
55 | logLevel: config.LOG_INFO,
56 |
57 |
58 | // enable / disable watching file and executing tests whenever any file changes
59 | autoWatch: true,
60 |
61 |
62 | // start these browsers
63 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
64 | browsers: ['PhantomJS'],
65 |
66 |
67 | // Continuous Integration mode
68 | // if true, Karma captures browsers, runs the tests and exits
69 | singleRun: true,
70 |
71 | // Concurrency level
72 | // how many browser should be started simultanous
73 | concurrency: Infinity,
74 |
75 | webpack: require('./webpack.test.js'),
76 |
77 | // Hide webpack build information from output
78 | webpackMiddleware: {
79 | noInfo: true
80 | }
81 | });
82 | };
83 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-espack-boilerplate",
3 | "version": "1.0.0",
4 | "description": "Angular 1.4x + ES6 boilerplate application using Webpack",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "webpack --config webpack.build.js --bail -p",
8 | "test": "karma start",
9 | "test:live": "karma start --auto-watch --no-single-run",
10 | "preupdate-webdriver": "npm install",
11 | "update-webdriver": "webdriver-manager update",
12 | "preprotractor": "npm run update-webdriver",
13 | "protractor": "protractor e2e-tests/protractor.conf.js",
14 | "eslint:app": "eslint app/**",
15 | "eslint:tests": "eslint tests/**",
16 | "test-travis": "karma start && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/ziyasal/ng-espack-boilerplate.git"
21 | },
22 | "keywords": [
23 | "angular",
24 | "webpack",
25 | "es6",
26 | "babel",
27 | "isparta",
28 | "boilerplate",
29 | "eslint",
30 | "jasmine",
31 | "karma"
32 | ],
33 | "author": "ziyasal",
34 | "license": "MIT",
35 | "devDependencies": {
36 | "angular-mocks": "1.4.8",
37 | "autoprefixer": "6.0.3",
38 | "babel-core": "6.2.1",
39 | "babel-loader": "6.2.0",
40 | "babel-preset-es2015": "6.1.18",
41 | "chai": "3.4.1",
42 | "css-loader": "0.23.0",
43 | "eslint": "1.10.3",
44 | "eslint-config-defaults": "7.1.1",
45 | "extract-text-webpack-plugin": "0.9.1",
46 | "file-loader": "0.8.4",
47 | "gulp": "3.9.0",
48 | "gulp-angular-templatecache": "1.8.0",
49 | "gulp-concat": "2.6.0",
50 | "gulp-livereload": "3.8.1",
51 | "gulp-sourcemaps": "1.6.0",
52 | "gulp-uglify": "1.5.1",
53 | "gulp-usemin": "0.3.16",
54 | "gulp-util": "3.0.7",
55 | "html-webpack-plugin": "1.6.2",
56 | "istanbul-instrumenter-loader": "1.0.0",
57 | "jasmine-core": "2.4.1",
58 | "json-server": "0.8.4",
59 | "karma": "0.13.15",
60 | "karma-chrome-launcher": "0.2.2",
61 | "karma-coverage": "0.5.3",
62 | "karma-jasmine": "0.3.6",
63 | "karma-phantomjs-launcher": "0.2.1",
64 | "karma-sourcemap-loader": "0.3.6",
65 | "karma-spec-reporter": "0.0.23",
66 | "karma-webpack": "1.7.0",
67 | "node-libs-browser": "0.5.3",
68 | "null-loader": "0.1.1",
69 | "phantomjs": "1.9.18",
70 | "postcss-loader": "0.8.0",
71 | "raw-loader": "0.5.1",
72 | "run-sequence": "1.1.5",
73 | "style-loader": "0.13.0",
74 | "webpack": "1.12.2",
75 | "webpack-dev-server": "1.12.1"
76 | },
77 | "dependencies": {
78 | "angular": "1.5.9",
79 | "angular-animate": "1.4.8",
80 | "angular-ui-router": "0.2.18",
81 | "angularjs-toaster": "0.4.15",
82 | "bootstrap": "3.3.7",
83 | "jquery": "2.2.4",
84 | "lodash": "4.13.1",
85 | "phantomjs": "1.9.20"
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/public/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "messages": [
3 | {
4 | "id": 1,
5 | "text": "Hi, Nikola Tesla"
6 | },
7 | {
8 | "id": 2,
9 | "text": "Hi, James Clerk Maxwell"
10 | },
11 | {
12 | "id": 3,
13 | "text": "Hi, Isaac Asimov"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | angular es6 webpack app
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/common/exception.spec.js:
--------------------------------------------------------------------------------
1 | describe('exception', ()=> {
2 |
3 | let exceptionInstance, loggerMock;
4 |
5 | beforeEach(()=> {
6 | angular.mock.module(function ($provide) {
7 | $provide.service('logger', function () {
8 | this.error = jasmine.createSpy('error');
9 | });
10 | });
11 |
12 | angular.mock.module('espackApp');
13 | });
14 |
15 | describe('#ctor', ()=> {
16 |
17 | beforeEach(()=> {
18 | _inject();
19 | });
20 |
21 | it('should exist', ()=> {
22 | expect(!!exceptionInstance).toBe(true);
23 | });
24 |
25 | it('should define catcher property', function () {
26 |
27 | expect(exceptionInstance.catcher).toBeDefined();
28 | expect(typeof exceptionInstance.catcher).toBe('function');
29 | });
30 |
31 | });
32 |
33 | function _inject() {
34 | inject((exception, logger) => {
35 | loggerMock = logger;
36 | exceptionInstance = exception;
37 | });
38 | }
39 | });
--------------------------------------------------------------------------------
/tests/common/logger.spec.js:
--------------------------------------------------------------------------------
1 | describe('Logger', ()=> {
2 |
3 | let loggerInstance, toaster, toasterMock;
4 |
5 | beforeEach(()=> {
6 | angular.mock.module(function ($provide) {
7 | $provide.service('toaster', function () {
8 | this.pop = jasmine.createSpy('pop');
9 | });
10 | });
11 |
12 | angular.mock.module('espackApp');
13 | });
14 |
15 | describe('#ctor', ()=> {
16 |
17 | beforeEach(()=> {
18 | _inject();
19 | });
20 |
21 | it('should exist', ()=> {
22 | expect(!!loggerInstance).toBe(true);
23 | });
24 |
25 | });
26 |
27 | describe('#info', ()=> {
28 |
29 | beforeEach(()=> {
30 | _inject();
31 | });
32 |
33 | it('should be defined', ()=> {
34 | expect(loggerInstance.info).toBeDefined();
35 | });
36 |
37 | it('should be defined', ()=> {
38 |
39 | let expectedMessage = 'TEST';
40 |
41 | spyOn(toasterMock, 'pop').and.callThrough();
42 |
43 | loggerInstance.info(expectedMessage);
44 |
45 | expect(toasterMock.pop).toHaveBeenCalledWith({type: 'info', body: expectedMessage});
46 |
47 | });
48 | });
49 |
50 | function _inject() {
51 | inject((logger, toaster) => {
52 | loggerInstance = logger;
53 | toasterMock = toaster;
54 | });
55 | }
56 | });
--------------------------------------------------------------------------------
/tests/components/message-list.directive.spec.js:
--------------------------------------------------------------------------------
1 | describe('MessageListDirective', ()=> {
2 |
3 | let element, scope, compile, rootScope,
4 | validTemplate = '',
5 | defaultData = [
6 | {
7 | id: 0,
8 | text: 'Hi, Loretta Fitzgerald'
9 | }
10 | ];
11 |
12 | beforeEach(angular.mock.module('espackApp'));
13 |
14 | beforeEach(()=> {
15 | _inject();
16 | });
17 |
18 | describe('when created', function () {
19 | it('should not render list when messages property is empty', function () {
20 | element = createDirective(null, '');
21 | var expected = element.find('li').size();
22 | expect(expected).toBe(0);
23 | });
24 |
25 | it('should render the expected output', function () {
26 | element = createDirective();
27 | var expected = element.find('li').first().text().trim();
28 | expect(expected).toBe(defaultData[0].text);
29 | });
30 | });
31 |
32 | describe('when the model changes', function () {
33 | // Add specs
34 | });
35 |
36 | describe('when destroyed', function () {
37 | // Add specs
38 | });
39 |
40 |
41 | function createDirective(data, template) {
42 | let elm;
43 |
44 | //setup scope state
45 | scope.vm = {messages: data || defaultData};
46 |
47 | //create directive
48 | elm = compile(template || validTemplate)(scope);
49 |
50 | //trigger watchers
51 | //scope.$apply();
52 | scope.$digest();
53 | return elm;
54 | }
55 |
56 | function _inject() {
57 | inject(($rootScope, $compile)=> {
58 | scope = $rootScope.$new();
59 | compile = $compile;
60 | rootScope = $rootScope;
61 | });
62 | }
63 | });
--------------------------------------------------------------------------------
/tests/filters/whitespace.filter.spec.js:
--------------------------------------------------------------------------------
1 | describe('WhiteSpaceFilter', ()=> {
2 |
3 | let filterFactory, filterInstance;
4 |
5 | beforeEach(()=> {
6 | angular.mock.module('espackApp');
7 | });
8 |
9 | describe('#ctor', ()=> {
10 |
11 | beforeEach(()=> {
12 | _inject();
13 | });
14 |
15 | it('should exist', ()=> {
16 | expect(!!filterFactory).toBe(true);
17 | });
18 |
19 | });
20 |
21 | describe('#whiteSpaceFilter', ()=> {
22 |
23 | beforeEach(()=> {
24 | _inject();
25 | });
26 |
27 | it('should return same value', ()=> {
28 | let actual = 'Sample Text';
29 | filterInstance = filterFactory('whiteSpace');
30 |
31 | var expected = 'SampleText';
32 | expect(filterInstance(actual)).toEqual(expected);
33 | });
34 |
35 | });
36 |
37 | function _inject() {
38 | inject($filter=> {
39 | filterFactory = $filter;
40 | });
41 | }
42 | });
--------------------------------------------------------------------------------
/tests/interceptors/seo-interceptor.spec.js:
--------------------------------------------------------------------------------
1 | describe("seoInterceptor", function () {
2 |
3 | const seoInterceptorName = "seoInterceptor";
4 |
5 | let seoInterceptor, httpProvider, httpBackend;
6 | beforeEach(angular.mock.module('espackApp', ($httpProvider)=> {
7 | httpProvider = $httpProvider;
8 | }));
9 |
10 | describe('#existence', () => {
11 |
12 | beforeEach(()=> {
13 | _inject();
14 | });
15 |
16 | it('should be added to $http interceptors', ()=> {
17 |
18 | var interceptorNames = httpProvider.interceptors
19 | .filter((item)=> item.prototype.constructor.name === seoInterceptorName)
20 | .map((item)=>item.prototype.constructor.name);
21 |
22 | expect(interceptorNames.length).toEqual(1);
23 | expect(interceptorNames).toEqual([seoInterceptorName])
24 |
25 | });
26 |
27 | });
28 |
29 | function _inject() {
30 | inject(($httpBackend) => {
31 | httpBackend = $httpBackend;
32 | });
33 | }
34 |
35 | });
--------------------------------------------------------------------------------
/tests/layout/layout-controller.spec.js:
--------------------------------------------------------------------------------
1 | describe('LayoutController', ()=> {
2 |
3 | let LayoutController, mock, rootScope, controllerFactory;
4 |
5 | const pageTitleSuffix = ' | AngularJS + ES6 application using Webpack';
6 |
7 | beforeEach(angular.mock.module('espackApp'));
8 |
9 | describe('#construct', ()=> {
10 |
11 | beforeEach(()=> {
12 | _inject();
13 | });
14 |
15 | it('should exist', ()=> {
16 | LayoutController = controllerFactory('LayoutController', mock);
17 | expect(!!LayoutController).toBe(true);
18 | });
19 |
20 | it('should define pageTitle property', ()=> {
21 | LayoutController = controllerFactory('LayoutController', mock);
22 | expect(LayoutController.pageTitle).toBeDefined();
23 | });
24 |
25 | it('should set pageTitle by handling $stateChangeSuccess event', function () {
26 |
27 | let stateData = {
28 | data: {pageTitle: 'TEST'}
29 | };
30 |
31 | LayoutController = controllerFactory('LayoutController', mock);
32 | rootScope.$broadcast('$stateChangeSuccess', stateData);
33 |
34 | expect(LayoutController.pageTitle).toBe(stateData.data.pageTitle + pageTitleSuffix)
35 | });
36 | });
37 |
38 | function _inject() {
39 | inject(($controller, $rootScope) => {
40 | rootScope = $rootScope;
41 | controllerFactory = $controller;
42 |
43 | mock = {
44 | $scope: $rootScope.$new()
45 | };
46 | });
47 | }
48 | });
--------------------------------------------------------------------------------
/tests/messages/messages-controller.spec.js:
--------------------------------------------------------------------------------
1 | describe('MessagesController', ()=> {
2 |
3 | let homeCtrl, mock, deferredResolution, rootScope, controllerFactory,
4 | mockData = [
5 | {
6 | id: 0,
7 | text: 'Hi, Loretta Fitzgerald'
8 | }
9 | ];
10 |
11 | beforeEach(angular.mock.module('espackApp'));
12 |
13 | describe('#construct', ()=> {
14 |
15 | beforeEach(()=> {
16 | _inject();
17 | });
18 |
19 | it('should exist', ()=> {
20 | homeCtrl = controllerFactory('MessagesController', mock);
21 | expect(!!homeCtrl).toBe(true);
22 | });
23 |
24 | it('should define a messages Array property', () => {
25 | homeCtrl = controllerFactory('MessagesController', mock);
26 | expect(homeCtrl.messages instanceof Array).toBe(true);
27 | });
28 | });
29 |
30 | describe('#init', ()=> {
31 |
32 | beforeEach(()=> {
33 | _inject();
34 | });
35 |
36 | it('should be defined', ()=> {
37 | homeCtrl = controllerFactory('MessagesController', mock);
38 | expect(typeof homeCtrl.activate).toBe('function');
39 | });
40 |
41 | it('should set messages property', () => {
42 | //given
43 | spyOn(mock.messageService, 'findAll').and.returnValue(deferredResolution.promise);
44 |
45 | //when
46 | deferredResolution.resolve(mockData);
47 | homeCtrl = controllerFactory('MessagesController', mock);
48 | rootScope.$digest();
49 |
50 | //then
51 | expect(homeCtrl.messages.map(item=> item.text)).toEqual(mockData.map(item=> item.text));
52 | expect(mock.messageService.findAll).toHaveBeenCalled();
53 | });
54 | });
55 |
56 | describe('#loadMessages', ()=> {
57 |
58 | beforeEach(()=> {
59 | _inject();
60 | });
61 |
62 | it('should set messages property', () => {
63 | spyOn(mock.messageService, 'findAll').and.returnValue(deferredResolution.promise);
64 | homeCtrl = controllerFactory('MessagesController', mock);
65 | homeCtrl.init = ()=> {
66 | };//skip initialization because of testing loadMessages
67 |
68 | homeCtrl.loadMessages();
69 | deferredResolution.resolve(mockData);
70 | rootScope.$digest();
71 |
72 | expect(homeCtrl.messages.map(item=> item.text)).toEqual(mockData.map(item => item.text));
73 | expect(mock.messageService.findAll).toHaveBeenCalled();
74 | });
75 |
76 | });
77 |
78 | function _inject() {
79 | inject(($controller, $rootScope, messageService, logger, $q) => {
80 | rootScope = $rootScope;
81 | controllerFactory = $controller;
82 | deferredResolution = $q.defer();
83 | mock = {
84 | $scope: $rootScope.$new(),
85 | logger,
86 | messageService
87 | };
88 | });
89 | }
90 | });
--------------------------------------------------------------------------------
/tests/services/message.service.spec.js:
--------------------------------------------------------------------------------
1 | describe('MessageService', ()=> {
2 |
3 | let messageSvc, httpBackend, loggerMock,
4 | mockData = [
5 | {
6 | id: 0,
7 | text: 'Hi, Loretta Fitzgerald'
8 | }
9 | ],
10 | mockErrorData = {'msg': 'An error occurred'};
11 |
12 | beforeEach(()=> {
13 |
14 | angular.mock.module(function ($provide) {
15 | $provide.service('logger', function () {
16 | this.error = jasmine.createSpy('error');
17 | });
18 | });
19 |
20 | angular.mock.module('espackApp');
21 | });
22 |
23 | describe('#ctor', ()=> {
24 |
25 | beforeEach(()=> {
26 | _inject();
27 | });
28 |
29 | it('should exist', ()=> {
30 | expect(!!messageSvc).toBe(true);
31 | });
32 |
33 | });
34 |
35 | describe('#findAll', ()=> {
36 |
37 | beforeEach(()=> {
38 | _inject();
39 | });
40 |
41 | it('should be defined', ()=> {
42 | expect(typeof messageSvc.findAll).toBe('function');
43 | });
44 |
45 | it('should make $http call', ()=> {
46 |
47 | let expectedData;
48 |
49 | //given
50 | httpBackend.whenGET('http://localhost:3000/messages').respond(200, mockData);
51 |
52 | //when
53 | messageSvc.findAll().then((data, status, headers, config)=> {
54 | expectedData = data;
55 | });
56 |
57 | httpBackend.flush();
58 |
59 | //then
60 | expect(expectedData.map((elm)=> elm.text)).toEqual(mockData.map((elm)=> elm.text));
61 | });
62 |
63 | it('should handle when $http call failed', ()=> {
64 |
65 | let expectedData, expectedMessage = 'XHR Failed for MessageService#findAll.' + mockErrorData;
66 |
67 | //given
68 | spyOn(loggerMock, 'error').and.callThrough();
69 | httpBackend.whenGET('http://localhost:3000/messages').respond(400, mockErrorData);
70 |
71 | //when
72 | messageSvc.findAll().then(()=> {
73 | }, (reason)=> {
74 | expectedData = reason;
75 | });
76 |
77 | httpBackend.flush();
78 |
79 | //then
80 | expect(expectedData.msg).toEqual(mockErrorData.msg);
81 | expect(loggerMock.error).toHaveBeenCalledWith(expectedMessage);
82 | });
83 |
84 | });
85 |
86 | describe('#find', ()=> {
87 |
88 | beforeEach(()=> {
89 | _inject();
90 | });
91 |
92 | it('should be defined', ()=> {
93 | expect(typeof messageSvc.find).toBe('function');
94 | });
95 |
96 | it('should make $http call', ()=> {
97 |
98 | let expectedData, mockId = 1;
99 |
100 | //given
101 | httpBackend.whenGET(`http://localhost:3000/messages/${mockId}`).respond(200, mockData[0]);
102 |
103 | //when
104 | messageSvc.find(mockId).then((data, status, headers, config)=> {
105 | expectedData = data;
106 | });
107 |
108 | httpBackend.flush();
109 |
110 | //then
111 | expect(expectedData.text).toEqual(mockData[0].text);
112 | });
113 |
114 | it('should handle when $http call failed', ()=> {
115 |
116 | let expectedData,
117 | expectedMessage = 'XHR Failed for MessageService#find.' + mockErrorData,
118 | mockId = 1;
119 |
120 | //given
121 | spyOn(loggerMock, 'error').and.callThrough();
122 | httpBackend.whenGET(`http://localhost:3000/messages/${mockId}`).respond(400, mockErrorData);
123 |
124 | //when
125 | messageSvc.find(mockId).then(()=> {
126 | }, (reason)=> {
127 | expectedData = reason;
128 | });
129 |
130 | httpBackend.flush();
131 |
132 | //then
133 | expect(expectedData.msg).toEqual(mockErrorData.msg);
134 | expect(loggerMock.error).toHaveBeenCalledWith(expectedMessage);
135 | });
136 | });
137 |
138 | afterEach(function () {
139 | httpBackend.verifyNoOutstandingExpectation();
140 | httpBackend.verifyNoOutstandingRequest();
141 | });
142 |
143 | function _inject() {
144 | inject((messageService, $httpBackend, logger)=> {
145 |
146 | messageSvc = messageService;
147 | httpBackend = $httpBackend;
148 | loggerMock = logger;
149 |
150 | });
151 | }
152 | });
--------------------------------------------------------------------------------
/tests/tests.webpack.js:
--------------------------------------------------------------------------------
1 | import 'angular';
2 | import '../app/app';
3 | import 'angular-mocks/angular-mocks';
4 |
5 | var testsContext = require.context('.', true, /.spec$/);
6 | testsContext.keys().forEach(testsContext);
--------------------------------------------------------------------------------
/webpack.build.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Webpack config for builds
3 | */
4 | module.exports = require('./webpack.make.js')({
5 | BUILD: true,
6 | TEST: false
7 | });
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Webpack config for development
3 | */
4 | module.exports = require('./webpack.make.js')({
5 | BUILD: false,
6 | TEST: false
7 | });
--------------------------------------------------------------------------------
/webpack.make.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Modules
4 | var webpack = require('webpack');
5 | var autoprefixer = require('autoprefixer');
6 | var HtmlWebpackPlugin = require('html-webpack-plugin');
7 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
8 |
9 | module.exports = function makeWebpackConfig(options) {
10 | /**
11 | * Environment type
12 | * BUILD is for generating minified builds
13 | * TEST is for generating test builds
14 | */
15 | var BUILD = !!options.BUILD;
16 | var TEST = !!options.TEST;
17 |
18 | var config = {};
19 |
20 | if (TEST) {
21 | config.entry = {}
22 | } else {
23 | config.entry = {
24 | app: './app/app.js'
25 | }
26 | }
27 |
28 | if (TEST) {
29 | config.output = {}
30 | } else {
31 | config.output = {
32 | // Absolute output directory
33 | path: __dirname + '/public',
34 |
35 | publicPath: BUILD ? '/' : 'http://localhost:8080/',
36 |
37 | filename: BUILD ? '[name].[hash].js' : '[name].bundle.js',
38 | //filename: 'bundle.js',
39 |
40 | chunkFilename: BUILD ? '[name].[hash].js' : '[name].bundle.js'
41 | //chunkFilename: BUILD ? 'bundle.js' : 'bundle.js'
42 | }
43 | }
44 |
45 | if (TEST) {
46 | config.devtool = 'inline-source-map';
47 | } else if (BUILD) {
48 | config.devtool = 'source-map';
49 | } else {
50 | config.devtool = 'eval-source-map';
51 | config.debug = false;
52 | config.output.pathinfo = true;
53 | config.cache = true;
54 | config.watch = true;
55 | }
56 |
57 | config.module = {
58 | preLoaders: [],
59 | loaders: [{
60 | test: /\.js$/,
61 | loader: 'babel',
62 | exclude: /node_modules/
63 | }, {
64 | test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
65 | loader: 'file'
66 | }, {
67 | test: /\.html$/,
68 | loader: 'raw'
69 | }]
70 | };
71 |
72 | if (TEST) {
73 | config.module.preLoaders.push({
74 | test: /\.js$/,
75 | exclude: [
76 | /node_modules/,
77 | /bower_components/,
78 | /\.spec\.js$/,
79 | /tests/
80 | ],
81 | loader: 'isparta-instrumenter'
82 | });
83 | }
84 |
85 | var cssLoader = {
86 | test: /\.css$/,
87 | loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss')
88 | };
89 |
90 | if (TEST) {
91 | cssLoader.loader = 'null';
92 | }
93 |
94 | config.module.loaders.push(cssLoader);
95 |
96 | config.postcss = [
97 | autoprefixer({
98 | browsers: ['last 2 version']
99 | })
100 | ];
101 |
102 | config.plugins = [
103 | new ExtractTextPlugin('[name].[hash].css', {
104 | disable: !BUILD || TEST
105 | }),
106 |
107 | // Provide jquery
108 | new webpack.ProvidePlugin({
109 | $: 'jquery',
110 | jQuery: 'jquery',
111 | 'window.jQuery': 'jquery'
112 | })
113 | ];
114 |
115 | if (!TEST) {
116 | config.plugins.push(
117 | new HtmlWebpackPlugin({
118 | template: './public/index.html',
119 | inject: 'body'
120 | })
121 | )
122 | }
123 |
124 | if (BUILD) {
125 | config.plugins.push(
126 | new webpack.NoErrorsPlugin(),
127 | new webpack.optimize.DedupePlugin(),
128 | new webpack.optimize.UglifyJsPlugin()
129 | );
130 | }
131 |
132 | return config;
133 | };
134 |
--------------------------------------------------------------------------------
/webpack.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Webpack config for tests
3 | */
4 | module.exports = require('./webpack.make.js')({
5 | BUILD: false,
6 | TEST: true
7 | });
--------------------------------------------------------------------------------