diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba2a97b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +coverage diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a5103ed --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +sudo: false +language: node_js +node_js: + - v4 + - v5 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c5b7a23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Alex Van Camp (http://alexvan.camp) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..38df29a --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# generator-nodecg [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] +> Yeoman generator for [NodeCG](http://nodecg.com/) bundles + +## Installation + +First, install [Yeoman](http://yeoman.io) and generator-nodecg using [npm](https://www.npmjs.com/) +(we assume you have pre-installed [node.js](https://nodejs.org/)). + +```bash +npm install -g yo +npm install -g generator-nodecg +``` + +Then generate your new bundle: + +```bash +yo nodecg +``` + +generator-nodecg also has specific sub-generators that can be used on existing bundles: + +```bash +yo nodecg:panel +yo nodecg:graphic +yo nodecg:extension +``` + +## Getting To Know Yeoman + +Yeoman has a heart of gold. He's a person with feelings and opinions, but he's very easy to work with. +If you think he's too opinionated, he can be easily convinced. +Feel free to [learn more about him](http://yeoman.io/). + +## Special Thanks + +Significant portions of this generator are based on [generator-node](https://github.com/yeoman/generator-node). +We thank the developers of [generator-node](https://github.com/yeoman/generator-node) and +[Yeoman](http://yeoman.io/) for their years of hard work. + +## License + +MIT © [Alex Van Camp](http://alexvan.camp) + + +[npm-image]: https://badge.fury.io/js/generator-nodecg.svg +[npm-url]: https://npmjs.org/package/generator-nodecg +[travis-image]: https://travis-ci.org/nodecg/generator-nodecg.svg?branch=master +[travis-url]: https://travis-ci.org/nodecg/generator-nodecg +[daviddm-image]: https://david-dm.org/nodecg/generator-nodecg.svg?theme=shields.io +[daviddm-url]: https://david-dm.org/nodecg/generator-nodecg diff --git a/generators/app/index.js b/generators/app/index.js new file mode 100644 index 0000000..7cf0d57 --- /dev/null +++ b/generators/app/index.js @@ -0,0 +1,248 @@ +'use strict'; + +var path = require('path'); +var yeoman = require('yeoman-generator'); +var chalk = require('chalk'); +var yosay = require('yosay'); +var _ = require('lodash'); +var askName = require('inquirer-npm-name'); +var extend = require('deep-extend'); +var mkdirp = require('mkdirp'); +var githubUsername = require('github-username'); +var parseAuthor = require('parse-author'); + +module.exports = yeoman.Base.extend({ + constructor: function () { + yeoman.generators.Base.apply(this, arguments); + + this.option('editorconfig', { + type: Boolean, + required: false, + desc: 'Add NodeCG\'s recommended editorconfig to your bundle' + }); + }, + + initializing: function () { + // Have Yeoman greet the user. + this.log(yosay( + 'Welcome to the ' + chalk.red('NodeCG bundle') + ' generator!' + )); + + this.pkg = this.fs.readJSON(this.destinationPath('package.json'), {}); + + // Pre set the default props from the information we have at this point + this.props = { + name: this.pkg.name, + description: this.pkg.description, + version: this.pkg.version, + homepage: this.pkg.homepage + }; + + if (_.isObject(this.pkg.author)) { + this.props.authorName = this.pkg.author.name; + this.props.authorEmail = this.pkg.author.email; + this.props.authorUrl = this.pkg.author.url; + } else if (_.isString(this.pkg.author)) { + var info = parseAuthor(this.pkg.author); + this.props.authorName = info.name; + this.props.authorEmail = info.email; + this.props.authorUrl = info.url; + } + }, + + prompting: { + askForModuleName: function () { + var done = this.async(); + + askName({ + name: 'name', + message: 'Your bundle Name', + default: _.kebabCase(path.basename(process.cwd())), + filter: _.kebabCase, + validate: function (str) { + return str.length > 0; + } + }, this, function (name) { + this.props.name = name; + done(); + }.bind(this)); + }, + + askFor: function () { + var done = this.async(); + + var prompts = [{ + name: 'description', + message: 'Description', + when: !this.props.description + }, { + name: 'homepage', + message: 'Project homepage url', + when: !this.props.homepage + }, { + name: 'authorName', + message: 'Author\'s Name', + when: !this.props.authorName, + default: this.user.git.name(), + store: true + }, { + name: 'authorEmail', + message: 'Author\'s Email', + when: !this.props.authorEmail, + default: this.user.git.email(), + store: true + }, { + name: 'authorUrl', + message: 'Author\'s Homepage', + when: !this.props.authorUrl, + store: true + }, { + name: 'keywords', + message: 'Package keywords (comma to split)', + when: !this.pkg.keywords, + filter: function (words) { + return words.split(/\s*,\s*/g); + } + }, { + name: 'compatibleRange', + message: 'What semver range of NodeCG versions is this bundle compatible with?', + type: 'input', + default: '~0.7.0' + }, { + name: 'dashboardPanel', + message: 'Would you like to make a dashboard panel for your bundle?', + type: 'confirm' + }, { + name: 'graphic', + message: 'Would you like to make a graphic for your bundle?', + type: 'confirm' + }, { + name: 'extension', + message: 'Would you like to add an extension to your bundle?', + type: 'confirm' + }]; + + this.prompt(prompts, function (props) { + this.props = extend(this.props, props); + done(); + }.bind(this)); + }, + + askForGithubAccount: function () { + var done = this.async(); + + githubUsername(this.props.authorEmail, function (err, username) { + if (err) { + username = username || ''; + } + this.prompt({ + name: 'githubAccount', + message: 'GitHub username or organization', + default: username + }, function (prompt) { + this.props.githubAccount = prompt.githubAccount; + done(); + }.bind(this)); + }.bind(this)); + } + }, + + writing: function () { + // Re-read the content at this point because a composed generator might modify it. + var currentPkg = this.fs.readJSON(this.destinationPath('package.json'), {}); + + var pkg = extend({ + name: _.kebabCase(this.props.name), + version: '0.0.0', + description: this.props.description, + homepage: this.props.homepage, + author: { + name: this.props.authorName, + email: this.props.authorEmail, + url: this.props.authorUrl + }, + files: [ + 'dashboard', + 'graphics', + 'extension.js', + 'extension' + ], + keywords: [ + 'nodecg-bundle' + ], + nodecg: { + compatibleRange: this.props.compatibleRange + } + }, currentPkg); + + // Combine the keywords + if (this.props.keywords) { + pkg.keywords = _.uniq(this.props.keywords.concat(pkg.keywords)); + } + + // Let's extend package.json so we're not overwriting user previous fields + this.fs.writeJSON(this.destinationPath('package.json'), pkg); + + // Populate and write the readme template + if (!this.fs.exists(this.destinationPath('README.md'))) { + this.fs.copyTpl( + this.templatePath('README.md'), + this.destinationPath('README.md'), + { + name: this.props.name, + compatibleRange: this.props.compatibleRange + } + ); + } + }, + + default: function () { + if (path.basename(this.destinationPath()) !== this.props.name) { + this.log( + 'Your bundle must be inside a folder named ' + this.props.name + '\n' + + 'I\'ll automatically create this folder.' + ); + mkdirp(this.props.name); + this.destinationRoot(this.destinationPath(this.props.name)); + } + + this.composeWith('node:git', { + options: { + name: this.props.name, + githubAccount: this.props.githubAccount + } + }, { + local: require.resolve('generator-node/generators/git') + }); + + if (!this.pkg.license) { + this.composeWith('license', { + options: { + name: this.props.authorName, + email: this.props.authorEmail, + website: this.props.authorUrl + } + }, { + local: require.resolve('generator-license/app') + }); + } + + if (this.props.dashboardPanel) { + this.composeWith('nodecg:panel', {}, { + local: require.resolve('./../panel') + }); + } + + if (this.props.graphic) { + this.composeWith('nodecg:graphic', {}, { + local: require.resolve('./../graphic') + }); + } + + if (this.props.extension) { + this.composeWith('nodecg:extension', {}, { + local: require.resolve('./../extension') + }); + } + } +}); diff --git a/generators/app/templates/README.md b/generators/app/templates/README.md new file mode 100644 index 0000000..5ffe701 --- /dev/null +++ b/generators/app/templates/README.md @@ -0,0 +1,4 @@ +<%- name %> is a [NodeCG](http://github.com/nodecg/nodecg) bundle. +It works with NodeCG versions which satisfy this [semver](https://docs.npmjs.com/getting-started/semantic-versioning) range: `<%- compatibleRange %>` +You will need to have an appropriate version of NodeCG installed to use it. + diff --git a/generators/extension/index.js b/generators/extension/index.js new file mode 100644 index 0000000..527b733 --- /dev/null +++ b/generators/extension/index.js @@ -0,0 +1,53 @@ +'use strict'; + +var yeoman = require('yeoman-generator'); +var extend = require('deep-extend'); + +module.exports = yeoman.Base.extend({ + initializing: function () { + this.props = {}; + }, + + prompting: { + askFor: function () { + var done = this.async(); + + var prompts = [{ + type: 'list', + name: 'type', + message: 'How should your extension be organized?', + choices: [{ + name: 'In one file (extension.js)', + value: 'file', + short: 'file' + }, { + name: 'In a folder (extension/index.js)', + value: 'folder', + short: 'folder' + }], + default: 'file' + }]; + + this.prompt(prompts, function (props) { + this.props = extend(this.props, props); + done(); + }.bind(this)); + } + }, + + writing: function () { + // If this bundle already has an extension, do nothing. + if (this.fs.exists(this.destinationPath('extension.js')) + || this.fs.exists(this.destinationPath('extension/index.js'))) { + return; + } + + var js = this.fs.read(this.templatePath('extension.js')); + + if (this.props.type === 'file') { + this.fs.write(this.destinationPath('extension.js'), js); + } else { + this.fs.write(this.destinationPath('extension/index.js'), js); + } + } +}); diff --git a/generators/extension/templates/extension.js b/generators/extension/templates/extension.js new file mode 100644 index 0000000..8c65eb0 --- /dev/null +++ b/generators/extension/templates/extension.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function (nodecg) { + +}; \ No newline at end of file diff --git a/generators/graphic/index.js b/generators/graphic/index.js new file mode 100644 index 0000000..0dc8ad6 --- /dev/null +++ b/generators/graphic/index.js @@ -0,0 +1,82 @@ +'use strict'; + +var yeoman = require('yeoman-generator'); +var extend = require('deep-extend'); + +module.exports = yeoman.Base.extend({ + initializing: function () { + this.props = {}; + }, + + prompting: { + askFor: function () { + var done = this.async(); + + var prompts = [{ + type: 'input', + name: 'file', + message: 'Your graphic\'s file', + default: 'index.html' + }, { + type: 'input', + name: 'width', + message: 'Your graphic\'s width (in pixels)', + default: 1280, + filter: function (input) { + return parseInt(input, 10); + }, + validate: function (input) { + return input > 0; + } + }, { + type: 'input', + name: 'height', + message: 'Your graphic\'s height (in pixels)', + default: 720, + filter: function (input) { + return parseInt(input, 10); + }, + validate: function (input) { + return input > 0; + } + }, { + type: 'confirm', + name: 'singleInstance', + message: 'Is this a "single instance" graphic?', + default: false + }]; + + this.prompt(prompts, function (props) { + this.props = extend(this.props, props); + done(); + }.bind(this)); + } + }, + + writing: function () { + var html = this.fs.read(this.templatePath('graphic.html')); + var graphicFilePath = this.destinationPath('graphics/' + this.props.file); + if (!this.fs.exists(graphicFilePath)) { + this.fs.write(graphicFilePath, html); + } + + var graphicProps = { + file: this.props.file, + width: this.props.width, + height: this.props.height + }; + + if (this.props.singleInstance) { + graphicProps.singleInstance = this.props.singleInstance; + } + + // Re-read the content at this point because a composed generator might modify it. + var currentPkg = this.fs.readJSON(this.destinationPath('package.json'), {}); + currentPkg.nodecg = currentPkg.nodecg || {}; + currentPkg.nodecg.graphics = currentPkg.nodecg.graphics || []; + currentPkg.nodecg.graphics.push(graphicProps); + + // Let's extend package.json so we're not overwriting user previous fields + this.fs.writeJSON(this.destinationPath('package.json'), currentPkg); + } +}); diff --git a/generators/graphic/templates/graphic.html b/generators/graphic/templates/graphic.html new file mode 100644 index 0000000..df2054d --- /dev/null +++ b/generators/graphic/templates/graphic.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/generators/panel/index.js b/generators/panel/index.js new file mode 100644 index 0000000..0c3e713 --- /dev/null +++ b/generators/panel/index.js @@ -0,0 +1,149 @@ +'use strict'; + +var yeoman = require('yeoman-generator'); +var extend = require('deep-extend'); +var _ = require('lodash'); + +module.exports = yeoman.Base.extend({ + initializing: function () { + this.props = {}; + }, + + prompting: { + // Begin by asking for just the panel name. + // This will be used to supply the default panel title. + askForPanelName: function () { + var done = this.async(); + + var prompts = [{ + type: 'input', + name: 'name', + message: 'Your panel\'s name', + filter: _.kebabCase + }]; + + this.prompt(prompts, function (props) { + this.props = extend(this.props, props); + done(); + }.bind(this)); + }, + + askFor: function () { + var done = this.async(); + + var prompts = [{ + type: 'input', + name: 'title', + message: 'Your panel\'s title', + default: _.startCase(this.props.name) + }, { + type: 'input', + name: 'width', + message: 'How many width units (1-8) should your panel be?', + default: 2, + filter: function (input) { + return parseInt(input, 10); + }, + validate: function (input) { + return input > 0 && input <= 8; + } + }, { + type: 'confirm', + name: 'dialog', + message: 'Is this panel a pop-up dialog?', + default: false + }, { + type: 'input', + name: 'headerColor', + message: 'What hex color would you like your panel\'s header to be?', + default: '#9f9bbd', + when: function (answers) { + return !answers.dialog; + } + }, { + type: 'confirm', + name: 'dialogConfirmBtn', + message: 'Should this dialog have a "confirm" button?', + default: true, + when: function (answers) { + return answers.dialog; + } + }, { + type: 'input', + name: 'dialogConfirmBtnLabel', + message: 'What should the "confirm" button\'s label be?', + default: 'Confirm', + when: function (answers) { + return answers.dialogConfirmBtn; + } + }, { + type: 'confirm', + name: 'dialogDismissBtn', + message: 'Should this dialog have a "dismiss" button?', + default: true, + when: function (answers) { + return answers.dialog; + } + }, { + type: 'input', + name: 'dialogDismissBtnLabel', + message: 'What should the "dismiss" button\'s label be?', + default: 'Dismiss', + when: function (answers) { + return answers.dialogDismissBtn; + } + }]; + + this.prompt(prompts, function (props) { + this.props = extend(this.props, props); + done(); + }.bind(this)); + } + }, + + writing: function () { + var html = this.fs.read(this.templatePath('panel.html')); + var panelFilePath = this.destinationPath('dashboard/' + this.props.name + '.html'); + if (!this.fs.exists(panelFilePath)) { + this.fs.write(panelFilePath, html); + } + + var panelProps = { + name: this.props.name, + title: this.props.title, + width: this.props.width, + file: this.props.name + '.html' + }; + + if (this.props.dialog) { + panelProps.dialog = this.props.dialog; + + if (this.props.dialogConfirmBtn) { + panelProps.dialogButtons = panelProps.dialogButtons || []; + panelProps.dialogButtons.push({ + name: this.props.dialogConfirmBtnLabel, + type: 'confirm' + }); + } + + if (this.props.dialogDismissBtn) { + panelProps.dialogButtons = panelProps.dialogButtons || []; + panelProps.dialogButtons.push({ + name: this.props.dialogDismissBtnLabel, + type: 'dismiss' + }); + } + } else { + panelProps.headerColor = this.props.headerColor; + } + + // Re-read the content at this point because a composed generator might modify it. + var currentPkg = this.fs.readJSON(this.destinationPath('package.json'), {}); + currentPkg.nodecg = currentPkg.nodecg || {}; + currentPkg.nodecg.dashboardPanels = currentPkg.nodecg.dashboardPanels || []; + currentPkg.nodecg.dashboardPanels.push(panelProps); + + // Let's extend package.json so we're not overwriting user previous fields + this.fs.writeJSON(this.destinationPath('package.json'), currentPkg); + } +}); diff --git a/generators/panel/templates/panel.html b/generators/panel/templates/panel.html new file mode 100644 index 0000000..df2054d --- /dev/null +++ b/generators/panel/templates/panel.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..6ac0881 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,57 @@ +'use strict'; +var path = require('path'); +var gulp = require('gulp'); +var eslint = require('gulp-eslint'); +var excludeGitignore = require('gulp-exclude-gitignore'); +var mocha = require('gulp-mocha'); +var istanbul = require('gulp-istanbul'); +var nsp = require('gulp-nsp'); +var plumber = require('gulp-plumber'); +var filter = require('gulp-filter'); + +gulp.task('static', function () { + return gulp.src('**/*.js') + .pipe(excludeGitignore()) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failAfterError()); +}); + +gulp.task('nsp', function (cb) { + nsp({package: path.resolve('package.json')}, cb); +}); + +gulp.task('pre-test', function () { + return gulp.src('generators/**/*.js') + .pipe(filter([ + '**', + '!extension/templates/**' + ])) + .pipe(excludeGitignore()) + .pipe(istanbul({ + includeUntested: true + })) + .pipe(istanbul.hookRequire()); +}); + +gulp.task('test', ['pre-test'], function (cb) { + var mochaErr; + + gulp.src('test/**/*.js') + .pipe(plumber()) + .pipe(mocha({reporter: 'spec'})) + .on('error', function (err) { + mochaErr = err; + }) + .pipe(istanbul.writeReports()) + .on('end', function () { + cb(mochaErr); + }); +}); + +gulp.task('watch', function () { + gulp.watch(['generators/**/*.js', 'test/**'], ['test']); +}); + +gulp.task('prepublish', ['nsp']); +gulp.task('default', ['static', 'test']); diff --git a/package.json b/package.json new file mode 100644 index 0000000..b324b76 --- /dev/null +++ b/package.json @@ -0,0 +1,60 @@ +{ + "name": "generator-nodecg", + "version": "0.0.0", + "description": "Yeoman generator for NodeCG bundles", + "homepage": "http://nodecg.com", + "author": { + "name": "Alex Van Camp", + "email": "email@alexvan.camp", + "url": "http://alexvan.camp" + }, + "files": [ + "generators" + ], + "main": "generators\\index.js", + "keywords": [ + "yeoman-generator", + "nodecg", + "bundle" + ], + "dependencies": { + "chalk": "^1.0.0", + "deep-extend": "^0.4.1", + "generator-license": "^3.1.1", + "generator-node": "^1.8.3", + "git-config": "0.0.7", + "github-username": "^2.1.0", + "inquirer-npm-name": "^1.0.0", + "lodash": "^4.2.1", + "mkdirp": "^0.5.1", + "parse-author": "^0.2.0", + "yeoman-generator": "^0.22.5", + "yosay": "^1.0.2" + }, + "devDependencies": { + "eslint-config-xo": "^0.9.2", + "gulp": "^3.9.0", + "gulp-eslint": "^1.0.0", + "gulp-exclude-gitignore": "^1.0.0", + "gulp-filter": "^3.0.1", + "gulp-istanbul": "^0.10.3", + "gulp-mocha": "^2.0.0", + "gulp-nsp": "^2.1.0", + "gulp-plumber": "^1.0.0", + "mockery": "^1.4.0", + "yeoman-assert": "^2.0.0", + "yeoman-test": "^1.0.0" + }, + "eslintConfig": { + "extends": "xo", + "env": { + "mocha": true + } + }, + "repository": "nodecg/generator-nodecg", + "scripts": { + "prepublish": "gulp prepublish", + "test": "gulp" + }, + "license": "MIT" +} diff --git a/test/app.js b/test/app.js new file mode 100644 index 0000000..fe0baf7 --- /dev/null +++ b/test/app.js @@ -0,0 +1,131 @@ +'use strict'; +var _ = require('lodash'); +var mockery = require('mockery'); +var path = require('path'); +var assert = require('yeoman-assert'); +var helpers = require('yeoman-test'); + +describe('nodecg:app', function () { + before(function () { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false + }); + + mockery.registerMock('npm-name', function (name, cb) { + cb(null, true); + }); + + mockery.registerMock('github-username', function (name, cb) { + cb(null, 'unicornUser'); + }); + + mockery.registerMock( + require.resolve('generator-license'), + helpers.createDummyGenerator() + ); + }); + + after(function () { + mockery.disable(); + }); + + describe('running on new project', function () { + before(function (done) { + this.answers = { + name: 'test-bundle', + description: 'A NodeCG bundle', + homepage: 'http://nodecg.com', + githubAccount: 'nodecg', + authorName: 'Alex Van Camp', + authorEmail: 'email@alexvan.camp', + authorUrl: 'http://alexvan.camp/', + keywords: ['foo', 'bar'] + }; + helpers.run(path.join(__dirname, '../generators/app')) + .withPrompts(this.answers) + .on('end', done); + }); + + it('creates files', function () { + assert.file([ + 'README.md' + ]); + }); + + it('creates package.json', function () { + assert.file('package.json'); + assert.jsonFileContent('package.json', { + name: 'test-bundle', + version: '0.0.0', + description: this.answers.description, + homepage: this.answers.homepage, + repository: 'nodecg/test-bundle', + author: { + name: this.answers.authorName, + email: this.answers.authorEmail, + url: this.answers.authorUrl + }, + files: [ + 'dashboard', + 'graphics', + 'extension.js', + 'extension' + ], + keywords: [ + 'foo', + 'bar', + 'nodecg-bundle' + ], + nodecg: { + compatibleRange: '~0.7.0' + } + }); + }); + + it('creates and fill contents in README.md', function () { + assert.file('README.md'); + assert.fileContent('README.md', 'test-bundle is a [NodeCG](http://github.com/nodecg/nodecg) bundle.'); + }); + }); + + describe('running on existing project', function () { + before(function (done) { + this.pkg = { + version: '1.0.34', + description: 'lots of fun', + homepage: 'http://nodecg.com', + repository: 'nodecg/test-bundle', + author: 'Alex Van Camp', + files: [ + 'dashboard', + 'graphics', + 'extension.js', + 'extension' + ], + keywords: ['bar', 'nodecg-bundle'], + nodecg: { + compatibleRange: '~0.7.0' + } + }; + helpers.run(path.join(__dirname, '../generators/app')) + .withPrompts({ + name: 'test-bundle' + }) + .on('ready', function (gen) { + gen.fs.writeJSON(gen.destinationPath('test-bundle/package.json'), this.pkg); + gen.fs.write(gen.destinationPath('test-bundle/README.md'), 'foo'); + }.bind(this)) + .on('end', done); + }); + + it('extends package.json keys with missing ones', function () { + var pkg = _.extend({name: 'test-bundle'}, this.pkg); + assert.jsonFileContent('package.json', pkg); + }); + + it('does not overwrite previous README.md', function () { + assert.fileContent('README.md', 'foo'); + }); + }); +}); diff --git a/test/extension.js b/test/extension.js new file mode 100644 index 0000000..609c053 --- /dev/null +++ b/test/extension.js @@ -0,0 +1,66 @@ +'use strict'; +var _ = require('lodash'); +var mockery = require('mockery'); +var path = require('path'); +var assert = require('yeoman-assert'); +var helpers = require('yeoman-test'); + +describe('nodecg:extension', function () { + context('running on new project', function () { + context('in file mode', function () { + before(function (done) { + helpers.run(path.join(__dirname, '../generators/extension')) + .withPrompts({type: 'file'}) + .on('end', done); + }); + + it('creates the file', function () { + assert.file('extension.js'); + }); + }); + + context('in folder mode', function () { + before(function (done) { + helpers.run(path.join(__dirname, '../generators/extension')) + .withPrompts({type: 'folder'}) + .on('end', done); + }); + + it('creates the file', function () { + assert.file('extension/index.js'); + }); + }); + }); + + context('running on existing project', function () { + context('in file mode', function () { + before(function (done) { + helpers.run(path.join(__dirname, '../generators/extension')) + .withPrompts({type: 'file'}) + .on('ready', function (gen) { + gen.fs.write(gen.destinationPath('extension.js'), 'foo'); + }.bind(this)) + .on('end', done); + }); + + it('does not overwrite previous extension file', function () { + assert.fileContent('extension.js', 'foo'); + }); + }); + + context('in folder mode', function () { + before(function (done) { + helpers.run(path.join(__dirname, '../generators/extension')) + .withPrompts({type: 'folder'}) + .on('ready', function (gen) { + gen.fs.write(gen.destinationPath('extension/index.js'), 'foo'); + }.bind(this)) + .on('end', done); + }); + + it('does not overwrite previous extension file', function () { + assert.fileContent('extension/index.js', 'foo'); + }); + }); + }); +}); diff --git a/test/graphic.js b/test/graphic.js new file mode 100644 index 0000000..82dc244 --- /dev/null +++ b/test/graphic.js @@ -0,0 +1,92 @@ +'use strict'; +var _ = require('lodash'); +var mockery = require('mockery'); +var path = require('path'); +var assert = require('yeoman-assert'); +var helpers = require('yeoman-test'); + +describe('nodecg:graphic', function () { + describe('running on new project', function () { + before(function (done) { + helpers.run(path.join(__dirname, '../generators/graphic')) + .withPrompts({ + file: 'index.html', + width: 1280, + height: 720, + singleInstance: false + }) + .on('end', done); + }); + + it('creates files', function () { + assert.file([ + 'graphics/index.html' + ]); + }); + + it('creates package.json', function () { + assert.file('package.json'); + assert.jsonFileContent('package.json', { + nodecg: { + graphics: [{ + file: 'index.html', + width: 1280, + height: 720 + }] + } + }); + }); + }); + + describe('running on existing project', function () { + before(function (done) { + this.pkg = { + name: 'test-bundle', + version: '1.0.34', + description: 'lots of fun', + homepage: 'http://nodecg.com', + repository: 'nodecg/test-bundle', + author: 'Alex Van Camp', + files: [ + 'dashboard', + 'graphics', + 'extension.js', + 'extension' + ], + keywords: ['bar', 'nodecg-bundle'], + nodecg: { + compatibleRange: '~0.7.0' + } + }; + helpers.run(path.join(__dirname, '../generators/graphic')) + .withPrompts({ + file: 'index.html', + width: 1280, + height: 720, + singleInstance: false + }) + .on('ready', function (gen) { + gen.fs.writeJSON(gen.destinationPath('package.json'), this.pkg); + gen.fs.write(gen.destinationPath('graphics/index.html'), 'foo'); + }.bind(this)) + .on('end', done); + }); + + it('extends package.json keys with missing ones', function () { + var pkg = _.extend({ + nodecg: { + graphics: [{ + file: 'index.html', + width: 1280, + height: 720 + }] + } + }, this.pkg); + assert.jsonFileContent('package.json', pkg); + }); + + it('does not overwrite previous html file', function () { + assert.fileContent('graphics/index.html', 'foo'); + }); + }); +}); diff --git a/test/panel.js b/test/panel.js new file mode 100644 index 0000000..914d6dc --- /dev/null +++ b/test/panel.js @@ -0,0 +1,86 @@ +'use strict'; +var _ = require('lodash'); +var mockery = require('mockery'); +var path = require('path'); +var assert = require('yeoman-assert'); +var helpers = require('yeoman-test'); + +describe('nodecg:panel', function () { + describe('running on new project', function () { + before(function (done) { + helpers.run(path.join(__dirname, '../generators/panel')) + .withPrompts({name: 'test-panel'}) + .on('end', done); + }); + + it('creates files', function () { + assert.file([ + 'dashboard/test-panel.html' + ]); + }); + + it('creates package.json', function () { + assert.file('package.json'); + assert.jsonFileContent('package.json', { + nodecg: { + dashboardPanels: [{ + name: 'test-panel', + title: 'Test Panel', + width: 2, + file: 'test-panel.html', + headerColor: '#9f9bbd' + }] + } + }); + }); + }); + + describe('running on existing project', function () { + before(function (done) { + this.pkg = { + name: 'test-bundle', + version: '1.0.34', + description: 'lots of fun', + homepage: 'http://nodecg.com', + repository: 'nodecg/test-bundle', + author: 'Alex Van Camp', + files: [ + 'dashboard', + 'graphics', + 'extension.js', + 'extension' + ], + keywords: ['bar', 'nodecg-bundle'], + nodecg: { + compatibleRange: '~0.7.0' + } + }; + helpers.run(path.join(__dirname, '../generators/panel')) + .withPrompts({name: 'test-panel'}) + .on('ready', function (gen) { + gen.fs.writeJSON(gen.destinationPath('package.json'), this.pkg); + gen.fs.write(gen.destinationPath('dashboard/test-panel.html'), 'foo'); + }.bind(this)) + .on('end', done); + }); + + it('extends package.json keys with missing ones', function () { + var pkg = _.extend({ + nodecg: { + dashboardPanels: [{ + name: 'test-panel', + title: 'Test Panel', + width: 2, + file: 'test-panel.html', + headerColor: '#9f9bbd' + }] + } + }, this.pkg); + assert.jsonFileContent('package.json', pkg); + }); + + it('does not overwrite previous html file', function () { + assert.fileContent('dashboard/test-panel.html', 'foo'); + }); + }); +});