├── .babelrc
├── .eslintrc.json
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── chromedriver
├── config
├── config
│ ├── config.protractor.js
│ └── protractor.conf.bs.js
├── deploy.sh
├── dist-assets
│ ├── README.md
│ └── manifest.json
├── dogen.js
├── e2e-test.js
├── prod-config.json
└── templates
│ ├── _ngmodule_
│ ├── _ngmodule_.component.js
│ ├── _ngmodule_.component.spec.js
│ ├── _ngmodule_.html
│ ├── _ngmodule_.less
│ └── index.js
│ └── _ngservice_
│ └── _ngservice_.srv.js
├── gulpfile.babel.js
├── index-screenshot.png
├── karma.conf.js
├── package.json
├── protractor.conf.js
├── specs.bundle.js
├── src
├── app.component.js
├── app.html
├── app.js
├── assets
│ ├── favicon.ico
│ └── icon-128x128.png
├── components
│ └── home
│ │ ├── home.component.js
│ │ ├── home.html
│ │ ├── home.less
│ │ ├── home.spec.js
│ │ └── index.js
├── config
│ ├── dev.config.js
│ ├── production.config.js
│ └── test.config.js
├── core
│ ├── components
│ │ ├── e-dropdown
│ │ │ ├── e-dropdown.component.js
│ │ │ ├── e-dropdown.less
│ │ │ ├── e-dropdown.spec.js
│ │ │ ├── e-dropdown.tpl.html
│ │ │ └── index.js
│ │ └── index.js
│ ├── index.js
│ └── services
│ │ ├── index.js
│ │ ├── other.service.srv.js
│ │ ├── some.service.srv.js
│ │ └── some.service.srv.spec.js
├── css
│ ├── core
│ │ ├── global.less
│ │ └── utils.less
│ ├── echoes-variables.less
│ ├── layout
│ │ ├── container.less
│ │ ├── navbar.less
│ │ └── sidebar.less
│ └── style.less
└── index.html
├── tests
├── e2e
│ └── app.spec.js
└── mocks
│ └── home.mock.js
├── webpack.config.js
└── webpack.config.production.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [ "babel-preset-env" ],
3 | "plugins": [
4 | "transform-object-rest-spread",
5 | "transform-class-properties",
6 | "transform-es2015-classes"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "sourceType": "module",
4 | "ecmaVersion": 8,
5 | "ecmaFeatures": {
6 | "modules": true,
7 | "arrowFunctions": true,
8 | "blockBindings": true,
9 | "destructuring": true,
10 | "classes": true,
11 | "spread": true,
12 | "restParams": true,
13 | "templateStrings": true,
14 | "experimentalObjectRestSpread": true,
15 | "objectLiteralShorthandMethods": true,
16 | "objectLiteralShorthandProperties": true,
17 | }
18 | },
19 | "rules": {
20 | "indent": [
21 | "error",
22 | 2
23 | ],
24 | "quotes": [
25 | 2,
26 | "single"
27 | ],
28 | "linebreak-style": [
29 | 2,
30 | "unix"
31 | ],
32 | "semi": [
33 | 2,
34 | "always"
35 | ]
36 | },
37 | "env": {
38 | "es6": true,
39 | "browser": true,
40 | "jasmine": true,
41 | "commonjs": true
42 | },
43 | "globals": {
44 | "angular": true,
45 | "inject": true,
46 | "__dirname": true,
47 | "process": true
48 | },
49 | "extends": "eslint:recommended"
50 | }
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 |
4 | # Folder config file
5 | Desktop.ini
6 |
7 | #Translations
8 | *.mo
9 |
10 | # Mac crap
11 | .DS_Store
12 |
13 | *.sublime-*
14 |
15 | # custom echoes files
16 | node_modules
17 | .tmp/
18 | bower_components/
19 | bundle*.js
20 | bundle*.map
21 | *.css.map
22 | *.tmp.*
23 | style.css
24 | npm-debug.log
25 | vendors*.js
26 | templates*.mdl*.js
27 | dist/
28 | .tags*
29 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - '5.1'
5 |
6 | env:
7 | global:
8 | - GH_REF: github.com/your-user/repo.git
9 | - secure: add-secure-hash
10 | install:
11 | - npm install karma-es6-shim
12 | - npm install
13 | before_script:
14 | # - ./BrowserStackLocal-linux $bs_key localhost,9001,0 &
15 | # - npm start &
16 | # - sleep 5
17 | after_script:
18 | - process.exit()
19 | after_success:
20 | - chmod +x ./gulp/deploy.sh
21 | - ./gulp/deploy.sh
22 | cache:
23 | directories:
24 | - $HOME/.nvm
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Oren Farhi
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SuperNova Starter
2 | 
3 |
4 | An opinionated boilerplate by [Angular ES6/ES2015 Style Guide](https://github.com/orizens/angular-es6-styleguide) by [orizens](http://orizens.com):
5 |
6 | 
7 | ## What's Inside
8 | - Angular 1.5.x
9 | - ui-router 1.x (component support)
10 | - Karma & Jasmine
11 | - Webpack - configured with:
12 | - ES6
13 | - LESS
14 | - ng-templates
15 | - ng-inject
16 | - sourcemaps for development and production
17 | - production build
18 | - fonts loader
19 | - bootstrap
20 | - font-awesome
21 | - npm scripts for development, testing (bdd) and production
22 | - modules scaffold with gulp-dogen
23 | - FULLSTACK: json-server for mocking backend api during development - available in [fullstack branch](https://github.com/orizens/supernova-angular-1.5.x-es6-starter/tree/fullstack)
24 |
25 | ## Quick Start
26 | - nodejs >= 5, npm > 3
27 | - clone this repo
28 |
29 | ```shell
30 | # clone repo
31 | # --depth 1 removes all but one .git commit history
32 | git clone --depth 1 https://github.com/orizens/supernova-angular-1.5.x-es6-starter.git
33 |
34 | # change directory to our repo
35 | cd supernova-angular-1.5.x-es6-starter
36 |
37 | # install dependencies with npm
38 | npm install
39 |
40 | # start dev server
41 | npm start
42 | navigate to http://localhost:9001 in your browser for the front end
43 |
44 | ```
45 |
46 | ## with fullstack branch
47 | in the **fullstack** branch, the ```npm start``` command also starts the json-server process.
48 | navigate to http://localhost:3000 in your browser to view the available routes in the local server api which is served from **[tests/mocks/db.json](tests/mocks/db.json)**.
49 | For more info: [json-server documentation](https://github.com/typicode/json-server)
50 |
51 | ## File Structure
52 | ```
53 | supernova-angular-1.5.x-es6-starter/
54 | ├──config/ * our configuration
55 | | ├──config/ * saved for e2e and remote e2e testing
56 | | ├──dist-assets/ * static files for production - these are copied to dist after with prod task
57 | | ├──templates/ * gulp-dogen modules templates for scaffolding modules
58 | | ├──deploy.sh * script used to deploy dist to github:gh-pages branch with TravisCI
59 | │ ├──dogen.js * gulp task to configure templates for scaffolding
60 | │ ├──e2e-test.js * saved for configuring e2e test runner
61 | │ └──server.js * gulp helper task to run server for dist
62 | │
63 | ├──src/ * source files of the whole app
64 | | ├──app.js * entry file for the app
65 | │ │
66 | | ├──index.html * main index page (app.js and vendors.js are added automatically )
67 | │ │
68 | │ ├──components/ * all SMART components should be created here
69 | │ │ ├──home/ * an example for a smart component: includes less
70 | │ │
71 | │ └──core/ * core layer of the app
72 | │ │ ├──components/ * all dumb components should be created here
73 | │ │ ├──services/ * all services of the app should be created here so SMART components can import it
74 | │ │ ├──css/ * app wide less styles
75 | │ │ ├──config/ * config phase for each environment
76 | │ │
77 | ├──tests/ * currently saved for mock files and e2e tests
78 | │
79 | ├──.eslintrc.json * eslint config
80 | ├──package.json * what npm uses to manage it's dependencies
81 | └──specs.bundle.js * used by karma to collect spec files to run tests
82 | └──webpack.config.js * webpack configuration file for development
83 | └──webpack.config.production.js * webpack configuration file for production
84 |
85 | ```
86 |
87 | ## Modules Scaffolding
88 | this package uses **gulp-dogen** for scaffolding.
89 | There are 2 templates in **config/templates**:
90 | 1. ngmodule
91 | 2. ngservice
92 |
93 | to scaffold, run in terminal:
94 | ```shell
95 | # to scaffold new ng component in src/components
96 | npm run dogen -- --ngmodule the-name-of-module
97 | # to scaffold new ng service in src/core/services
98 | npm run dogen -- --ngservice the-name-of-service
99 | ```
100 |
101 | ## Adding 3rd party / vendors
102 | the build creates 2 bundled files:
103 | 1. app.bundle.js - includes the files created by you
104 | 2. vendors.bundle.js - includes the files created by the list configured in **webpack.config.js**. Simply add the npm package name to the list of vendors.
105 |
106 | ## Testing with BDD
107 | to run tests while developing use:
108 | ```npm run bdd```
109 | to run tests once use:
110 | ```npm test```
111 | running unit tests in debug mode - ```npm run testd```
112 |
113 | ## Creating A Production Build
114 | A production build is configured by webpack.config.production.js.
115 | to create a build, simply run:
116 | ```npm run prod```
117 | This creates a **dist** directory with a minified and bundled version of the app.
118 |
119 | You can run a local server to check the dist build by ```npm run prod:serve```. It is based on lite-server and can be extended through **config/prod-config.json** by the [various options available to it](https://github.com/johnpapa/lite-server).
120 |
121 | ## Plans To Add
122 | - ~~testing for all files - Currently testing only core directory~~
123 | - protractor / e2e
124 | - browserstack configuration / remote e2e
125 | - ngMaterial
126 | - ~~component router~~ no version for ng1 yet
127 | - rxjs (?) - still uses **$scope** to create observables
128 | - ngRedux
129 |
130 | ## Contributing
131 | To request a feature - please open an issue.
132 | Better than this, open a PR with your proposed update.
133 |
134 | Thanks.
135 |
--------------------------------------------------------------------------------
/chromedriver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orizens/supernova-angular-1.5.x-es6-starter/adabcdf68b10152c9a66f76de0ab19bb7095b5f5/chromedriver
--------------------------------------------------------------------------------
/config/config/config.protractor.js:
--------------------------------------------------------------------------------
1 | // adopted from Wishtack
2 | // NOTICE:
3 | // add these to dev dependencies if testing will be done
4 | // with browserstack
5 | // "wt-protractor-runner": "^0.2.2",
6 | // "wt-protractor-utils": "^1.0.0"
7 |
8 | import extend from 'node.extend';
9 | import protractorUtils from 'wt-protractor-utils';
10 |
11 | export function configProtractor() {
12 | var browserstack = extend(true /* Deep copy. */, {}, protractorUtils.platform.browserstack);
13 |
14 | var protractorBaseConfig = {
15 | specs: __dirname + '/../../tests/e2e/*.js'
16 | };
17 |
18 | // browsertstack.capabilities['browserstack.user'] = process.env.BROWSERSTACK_USER;
19 | browserstack.capabilities['browserstack.user'] = process.env.bs_user;
20 | // browserstack.capabilities['browserstack.key'] = process.env.BROWSERSTACK_KEY;
21 | browserstack.capabilities['browserstack.key'] = process.env.bs_key;
22 | browserstack.capabilities['browserstack.local'] = 'true';
23 |
24 | return {
25 | configList: [
26 | /* OS X / Chrome. */
27 | protractorUtils.mergeConfig({
28 | configList: [
29 | protractorBaseConfig,
30 | browserstack,
31 | protractorUtils.os.osx,
32 | protractorUtils.browser.chrome
33 | ]
34 | }),
35 | // /* OS X / Safari. */
36 | // protractorUtils.mergeConfig({
37 | // configList: [
38 | // protractorBaseConfig,
39 | // browserstack,
40 | // protractorUtils.os.osx,
41 | // protractorUtils.browser.safari
42 | // ]
43 | // }),
44 | /* Windows / Chrome. */
45 | protractorUtils.mergeConfig({
46 | configList: [
47 | protractorBaseConfig,
48 | browserstack,
49 | protractorUtils.os.windows,
50 | protractorUtils.browser.chrome
51 | ]
52 | })
53 | /* Windows / Internet Explorer. */
54 | // protractorUtils.mergeConfig({
55 | // configList: [
56 | // protractorBaseConfig,
57 | // browserstack,
58 | // protractorUtils.os.windows,
59 | // protractorUtils.browser.internetExplorer
60 | // ]
61 | // }),
62 | // /* Android. */
63 | // protractorUtils.mergeConfig({
64 | // configList: [
65 | // protractorBaseConfig,
66 | // browserstack,
67 | // protractorUtils.os.android
68 | // ]
69 | // }),
70 | /* iPad. */
71 | // protractorUtils.mergeConfig({
72 | // configList: [
73 | // protractorBaseConfig,
74 | // browserstack,
75 | // protractorUtils.device.iPad
76 | // ]
77 | // })
78 | // /* iPhone. */
79 | // protractorUtils.mergeConfig({
80 | // configList: [
81 | // protractorBaseConfig,
82 | // browserstack,
83 | // protractorUtils.device.iPhone
84 | // ]
85 | // })
86 | ]
87 | };
88 | }
89 |
--------------------------------------------------------------------------------
/config/config/protractor.conf.bs.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | capabilities: {
3 | 'browserstack.user' : process.env.bs_user,
4 | 'browserstack.key' : process.env.bs_key,
5 |
6 | // Needed for testing localhost
7 | 'browserstack.local' : 'true',
8 | 'browserstack.debug': 'true',
9 |
10 | // Settings for the browser you want to test
11 | 'browserName' : 'Chrome',
12 | 'browser_version' : '39.0',
13 | 'os' : 'OS X',
14 | 'os_version' : 'Mavericks',
15 | 'resolution' : '1024x768'
16 | },
17 |
18 | framework: 'jasmine2',
19 |
20 | // Browserstack's selenium server address
21 | seleniumAddress: 'http://hub.browserstack.com/wd/hub',
22 |
23 | // Pattern for finding spec files
24 | specs: ['../../tests/e2e/**/*spec.js'],
25 |
26 | onPrepare: function() {
27 | var SpecReporter = require('jasmine-spec-reporter');
28 | // add jasmine spec reporter
29 | jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true}));
30 | },
31 |
32 | jasmineNodeOpts: {
33 | print: function() {}
34 | }
35 | }
--------------------------------------------------------------------------------
/config/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | if [ "$TRAVIS_BRANCH" == "master" ]; then
3 | git config --global user.email "your-github-email@email.com"
4 | git config --global user.name "travis-ci"
5 | npm run release
6 | cd dist
7 | git init
8 | git add .
9 | git commit -m "deployed commit ${TRAVIS_COMMIT} from travis"
10 | git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master:gh-pages > /dev/null 2>&1
11 | fi
12 |
--------------------------------------------------------------------------------
/config/dist-assets/README.md:
--------------------------------------------------------------------------------
1 | # Assets For production
2 | various assets can be kept here - these will be added to production bundle through webpack config.
3 |
--------------------------------------------------------------------------------
/config/dist-assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "My App",
3 | "short_name": "my app",
4 | "description": "the app features...",
5 | "icons": [{
6 | "src": "assets/icon-128x128.png",
7 | "sizes": "128x128",
8 | "type": "image/png"
9 | }],
10 | "start_url": "/index.html?utm_source=web_app_manifest",
11 | "display": "standalone"
12 | }
13 |
--------------------------------------------------------------------------------
/config/dogen.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var dogen = require('gulp-dogen');
3 |
4 | dogen.config({
5 | templatesPath: 'config/templates',
6 | gulp: gulp
7 | });
8 |
9 | // This will create this gulp task as:
10 | // gulp dogen --ngmodule the-name-of-module
11 | dogen.task('ngmodule', 'src/components/');
12 | dogen.task('ngservice', 'src/core/services');
13 |
--------------------------------------------------------------------------------
/config/e2e-test.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import protractorRunner from 'wt-protractor-runner';
3 |
4 | gulp.task('test:e2e', (done) => {
5 | // Load config
6 | var config = require('./config/config.protractor')();
7 | // Run tests
8 | protractorRunner(config)(done);
9 |
10 | });
11 |
--------------------------------------------------------------------------------
/config/prod-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 9002,
3 | "files": ["./dist/**/*.{html,htm,css,js}"],
4 | "server": { "baseDir": "./dist" }
5 | }
--------------------------------------------------------------------------------
/config/templates/_ngmodule_/_ngmodule_.component.js:
--------------------------------------------------------------------------------
1 | import template from './_ngmodule_.html';
2 |
3 | export let =ngmodule=Component = {
4 | templateUrl: template,
5 | selector: '=ngmodule=',
6 | bindings: {
7 |
8 | },
9 | controller: class =ngmodule=Ctrl {
10 | /* @ngInject */
11 | constructor () {
12 | // Object.assign(this, ...arguments);
13 |
14 | }
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/config/templates/_ngmodule_/_ngmodule_.component.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import _ngmodule_Module, { =ngmodule=Component } from './index.js';
3 |
4 | describe('_ngmodule_ Component', () => {
5 | var ctrl;
6 |
7 | beforeEach(window.module(_ngmodule_Module));
8 |
9 | beforeEach(window.inject(($componentController) => {
10 | ctrl = $componentController(=ngmodule=Component.selector, {
11 |
12 | });
13 | }));
14 |
15 | it('should do what it is supposed to do', () => {
16 | const expected = '';
17 | const actual = '';
18 | expect(actual).toMatch(expected);
19 | });
20 |
21 | });
22 |
--------------------------------------------------------------------------------
/config/templates/_ngmodule_/_ngmodule_.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/templates/_ngmodule_/_ngmodule_.less:
--------------------------------------------------------------------------------
1 | // actions cell
2 | ._ngmodule_ {
3 |
4 | }
5 |
--------------------------------------------------------------------------------
/config/templates/_ngmodule_/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { =ngmodule=Component } from './_ngmodule_.component';
3 | import '_ngmodule_.less';
4 |
5 | export * from './_ngmodule_.component';
6 |
7 | export default angular.module('_ngmodule_', [
8 |
9 | ])
10 | .config(config)
11 | .component(=ngmodule=Component.selector, =ngmodule=Component)
12 | .name;
13 | /* @ngInject */
14 | function config () {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/config/templates/_ngservice_/_ngservice_.srv.js:
--------------------------------------------------------------------------------
1 | /* @ngInject */
2 | export default function _ngservice_(dependencies) {
3 | var service = {
4 | func: func
5 | };
6 | return service;
7 |
8 | ////////////////
9 |
10 | function func() {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 |
3 | // import './gulp/e2e-test.js';
4 | import './config/dogen.js';
5 |
--------------------------------------------------------------------------------
/index-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orizens/supernova-angular-1.5.x-es6-starter/adabcdf68b10152c9a66f76de0ab19bb7095b5f5/index-screenshot.png
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const isDebug = process.env.DEBUG || false;
6 | const isTravis = process.env.TRAVIS || false;
7 | const KARMA_SINGLE_RUN_FLAG = process.argv.filter(s => s.includes('single-run'));
8 | const RUN_ONCE = process.env.BDD || isDebug ? false : KARMA_SINGLE_RUN_FLAG ?
9 | true : false;
10 | const browsers = isTravis ? ['PhantomJS'] : [isDebug ? 'Chrome' : 'PhantomJS'];
11 |
12 | const options = {
13 | basePath: '',
14 | browsers: browsers,
15 | frameworks: ['jasmine'],
16 | autoWatch: true,
17 | singleRun: RUN_ONCE,
18 | files: [
19 | // es6-shim is currently needed for phantomjs
20 | './node_modules/es6-shim/es6-shim.js',
21 | 'specs.bundle.js',
22 | './src/core/**/*.spec.js',
23 | './src/components/**/*.spec.js'
24 | ],
25 | preprocessors: {
26 | 'specs.bundle.js': ['webpack', 'sourcemap'],
27 | './src/core/**/*.spec.js': ['webpack', 'sourcemap'],
28 | './src/components/**/*.spec.js': ['webpack', 'sourcemap']
29 | },
30 | webpack: {
31 | devtool: 'inline-source-map',
32 | module: {
33 | loaders: [{
34 | test: /\.js$/,
35 | exclude: /(node_modules)/,
36 | loaders: ['babel']
37 | }, {
38 | test: /\.html$/,
39 | loader: 'ngtemplate!html',
40 | exclude: /(index)/
41 | }, {
42 | test: /\.less$/,
43 | loader: ExtractTextPlugin.extract('css?sourceMap!' +
44 | 'less?sourceMap')
45 | }]
46 | },
47 | plugins: [
48 | new ExtractTextPlugin('[name].[chunkhash].style.css')
49 | ],
50 | resolve: {}
51 | },
52 | webpackMiddleware: {
53 | noInfo: true
54 | },
55 | plugins: [
56 | require('karma-webpack'),
57 | require('karma-sourcemap-loader'),
58 | 'karma-phantomjs-launcher',
59 | 'karma-chrome-launcher',
60 | 'karma-jasmine',
61 | // 'karma-html-reporter',
62 | // 'karma-spec-reporter',
63 | 'karma-mocha-reporter',
64 | 'karma-clear-screen-reporter'
65 | // 'karma-browserstack-launcher'
66 | ],
67 | reporters: [
68 | // 'progress',
69 | // 'spec',
70 | // 'coverage',
71 | 'mocha',
72 | 'clear-screen'
73 | ],
74 | mochaReporter: {
75 | // output: 'autowatch'
76 | }
77 | // the default configuration
78 | // htmlReporter: {
79 | // outputDir: 'karma_html',
80 | // templatePath: './node_modules/karma-html-reporter/jasmine_template.html'
81 | // }
82 | };
83 |
84 | // var browserStackOptions = {
85 | // // global config of your BrowserStack account
86 | // browserStack: {
87 | // username: process.env.bs_user,
88 | // accessKey: process.env.bs_key
89 | // },
90 |
91 | // // define browsers
92 | // customLaunchers: {
93 | // bs_chrome_mac: {
94 | // base: 'BrowserStack',
95 | // browser: 'chrome',
96 | // browser_version: '39.0',
97 | // os: 'OS X',
98 | // os_version: 'Mountain Lion'
99 | // },
100 | // bs_chrome_windows: {
101 | // base: 'BrowserStack',
102 | // browser : 'chrome',
103 | // browser_version : '39.0',
104 | // os : 'Windows',
105 | // os_version : '8'
106 | // }
107 | // },
108 |
109 | // browsers: ['bs_chrome_mac', 'bs_chrome_windows']
110 | // };
111 |
112 | module.exports = function(config) {
113 | // if (isTravis) {
114 | // Object.keys(browserStackOptions).forEach(function (key) {
115 | // options[key] = browserStackOptions[key];
116 | // });
117 | // }
118 | config.set(options);
119 | };
120 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "supernova-angular-1.5.x-es6-starter",
3 | "version": "0.0.3",
4 | "description": "starter kit for angular > 1.5.x, > es6, karma and jasmine",
5 | "main": "src/index.html",
6 | "directories": {},
7 | "scripts": {
8 | "e2e": "protractor protractor.conf.js",
9 | "e2ed": "protractor debug protractor.conf.js",
10 | "e2e:remote": "protractor ./gulp/config/protractor.conf.bs.js",
11 | "clean": "npm run clean:dist && rimraf node_modules/",
12 | "clean:dist": "rimraf dist/",
13 | "test": "karma start",
14 | "testd": "DEBUG=true npm test",
15 | "bdd": "BDD=true npm test",
16 | "start": "npm run dev",
17 | "wpw": "webpack -d --watch",
18 | "dogen": "gulp dogen",
19 | "dev": "webpack-dev-server --hot --inline --progress --colors --content-base src --port 9001",
20 | "prod": "npm run clean:dist && webpack --config=webpack.config.production.js -p",
21 | "prod:serve": "lite-server -c config/prod-config.json"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/orizens/supernova-angular-1.5.x-es6-starter.git"
26 | },
27 | "keywords": [
28 | "angular",
29 | "karma",
30 | "es6",
31 | "jasmine"
32 | ],
33 | "author": "Oren Farhi",
34 | "license": "MIT",
35 | "readmeFilename": "README.md",
36 | "devDependencies": {
37 | "angular": "^1.5.8",
38 | "angular-animate": "^1.5.8",
39 | "angular-local-storage": "^0.2.2",
40 | "angular-mocks": "^1.5.8",
41 | "angular-route": "^1.5.8",
42 | "angular-sanitize": "^1.5.8",
43 | "angular-ui-bootstrap": "^1.1.2",
44 | "angular-ui-router": "^1.0.0-beta.2",
45 | "babel-core": "^6.2.1",
46 | "babel-loader": "^6.2.1",
47 | "babel-plugin-transform-class-properties": "^6.24.1",
48 | "babel-plugin-transform-es2015-classes": "^6.24.1",
49 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
50 | "babel-preset-env": "^1.6.0",
51 | "bootstrap": "^3.3.6",
52 | "copy-webpack-plugin": "^3.0.1",
53 | "css-loader": "^0.23.1",
54 | "es6-shim": "^0.35.1",
55 | "eslint": "^2.8.0",
56 | "eslint-config-standard": "^4.4.0",
57 | "eslint-plugin-standard": "^1.3.1",
58 | "extract-text-webpack-plugin": "^1.0.1",
59 | "file-loader": "^0.8.5",
60 | "font-awesome": "^4.5.0",
61 | "font-awesome-webpack": "0.0.4",
62 | "gulp": "3.9.1",
63 | "gulp-dogen": "0.1.9",
64 | "html-loader": "^0.4.0",
65 | "html-webpack-plugin": "^2.17.0",
66 | "jasmine-core": "^2.4.1",
67 | "jasmine-spec-reporter": "^2.2.3",
68 | "karma": "^0.13.15",
69 | "karma-babel-preprocessor": "^6.0.1",
70 | "karma-chrome-launcher": "^0.2.1",
71 | "karma-clear-screen-reporter": "^1.0.0",
72 | "karma-jasmine": "^0.3.6",
73 | "karma-json-fixtures-preprocessor": "0.0.5",
74 | "karma-mocha-reporter": "^1.1.1",
75 | "karma-phantomjs-launcher": "^1.0.0",
76 | "karma-sourcemap-loader": "^0.3.7",
77 | "karma-spec-reporter": "0.0.13",
78 | "karma-webpack": "^1.7.0",
79 | "less": "^2.7.1",
80 | "less-loader": "^2.2.3",
81 | "lite-server": "^2.2.2",
82 | "ng-annotate-loader": "^0.1.0",
83 | "ng-annotate-webpack-plugin": "^0.1.2",
84 | "ngtemplate-loader": "^1.3.1",
85 | "phantomjs-prebuilt": "^2.1.7",
86 | "rimraf": "^2.5.4",
87 | "run-sequence": "^1.1.0",
88 | "selenium-webdriver": "^2.47.0",
89 | "style-loader": "^0.13.1",
90 | "url-loader": "^0.5.7",
91 | "webpack": "^1.13.1",
92 | "webpack-dev-server": "^1.14.1"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | framework: 'jasmine2',
3 |
4 | capabilities: {
5 | // 'browserName': 'phantomjs',
6 | 'browserName': 'chrome',
7 | },
8 | // 'phantomjs.binary.path': require('phantomjs').path,
9 | seleniumAddress: 'http://localhost:4444/wd/hub',
10 | specs: ['tests/e2e/*spec.js'],
11 | directConnect: true,
12 |
13 | onPrepare: function() {
14 | var SpecReporter = require('jasmine-spec-reporter');
15 | // add jasmine spec reporter
16 | jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true}));
17 | },
18 | jasmineNodeOpts: {
19 | print: function() {}
20 | }
21 | };
--------------------------------------------------------------------------------
/specs.bundle.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable */
2 | import angular from 'angular';
3 | import mocks from 'angular-mocks';
4 | /*eslint-enable */
--------------------------------------------------------------------------------
/src/app.component.js:
--------------------------------------------------------------------------------
1 | import template from './app.html';
2 |
3 | export let AppComponent = {
4 | templateUrl: template,
5 | selector: 'app',
6 | bindings: {},
7 | controller: class AppCtrl {
8 | /* @ngInject */
9 | constructor($state) {
10 | Object.assign(this, { $state });
11 | }
12 |
13 | $onInit() {
14 |
15 | }
16 | }
17 | };
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | import './css/style.less';
2 | require('font-awesome-webpack');
3 |
4 | import angular from 'angular';
5 | // import Angular2To1 from 'angular2to1';
6 | import AngularUiRouter from 'angular-ui-router';
7 | import AngularAnimate from 'angular-animate';
8 | import AngularSanitize from 'angular-sanitize';
9 | import AngularBootstrap from 'angular-ui-bootstrap';
10 | /*eslint-disable */
11 | import LocalStorageModule from 'angular-local-storage';
12 | /*eslint-enable */
13 | import AppCore from './core';
14 | import { AppComponent } from './app.component';
15 |
16 | import Home from './components/home';
17 |
18 | const appName = 'myApp';
19 |
20 | angular.module(appName, [
21 | // framework wide components
22 | AngularUiRouter,
23 | AngularAnimate,
24 | AngularSanitize,
25 | AngularBootstrap,
26 |
27 | // services
28 | 'LocalStorageModule',
29 | AppCore,
30 |
31 | // ui-components
32 | Home
33 | ])
34 | .config(config)
35 | .component(AppComponent.selector, AppComponent);
36 |
37 | /* @ngInject */
38 | function config ($stateProvider, $urlRouterProvider, localStorageServiceProvider) {
39 |
40 | localStorageServiceProvider.setPrefix(appName);
41 |
42 | $stateProvider
43 | .state('home', {
44 | url: '/',
45 | component: 'home'
46 | });
47 |
48 | $urlRouterProvider.otherwise('/');
49 | }
50 |
51 | angular.element(document).ready(() => {
52 | angular.bootstrap(document, [appName]);
53 | });
54 |
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orizens/supernova-angular-1.5.x-es6-starter/adabcdf68b10152c9a66f76de0ab19bb7095b5f5/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/assets/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orizens/supernova-angular-1.5.x-es6-starter/adabcdf68b10152c9a66f76de0ab19bb7095b5f5/src/assets/icon-128x128.png
--------------------------------------------------------------------------------
/src/components/home/home.component.js:
--------------------------------------------------------------------------------
1 | import './home.less';
2 | import template from './home.html';
3 |
4 | export let HomeComponent = {
5 | templateUrl: template,
6 | selector: 'home',
7 | bindings: {},
8 | /* @ngInject */
9 | controller: class HomeCtrl {
10 | /* @ngInject */
11 | constructor($state) {
12 | // TODO - constructor arguments that should be available on "this"
13 | // should be added to the assign object
14 | Object.assign(this, { $state });
15 | this.title = 'SuperNova';
16 | this.note = 'Angular 1.5x, Es6, Karma, Jasmine & Webpack, ui-router';
17 | }
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/home/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
52 |
53 |
54 |
55 |
SuperNova Starter
56 |
What's Inside?
57 |
58 | -
59 | Opinionated structure by Angular ES6/ES2015 Style Guide
60 |
61 | - Angular 1.5.x
62 | - Karma & Jasmine
63 | -
64 | Webpack - configured with:
65 |
66 | - ES6
67 | - LESS
68 | - ng-templates
69 | - ng-inject
70 | - sourcemaps for development and production
71 | - production build
72 | - fonts loader
73 |
74 |
75 | - Bootstrap
76 | - Font Awesome
77 | - npm scripts for development, testing (bdd) and production
78 | - modules scaffold with gulp-dogen
79 |
80 |
81 |
82 |
83 |
84 | Template Design By KeenThemes
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/components/home/home.less:
--------------------------------------------------------------------------------
1 | @import url(../../css/core/global.less);
2 |
3 | @media (min-width: 320px) {
4 | .home {
5 |
6 | }
7 | }
8 |
9 | @media (min-width: 768px) {
10 | .home {
11 |
12 | }
13 | }
14 |
15 | /***
16 | User Profile Sidebar by @keenthemes
17 | A component of Metronic Theme - #1 Selling Bootstrap 3 Admin Theme in Themeforest: http://j.mp/metronictheme
18 | Licensed under MIT
19 | ***/
20 |
21 | body {
22 | background: #F1F3FA;
23 | }
24 |
25 | /* Profile container */
26 | .profile {
27 | margin: 20px 0;
28 | }
29 |
30 | /* Profile sidebar */
31 | .profile-sidebar {
32 | padding: 20px 0 10px 0;
33 | background: #fff;
34 | }
35 |
36 | .profile-userpic img {
37 | float: none;
38 | margin: 0 auto;
39 | width: 50%;
40 | height: 50%;
41 | -webkit-border-radius: 50% !important;
42 | -moz-border-radius: 50% !important;
43 | border-radius: 50% !important;
44 | }
45 |
46 | .profile-usertitle {
47 | text-align: center;
48 | margin-top: 20px;
49 | }
50 |
51 | .profile-usertitle-name {
52 | color: #5a7391;
53 | font-size: 16px;
54 | font-weight: 600;
55 | margin-bottom: 7px;
56 | }
57 |
58 | .profile-usertitle-job {
59 | text-transform: uppercase;
60 | color: #5b9bd1;
61 | font-size: 12px;
62 | font-weight: 600;
63 | margin-bottom: 15px;
64 | }
65 |
66 | .profile-userbuttons {
67 | text-align: center;
68 | margin-top: 10px;
69 | }
70 |
71 | .profile-userbuttons .btn {
72 | text-transform: uppercase;
73 | font-size: 11px;
74 | font-weight: 600;
75 | padding: 6px 15px;
76 | margin-right: 5px;
77 | }
78 |
79 | .profile-userbuttons .btn:last-child {
80 | margin-right: 0px;
81 | }
82 |
83 | .profile-usermenu {
84 | margin-top: 30px;
85 | }
86 |
87 | .profile-usermenu ul li {
88 | border-bottom: 1px solid #f0f4f7;
89 | }
90 |
91 | .profile-usermenu ul li:last-child {
92 | border-bottom: none;
93 | }
94 |
95 | .profile-usermenu ul li a {
96 | color: #93a3b5;
97 | font-size: 14px;
98 | font-weight: 400;
99 | }
100 |
101 | .profile-usermenu ul li a i {
102 | margin-right: 8px;
103 | font-size: 14px;
104 | }
105 |
106 | .profile-usermenu ul li a:hover {
107 | background-color: #fafcfd;
108 | color: #5b9bd1;
109 | }
110 |
111 | .profile-usermenu ul li.active {
112 | border-bottom: none;
113 | }
114 |
115 | .profile-usermenu ul li.active a {
116 | color: #5b9bd1;
117 | background-color: #f6f9fb;
118 | border-left: 2px solid #5b9bd1;
119 | margin-left: -2px;
120 | }
121 |
122 | /* Profile Content */
123 | .profile-content {
124 | padding: 20px;
125 | background: #fff;
126 | min-height: 460px;
127 | }
128 |
--------------------------------------------------------------------------------
/src/components/home/home.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import HomeModule, { HomeComponent } from './index.js';
3 | // if a mock json object is needed for tests
4 | // import HomeMock from '../../../tests/mocks/home.mock.json';
5 |
6 | describe('Home Component', () => {
7 | let ctrl;
8 |
9 | beforeEach(window.module(HomeModule));
10 |
11 | beforeEach(window.inject(($componentController) => {
12 | ctrl = $componentController(HomeComponent.selector, {
13 | $state: {}
14 | });
15 | }));
16 |
17 | it('should have a title', () => {
18 | const expected = 'SuperNova';
19 | const actual = ctrl.title;
20 | expect(actual).toMatch(expected);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/home/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import uiRouter from 'angular-ui-router';
3 | import AppCore from '../../core';
4 | import { HomeComponent } from './home.component';
5 |
6 | export * from './home.component';
7 |
8 | export default angular.module('home', [
9 | AppCore,
10 | uiRouter
11 | ])
12 | .config(config)
13 | .component(HomeComponent.selector, HomeComponent)
14 | .name;
15 | // .config(config);
16 |
17 | /* @ngInject */
18 | function config ($stateProvider) {
19 | // $stateProvider
20 | // .state('home', {
21 | // url: '/home',
22 | // template: ''
23 | // });
24 | }
25 |
--------------------------------------------------------------------------------
/src/config/dev.config.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | // TODO - make app name a nodejs ENV variable
4 | angular.module('myApp')
5 | .config(config);
6 |
7 | /* @ngInject */
8 | function config ($compileProvider) {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/config/production.config.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | angular.module('myApp')
4 | .config(config);
5 |
6 | /* @ngInject */
7 | function config ($compileProvider) {
8 | $compileProvider.debugInfoEnabled(false);
9 | }
10 |
--------------------------------------------------------------------------------
/src/config/test.config.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 |
3 | angular.module('myApp')
4 | .config(config);
5 |
6 | /* @ngInject */
7 | function config ($compileProvider) {
8 | $compileProvider.debugInfoEnabled(true);
9 | }
10 |
--------------------------------------------------------------------------------
/src/core/components/e-dropdown/e-dropdown.component.js:
--------------------------------------------------------------------------------
1 | import './e-dropdown.less';
2 | import template from './e-dropdown.tpl.html';
3 |
4 | // Usage:
5 | //
8 | // Creates:
9 | //
10 | export let eDropdown = {
11 | selector: 'eDropdown',
12 | replace: true,
13 | templateUrl: template,
14 | bindings: {
15 | label: '@',
16 | icon: '@',
17 | items: '<',
18 | onSelect: '&',
19 | selected: '@'
20 | },
21 | controllerAs: 'vm',
22 | /* @ngInject */
23 | controller: function() {
24 | // var vm = this;
25 | this.activeIndex = this.selected !== '' ? parseInt(this.selected) : 0;
26 | this.handleClick = handleClick;
27 | this.status = {
28 | isOpen: false
29 | };
30 | this.displayLabel = this.items[this.activeIndex];
31 |
32 | function handleClick(item, $index) {
33 | this.activeIndex = $index;
34 | this.displayLabel = this.items[this.activeIndex];
35 | this.onSelect({
36 | item: item,
37 | index: $index
38 | });
39 | }
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/src/core/components/e-dropdown/e-dropdown.less:
--------------------------------------------------------------------------------
1 | .e-dropdown {
2 | padding: 10px 15px;
3 | color: #777;
4 | }
5 | .uib-dropdown-menu {
6 | li {
7 | cursor: pointer;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/core/components/e-dropdown/e-dropdown.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import eDropdownModule from './index.js';
3 | // import eDropdownComponent from './e-dropdown.component';
4 |
5 | describe('Unit: dropdown directive - ', () => {
6 | let element, scope, compile, find;
7 | let dropdownHtml = `
8 |
13 | `;
14 | beforeEach(window.module(eDropdownModule));
15 |
16 | beforeEach(window.inject(($compile, $rootScope) => {
17 | compile = $compile;
18 | scope = $rootScope.$new();
19 | scope.onPresetChange = (item) => item;
20 | scope.presets = ['All', 'Albums', 'Live'];
21 | element = angular.element(dropdownHtml);
22 | $compile(element)(scope);
23 | find = (s) => element[0].querySelectorAll(s);
24 | scope.$digest();
25 | }));
26 |
27 | it('should render a dropdown element', () => {
28 | const actual = find('.e-dropdown').length;
29 | const expected = 1;
30 | expect(actual).toEqual(expected);
31 | });
32 |
33 | it('should render items if given presets', () => {
34 | expect(find('li').length).toBe(scope.presets.length);
35 | });
36 |
37 | it('should render a "tag" icon', () => {
38 | expect(find('i[class*="-tag"]').length).toBe(1);
39 | });
40 |
41 | it('should render the label according to the "label" attribute', () => {
42 | expect(find('.dropdown-toggle')[0].innerText.trim()).toBe('Preset');
43 | });
44 |
45 | it('should call a function when select has changed', () => {
46 | spyOn(scope, 'onPresetChange');
47 | element.isolateScope().vm.handleClick([scope.presets[0], 0]);
48 | expect(scope.onPresetChange).toHaveBeenCalled();
49 | });
50 |
51 | it('should call a function with the selected item when select has changed', () => {
52 | spyOn(scope, 'onPresetChange');
53 | element.isolateScope().vm.handleClick([scope.presets[0], 0]);
54 | expect(scope.onPresetChange).toHaveBeenCalledWith([scope.presets[0], 0]);
55 | });
56 |
57 | it('should set the selected item as active', () => {
58 | let index = 1;
59 | element.isolateScope().vm.handleClick(scope.presets[index], index);
60 | scope.$digest();
61 | expect(element.find('li').eq(index).hasClass('active')).toBeTruthy();
62 | });
63 |
64 | it('should set a predefined selected index from attribute', () => {
65 | const dropdownWithSelectedIndex = dropdownHtml;
66 | element = angular.element(dropdownWithSelectedIndex);
67 | element[0].setAttribute('selected', 1);
68 | compile(element)(scope);
69 | scope.$digest();
70 | expect(element.find('li').eq(1).hasClass('active')).toBeTruthy();
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/src/core/components/e-dropdown/e-dropdown.tpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{vm.label}}
4 | {{ vm.displayLabel }}
5 |
6 |
7 |
14 |
15 |
--------------------------------------------------------------------------------
/src/core/components/e-dropdown/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import { eDropdown } from './e-dropdown.component';
3 |
4 | export default angular.module('eDropdown', [])
5 | .component(eDropdown.selector, eDropdown)
6 | .name;
7 |
--------------------------------------------------------------------------------
/src/core/components/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import eDropdown from './e-dropdown';
3 |
4 | export default angular.module('core.components', [
5 | eDropdown
6 | ]);
7 |
--------------------------------------------------------------------------------
/src/core/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import components from './components';
3 | import services from './services';
4 |
5 | export default angular.module('app.core', [
6 | components.name,
7 | services.name
8 | ])
9 | .name;
10 |
--------------------------------------------------------------------------------
/src/core/services/index.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | // uncomment if you need a local storage solution
3 | // import LocalStorageModule from 'angular-local-storage';
4 | import SomeService from './some.service.srv.js';
5 | import OtherService from './other.service.srv.js';
6 |
7 | export default angular
8 | .module('core.services', [
9 | // 'LocalStorageModule'
10 | ])
11 | .factory('SomeService', SomeService)
12 | .service('OtherService', OtherService)
13 | ;
14 |
--------------------------------------------------------------------------------
/src/core/services/other.service.srv.js:
--------------------------------------------------------------------------------
1 | export default class OtherService {
2 | constructor() {
3 | this.data = {
4 | url: 'some-url.com'
5 | };
6 | }
7 |
8 | fetch () {
9 | return this.data.url;
10 | }
11 | }
--------------------------------------------------------------------------------
/src/core/services/some.service.srv.js:
--------------------------------------------------------------------------------
1 | /* @ngInject */
2 | export default function SomeService ($http) {
3 | const url = 'https://www.googleapis.com/youtube/v3/search';
4 | const exports = {
5 | search
6 | };
7 |
8 | return exports;
9 |
10 | ///////////////
11 |
12 | function search (query){
13 | return $http.get(url, {
14 | params: { q: query }
15 | });
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/core/services/some.service.srv.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import HomeMockJson from '../../../tests/mocks/home.mock';
3 | import CoreServicesModule from './index.js';
4 |
5 | describe('Some Service', () => {
6 | let httpBackend, someService;
7 |
8 | beforeEach(window.module(CoreServicesModule.name));
9 |
10 | beforeEach(window.inject(($controller, $injector, $httpBackend) => {
11 | someService = $injector.get('SomeService');
12 | httpBackend = $httpBackend;
13 | httpBackend
14 | .whenGET(/www.googleapis.com/)
15 | .respond(HomeMockJson);
16 | // add spies here
17 | }));
18 |
19 | it('should have a search function', () => {
20 | expect(someService.search).toBeDefined();
21 | });
22 |
23 | it('should open a get request when search', () => {
24 | someService.search();
25 | httpBackend.flush();
26 | httpBackend.expectGET();
27 | });
28 |
29 | it('should search with a query', () => {
30 | const query = 'music albums';
31 | const expected = new RegExp(`q=${query}`);
32 | someService.search(query);
33 | httpBackend.flush();
34 | httpBackend.expectGET(expected);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/src/css/core/global.less:
--------------------------------------------------------------------------------
1 | @import url(../echoes-variables.less);
2 |
3 | .transform(@prop) {
4 | -webkit-transform: @prop;
5 | -moz-transform: @prop;
6 | transform: @prop;
7 | }
8 |
9 | .active-link-style(@color: #555, @bg: #e5e5e5, @shadow: rgba(0,0,0,0.125)){
10 | color: @color;
11 | text-decoration: none;
12 | background-color: @bg;
13 | box-shadow: inset 0 3px 8px @shadow;
14 | }
--------------------------------------------------------------------------------
/src/css/core/utils.less:
--------------------------------------------------------------------------------
1 | /* applying a nicer UX*/
2 | .nicer-ux *,
3 | .ux-maker {
4 | -webkit-transition: all 0.3s ease-out;
5 | -moz-transition: all 0.3s ease-out;
6 | -ms-transition: all 0.3s ease-out;
7 | -o-transition: all 0.3s ease-out;
8 | transition: all 0.3s ease-out;
9 | }
10 |
11 | .ellipsis {
12 | white-space: nowrap;
13 | overflow: hidden;
14 | text-overflow: ellipsis;
15 | }
16 |
17 | .btn-transparent {
18 | background-color: transparent !important;
19 | }
20 | .text-primary {
21 | color: @brand-primary !important;
22 | }
23 |
--------------------------------------------------------------------------------
/src/css/echoes-variables.less:
--------------------------------------------------------------------------------
1 | // bootstrap variables for flat design
2 | //
3 | // Variables
4 | // --------------------------------------------------
5 |
6 | // == Colors
7 | //
8 | //##
9 |
10 | // Color swatches
11 | @turquoise: #1abc9c;
12 | @green-sea: #16a085;
13 |
14 | @emerald: #2ecc71;
15 | @nephritis: #27ae60;
16 |
17 | @peter-river: #3498db;
18 | @belize-hole: #2980b9;
19 |
20 | @amethyst: #9b59b6;
21 | @wisteria: #8e44ad;
22 |
23 | @wet-asphalt: #34495e;
24 | @midnight-blue: #2c3e50;
25 |
26 | @sun-flower: #f1c40f;
27 | @orange: #f39c12;
28 |
29 | @carrot: #e67e22;
30 | @pumpkin: #d35400;
31 |
32 | @alizarin: #e74c3c;
33 | @pomegranate: #c0392b;
34 |
35 | @clouds: #ecf0f1;
36 | @silver: #bdc3c7;
37 |
38 | @concrete: #95a5a6;
39 | @asbestos: #7f8c8d;
40 |
41 | // Grays
42 | // @gray: @concrete;
43 | // @gray-light: @silver;
44 | @inverse: white;
45 |
46 | @gray-darker: lighten(#000, 13.5%); // #222
47 | @gray-dark: lighten(#000, 20%); // #333
48 | @gray: lighten(#000, 33.5%); // #555
49 | @gray-light: lighten(#000, 46.7%); // #777
50 | @gray-lighter: lighten(#000, 93.5%); // #eee
51 | @dark: #000;
52 | // Brand colors
53 | @brand-primary: @turquoise;
54 | @brand-secondary: @green-sea;
55 | @brand-success: @emerald;
56 | @brand-warning: @sun-flower;
57 | @brand-danger: @alizarin;
58 | @brand-info: @peter-river;
59 |
60 |
61 | //== Scaffolding
62 | //
63 | //## Settings for some of the most global styles.
64 |
65 | @body-bg: #ecf0f1;
66 | @text-color: @gray-dark;
67 |
68 | //** Global textual link color.
69 | @link-color: @brand-primary;
70 | @link-hover-color: @turquoise;
71 |
72 |
73 | //== Typography
74 | //
75 | //## Font, line-height for body text, headings, and more.
76 |
77 | @font-family-base: 'Open Sans', Helvetica, Arial, sans-serif;
78 | @font-family-demo: "Helvetica Neue", Helvetica, Arial, sans-serif;
79 | @font-family-monospace: Monaco, Menlo, Consolas, "Courier New", monospace;
80 | @font-family-narrow: 'Open Sans Condensed', 'Open Sans', Helvetica;
81 | @font-size-base: 14px;
82 |
83 | @font-size-h1: floor((@font-size-base * 2.6)); // ~36px
84 | @font-size-h2: floor((@font-size-base * 2.15)); // ~30px
85 | @font-size-h3: ceil((@font-size-base * 1.7)); // ~24px
86 | @font-size-h4: ceil((@font-size-base * 1.25)); // ~18px
87 | @font-size-h5: @font-size-base;
88 | @font-size-h6: ceil((@font-size-base * 0.85)); // ~12px
89 |
90 | @line-height-base: 1.428571429; // 20/14
91 | @line-height-computed: floor(@font-size-base * @line-height-base); // ~31px
92 |
93 | @headings-font-family: inherit;
94 | @headings-font-weight: 700;
95 | @headings-line-height: 1.1;
96 | @headings-color: inherit;
97 |
98 |
99 | //== Iconography
100 | //
101 | //## Specify custom locations of the include Glyphicons icon font.
102 |
103 | @icon-font-path: "../fonts/";
104 | @icon-font-name: "glyphicons-halflings-regular";
105 | @icon-font-svg-id: "glyphicons_halflingsregular";
106 |
107 | //** Icon sizes for use in components
108 | @icon-normal: 16px;
109 | @icon-medium: 18px;
110 | @icon-large: 32px;
111 |
112 |
113 | //== Components
114 | //
115 | //## Define common padding and border radius sizes and more.
116 | @padding-base-vertical: 0px;
117 | @padding-base-horizontal: 6px;
118 |
119 | @padding-large-vertical: 10px;
120 | @padding-large-horizontal: 16px;
121 |
122 | @padding-small-vertical: 5px;
123 | @padding-small-horizontal: 10px;
124 |
125 | @padding-xs-vertical: 1px;
126 | @padding-xs-horizontal: 5px;
127 |
128 | @line-height-large: 1.33;
129 | @line-height-small: 1.5;
130 |
131 | @border-radius-base: 1px;
132 | @border-radius-large: 3px;
133 | @border-radius-small: 0px;
134 |
135 | //** Default font-size in components
136 | @component-font-size-base: ceil(@font-size-base * 0.833); // ~15px
137 |
138 | // Border-radius
139 | @border-radius-base: 4px;
140 | @border-radius-large: 6px;
141 | @border-radius-small: 3px;
142 |
143 |
144 | //== Buttons
145 | //
146 | //## For each of Flat UI's buttons, define text, background, font size and height.
147 |
148 | @btn-font-size-base: @component-font-size-base;
149 | @btn-font-size-xs: ceil(@component-font-size-base * 0.80); // ~12px
150 | @btn-font-size-sm: floor(@component-font-size-base * 0.867); // ~13px
151 | @btn-font-size-lg: ceil(@component-font-size-base * 1.133); // ~17px
152 | @btn-font-size-hg: floor(@component-font-size-base * 1.467); // ~22px
153 |
154 | @btn-line-height-base: 1.4; // ~21px
155 | @btn-line-height-hg: 1.227; // ~27px
156 | @btn-line-height-lg: 1.471; // ~25px
157 | @btn-line-height-sm: 1.385; // ~16px
158 | @btn-line-height-xs: 1.083; // ~13px
159 |
160 | @btn-social-font-size-base: floor(@component-font-size-base * 0.867); // ~13px
161 | @btn-social-line-height-base: 1.077; // ~14px
162 |
163 | @btn-font-weight: normal;
164 |
165 | @btn-default-color: #333;
166 | @btn-default-bg: #fff;
167 | @btn-default-border: #ccc;
168 |
169 | @btn-primary-color: #fff;
170 | @btn-primary-bg: @brand-primary;
171 | @btn-primary-border: darken(@btn-primary-bg, 5%);
172 |
173 | @btn-success-color: #fff;
174 | @btn-success-bg: @brand-success;
175 | @btn-success-border: darken(@btn-success-bg, 5%);
176 |
177 | @btn-info-color: #fff;
178 | @btn-info-bg: @brand-info;
179 | @btn-info-border: darken(@btn-info-bg, 5%);
180 |
181 | @btn-warning-color: #fff;
182 | @btn-warning-bg: @brand-warning;
183 | @btn-warning-border: darken(@btn-warning-bg, 5%);
184 |
185 | @btn-danger-color: #fff;
186 | @btn-danger-bg: @brand-danger;
187 | @btn-danger-border: darken(@btn-danger-bg, 5%);
188 |
189 | @btn-link-disabled-color: @gray-light;
190 |
191 |
192 | //== Forms
193 | //
194 | //##
195 |
196 | @input-font-size-base: @component-font-size-base;
197 | @input-font-size-small: floor(@component-font-size-base * 0.867); // ~13px
198 | @input-font-size-large: ceil(@component-font-size-base * 1.133); // ~17px
199 | @input-font-size-huge: floor(@component-font-size-base * 1.467); // ~22px
200 |
201 | @input-line-height-base: 1.467; // ~22px
202 | @input-line-height-small: 1.462; // ~19px
203 | @input-line-height-large: 1.235; // ~21px
204 | @input-line-height-huge: 1.318; // ~29px
205 |
206 | @input-icon-font-size: ceil(@component-font-size-base * 1.333); // ~20px
207 |
208 | @input-bg: @inverse;
209 | @input-bg-disabled: mix(@gray, white, 10%);
210 |
211 | @input-height-small: 35px;
212 | @input-height-base: 41px;
213 | @input-height-large: 45px;
214 | @input-height-huge: 53px;
215 |
216 | @input-border-radius: @border-radius-large;
217 |
218 | @legend-color: inherit;
219 |
220 |
221 | //== Forms
222 | //
223 | //##
224 |
225 | @input-font-size-base: @component-font-size-base;
226 | @input-font-size-small: floor(@component-font-size-base * 0.867); // ~13px
227 | @input-font-size-large: ceil(@component-font-size-base * 1.133); // ~17px
228 | @input-font-size-huge: floor(@component-font-size-base * 1.467); // ~22px
229 |
230 | @input-line-height-base: 1.467; // ~22px
231 | @input-line-height-small: 1.462; // ~19px
232 | @input-line-height-large: 1.235; // ~21px
233 | @input-line-height-huge: 1.318; // ~29px
234 |
235 | @input-icon-font-size: ceil(@component-font-size-base * 1.333); // ~20px
236 |
237 | @input-bg: @inverse;
238 | @input-bg-disabled: mix(@gray, white, 10%);
239 |
240 | @input-height-small: 35px;
241 | @input-height-base: 41px;
242 | @input-height-large: 45px;
243 | @input-height-huge: 53px;
244 |
245 | @input-border-radius: @border-radius-large;
246 |
247 | @legend-color: inherit;
248 |
249 |
250 | //== Pagination
251 | //
252 | //##
253 |
254 | @pagination-color: mix(@brand-primary, white, 20%);
255 |
256 |
257 | //== Pager
258 | //
259 | //##
260 |
261 | @pager-padding: 9px 15px 10px;
262 |
263 |
264 | //== Navbar
265 | //
266 | //##
267 |
268 | // Basics of a navbar
269 | @zindex-navbar: 1000;
270 | @zindex-dropdown: 1000;
271 | @zindex-popover: 1060;
272 | @zindex-tooltip: 1070;
273 | @zindex-navbar-fixed: 1030;
274 | @zindex-modal-background: 1040;
275 | @zindex-modal: 1050;
276 |
277 | @navbar-height-base: 53px;
278 | @navbar-height-large: 76px;
279 | @navbar-input-line-height: 1.4; // ~21px
280 | @navbar-margin-bottom: @line-height-computed;
281 | @navbar-border-radius: @border-radius-small;
282 |
283 | @navbar-height: 40px;
284 | // @navbar-border-radius: @border-radius-base;
285 | @navbar-padding-horizontal: floor((@grid-gutter-width / 2)) - 2;
286 | @navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
287 | // @navbar-collapse-max-height: 340px;
288 |
289 | @navbar-default-color: #777;
290 | @navbar-default-bg: #f8f8f8;
291 | @navbar-default-border: darken(@navbar-default-bg, 6.5%);
292 |
293 | // Navbar links
294 | @navbar-default-link-color: #777;
295 | @navbar-default-link-hover-color: #333;
296 | @navbar-default-link-hover-bg: transparent;
297 | @navbar-default-link-active-color: #555;
298 | @navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
299 | @navbar-default-link-disabled-color: #ccc;
300 | @navbar-default-link-disabled-bg: transparent;
301 |
302 | // Navbar brand label
303 | @navbar-default-brand-color: @navbar-default-link-color;
304 | @navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
305 | @navbar-default-brand-hover-bg: transparent;
306 |
307 | // Navbar toggle
308 | @navbar-default-toggle-hover-bg: #ddd;
309 | @navbar-default-toggle-icon-bar-bg: #888;
310 | @navbar-default-toggle-border-color: #ddd;
311 |
312 | // customed
313 | // Navbar nav carets
314 | @navbar-default-caret-color: @navbar-default-link-color;
315 | @navbar-default-caret-hover-color: @navbar-default-link-hover-color;
316 | @navbar-default-caret-active-color: @navbar-default-link-active-color;
317 |
318 | // Navbar form
319 | @navbar-default-form-placeholder: spin(tint(@brand-primary, 60%), 2);
320 | @navbar-default-form-icon: desaturate(tint(@brand-primary, 45%), 2%);
321 | @navbar-default-form-border: shade(@navbar-default-bg, 3%);
322 |
323 |
324 | // Inverted navbar
325 | // Reset inverted navbar basics
326 | @navbar-inverse-divider: darken(@brand-primary, 3%);
327 |
328 | // Reset inverted navbar basics
329 | @navbar-inverse-color: @gray-light;
330 | @navbar-inverse-bg: #222;
331 | @navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
332 |
333 | // Inverted navbar links
334 | @navbar-inverse-link-color: @inverse;
335 | @navbar-inverse-link-hover-color: @brand-secondary;
336 | @navbar-inverse-link-hover-bg: transparent;
337 | @navbar-inverse-link-active-color: @navbar-inverse-link-color;
338 | @navbar-inverse-link-active-bg: @brand-secondary;
339 | @navbar-inverse-link-disabled-color: #444;
340 | @navbar-inverse-link-disabled-bg: transparent;
341 |
342 | // Navbar nav carets
343 | @navbar-inverse-caret-color: lighten(desaturate(@brand-primary, 7%), 9%);
344 | @navbar-inverse-caret-hover-color: @navbar-inverse-link-hover-color;
345 | @navbar-inverse-caret-active-color: @navbar-inverse-link-active-color;
346 |
347 | // Inverted navbar brand label
348 | @navbar-inverse-brand-color: @navbar-inverse-link-color;
349 | @navbar-inverse-brand-hover-color: @navbar-inverse-link-hover-color;
350 | @navbar-inverse-brand-hover-bg: transparent;
351 |
352 | // Inverted navbar toggle
353 | @navbar-inverse-toggle-color: @navbar-inverse-link-color;
354 | @navbar-inverse-toggle-hover-color: @navbar-inverse-link-hover-color;
355 |
356 | // Navbar form
357 | @navbar-inverse-form-bg: darken(@brand-primary, 6%);
358 | @navbar-inverse-form-placeholder: desaturate(lighten(@brand-primary, 13%), 7%);
359 | @navbar-inverse-form-icon: desaturate(lighten(@brand-primary, 13%), 6%);
360 | @navbar-inverse-form-border: @navbar-inverse-divider;
361 |
362 | // Dropdown menu
363 | @navbar-inverse-dropdown-arrow: @navbar-inverse-bg;
364 | @navbar-inverse-dropdown-bg: @navbar-inverse-bg;
365 | @navbar-inverse-dropdown-link-color: mix(@navbar-inverse-bg, @navbar-inverse-color, 15%);
366 | @navbar-inverse-dropdown-link-hover-color: @inverse;
367 | @navbar-inverse-dropdown-link-hover-bg: @brand-secondary;
368 |
369 |
370 | //== Dropdown Menu
371 | //
372 | //##
373 | @bg-dark-color: #323538;
374 | @dropdown-background: @bg-dark-color;
375 | @dropdown-bg: @bg-dark-color;
376 | @dropdown-link-hover-color: @inverse;
377 | @dropdown-link-hover-bg: @brand-primary;
378 | @dropdown-link-color: @inverse;
379 | // @dropdown-fallback-border;
380 | // @dropdown-border;
381 | // @border-radius-base;
382 |
383 |
384 | //== Iconbar
385 | //
386 | //##
387 |
388 | @iconbar-background: mix(@brand-primary, black, 85%);
389 |
390 |
391 | //== Progress bars
392 | //
393 | //##
394 |
395 | @progress-height: 12px;
396 |
397 |
398 | //== Slider
399 | //
400 | //##
401 |
402 | @slider-height: 12px;
403 | @slider-value-font-size: floor(@component-font-size-base * 0.867); // ~13px;
404 |
405 | @slider-handle-bg: mix(@brand-secondary, black, 85%);
406 | @slider-handle-hover-bg: mix(@brand-secondary, white, 80%);
407 | @slider-handle-active-bg: mix(@brand-secondary, black, 85%);
408 |
409 | @slider-range-bg: @brand-secondary;
410 |
411 | @slider-segment-bg: mix(desaturate(@brand-primary, 15%), white, 20%);
412 |
413 |
414 | //== Switch
415 | //
416 | //##
417 |
418 | @switch-border-radius: 30px;
419 | @switch-width: 80px;
420 |
421 |
422 | //== Thumbnails
423 | //
424 | //##
425 |
426 | //** Padding around the thumbnail image
427 | @thumbnail-padding: 4px;
428 | //** Thumbnail background color
429 | @thumbnail-bg: @body-bg;
430 | //** Thumbnail border color
431 | @thumbnail-border: @gray-light;
432 | //** Thumbnail border radius
433 | @thumbnail-border-radius: @border-radius-large;
434 |
435 | //** Custom text color for thumbnail captions
436 | @thumbnail-caption-color: @text-color;
437 | //** Padding around the thumbnail caption
438 | @thumbnail-caption-padding: 9px;
439 |
440 |
441 | //== Media queries breakpoints
442 | //
443 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
444 |
445 | // Extra small screen / phone
446 | @screen-xs-min: 480px;
447 |
448 | // Small screen / tablet
449 | @screen-sm-min: 768px;
450 |
451 | // Medium screen / desktop
452 | @screen-md-min: 992px;
453 |
454 | // Large screen / wide desktop
455 | @screen-lg-min: 1200px;
456 |
457 | // So media queries don't overlap when required, provide a maximum
458 | @screen-xs-max: (@screen-sm-min - 1);
459 | @screen-sm-max: (@screen-md-min - 1);
460 | @screen-md-max: (@screen-lg-min - 1);
461 |
462 |
463 | //== Grid system
464 | //
465 | //## Define your custom responsive grid.
466 |
467 | //** Number of columns in the grid.
468 | @grid-columns: 12;
469 | //** Padding between columns. Gets divided in half for the left and right.
470 | @grid-gutter-width: 2rem;
471 | // Navbar collapse
472 | //** Point at which the navbar becomes uncollapsed.
473 | @grid-float-breakpoint: @screen-sm-min;
474 | //** Point at which the navbar begins collapsing.
475 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
476 |
477 | // Form states and alerts
478 | //
479 | //## Define colors for form feedback states and, by default, alerts.
480 |
481 | @state-success-text: @brand-success;
482 | @state-success-bg: #dff0d8;
483 | @state-success-border: darken(spin(@state-success-bg, -10), 5%);
484 |
485 | @state-info-text: @brand-info;
486 | @state-info-bg: #d9edf7;
487 | @state-info-border: darken(spin(@state-info-bg, -10), 7%);
488 |
489 | @state-warning-text: @brand-warning;
490 | @state-warning-bg: #fcf8e3;
491 | @state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
492 |
493 | @state-danger-text: @brand-danger;
494 | @state-danger-bg: #f2dede;
495 | @state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
496 |
497 |
498 | // Code
499 | //
500 | //##
501 |
502 | @code-color: #c7254e;
503 | @code-bg: #f9f2f4;
504 |
505 | @kbd-color: @inverse;
506 | @kbd-bg: @brand-primary;
507 |
508 | @pre-bg: @inverse;
509 | @pre-color: inherit;
510 | @pre-border-color: mix(@brand-primary, @inverse, 12%);
511 | @pre-scrollable-max-height: 340px;
512 | @pre-border-radius: @border-radius-large;
513 |
514 |
515 | // Type
516 | //
517 | //##
518 |
519 | //** Text muted color
520 | @text-muted: @gray-light;
521 | //** Abbreviations and acronyms border color
522 | @abbr-border-color: @gray-light;
523 | //** Headings small color
524 | @headings-small-color: mix(@brand-primary, @inverse, 12%);
525 | //** Blockquote small color
526 | @blockquote-small-color: inherit;
527 | //** Blockquote border color
528 | @blockquote-border-color: mix(@brand-primary, @inverse, 12%);
529 | //** Page header border color
530 | @page-header-border-color: mix(@brand-primary, @inverse, 12%);
531 |
532 |
533 | // Miscellaneous
534 | //
535 | //##
536 |
537 | //** Hr border color
538 | @hr-border: mix(@brand-primary, @inverse, 63%);
539 |
540 | //** Horizontal forms & lists
541 | @component-offset-horizontal: 180px;
542 |
543 | @drawer-width: 29.5rem;
--------------------------------------------------------------------------------
/src/css/layout/container.less:
--------------------------------------------------------------------------------
1 | @import url(../echoes-variables.less);
2 |
3 | @media (min-width: 320px) {
4 | .container {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/css/layout/navbar.less:
--------------------------------------------------------------------------------
1 | @import url(../core/global.less);
2 |
3 | @media (min-width: 320px) {
4 | .navbar {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/css/layout/sidebar.less:
--------------------------------------------------------------------------------
1 | @import url(../echoes-variables.less);
2 |
3 | @media (min-width: 320px) {
4 | .sidebar {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/css/style.less:
--------------------------------------------------------------------------------
1 | /* ===== Primary Styles ========================================================
2 | Author: Oren Farhi, http://orizens.com
3 | ========================================================================== */
4 | /* Application */
5 | @import url(../../node_modules/bootstrap/less/bootstrap.less);
6 | // @import url(../../node_modules/font-awesome/less/font-awesome.less);
7 | @import url(./core/global.less);
8 | @import url(./core/utils.less);
9 | // @fa-font-path: "/";
10 |
11 | @import url(./layout/container.less);
12 | @import url(./layout/navbar.less);
13 | @import url(./layout/sidebar.less);
14 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | My App
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | loading...

32 |
33 |
34 |
35 |
47 |
48 |
--------------------------------------------------------------------------------
/tests/e2e/app.spec.js:
--------------------------------------------------------------------------------
1 | describe('My App', () => {
2 | it('should search and display results', () => {
3 | const actual = {};
4 | const expected = {};
5 | expect(actual).toBe(expected);
6 | });
7 | });
--------------------------------------------------------------------------------
/tests/mocks/home.mock.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'title': 'feels like home'
3 | };
4 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const getPath = (pathToFile) => path.resolve(__dirname, pathToFile);
7 |
8 | module.exports = {
9 | devtool: 'inline-source-map',
10 | entry: {
11 | app: [
12 | getPath('./src/app.js'),
13 | getPath('./src/config/dev.config.js')
14 | ],
15 | vendors: [
16 | 'angular',
17 | 'angular-ui-router',
18 | 'angular-animate',
19 | 'angular-sanitize',
20 | 'angular-ui-bootstrap',
21 | 'angular-local-storage'
22 | ]
23 | },
24 | output: {
25 | path: getPath('./dist'),
26 | filename: '[name].[hash].bundle.js',
27 | sourceMapFilename: '[name].[hash].bundle.map'
28 | },
29 |
30 | resolve: {
31 | modulesDirectories: [
32 | 'node_modules', 'src/component', 'src/core', 'src/css'
33 | ],
34 | extensions: [ '', '.js', 'less', 'html' ]
35 | },
36 | module: {
37 | loaders: [{
38 | test: /\.js$/,
39 | exclude: /(node_modules)/,
40 | loaders: ['babel']
41 | }, {
42 | test: /\.html$/,
43 | loader: 'ngtemplate!html',
44 | exclude: /(index)/
45 | }, {
46 | test: /\.less$/,
47 | loader: ExtractTextPlugin.extract('css?sourceMap!' + 'less?sourceMap')
48 | },
49 | // FONTS
50 | {
51 | test: /\.woff$/,
52 | loader: 'url?limit=100000&name=./fonts/[name]/[hash].[ext]'
53 | }, {
54 | test: /\.eot$/,
55 | loader: 'file'
56 | }, {
57 | test: /\.svg$/,
58 | loader: 'url?limit=100000&name=./fonts/[name]/[hash].[ext]'
59 | },
60 | // the url-loader uses DataUrls.
61 | // the file-loader emits files.
62 | {
63 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
64 | loader: 'url?limit=10000&minetype=application/font-woff'
65 | }, {
66 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
67 | loader: 'file'
68 | },
69 | {
70 | test: /\.(jpg|png|gif)$/,
71 | loader: 'file'
72 | }]
73 | },
74 |
75 | plugins: [
76 | // this is a performance hit by ~10 seconds
77 | // new ngAnnotatePlugin({
78 | // add: true
79 | // // other ng-annotate options here
80 | // }),
81 | new webpack.optimize.CommonsChunkPlugin({
82 | name: 'vendors',
83 | fileName: 'vendors.[hash].js',
84 | minChunks: Infinity
85 | }),
86 | new webpack.optimize.AggressiveMergingPlugin({}),
87 | new webpack.optimize.OccurenceOrderPlugin(true),
88 | new ExtractTextPlugin('[name].[hash].style.css'),
89 | // HtmlWebpackPlugin
90 | // See: https://github.com/ampedandwired/html-webpack-plugin
91 | new HtmlWebpackPlugin({
92 | template: 'html!./src/index.html'
93 | })
94 | ]
95 | };
96 |
--------------------------------------------------------------------------------
/webpack.config.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const CopyWebpackPlugin = require('copy-webpack-plugin');
7 | const getPath = (pathToFile) => path.resolve(__dirname, pathToFile);
8 |
9 | module.exports = {
10 | devtool: 'source-map',
11 | entry: {
12 | app: [
13 | getPath('./src/app.js'),
14 | getPath('./src/config/production.config.js')
15 | ],
16 | vendors: [
17 | 'angular',
18 | 'angular-ui-router',
19 | 'angular-animate',
20 | 'angular-sanitize',
21 | 'angular-ui-bootstrap',
22 | 'angular-local-storage'
23 | ]
24 | },
25 | output: {
26 | path: getPath('./dist'),
27 | filename: '[name].[chunkhash].bundle.js'
28 | },
29 | module: {
30 | loaders: [{
31 | test: /\.js$/,
32 | exclude: /(node_modules)/,
33 | loaders: ['babel']
34 | }, {
35 | test: /\.html$/,
36 | loader: 'ngtemplate!html',
37 | exclude: /(index)/
38 | }, {
39 | test: /\.less$/,
40 | loader: ExtractTextPlugin.extract('css?sourceMap!less?sourceMap')
41 | },
42 | // FONTS
43 | {
44 | test: /\.woff$/,
45 | loader: 'url?limit=100000&name=./fonts/[name]/[hash].[ext]'
46 | }, {
47 | test: /\.eot$/,
48 | loader: 'file'
49 | }, {
50 | test: /\.svg$/,
51 | loader: 'url?limit=100000&name=./fonts/[name]/[hash].[ext]'
52 | },
53 | // the url-loader uses DataUrls.
54 | // the file-loader emits files.
55 | {
56 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
57 | loader: 'url?limit=10000&minetype=application/font-woff'
58 | }, {
59 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
60 | loader: 'file'
61 | }, {
62 | test: /\.(jpg|png|gif)$/,
63 | loader: 'file'
64 | }
65 | ]},
66 | plugins: [
67 | new ngAnnotatePlugin({
68 | add: true
69 | // other ng-annotate options here
70 | }),
71 | new webpack.optimize.CommonsChunkPlugin('vendors',
72 | '[name].[chunkhash].vendors.js'),
73 | new ExtractTextPlugin('[name].[chunkhash].style.css'),
74 | // See: https://github.com/ampedandwired/html-webpack-plugin
75 | new HtmlWebpackPlugin({
76 | template: 'html!./src/index.html'
77 | }),
78 | new CopyWebpackPlugin([{
79 | from: 'config/dist-assets/CNAME'
80 | }, {
81 | from: 'config/dist-assets/manifest.json'
82 | }, {
83 | from: 'src/assets',
84 | to: 'assets'
85 | }])
86 | ]
87 | };
88 |
--------------------------------------------------------------------------------