├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── generators ├── app │ ├── index.js │ └── templates │ │ └── src │ │ └── index.html ├── component │ ├── index.js │ └── templates │ │ └── src │ │ └── app │ │ ├── component.spec.babel │ │ └── component.vue ├── hello │ ├── index.js │ └── templates │ │ └── src │ │ ├── app │ │ ├── Hello.spec.babel │ │ └── Hello.vue │ │ ├── index.babel │ │ ├── index.css │ │ ├── index.less │ │ ├── index.scss │ │ └── index.styl ├── techs │ ├── index.js │ └── templates │ │ └── src │ │ ├── app │ │ ├── Footer.spec.babel │ │ ├── Footer.vue │ │ ├── Header.spec.babel │ │ ├── Header.vue │ │ ├── Main.spec.babel │ │ ├── Main.vue │ │ ├── Title.spec.babel │ │ ├── Title.vue │ │ └── techs │ │ │ ├── Tech.spec.babel │ │ │ ├── Tech.vue │ │ │ ├── Techs.spec.babel │ │ │ └── Techs.vue │ │ ├── index.babel │ │ ├── index.css │ │ ├── index.less │ │ ├── index.scss │ │ └── index.styl └── todoMVC │ ├── index.js │ └── templates │ └── src │ ├── app │ ├── components │ │ ├── Footer.spec.babel │ │ ├── Footer.vue │ │ ├── Header.spec.babel │ │ ├── Header.vue │ │ ├── MainSection.spec.babel │ │ ├── MainSection.vue │ │ ├── TodoItem.spec.babel │ │ ├── TodoItem.vue │ │ ├── TodoTextInput.spec.babel │ │ └── TodoTextInput.vue │ ├── constants │ │ ├── ActionTypes.babel │ │ ├── TodoFilters.babel │ │ └── VisibilityFilters.babel │ ├── containers │ │ └── App.vue │ └── store │ │ ├── actions.babel │ │ ├── index.babel │ │ ├── mutations.babel │ │ └── mutations.spec.babel │ ├── index.babel │ ├── index.css │ ├── index.html │ ├── index.less │ ├── index.scss │ └── index.styl ├── gulpfile.js ├── index.js ├── package.json └── test ├── app ├── index.composing.js ├── index.configuring.js ├── index.fountain.js ├── index.sample.js └── index.writing.js ├── component └── index.js ├── hello └── index.js ├── techs └── index.js └── todoMVC └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | after_success: 5 | - bash <(curl -s https://codecov.io/bash) 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Matthieu Lux (http://swiip.github.io/) & Mehdy Dara (https://github.com/zckrs) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :warning: Unmaintained 2 | 3 | This project has been **archived** and is considered **outdated** and **unmaintained**. 4 | 5 | With the massive rise of the CLI tools by each major JavaScript Web frameworks, it was become irrelevant to pursue the quest of being a competitive project scaffolder for modern Web project. 6 | 7 | We officialy advise to use the coresponding CLI tools for the framework you use: 8 | 9 | - [create-react-app](https://github.com/facebook/create-react-app) for [React](https://reactjs.org/) 10 | - [Vue CLI](https://cli.vuejs.org/) for [Vue.js](https://vuejs.org/) 11 | - [Angular CLI](https://cli.angular.io/) for [Angular](https://angular.io/) 12 | 13 | Of course, we have some regrets regarding our users, Yeoman users and some goals we had with Fountain (like giving important tool choices to users, harmonizing projects configurations between frameworks...) but still, you can use official CLI tools with confidence as they are great project which went further for development experience and Web optimization. 14 | 15 |

16 | 17 | FountainJS 18 | 19 |

20 | 21 | [![Build Status](https://travis-ci.org/FountainJS/generator-fountain-vue.svg?branch=master)](https://travis-ci.org/FountainJS/generator-fountain-vue) 22 | [![codecov](https://codecov.io/gh/FountainJS/generator-fountain-vue/branch/master/graph/badge.svg)](https://codecov.io/gh/FountainJS/generator-fountain-vue) 23 | 24 | # Fountain Vue.js 2 Generator 25 | 26 | [![Vue 2](http://fountainjs.io/assets/imgs/vue.png)](https://vuejs.org/) 27 | 28 | > This Yeoman generator allows you to start an Vue.js web app with the best Developer Experience out of the box! 29 | 30 | > No matter what framework or module management you want to use, we got you covered with a cutting edge working configuration. 31 | 32 | > We use [Gulp 4](http://gulpjs.com/) as a task manager but we'll ask you questions about: 33 | - Modules management: Webpack 34 | - JS preprocessor: Babel 35 | - CSS preprocessor: Sass, Stylus, Less, none 36 | 37 | This generator is a sub-generator of the the Yeoman Fountain generator for webapps [generator-fountain-webapp](https://github.com/FountainJS/generator-fountain-webapp). 38 | 39 | ## Generator Fountain Vue 2 structure 40 | 41 | To take profit of the best of the Yeoman infrastructure, we heavily relies on the composability natures of the generators. 42 | 43 | Thereby, each needs of your future application will be addressed by a dedicated Yeoman generator (each will be used depending of the options you selected or not). 44 | 45 | More informations in [DESIGN.md](http://fountainjs.io/doc/design). 46 | 47 | 48 | ### Web tooling layer 49 | [![Gulp](http://fountainjs.io/assets/imgs/gulp.png)](https://github.com/FountainJS/generator-fountain-gulp) 50 | [![ESLint](http://fountainjs.io/assets/imgs/eslint.png)](https://github.com/FountainJS/generator-fountain-eslint) 51 | [![BrowserSync](http://fountainjs.io/assets/imgs/browsersync.png)](https://github.com/FountainJS/generator-fountain-browsersync) 52 | [![Karma](http://fountainjs.io/assets/imgs/karma.png)](https://github.com/FountainJS/generator-fountain-karma) 53 | 54 | ### Module management layer 55 | [![Webpack](http://fountainjs.io/assets/imgs/webpack.png)](https://github.com/FountainJS/generator-fountain-webpack) 56 | [![SystemJS](http://fountainjs.io/assets/imgs/systemjs.png)](https://github.com/FountainJS/generator-fountain-systemjs) 57 | [![Bower](http://fountainjs.io/assets/imgs/bower.png)](https://github.com/FountainJS/generator-fountain-inject) 58 | 59 | 60 | ## Usage 61 | 62 | ### Requirement Node 6+ && NPM 3+ 63 | This generator is targeted to be used with Node >= 6.0.0 and NPM => 3.0.0. You can check your version number with the command 64 | ``` 65 | node --version && npm --version 66 | ``` 67 | 68 | ### Install 69 | 70 | ##### Install required tools `yo`: 71 | ``` 72 | npm install -g yo 73 | ``` 74 | 75 | ##### Install `generator-fountain-vue`: 76 | ``` 77 | npm install -g generator-fountain-vue 78 | ``` 79 | 80 | 81 | ### Run 82 | 83 | ##### Create a new directory, and go into: 84 | ``` 85 | mkdir my-new-project && cd my-new-project 86 | ``` 87 | 88 | ##### Run `yo fountain-vue`, and select desired technologies: 89 | ``` 90 | yo fountain-vue 91 | ``` 92 | #### Use NPM scripts 93 | 94 | - `npm run build` to build an optimized version of your application in /dist 95 | - `npm run serve` to launch a browser sync server on your source files 96 | - `npm run serve:dist` to launch a server on your optimized application 97 | - `npm run test` to launch your unit tests with Karma 98 | - `npm run test:auto` to launch your unit tests with Karma in watch mode 99 | 100 | 101 | #### Or Gulp tasks 102 | 103 | If you have [`gulp-cli`](https://www.npmjs.com/package/gulp-cli) installed in global packages you can use equivalent: 104 | 105 | - `gulp` or `gulp build` 106 | - `gulp serve` 107 | - `gulp serve:dist` 108 | - `gulp test` 109 | - `gulp test:auto` 110 | 111 | **If you don't have [`gulp-cli`](https://www.npmjs.com/package/gulp-cli) installed in global, you should have this error:** 112 | > /usr/local/lib/node_modules/gulp/bin/gulp.js:121 113 | gulpInst.start.apply(gulpInst, toRun); 114 | TypeError: Cannot read property 'apply' of undefined 115 | 116 | ### Sub-generators 117 | 118 | Few sub-generators are available. You can see the full list by running `yo --generators`. 119 | Each generator has 2 options: 120 | - You can set the name of the generated item with `--name` 121 | - You can set the path of the generated item with `--dir` 122 | 123 | **Example:** 124 | 125 | ``` 126 | yo fountain-vue:component --name myComponent --dir components/game 127 | ``` 128 | 129 | ### [Start development](http://fountainjs.io/doc/usage/#use-npm-scripts) 130 | 131 | 132 | ## [Changelog](https://github.com/FountainJS/generator-fountain-vue/releases) 133 | 134 | 135 | ## [Contributing](http://fountainjs.io/doc/contributing) 136 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fountain = require('fountain-generator'); 4 | const version = require('../../package.json').version; 5 | 6 | module.exports = fountain.Base.extend({ 7 | prompting: { 8 | fountain() { 9 | this.options.framework = 'vue'; 10 | this.options.js = 'babel'; 11 | this.options.modules = 'webpack'; 12 | return this.fountainPrompting(); 13 | }, 14 | 15 | sample() { 16 | this.option('sample', {type: Boolean, required: false}); 17 | 18 | const prompts = [{ 19 | when: !this.options.sample, 20 | type: 'list', 21 | name: 'sample', 22 | message: 'Do you want a sample app?', 23 | choices: [ 24 | {name: 'A working landing page', value: 'techs'}, 25 | {name: 'Just a Hello World', value: 'hello'}, 26 | {name: 'TodoMVC', value: 'todoMVC'} 27 | ] 28 | }, { 29 | when: !this.options.router, 30 | type: 'list', 31 | name: 'router', 32 | message: 'Would you like a router?', 33 | choices: [ 34 | {name: 'Vue Router 2', value: 'router'}, 35 | {name: 'None', value: 'none'} 36 | ] 37 | }]; 38 | 39 | return this.prompt(prompts).then(props => { 40 | Object.assign(this.props, props); 41 | }); 42 | } 43 | }, 44 | 45 | configuring() { 46 | this.config.set('version', version); 47 | this.config.set('props', this.props); 48 | this.mergeJson('package.json', { 49 | dependencies: { 50 | vue: '^2.1.10' 51 | } 52 | }); 53 | if (this.props.router === 'router') { 54 | this.mergeJson('package.json', { 55 | dependencies: { 56 | 'vue-router': '^2.2.1' 57 | } 58 | }); 59 | } 60 | }, 61 | 62 | composing() { 63 | const options = { 64 | framework: this.props.framework, 65 | modules: this.props.modules, 66 | js: this.props.js, 67 | ci: this.props.ci, 68 | css: this.props.css, 69 | router: this.props.router, 70 | sample: this.props.sample, 71 | skipInstall: this.props.skipInstall, 72 | skipCache: this.props.skipCache 73 | }; 74 | 75 | this.composeWith(require.resolve(`../${this.props.sample}`), options); 76 | this.composeWith(require.resolve('generator-fountain-gulp/generators/app'), options); 77 | }, 78 | 79 | writing() { 80 | this.copyTemplate('src/index.html', 'src/index.html', {router: this.props.router}); 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /generators/app/templates/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FountainJS 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /generators/component/index.js: -------------------------------------------------------------------------------- 1 | const fountain = require('fountain-generator'); 2 | 3 | module.exports = fountain.Base.extend({ 4 | writing() { 5 | const name = this.options.name || 'myComponent'; 6 | const titleCase = string => string.charAt(0).toUpperCase() + string.slice(1); 7 | const lowerCase = string => string.charAt(0).toLowerCase() + string.slice(1); 8 | const path = this.options.dir ? `app/${this.options.dir}` : `app`; 9 | const props = { 10 | componentName: lowerCase(name), 11 | className: titleCase(name), 12 | modules: this.config.get('props').modules, 13 | js: 'babel', 14 | framework: 'vue', 15 | name 16 | }; 17 | this.copyTemplate(`src/app/component.vue`, `src/${path}/${name}.vue`, props); 18 | this.copyTemplate(`src/app/component.spec.js`, `src/${path}/${name}.spec.js`, props); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /generators/component/templates/src/app/component.spec.babel: -------------------------------------------------------------------------------- 1 | import <%- className %> from './<%- name %>.vue'; 2 | 3 | describe('<%- className %>', () => { 4 | it('should be a <%- componentName %>', () => { 5 | expect(<%- className %>.name).toEqual('<%- componentName %>'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /generators/component/templates/src/app/component.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /generators/hello/index.js: -------------------------------------------------------------------------------- 1 | const fountain = require('fountain-generator'); 2 | 3 | module.exports = fountain.Base.extend({ 4 | writing: { 5 | src() { 6 | const src = [ 7 | 'src/index.css', 8 | 'src/index.js', 9 | 'src/app/Hello.vue', 10 | 'src/app/Hello.spec.js' 11 | ]; 12 | src.map(file => this.copyTemplate(file, file)); 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /generators/hello/templates/src/app/Hello.spec.babel: -------------------------------------------------------------------------------- 1 | import Hello from './Hello.vue'; 2 | 3 | describe('Hello', () => { 4 | it('should be a hello', () => { 5 | expect(Hello.name).toEqual('Hello'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /generators/hello/templates/src/app/Hello.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /generators/hello/templates/src/index.babel: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Hello from './app/Hello.vue'; 3 | 4 | import './index.<%- css %>'; 5 | <% if (router === 'router') { -%> 6 | import VueRouter from 'vue-router'; 7 | Vue.use(VueRouter); 8 | 9 | const router = new VueRouter({ 10 | mode: 'history', 11 | routes: [ 12 | { 13 | path: '/', 14 | components: { 15 | default: Hello 16 | } 17 | } 18 | ] 19 | }); 20 | <% } -%> 21 | 22 | export default new Vue({ 23 | el: '#root', 24 | <% if (router === 'router') { -%> 25 | router, 26 | render: h => h('router-view') 27 | <% } else { -%> 28 | render: h => h(Hello) 29 | <% } -%> 30 | }); 31 | -------------------------------------------------------------------------------- /generators/hello/templates/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: lightgrey; 3 | } 4 | -------------------------------------------------------------------------------- /generators/hello/templates/src/index.less: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: lightgrey; 3 | } 4 | -------------------------------------------------------------------------------- /generators/hello/templates/src/index.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: lightgrey; 3 | } 4 | -------------------------------------------------------------------------------- /generators/hello/templates/src/index.styl: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: lightgrey; 3 | } 4 | -------------------------------------------------------------------------------- /generators/techs/index.js: -------------------------------------------------------------------------------- 1 | const fountain = require('fountain-generator'); 2 | 3 | module.exports = fountain.Base.extend({ 4 | configuring() { 5 | this.mergeJson('package.json', { 6 | dependencies: { 7 | 'vue-resource': '^1.2.0' 8 | } 9 | }); 10 | }, 11 | 12 | writing: { 13 | src() { 14 | const src = [ 15 | 'src/index.css', 16 | 'src/index.js', 17 | 18 | 'src/app/techs/Tech.vue', 19 | 'src/app/techs/Tech.spec.js', 20 | 'src/app/techs/Techs.vue', 21 | 'src/app/techs/Techs.spec.js', 22 | 'src/app/Footer.vue', 23 | 'src/app/Footer.spec.js', 24 | 'src/app/Header.vue', 25 | 'src/app/Header.spec.js', 26 | 'src/app/Main.vue', 27 | 'src/app/Main.spec.js', 28 | 'src/app/Title.vue', 29 | 'src/app/Title.spec.js' 30 | ]; 31 | src.map(file => this.copyTemplate(file, file)); 32 | } 33 | }, 34 | 35 | techs() { 36 | this.prepareTechJson(); 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Footer.spec.babel: -------------------------------------------------------------------------------- 1 | import Footer from './Footer.vue'; 2 | 3 | describe('Footer', () => { 4 | it('should be a footer', () => { 5 | expect(Footer.name).toEqual('Footer'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Footer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Header.spec.babel: -------------------------------------------------------------------------------- 1 | import Header from './Header.vue'; 2 | 3 | describe('Header', () => { 4 | it('should be a header', () => { 5 | expect(Header.name).toEqual('Header'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Header.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Main.spec.babel: -------------------------------------------------------------------------------- 1 | import Main from './Main.vue'; 2 | 3 | describe('Main', () => { 4 | it('should be a main', () => { 5 | expect(Main.name).toEqual('Main'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Main.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 28 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Title.spec.babel: -------------------------------------------------------------------------------- 1 | import Title from './Title.vue'; 2 | 3 | describe('Title', () => { 4 | it('should be a title', () => { 5 | expect(Title.name).toEqual('Title'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/Title.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/techs/Tech.spec.babel: -------------------------------------------------------------------------------- 1 | import Tech from './Tech.vue'; 2 | 3 | describe('Tech', () => { 4 | it('should be a tech', () => { 5 | expect(Tech.name).toEqual('Tech'); 6 | }); 7 | 8 | it(`should return ['tech']`, () => { 9 | expect(Tech.props).toEqual(['tech']); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/techs/Tech.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/techs/Techs.spec.babel: -------------------------------------------------------------------------------- 1 | import Techs from './Techs.vue'; 2 | 3 | describe('Techs', () => { 4 | it('should be a techs', () => { 5 | expect(Techs.name).toEqual('Techs'); 6 | }); 7 | 8 | it('should return []', () => { 9 | expect(Techs.data().techs).toEqual([]); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /generators/techs/templates/src/app/techs/Techs.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 38 | -------------------------------------------------------------------------------- /generators/techs/templates/src/index.babel: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Main from './app/Main.vue'; 3 | 4 | import './index.<%- css %>'; 5 | <% if (router === 'router') { -%> 6 | import VueRouter from 'vue-router'; 7 | Vue.use(VueRouter); 8 | 9 | const router = new VueRouter({ 10 | mode: 'history', 11 | routes: [ 12 | { 13 | path: '/', 14 | components: { 15 | default: Main 16 | } 17 | } 18 | ] 19 | }); 20 | <% } -%> 21 | 22 | export default new Vue({ 23 | el: '#root', 24 | <% if (router === 'router') { -%> 25 | router, 26 | render: h => h('router-view') 27 | <% } else { -%> 28 | render: h => h(Main) 29 | <% } -%> 30 | }); 31 | -------------------------------------------------------------------------------- /generators/techs/templates/src/index.css: -------------------------------------------------------------------------------- 1 | html, body, #root { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Open Sans', sans-serif; 5 | } 6 | 7 | body, h1, h2, h3 { 8 | font-weight: 300 9 | } 10 | 11 | a { 12 | color: white; 13 | text-decoration: none; 14 | } 15 | 16 | a:hover { 17 | color: #5aadbb; 18 | } 19 | 20 | .main-container { 21 | display: flex; 22 | flex-direction: column; 23 | min-height: 100%; 24 | } 25 | .main { 26 | flex: 1; 27 | display: flex; 28 | flex-direction: column; 29 | } 30 | 31 | .header { 32 | display: flex; 33 | align-items: center; 34 | background-color: #1f1f1f; 35 | } 36 | .header-title { 37 | flex: 1; 38 | font-size: 1.5rem; 39 | margin: 1rem; 40 | } 41 | .header-date { 42 | flex: 1; 43 | text-align: right; 44 | margin: 1rem; 45 | color: white; 46 | } 47 | 48 | .title { 49 | display: flex; 50 | flex-direction: column; 51 | align-items: center; 52 | padding: 1rem; 53 | background-color: #cf4646; 54 | color: white; 55 | } 56 | .title-h1 { 57 | font-weight: 300; 58 | font-size: 4rem; 59 | margin: 1rem; 60 | } 61 | .title-logo { 62 | height: 12rem; 63 | background-color: white; 64 | border-radius: 1rem; 65 | margin: 1rem; 66 | } 67 | .title-h2 { 68 | font-weight: 300; 69 | font-size: 2rem; 70 | margin: .5rem; 71 | } 72 | 73 | .footer { 74 | padding: 0.5rem; 75 | font-size: 1rem; 76 | background-color: #1f1f1f; 77 | text-align: center; 78 | color: white; 79 | } 80 | 81 | .techs-container { 82 | margin: 1rem; 83 | } 84 | .techs-h2 { 85 | font-weight: 300; 86 | font-size: 1.5rem; 87 | } 88 | .techs { 89 | display: flex; 90 | flex-direction: row; 91 | flex-wrap: wrap; 92 | justify-content: space-around; 93 | } 94 | 95 | .tech { 96 | height: 15rem; 97 | width: 15rem; 98 | border: 1px solid lightgray; 99 | border-radius: 1rem; 100 | margin: 1rem; 101 | padding: 1rem; 102 | } 103 | .tech-logo { 104 | width: 5rem; 105 | height: 5rem; 106 | float: right; 107 | margin: 0 0 .5rem .5rem; 108 | } 109 | .tech-h3 { 110 | font-size: 1.5rem; 111 | margin: 0 0 2rem 0; 112 | } 113 | -------------------------------------------------------------------------------- /generators/techs/templates/src/index.less: -------------------------------------------------------------------------------- 1 | html, body, #root { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Open Sans', sans-serif; 5 | } 6 | 7 | body, h1, h2, h3 { 8 | font-weight: 300 9 | } 10 | 11 | a { 12 | color: white; 13 | text-decoration: none; 14 | 15 | &:hover { 16 | color: #5aadbb; 17 | } 18 | } 19 | 20 | .main-container { 21 | display: flex; 22 | flex-direction: column; 23 | min-height: 100%; 24 | } 25 | .main { 26 | flex: 1; 27 | display: flex; 28 | flex-direction: column; 29 | } 30 | 31 | .header { 32 | display: flex; 33 | align-items: center; 34 | background-color: #1f1f1f; 35 | } 36 | .header-title { 37 | flex: 1; 38 | font-size: 1.5rem; 39 | margin: 1rem; 40 | } 41 | .header-date { 42 | flex: 1; 43 | text-align: right; 44 | margin: 1rem; 45 | color: white; 46 | } 47 | 48 | .title { 49 | display: flex; 50 | flex-direction: column; 51 | align-items: center; 52 | padding: 1rem; 53 | background-color: #cf4646; 54 | color: white; 55 | } 56 | .title-h1 { 57 | font-weight: 300; 58 | font-size: 4rem; 59 | margin: 1rem; 60 | } 61 | .title-logo { 62 | height: 12rem; 63 | background-color: white; 64 | border-radius: 1rem; 65 | margin: 1rem; 66 | } 67 | .title-h2 { 68 | font-weight: 300; 69 | font-size: 2rem; 70 | margin: .5rem; 71 | } 72 | 73 | .footer { 74 | padding: 0.5rem; 75 | font-size: 1rem; 76 | background-color: #1f1f1f; 77 | text-align: center; 78 | color: white; 79 | } 80 | 81 | .techs-container { 82 | margin: 1rem; 83 | } 84 | .techs-h2 { 85 | font-weight: 300; 86 | font-size: 1.5rem; 87 | } 88 | .techs { 89 | display: flex; 90 | flex-direction: row; 91 | flex-wrap: wrap; 92 | justify-content: space-around; 93 | } 94 | 95 | .tech { 96 | height: 15rem; 97 | width: 15rem; 98 | border: 1px solid lightgray; 99 | border-radius: 1rem; 100 | margin: 1rem; 101 | padding: 1rem; 102 | } 103 | .tech-logo { 104 | width: 5rem; 105 | height: 5rem; 106 | float: right; 107 | margin: 0 0 .5rem .5rem; 108 | } 109 | .tech-h3 { 110 | font-size: 1.5rem; 111 | margin: 0 0 2rem 0; 112 | } 113 | -------------------------------------------------------------------------------- /generators/techs/templates/src/index.scss: -------------------------------------------------------------------------------- 1 | html, body, #root { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Open Sans', sans-serif; 5 | } 6 | 7 | body, h1, h2, h3 { 8 | font-weight: 300 9 | } 10 | 11 | a { 12 | color: white; 13 | text-decoration: none; 14 | 15 | &:hover { 16 | color: #5aadbb; 17 | } 18 | } 19 | 20 | .main-container { 21 | display: flex; 22 | flex-direction: column; 23 | min-height: 100%; 24 | } 25 | .main { 26 | flex: 1; 27 | display: flex; 28 | flex-direction: column; 29 | } 30 | 31 | .header { 32 | display: flex; 33 | align-items: center; 34 | background-color: #1f1f1f; 35 | } 36 | .header-title { 37 | flex: 1; 38 | font-size: 1.5rem; 39 | margin: 1rem; 40 | } 41 | .header-date { 42 | flex: 1; 43 | text-align: right; 44 | margin: 1rem; 45 | white: white; 46 | } 47 | 48 | .title { 49 | display: flex; 50 | flex-direction: column; 51 | align-items: center; 52 | padding: 1rem; 53 | background-color: #cf4646; 54 | color: white; 55 | } 56 | .title-h1 { 57 | font-weight: 300; 58 | font-size: 4rem; 59 | margin: 1rem; 60 | } 61 | .title-logo { 62 | height: 12rem; 63 | background-color: white; 64 | border-radius: 1rem; 65 | margin: 1rem; 66 | } 67 | .title-h2 { 68 | font-weight: 300; 69 | font-size: 2rem; 70 | margin: .5rem; 71 | } 72 | 73 | .footer { 74 | padding: 0.5rem; 75 | font-size: 1rem; 76 | background-color: #1f1f1f; 77 | text-align: center; 78 | color: white; 79 | } 80 | 81 | .techs-container { 82 | margin: 1rem; 83 | } 84 | .techs-h2 { 85 | font-weight: 300; 86 | font-size: 1.5rem; 87 | } 88 | .techs { 89 | display: flex; 90 | flex-direction: row; 91 | flex-wrap: wrap; 92 | justify-content: space-around; 93 | } 94 | 95 | .tech { 96 | height: 15rem; 97 | width: 15rem; 98 | border: 1px solid lightgray; 99 | border-radius: 1rem; 100 | margin: 1rem; 101 | padding: 1rem; 102 | } 103 | .tech-logo { 104 | width: 5rem; 105 | height: 5rem; 106 | float: right; 107 | margin: 0 0 .5rem .5rem; 108 | } 109 | .tech-h3 { 110 | font-size: 1.5rem; 111 | margin: 0 0 2rem 0; 112 | } 113 | -------------------------------------------------------------------------------- /generators/techs/templates/src/index.styl: -------------------------------------------------------------------------------- 1 | html, body, #root { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Open Sans', sans-serif; 5 | } 6 | 7 | body, h1, h2, h3 { 8 | font-weight: 300 9 | } 10 | 11 | a { 12 | color: white; 13 | text-decoration: none; 14 | 15 | &:hover { 16 | color: #5aadbb; 17 | } 18 | } 19 | 20 | .main-container { 21 | display: flex; 22 | flex-direction: column; 23 | min-height: 100%; 24 | } 25 | .main { 26 | flex: 1; 27 | display: flex; 28 | flex-direction: column; 29 | } 30 | 31 | .header { 32 | display: flex; 33 | align-items: center; 34 | background-color: #1f1f1f; 35 | } 36 | .header-title { 37 | flex: 1; 38 | font-size: 1.5rem; 39 | margin: 1rem; 40 | } 41 | .header-date { 42 | flex: 1; 43 | text-align: right; 44 | margin: 1rem; 45 | color: white; 46 | } 47 | 48 | .title { 49 | display: flex; 50 | flex-direction: column; 51 | align-items: center; 52 | padding: 1rem; 53 | background-color: #cf4646; 54 | color: white; 55 | } 56 | .title-h1 { 57 | font-weight: 300; 58 | font-size: 4rem; 59 | margin: 1rem; 60 | } 61 | .title-logo { 62 | height: 12rem; 63 | background-color: white; 64 | border-radius: 1rem; 65 | margin: 1rem; 66 | } 67 | .title-h2 { 68 | font-weight: 300; 69 | font-size: 2rem; 70 | margin: .5rem; 71 | } 72 | 73 | .footer { 74 | padding: 0.5rem; 75 | font-size: 1rem; 76 | background-color: #1f1f1f; 77 | text-align: center; 78 | color: white; 79 | } 80 | 81 | .techs-container { 82 | margin: 1rem; 83 | } 84 | .techs-h2 { 85 | font-weight: 300; 86 | font-size: 1.5rem; 87 | } 88 | .techs { 89 | display: flex; 90 | flex-direction: row; 91 | flex-wrap: wrap; 92 | justify-content: space-around; 93 | } 94 | 95 | .tech { 96 | height: 15rem; 97 | width: 15rem; 98 | border: 1px solid lightgray; 99 | border-radius: 1rem; 100 | margin: 1rem; 101 | padding: 1rem; 102 | } 103 | .tech-logo { 104 | width: 5rem; 105 | height: 5rem; 106 | float: right; 107 | margin: 0 0 .5rem .5rem; 108 | } 109 | .tech-h3 { 110 | font-size: 1.5rem; 111 | margin: 0 0 2rem 0; 112 | } 113 | -------------------------------------------------------------------------------- /generators/todoMVC/index.js: -------------------------------------------------------------------------------- 1 | const fountain = require('fountain-generator'); 2 | 3 | module.exports = fountain.Base.extend({ 4 | configuring: { 5 | pkg() { 6 | this.mergeJson('package.json', { 7 | dependencies: { 8 | 'vuex': '^2.1.2', 9 | 'todomvc-app-css': '^2.0.6' 10 | } 11 | }); 12 | }, 13 | 14 | babel() { 15 | this.mergeJson('.babelrc', {plugins: ['transform-object-rest-spread']}); 16 | } 17 | }, 18 | 19 | writing: { 20 | src() { 21 | const src = [ 22 | 'src/index.html', 23 | 'src/index.js', 24 | 25 | 'src/app/components/Footer.vue', 26 | 'src/app/components/Footer.spec.js', 27 | 'src/app/components/Header.vue', 28 | 'src/app/components/Header.spec.js', 29 | 'src/app/components/MainSection.vue', 30 | 'src/app/components/MainSection.spec.js', 31 | 'src/app/components/TodoItem.vue', 32 | 'src/app/components/TodoItem.spec.js', 33 | 'src/app/components/TodoTextInput.vue', 34 | 'src/app/components/TodoTextInput.spec.js', 35 | 36 | 'src/app/constants/ActionTypes.js', 37 | 'src/app/constants/TodoFilters.js', 38 | 'src/app/constants/VisibilityFilters.js', 39 | 40 | 'src/app/containers/App.vue', 41 | 42 | 'src/app/store/actions.js', 43 | 'src/app/store/index.js', 44 | 'src/app/store/mutations.js', 45 | 'src/app/store/mutations.spec.js' 46 | ]; 47 | src.map(file => this.copyTemplate(file, file)); 48 | } 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/Footer.spec.babel: -------------------------------------------------------------------------------- 1 | import Footer from './Footer.vue'; 2 | 3 | describe('Footer', () => { 4 | it('should have correct props', () => { 5 | expect(Footer.props).toEqual(['activeCount', 'completedCount', 'selectedFilter', 'onShow']); 6 | }); 7 | 8 | it('should return filterTitles and filters', () => { 9 | expect(Object.keys(Footer.data())).toEqual(['filterTitles', 'filters']); 10 | }); 11 | 12 | describe('methods', () => { 13 | it('should call this.clearCompleted', () => { 14 | const clearCompleted = jasmine.createSpy('clearCompleted'); 15 | Footer.clearCompleted = clearCompleted; 16 | Footer.methods.handleClear.call(Footer); 17 | expect(clearCompleted).toHaveBeenCalled(); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 46 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/Header.spec.babel: -------------------------------------------------------------------------------- 1 | import Header from './Header.vue'; 2 | 3 | describe('Header', () => { 4 | describe('methods', () => { 5 | it('should call this.addTodo', () => { 6 | const addTodo = jasmine.createSpy('completeAll'); 7 | Header.addTodo = addTodo; 8 | Header.methods.handleSave.call(Header, 'test'); 9 | expect(addTodo).toHaveBeenCalledWith('test'); 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/Header.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/MainSection.spec.babel: -------------------------------------------------------------------------------- 1 | import MainSection from './MainSection.vue'; 2 | 3 | describe('MainSection', () => { 4 | it('should have correct props', () => { 5 | expect(MainSection.props).toEqual(['filter']); 6 | }); 7 | 8 | describe('computed properties', () => { 9 | beforeEach(() => { 10 | MainSection.state = { 11 | todos: [{text: 'Use Redux', completed: false, id: 0}] 12 | }; 13 | }); 14 | 15 | it('should return true', () => { 16 | MainSection.completedCount = 1; 17 | MainSection.todos = MainSection.state.todos; 18 | expect(MainSection.computed.checked.call(MainSection)).toEqual(true); 19 | }); 20 | 21 | it('should return 0', () => { 22 | MainSection.todos = MainSection.state.todos; 23 | expect(MainSection.computed.completedCount.call(MainSection)).toEqual(0); 24 | }); 25 | 26 | it('should return 1', () => { 27 | MainSection.todos = MainSection.state.todos; 28 | MainSection.completedCount = 0; 29 | expect(MainSection.computed.activeCount.call(MainSection)).toEqual(1); 30 | }); 31 | 32 | it(`should return 'show_all'`, () => { 33 | MainSection.mainFilter = 'show_all'; 34 | expect(MainSection.computed.selectedFilter.call(MainSection).type).toEqual('show_all'); 35 | }); 36 | }); 37 | 38 | describe('methods', () => { 39 | it('should call actions.completeAll', () => { 40 | const completeAll = jasmine.createSpy('completeAll'); 41 | MainSection.completeAll = completeAll; 42 | MainSection.methods.handleCompleteAll.call(MainSection); 43 | expect(completeAll).toHaveBeenCalled(); 44 | }); 45 | 46 | it('should set this.filter', () => { 47 | MainSection.methods.handleShow.call(MainSection, 'show_completed'); 48 | expect(MainSection.mainFilter).toEqual('show_completed'); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/MainSection.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 71 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/TodoItem.spec.babel: -------------------------------------------------------------------------------- 1 | import TodoItem from './TodoItem.vue'; 2 | 3 | describe('TodoItem', () => { 4 | it('should have correct props', () => { 5 | expect(TodoItem.props).toEqual(['todo']); 6 | }); 7 | 8 | it('should return editing', () => { 9 | expect(TodoItem.data().editing).toBe(false); 10 | }); 11 | 12 | describe('methods', () => { 13 | it('should set this.editing of true', () => { 14 | TodoItem.methods.handleDoubleClick.call(TodoItem); 15 | expect(TodoItem.editing).toBe(true); 16 | }); 17 | 18 | it('should call actions.completeTodo', () => { 19 | const completeTodo = jasmine.createSpy('completeTodo'); 20 | TodoItem.completeTodo = completeTodo; 21 | TodoItem.methods.handleChange.call(TodoItem, 1); 22 | expect(completeTodo).toHaveBeenCalledWith(1); 23 | }); 24 | 25 | it('should call actions.deleteTodo', () => { 26 | TodoItem.todo = {id: 2}; 27 | const deleteTodo = jasmine.createSpy('deleteTodo'); 28 | TodoItem.deleteTodo = deleteTodo; 29 | TodoItem.methods.handleSave.call(TodoItem, ''); 30 | expect(deleteTodo).toHaveBeenCalledWith(2); 31 | }); 32 | 33 | it('should call actions.editTodo', () => { 34 | TodoItem.todo = {id: 2}; 35 | const editTodo = jasmine.createSpy('editTodo'); 36 | TodoItem.editTodo = editTodo; 37 | TodoItem.methods.handleSave.call(TodoItem, 'test'); 38 | expect(editTodo).toHaveBeenCalledWith(2, 'test'); 39 | }); 40 | 41 | it('should call actions.deleteTodo', () => { 42 | TodoItem.todo = {id: 2}; 43 | const deleteTodo = jasmine.createSpy('deleteTodo'); 44 | TodoItem.deleteTodo = deleteTodo; 45 | TodoItem.methods.handleDestroy.call(TodoItem, 3); 46 | expect(deleteTodo).toHaveBeenCalledWith(3); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/TodoItem.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 59 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/TodoTextInput.spec.babel: -------------------------------------------------------------------------------- 1 | import TodoTextInput from './TodoTextInput.vue'; 2 | 3 | describe('TodoTextInput', () => { 4 | it('should have correct props', () => { 5 | expect(TodoTextInput.props).toEqual(['text', 'editing', 'newTodo', 'placeholder', 'onSave']); 6 | }); 7 | 8 | it('should call $el.focus', () => { 9 | const focus = jasmine.createSpy('focus'); 10 | TodoTextInput.$el = {focus}; 11 | TodoTextInput.ready(); 12 | expect(focus).toHaveBeenCalled(); 13 | }); 14 | 15 | describe('methods', () => { 16 | it('should call onSave', () => { 17 | TodoTextInput.input = 'hello'; 18 | TodoTextInput.newTodo = false; 19 | const onSave = jasmine.createSpy('onSave'); 20 | TodoTextInput.onSave = onSave; 21 | TodoTextInput.methods.handleBlur.call(TodoTextInput); 22 | expect(onSave).toHaveBeenCalledWith('hello'); 23 | }); 24 | 25 | it('should call onSave and empty text', () => { 26 | TodoTextInput.input = 'goodbye'; 27 | TodoTextInput.newTodo = true; 28 | const onSave = jasmine.createSpy('onSave'); 29 | TodoTextInput.onSave = onSave; 30 | TodoTextInput.methods.handleSubmit.call(TodoTextInput, {keyCode: 13}); 31 | expect(onSave).toHaveBeenCalledWith('goodbye'); 32 | expect(TodoTextInput.input).toEqual(''); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/components/TodoTextInput.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/constants/ActionTypes.babel: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'ADD_TODO'; 2 | export const DELETE_TODO = 'DELETE_TODO'; 3 | export const EDIT_TODO = 'EDIT_TODO'; 4 | export const COMPLETE_TODO = 'COMPLETE_TODO'; 5 | export const COMPLETE_ALL = 'COMPLETE_ALL'; 6 | export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'; 7 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/constants/TodoFilters.babel: -------------------------------------------------------------------------------- 1 | export const SHOW_ALL = 'show_all'; 2 | export const SHOW_COMPLETED = 'show_completed'; 3 | export const SHOW_ACTIVE = 'show_active'; 4 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/constants/VisibilityFilters.babel: -------------------------------------------------------------------------------- 1 | import {SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE} from './TodoFilters'; 2 | 3 | function showAll() { 4 | return true; 5 | } 6 | 7 | function showCompleted(todo) { 8 | return todo.completed; 9 | } 10 | 11 | function showActive(todo) { 12 | return !todo.completed; 13 | } 14 | 15 | export default { 16 | [SHOW_ALL]: {filter: showAll, type: SHOW_ALL}, 17 | [SHOW_COMPLETED]: {filter: showCompleted, type: SHOW_COMPLETED}, 18 | [SHOW_ACTIVE]: {filter: showActive, type: SHOW_ACTIVE} 19 | }; 20 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/containers/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/store/actions.babel: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | const makeAction = type => ({commit}, ...args) => commit(type, ...args); 4 | 5 | export const addTodo = makeAction(types.ADD_TODO); 6 | export const deleteTodo = makeAction(types.DELETE_TODO); 7 | export const editTodo = makeAction(types.EDIT_TODO); 8 | export const completeTodo = makeAction(types.COMPLETE_TODO); 9 | export const completeAll = makeAction(types.COMPLETE_ALL); 10 | export const clearCompleted = makeAction(types.CLEAR_COMPLETED); 11 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/store/index.babel: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import mutations, {initialState as state} from './mutations'; 4 | import * as actions from './actions'; 5 | 6 | Vue.use(Vuex); 7 | 8 | export default new Vuex.Store({ 9 | state, 10 | mutations, 11 | actions, 12 | getters: { 13 | todos: state => state.todos 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/store/mutations.babel: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from '../constants/ActionTypes'; 2 | 3 | export const initialState = { 4 | todos: [ 5 | { 6 | text: 'Use Redux', 7 | completed: false, 8 | id: 0 9 | } 10 | ] 11 | }; 12 | 13 | export default { 14 | [ActionTypes.ADD_TODO](state, text) { 15 | state.todos.unshift({ 16 | id: state.todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1, 17 | completed: false, 18 | text 19 | }); 20 | }, 21 | [ActionTypes.DELETE_TODO](state, id) { 22 | state.todos = state.todos.filter(todo => todo.id !== id); 23 | }, 24 | [ActionTypes.EDIT_TODO](state, id, text) { 25 | state.todos = state.todos.map(todo => 26 | todo.id === id ? 27 | Object.assign({}, todo, {text}) : 28 | todo 29 | ); 30 | }, 31 | [ActionTypes.COMPLETE_TODO](state, id) { 32 | state.todos = state.todos.map(todo => 33 | todo.id === id ? 34 | Object.assign({}, todo, {completed: !todo.completed}) : 35 | todo 36 | ); 37 | }, 38 | [ActionTypes.COMPLETE_ALL](state) { 39 | const areAllMarked = state.todos.every(todo => todo.completed); 40 | state.todos = state.todos.map(todo => Object.assign({}, todo, { 41 | completed: !areAllMarked 42 | })); 43 | }, 44 | [ActionTypes.CLEAR_COMPLETED](state) { 45 | state.todos = state.todos.filter(todo => todo.completed === false); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/app/store/mutations.spec.babel: -------------------------------------------------------------------------------- 1 | import mutations from './mutations'; 2 | import * as types from '../constants/ActionTypes'; 3 | 4 | describe('todos reducer', () => { 5 | it('should handle ADD_TODO', () => { 6 | const addTodo = mutations[types.ADD_TODO]; 7 | const state = {todos: []}; 8 | addTodo(state, 'Run the tests'); 9 | expect(state.todos).toEqual([ 10 | { 11 | text: 'Run the tests', 12 | completed: false, 13 | id: 0 14 | } 15 | ]); 16 | 17 | state.todos = [{text: 'Use Redux', completed: false, id: 0}]; 18 | addTodo(state, 'Run the tests'); 19 | expect(state.todos).toEqual([ 20 | { 21 | text: 'Run the tests', 22 | completed: false, 23 | id: 1 24 | }, { 25 | text: 'Use Redux', 26 | completed: false, 27 | id: 0 28 | } 29 | ]); 30 | 31 | state.todos = [ 32 | { 33 | text: 'Run the tests', 34 | completed: false, 35 | id: 1 36 | }, { 37 | text: 'Use Redux', 38 | completed: false, 39 | id: 0 40 | } 41 | ]; 42 | addTodo(state, 'Fix the tests'); 43 | expect(state.todos).toEqual([ 44 | { 45 | text: 'Fix the tests', 46 | completed: false, 47 | id: 2 48 | }, { 49 | text: 'Run the tests', 50 | completed: false, 51 | id: 1 52 | }, { 53 | text: 'Use Redux', 54 | completed: false, 55 | id: 0 56 | } 57 | ]); 58 | }); 59 | 60 | it('should handle DELETE_TODO', () => { 61 | const deleteTodo = mutations[types.DELETE_TODO]; 62 | const state = {todos: [ 63 | { 64 | text: 'Run the tests', 65 | completed: false, 66 | id: 1 67 | }, { 68 | text: 'Use Redux', 69 | completed: false, 70 | id: 0 71 | } 72 | ]}; 73 | deleteTodo(state, 1); 74 | expect(state.todos).toEqual([ 75 | { 76 | text: 'Use Redux', 77 | completed: false, 78 | id: 0 79 | } 80 | ]); 81 | }); 82 | 83 | it('should handle EDIT_TODO', () => { 84 | const editTodo = mutations[types.EDIT_TODO]; 85 | const state = {todos: [ 86 | { 87 | text: 'Run the tests', 88 | completed: false, 89 | id: 1 90 | }, { 91 | text: 'Use Redux', 92 | completed: false, 93 | id: 0 94 | } 95 | ]}; 96 | editTodo(state, 1, 'Fix the tests'); 97 | expect(state.todos).toEqual([ 98 | { 99 | text: 'Fix the tests', 100 | completed: false, 101 | id: 1 102 | }, { 103 | text: 'Use Redux', 104 | completed: false, 105 | id: 0 106 | } 107 | ]); 108 | }); 109 | 110 | it('should handle COMPLETE_TODO', () => { 111 | const completeTodo = mutations[types.COMPLETE_TODO]; 112 | const state = {todos: [ 113 | { 114 | text: 'Run the tests', 115 | completed: false, 116 | id: 1 117 | }, { 118 | text: 'Use Redux', 119 | completed: false, 120 | id: 0 121 | } 122 | ]}; 123 | completeTodo(state, 1); 124 | expect(state.todos).toEqual([ 125 | { 126 | text: 'Run the tests', 127 | completed: true, 128 | id: 1 129 | }, { 130 | text: 'Use Redux', 131 | completed: false, 132 | id: 0 133 | } 134 | ]); 135 | }); 136 | 137 | it('should handle COMPLETE_ALL', () => { 138 | const completeAll = mutations[types.COMPLETE_ALL]; 139 | const state = {todos: [ 140 | { 141 | text: 'Run the tests', 142 | completed: true, 143 | id: 1 144 | }, { 145 | text: 'Use Redux', 146 | completed: false, 147 | id: 0 148 | } 149 | ]}; 150 | completeAll(state); 151 | expect(state.todos).toEqual([ 152 | { 153 | text: 'Run the tests', 154 | completed: true, 155 | id: 1 156 | }, { 157 | text: 'Use Redux', 158 | completed: true, 159 | id: 0 160 | } 161 | ]); 162 | 163 | // Unmark if all todos are currently completed 164 | completeAll(state); 165 | expect(state.todos).toEqual([ 166 | { 167 | text: 'Run the tests', 168 | completed: false, 169 | id: 1 170 | }, { 171 | text: 'Use Redux', 172 | completed: false, 173 | id: 0 174 | } 175 | ]); 176 | }); 177 | 178 | it('should handle CLEAR_COMPLETED', () => { 179 | const clearCompleted = mutations[types.CLEAR_COMPLETED]; 180 | const state = {todos: [ 181 | { 182 | text: 'Run the tests', 183 | completed: true, 184 | id: 1 185 | }, { 186 | text: 'Use Redux', 187 | completed: false, 188 | id: 0 189 | } 190 | ]}; 191 | clearCompleted(state); 192 | expect(state.todos).toEqual([ 193 | { 194 | text: 'Use Redux', 195 | completed: false, 196 | id: 0 197 | } 198 | ]); 199 | }); 200 | 201 | it('should not generate duplicate ids after CLEAR_COMPLETED', () => { 202 | const completeTodo = mutations[types.COMPLETE_TODO]; 203 | const clearCompleted = mutations[types.CLEAR_COMPLETED]; 204 | const addTodo = mutations[types.ADD_TODO]; 205 | const state = {todos: [ 206 | { 207 | id: 0, 208 | completed: false, 209 | text: 'Use Redux' 210 | }, { 211 | id: 1, 212 | completed: false, 213 | text: 'Write tests' 214 | } 215 | ]}; 216 | completeTodo(state, 0); 217 | clearCompleted(state); 218 | addTodo(state, 'Write more tests'); 219 | expect(state.todos).toEqual([ 220 | { 221 | text: 'Write more tests', 222 | completed: false, 223 | id: 2 224 | }, { 225 | text: 'Write tests', 226 | completed: false, 227 | id: 1 228 | } 229 | ]); 230 | }); 231 | }); 232 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/index.babel: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import 'todomvc-app-css/index.css'; 3 | 4 | import App from './app/containers/App.vue'; 5 | import store from './app/store/index'; 6 | <% if (router === 'router') { -%> 7 | import VueRouter from 'vue-router'; 8 | Vue.use(VueRouter); 9 | 10 | const router = new VueRouter({ 11 | mode: 'history', 12 | routes: [ 13 | { 14 | path: '/', 15 | components: { 16 | default: App 17 | } 18 | } 19 | ] 20 | }); 21 | <% } -%> 22 | 23 | export default new Vue({ 24 | el: '#root', 25 | store, 26 | <% if (router === 'router') { -%> 27 | router, 28 | render: h => h('router-view') 29 | <% } else { -%> 30 | render: h => h(App) 31 | <% } -%> 32 | }); 33 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FountainJS/generator-fountain-vue/067b11528a55f7ec5d0b2aca86f36294ec195330/generators/todoMVC/templates/src/index.css -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redux TodoMVC 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/index.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FountainJS/generator-fountain-vue/067b11528a55f7ec5d0b2aca86f36294ec195330/generators/todoMVC/templates/src/index.less -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/index.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FountainJS/generator-fountain-vue/067b11528a55f7ec5d0b2aca86f36294ec195330/generators/todoMVC/templates/src/index.scss -------------------------------------------------------------------------------- /generators/todoMVC/templates/src/index.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FountainJS/generator-fountain-vue/067b11528a55f7ec5d0b2aca86f36294ec195330/generators/todoMVC/templates/src/index.styl -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const gulp = require('gulp'); 5 | const eslint = require('gulp-eslint'); 6 | const spawn = require('cross-spawn'); 7 | const excludeGitignore = require('gulp-exclude-gitignore'); 8 | const nsp = require('gulp-nsp'); 9 | 10 | gulp.task('nsp', nodeSecurityProtocol); 11 | gulp.task('watch', watch); 12 | gulp.task('static', eslintCheck); 13 | gulp.task('test', gulp.series([avaTest, nycReport])); 14 | 15 | gulp.task('prepublish', gulp.series('nsp')); 16 | gulp.task('default', gulp.series('static', 'test')); 17 | 18 | function nodeSecurityProtocol(cb) { 19 | nsp({package: path.resolve('package.json')}, cb); 20 | } 21 | 22 | function eslintCheck() { 23 | return gulp.src(['**/*.js', '!**/templates/**']) 24 | .pipe(excludeGitignore()) 25 | .pipe(eslint()) 26 | .pipe(eslint.format()) 27 | .pipe(eslint.failAfterError()); 28 | } 29 | 30 | function avaTest() { 31 | return spawn('./node_modules/.bin/nyc', ['--all', '--reporter=lcov', './node_modules/.bin/ava'], {stdio: 'inherit'}); 32 | } 33 | 34 | function nycReport() { 35 | return spawn('./node_modules/.bin/nyc', ['report', '--colors'], {stdio: 'inherit'}); 36 | } 37 | 38 | function watch() { 39 | return spawn('./node_modules/.bin/nyc', ['--all', '--reporter=lcov', './node_modules/.bin/ava', '--watch'], {stdio: 'inherit'}); 40 | } 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | app: require.resolve('./generators/app'), 3 | component: require.resolve('./generators/component'), 4 | hello: require.resolve('./generators/hello'), 5 | techs: require.resolve('./generators/techs'), 6 | todoMVC: require.resolve('./generators/todoMVC') 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-fountain-vue", 3 | "version": "1.0.0", 4 | "description": "Yeoman Fountain generator to scaffold a webapp with Vue.js written in ES6 (Babel) through Webpack including tools Gulp 4, ESLint, Browsersync and Karma", 5 | "homepage": "", 6 | "repository": "fountainjs/generator-fountain-vue", 7 | "author": "The Fountain Team", 8 | "contributors": [ 9 | "Matthieu Lux (http://swiip.github.io/)", 10 | "Mehdy Dara (http://eleven-labs.com/)", 11 | "Micael Mbagira (http://micaelmbagira.github.io)" 12 | ], 13 | "main": "index.js", 14 | "keywords": [ 15 | "yeoman-generator", 16 | "yeoman", 17 | "generator", 18 | "gulp", 19 | "gulpfile", 20 | "fountain", 21 | "cli", 22 | "vue" 23 | ], 24 | "dependencies": { 25 | "fountain-generator": "^1.0.0", 26 | "generator-fountain-gulp": "^1.0.0" 27 | }, 28 | "devDependencies": { 29 | "ava": "^0.18.2", 30 | "babel-eslint": "^7.1.1", 31 | "chai": "^3.5.0", 32 | "chai-spies": "^0.7.1", 33 | "cross-spawn": "^5.0.1", 34 | "eslint": "^3.15.0", 35 | "eslint-config-xo-space": "^0.15.0", 36 | "eslint-plugin-babel": "^4.0.1", 37 | "gulp": "gulpjs/gulp#4.0", 38 | "gulp-eslint": "^3.0.1", 39 | "gulp-exclude-gitignore": "^1.1.1", 40 | "gulp-nsp": "^2.4.2", 41 | "nyc": "^10.1.2" 42 | }, 43 | "nyc": { 44 | "include": [ 45 | "generators/**/*.js" 46 | ], 47 | "exclude": [ 48 | "generators/**/templates/**" 49 | ] 50 | }, 51 | "scripts": { 52 | "test": "gulp", 53 | "prepublish": "gulp prepublish" 54 | }, 55 | "license": "MIT", 56 | "eslintConfig": { 57 | "extends": "xo-space/esnext", 58 | "rules": { 59 | "func-names": ["error", "as-needed"], 60 | "quote-props": ["error", "consistent"] 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/app/index.composing.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | const chai = require('chai'); 3 | const expect = chai.expect; 4 | const spies = require('chai-spies'); 5 | chai.use(spies); 6 | const TestUtils = require('fountain-generator').TestUtils; 7 | 8 | let context; 9 | 10 | test.before(() => { 11 | context = TestUtils.mock('app'); 12 | context.composeWith = () => {}; 13 | require('../../generators/app/index'); 14 | }); 15 | 16 | test(`Call this.composeWith twice`, () => { 17 | const spy = chai.spy.on(context, 'composeWith'); 18 | context.props = {sample: 'todoMVC'}; 19 | TestUtils.call(context, 'composing', {modules: context.props.modules, sample: context.props.sample}); 20 | const options = { 21 | framework: context.props.framework, 22 | modules: context.props.modules, 23 | js: context.props.js, 24 | ci: context.props.ci, 25 | css: context.props.css, 26 | router: context.props.router, 27 | sample: context.props.sample, 28 | skipInstall: context.props.skipInstall, 29 | skipCache: context.props.skipCache 30 | }; 31 | expect(spy).to.have.been.called.twice(); 32 | expect(spy).to.have.been.called.with(require.resolve('../../generators/todoMVC'), options); 33 | expect(spy).to.have.been.called.with(require.resolve('generator-fountain-gulp/generators/app'), options); 34 | }); 35 | -------------------------------------------------------------------------------- /test/app/index.configuring.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | const chai = require('chai'); 3 | const expect = chai.expect; 4 | const spies = require('chai-spies'); 5 | chai.use(spies); 6 | const TestUtils = require('fountain-generator').TestUtils; 7 | 8 | let context; 9 | 10 | test.before(() => { 11 | context = TestUtils.mock('app'); 12 | require('../../generators/app/index'); 13 | }); 14 | 15 | test.beforeEach(() => { 16 | context.mergeJson['package.json'] = {}; 17 | }); 18 | 19 | test('Call this.config.set twice', () => { 20 | context.config = { 21 | set: () => {} 22 | }; 23 | const spy = chai.spy.on(context.config, 'set'); 24 | TestUtils.call(context, 'configuring'); 25 | expect(spy).to.have.been.called.twice(); 26 | expect(spy).to.have.been.called.with('version'); 27 | expect(spy).to.have.been.called.with('props'); 28 | }); 29 | 30 | test(`Add 'vue' and 'vue-router' to package.json dependencies`, t => { 31 | TestUtils.call(context, 'configuring', {router: 'router'}); 32 | t.is(context.mergeJson['package.json'].dependencies.vue, '^2.1.10'); 33 | t.is(context.mergeJson['package.json'].dependencies['vue-router'], '^2.2.1'); 34 | }); 35 | 36 | test(`Add 'vue' to package.json dependencies`, t => { 37 | TestUtils.call(context, 'configuring', {router: 'none'}); 38 | t.is(context.mergeJson['package.json'].dependencies.vue, '^2.1.10'); 39 | t.is(context.mergeJson['package.json'].dependencies['vue-router'], undefined); 40 | }); 41 | -------------------------------------------------------------------------------- /test/app/index.fountain.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | const chai = require('chai'); 3 | const expect = chai.expect; 4 | const spies = require('chai-spies'); 5 | chai.use(spies); 6 | const TestUtils = require('fountain-generator').TestUtils; 7 | 8 | let context; 9 | 10 | test.before(() => { 11 | context = TestUtils.mock('app'); 12 | require('../../generators/app/index'); 13 | }); 14 | 15 | test('Set framework and call this.fountainPrompting', t => { 16 | context.fountainPrompting = () => {}; 17 | const spy = chai.spy.on(context, 'fountainPrompting'); 18 | TestUtils.call(context, 'prompting.fountain'); 19 | t.is(context.options.framework, 'vue'); 20 | expect(spy).to.have.been.called.once(); 21 | }); 22 | -------------------------------------------------------------------------------- /test/app/index.sample.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | const chai = require('chai'); 3 | const spies = require('chai-spies'); 4 | chai.use(spies); 5 | const TestUtils = require('fountain-generator').TestUtils; 6 | 7 | let context; 8 | 9 | test.before(() => { 10 | context = TestUtils.mock('app'); 11 | require('../../generators/app/index'); 12 | }); 13 | 14 | test(`Set sample of 'techs'`, async t => { 15 | context.option = () => {}; 16 | context.prompt = () => new Promise(resolve => resolve({sample: 'techs'})); 17 | TestUtils.call(context, 'prompting.sample').then(() => { 18 | t.is(context.props.sample, 'techs'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/app/index.writing.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const test = require('ava'); 3 | const chai = require('chai'); 4 | const spies = require('chai-spies'); 5 | chai.use(spies); 6 | const TestUtils = require('fountain-generator').TestUtils; 7 | 8 | let context; 9 | 10 | test.before(() => { 11 | context = TestUtils.mock('app'); 12 | require('../../generators/app/index'); 13 | process.chdir(path.resolve(__dirname, '../../')); 14 | }); 15 | 16 | test.beforeEach(() => { 17 | context.copyTemplate['src/index.html'] = null; 18 | }); 19 | 20 | test(`Write 'index.html'`, t => { 21 | TestUtils.call(context, 'writing'); 22 | t.true(context.copyTemplate['src/index.html'].length > 0); 23 | }); 24 | -------------------------------------------------------------------------------- /test/component/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const test = require('ava'); 3 | const chai = require('chai'); 4 | const expect = chai.expect; 5 | const spies = require('chai-spies'); 6 | chai.use(spies); 7 | const TestUtils = require('fountain-generator').TestUtils; 8 | 9 | let context; 10 | 11 | test.before(() => { 12 | context = TestUtils.mock('component'); 13 | context.config = {get: () => context.props}; 14 | require('../../generators/component/index'); 15 | process.chdir(path.resolve(__dirname, '../../')); 16 | }); 17 | 18 | test(`Call this.copyTemplate 2 times without 'dir' option`, t => { 19 | const spy = chai.spy.on(context, 'copyTemplate'); 20 | TestUtils.call(context, 'writing', {js: 'babel'}); 21 | expect(spy).to.have.been.called.exactly(2); 22 | t.true(context.copyTemplate['src/app/myComponent.vue'].length > 0); 23 | t.true(context.copyTemplate['src/app/myComponent.spec.js'].length > 0); 24 | }); 25 | 26 | test(`Call this.copyTemplate 2 times with 'dir' option`, t => { 27 | const spy = chai.spy.on(context, 'copyTemplate'); 28 | TestUtils.call(context, 'writing', {dir: 'game', js: 'babel'}); 29 | expect(spy).to.have.been.called.exactly(2); 30 | t.true(context.copyTemplate['src/app/game/myComponent.vue'].length > 0); 31 | t.true(context.copyTemplate['src/app/game/myComponent.spec.js'].length > 0); 32 | }); 33 | -------------------------------------------------------------------------------- /test/hello/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const test = require('ava'); 3 | const chai = require('chai'); 4 | const expect = chai.expect; 5 | const spies = require('chai-spies'); 6 | chai.use(spies); 7 | const TestUtils = require('fountain-generator').TestUtils; 8 | 9 | let context; 10 | 11 | const files = [ 12 | 'src/index.css', 13 | 'src/index.js', 14 | 'src/app/Hello.vue', 15 | 'src/app/Hello.spec.js' 16 | ]; 17 | 18 | test.before(() => { 19 | context = TestUtils.mock('hello'); 20 | require('../../generators/hello/index'); 21 | process.chdir(path.resolve(__dirname, '../../')); 22 | }); 23 | 24 | test(`Call this.copyTemplate 20 times`, t => { 25 | const spy = chai.spy.on(context, 'copyTemplate'); 26 | TestUtils.call(context, 'writing.src', { 27 | js: 'babel', 28 | version: require('../../package.json').version, 29 | date: new Date().toString() 30 | }); 31 | expect(spy).to.have.been.called.exactly(files.length); 32 | files.forEach(file => t.true(context.copyTemplate[file].length > 0)); 33 | }); 34 | -------------------------------------------------------------------------------- /test/techs/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const test = require('ava'); 3 | const chai = require('chai'); 4 | const expect = chai.expect; 5 | const spies = require('chai-spies'); 6 | chai.use(spies); 7 | const TestUtils = require('fountain-generator').TestUtils; 8 | 9 | let context; 10 | 11 | const files = [ 12 | 'src/index.css', 13 | 'src/index.js', 14 | 'src/app/techs/Tech.vue', 15 | 'src/app/techs/Tech.spec.js', 16 | 'src/app/techs/Techs.vue', 17 | 'src/app/techs/Techs.spec.js', 18 | 'src/app/Footer.vue', 19 | 'src/app/Footer.spec.js', 20 | 'src/app/Header.vue', 21 | 'src/app/Header.spec.js', 22 | 'src/app/Main.vue', 23 | 'src/app/Main.spec.js', 24 | 'src/app/Title.vue', 25 | 'src/app/Title.spec.js' 26 | ]; 27 | 28 | test.before(() => { 29 | context = TestUtils.mock('techs'); 30 | require('../../generators/techs/index'); 31 | process.chdir(path.resolve(__dirname, '../../')); 32 | }); 33 | 34 | test(`Add 'vue-resource' to package.json dependencies`, () => { 35 | TestUtils.call(context, 'configuring'); 36 | expect(context.mergeJson['package.json'].dependencies['vue-resource']).to.equal('^1.2.0'); 37 | }); 38 | 39 | test(`Call this.copyTemplate 14 times`, t => { 40 | const spy = chai.spy.on(context, 'copyTemplate'); 41 | TestUtils.call(context, 'writing.src', { 42 | js: 'babel', 43 | version: require('../../package.json').version, 44 | date: new Date().toString() 45 | }); 46 | expect(spy).to.have.been.called.exactly(files.length); 47 | files.forEach(file => t.true(context.copyTemplate[file].length > 0)); 48 | }); 49 | 50 | test('Call this.prepareTechJson', () => { 51 | context.prepareTechJson = () => {}; 52 | const spy = chai.spy.on(context, 'prepareTechJson'); 53 | TestUtils.call(context, 'techs'); 54 | expect(spy).to.have.been.called.once(); 55 | }); 56 | -------------------------------------------------------------------------------- /test/todoMVC/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const test = require('ava'); 3 | const chai = require('chai'); 4 | const expect = chai.expect; 5 | const spies = require('chai-spies'); 6 | chai.use(spies); 7 | const TestUtils = require('fountain-generator').TestUtils; 8 | 9 | let context; 10 | 11 | const files = [ 12 | 'src/index.html', 13 | 'src/index.js', 14 | 'src/app/components/Footer.vue', 15 | 'src/app/components/Footer.spec.js', 16 | 'src/app/components/Header.vue', 17 | 'src/app/components/Header.spec.js', 18 | 'src/app/components/MainSection.vue', 19 | 'src/app/components/MainSection.spec.js', 20 | 'src/app/components/TodoItem.vue', 21 | 'src/app/components/TodoItem.spec.js', 22 | 'src/app/components/TodoTextInput.vue', 23 | 'src/app/components/TodoTextInput.spec.js', 24 | 'src/app/constants/ActionTypes.js', 25 | 'src/app/constants/TodoFilters.js', 26 | 'src/app/constants/VisibilityFilters.js', 27 | 'src/app/containers/App.vue', 28 | 'src/app/store/actions.js', 29 | 'src/app/store/index.js', 30 | 'src/app/store/mutations.js', 31 | 'src/app/store/mutations.spec.js' 32 | ]; 33 | 34 | test.before(() => { 35 | context = TestUtils.mock('todoMVC'); 36 | require('../../generators/todoMVC/index'); 37 | process.chdir(path.resolve(__dirname, '../../')); 38 | }); 39 | 40 | test(`Add 'todomvc-app-css' to package.json dependencies`, () => { 41 | TestUtils.call(context, 'configuring.pkg'); 42 | expect(context.mergeJson['package.json'].dependencies.vuex).to.equal('^2.1.2'); 43 | expect(context.mergeJson['package.json'].dependencies['todomvc-app-css']).to.equal('^2.0.6'); 44 | }); 45 | 46 | test(`Add 'transform-object-rest-spread' to babelrc`, () => { 47 | TestUtils.call(context, 'configuring.babel'); 48 | expect(context.mergeJson['.babelrc'].plugins).to.deep.equal(['transform-object-rest-spread']); 49 | }); 50 | 51 | test(`Call this.copyTemplate 20 times`, t => { 52 | const spy = chai.spy.on(context, 'copyTemplate'); 53 | TestUtils.call(context, 'writing.src', { 54 | js: 'babel', 55 | version: require('../../package.json').version, 56 | date: new Date().toString() 57 | }); 58 | expect(spy).to.have.been.called.exactly(files.length); 59 | files.forEach(file => t.true(context.copyTemplate[file].length > 0)); 60 | }); 61 | --------------------------------------------------------------------------------