diff --git a/app/config.js b/app/config.js index 4fcd4e86..e72f4676 100644 --- a/app/config.js +++ b/app/config.js @@ -51,7 +51,68 @@ module.exports = { name: 'includeJQuery', message: 'Would you like to include jQuery?', default: true, - when: answers => !answers.features.includes('includeBootstrap') + when: answers => + answers.features && !answers.features.includes('includeBootstrap') + }, + { + type: 'confirm', + name: 'includeTest', + message: 'Would you like to add testing?', + default: true + }, + { + type: 'checkbox', + name: 'testsWanted', + message: 'Which type of test would you like to cover?', + when: answers => answers.includeTest, + choices: [ + { + name: 'End to end (e2e)', + value: 'includeE2e', + checked: true + }, + { + name: 'Unit Test', + value: 'includeUnit', + checked: true + } + ] + }, + { + type: 'list', + name: 'unitTestFramework', + message: 'Which test framework would you like to use?', + when: answers => + answers.includeTest && + answers.testsWanted && + answers.testsWanted.includes('includeUnit'), + choices: [ + { + name: 'Jest', + value: 'jest', + checked: true + }, + { + name: 'Ava', + value: 'ava', + checked: true + }, + { + name: 'Jasmine', + value: 'jasmine', + checked: true + }, + { + name: 'Mocha (Testing driven design)', + value: 'mochaTdd', + checked: true + }, + { + name: 'Mocha (Behaviour Driven Design)', + value: 'mochaBdd', + checked: true + } + ] } ], dirsToCreate: ['app/images', 'app/fonts'], diff --git a/app/index.js b/app/index.js index d04d7b47..c6ee8f4e 100644 --- a/app/index.js +++ b/app/index.js @@ -15,19 +15,9 @@ module.exports = class extends Generator { initializing() { this.pkg = require('../package.json'); - if (this.options['skip-test-framework']) { return; } - - this.composeWith( - require.resolve( - `generator-${this.options['test-framework']}/generators/app` - ), - { - 'skip-install': this.options['skip-install'] - } - ); } prompting() { @@ -41,7 +31,9 @@ module.exports = class extends Generator { return this.prompt(config.prompts).then(answers => { const features = answers.features; + const testsWanted = answers.testsWanted; const hasFeature = feat => features && features.includes(feat); + const hasTest = type => testsWanted && testsWanted.includes(type); // manually deal with the response, get back and store the results. // we change a bit this way of doing to automatically do this in the self.prompt() method. @@ -50,6 +42,10 @@ module.exports = class extends Generator { this.includeModernizr = hasFeature('includeModernizr'); this.includeAnalytics = hasFeature('includeAnalytics'); this.includeJQuery = answers.includeJQuery; + this.includeE2e = hasTest('includeE2e'); + this.includeUnit = hasTest('includeUnit'); + this.includeTest = this.includeE2e || this.includeUnit; + this.unitTestFramework = answers.unitTestFramework; }); } @@ -61,10 +57,13 @@ module.exports = class extends Generator { version: this.pkg.version, includeSass: this.includeSass, includeBootstrap: this.includeBootstrap, - testFramework: this.options['test-framework'], includeJQuery: this.includeJQuery, includeModernizr: this.includeModernizr, - includeAnalytics: this.includeAnalytics + includeAnalytics: this.includeAnalytics, + includeTest: this.includeTest, + includeE2e: this.includeE2e, + includeUnit: this.includeUnit, + unitTestFramework: this.unitTestFramework }; const copy = (input, output) => { @@ -98,6 +97,18 @@ module.exports = class extends Generator { copy('modernizr.json', 'modernizr.json'); } + if (this.includeE2e) { + copy('demo_cypress.js', 'cypress/integration/index_spec.js'); + } + + if (this.includeUnit && this.unitTestFramework === 'jest') { + copy('demo_jest.js', '__test__/main.test.js'); + } + + if (this.includeUnit && this.unitTestFramework === 'ava') { + copy('demo_ava.js', '__test__/main.test.js'); + } + let cssFile = `main.${this.includeSass ? 'scss' : 'css'}`; copyTpl(cssFile, `app/styles/${cssFile}`, templateData); } diff --git a/app/templates/_package.json b/app/templates/_package.json index 0d1e1ea4..505ed22f 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -15,11 +15,20 @@ <%_ } -%> }, "devDependencies": { + <%_ if (includeUnit && unitTestFramework === 'ava') { -%> + "ava": "^2.0.0", + <%_ } -%> "@babel/core": "^7.4.5", "@babel/preset-env": "^7.4.5", "autoprefixer": "^9.5.1", "browser-sync": "^2.26.5", "cross-env": "^5.2.0", + <%_ if (includeE2e) { -%> + "cypress": "^3.3.1", + <%_ } -%> + <%_ if (includeUnit && unitTestFramework === 'ava') { -%> + "esm": "^3.2.25", + <%_ } -%> "cssnano": "^4.1.10", "del": "^4.1.1", "gulp": "^4.0.2", @@ -39,21 +48,61 @@ "gulp-sass": "^4.0.2", <%_ } -%> "gulp-size": "^3.0.0", + <%_ if (includeUnit && unitTestFramework === 'jest') { -%> + "jest": "^24.8.0", + <%_ } -%> "gulp-sourcemaps": "^2.6.5", "gulp-uglify": "^3.0.2", "gulp-useref": "^3.1.6", <%_ if (includeModernizr) { -%> "mkdirp": "^0.5.1", <%_ } -%> - "mocha": "^6.1.4", - "yargs": "13.2.4" + <%_ if (includeUnit && unitTestFramework === 'ava') { -%> + "nyc": "^14.1.1", + <%_ } -%> + <%_ if (includeE2e) { -%> + "start-server-and-test": "^1.9.1", + <%_ } -%> + "yargs": "12.0.5" }, + <%_ if (includeUnit && unitTestFramework === 'ava') { -%> + "ava": { + "files": [ + "__test__/**/*.js" + ], + "require": [ + "esm" + ] + }, + <%_ } -%> "scripts": { + <%_ if (includeUnit && unitTestFramework === "ava") { -%> + "test:unit": "ava --verbose", + "test:unit-coverage": "nyc ava", + "test:unit-watch": "ava --verbose --watch", + <%_ } -%> + <%_ if (includeUnit && unitTestFramework === "jest") { -%> + "test:unit": "jest", + "test:unit-coverage": "jest --coverage", + "test:unit-watch": "jest --watchAll", + <%_ } -%> + <%_ if (includeE2e) { -%> + "test:e2e-open": "cypress open", + "test:e2e": "start-server-and-test start http://localhost:9000 test:e2e-open", + <%_ } -%> "serve:test": "cross-env NODE_ENV=test gulp serve", "serve:dist": "cross-env NODE_ENV=production gulp serve", "start": "gulp serve", "build": "cross-env NODE_ENV=production gulp", - "test": "npm run serve:test", + <%_ if (includeE2e && !includeUnit) { -%> + "test": "npm run test:e2e", + <%_ } -%> + <%_ if (includeUnit && !includeE2e) { -%> + "test": "npm run test:unit", + <%_ } -%> + <%_ if (includeE2e && includeUnit) { -%> + "test": "npm run test:unit && npm run test:e2e", + <%_ } -%> "tasks": "gulp --tasks" }, "browserslist": [ diff --git a/app/templates/babelrc b/app/templates/babelrc index a29ac998..59ffdf46 100644 --- a/app/templates/babelrc +++ b/app/templates/babelrc @@ -1,5 +1,9 @@ { "presets": [ - "@babel/preset-env" + ["@babel/preset-env", { + "targets": { + "esmodules": true + } + }] ] -} +} \ No newline at end of file diff --git a/app/templates/demo_ava.js b/app/templates/demo_ava.js new file mode 100644 index 00000000..aafd702f --- /dev/null +++ b/app/templates/demo_ava.js @@ -0,0 +1,6 @@ +import test from 'ava'; +import { greeting } from '../app/scripts/main.js' + +test('Say Allo!', t => { + t.is(greeting(), '\'Allo \'Allo!'); +}); \ No newline at end of file diff --git a/app/templates/demo_cypress.js b/app/templates/demo_cypress.js new file mode 100644 index 00000000..50b3f2da --- /dev/null +++ b/app/templates/demo_cypress.js @@ -0,0 +1,14 @@ +describe('My First Test', function() { + beforeEach(() => { + cy.visit('http://localhost:9000') + }) + it('Should body exist', function() { + cy.get('body') + .should('be.visible') + }) + + it('Should contains title', function(){ + cy.get("h1") + .should('have.text', `'Allo, 'Allo!`) + }) + }) \ No newline at end of file diff --git a/app/templates/demo_jest.js b/app/templates/demo_jest.js new file mode 100644 index 00000000..fb481b5b --- /dev/null +++ b/app/templates/demo_jest.js @@ -0,0 +1,5 @@ +import { greeting } from '../app/scripts/main.js' + +test('Say Allo!', () => { + expect(greeting()).toBe('\'Allo \'Allo!'); +}); \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html index ed9664f8..3e418190 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -116,7 +116,11 @@

'Allo, 'Allo!

<%_ } -%> + <%_ if (includeUnit) { -%> + + <%_ } else { -%> + <%_ } -%> diff --git a/app/templates/main.js b/app/templates/main.js index 3b21d0d9..cb478f12 100644 --- a/app/templates/main.js +++ b/app/templates/main.js @@ -1,4 +1,12 @@ -console.log('\'Allo \'Allo!'); +<%_ if (includeUnit) { -%> +export function greeting() { + return '\'Allo \'Allo!'; +} + +console.log(greeting()); +<%_ } else { -%> + console.log('\'Allo \'Allo!'); +<%_ } -%> <%_ if (includeBootstrap) { -%> // Uncomment to enable Bootstrap tooltips diff --git a/package.json b/package.json index 86e8b74d..7606e13d 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,6 @@ ], "dependencies": { "command-exists": "^1.2.8", - "generator-jasmine": "^2.0.1", - "generator-mocha": "^2.0.3", "mkdirp": "^0.5.1", "yeoman-generator": "^4.0.1", "yosay": "^2.0.2" diff --git a/test/general.js b/test/general.js index 2e6d42fe..4cd21e6f 100644 --- a/test/general.js +++ b/test/general.js @@ -33,8 +33,7 @@ describe('general', () => { 'app/scripts/main.js', 'app/styles/main.css', 'app/images', - 'app/fonts', - 'test' + 'app/fonts' ]); }); @@ -43,7 +42,6 @@ describe('general', () => { assert.fileContent('package.json', '"serve:dist"'); assert.fileContent('package.json', '"start"'); assert.fileContent('package.json', '"build"'); - assert.fileContent('package.json', '"test"'); assert.fileContent('package.json', '"tasks"'); }); diff --git a/test/test-framework.js b/test/test-framework.js deleted file mode 100644 index 02d95de6..00000000 --- a/test/test-framework.js +++ /dev/null @@ -1,33 +0,0 @@ -const path = require('path'); -const helpers = require('yeoman-test'); -const assert = require('yeoman-assert'); - -describe('test framework', () => { - describe('mocha', () => { - before(done => { - helpers - .run(path.join(__dirname, '../app')) - .withOptions({ 'test-framework': 'mocha' }) - .withPrompts({ features: [] }) - .on('end', done); - }); - - it('generates the expected fixture', () => { - assert.fileContent('test/index.html', 'mocha'); - }); - }); - - describe('jasmine', () => { - before(done => { - helpers - .run(path.join(__dirname, '../app')) - .withOptions({ 'test-framework': 'jasmine' }) - .withPrompts({ features: [] }) - .on('end', done); - }); - - it('generates the expected fixture', () => { - assert.fileContent('test/index.html', 'jasmine'); - }); - }); -}); diff --git a/test/tests-handler.js b/test/tests-handler.js new file mode 100644 index 00000000..23558d81 --- /dev/null +++ b/test/tests-handler.js @@ -0,0 +1,352 @@ +const path = require('path'); +const helpers = require('yeoman-test'); +const assert = require('yeoman-assert'); + +function avaPresent() { + assert.fileContent('package.json', '"test:unit": "ava --verbose"'); + assert.fileContent('package.json', '"test:unit-coverage": "nyc ava"'); + assert.fileContent( + 'package.json', + '"test:unit-watch": "ava --verbose --watch"' + ); + assert.fileContent('package.json', '"ava": '); + assert.fileContent('package.json', '"nyc": '); + assert.fileContent('package.json', '"esm": '); + assert.fileContent('package.json', '"ava": {'); + assert.file('__test__/main.test.js'); +} + +function avaNotPresent() { + assert.noFileContent('package.json', '"test:unit": "ava"'); + assert.noFileContent('package.json', '"test:unit-coverage": "nyc ava"'); + assert.noFileContent('package.json', '"test:unit-watch": "ava --watchAll"'); + assert.noFileContent('package.json', '"ava": '); + assert.noFileContent('package.json', '"nyc": '); + assert.noFileContent('package.json', '"esm": '); + assert.noFileContent('package.json', '"ava": {'); +} + +function jestPresent() { + assert.fileContent('package.json', '"test:unit": "jest"'); + assert.fileContent('package.json', '"test:unit-coverage": "jest --coverage"'); + assert.fileContent('package.json', '"test:unit-watch": "jest --watchAll"'); + assert.fileContent('package.json', '"jest": '); + assert.file('__test__/main.test.js'); +} + +function jestNotPresent() { + assert.noFileContent('package.json', '"test:unit": "jest"'); + assert.noFileContent( + 'package.json', + '"test:unit-coverage": "jest --coverage"' + ); + assert.noFileContent('package.json', '"test:unit-watch": "jest --watchAll"'); + assert.noFileContent('package.json', '"jest": '); +} + +function unitPresent() { + assert.fileContent('app/index.html', 'type="module" src="scripts/main.js"'); + assert.fileContent('app/scripts/main.js', 'export function greeting'); + assert.fileContent('package.json', '"test:unit": "'); +} + +function unitNotPresent() { + assert.noFileContent('app/index.html', 'type="module" src="scripts/main.js"'); + assert.noFileContent('app/scripts/main.js', 'export function greeting'); + assert.noFileContent('package.json', '"test:unit": "'); + assert.noFile('__test__/main.test.js'); +} + +function e2ePresent() { + assert.fileContent('package.json', '"cypress": "'); + assert.fileContent('package.json', '"start-server-and-test": "'); + assert.fileContent('package.json', '"test:e2e-open": "'); + assert.fileContent('package.json', '"test:e2e": "'); +} + +function e2eNotPresent() { + assert.noFileContent('package.json', '"cypress": "'); + assert.noFileContent('package.json', '"start-server-and-test": "'); + assert.noFileContent('package.json', '"test:e2e-open": "'); + assert.noFileContent('package.json', '"test:e2e": "'); +} + +describe('Test handler', () => { + describe('When user refused unit and e2e tests directly', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ includeTest: false }) + .on('end', done); + }); + + it('Should not add unit tests', () => { + unitNotPresent(); + }); + + it('Should not add e2e tests', () => { + e2eNotPresent(); + }); + + it('Should not create tests tasks', () => { + assert.noFileContent('package.json', '"test": '); + }); + }); + + describe('When user agrees to add test but avoid to pick unit and e2e test', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ includeTest: true, testsWanted: [] }) + .on('end', done); + }); + + it('Should not add any unit tests', () => { + unitNotPresent(); + }); + + it('Should not add e2e tests', () => { + e2eNotPresent(); + }); + + it('Should not create tests tasks', () => { + assert.noFileContent('package.json', '"test": '); + }); + }); + + describe('When user agrees to test only e2e', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ includeTest: true, testsWanted: ['includeE2e'] }) + .on('end', done); + }); + + it('Should not add any unit tests', () => { + unitNotPresent(); + }); + + it('Should add e2e tests', () => { + e2ePresent(); + }); + + it('Should not create combined tests task', () => { + assert.noFileContent( + 'package.json', + '"test": "npm run test:unit && npm run test:e2e"' + ); + }); + }); + + describe('When user agrees to e2e testing add Unit Testing (Ava)', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ + includeTest: true, + testsWanted: ['includeE2e', 'includeUnit'], + unitTestFramework: 'ava' + }) + .on('end', done); + }); + + it('Should add e2e tests', () => { + e2ePresent(); + }); + + it('Should add unit tests Basics', () => { + unitPresent(); + }); + + it('Should create combined tests task', () => { + assert.fileContent( + 'package.json', + '"test": "npm run test:unit && npm run test:e2e"' + ); + }); + + it('Should add unit tests for Ava', () => { + avaPresent(); + }); + + it('Should not add unit tests for Jest', () => { + jestNotPresent(); + }); + + it.skip('Should not add unit tests for Jasmine', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Mocha (TDD)', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Mocha (BDD)', () => { + //@TODO + }); + }); + + describe('When user agrees to add only Unit Testing (Jest)', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ + includeTest: true, + testsWanted: ['includeUnit'], + unitTestFramework: 'jest' + }) + .on('end', done); + }); + + it('Should not add e2e tests', () => { + e2eNotPresent(); + }); + + it('Should add unit tests Basics', () => { + unitPresent(); + }); + + it('Should add unit tests for Jest', () => { + jestPresent(); + }); + + it('Should not add unit tests for Ava', () => { + avaNotPresent(); + }); + + it.skip('Should not add unit tests for Jasmine', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Mocha (TDD)', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Mocha (BDD)', () => { + //@TODO + }); + }); + + describe('When user agrees to add only Unit Testing (Jasmine)', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ + includeTest: true, + testsWanted: ['includeUnit'], + unitTestFramework: 'jasmine' + }) + .on('end', done); + }); + + it('Should not add e2e tests', () => { + e2eNotPresent(); + }); + + it.skip('Should add unit tests Basics', () => { + unitPresent(); + }); + + it.skip('Should add unit tests for Jasmine', () => { + //@TODO + }); + + it('Should not add unit tests for Jest', () => { + jestNotPresent(); + }); + + it('Should not add unit tests for Ava', () => { + avaNotPresent(); + }); + + it.skip('Should not add unit tests for Mocha (TDD)', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Mocha (BDD)', () => { + //@TODO + }); + }); + + describe('When user agrees to add only Unit Testing for Mocha (TDD)', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ + includeTest: true, + testsWanted: ['includeUnit'], + unitTestFramework: 'mochaTdd' + }) + .on('end', done); + }); + + it('Should not add e2e tests', () => { + e2eNotPresent(); + }); + + it.skip('Should add unit tests Basics', () => { + unitPresent(); + }); + + it.skip('Should add unit tests for Mocha (TDD)', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Jasmine', () => { + //@TODO + }); + + it('Should not add unit tests for Jest', () => { + jestNotPresent(); + }); + + it('Should not add unit tests for Ava', () => { + avaNotPresent(); + }); + + it.skip('Should not add unit tests for Mocha (BDD)', () => { + //@TODO + }); + }); + + describe('When user agrees to add only Unit Testing for Mocha (BDD)', () => { + before(done => { + helpers + .run(path.join(__dirname, '../app')) + .withPrompts({ + includeTest: true, + testsWanted: ['includeUnit'], + unitTestFramework: 'mochaBdd' + }) + .on('end', done); + }); + + it('Should not add e2e tests', () => { + e2eNotPresent(); + }); + + it.skip('Should add unit tests Basics', () => { + unitPresent(); + }); + + it.skip('Should add unit tests for Mocha (BDD)', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Mocha (TDD)', () => { + //@TODO + }); + + it.skip('Should not add unit tests for Jasmine', () => { + //@TODO + }); + + it('Should not add unit tests for Jest', () => { + jestNotPresent(); + }); + + it('Should not add unit tests for Ava', () => { + avaNotPresent(); + }); + }); +});