diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..83eb143 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,11 @@ +// Added jsx-quotes and react/jsx-quotes to remove deprecation warning ("The react/jsx-quotes rule is deprecated. Please use the jsx-quotes rule instead.") +{ + "env": { + "jasmine": true + }, + "extends": "eslint-config-airbnb-es5", + "rules": { + "jsx-quotes": [2, "prefer-double"], + "react/jsx-quotes": 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md index 02e91b9..ebfc70e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,57 @@ -# gulp-tasks -Collection of reusable gulp tasks + +# Gulp Tasks + +> Collection of reusable gulp tasks + +## Usage + +Set up Gulp: https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md + +Install this repository as an NPM dependency to integrate its gulp tasks: + +In your package.json: +``` +{ + ... + "devDependencies": { + ... + "gulp-tasks": "git@github.com:SmaatoUI/gulp-tasks.git#v0.1.0", + ... + } + ... +} +``` + +On the command line: +```bash +npm install +``` + +In your gulpfile.js: +```javascript +var lint = require('gulp-tasks/src/lint'); + +lint.eslint({ + src: './**/*.js', + taskName: 'lint' +}); +``` + +Use 'lint' in other gulp tasks or run it from the command line: +```bash +gulp lint +``` + +## Available Gulp Tasks + +### assets.copy +### deploy.awsS3 +### lint.eslint +### localWebServer.connect +### scripts.browserifyAndWatchify +### scripts.uglify +### styles.compassAndPostcss +### styles.minifyCss +### templates.jade +### tests.karma +### tests.nightwatch diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..cbc1e85 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,20 @@ + +var gulp = require('gulp'); +var gulpJasmine = require('gulp-jasmine'); +var lint = require('./src/lint'); + +lint.eslint({ + src: [ + './*.js', + './src/**/*.js' + ] +}); + +gulp.task('test', ['lint'], () => { + return gulp.src('./src/**/*.js') + .pipe(gulpJasmine({ + verbose: true + })); +}); + +gulp.task('default', ['test']); diff --git a/package.json b/package.json new file mode 100644 index 0000000..28e7283 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "gulp-tasks", + "private": true, + "version": "0.1.0", + "description": "Collection of reusable gulp tasks", + "scripts": { + "test": "gulp" + }, + "devDependencies": { + "autoprefixer": "6.0.3", + "babel-eslint": "4.1.3", + "babelify": "6.4.0", + "browserify": "12.0.0", + "browserify-hmr": "0.3.1", + "css-mqpacker": "4.0.0", + "del": "2.0.2", + "eslint": "1.7.1", + "eslint-config-airbnb-es5": "1.0.8", + "eslint-plugin-react": "3.6.2", + "gulp": "3.9.0", + "gulp-awspublish": "3.0.1", + "gulp-babel": "5.3.0", + "gulp-compass": "2.1.0", + "gulp-connect": "2.2.0", + "gulp-cssmin": "0.1.7", + "gulp-eslint": "1.0.0", + "gulp-jade": "1.1.0", + "gulp-jasmine": "2.1.0", + "gulp-nightwatch": "0.2.6", + "gulp-postcss": "6.0.1", + "gulp-rename": "1.2.2", + "gulp-replace": "0.5.4", + "gulp-uglify": "1.4.2", + "gulp-util": "3.0.7", + "karma": "0.13.14", + "minimist": "1.2.0", + "pre-commit": "1.1.1", + "run-sequence": "1.1.4", + "vinyl-source-stream": "1.1.0", + "watchify": "3.5.0" + }, + "pre-commit": [ + "test" + ] +} diff --git a/src/assets.js b/src/assets.js new file mode 100644 index 0000000..c7bc486 --- /dev/null +++ b/src/assets.js @@ -0,0 +1,27 @@ + +/** + * @description Asset related tasks + */ + +// Copy and trigger live reload +module.exports.copy = (config) => { + var gulp = require('gulp'); + var gulpConnect = require('gulp-connect'); + + var ASSETS_CONFIG = Object.assign({ + dst: './dist/assets', + src: './src/assets/**/*', + taskName: 'assets' + }, config); + + if (!ASSETS_CONFIG.dst || !ASSETS_CONFIG.src) { + throw new Error('Invalid configuration: value of dst needs to be a path and value of src needs to be a glob or an array of globs.'); + } + + gulp.task(ASSETS_CONFIG.taskName, () => { + return gulp + .src(ASSETS_CONFIG.src) + .pipe(gulp.dest(ASSETS_CONFIG.dst)) + .pipe(gulpConnect.reload()); + }); +}; diff --git a/src/assets.spec.js b/src/assets.spec.js new file mode 100644 index 0000000..ad6efdb --- /dev/null +++ b/src/assets.spec.js @@ -0,0 +1,64 @@ + +describe('Assets Gulp Task Module', () => { + var assets = require('./assets.js'); + var gulp = require('gulp'); + var runSequence = require('run-sequence'); + + it('is an object', () => { + expect(typeof assets).toBe('object'); + }); + + describe('Copy Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof assets.copy).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return assets.copy({ + dst: false + }); + }).toThrow(); + + expect(() => { + return assets.copy({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + assets.copy({ + dst: './shouldNotExist/dist/assets', + src: './shouldNotExist/src/assets/**/*', + taskName: 'assetsTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.assetsTest).toBeDefined(); + }); + + describe('Copy Gulp Task', () => { + beforeEach((done) => { + /* + The provided done function has to be called to proceed and therefore + allows to do async operations here + runSequence runs gulp tasks in order and accepts a callback as the last + argument which is used to ensure that the gulp task finished before + assertions are evaluated + */ + runSequence( + 'assetsTest', + done + ); + }); + + it('completes successfully', () => { + expect(gulp.tasks.assetsTest.done).toBe(true); + }); + }); + }); +}); diff --git a/src/deploy.js b/src/deploy.js new file mode 100644 index 0000000..a6c0ff9 --- /dev/null +++ b/src/deploy.js @@ -0,0 +1,51 @@ + +/** + * @description Deployment related tasks + */ + +// AWS SDK +module.exports.awsS3 = (config) => { + var gulp = require('gulp'); + var gulpAwspublish = require('gulp-awspublish'); + var minimist = require('minimist'); + + var DEPLOY_CONFIG = Object.assign({ + bucketEnv: 'AWS_S3_BUCKET', + src: './dist/**/*.*', + taskName: 'deploy' + }, config); + + if (!DEPLOY_CONFIG.src) { + throw new Error('Invalid configuration: value of src needs to be a glob or an array of globs.'); + } + + gulp.task(DEPLOY_CONFIG.taskName, () => { + /* + To manually deploy the working copy the following command can be used: + gulp deploy --accessKeyId=XXX --bucket=XXX --secretAccessKey=XXX + + The arguments can be provided via the command line as in this example where + - accessKeyId is the AWS access key id, + - bucket is the AWS S3 bucket and + - secretAccessKey is the AWS secret access key. + + If the arguments are not provided via the command line they will be read + from the environment variables + - AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY + - the environment variable for the bucket is set via the bucketEnv argument + */ + var commandLineArguments = minimist(process.argv.slice(2)); + var publisher = gulpAwspublish.create({ + accessKeyId: commandLineArguments.accessKeyId || process.env.AWS_ACCESS_KEY_ID, + params: { + Bucket: commandLineArguments.bucket || process.env[DEPLOY_CONFIG.bucketEnv] + }, + secretAccessKey: commandLineArguments.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY + }); + return gulp.src(DEPLOY_CONFIG.src) + .pipe(publisher.publish()) + .pipe(publisher.sync()) + .pipe(gulpAwspublish.reporter()); + }); +}; diff --git a/src/deploy.spec.js b/src/deploy.spec.js new file mode 100644 index 0000000..ff839df --- /dev/null +++ b/src/deploy.spec.js @@ -0,0 +1,37 @@ + +describe('Deploy Gulp Task Module', () => { + var deploy = require('./deploy.js'); + var gulp = require('gulp'); + + it('is an object', () => { + expect(typeof deploy).toBe('object'); + }); + + describe('AWS S3 Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof deploy.awsS3).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return deploy.awsS3({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + deploy.awsS3({ + bucketEnv: 'SHOULD_NOT_EXIST', + src: './shouldNotExist/dist/**/*.*', + taskName: 'deployTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.deployTest).toBeDefined(); + }); + }); +}); diff --git a/src/lint.js b/src/lint.js new file mode 100644 index 0000000..f736e35 --- /dev/null +++ b/src/lint.js @@ -0,0 +1,29 @@ + +/** + * @description Lint related tasks + */ + +// ESLint +module.exports.eslint = (config) => { + var gulp = require('gulp'); + var gulpEslint = require('gulp-eslint'); + + var LINT_CONFIG = Object.assign({ + src: './src/**/*.js', + taskName: 'lint' + }, config); + + if (!LINT_CONFIG.src) { + throw new Error('Invalid configuration: value of src needs to be a glob or an array of globs.'); + } + + gulp.task(LINT_CONFIG.taskName, () => { + return gulp.src(LINT_CONFIG.src) + .pipe(gulpEslint()) + .pipe(gulpEslint.format()) + .pipe(gulpEslint.failAfterError()) + .on('error', () => { + throw new Error('Linting failed'); + }); + }); +}; diff --git a/src/lint.spec.js b/src/lint.spec.js new file mode 100644 index 0000000..905fdd1 --- /dev/null +++ b/src/lint.spec.js @@ -0,0 +1,57 @@ + +describe('Lint Gulp Task Module', () => { + var gulp = require('gulp'); + var lint = require('./lint.js'); + var runSequence = require('run-sequence'); + + it('is an object', () => { + expect(typeof lint).toBe('object'); + }); + + describe('ESLint Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof lint.eslint).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return lint.eslint({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + lint.eslint({ + src: './shouldNotExist/src/**/*.js', + taskName: 'lintTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.lintTest).toBeDefined(); + }); + + describe('ESLint Gulp Task', () => { + beforeEach((done) => { + /* + The provided done function has to be called to proceed and therefore + allows to do async operations here + runSequence runs gulp tasks in order and accepts a callback as the last + argument which is used to ensure that the gulp task finished before + assertions are evaluated + */ + runSequence( + 'lintTest', + done + ); + }); + + it('completes successfully', () => { + expect(gulp.tasks.lintTest.done).toBe(true); + }); + }); + }); +}); diff --git a/src/localWebServer.js b/src/localWebServer.js new file mode 100644 index 0000000..14d111c --- /dev/null +++ b/src/localWebServer.js @@ -0,0 +1,18 @@ + +/** + * @description Local web server tasks + */ + +// Connect +module.exports.connect = (config) => { + var gulp = require('gulp'); + var gulpConnect = require('gulp-connect'); + + var CONNECT_CONFIG = Object.assign({ + taskName: 'serveLocally' + }, config); + + gulp.task(CONNECT_CONFIG.taskName, () => { + gulpConnect.server(CONNECT_CONFIG); + }); +}; diff --git a/src/localWebServer.spec.js b/src/localWebServer.spec.js new file mode 100644 index 0000000..5fc66c6 --- /dev/null +++ b/src/localWebServer.spec.js @@ -0,0 +1,27 @@ + +describe('Local Web Server Gulp Task Module', () => { + var gulp = require('gulp'); + var localWebServer = require('./localWebServer.js'); + + it('is an object', () => { + expect(typeof localWebServer).toBe('object'); + }); + + describe('Connect Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof localWebServer.connect).toBe('function'); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + localWebServer.connect({ + taskName: 'localWebServerTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.localWebServerTest).toBeDefined(); + }); + }); +}); diff --git a/src/scripts.js b/src/scripts.js new file mode 100644 index 0000000..b4e5bf3 --- /dev/null +++ b/src/scripts.js @@ -0,0 +1,107 @@ + +/** + * @description Script related tasks + */ + +// Browserify, Browserify-HMR, Babelify, Watchify +module.exports.browserifyAndWatchify = (config) => { + var babelify = require('babelify'); + var browserify = require('browserify'); + var browserifyHmr = require('browserify-hmr'); + var gulp = require('gulp'); + var gulpUtil = require('gulp-util'); + var vinylSourceStream = require('vinyl-source-stream'); + var watchify = require('watchify'); + + var BROWSERIFY_CONFIG = {}; + var SCRIPTS_CONFIG = Object.assign({ + dst: './dist/js', + src: './src/index.js', + taskName: 'scripts' + }, config); + + if (!SCRIPTS_CONFIG.dst || !SCRIPTS_CONFIG.src) { + throw new Error('Invalid configuration: value of dst needs to be a path and value of src needs to be a glob or an array of globs.'); + } + + var bundleUsingBrowserify = (withWatchify) => { + /* + Watchify, a watch mode for browserify builds, will be enabled if + withWatchify is true. The task will not exit and if a source file is changed + the browserify bundler will emit an update event and the scripts will be + rewritten. Since the browserify bundler is changed incrementally it is much + faster than creating a new browserify bundler. + If withWatchify is false the scripts will only be written once and the task + will exit. + */ + var writeScriptsFromBundle = (bundle) => { + return bundle + .pipe(vinylSourceStream('dist.js')) + .pipe(gulp.dest(SCRIPTS_CONFIG.dst)); + }; + var bundler; + var startTime; + + if (withWatchify) { + BROWSERIFY_CONFIG = watchify.args; + } + + BROWSERIFY_CONFIG.debug = (process.env.NODE_ENV !== 'production'); + + bundler = browserify(SCRIPTS_CONFIG.src, BROWSERIFY_CONFIG); + + if (withWatchify) { + bundler.plugin(browserifyHmr); + bundler = watchify(bundler); + } + + bundler.transform(babelify); + + if (withWatchify) { + bundler.on('update', () => { + gulpUtil.log('Starting to update scripts'); + startTime = (new Date().getTime()); + + writeScriptsFromBundle(bundler.bundle()) + .on('end', () => { + gulpUtil.log('Finished updating scripts after', ((new Date().getTime()) - startTime), 'ms'); + }); + }); + } + + return writeScriptsFromBundle(bundler.bundle()); + }; + + gulp.task(SCRIPTS_CONFIG.taskName, () => { + return bundleUsingBrowserify(false); + }); + + gulp.task((SCRIPTS_CONFIG.taskName + 'ThenWatch'), () => { + return bundleUsingBrowserify(true); + }); +}; + +// UglifyJS +module.exports.uglify = (config) => { + var gulp = require('gulp'); + var gulpRename = require('gulp-rename'); + var gulpUglify = require('gulp-uglify'); + + var SCRIPTS_CONFIG = Object.assign({ + src: './dist/js', + taskName: 'minifyScripts' + }, config); + + if (!SCRIPTS_CONFIG.src) { + throw new Error('Invalid configuration: value of src needs to be a glob or an array of globs.'); + } + + gulp.task(SCRIPTS_CONFIG.taskName, () => { + return gulp.src((SCRIPTS_CONFIG.src + '/dist.js')) + .pipe(gulpUglify({ + mangle: true + })) + .pipe(gulpRename('dist.min.js')) + .pipe(gulp.dest(SCRIPTS_CONFIG.src)); + }); +}; diff --git a/src/scripts.spec.js b/src/scripts.spec.js new file mode 100644 index 0000000..7e18e89 --- /dev/null +++ b/src/scripts.spec.js @@ -0,0 +1,71 @@ + +describe('Scripts Gulp Task Module', () => { + var gulp = require('gulp'); + var scripts = require('./scripts.js'); + + it('is an object', () => { + expect(typeof scripts).toBe('object'); + }); + + describe('Browserify/Watchify Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof scripts.browserifyAndWatchify).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return scripts.browserifyAndWatchify({ + dst: false + }); + }).toThrow(); + + expect(() => { + return scripts.browserifyAndWatchify({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + scripts.browserifyAndWatchify({ + dst: './shouldNotExist/dist/js', + src: './shouldNotExist/src/index.js', + taskName: 'scriptsTest' + }); + }).not.toThrow(); + }); + + it('registers two gulp tasks', () => { + expect(gulp.tasks.scriptsTest).toBeDefined(); + expect(gulp.tasks.scriptsTestThenWatch).toBeDefined(); + }); + }); + + describe('Uglify Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof scripts.uglify).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return scripts.uglify({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + scripts.uglify({ + src: './shouldNotExist/dist/js', + taskName: 'minifyScriptsTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.minifyScriptsTest).toBeDefined(); + }); + }); +}); diff --git a/src/styles.js b/src/styles.js new file mode 100644 index 0000000..c205ada --- /dev/null +++ b/src/styles.js @@ -0,0 +1,105 @@ + +/** + * @description Style related tasks + */ + +// Compass and PostCSS +module.exports.compassAndPostcss = (config) => { + var autoprefixer = require('autoprefixer'); + var cssMqpacker = require('css-mqpacker'); + var del = require('del'); + var gulp = require('gulp'); + var gulpCompass = require('gulp-compass'); + var gulpConnect = require('gulp-connect'); + var gulpPostcss = require('gulp-postcss'); + var gulpRename = require('gulp-rename'); + var gulpReplace = require('gulp-replace'); + var runSequence = require('run-sequence'); + + var STYLES_CONFIG = Object.assign({ + dst: './dist/css', + src: './src/**/*.scss', + taskName: 'styles' + }, config); + + if (!STYLES_CONFIG.dst || !STYLES_CONFIG.src) { + throw new Error('Invalid configuration: value of dst needs to be a path and value of src needs to be a glob or an array of globs.'); + } + + gulp.task((STYLES_CONFIG.taskName + ':compass'), () => { + return gulp.src(STYLES_CONFIG.src) + .pipe(gulpCompass({ + css: STYLES_CONFIG.dst, + import_path: './node_modules', + sass: './app', + sourcemap: true + })) + .on('error', () => { + throw new Error('Compass failed'); + }) + .pipe(gulp.dest(STYLES_CONFIG.dst)); + }); + + gulp.task((STYLES_CONFIG.taskName + ':postCss'), () => { + return gulp.src((STYLES_CONFIG.dst + '/index.css')) + .pipe(gulpPostcss([ + autoprefixer({ + browsers: ['last 2 versions'] + }), + cssMqpacker + ])) + .pipe(gulp.dest(STYLES_CONFIG.dst)); + }); + + gulp.task((STYLES_CONFIG.taskName + ':renameDstIndexCss'), () => { + return gulp.src((STYLES_CONFIG.dst + '/*')) + // Replace occurences of index.css with dist.css inside of files + .pipe(gulpReplace('index.css', 'dist.css')) + // Rename files from *index*.* to *dist*.* + .pipe(gulpRename((path) => { + path.basename = path.basename.replace('index', 'dist'); + })) + .pipe(gulp.dest(STYLES_CONFIG.dst)) + .pipe(gulpConnect.reload()); + }); + + gulp.task((STYLES_CONFIG.taskName + ':deleteDstIndexCss'), () => { + return del([ + (STYLES_CONFIG.dst + '/index.css'), + (STYLES_CONFIG.dst + '/index.css.map') + ]); + }); + + gulp.task(STYLES_CONFIG.taskName, (callback) => { + runSequence( + (STYLES_CONFIG.taskName + ':compass'), + (STYLES_CONFIG.taskName + ':postCss'), + (STYLES_CONFIG.taskName + ':renameDstIndexCss'), + (STYLES_CONFIG.taskName + ':deleteDstIndexCss'), + callback + ); + }); +}; + +// Minifying styles with clean-css +module.exports.minifyCss = (config) => { + var gulp = require('gulp'); + var gulpCssmin = require('gulp-cssmin'); + var gulpRename = require('gulp-rename'); + + var STYLES_CONFIG = Object.assign({ + src: './dist/css', + taskName: 'minifyStyles' + }, config); + + if (!STYLES_CONFIG.src) { + throw new Error('Invalid configuration: value of src needs to be a glob or an array of globs.'); + } + + gulp.task(STYLES_CONFIG.taskName, () => { + return gulp.src((STYLES_CONFIG.src + '/dist.css')) + .pipe(gulpCssmin()) + .pipe(gulpRename('dist.min.css')) + .pipe(gulp.dest(STYLES_CONFIG.src)); + }); +}; diff --git a/src/styles.spec.js b/src/styles.spec.js new file mode 100644 index 0000000..f7289a6 --- /dev/null +++ b/src/styles.spec.js @@ -0,0 +1,74 @@ + +describe('Styles Gulp Task Module', () => { + var gulp = require('gulp'); + var styles = require('./styles.js'); + + it('is an object', () => { + expect(typeof styles).toBe('object'); + }); + + describe('Compass/Postcss Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof styles.compassAndPostcss).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return styles.compassAndPostcss({ + dst: false + }); + }).toThrow(); + + expect(() => { + return styles.compassAndPostcss({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + styles.compassAndPostcss({ + dst: './shouldNotExist/dist/css', + src: './shouldNotExist/src/**/*.scss', + taskName: 'stylesTest' + }); + }).not.toThrow(); + }); + + it('registers five gulp tasks', () => { + expect(gulp.tasks.stylesTest).toBeDefined(); + expect(gulp.tasks['stylesTest:compass']).toBeDefined(); + expect(gulp.tasks['stylesTest:postCss']).toBeDefined(); + expect(gulp.tasks['stylesTest:renameDstIndexCss']).toBeDefined(); + expect(gulp.tasks['stylesTest:deleteDstIndexCss']).toBeDefined(); + }); + }); + + describe('clean-css Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof styles.minifyCss).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return styles.minifyCss({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + styles.minifyCss({ + src: './shouldNotExist/dist/css', + taskName: 'minifyStylesTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.minifyStylesTest).toBeDefined(); + }); + }); +}); diff --git a/src/templates.js b/src/templates.js new file mode 100644 index 0000000..a7e407e --- /dev/null +++ b/src/templates.js @@ -0,0 +1,32 @@ + +/** + * @description Template related tasks + */ + +// Jade +module.exports.jade = (config) => { + var gulp = require('gulp'); + var gulpConnect = require('gulp-connect'); + var gulpJade = require('gulp-jade'); + + var TEMPLATES_CONFIG = Object.assign({ + dst: './dist', + src: './src/**/*.jade', + taskName: 'templates' + }, config); + + if (!TEMPLATES_CONFIG.dst || !TEMPLATES_CONFIG.src) { + throw new Error('Invalid configuration: value of dst needs to be a path and value of src needs to be a glob or an array of globs.'); + } + + gulp.task(TEMPLATES_CONFIG.taskName, () => { + return gulp.src(TEMPLATES_CONFIG.src) + .pipe(gulpJade({ + locals: { + DATE_TIME: (new Date().getTime()) + } + })) + .pipe(gulp.dest(TEMPLATES_CONFIG.dst)) + .pipe(gulpConnect.reload()); + }); +}; diff --git a/src/templates.spec.js b/src/templates.spec.js new file mode 100644 index 0000000..1f629c6 --- /dev/null +++ b/src/templates.spec.js @@ -0,0 +1,64 @@ + +describe('Templates Gulp Task Module', () => { + var gulp = require('gulp'); + var runSequence = require('run-sequence'); + var templates = require('./templates.js'); + + it('is an object', () => { + expect(typeof templates).toBe('object'); + }); + + describe('Jade Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof templates.jade).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return templates.jade({ + dst: false + }); + }).toThrow(); + + expect(() => { + return templates.jade({ + src: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + templates.jade({ + dst: './shouldNotExist/dist', + src: './shouldNotExist/src/**/*.jade', + taskName: 'templatesTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.templatesTest).toBeDefined(); + }); + + describe('Jade Gulp Task', () => { + beforeEach((done) => { + /* + The provided done function has to be called to proceed and therefore + allows to do async operations here + runSequence runs gulp tasks in order and accepts a callback as the last + argument which is used to ensure that the gulp task finished before + assertions are evaluated + */ + runSequence( + 'templatesTest', + done + ); + }); + + it('completes successfully', () => { + expect(gulp.tasks.templatesTest.done).toBe(true); + }); + }); + }); +}); diff --git a/src/tests.js b/src/tests.js new file mode 100644 index 0000000..7ee9734 --- /dev/null +++ b/src/tests.js @@ -0,0 +1,118 @@ + +/** + * @description Test related tasks + */ + +// Karma +module.exports.karma = (config) => { + var gulp = require('gulp'); + var karmaServer = require('karma').Server; + + var KARMA_CONFIG = Object.assign({ + taskName: 'unit' + }, config); + + gulp.task(KARMA_CONFIG.taskName, (callback) => { + return karmaServer.start(KARMA_CONFIG, (exitStatus) => { + if (exitStatus) { + throw new Error('Unit testing failed'); + } else { + callback(exitStatus); + } + }); + }); +}; + +// Nightwatch +module.exports.nightwatch = (config) => { + var del = require('del'); + var gulp = require('gulp'); + var gulpBabel = require('gulp-babel'); + var gulpConnect = require('gulp-connect'); + var gulpNightwatch = require('gulp-nightwatch'); + var gulpReplace = require('gulp-replace'); + var runSequence = require('run-sequence'); + + var NIGHTWATCH_CONFIG = Object.assign({ + connect: { + root: './dist' + }, + dir: './e2e/', + shim: false, + taskName: 'e2e' + }, config); + + if (!NIGHTWATCH_CONFIG.connect || !NIGHTWATCH_CONFIG.dir) { + throw new Error('Invalid configuration: value of connect needs to be an object and value of dir needs to be a path.'); + } + + gulp.task((NIGHTWATCH_CONFIG.taskName + ':startConnect'), () => { + gulpConnect.server(NIGHTWATCH_CONFIG.connect); + }); + + gulp.task((NIGHTWATCH_CONFIG.taskName + ':clean'), (callback) => { + return del([ + NIGHTWATCH_CONFIG.dir + 'dist/**/*', + NIGHTWATCH_CONFIG.dir + 'dist/' + ], callback); + }); + + gulp.task((NIGHTWATCH_CONFIG.taskName + ':compileTests'), () => { + return gulp.src(NIGHTWATCH_CONFIG.dir + 'src/**/*.js') + .pipe(gulpBabel()) + .pipe(gulp.dest(NIGHTWATCH_CONFIG.dir + 'dist')); + }); + + gulp.task((NIGHTWATCH_CONFIG.taskName + ':addShim'), (callback) => { + if (NIGHTWATCH_CONFIG.shim) { + return gulp.src(['./dist/index.html']) + .pipe(gulpReplace(' { + return gulp.src('') + .pipe(gulpNightwatch({ + configFile: NIGHTWATCH_CONFIG.dir + 'config/nightwatch.json', + cliArgs: ['--env phantomjs'] + })) + .on('error', () => { + // If there's an error we need to complete the task and remove the shim + NIGHTWATCH_CONFIG.wasNightwatchFailing = true; + this.emit('end'); + }); + }); + + gulp.task((NIGHTWATCH_CONFIG.taskName + ':removeShim'), (callback) => { + if (NIGHTWATCH_CONFIG.shim) { + return gulp.src(['./dist/index.html']) + .pipe(gulpReplace((NIGHTWATCH_CONFIG.shim + ' { + gulpConnect.serverClose(); + }); + + gulp.task(NIGHTWATCH_CONFIG.taskName, (callback) => { + runSequence( + (NIGHTWATCH_CONFIG.taskName + ':startConnect'), + (NIGHTWATCH_CONFIG.taskName + ':clean'), + (NIGHTWATCH_CONFIG.taskName + ':compileTests'), + (NIGHTWATCH_CONFIG.taskName + ':addShim'), + (NIGHTWATCH_CONFIG.taskName + ':nightwatch'), + (NIGHTWATCH_CONFIG.taskName + ':removeShim'), + (NIGHTWATCH_CONFIG.taskName + ':stopConnect'), + (error) => { + if (NIGHTWATCH_CONFIG.wasNightwatchFailing) { + throw new Error('E2E testing failed'); + } + callback(error); + } + ); + }); +}; diff --git a/src/tests.spec.js b/src/tests.spec.js new file mode 100644 index 0000000..adccf36 --- /dev/null +++ b/src/tests.spec.js @@ -0,0 +1,71 @@ + +describe('Tests Gulp Task Module', () => { + var gulp = require('gulp'); + var tests = require('./tests.js'); + + it('is an object', () => { + expect(typeof tests).toBe('object'); + }); + + describe('Karma Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof tests.karma).toBe('function'); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + tests.karma({ + taskName: 'unitTest' + }); + }).not.toThrow(); + }); + + it('registers a gulp task', () => { + expect(gulp.tasks.unitTest).toBeDefined(); + }); + }); + + describe('Nightwatch Gulp Task Declaration', () => { + it('is a function', () => { + expect(typeof tests.nightwatch).toBe('function'); + }); + + it('can not be called with an invalid configuration', () => { + expect(() => { + return tests.nightwatch({ + connect: false + }); + }).toThrow(); + + expect(() => { + return tests.nightwatch({ + dir: false + }); + }).toThrow(); + }); + + it('can be called with a valid configuration', () => { + expect(() => { + tests.nightwatch({ + connect: { + root: './shouldNotExist/dist' + }, + dir: './shouldNotExist/e2e/', + shim: '', + taskName: 'e2eTest' + }); + }).not.toThrow(); + }); + + it('registers eight gulp tasks', () => { + expect(gulp.tasks.e2eTest).toBeDefined(); + expect(gulp.tasks['e2eTest:startConnect']).toBeDefined(); + expect(gulp.tasks['e2eTest:clean']).toBeDefined(); + expect(gulp.tasks['e2eTest:compileTests']).toBeDefined(); + expect(gulp.tasks['e2eTest:addShim']).toBeDefined(); + expect(gulp.tasks['e2eTest:nightwatch']).toBeDefined(); + expect(gulp.tasks['e2eTest:removeShim']).toBeDefined(); + expect(gulp.tasks['e2eTest:stopConnect']).toBeDefined(); + }); + }); +});