├── .gitignore ├── .npmignore ├── test ├── fixtures │ ├── my-addon │ │ ├── addon │ │ │ ├── .gitkeep │ │ │ ├── templates │ │ │ │ └── components │ │ │ │ │ └── my-component.hbs │ │ │ └── components │ │ │ │ └── my-component.js │ │ ├── app │ │ │ ├── .gitkeep │ │ │ └── components │ │ │ │ └── my-component.js │ │ ├── vendor │ │ │ └── .gitkeep │ │ ├── tests │ │ │ ├── unit │ │ │ │ └── .gitkeep │ │ │ ├── dummy │ │ │ │ ├── app │ │ │ │ │ ├── styles │ │ │ │ │ │ └── app.css │ │ │ │ │ ├── components │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── helpers │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── models │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── routes │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── controllers │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── templates │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ │ └── application.hbs │ │ │ │ │ ├── resolver.js │ │ │ │ │ ├── router.js │ │ │ │ │ ├── app.js │ │ │ │ │ └── index.html │ │ │ │ ├── public │ │ │ │ │ ├── robots.txt │ │ │ │ │ └── crossdomain.xml │ │ │ │ └── config │ │ │ │ │ ├── targets.js │ │ │ │ │ └── environment.js │ │ │ ├── integration │ │ │ │ └── .gitkeep │ │ │ ├── .eslintrc.js │ │ │ ├── helpers │ │ │ │ ├── destroy-app.js │ │ │ │ ├── resolver.js │ │ │ │ ├── start-app.js │ │ │ │ └── module-for-acceptance.js │ │ │ ├── test-helper.js │ │ │ └── index.html │ │ ├── .watchmanconfig │ │ ├── index.js │ │ ├── config │ │ │ ├── environment.js │ │ │ └── ember-try.js │ │ ├── blueprints │ │ │ └── my-addon │ │ │ │ └── index.js │ │ ├── .eslintrc.js │ │ ├── testem.js │ │ ├── .npmignore │ │ ├── .ember-cli │ │ ├── .gitignore │ │ ├── .editorconfig │ │ ├── ember-cli-build.js │ │ ├── README.md │ │ ├── LICENSE.md │ │ ├── .travis.yml │ │ └── package.json │ └── random-template.hbs ├── .eslintrc ├── lint-test.js ├── helpers │ └── create-addon.js └── acceptance │ ├── simple-test.js │ └── complicated-test.js ├── .eslintignore ├── lib ├── utilities │ ├── debug.js │ ├── chdir.js │ ├── kill-cli-process.js │ ├── run-new.js │ ├── symlink-directory.js │ ├── move-directory.js │ ├── run-ember.js │ ├── log-on-failure.js │ ├── copy-fixture-files.js │ ├── temp.js │ ├── run-command.js │ └── pristine.js ├── index.js ├── models │ ├── addon-test-app.js │ └── app.js └── commands │ └── start-server.js ├── .eslintrc ├── .travis.yml ├── CHANGELOG.md ├── .editorconfig ├── appveyor.yml ├── scripts └── precook-node-modules.js ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /test 2 | /*.* 3 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/addon/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/fixtures/**/*.js 2 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/random-template.hbs: -------------------------------------------------------------------------------- 1 | random template -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/integration/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{my-component}} -------------------------------------------------------------------------------- /test/fixtures/my-addon/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/addon/templates/components/my-component.hbs: -------------------------------------------------------------------------------- 1 | my-addon is working -------------------------------------------------------------------------------- /test/fixtures/my-addon/app/components/my-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'my-addon/components/my-component'; 2 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | embertest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'my-addon' 6 | }; 7 | -------------------------------------------------------------------------------- /lib/utilities/debug.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const debug = require('debug')('ember-cli-addon-tests'); 4 | 5 | module.exports = debug; 6 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = function(/* environment, appConfig */) { 5 | return { }; 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function destroyApp(application) { 4 | Ember.run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /test/lint-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const lint = require('mocha-eslint'); 4 | 5 | lint([ 6 | 'lib', 7 | 'scripts', 8 | 'test' 9 | ], { 10 | timeout: 5000 11 | }); 12 | -------------------------------------------------------------------------------- /lib/utilities/chdir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const debug = require('./debug'); 4 | 5 | module.exports = function(path) { 6 | debug('chdir; path=%s', path); 7 | process.chdir(path); 8 | }; 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "sane", 3 | "parserOptions": { 4 | "ecmaVersion": 6 5 | }, 6 | "env": { 7 | "node": true, 8 | "es6": true 9 | }, 10 | "rules": { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const App = require('./models/app'); 4 | const AddonTestApp = require('./models/addon-test-app'); 5 | 6 | module.exports = { 7 | App, 8 | AddonTestApp 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/addon/components/my-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import layout from '../templates/components/my-component'; 3 | 4 | export default Ember.Component.extend({ 5 | layout 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | import { start } from 'ember-cli-qunit'; 6 | 7 | setResolver(resolver); 8 | start(); 9 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/blueprints/my-addon/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | normalizeEntityName() { 4 | // this should fail if dependencies aren't installed 5 | require('broccoli-asset-rev'); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | browsers: [ 4 | 'ie 9', 5 | 'last 1 Chrome versions', 6 | 'last 1 Firefox versions', 7 | 'last 1 Safari versions' 8 | ] 9 | }; 10 | -------------------------------------------------------------------------------- /lib/utilities/kill-cli-process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(childProcess) { 4 | if (process.platform === 'win32') { 5 | childProcess.send({ kill: true }); 6 | } else { 7 | childProcess.kill('SIGINT'); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2017, 5 | sourceType: 'module' 6 | }, 7 | extends: 'eslint:recommended', 8 | env: { 9 | browser: true 10 | }, 11 | rules: { 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "8" 5 | - "10" 6 | 7 | before_install: 8 | 9 | # TODO: remove when we stop testing against node 6 10 | - if [[ `npm -v` < 6* ]]; then npm i -g npm@6; fi 11 | 12 | - npm install -g bower 13 | - bower --version 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.7.0 / 2017-05-05 2 | ================== 3 | 4 | * Fix Node 4 and better handle ember-source 5 | * Fix compatibility with ember-cli@2.13 6 | * Refactor pristine for clarity 7 | * Add eslint 8 | * Force npm3 for Node 4 9 | * Bump dependencies to fix tests 10 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/testem.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | test_page: 'tests/index.html?hidepassed', 4 | disable_watching: true, 5 | launch_in_ci: [ 6 | 'PhantomJS' 7 | ], 8 | launch_in_dev: [ 9 | 'PhantomJS', 10 | 'Chrome' 11 | ] 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/.npmignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /config/ember-try.js 3 | /dist 4 | /tests 5 | /tmp 6 | **/.gitkeep 7 | .bowerrc 8 | .editorconfig 9 | .ember-cli 10 | .gitignore 11 | .eslintrc.js 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | const Router = Ember.Router.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | }); 11 | 12 | export default Router; 13 | -------------------------------------------------------------------------------- /lib/utilities/run-new.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const runEmber = require('./run-ember'); 4 | 5 | module.exports = function(name, options) { 6 | let args = [name, '--disable-analytics', '--watcher=node', '--skip-git']; 7 | if (options) { 8 | args = args.concat(options); 9 | } 10 | 11 | return runEmber('new', args); 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /lib/utilities/symlink-directory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const symlinkOrCopySync = require('symlink-or-copy').sync; 4 | const debug = require('./debug'); 5 | const path = require('path'); 6 | 7 | module.exports = function(from, to) { 8 | debug('symlinking; from=' + from + '; to=' + path.resolve(to)); 9 | symlinkOrCopySync(from, to); 10 | }; 11 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | const App = Ember.Application.extend({ 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix, 9 | Resolver 10 | }); 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log* 17 | yarn-error.log 18 | testem.log 19 | 20 | # ember-try 21 | .node_modules.ember-try/ 22 | bower.json.ember-try 23 | package.json.ember-try 24 | -------------------------------------------------------------------------------- /lib/utilities/move-directory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const debug = require('./debug'); 5 | const fs = require('fs-extra'); 6 | 7 | /* 8 | * Moves a directory, but only if the target doesn't exist. 9 | */ 10 | module.exports = function moveDirectory(from, to) { 11 | from = path.resolve(from); 12 | 13 | if (!fs.existsSync(to)) { 14 | debug('moving directory; from=' + from + '; to=' + to); 15 | fs.renameSync(from, to); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /lib/utilities/run-ember.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const findup = require('findup-sync'); 5 | const runCommand = require('./run-command'); 6 | 7 | module.exports = function(command, options, dirPath) { 8 | let emberCLIPath = findup('node_modules/ember-cli', { 9 | cwd: dirPath || __dirname 10 | }); 11 | 12 | let args = [path.join(emberCLIPath, 'bin', 'ember'), command].concat(options); 13 | 14 | return runCommand.apply(undefined, args); 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | let attributes = Ember.merge({}, config.APP); 7 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 8 | 9 | return Ember.run(() => { 10 | let application = Application.create(attributes); 11 | application.setupForTesting(); 12 | application.injectTestHelpers(); 13 | return application; 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 5 | 6 | module.exports = function(defaults) { 7 | let app = new EmberAddon(defaults, { 8 | // Add options here 9 | }); 10 | 11 | /* 12 | This build file specifies the options for the dummy test app of this 13 | addon, located in `/tests/dummy` 14 | This build file does *not* influence how the addon or the app using it 15 | behave. You most likely want to be modifying `./index.js` or app's build file 16 | */ 17 | 18 | return app.toTree(); 19 | }; 20 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "6" 4 | - nodejs_version: "8" 5 | - nodejs_version: "10" 6 | 7 | # Fix line endings in Windows. (runs before repo cloning) 8 | init: 9 | - git config --global core.autocrlf true 10 | 11 | # Install scripts. (runs after repo cloning) 12 | install: 13 | - ps: Install-Product node $env:nodejs_version 14 | - npm i -g npm@^6 15 | - npm install -g bower 16 | - npm install 17 | 18 | # Post-install test scripts. 19 | test_script: 20 | - node --version 21 | - npm --version 22 | - bower --version 23 | - npm test 24 | 25 | # Don't actually build. 26 | build: off 27 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/README.md: -------------------------------------------------------------------------------- 1 | # my-addon 2 | 3 | This README outlines the details of collaborating on this Ember addon. 4 | 5 | ## Installation 6 | 7 | * `git clone ` this repository 8 | * `cd my-addon` 9 | * `npm install` 10 | 11 | ## Running 12 | 13 | * `ember serve` 14 | * Visit your app at [http://localhost:4200](http://localhost:4200). 15 | 16 | ## Running Tests 17 | 18 | * `npm test` (Runs `ember try:each` to test your addon against multiple Ember versions) 19 | * `ember test` 20 | * `ember test --server` 21 | 22 | ## Building 23 | 24 | * `ember build` 25 | 26 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 27 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /test/helpers/create-addon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const makeTemp = require('temp').track(); 5 | const copyFixtureFiles = require('../../lib/utilities/copy-fixture-files'); 6 | const fs = require('fs-extra'); 7 | 8 | const addonName = 'my-addon'; 9 | 10 | module.exports = function(createApp) { 11 | let previousCwd = process.cwd(); 12 | let tmp = makeTemp.mkdirSync(); 13 | let addonPath = path.join(tmp, addonName); 14 | 15 | fs.ensureDirSync(addonPath); 16 | 17 | return copyFixtureFiles(addonName, addonPath) 18 | .then(() => { 19 | process.chdir(addonPath); 20 | 21 | return createApp(); 22 | }).then(() => { 23 | process.chdir(previousCwd); 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import Ember from 'ember'; 3 | import startApp from '../helpers/start-app'; 4 | import destroyApp from '../helpers/destroy-app'; 5 | 6 | const { RSVP: { resolve } } = Ember; 7 | 8 | export default function(name, options = {}) { 9 | module(name, { 10 | beforeEach() { 11 | this.application = startApp(); 12 | 13 | if (options.beforeEach) { 14 | return options.beforeEach.apply(this, arguments); 15 | } 16 | }, 17 | 18 | afterEach() { 19 | let afterEach = options.afterEach && options.afterEach.apply(this, arguments); 20 | return resolve(afterEach).then(() => destroyApp(this.application)); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/utilities/log-on-failure.js: -------------------------------------------------------------------------------- 1 | /* global beforeEach, afterEach */ 2 | 'use strict'; 3 | 4 | let logSink = []; 5 | 6 | if (typeof beforeEach !== 'undefined') { 7 | beforeEach(function() { 8 | logSink = []; 9 | }); 10 | } 11 | 12 | if (typeof afterEach !== 'undefined') { 13 | afterEach(function() { 14 | if (this.currentTest.state !== 'passed') { 15 | // It would be preferable to attach the log output to the error object 16 | // (this.currentTest.err) and have Mocha report it somehow, so that the 17 | // error message and log output show up in the same place. This doesn't 18 | // seem to be possible though. 19 | console.log(logSink.join('\n')); // eslint-disable-line no-console 20 | } 21 | logSink = []; 22 | }); 23 | } 24 | 25 | function logOnFailure(s) { 26 | logSink.push(s); 27 | } 28 | 29 | module.exports = logOnFailure; 30 | -------------------------------------------------------------------------------- /test/acceptance/simple-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const denodeify = require('denodeify'); 5 | const request = denodeify(require('request')); 6 | const AddonTestApp = require('../../lib').AddonTestApp; 7 | const createAddon = require('../helpers/create-addon'); 8 | 9 | describe('Acceptance | simple', function() { 10 | this.timeout(process.platform === 'win32' ? 500000 : 300000); 11 | 12 | let app; 13 | 14 | before(function() { 15 | app = new AddonTestApp(); 16 | 17 | return createAddon(() => { 18 | return app.create('dummy', { 19 | fixturesPath: 'tests' 20 | }); 21 | }); 22 | }); 23 | 24 | beforeEach(function() { 25 | return app.startServer(); 26 | }); 27 | 28 | afterEach(function() { 29 | return app.stopServer(); 30 | }); 31 | 32 | it('works', function() { 33 | return request('http://localhost:49741/assets/vendor.js') 34 | .then(response => { 35 | expect(response.body).to.contain('my-addon is working'); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /lib/utilities/copy-fixture-files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const findup = require('findup-sync'); 5 | const debug = require('../utilities/debug'); 6 | const copy = require('fs-extra').copy; 7 | 8 | function findFixturesPath(fixturesPath) { 9 | fixturesPath = findup(fixturesPath || 'test/fixtures'); 10 | 11 | if (!fixturesPath) { 12 | fixturesPath = findup('tests/fixtures'); 13 | } 14 | 15 | if (!fixturesPath) { 16 | throw new Error('Could not find fixtures directory. Make sure you have a fixtures directory in your `test/` directory. You may encounter this issue if you have npm linked this package; copy it to your node_modules directory instead.'); 17 | } 18 | 19 | return fixturesPath; 20 | } 21 | 22 | function copyFixtureFiles(appName, destDir, fixturesPath) { 23 | if (!fixturesPath || !path.isAbsolute(fixturesPath)) { 24 | fixturesPath = findFixturesPath(fixturesPath); 25 | } 26 | 27 | let sourceDir = path.join(fixturesPath, appName); 28 | 29 | debug('copying fixtures; from=%s; to=%s', sourceDir, destDir); 30 | 31 | return copy(sourceDir, destDir, { 32 | overwrite: true 33 | }); 34 | } 35 | 36 | module.exports = copyFixtureFiles; 37 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /scripts/precook-node-modules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const temp = require('temp').track(); 4 | const path = require('path'); 5 | const fs = require('fs-extra'); 6 | const findup = require('findup-sync'); 7 | 8 | const moveDirectory = require('../lib/utilities/move-directory'); 9 | const runNew = require('../lib/utilities/run-new'); 10 | const symlinkDirectory = require('../lib/utilities/symlink-directory'); 11 | 12 | const tmpDir = temp.mkdirSync(); 13 | const root = process.cwd(); 14 | const appName = 'precooked-app'; 15 | 16 | const pkg = findup('package.json'); 17 | const name = fs.readJsonSync(pkg).name; 18 | 19 | fs.ensureDir('tmp') 20 | .then(() => { 21 | process.chdir(tmpDir); 22 | return runNew(appName); 23 | }) 24 | .then(() => { 25 | let precooked = path.join(root, 'tmp', 'precooked_node_modules', 'node_modules'); 26 | fs.mkdirSync(path.dirname(precooked)); 27 | moveDirectory(path.join(tmpDir, appName, 'node_modules'), precooked); 28 | symlinkDirectory(root, path.join(precooked, name)); 29 | }) 30 | .catch((e) => { 31 | console.log(e); // eslint-disable-line no-console 32 | }); 33 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | # we recommend testing addons with the same minimum supported node version as Ember CLI 5 | # so that your addon works for all apps 6 | - "4" 7 | 8 | sudo: false 9 | 10 | cache: 11 | directories: 12 | - $HOME/.npm 13 | 14 | env: 15 | # we recommend new addons test the current and previous LTS 16 | # as well as latest stable release (bonus points to beta/canary) 17 | - EMBER_TRY_SCENARIO=ember-lts-2.8 18 | - EMBER_TRY_SCENARIO=ember-lts-2.12 19 | - EMBER_TRY_SCENARIO=ember-release 20 | - EMBER_TRY_SCENARIO=ember-beta 21 | - EMBER_TRY_SCENARIO=ember-canary 22 | - EMBER_TRY_SCENARIO=ember-default 23 | 24 | matrix: 25 | fast_finish: true 26 | allow_failures: 27 | - env: EMBER_TRY_SCENARIO=ember-canary 28 | 29 | before_install: 30 | - npm config set spin false 31 | - npm install -g npm@4 32 | - npm --version 33 | - npm install -g phantomjs-prebuilt 34 | - phantomjs --version 35 | 36 | install: 37 | - npm install 38 | 39 | script: 40 | # Usually, it's ok to finish the test scenario without reverting 41 | # to the addon's original dependency state, skipping "cleanup". 42 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO --skip-cleanup 43 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = function(environment) { 5 | let ENV = { 6 | modulePrefix: 'dummy', 7 | environment, 8 | rootURL: '/', 9 | locationType: 'auto', 10 | EmberENV: { 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. 'with-controller': true 14 | }, 15 | EXTEND_PROTOTYPES: { 16 | // Prevent Ember Data from overriding Date.parse. 17 | Date: false 18 | } 19 | }, 20 | 21 | APP: { 22 | // Here you can pass flags/options to your application instance 23 | // when it is created 24 | } 25 | }; 26 | 27 | if (environment === 'development') { 28 | // ENV.APP.LOG_RESOLVER = true; 29 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 30 | // ENV.APP.LOG_TRANSITIONS = true; 31 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 32 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 33 | } 34 | 35 | if (environment === 'test') { 36 | // Testem prefers this... 37 | ENV.locationType = 'none'; 38 | 39 | // keep test console output quieter 40 | ENV.APP.LOG_ACTIVE_GENERATION = false; 41 | ENV.APP.LOG_VIEW_LOOKUPS = false; 42 | 43 | ENV.APP.rootElement = '#ember-testing'; 44 | } 45 | 46 | if (environment === 'production') { 47 | 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-addon-tests", 3 | "version": "0.11.1", 4 | "description": "A set of integration test helpers for Ember CLI addons", 5 | "keywords": [ 6 | "ember-cli" 7 | ], 8 | "homepage": "https://github.com/tomdale/ember-cli-addon-tests#readme", 9 | "bugs": { 10 | "url": "https://github.com/tomdale/ember-cli-addon-tests/issues" 11 | }, 12 | "license": "MIT", 13 | "author": "Tom Dale ", 14 | "main": "lib/index.js", 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/tomdale/ember-cli-addon-tests.git" 18 | }, 19 | "scripts": { 20 | "test": "cross-env DEBUG=ember-cli-addon-tests npm run test:mocha", 21 | "test:mocha": "mocha test test/acceptance" 22 | }, 23 | "dependencies": { 24 | "chalk": "^2.0.1", 25 | "debug": "^3.0.0", 26 | "denodeify": "^1.2.1", 27 | "findup-sync": "^2.0.0", 28 | "fs-extra": "^5.0.0", 29 | "lodash": "^4.0.0", 30 | "semver": "^5.3.0", 31 | "symlink-or-copy": "^1.1.3", 32 | "temp": "^0.8.3" 33 | }, 34 | "devDependencies": { 35 | "chai": "^4.0.2", 36 | "cross-env": "^5.0.0", 37 | "ember-cli": "~3.8.0", 38 | "ember-cli-fastboot": "^1.0.0", 39 | "eslint-config-sane": "^0.6.0", 40 | "eslint-plugin-prefer-let": "^1.0.1", 41 | "mocha": "^5.0.0", 42 | "mocha-eslint": "^4.0.0", 43 | "request": "^2.75.0" 44 | }, 45 | "engines": { 46 | "node": ">= 6" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-addon", 3 | "version": "0.0.0", 4 | "description": "The default blueprint for ember-cli addons.", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "license": "MIT", 9 | "author": "", 10 | "directories": { 11 | "doc": "doc", 12 | "test": "tests" 13 | }, 14 | "repository": "", 15 | "scripts": { 16 | "build": "ember build", 17 | "start": "ember server", 18 | "test": "ember try:each" 19 | }, 20 | "dependencies": { 21 | "ember-cli-babel": "^6.3.0", 22 | "ember-cli-htmlbars": "^2.0.1" 23 | }, 24 | "devDependencies": { 25 | "broccoli-asset-rev": "^2.4.5", 26 | "ember-ajax": "^3.0.0", 27 | "ember-cli": "~2.14.0", 28 | "ember-cli-dependency-checker": "^1.3.0", 29 | "ember-cli-eslint": "^3.0.0", 30 | "ember-cli-htmlbars-inline-precompile": "^0.4.3", 31 | "ember-cli-inject-live-reload": "^1.4.1", 32 | "ember-cli-qunit": "^4.0.0", 33 | "ember-cli-shims": "^1.1.0", 34 | "ember-cli-sri": "^2.1.0", 35 | "ember-cli-uglify": "^1.2.0", 36 | "ember-disable-prototype-extensions": "^1.1.2", 37 | "ember-export-application-global": "^2.0.0", 38 | "ember-load-initializers": "^1.0.0", 39 | "ember-resolver": "^4.0.0", 40 | "ember-source": "~2.14.0", 41 | "loader.js": "^4.2.3" 42 | }, 43 | "engines": { 44 | "node": "^4.5 || 6.* || >= 7.*" 45 | }, 46 | "ember-addon": { 47 | "configPath": "tests/dummy/config" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/addon-test-app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | const util = require('util'); 6 | const EOL = require('os').EOL; 7 | const App = require('./app'); 8 | const pristine = require('../utilities/pristine'); 9 | const copyFixtureFiles = require('../utilities/copy-fixture-files'); 10 | 11 | function AddonTestApp() { 12 | App.call(this); 13 | } 14 | 15 | util.inherits(AddonTestApp, App); 16 | 17 | // Public API for putting an app under test. If the app doesn't 18 | // exist already, it will create it and put it into the `pristine` 19 | // directory, then put a copy into `under-test`. Subsequent calls 20 | // to `createApp()` will use the pristine app as a cache. 21 | AddonTestApp.prototype.create = function(appName, options) { 22 | this.appName = appName; 23 | options = options || {}; 24 | 25 | return pristine.createApp(appName, options) 26 | .then(appPath => { 27 | this.path = appPath; 28 | return options.noFixtures ? 29 | Promise.resolve() : 30 | copyFixtureFiles(appName, appPath, options.fixturesPath); 31 | }) 32 | .then(() => this); 33 | }; 34 | 35 | AddonTestApp.prototype.editPackageJSON = function(cb) { 36 | let packageJSONPath = path.join(this.path, 'package.json'); 37 | let pkg = fs.readJsonSync(packageJSONPath); 38 | cb(pkg); 39 | fs.writeJsonSync(packageJSONPath, pkg, { 40 | spaces: 2, 41 | EOL 42 | }); 43 | }; 44 | 45 | module.exports = AddonTestApp; 46 | -------------------------------------------------------------------------------- /lib/commands/start-server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const debug = require('../utilities/debug'); 4 | const runEmber = require('../utilities/run-ember'); 5 | const defaults = require('lodash/defaults'); 6 | const temp = require('../utilities/temp'); 7 | 8 | module.exports = function runServer(options) { 9 | return new Promise((resolve, reject) => { 10 | options = options || { }; 11 | 12 | defaults(options, { 13 | port: '49741', 14 | command: 'server', 15 | detectServerStart 16 | }); 17 | 18 | let args = [ 19 | '--port', options.port 20 | ]; 21 | 22 | if (options.additionalArguments) { 23 | args = args.concat(options.additionalArguments); 24 | } 25 | 26 | let longRunningServerPromise; 27 | 28 | let commandOptions = { 29 | verbose: true, 30 | 31 | onOutput(output, child) { 32 | if (options.detectServerStart(output)) { 33 | resolve({ 34 | server: child, 35 | longRunningServerPromise 36 | }); 37 | } 38 | } 39 | }; 40 | 41 | args.push(commandOptions); 42 | 43 | debug('starting server; command=%s; port=%s', options.command, options.port); 44 | 45 | longRunningServerPromise = runEmber(options.command, args, temp.pristineNodeModulesPath) 46 | .then(() => { 47 | throw new Error('The server should not have exited successfully.'); 48 | }) 49 | .catch(reject); 50 | }); 51 | }; 52 | 53 | function detectServerStart(output) { 54 | let indicators = [ 55 | 'Ember FastBoot running at', 56 | 'Build successful' 57 | ]; 58 | 59 | for (let indicator of indicators) { 60 | if (output.indexOf(indicator) > -1) { 61 | return true; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/models/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const startServer = require('../commands/start-server'); 5 | const chdir = require('../utilities/chdir'); 6 | const runCommand = require('../utilities/run-command'); 7 | const killCliProcess = require('../utilities/kill-cli-process'); 8 | 9 | function App(path) { 10 | this.path = path; 11 | } 12 | 13 | App.prototype.run = function() { 14 | let previousCwd = process.cwd(); 15 | chdir(this.path); 16 | 17 | return runCommand.apply(null, arguments) 18 | .then(function(result) { 19 | chdir(previousCwd); 20 | return result; 21 | }, function(err) { 22 | chdir(previousCwd); 23 | throw err; 24 | }); 25 | }; 26 | 27 | App.prototype.runEmberCommand = function() { 28 | let cliPath = path.join(this.path, 'node_modules', 'ember-cli', 'bin', 'ember'); 29 | let args = Array.prototype.slice.apply(arguments); 30 | 31 | return this.run.apply(this, [cliPath].concat(args)); 32 | }; 33 | 34 | App.prototype.filePath = function(filePath) { 35 | return path.join(this.path, filePath); 36 | }; 37 | 38 | App.prototype.startServer = function(options) { 39 | let previousCwd = process.cwd(); 40 | process.chdir(this.path); 41 | 42 | return startServer(options) 43 | .then(result => { 44 | this.server = result.server; 45 | this._longRunningServerPromise = result.longRunningServerPromise; 46 | process.chdir(previousCwd); 47 | }); 48 | }; 49 | 50 | App.prototype.stopServer = function() { 51 | if (!this.server) { 52 | throw new Error('You must call `startServer()` before calling `stopServer()`.'); 53 | } 54 | 55 | killCliProcess(this.server); 56 | 57 | return this._longRunningServerPromise.catch(() => { 58 | this.server = null; 59 | }); 60 | }; 61 | 62 | module.exports = App; 63 | -------------------------------------------------------------------------------- /test/fixtures/my-addon/config/ember-try.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | scenarios: [ 4 | { 5 | name: 'ember-lts-2.8', 6 | bower: { 7 | dependencies: { 8 | 'ember': 'components/ember#lts-2-8' 9 | }, 10 | resolutions: { 11 | 'ember': 'lts-2-8' 12 | } 13 | }, 14 | npm: { 15 | devDependencies: { 16 | 'ember-source': null 17 | } 18 | } 19 | }, 20 | { 21 | name: 'ember-lts-2.12', 22 | npm: { 23 | devDependencies: { 24 | 'ember-source': '~2.12.0' 25 | } 26 | } 27 | }, 28 | { 29 | name: 'ember-release', 30 | bower: { 31 | dependencies: { 32 | 'ember': 'components/ember#release' 33 | }, 34 | resolutions: { 35 | 'ember': 'release' 36 | } 37 | }, 38 | npm: { 39 | devDependencies: { 40 | 'ember-source': null 41 | } 42 | } 43 | }, 44 | { 45 | name: 'ember-beta', 46 | bower: { 47 | dependencies: { 48 | 'ember': 'components/ember#beta' 49 | }, 50 | resolutions: { 51 | 'ember': 'beta' 52 | } 53 | }, 54 | npm: { 55 | devDependencies: { 56 | 'ember-source': null 57 | } 58 | } 59 | }, 60 | { 61 | name: 'ember-canary', 62 | bower: { 63 | dependencies: { 64 | 'ember': 'components/ember#canary' 65 | }, 66 | resolutions: { 67 | 'ember': 'canary' 68 | } 69 | }, 70 | npm: { 71 | devDependencies: { 72 | 'ember-source': null 73 | } 74 | } 75 | }, 76 | { 77 | name: 'ember-default', 78 | npm: { 79 | devDependencies: {} 80 | } 81 | } 82 | ] 83 | }; 84 | -------------------------------------------------------------------------------- /test/acceptance/complicated-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const expect = require('chai').expect; 5 | const denodeify = require('denodeify'); 6 | const request = denodeify(require('request')); 7 | const AddonTestApp = require('../../lib').AddonTestApp; 8 | const copy = require('fs-extra').copy; 9 | const createAddon = require('../helpers/create-addon'); 10 | 11 | describe('Acceptance | complicated', function() { 12 | this.timeout(process.platform === 'win32' ? 500000 : 300000); 13 | 14 | let app; 15 | 16 | before(function() { 17 | app = new AddonTestApp(); 18 | 19 | return createAddon(() => { 20 | return app.create('dummy', { 21 | fixturesPath: 'tests', 22 | skipNpm: true 23 | }); 24 | }).then(() => { 25 | return copy( 26 | path.join(__dirname, '../fixtures/random-template.hbs'), 27 | app.filePath('app/templates/random-template.hbs') 28 | ); 29 | }).then(() => { 30 | app.editPackageJSON(pkg => { 31 | pkg.devDependencies['ember-cli-fastboot'] = process.env.npm_package_devDependencies_ember_cli_fastboot; 32 | }); 33 | return app.run('npm', 'install'); 34 | }); 35 | }); 36 | 37 | beforeEach(function() { 38 | return app.startServer({ 39 | detectServerStart(output) { 40 | return output.indexOf('Serving on ') > -1; 41 | } 42 | }); 43 | }); 44 | 45 | afterEach(function() { 46 | return app.stopServer(); 47 | }); 48 | 49 | it('works', function() { 50 | return request({ 51 | url: 'http://localhost:49741', 52 | headers: { 53 | // We have to send the `Accept` header so the ember-cli server sees this as a request to `index.html` and sets 54 | // `req.serveUrl`, that ember-cli-fastboot needs in its middleware 55 | // See https://github.com/ember-cli/ember-cli/blob/86a903f/lib/tasks/server/middleware/history-support/index.js#L55 56 | // and https://github.com/ember-fastboot/ember-cli-fastboot/blob/28213e0/index.js#L160 57 | 'Accept': 'text/html' 58 | } 59 | }) 60 | .then(response => { 61 | expect(response.body).to.contain('my-addon is working'); 62 | }); 63 | }); 64 | 65 | it('can run a second one right after the first on the same port (assert port cleanup)', function() { 66 | return request({ 67 | url: 'http://localhost:49741', 68 | headers: { 69 | 'Accept': 'text/html' 70 | } 71 | }) 72 | .then(response => { 73 | expect(response.body).to.contain('my-addon is working'); 74 | }); 75 | }); 76 | 77 | it('exposes `app.path` for manual fixture copying', function() { 78 | return request('http://localhost:49741/assets/dummy.js') 79 | .then(response => { 80 | expect(response.body).to.contain('random template'); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /lib/utilities/temp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | const makeTemp = require('temp').track(); 6 | 7 | const symlinkDirectory = require('./symlink-directory'); 8 | const debug = require('./debug'); 9 | 10 | const temp = module.exports = { 11 | path: null, 12 | pristinePath: null, 13 | pristineNodeModulesPath: null, 14 | pristineBowerComponentsPath: null, 15 | underTestPath: null, 16 | 17 | ensureCreated 18 | }; 19 | 20 | // Creates a temp directory outside the project for creating new Ember CLI 21 | // apps in. (New apps cannot be created in the project directory because it is itself 22 | // an Ember app and Ember CLI doesn't like that). 23 | // 24 | // Once created, this function creates two directories: 25 | // 26 | // - `pristine` 27 | // - `under-test` 28 | // 29 | // `pristine` is used to store pristine versions of apps, as well as for caching 30 | // `node_modules` and `bower_components` directories. As multiple tests get run, 31 | // the `pristine` directory is used as a cache to avoid expensive operations such 32 | // as `ember new` and `npm install`. 33 | // 34 | // `under-test` is the directory where the app being tested by the current acceptance 35 | // test goes. At the beginning of the test, the app is copied from `pristine` to 36 | // `under-test`. 37 | 38 | function ensureCreated() { 39 | if (temp.path) { 40 | return temp.path; 41 | } 42 | 43 | temp.path = makeTemp.mkdirSync(); 44 | 45 | temp.pristinePath = path.join(temp.path, 'pristine'); 46 | temp.pristineNodeModulesPath = path.join(temp.pristinePath, 'node_modules'); 47 | temp.pristineBowerComponentsPath = path.join(temp.pristinePath, 'bower_components'); 48 | 49 | temp.underTestPath = path.join(temp.path, 'under-test'); 50 | 51 | fs.mkdirsSync(temp.pristinePath); 52 | fs.mkdirsSync(temp.underTestPath); 53 | 54 | debug('created tmp; path=' + temp.path); 55 | 56 | // To speed up test runs, use the project's `tmp/precooked_node_modules` directory 57 | // if it exists. 58 | symlinkPrecookedNodeModules(); 59 | 60 | return temp.path; 61 | } 62 | 63 | // If the user has supplied a `tmp/precooked_node_modules` directory, that is symlinked 64 | // into the `pristine` directory before an app is created. That will be used rather than 65 | // having `ember new` do an `npm install`, saving significant time. 66 | function symlinkPrecookedNodeModules() { 67 | let precookedNodeModulesPath = path.join(process.cwd(), 'tmp', 'precooked_node_modules', 'node_modules'); 68 | 69 | // If the user running the tests has provided a "precooked" node_modules directory to be used 70 | // by an Ember app, we use that as the pristine version instead of running `npm install`. This 71 | // greatly reduces the time tests take to run. 72 | if (fs.existsSync(precookedNodeModulesPath)) { 73 | debug('symlinking precooked node_modules; path=' + precookedNodeModulesPath); 74 | symlinkDirectory(precookedNodeModulesPath, temp.pristineNodeModulesPath); 75 | } else { 76 | debug('no precooked node_modules'); 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /lib/utilities/run-command.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const denodeify = require('denodeify'); 4 | const chalk = require('chalk'); 5 | const childProcess = require('child_process'); 6 | const spawn = childProcess.spawn; 7 | const defaults = require('lodash/defaults'); 8 | const killCliProcess = require('./kill-cli-process'); 9 | const debug = require('./debug'); 10 | const logOnFailure = require('./log-on-failure'); 11 | const exec = denodeify(childProcess.exec); 12 | 13 | const isWindows = process.platform === 'win32'; 14 | 15 | module.exports = function run(/* command, args, options */) { 16 | let command = arguments[0]; 17 | let args = Array.prototype.slice.call(arguments, 1); 18 | let options = {}; 19 | 20 | if (typeof args[args.length - 1] === 'object') { 21 | options = args.pop(); 22 | } 23 | 24 | debug('running command=' + command + '; args=' + args + '; cwd=' + process.cwd()); 25 | 26 | if (isWindows && (command === 'npm' || command === 'bower')) { 27 | return exec(command + ' ' + args.join(' ')); 28 | } 29 | 30 | options = defaults(options, { 31 | 32 | onOutput(string) { 33 | options.log(string); 34 | }, 35 | 36 | onError(string) { 37 | options.log(chalk.red(string)); 38 | }, 39 | 40 | log(string) { 41 | debug(string); 42 | 43 | logOnFailure(string); 44 | } 45 | }); 46 | 47 | return new Promise((resolve, reject) => { 48 | let opts = {}; 49 | if (isWindows) { 50 | args = ['"' + command + '"'].concat(args); 51 | command = 'node'; 52 | opts.windowsVerbatimArguments = true; 53 | opts.stdio = [null, null, null, 'ipc']; 54 | } 55 | let child = spawn(command, args, opts); 56 | let result = { 57 | output: [], 58 | errors: [], 59 | code: null 60 | }; 61 | 62 | if (options.onChildSpawned) { 63 | let onChildSpawnedPromise = new Promise((childSpawnedResolve, childSpawnedReject) => { 64 | try { 65 | options.onChildSpawned(child).then(childSpawnedResolve, childSpawnedReject); 66 | } catch (err) { 67 | childSpawnedReject(err); 68 | } 69 | }); 70 | onChildSpawnedPromise 71 | .then(() => { 72 | if (options.killAfterChildSpawnedPromiseResolution) { 73 | killCliProcess(child); 74 | } 75 | }, err => { 76 | result.testingError = err; 77 | if (options.killAfterChildSpawnedPromiseResolution) { 78 | killCliProcess(child); 79 | } 80 | }); 81 | } 82 | 83 | child.stdout.on('data', data => { 84 | let string = data.toString(); 85 | 86 | options.onOutput(string, child); 87 | 88 | result.output.push(string); 89 | }); 90 | 91 | child.stderr.on('data', data => { 92 | let string = data.toString(); 93 | 94 | options.onError(string, child); 95 | 96 | result.errors.push(string); 97 | }); 98 | 99 | child.on('close', (code, signal) => { 100 | result.code = code; 101 | result.signal = signal; 102 | 103 | if (code === 0) { 104 | resolve(result); 105 | } else { 106 | reject(result); 107 | } 108 | }); 109 | }); 110 | }; 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember CLI Addon Tests 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/tomdale/ember-cli-addon-tests.svg)](https://greenkeeper.io/) 4 | [![npm version](https://badge.fury.io/js/ember-cli-addon-tests.svg)](https://badge.fury.io/js/ember-cli-addon-tests) 5 | [![Build Status - Travis](https://travis-ci.org/tomdale/ember-cli-addon-tests.svg?branch=master)](https://travis-ci.org/tomdale/ember-cli-addon-tests) 6 | [![Build Status - AppVeyor](https://ci.appveyor.com/api/projects/status/ifp893hf5s6j5uuy/branch/master?svg=true)](https://ci.appveyor.com/project/tomdale/ember-cli-addon-tests/branch/master) 7 | 8 | Test helpers for testing Ember CLI addons inside the context of a real 9 | Ember app. 10 | 11 | Previously, it was difficult to do real integration testing with Ember 12 | CLI addons because the process of creating a new Ember app is very slow, due 13 | to the required `npm install` and `bower install` steps. 14 | 15 | This package automates the process of creating a new Ember CLI app and 16 | caching its npm and Bower dependencies, so each test run can get a fresh 17 | app in very little time. Best of all, you'll be testing your addon in a 18 | real app so you can catch integration issues early. 19 | 20 | **Stability Note**: API likely to change 21 | 22 | ## Installation 23 | 24 | ```sh 25 | npm install ember-cli-addon-tests --save-dev 26 | ``` 27 | 28 | ## Example 29 | 30 | ```js 31 | 'use strict'; 32 | 33 | const expect = require('chai').expect; 34 | const denodeify = require('denodeify'); 35 | const request = denodeify(require('request')); 36 | const AddonTestApp = require('ember-cli-addon-tests').AddonTestApp; 37 | 38 | describe('serve assets acceptance', function() { 39 | this.timeout(300000); 40 | 41 | let app; 42 | 43 | before(function() { 44 | app = new AddonTestApp(); 45 | 46 | return app.create('dummy') 47 | .then(() => { 48 | return app.startServer(); 49 | }); 50 | }); 51 | 52 | after(function() { 53 | return app.stopServer(); 54 | }); 55 | 56 | it('/index.html', function() { 57 | return request({ 58 | url: 'http://localhost:49741', 59 | headers: { 60 | 'Accept': 'text/html' 61 | } 62 | }) 63 | .then(response => { 64 | expect(response.statusCode).to.equal(200); 65 | expect(response.headers["content-type"]).to.eq("text/html"); 66 | expect(response.body).to.contain(""); 67 | }); 68 | }); 69 | 70 | it('/assets/vendor.js', function() { 71 | return request('http://localhost:49741/assets/vendor.js') 72 | .then(response => { 73 | expect(response.statusCode).to.equal(200); 74 | expect(response.headers["content-type"]).to.eq("application/javascript"); 75 | expect(response.body).to.contain("Ember ="); 76 | }); 77 | }); 78 | 79 | it('/assets/dummy.js', function() { 80 | return request('http://localhost:49741/assets/dummy.js') 81 | .then(response => { 82 | expect(response.statusCode).to.equal(200); 83 | expect(response.headers["content-type"]).to.eq("application/javascript"); 84 | expect(response.body).to.contain("this.route('posts')"); 85 | }); 86 | }); 87 | }); 88 | ``` 89 | 90 | See the [ember-cli-fastboot tests](https://github.com/ember-fastboot/ember-cli-fastboot/tree/master/test) 91 | for real world examples. 92 | 93 | ## Defining a New App 94 | 95 | Creates a new app for testing. 96 | 97 | ```js 98 | const AddonTestApp = require('ember-cli-addon-tests').AddonTestApp; 99 | app = new AddonTestApp(); 100 | ``` 101 | 102 | ## Creating the App 103 | 104 | This starts the process of actually creating a new Ember CLI app on 105 | disk. The first run may take several minutes while the `npm install` 106 | happens. Subsequent runs will be faster. Pass the name of the 107 | application as the first argument. 108 | 109 | ```js 110 | // returns a promise 111 | app.create('my-app'); 112 | ``` 113 | 114 | ### "Precooking" Node Modules 115 | 116 | You can "precook" (essentially pre-install) the node modules for the test 117 | applications by using `scripts/precook-node-modules.js`. This will speed up 118 | test runs by configuring a `node_modules` directory that will be reused. 119 | 120 | ### Options 121 | 122 | You can customize the app by supplying an options hash: 123 | 124 | ```js 125 | // returns a promise 126 | app.create('my-app', { 127 | emberVersion: 'release' 128 | }); 129 | ``` 130 | 131 | The following options exist: 132 | 133 | | option | description | defaults to | 134 | |------------------|-----------------------------------------------------------------------------------------------|---------------------| 135 | | emberVersion | Set the ember version the app should be created with, as you would in your `bower.json` | canary | 136 | | emberDataVersion | Set the version of ember-data, as you would in your `package.json` | emberjs/data#master | 137 | | fixturesPath | The path to look for your fixture files (see below) | test/fixtures | 138 | | noFixtures | Disables the use of fixture files | false | 139 | | skipNpm | Useful if you want to edit the `package.json` and install later (skips the default blueprint) | false | 140 | 141 | 142 | ### Fixtures 143 | 144 | You will probably want to add files to the Ember application that you 145 | want to test your addon with. Ember CLI Addon Tests will automatically 146 | copy fixtures on top of the base Ember CLI app, based on the name of the 147 | application that you created. 148 | 149 | For example, if you call `app.create('my-app')`, the test helper will 150 | look for a file called `test/fixtures/my-app` in your addon's directory 151 | and will copy them to the test app, overwriting any files that exist. 152 | 153 | If you do not need fixture files in your test, you can disable them by 154 | specifying the `noFixtures` option. 155 | 156 | Once the promise resolves, you can inspect the temporary location of the 157 | app under test via `app.path`: 158 | 159 | ```js 160 | app.create('my-app').then(() => { 161 | console.log(app.path); 162 | // /var/folders/vc/wjjhq0f542q3dn2109clfy81dlk662/T/d-117613-7500-1bq89dh.8ts6wuq5mi/under-test/my-app 163 | // or 164 | // C:\Users\kelly\AppData\Local\Temp\d-117613-15884-1j1bw40.5kbh\under-test\my-app 165 | }); 166 | ``` 167 | 168 | ## Editing App's `package.json` 169 | 170 | If your addon depends on end developers configuring their application's 171 | `package.json`, you can edit the test app's `package.json` with the 172 | `editPackageJSON` method: 173 | 174 | ```js 175 | // runs synchronously 176 | app.editPackageJSON(pkg => { 177 | pkg.devDependencies['fake-addon'] = "*"; 178 | pkg.devDependencies['fake-addon-2'] = "*"; 179 | }); 180 | ``` 181 | 182 | You should not call `app.editPackageJSON()` until after the `create()` 183 | promise has resolved. 184 | 185 | ## Starting the Server 186 | 187 | To test the assets served by Ember CLI, you can start the server (i.e., 188 | `ember serve`) via the `startServer()` method: 189 | 190 | ```js 191 | // returns a promise 192 | app.startServer(); 193 | ``` 194 | 195 | You can also pass additional command line arguments via the 196 | `additionalArguments` option: 197 | 198 | ```js 199 | // equivalent to `ember serve -prod` 200 | app.startServer({ 201 | additionalArguments: ['-prod'] 202 | }); 203 | ``` 204 | 205 | You can run your own command like `ember foo` instead of `ember serve`. 206 | Then you need to tell it what to look for in the console to know it is ready.: 207 | 208 | ```js 209 | app.startServer({ 210 | command: 'foo', 211 | detectServerStart(output) { 212 | return output.indexOf('foo is ready') > -1; 213 | } 214 | }); 215 | ``` 216 | 217 | ## Stopping the Server 218 | 219 | After your tests, stop the development server via `stopServer()`. 220 | 221 | ```js 222 | app.stopServer(); 223 | ``` 224 | 225 | ## Running Commands 226 | 227 | You can run arbitrary commands inside the test app via the `run()` 228 | method. Takes a command and optional arguments. 229 | 230 | ```js 231 | // returns a promise 232 | app.run('ember', 'build', '--verbose'); 233 | ``` 234 | 235 | ## Running Ember CLI Commands 236 | 237 | You can run commands using the app's version of Ember CLI via the 238 | `runEmberCommand` method: 239 | 240 | ```js 241 | // equivalent to `ember build --environment production` 242 | app.runEmberCommand('build', '--environment', 'production'); 243 | ``` 244 | 245 | ## Cleanup 246 | 247 | Temporary directories are automatically deleted once the process exits. 248 | -------------------------------------------------------------------------------- /lib/utilities/pristine.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const debug = require('./debug'); 5 | const temp = require('./temp'); 6 | const chdir = require('./chdir'); 7 | const fs = require('fs-extra'); 8 | const findup = require('findup-sync'); 9 | const existsSync = fs.existsSync; 10 | const moveDirectory = require('./move-directory'); 11 | const symlinkDirectory = require('./symlink-directory'); 12 | const runCommand = require('./run-command'); 13 | const runEmber = require('./run-ember'); 14 | const runNew = require('./run-new'); 15 | const semver = require('semver'); 16 | 17 | // As of Ember-CLI@2.13.0, it no longer uses Bower by default 18 | const usesBower = semver.lt(require('ember-cli/package').version, '2.13.0'); 19 | const runCommandOptions = { 20 | // Note: We must override the default logOnFailure logging, because we are 21 | // not inside a test. 22 | log() { 23 | return; // no output for initial application build 24 | } 25 | }; 26 | 27 | let previousCwd; 28 | 29 | /** 30 | * Creates a new application with the given name. If the application was created 31 | * previously, a fresh clone will be created. 32 | * 33 | * @param {String} appName 34 | * @param {Object} [options] 35 | * @param {String} [options.emberVersion] 36 | * @param {String} [options.emberDataVersion] 37 | */ 38 | function createApp(appName, options) { 39 | let tempDir = temp.ensureCreated(); 40 | 41 | previousCwd = process.cwd(); 42 | debug('previousCwd=%s', previousCwd); 43 | 44 | chdir(tempDir); 45 | 46 | let pristineAppPath = path.join(temp.pristinePath, appName); 47 | let underTestAppPath = path.join(temp.underTestPath, appName); 48 | 49 | // If this app was already tested, delete the copy. 50 | // This ensures that any modifications made during testing are 51 | // reset. 52 | if (existsSync(underTestAppPath)) { 53 | fs.removeSync(underTestAppPath); 54 | } 55 | 56 | // If a pristine version of the app doesn't exist, create one by installing it. 57 | let appInstallation; 58 | if (!fs.existsSync(pristineAppPath)) { 59 | appInstallation = installPristineApp(appName, options); 60 | } else { 61 | appInstallation = Promise.resolve(); 62 | } 63 | 64 | return appInstallation.then(() => { 65 | copyUnderTestApp(pristineAppPath, underTestAppPath); 66 | chdir(previousCwd); 67 | return underTestAppPath; 68 | }); 69 | } 70 | 71 | module.exports = { 72 | createApp 73 | }; 74 | 75 | function installPristineApp(appName, options) { 76 | let hasNodeModules = hasPristineNodeModules(); 77 | let hasBowerComponents = hasPristineBowerComponents(); 78 | let skipNpm = options.skipNpm; 79 | 80 | chdir(temp.pristinePath); 81 | 82 | // Install a vanilla app and cd into it 83 | let args = generateArgsForEmberNew( 84 | hasNodeModules, 85 | hasBowerComponents, 86 | skipNpm 87 | ); 88 | let promise = runNew(appName, args) 89 | .catch(handleResult) 90 | .then(() => chdir(path.join(temp.pristinePath, appName))); 91 | 92 | let setupOptions = { 93 | appName, 94 | hasNodeModules, 95 | hasBowerComponents, 96 | skipNpm, 97 | emberVersion: options.emberVersion || 'canary', 98 | emberDataVersion: options.emberDataVersion || '~3.8.0' 99 | }; 100 | 101 | promise = promise 102 | .then(() => nodeModulesSetup(setupOptions)) 103 | .then(() => { 104 | if (usesBower) { 105 | return bowerSetup(setupOptions); 106 | } 107 | }); 108 | 109 | // All dependencies should be installed, so symlink them into the app and 110 | // run the addon's blueprint to finish the app creation. 111 | return promise 112 | .then(() => linkDependencies(appName)) 113 | .then(runAddonGenerator); 114 | } 115 | 116 | // Generates the arguments to pass to `ember new`. Optionally skipping the 117 | // npm and/or bower installation phases. 118 | function generateArgsForEmberNew(hasNodeModules, hasBowerComponents, skipNpm) { 119 | let extraOptions = []; 120 | 121 | if (skipNpm || hasNodeModules) { 122 | debug('skipping npm'); 123 | extraOptions.push('--skip-npm'); 124 | } 125 | 126 | if (hasBowerComponents) { 127 | debug('skipping bower'); 128 | extraOptions.push('--skip-bower'); 129 | } 130 | 131 | return extraOptions.concat(runCommandOptions); 132 | } 133 | 134 | function nodeModulesSetup(options) { 135 | let appName = options.appName; 136 | let emberVersion = options.emberVersion; 137 | let emberDataVersion = options.emberDataVersion; 138 | let hasNodeModules = options.hasNodeModules; 139 | let skipNpm = options.skipNpm; 140 | 141 | // Modifies the package.json to include correct versions of dependencies 142 | let promise = Promise.resolve() 143 | .then(() => addEmberDataToDependencies(appName, emberDataVersion)) 144 | .then(() => updateEmberSource(appName, emberVersion)); 145 | 146 | if (!hasNodeModules) { 147 | if (!skipNpm) { 148 | promise = promise 149 | .then(() => { 150 | debug('installing ember-disable-prototype-extensions'); 151 | return runCommand('npm', 'install', 'ember-disable-prototype-extensions'); 152 | }) 153 | .then(() => { 154 | debug('installed ember-disable-prototype-extension'); 155 | return runCommand('npm', 'install'); 156 | }) 157 | .then(() => debug('installed ember-data ' + emberDataVersion)); 158 | } 159 | promise = promise 160 | .then(() => symlinkAddon(appName)) 161 | .then(() => movePristineNodeModules(appName)); 162 | } 163 | 164 | return promise.then(() => addAddonUnderTestToDependencies(appName)); 165 | } 166 | 167 | function bowerSetup(options) { 168 | let appName = options.appName; 169 | let emberVersion = options.emberVersion; 170 | let hasBowerComponents = options.hasBowerComponents; 171 | 172 | let promise = Promise.resolve() 173 | .then(() => addEmberToBowerJSON(appName, emberVersion)) 174 | .then(() => removeEmberDataFromBowerJSON(appName)); 175 | 176 | if (!hasBowerComponents) { 177 | promise = promise 178 | .then(() => runCommand('bower', 'install')) 179 | .then(() => debug('installed ember ' + emberVersion)) 180 | .then(() => movePristineBowerComponents(appName)); 181 | } 182 | 183 | return promise; 184 | } 185 | 186 | function hasPristineNodeModules() { 187 | return existsSync(temp.pristineNodeModulesPath); 188 | } 189 | 190 | function hasPristineBowerComponents() { 191 | return existsSync(temp.pristineBowerComponentsPath); 192 | } 193 | 194 | function movePristineNodeModules(appName) { 195 | let nodeModulesPath = path.join(temp.pristinePath, appName, 'node_modules'); 196 | moveDirectory(nodeModulesPath, temp.pristineNodeModulesPath); 197 | } 198 | 199 | function movePristineBowerComponents(appName) { 200 | let bowerComponentsPath = path.join(temp.pristinePath, appName, 'bower_components'); 201 | moveDirectory(bowerComponentsPath, temp.pristineBowerComponentsPath); 202 | } 203 | 204 | function handleResult(result) { 205 | debug('handling result'); 206 | throw result; 207 | } 208 | 209 | function copyUnderTestApp(pristineAppPath, underTestAppPath) { 210 | debug('copying pristine app; from=' + pristineAppPath + '; to=' + underTestAppPath); 211 | fs.copySync(pristineAppPath, underTestAppPath); 212 | debug('copying complete'); 213 | } 214 | 215 | function addEmberToBowerJSON(appName, version) { 216 | let bowerJSONPath = path.join(temp.pristinePath, appName, 'bower.json'); 217 | let bowerJSON = fs.readJsonSync(bowerJSONPath); 218 | 219 | bowerJSON.resolutions = { 220 | 'ember': version 221 | }; 222 | 223 | bowerJSON.dependencies['ember'] = version; 224 | 225 | fs.writeJsonSync(bowerJSONPath, bowerJSON); 226 | } 227 | 228 | function removeEmberDataFromBowerJSON(appName) { 229 | let bowerJSONPath = path.join(temp.pristinePath, appName, 'bower.json'); 230 | let bowerJSON = fs.readJsonSync(bowerJSONPath); 231 | 232 | delete bowerJSON.dependencies['ember-data']; 233 | 234 | fs.writeJsonSync(bowerJSONPath, bowerJSON); 235 | } 236 | 237 | function symlinkAddon(appName) { 238 | let pkg = findAddonPackageJSON(); 239 | let addonPath = findAddonPath(); 240 | let nodeModules = path.join(temp.pristinePath, appName, 'node_modules'); 241 | 242 | // if option skipNpm is supplied, node_modules doesn't exist yet 243 | fs.ensureDirSync(nodeModules); 244 | 245 | let addonSymlinkPath = path.join(nodeModules, pkg.name); 246 | let pkgScope = path.dirname(pkg.name); 247 | 248 | debug('symlinking %s', pkg.name); 249 | 250 | if (pkgScope !== '.') { 251 | debug('symlink: creating directory %s for scoped package name', pkgScope); 252 | // In case the package has a scoped name, make sure the scope directoy exists before symlinking 253 | fs.ensureDirSync(path.join(temp.pristinePath, appName, 'node_modules', pkgScope)); 254 | } 255 | 256 | if (existsSync(addonSymlinkPath)) { 257 | let stats = fs.lstatSync(addonSymlinkPath); 258 | if (stats.isSymbolicLink()) { 259 | debug('%s is already symlinked', pkg.name); 260 | return; 261 | } 262 | 263 | fs.removeSync(addonSymlinkPath); 264 | } 265 | 266 | symlinkDirectory(addonPath, addonSymlinkPath); 267 | } 268 | 269 | function addEmberDataToDependencies(appName, version) { 270 | let packageJSONPath = path.join(temp.pristinePath, appName, 'package.json'); 271 | 272 | debug('installing ember-data ' + version); 273 | 274 | let packageJSON = fs.readJsonSync(packageJSONPath); 275 | 276 | packageJSON.devDependencies['ember-data'] = version; 277 | 278 | fs.writeJsonSync('package.json', packageJSON); 279 | } 280 | 281 | function updateEmberSource(appName, version) { 282 | let packageJSONPath = path.join(temp.pristinePath, appName, 'package.json'); 283 | let packageJSON = fs.readJsonSync(packageJSONPath); 284 | 285 | // If we're not using bower, but the ember version is canary, we change it to 286 | // latest instead. This is because ember-source does not support canary releases. 287 | if (!usesBower && version === 'canary') { 288 | debug('ember-source cannot use canary releases, defaulting to beta'); 289 | version = 'latest'; 290 | } 291 | 292 | if (packageJSON.devDependencies.hasOwnProperty('ember-source')) { 293 | // If we're using bower, we need to remove ember-source from package.json, 294 | // otherwise we update to the appropriate version. 295 | if (usesBower) { 296 | debug('removing ember-source from NPM '); 297 | delete packageJSON.devDependencies['ember-source']; 298 | } else { 299 | debug('updating ember-source version to %s', version); 300 | packageJSON.devDependencies['ember-source'] = version; 301 | } 302 | 303 | fs.writeJsonSync('package.json', packageJSON); 304 | } 305 | } 306 | 307 | function addAddonUnderTestToDependencies(appName) { 308 | let pkg = findAddonPackageJSON(); 309 | let packageJSONPath = path.join(temp.pristinePath, appName, 'package.json'); 310 | 311 | debug('installing %s as addon for application', pkg.name); 312 | 313 | // Read the current version of the FastBoot addon under test, then add that 314 | // to the Ember app's package.json. 315 | let packageJSON = fs.readJsonSync(packageJSONPath); 316 | 317 | packageJSON.devDependencies[pkg.name] = pkg.version; 318 | 319 | fs.writeJsonSync('package.json', packageJSON); 320 | } 321 | 322 | function runAddonGenerator() { 323 | let pkg = findAddonPackageJSON(); 324 | 325 | debug('running %s generator', pkg.name); 326 | 327 | let blueprintName = pkg.name; 328 | let emberAddon = pkg['ember-addon']; 329 | 330 | if (emberAddon && emberAddon.defaultBlueprint) { 331 | blueprintName = emberAddon.defaultBlueprint; 332 | } 333 | 334 | return runEmber('generate', [blueprintName]) 335 | .catch(() => { 336 | debug('default blueprint failed'); 337 | }); 338 | } 339 | 340 | function findAddonPath() { 341 | return path.dirname(findAddonPackageJSONPath()); 342 | } 343 | 344 | function findAddonPackageJSON() { 345 | let pkgPath = findAddonPackageJSONPath(); 346 | let pkg = fs.readJsonSync(pkgPath); 347 | return pkg; 348 | } 349 | 350 | function findAddonPackageJSONPath() { 351 | let foundPath = findup('package.json', { 352 | cwd: previousCwd 353 | }); 354 | 355 | if (foundPath) { 356 | debug('found addon package.json; path=%s', foundPath); 357 | } else { 358 | debug('couldn\'t find addon package.json'); 359 | } 360 | 361 | return foundPath; 362 | } 363 | 364 | function linkDependencies(appName) { 365 | let nodeModulesAppPath = path.join(temp.pristinePath, appName, 'node_modules'); 366 | symlinkDirectory(temp.pristineNodeModulesPath, nodeModulesAppPath); 367 | 368 | if (usesBower) { 369 | let bowerComponentsAppPath = path.join(temp.pristinePath, appName, 'bower_components'); 370 | symlinkDirectory(temp.pristineBowerComponentsPath, bowerComponentsAppPath); 371 | } 372 | } 373 | --------------------------------------------------------------------------------