diff --git a/addon/ng2/commands/get.ts b/addon/ng2/commands/get.ts index 00cd7d6cf326..1bdf3d06d86e 100644 --- a/addon/ng2/commands/get.ts +++ b/addon/ng2/commands/get.ts @@ -1,29 +1,30 @@ import * as chalk from 'chalk'; import * as Command from 'ember-cli/lib/models/command'; -import {CliConfig} from '../models/config'; +import {Config} from '../models/config'; const GetCommand = Command.extend({ name: 'get', - description: 'Set a value in the configuration.', - works: 'outsideProject', + description: 'Get a value from the configuration.', + works: 'everywhere', availableOptions: [], run: function (commandOptions, rawArgs): Promise { return new Promise(resolve => { - const value = new CliConfig().get(rawArgs[0]); - if (value === null) { + const config = new Config(); + config.validatePath(rawArgs[0]); + const val = config.get(config.config, rawArgs[0]); + + if (val === null) { console.error(chalk.red('Value cannot be found.')); - } else if (typeof value == 'object') { - console.log(JSON.stringify(value)); } else { - console.log(value); + console.log(val); } + resolve(); }); } }); module.exports = GetCommand; -module.exports.overrideCore = true; diff --git a/addon/ng2/commands/set.ts b/addon/ng2/commands/set.ts index ad695b21e773..871a64d4f70b 100644 --- a/addon/ng2/commands/set.ts +++ b/addon/ng2/commands/set.ts @@ -1,11 +1,11 @@ import * as Command from 'ember-cli/lib/models/command'; -import {CliConfig} from '../models/config'; +import {Config} from '../models/config'; const SetCommand = Command.extend({ name: 'set', description: 'Set a value in the configuration.', - works: 'outsideProject', + works: 'everywhere', availableOptions: [ { name: 'global', type: Boolean, default: false, aliases: ['g'] }, @@ -13,8 +13,14 @@ const SetCommand = Command.extend({ run: function (commandOptions, rawArgs): Promise { return new Promise(resolve => { - const config = new CliConfig(); - config.set(rawArgs[0], rawArgs[1], commandOptions.force); + let config = new Config(); + + for (let arg of rawArgs) { + let [key, value] = arg.split('='); + config.validatePath(key); + config.set(key, value); + } + config.save(); resolve(); }); @@ -22,4 +28,3 @@ const SetCommand = Command.extend({ }); module.exports = SetCommand; -module.exports.overrideCore = true; diff --git a/addon/ng2/models/config.ts b/addon/ng2/models/config.ts index 30aee51cd377..bb767c08f32d 100644 --- a/addon/ng2/models/config.ts +++ b/addon/ng2/models/config.ts @@ -1,125 +1,186 @@ import * as fs from 'fs'; import * as path from 'path'; - +import * as Proxy from 'harmony-proxy'; +import * as Reflect from 'harmony-reflect'; +import * as ObjectAssign from '../utilities/object-assign'; export const CLI_CONFIG_FILE_NAME = 'angular-cli.json'; +export const ArrayMethods: Array = ['pop', 'push', 'reverse', 'shift', 'sort', 'unshift']; -export interface CliConfigJson { - routes?: { [name: string]: any }, - packages?: { [name: string]: any } -} +export const handler = { + get: function(target: any, key: any, receiver: any) { + if (key === 'toJSON') { + return () => target; + } + if (key === 'length') { + return; + } -function _findUp(name: string, from: string) { - let currentDir = from; - while (currentDir && currentDir != '/') { - const p = path.join(currentDir, name); - if (fs.existsSync(p)) { - return p; + if (key === 'inspect') { + return target; } - currentDir = path.resolve(currentDir, '..'); - } + if (target.type === 'array') { + const arr: Array = []; + arr[key].call(target.enum); + } - return null; -} + if (ArrayMethods.indexOf(key) === -1) { + if (!(key in target)) { + target[key] = new Proxy({ type: 'object' }, handler); + } + return Reflect.get(target, key, receiver); + } else { + return Reflect.get([], key, receiver); + } + }, + set: function(target: any, key: any, value: any) { + if (key === 'length') { + return; + } + if (!isNaN(key) && parseInt(key, 10) === 0) { + if (!target.enum) { + target.type = 'array'; + target.enum = []; + } -export class CliConfig { - constructor(private _config: CliConfigJson = CliConfig.fromProject()) {} + if (value) { + target.enum.push(value); + } + } else { + if (target[key] && target[key].type) { + let type: string = target[key].type; + let assigningType: string; + + if (!value && value === null) { + assigningType = 'null'; + } else if (value && Array.isArray(value) && typeof value !== 'string') { + assigningType = 'array'; + } else { + assigningType = typeof value; + } - save(path: string = CliConfig.configFilePath()) { - if (!path) { - throw new Error('Could not find config path.'); - } + if (type !== assigningType) { + throw new Error(`Cannot assign value of type '${assigningType}' to an property with type '${type}'.`); + } + } - fs.writeFileSync(path, JSON.stringify(this._config, null, 2), { encoding: 'utf-8' }); + if (!value && value === null) { + target[key] = { type: 'null', value: value }; + } else if (value && Array.isArray(value) && typeof value !== 'string') { + target[key] = { type: 'array', enum: value }; + } else { + if (typeof value === 'object' && Object.getOwnPropertyNames(value).length === 0) { + target[key] = { type: typeof value }; + } else { + target[key] = { type: typeof value, value: value }; + } + } + } } +}; + +export interface ConfigJson { + routes?: { [name: string]: any }, + packages?: { [name: string]: any } +} - set(jsonPath: string, value: any, force: boolean = false): boolean { - let { parent, name, remaining } = this._findParent(jsonPath); - while (force && remaining) { - if (remaining.indexOf('.') != -1) { - // Create an empty map. - // TODO: create the object / array based on the Schema of the configuration. - parent[name] = {}; +export class Config { + path: ConfigJson; + config: Proxy; + + constructor(path?: string) { + if (path) { + try { + fs.accessSync(path); + this.path = path; + this.config = new Proxy({}, handler); + } catch (e) { + throw new Error(`${path} not found.`); } + } else { + this.path = this._configFilePath(); + this.config = new Proxy(this._fromProject(), handler); + } + } + public save(): void { + try { + let config = ObjectAssign({}, JSON.parse(fs.readFileSync(this.path, 'utf8')), this.config.toJSON()); + fs.writeFileSync(this.path, JSON.stringify(config, null, 2), 'utf8'); + } catch (e) { + throw new Error(`Error while saving config.`); } + } - parent[name] = value; - return true; + public set(path: string, value: any): void { + const levels = path.split('.'); + let current = this.config; + let i = 0; + while (i < levels.length - 1) { + delete current[levels[i]]; + current = current[levels[i]]; + i += 1; + } + + current[levels[levels.length - 1]] = value; } - get(jsonPath: string): any { - let { parent, name, remaining } = this._findParent(jsonPath); - if (remaining || !(name in parent)) { - return null; + public get(obj: any, path: string): any { + const levels = path.split('.'); + let current = obj; + let i = 0; + while (i < levels.length) { + if (current[levels[i]]) { + current = current[levels[i]]; + i += 1; + } else { + return null; + } + } + + if (current.type === 'array') { + return current.enum; + } else if (current.type === 'object') { + return current; } else { - return parent[name]; + return current.value; } } - private _validatePath(jsonPath: string) { + public validatePath(jsonPath: string) { if (!jsonPath.match(/^(?:[-_\w\d]+(?:\[\d+\])*\.)*(?:[-_\w\d]+(?:\[\d+\])*)$/)) { throw `Invalid JSON path: "${jsonPath}"`; } } - private _findParent(jsonPath: string): { parent: any, name: string | number, remaining?: string } { - this._validatePath(jsonPath); - - let parent: any = null; - let current: any = this._config; - - const splitPath = jsonPath.split('.'); - let name: string | number = ''; - - while (splitPath.length > 0) { - const m = splitPath.shift().match(/^(.*?)(?:\[(\d+)\])*$/); - - name = m[1]; - const index: string = m[2]; - parent = current; - current = current[name]; - - if (current === null || current === undefined) { - return { - parent, - name, - remaining: (!isNaN(index) ? `[${index}]` : '') + splitPath.join('.') - }; + private _findUp(name: string, from: string): string { + let currentDir = from; + while (currentDir && currentDir != '/') { + const p = path.join(currentDir, name); + if (fs.existsSync(p)) { + return p; } - if (!isNaN(index)) { - name = index; - parent = current; - current = current[index]; - - if (current === null || current === undefined) { - return { - parent, - name, - remaining: splitPath.join('.') - }; - } - } + currentDir = path.resolve(currentDir, '..'); } - return { parent, name }; + return null; } - static configFilePath(projectPath?: string): string { + private _configFilePath(projectPath?: string): string { // Find the configuration, either where specified, in the angular-cli project // (if it's in node_modules) or from the current process. - return (projectPath && _findUp(CLI_CONFIG_FILE_NAME, projectPath)) - || _findUp(CLI_CONFIG_FILE_NAME, __dirname) - || _findUp(CLI_CONFIG_FILE_NAME, process.cwd()); + return (projectPath && this._findUp(CLI_CONFIG_FILE_NAME, projectPath)) + || this._findUp(CLI_CONFIG_FILE_NAME, __dirname) + || this._findUp(CLI_CONFIG_FILE_NAME, process.cwd()); } - static fromProject(): CliConfigJson { - const configPath = this.configFilePath(); + private _fromProject(): ConfigJson { + const configPath = this._configFilePath(); return configPath ? require(configPath) : {}; } } diff --git a/addon/ng2/utilities/object-assign.js b/addon/ng2/utilities/object-assign.js new file mode 100644 index 000000000000..912263155d0b --- /dev/null +++ b/addon/ng2/utilities/object-assign.js @@ -0,0 +1,40 @@ +/* eslint-disable no-unused-vars */ +'use strict'; + +var hasOwnProperty = Object.prototype.hasOwnProperty; +var propIsEnumerable = Object.prototype.propertyIsEnumerable; + +function toObject(val) { + if (val === null || val === undefined) { + throw new TypeError('Object.assign cannot be called with null or undefined'); + } + + return Object(val); +} + +module.exports = Object.assign || function (target, source) { + var from; + var to = toObject(target); + var symbols; + + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); + + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + + if (Object.getOwnPropertySymbols) { + symbols = Object.getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } + + return to; +}; \ No newline at end of file diff --git a/bin/ng b/bin/ng index 9b2c78afd776..6924a6954444 100755 --- a/bin/ng +++ b/bin/ng @@ -1,4 +1,4 @@ -#!/usr/bin/env node +#!node --harmony-proxies 'use strict'; // Provide a title to the process in `ps` diff --git a/package.json b/package.json index f6a9e70c360f..a67c375f875b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "keywords": [], "scripts": { - "test": "node tests/runner", + "test": "node --harmony-proxies tests/runner", "lint": "eslint ." }, "repository": { @@ -42,6 +42,8 @@ "exit": "^0.1.2", "fs-extra": "^0.26.6", "glob": "^7.0.3", + "harmony-proxy": "^1.0.1", + "harmony-reflect": "^1.4.5", "leek": "0.0.21", "lodash": "^4.6.1", "multidep": "^2.0.0", diff --git a/tests/models/config.spec.ts b/tests/models/config.spec.ts new file mode 100644 index 000000000000..0d823d8a2563 --- /dev/null +++ b/tests/models/config.spec.ts @@ -0,0 +1,449 @@ +import {Config} from '../../addon/ng2/models/config'; + +const fs = require('fs'); +const path = require('path'); +const expect = require('chai').expect; +const schema = path.resolve(process.cwd(), 'test.json'); + +function getContents() { + return JSON.parse(fs.readFileSync(schema, 'utf8')); +} + +describe('Config Tests', () => { + before(() => { + process.chdir(process.cwd()); + }); + + beforeEach(() => { + fs.writeFileSync(schema, JSON.stringify({}), 'utf8'); + }); + + afterEach(() => { + try { + fs.accessSync(schema); + fs.unlinkSync(schema); + } catch (e) { /* */ } + }); + + it('Throws an error if .json file not exists', () => { + fs.unlinkSync(schema); + + let fn = () => { + return new Config(schema); + } + + expect(fn).to.throw('test.json not found.'); + }); + + it('Does not throw an error if .json file exists', () => { + let fn = () => { + return new Config(schema); + } + + expect(fn).to.not.throw(); + }); + + it('Does save a string into config with type string', () => { + let file = new Config(schema); + file.config.var = 'test'; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('string'); + expect(json.var.value).to.be.equal('test'); + }); + + it('Does save a boolean into config with type boolean', () => { + let file = new Config(schema); + file.config.var = true; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('boolean'); + expect(json.var.value).to.be.equal(true); + }); + + it('Does save a number into config with type number', () => { + let file = new Config(schema); + file.config.var = 10; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('number'); + expect(json.var.value).to.be.equal(10); + }); + + it('Does save an array into config with type array', () => { + let file = new Config(schema); + file.config.var = ['test']; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.include('test'); + expect(json.var.enum).to.deep.equal(['test']); + }); + + it('Does save a object into config with type object', () => { + let file = new Config(schema); + file.config.var = { foo: 'bar' }; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(json.var.value).to.deep.equal({ foo: 'bar' }); + }); + + it('Does save a null into config with type null', () => { + let file = new Config(schema); + file.config.var = null; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('null'); + expect(json.var.value).to.be.equal(null); + }); + + it('Does save a number with type number', () => { + let file = new Config(schema); + file.config.var = 100; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('number'); + expect(json.var.value).to.equal(100); + }); + + it('Does update a value as type number', () => { + let file = new Config(schema); + file.config.var = 100; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('number'); + expect(json.var.value).to.equal(100); + + file.config.var = 200; + + file.save(); + + json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('number'); + expect(json.var.value).to.equal(200); + }); + + it('Does throw if try to assign different type to a type number', () => { + let file = new Config(schema); + file.config.var = 100; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('number'); + expect(json.var.value).to.equal(100); + + function test() { + file.config.var = []; + } + + expect(test).to.throw('Cannot assign value of type \'array\' to an property with type \'number\''); + }); + + it('Does save a string with special characters', () => { + let file = new Config(schema); + file.config.var = 'testABC123[]\'()ŠĐČĆŽšđčćž,.-<>!"#$%&'; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('string'); + expect(json.var.value).to.include('\''); + expect(json.var.value).to.include('[]'); + expect(json.var.value).to.equal('testABC123[]\'()ŠĐČĆŽšđčćž,.-<>!"#$%&'); + }); + + it('Does save a string with exact length', () => { + let file = new Config(schema); + file.config.var = 'abcdefghij'; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('string'); + expect(json.var.value).to.have.lengthOf(10); + }); + + it('Does rewrite a string with new value, stays type of string and have a new value', () => { + let file = new Config(schema); + file.config.var = 'test'; + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('string'); + + file.config.var = 'bla'; + file.save(); + json = getContents(); + + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('string'); + expect(json.var.value).to.equal('bla'); + }); + + it('Does throw if want to assign different type to a type of string', () => { + let file = new Config(schema); + file.config.var = 'test'; + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('string'); + expect(json.var.value).to.equal('test'); + + function test() { + file.config.var = []; + } + + expect(test).to.throw('Cannot assign value of type \'array\' to an property with type \'string\'') + }); + + it('Does initialize an empty array via `file.config.var = []`', () => { + let file = new Config(schema); + file.config.var = []; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(0); + }); + + it('Does initialize a new array with property via `file.config.var = [\'test\']`', () => { + let file = new Config(schema); + file.config.var = ['test']; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(1); + expect(json.var.enum).to.deep.includes('test'); + }); + + it('Does initialize a new array with multiple properties via `file.config.var = [\'test1\', \'test2\', \'test3\']`', () => { + let file = new Config(schema); + file.config.var = ['test1', 'test2', 'test3']; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(3); + expect(json.var.enum).to.deep.includes('test1'); + expect(json.var.enum).to.deep.includes('test2'); + expect(json.var.enum).to.deep.includes('test3'); + }); + + it('`push(\'test\')` works on initialized empty array', () => { + let file = new Config(schema); + file.config.var = []; + file.config.var.enum.push('test'); + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(1); + expect(json.var.enum).to.deep.includes('test'); + }); + + it('`push(\'test\')` works on an array with properties', () => { + let file = new Config(schema); + file.config.var = ['bla1', 'bla2', 'bla3']; + file.config.var.enum.push('test'); + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(4); + expect(json.var.enum).to.deep.includes('bla1'); + expect(json.var.enum).to.deep.includes('bla2'); + expect(json.var.enum).to.deep.includes('bla3'); + expect(json.var.enum).to.deep.includes('test'); + }); + + it('`push(\'test\')` creates a new array and add a value `test` to it', () => { + let file = new Config(schema); + file.config.var.push('test'); + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(1); + expect(json.var.enum).to.deep.includes('test'); + }); + + it('Throws an error if want to assign type of string to type of array', () => { + let file = new Config(schema); + file.config.var = []; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('array'); + expect(json.var.enum).to.be.array; + expect(json.var.enum).to.have.lengthOf(0); + + function test() { + file.config.var = 'test'; + } + + expect(test).to.throw('Cannot assign value of type \'string\' to an property with type \'array\''); + }); + + it('Does save an empty object with type object without additional properties other than type', () => { + let file = new Config(schema); + file.config.var = {}; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(Object.keys(json.var)).to.have.lengthOf(1); + }); + + it('Does initializes an object with properties', () => { + let file = new Config(schema); + file.config.var = { foo: 'bar', bar: 'foo' }; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(json.var.value.foo).to.equal('bar'); + expect(json.var.value.bar).to.equal('foo'); + expect(Object.keys(json.var.value)).to.have.lengthOf(2); + }); + + it('Does add an value to existing object', () => { + let file = new Config(schema); + file.config.var = { foo: 'bar', bar: 'foo' }; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(json.var.value.foo).to.equal('bar'); + expect(json.var.value.bar).to.equal('foo'); + expect(Object.keys(json.var.value)).to.have.lengthOf(2); + + file.config.var.value.test = 'bla'; + file.save(); + + json = getContents(); + expect(json.var.value.test).to.be.equal('bla'); + expect(Object.keys(json.var.value)).to.have.lengthOf(3); + }); + + it('Does rewrite an existing property value with new one', () => { + let file = new Config(schema); + file.config.var = { foo: 'bar', bar: 'foo' }; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(json.var.value.foo).to.equal('bar'); + expect(json.var.value.bar).to.equal('foo'); + expect(Object.keys(json.var.value)).to.have.lengthOf(2); + + file.config.var.value.foo = 'test123'; + file.save(); + + json = getContents(); + expect(json.var.value.foo).to.be.equal('test123'); + expect(Object.keys(json.var.value)).to.have.lengthOf(2); + }); + + it('Does delete an existing property', () => { + let file = new Config(schema); + file.config.var = { foo: 'bar', bar: 'foo' }; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(json.var.value.foo).to.equal('bar'); + expect(json.var.value.bar).to.equal('foo'); + expect(Object.keys(json.var.value)).to.have.lengthOf(2); + + delete file.config.var.value.foo; + file.save(); + + json = getContents(); + expect(Object.keys(json.var.value)).to.have.lengthOf(1); + }); + + it('Does throw if try to assign different type to type object', () => { + let file = new Config(schema); + file.config.var = { foo: 'bar', bar: 'foo' }; + + file.save(); + + let json = getContents(); + expect(json.var).to.be.exist; + expect(json.var.type).to.be.equal('object'); + expect(json.var.value.foo).to.equal('bar'); + expect(json.var.value.bar).to.equal('foo'); + expect(Object.keys(json.var.value)).to.have.lengthOf(2); + + function test() { + file.config.var = 100; + } + + expect(test).to.throw('Cannot assign value of type \'number\' to an property with type \'object\''); + }); + +}); diff --git a/tests/runner.js b/tests/runner.js index 4b89bf271ca6..06c9b2a1c335 100644 --- a/tests/runner.js +++ b/tests/runner.js @@ -1,10 +1,40 @@ +/* eslint-disable no-console */ 'use strict'; +const fs = require('fs'); +const ts = require('typescript'); +const old = require.extensions['.ts']; + +require.extensions['.ts'] = function(m, filename) { + if (!filename.match(/angular-cli/) && filename.match(/node_modules/)) { + if (old) { + return old(m, filename); + } + return m._compile(fs.readFileSync(filename), filename); + } + + const source = fs.readFileSync(filename).toString(); + + try { + const result = ts.transpile(source, { + target: ts.ScriptTarget.ES5, + module: ts.ModuleKind.CommonJs + }); + + // Send it to node to execute. + return m._compile(result, filename); + } catch (err) { + console.error('Error while running script "' + filename + '":'); + console.error(err.stack); + throw err; + } +}; + var Mocha = require('mocha'); var glob = require('glob'); -var root = 'tests/{unit,acceptance,e2e}'; -var specFiles = glob.sync(root + '/**/*.spec.js'); +var root = 'tests/{acceptance,models,e2e}'; +var specFiles = glob.sync(root + '/**/*.spec.*'); var mocha = new Mocha({ timeout: 5000, reporter: 'spec' }); specFiles.forEach(mocha.addFile.bind(mocha));