diff --git a/.babelrc b/.babelrc
index 2aec121..9aeec14 100644
--- a/.babelrc
+++ b/.babelrc
@@ -2,8 +2,21 @@
"presets": ["es2015", "stage-2"],
"plugins": [
["module-resolver", {
- "root": ["./src"],
- "alias": { "commands": "./commands" }
+ "root": ["./lib"],
+ "alias": {
+ "bin": "bin",
+ "bundle": "bundle",
+ "clean": "clean",
+ "help": "help",
+ "util": "util",
+ "visitors": "visitors",
+ "visualize": "visualize"
+ }
}]
- ]
+ ],
+ "env": {
+ "test": {
+ "plugins": ["istanbul"]
+ }
+ }
}
diff --git a/.gitignore b/.gitignore
index 6ad53d1..a0d231d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
# Project
-/bin
-lib
+.nyc_output
coverage
node_modules
diff --git a/.istanbul.yml b/.istanbul.yml
index 10e292d..02b328d 100644
--- a/.istanbul.yml
+++ b/.istanbul.yml
@@ -1,5 +1,6 @@
instrumentation:
root: src
+ include-all-sources: true
extensions:
- .es6
- .js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac35c19..efe4ef1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1 @@
## Rinoa v1.0.0 - ROADMAP
-
-##### Features:
-* [ ] Esprima ES6 AST Parsing
- * [ ] Imports / Exports
- * [ ] Annotations
-* [ ] Output formats
- * [ ] UMD (Global, AMD, CommonJS)
- * [ ] AMD
- * [ ] CommonJS
- * [ ] ES
-* [ ] Multiple Bundles
diff --git a/README.md b/README.md
index 377b058..f758e2c 100644
--- a/README.md
+++ b/README.md
@@ -3,31 +3,58 @@
[](https://travis-ci.org/nahuelio/squarebox)
[](https://coveralls.io/github/nahuelio/squarebox)
+[]()
+[](http://www.opensource.org/licenses/mit-license.php)
+[]()
+
+#### Introduction
+
+Experimental ES6/CommonJS/AMD Module Bundler
+
+#### Installation
+
+```npm install [-g] squarebox```
+
+#### Usage
+
+CLI Commands
```
-Badges
+global option: -c (sqbox.js|.sqboxrc|URL) | Default: ./.sqboxrc
+contextual help - sqbox [command] help
+
+* sqbox help - global help (list of commands and general usage)
+* sbox bundle --options
+* sbox clean
+* sbox visualize
```
-#### Introduction
+Programmatic API
```
-TODO
+const sqbox = require('squarebox');
+sqbox.clean([opts])
+ .bundle([config])
+ .visualize([opts]);
```
-#### Installation
+#### Official Documentation
```
TODO
```
-#### Usage
+#### Configuration
+
```
TODO
```
#### Contribute
+
```
TODO
```
-Built with :heart: by [NahuelIO](http://nahuel.io) - [MIT License](http://www.opensource.org/licenses/mit-license.php)
+[](http://nahuel.io)
+by [Nahuel IO](http://nahuel.io)
diff --git a/bin/sqbox b/bin/sqbox
new file mode 100644
index 0000000..4a3f017
--- /dev/null
+++ b/bin/sqbox
@@ -0,0 +1,22 @@
+#!/usr/bin/env node
+
+require('babel-register')({
+ "presets": ["es2015", "stage-2"],
+ "plugins": [
+ ["module-resolver", {
+ "root": ["../"],
+ "cwd": __dirname,
+ "alias": {
+ "bin": "bin",
+ "bundle": "bundle",
+ "clean": "clean",
+ "help": "help",
+ "util": "util",
+ "visitors": "visitors",
+ "visualize": "visualize"
+ }
+ }]
+ ]
+});
+require('babel-polyfill');
+require('commands/bin/sqbox').run(__dirname);
diff --git a/lib/bin/commands.json b/lib/bin/commands.json
new file mode 100644
index 0000000..e4301ca
--- /dev/null
+++ b/lib/bin/commands.json
@@ -0,0 +1,49 @@
+[{
+ "name": "help",
+ "aliases": ["help"],
+ "description": "Help",
+ "path": "help/help",
+ "options": {
+ "clean": {},
+ "bundle": {},
+ "graph": {}
+ }
+}, {
+ "name": "clean",
+ "aliases": ["clean"],
+ "description": "Clean output directory",
+ "path": "clean/clean",
+ "options": {
+ "override": { "default": false },
+ "config": { "default": ".sqboxrc" },
+ "url": {},
+ "target": { "default": "./dist" }
+ }
+}, {
+ "name": "bundle",
+ "aliases": ["bundle"],
+ "description": "Module Bundler",
+ "path": "bundle/bundle",
+ "options": {
+ "override": { "default": false },
+ "config": { "default": ".sqboxrc" },
+ "url": { "alias": "u" },
+ "scan": { "alias": "s", "default": "." },
+ "exclude": { "alias": "x" },
+ "extensions": { "alias": "e", "default": ".js,.jsx,.es6,.es" },
+ "alias": { "alias": "a", "default": {} },
+ "target": { "alias": "t", "default": "dist>umd:./dist" },
+ "logLevel": { "alias": "lv", "default": "output" }
+ }
+}, {
+ "name": "graph",
+ "aliases": ["graph"],
+ "description": "Generates a graphical representation of your bundles",
+ "path": "visualize/graph",
+ "options": {
+ "override": { "default": false },
+ "config": { "default": ".sqboxrc" },
+ "url": {},
+ "target": { "default": "./dist" }
+ }
+}]
diff --git a/lib/bin/sqbox.es6 b/lib/bin/sqbox.es6
new file mode 100644
index 0000000..8624bf0
--- /dev/null
+++ b/lib/bin/sqbox.es6
@@ -0,0 +1,152 @@
+/**
+* @module bin
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import 'util/mixins';
+import extend from 'extend';
+import Collection from 'util/adt/collection';
+import Factory from 'util/factory/factory';
+import Command from 'command';
+import CommandsList from './commands.json';
+import logger from 'util/logger/logger';
+
+let enforcer = Symbol('SquareBox');
+
+/**
+* Class SquareBox
+* @extends {Command}
+*
+* @uses {bin.visitor.Commander}
+**/
+class SquareBox extends Command {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Object} [args = {}] - Constructor arguments
+ * @return {bin.SquareBox}
+ **/
+ constructor(...args) {
+ super(...args);
+ return SquareBox.isPrivate(this.attachEvents());
+ }
+
+ /**
+ * Attaches Events
+ * @public
+ * @return {bin.SquareBox}
+ **/
+ attachEvents() {
+ this.once(SquareBox.events.done, this.after);
+ return this;
+ }
+
+ /**
+ * Before Run
+ * @public
+ * @override
+ * @return {bin.SquareBox}
+ **/
+ before() {
+ super.before();
+ this.commander.read();
+ return this;
+ }
+
+ /**
+ * Run
+ * @public
+ * @override
+ * @return {bin.SquareBox}
+ **/
+ run() {
+ this.before();
+ this.configuration.parse().then(_.bind(this.onConfiguration, this));
+ return this;
+ }
+
+ /**
+ * Configuration Loaded and Parsed Handler
+ * @public
+ * @param {visitors.Configuration} configuration - configuration visitor reference
+ * @return {bin.SquareBox}
+ **/
+ onConfiguration() {
+ const { path } = this.options;
+ //console.log(_.pick(this, Command.options));
+ // TODO: Factory.get(path, this.params()).run();
+ return this;
+ }
+
+ /**
+ * Constructor Validation
+ * @public
+ * @throws {Error} Private violation
+ * @param {Symbol} pte - constructor enforcer
+ * @return {bin.SquareBox}
+ **/
+ isPrivate(pte) {
+ if(!_.isEqual(pte, enforcer)) throw new Error('Private Violation');
+ return this;
+ }
+
+ /**
+ * Register Command Factories
+ * @public
+ * @override
+ * @return {bin.SquareBox}
+ **/
+ register() {
+ super.register();
+ Factory.registerAll(this.constructor.commands);
+ return this;
+ }
+
+ /**
+ * Available Commands
+ * @static
+ * @type {util.adt.Collection}
+ **/
+ static commands = Collection.new(CommandsList);
+
+ /**
+ * SquareBox visitors
+ * @static
+ * @override
+ * @type {Array}
+ **/
+ static visitors = [
+ 'visitors/commander',
+ 'visitors/configuration'
+ ].concat(Command.visitors);
+
+ /**
+ * Static enforcer validation
+ * @static
+ * @param {bin.SquareBox} instance - squarebox instance reference
+ * @return {bin.SquareBox}
+ **/
+ static isPrivate(instance) {
+ return instance.isPrivate(enforcer);
+ }
+
+ /**
+ * Command options
+ * @static
+ * @type {Array}
+ **/
+ static options = Command.options.concat(['options']);
+
+ /**
+ * Static Run
+ * @static
+ * @param {String} [cwd = process.cwd()] - base path
+ * @return {bin.SquareBox}
+ **/
+ static run(cwd = process.cwd()) {
+ return this.new({ cwd }).run();
+ }
+
+}
+
+export default SquareBox;
diff --git a/lib/bundle/bundle.es6 b/lib/bundle/bundle.es6
new file mode 100644
index 0000000..5f228bc
--- /dev/null
+++ b/lib/bundle/bundle.es6
@@ -0,0 +1,29 @@
+/**
+* @module bundle
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Command from 'command';
+
+/**
+* Class Bundle
+* @extends {Command}
+**/
+class Bundle extends Command {
+
+ /**
+ * Run
+ * @public
+ * @override
+ * @return {bundle.Bundle}
+ **/
+ run() {
+ // TODO
+ console.log('Bundle.run()...');
+ return super.run();
+ }
+
+}
+
+export default Bundle;
diff --git a/lib/clean/clean.es6 b/lib/clean/clean.es6
new file mode 100644
index 0000000..0f9eaea
--- /dev/null
+++ b/lib/clean/clean.es6
@@ -0,0 +1,29 @@
+/**
+* @module clean
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Command from 'command';
+
+/**
+* Class Clean
+* @extends {Command}
+**/
+class Clean extends Command {
+
+ /**
+ * Run
+ * @public
+ * @override
+ * @return {clean.Clean}
+ **/
+ run() {
+ // TODO
+ console.log('Clean.run()...');
+ return super.run();
+ }
+
+}
+
+export default Clean;
diff --git a/lib/command.es6 b/lib/command.es6
new file mode 100644
index 0000000..4dedf69
--- /dev/null
+++ b/lib/command.es6
@@ -0,0 +1,245 @@
+/**
+* @module
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { EventEmitter } from 'events';
+import _ from 'util/mixins';
+import extend from 'extend';
+import Factory from 'util/factory/factory';
+import Visited from 'util/visitor/visited';
+
+/**
+* Class Command
+* @extends {util.visitor.Visited}
+*
+* @uses {visitors.formatter.Json}
+* @uses {visitors.async.Asynchronous}
+**/
+class Command extends Visited {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Object} [args = {}] - constructor arguments
+ * @return {Command}
+ **/
+ constructor(args = {}) {
+ super();
+ return this.settings(args).register().acceptAll();
+ }
+
+ /**
+ * Set settings
+ * @see {visitors.Configuration}
+ * @public
+ * @param {Object} [options = {}] - command options
+ * @return {Command}
+ **/
+ settings(options) {
+ return extend(true, this, this.constructor.defaults, _.pick(options, this.constructor.options));
+ }
+
+ /**
+ * Registers Visitors
+ * @public
+ * @return {Command}
+ **/
+ register() {
+ Factory.basePath(this.dirname).registerAll(this.constructor.visitors);
+ return this;
+ }
+
+ /**
+ * Accepts All Visitors
+ * @public
+ * @return {command.Command}
+ **/
+ acceptAll() {
+ return _.reduce(this.constructor.visitors, (memo, v) => memo.accept(Factory.get(v, this)), this);
+ }
+
+ /**
+ * Proxified asynchronous next strategy
+ * FIXME: Implement when the command gets rejected.
+ * @public
+ * @override
+ * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @return {Promise}
+ **/
+ next(adt, resolve, reject) {
+ this.once(Command.events.done, resolve);
+ return this.run();
+ }
+
+ /**
+ * Default hook before run
+ * @public
+ * @return {Command}
+ **/
+ before() {
+ return this.pending();
+ }
+
+ /**
+ * Default Run
+ * @public
+ * @return {Command}
+ **/
+ run() {
+ return this.before().after();
+ }
+
+ /**
+ * Default hook after run
+ * @public
+ * @return {Command}
+ **/
+ after() {
+ return this.done();
+ }
+
+ /**
+ * Command Pending exeuction state
+ * @public
+ * @return {command.Command}
+ **/
+ pending() {
+ this.emit(Command.events.pending, this);
+ return this;
+ }
+
+ /**
+ * Command Done exeuction state
+ * @public
+ * @return {command.Command}
+ **/
+ done() {
+ this.emit(Command.events.done, this);
+ return this;
+ }
+
+ /**
+ * Retrieves source
+ * @public
+ * @return {Object}
+ **/
+ source() {
+ return this.source;
+ }
+
+ /**
+ * Retrieves and resolves scan directory (glob)
+ * @public
+ * @return {String}
+ **/
+ scan() {
+ return this.source().scan;
+ }
+
+ /**
+ * Retrieves and resolves excluded folders
+ * @public
+ * @return {Array}
+ **/
+ exclude() {
+ return this.source().exclude;
+ }
+
+ /**
+ * Retrieves list of extensions to scan
+ * @public
+ * @return {Array}
+ **/
+ extensions() {
+ return this.source().extensions;
+ }
+
+ /**
+ * Retrieves aliases for modules
+ * @public
+ * @return {Object}
+ **/
+ alias() {
+ return this.source().alias;
+ }
+
+ /**
+ * Retrieves target
+ * @public
+ * @return {Object}
+ **/
+ target() {
+ return this.target;
+ }
+
+ /**
+ * Retrieve targets by using a given predicate passed by parameter
+ * @public
+ * @param {Function} predicate - predicate to walk over the targets
+ * @return {Array}
+ **/
+ targets(predicate) {
+ return _.defined(predicate) && _.isFunction(predicate) ? _.map(this.target, predicate, this) : [];
+ }
+
+ /**
+ * Retrieves command options
+ * @public
+ * @return {Object}
+ **/
+ getOptions() {
+ return this.options;
+ }
+
+ /**
+ * Command Visitors
+ * @static
+ * @type {Array}
+ **/
+ static visitors = [
+ 'visitors/formatter/json',
+ 'visitors/async/async'
+ ];
+
+ /**
+ * Command Defaults
+ * @static
+ * @type {Object}
+ **/
+ static defaults = {
+ env: 'development',
+ dirname: __dirname,
+ cwd: process.cwd()
+ };
+
+ /**
+ * Command options
+ * @static
+ * @type {Array}
+ **/
+ static options = [
+ 'env',
+ 'dirname',
+ 'cwd',
+ 'scan',
+ 'exclude',
+ 'extensions',
+ 'alias',
+ 'target'
+ ];
+
+ /**
+ * Command Events
+ * @static
+ * @type {Object}
+ **/
+ static events = {
+ pending: 'commands:command:pending',
+ done: 'commands:command:done'
+ };
+
+}
+
+export default Command;
diff --git a/lib/help/help.es6 b/lib/help/help.es6
new file mode 100644
index 0000000..51b7639
--- /dev/null
+++ b/lib/help/help.es6
@@ -0,0 +1,29 @@
+/**
+* @module help
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Command from 'command';
+
+/**
+* Class Clean
+* @extends {Command}
+**/
+class Clean extends Command {
+
+ /**
+ * Run
+ * @public
+ * @override
+ * @return {help.Clean}
+ **/
+ run() {
+ // TODO
+ console.log('Help.run()...');
+ return super.run();
+ }
+
+}
+
+export default Clean;
diff --git a/src/commands/util/adt/collection.es6 b/lib/util/adt/collection.es6
similarity index 63%
rename from src/commands/util/adt/collection.es6
rename to lib/util/adt/collection.es6
index 992b8e5..1beed2c 100644
--- a/src/commands/util/adt/collection.es6
+++ b/lib/util/adt/collection.es6
@@ -1,42 +1,28 @@
/**
-* @module commands.util.adt
+* @module util.adt
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
import { EventEmitter } from 'events';
import _ from 'underscore';
import extend from 'extend';
-import Iterator from './iterator';
+import Iterator from 'util/adt/iterator';
/**
* Class Collection
* @extends events.EventEmitter
**/
-export default class Collection extends EventEmitter {
-
- /**
- * Internal Array
- * @private
- * @type {Array}
- **/
- _collection = [];
-
- /**
- * Interface for objects
- * @private
- * @type {Function}
- **/
- _interface = null;
+class Collection extends EventEmitter {
/**
* Constructor
* @public
* @param [initial = []] {Array} Initial Array
* @param [opts = {}] {Object} collection options
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
constructor(initial = [], opts = {}) {
super();
- Collection._aggregate(extend(true, this, { _interface: opts.interface }))
+ extend(true, this, _.omit(opts, 'silent', 'interface'), { _collection: [], _interface: opts.interface });
return (initial.length > 0) ? this.set(initial) : this;
}
@@ -57,10 +43,11 @@ export default class Collection extends EventEmitter {
* @param name {String} event name
* @param [opts = {}] {Object} additional options
* @param [...args] {Any} additional arguments to pass to the emit
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
- _fire(name, opts = {}, ...args) {
- return !opts.silent ? this.emit(name, this, ...args) : this;
+ _fire(name, opts, ...args) {
+ if(!opts.silent) this.emit(name, this, ...args);
+ return this;
}
/**
@@ -76,16 +63,28 @@ export default class Collection extends EventEmitter {
return (this.hasInterface() && !_.instanceOf(e, this._interface)) ? w() : wo();
}
+ /**
+ * Returns a json representation of a given element only if this collection has defined an interface
+ * and the given element implements {util.proxy.Json} interface
+ * @private
+ * @param element {Any} element to get json representation
+ * @return {Any}
+ **/
+ _toJSON(element) {
+ return (this.hasInterface() && element.toJSON) ? element.toJSON() : element;
+ }
+
/**
* Default instanciation strategy for new elements added in this collection
* @private
* @param e {Any} element to instanciate
- * @param [opts = {}] {Object} additional options
+ * @param opts {Object} additional options
* @return {Any}
**/
- _new(e, opts = {}) {
- if(!this._valid(e)) return null;
- return this._when(e, () => _.defined(opts.new) ? opts.new(e) : new this._interface(e), () => e);
+ _new(e, opts) {
+ return this._when(e,
+ () => _.defined(opts.new && !this.hasInterface()) ? opts.new(e) : new this._interface(e),
+ () => e);
}
/**
@@ -94,7 +93,7 @@ export default class Collection extends EventEmitter {
* @fires {Collection.events.set}
* @param col {Array} collection of new elements
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
set(col, opts = {}) {
if(!this._valid(col) || !_.isArray(col)) return this;
@@ -112,9 +111,11 @@ export default class Collection extends EventEmitter {
* @return {Any}
**/
add(element, opts = {}) {
- this._collection.push(this._new(element, opts));
- this._fire(Collection.events.add, opts, element);
- return element;
+ if(!this._valid(element)) return null;
+ let added = this._new(element, opts);
+ this._collection.push(added);
+ this._fire(Collection.events.add, opts, added);
+ return added;
}
/**
@@ -123,11 +124,12 @@ export default class Collection extends EventEmitter {
* @fires {Collection.events.addall}
* @param [col = []] {Array} collection of new elements to add
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
addAll(col = [], opts = {}) {
- _.each(col, (e) => this.add(e, extend(true, opts, { silent: true })));
- return this._fire(Collection.events.addall, opts);
+ if(!_.isArray(col) || col.length === 0) return this;
+ let added = _.map(col, (e) => this.add(e, extend(true, {}, opts, { silent: true })));
+ return this._fire(Collection.events.addall, opts, added);
}
/**
@@ -148,7 +150,7 @@ export default class Collection extends EventEmitter {
**/
contains(element) {
if(!this._valid(element)) return false;
- return this.some((e) => _.isEqual(e, element));
+ return this.some((e) => _.isEqual(this._toJSON(e), element));
}
/**
@@ -158,7 +160,7 @@ export default class Collection extends EventEmitter {
* @return {Boolean}
**/
containsAll(elements = []) {
- if(!this._valid(elements)) return false;
+ if(!this._valid(elements) || elements.length === 0) return false;
return _.every(_.map(elements, this.contains, this));
}
@@ -174,18 +176,35 @@ export default class Collection extends EventEmitter {
}
/**
- * Removes an element at the given index. If no index is passed, this method will remove the last element in
+ * Removes an element at the given index. If no index is passed, this method will remove the first element in
* this collection.
* @public
* @fires {Collection.events.remove}
- * @param [ix = (this._collection.size() - 1)] {Number} index used to remove
+ * @param [ix = 0] {Number} index used to remove
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Collection}
+ * @return {Number}
**/
- remove(ix = (this._collection.size() - 1), opts = {}) {
- if(!this._valid(ix) || !_.isNumber(ix) || ix > (this.size() - 1)) return this;
+ removeAt(ix = 0, opts = {}) {
+ if(!this._valid(ix) || !_.isNumber(ix) || ix > (this.size() - 1)) return null;
this._collection.splice(ix, 1);
- return this._fire(Collection.events.remove, opts);
+ this._fire(Collection.events.remove, opts, ix);
+ return ix;
+ }
+
+ /**
+ * Remove a given element
+ * @public
+ * @fires {Collection.events.remove}
+ * @param element {Any} element to remove
+ * @param [opts = {}] {Object} additional options
+ * @return {Number}
+ **/
+ remove(element, opts = {}) {
+ if(!this._valid(element)) return null;
+ let ix = this.findIndex((e) => _.isEqual(this._toJSON(e), this._toJSON(element)));
+ if(ix === -1) return null;
+ this._fire(Collection.events.remove, opts, this._collection.splice(ix, 1));
+ return element;
}
/**
@@ -194,21 +213,29 @@ export default class Collection extends EventEmitter {
* @fires {Collection.events.removeall}
* @param [col = []] {Array} collection of elements to remove
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
removeAll(col = [], opts = {}) {
-
- return this._fire(Collection.events.removeall, opts);
+ if(!_.isArray(col) || col.length === 0) return this;
+ let removed = _.map(col, (e) => this.remove(e, extend(true, {}, opts, { silent: true })));
+ return this._fire(Collection.events.removeall, opts, _.compact(removed));
}
/**
* Removes elements by a given predicate
* @public
+ * @fires {Collection.events.remove}
* @param predicate {Function} predicate used to evaluate
- * @return {commands.util.adt.Collection}
+ * @param [opts = {}] {Object} additional options
+ * @return {util.adt.Collection}
**/
- removeBy() {
- // TODO
+ removeBy(predicate, opts = {}) {
+ if(!this._valid(predicate) || !_.isFunction(predicate)) return this;
+ for(var i = 0, len = this.size(); i < len; i++) {
+ if(predicate(this._collection[i], i, this._collection)) {
+ this.removeAt(i, opts); i--; len--;
+ }
+ }
return this;
}
@@ -219,17 +246,19 @@ export default class Collection extends EventEmitter {
* @fires {Collection.events.sort}
* @param [opts = {}] {Object} additional options
* @param comparator {Function} comparator reference
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
sort(comparator, opts = {}) {
- // TODO
+ (!_.defined(comparator) || !_.isFunction(comparator)) ?
+ this._collection.sort() :
+ this._collection.sort(comparator);
return this._fire(Collection.events.sort, opts);
}
/**
* Returns a new iterator instance of this collection
* @public
- * @return {commands.util.adt.Iterator}
+ * @return {util.adt.Iterator}
**/
iterator() {
return Iterator.new(this._collection);
@@ -240,10 +269,10 @@ export default class Collection extends EventEmitter {
* @public
* @fires {Collection.events.reset}
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
reset(opts = {}) {
- // TODO
+ this._collection = [];
return this._fire(Collection.events.reset, opts);
}
@@ -292,45 +321,45 @@ export default class Collection extends EventEmitter {
/**
* @event set
**/
- set: 'commands:util:adt:collection:set',
+ set: 'util:adt:collection:set',
/**
* @event add
**/
- add: 'commands:util:adt:collection:add',
+ add: 'util:adt:collection:add',
/**
* @event addall
**/
- addall: 'commands:util:adt:collection:addall',
+ addall: 'util:adt:collection:addall',
/**
* @event remove
**/
- remove: 'commands:util:adt:collection:remove',
+ remove: 'util:adt:collection:remove',
/**
* @event removeall
**/
- removeall: 'commands:util:adt:collection:removeall',
+ removeall: 'util:adt:collection:removeall',
/**
* @event reset
**/
- reset: 'commands:util:adt:collection:reset',
+ reset: 'util:adt:collection:reset',
/**
* @event sort
**/
- sort: 'commands:util:adt:collection:sort'
+ sort: 'util:adt:collection:sort'
};
/**
* Underscore interface methods for aggregation
* @static
- * @type Array
+ * @return {Array}
**/
- static UNDERSCORE = [
+ static UNDERSCORE = () => [
'each',
'map',
'findWhere',
@@ -369,27 +398,29 @@ export default class Collection extends EventEmitter {
/**
* Underscore aggregation
* @static
- * @param instance {commands.util.adt.Collection}
- * @return {commands.util.adt.Collection}
+ * @param instance {util.adt.Collection}
+ * @return {util.adt.Collection}
**/
- static _aggregate(instance) {
- _.each(this.UNDERSCORE, function(method) {
- if(_[method] && !_.defined(instance[method])) {
- instance[method] = _.bind(function() {
- return _[method].apply(this, [this._collection].concat(_.toArray(arguments)));
- }, instance);
- }
+ static _aggregate() {
+ _.each(this.UNDERSCORE(), function(method) {
+ if(!_[method] || _.defined(this.prototype[method])) return;
+ this.prototype[method] = function() {
+ return _[method].apply(this, [this._collection].concat(_.toArray(arguments)));
+ };
}, this);
+ return this;
}
/**
* Static constructor
* @static
* @param [...args] {Any} Constructor arguments
- * @return {commands.util.adt.Collection}
+ * @return {util.adt.Collection}
**/
static new(...args) {
return new this(...args);
}
}
+
+export default Collection._aggregate();
diff --git a/src/commands/util/adt/iterator.es6 b/lib/util/adt/iterator.es6
similarity index 86%
rename from src/commands/util/adt/iterator.es6
rename to lib/util/adt/iterator.es6
index 8855ccf..1c73528 100644
--- a/src/commands/util/adt/iterator.es6
+++ b/lib/util/adt/iterator.es6
@@ -1,5 +1,5 @@
/**
-* @module commands.util.adt
+* @module util.adt
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
import { EventEmitter } from 'events';
@@ -9,7 +9,7 @@ import _ from 'underscore';
* Class Iterator
* @extends events.EventEmitter
**/
-export default class Iterator extends EventEmitter {
+class Iterator extends EventEmitter {
/**
* Internal collection
@@ -29,7 +29,7 @@ export default class Iterator extends EventEmitter {
* Constructor
* @public
* @param [initial = []] {Array} initial elements in the collection
- * @return {commands.util.adt.Iterator}
+ * @return {util.adt.Iterator}
**/
constructor(initial = []) {
super();
@@ -53,7 +53,7 @@ export default class Iterator extends EventEmitter {
* @public
* @param [col = []] {Array} elements to set to this iterator
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Iterator}
+ * @return {util.adt.Iterator}
**/
set(col = [], opts = {}) {
if(!this._valid(col) || col.length === 0) return this;
@@ -82,7 +82,7 @@ export default class Iterator extends EventEmitter {
/**
* Reset the cursor to the beginning to the index 0
* @public
- * @return {commands.util.adt.Iterator}
+ * @return {util.adt.Iterator}
**/
rewind() {
this._pointer = 0;
@@ -94,7 +94,7 @@ export default class Iterator extends EventEmitter {
* @public
* @fires {Iterator.events.remove}
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Iterator}
+ * @return {util.adt.Iterator}
**/
remove(opts = {}) {
if(this._collection.length === 0) return this;
@@ -111,22 +111,24 @@ export default class Iterator extends EventEmitter {
/**
* @event set
**/
- set: 'commands:util:adt:iterator:set',
+ set: 'util:adt:iterator:set',
/**
* @event remove
**/
- remove: 'commands:util:adt:iterator:remove'
+ remove: 'util:adt:iterator:remove'
}
/**
* Static Constructor
* @static
* @param [...args] {Any} constructor arguments
- * @return {commands.util.adt.Iterator}
+ * @return {util.adt.Iterator}
**/
static new(...args) {
return new this(...args);
}
}
+
+export default Iterator;
diff --git a/lib/util/adt/queue-async.es6 b/lib/util/adt/queue-async.es6
new file mode 100644
index 0000000..f485e3c
--- /dev/null
+++ b/lib/util/adt/queue-async.es6
@@ -0,0 +1,135 @@
+/**
+* @module util.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Queue from 'util/adt/queue';
+import Asynchronous from 'visitors/async/async';
+
+/**
+* Class QueueAsync
+* Defines the interface of an asynchronous FIFO Queue (FirstIn-FirstOut).
+* Interface for objects on this queue, **must** implement method `next` of
+* {@link visitors.async.Asynchronous} for promise resolution.
+* @example
+*
Usage
+*
+* let myqueue = QueueAsync.new([1,2,3], { capacity: 3, interface: [Class] })
+* .on(QueueAsync.events.next, (element) => console.log('Next: ', element))
+* .on(QueueAsync.events.end, (results) => console.log('End: ', results))
+* const mypromise = p.poll(); // asynchronous
+* @extends util.adt.Queue
+**/
+class QueueAsync extends Queue {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param {Array} [initial = []] - initial elements
+ * @param {Object} [opts = {}] - collection options
+ * @return {util.adt.QueueAsync}
+ **/
+ constructor(initial = [], opts = {}) {
+ return super(initial, extend(true, opts, { _visitor: Asynchronous.new(), _last: [] }));
+ }
+
+ /**
+ * Default instanciation strategy for new elements added in this collection
+ * @private
+ * @override
+ * @param {Any} e - element to instanciate
+ * @param {Object} opts - additional options
+ * @return {Any}
+ **/
+ _new(e, opts) {
+ let element = super._new(e, opts);
+ this._visitor.validate(element);
+ return element.accept(this._visitor);
+ }
+
+ /**
+ * Resets Last Results
+ * @public
+ * @return {util.adt.QueueAsync}
+ **/
+ _resetLast() {
+ this._last = [];
+ return this;
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, or returns null if this queue is empty
+ * @public
+ * @async
+ * @override
+ * @param {Object} [opts = {}] - additional options
+ * @param {Boolean} [next = false] - async queue already started
+ * @return {Promise}
+ **/
+ async poll(opts = {}, next = false) {
+ if(!next) this._resetLast();
+ const res = await this.next(opts);
+ return this.onNext(res, opts);
+ }
+
+ /**
+ * Asynchronous Queue next
+ * @public
+ * @emits {QueueAsync.events.next} - when opts.silent is false or undefined
+ * @param {Object} [opts] - additional options
+ * @return {Promise}
+ **/
+ next(opts) {
+ let element = super.poll(opts);
+ if(!opts.silent) this.emit(QueueAsync.events.next, element);
+ return element.execute(this);
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, or returns null if this queue is empty
+ * @public
+ * @param {Promise} res - current promise (resolved or rejected)
+ * @param {Object} [opts] - additional options
+ * @return {Any}
+ **/
+ onNext(res, opts) {
+ this._last.push(res);
+ return this.isEmpty() ? this.end(opts) : this.poll(opts, true);
+ }
+
+ /**
+ * Asynchronous Queue end will return last results.
+ * @public
+ * @emits {QueueAsync.events.end} - when opts.silent is false or undefined
+ * @param {Object} [opts = {}] - additional options
+ * @return {Array}
+ **/
+ end(opts) {
+ if(!opts.silent) this.emit(QueueAsync.events.end, this._last);
+ return this._last;
+ }
+
+ /**
+ * Queue Events
+ * @static
+ * @type {Object}
+ **/
+ static events = extend(false, {}, Queue.events, {
+
+ /**
+ * @event next
+ **/
+ next: 'util:adt:queue-async:next',
+
+ /**
+ * @event end
+ **/
+ end: 'util:adt:queue-async:end'
+
+ });
+
+}
+
+export default QueueAsync;
diff --git a/src/commands/util/adt/queue.es6 b/lib/util/adt/queue.es6
similarity index 51%
rename from src/commands/util/adt/queue.es6
rename to lib/util/adt/queue.es6
index 89fccca..2a785f8 100644
--- a/src/commands/util/adt/queue.es6
+++ b/lib/util/adt/queue.es6
@@ -1,11 +1,11 @@
/**
-* @module commands.util.adt
+* @module util.adt
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
import _ from 'underscore';
import extend from 'extend';
-import Collection from 'commands/util/adt/collection';
-import QueueException from 'commands/util/exception/adt/queue';
+import Collection from 'util/adt/collection';
+import QueueException from 'util/exception/adt/queue';
/**
* Class Queue
@@ -15,67 +15,63 @@ import QueueException from 'commands/util/exception/adt/queue';
*
* let myqueue = new Queue([1,2,3], { capacity: 4 }); // initial is set to capacity was set to 4
* myqueue.offer(4); // Adds one more element without violating capacity (3)
-* myqueue.poll();
+* myqueue.peek();
*
* let myqueue = new Queue([{ name: 'one' }, { name: 'two' }], { capacity: 3, interface: MyClass });
* myqueue.offer({ name: 3 }); // Adds one more element without violating capacity (3)
* myqueue.poll();
-* @extends commands.util.adt.Collection
+* @extends util.adt.Collection
**/
-export default class Queue extends Collection {
-
- /**
- * Queue capacity
- * @public
- * @type {Number}
- **/
- capacity = 0
+class Queue extends Collection {
/**
* Constructor
* @public
* @override
- * @param [intial = []] {Array} initial collection of elements
- * @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Queue}
+ * @param [initial = []] {Array} Initial Array
+ * @param [opts = {}] {Object} collection options
+ * @return {util.adt.Queue}
**/
constructor(initial = [], opts = {}) {
- super(initial, opts);
- return extend(true, this, { capacity: opts.capacity });
+ const { capacity } = opts;
+ return super(initial, extend(true, opts, _.defined(capacity) ? { capacity } : { capacity: 0 }));
}
/**
- * Validates capacity of the queue to either decide, to add or not the element on this queue
- * @TODO: Keep an eye on the order of validations while unit testing...
+ * Validates element or array of elements to decide either, to add or not the elements on this queue
* @private
* @override
- * @throws {commands.util.exceptions.QueueException}
+ * @throws {util.exceptions.QueueException}
* @param element {Any} element to validate
- * @param opts {Object} additional options
* @return {Boolean}
**/
- _valid(element, opts) {
- if(this.size() >= this.capacity) return false;
- if(!_.defined(opts.capacity))
- throw QueueException.new({ type: 'capacityUndefined', level: QueueException.fatal });
- if(_.isArray(element) && element.length > this.capacity)
- throw QueueException.new({ type: 'capacityViolation', level: QueueException.fatal },
- { capacity: this.capacity });
+ _valid(element) {
+ const { capacity } = this;
+ if(_.isArray(element) && (element.length > capacity))
+ throw QueueException.new('capacityViolation', { capacity });
return super._valid(element);
}
+ /**
+ * Returns true if the current size has reached the capacity of the queye, false otherwise
+ * @private
+ * @return {Boolean}
+ **/
+ _validCapacity() {
+ return (this.size() < this.capacity);
+ }
+
/**
* Set initial collection of elements on this queue
* @public
* @override
* @param [col = []] {Array} collection of elements
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Queue}
+ * @return {util.adt.Queue}
**/
set(col = [], opts = {}) {
- if(!this._valid(col, opts)) return this;
- this.capacity = opts.capacity;
- return super.set(col);
+ if(!this._validCapacity() || !this._valid(col) || !_.isArray(col) || col.length === 0) return this;
+ return super.set(col, opts);
}
/**
@@ -88,9 +84,8 @@ export default class Queue extends Collection {
* @return {Boolean}
**/
offer(element, opts = {}) {
- if(!this._valid(element, opts)) return false;
- this.add(element, extend(true, opts, { silent true }))
- ._fire(Queue.events.offer, this, element);
+ if(!this._validCapacity() || !this._valid(element)) return false;
+ this._fire(Queue.events.offer, opts, this.add(element, extend(true, {}, opts, { silent: true })));
return true;
}
@@ -106,10 +101,14 @@ export default class Queue extends Collection {
/**
* Retrieves and removes the head of this queue, or returns null if this queue is empty
* @public
+ * @param [opts = {}] {Object} additional options
* @return {Object}
**/
- poll() {
- return (this.size() > 0) ? this.remove(0, { silent: true })._fire(Queue.events.poll) : null;
+ poll(opts = {}) {
+ if(this.size() === 0) return null;
+ let polled = this.remove(this.get(0), { silent: true });
+ this._fire(Queue.events.poll, opts, polled);
+ return polled;
}
/**
@@ -117,26 +116,18 @@ export default class Queue extends Collection {
* @static
* @type {Object}
**/
- static events = extend(false, Collection.events, {
+ static events = extend(false, {}, Collection.events, {
/**
* @event offer
**/
- offer: 'commands:util:adt:queue:offer',
+ offer: 'util:adt:queue:offer',
/**
* @event poll
**/
- poll: 'commands:util:adt:queue:poll'
- })
-
- /**
- * Static constructor
- * @static
- * @param [...args] {Any} Constructor arguments
- * @return {commands.util.adt.Queue}
- **/
- static new(...args) {
- return new this(...args);
- }
+ poll: 'util:adt:queue:poll'
+ });
}
+
+export default Queue;
diff --git a/lib/util/adt/stack-async.es6 b/lib/util/adt/stack-async.es6
new file mode 100644
index 0000000..bc69f46
--- /dev/null
+++ b/lib/util/adt/stack-async.es6
@@ -0,0 +1,135 @@
+/**
+* @module util.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Stack from 'util/adt/stack';
+import Asynchronous from 'visitors/async/async';
+
+/**
+* Class StackAsync
+* Defines the interface of a Stack LIFO (LastIn-FirstOut)
+* Interface for objects on this stack, must implement method `next` of {@link util.proxy.Asynchronous}
+* for promise resolution.
+* @example
+* Usage
+*
+* let mystack = StackAsync.new([1,2,3], { interface: [Class] })
+* .on(StackAsync.events.next, (element) => console.log('Next: ', element))
+* .on(StackAsync.events.end, (results) => console.log('End: ', results))
+* const mypromise = mystack.pop(); // asynchronous
+* @extends util.adt.Stack
+**/
+class StackAsync extends Stack {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param [initial = []] {Array} Initial Array
+ * @param [opts = {}] {Object} collection options
+ * @return {util.adt.StackAsync}
+ **/
+ constructor(initial = [], opts = {}) {
+ return super(initial, extend(true, opts, { _visitor: Asynchronous.new(), _last: [] }));
+ }
+
+ /**
+ * Default instanciation strategy for new elements added in this collection
+ * @private
+ * @override
+ * @param e {Any} element to instanciate
+ * @param opts {Object} additional options
+ * @return {Any}
+ **/
+ _new(e, opts) {
+ let element = super._new(e, opts);
+ this._visitor.validate(element);
+ return element.accept(this._visitor);
+ }
+
+ /**
+ * Resets Last Results
+ * @public
+ * @return {util.adt.StackAsync}
+ **/
+ _resetLast() {
+ this._last = [];
+ return this;
+ }
+
+ /**
+ * Retrieves and removes the head of this stack, or returns null if this stack is empty
+ * @public
+ * @async
+ * @override
+ * @param [opts = {}] {Object} additional options
+ * @param {Boolean} [next = false] - async queue already started
+ * @return {Promise}
+ **/
+ async pop(opts = {}, next = false) {
+ if(!next) this._resetLast();
+ const res = await this.next(opts);
+ return this.onNext(res, opts);
+ }
+
+ /**
+ * Asynchronous Queue next
+ * @public
+ * @emits {StackAsync.events.next} - when opts.silent = false or undefined
+ * @param [opts] {Object} additional options
+ * @return {Promise}
+ **/
+ next(opts) {
+ let element = super.pop(opts);
+ if(!opts.silent) this.emit(StackAsync.events.next, element);
+ return element.execute(this);
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, or returns null if this queue is empty
+ * @public
+ * @param res {Promise} promise reference with resolution (resolved or rejected)
+ * @param [opts] {Object} additional options
+ * @return {Promise}
+ **/
+ onNext(res, opts) {
+ this._last.push(res);
+ return this.isEmpty() ? this.end(opts) : this.pop(opts, true);
+ }
+
+ /**
+ * Asynchronous Queue end
+ * @public
+ * @emits {StackAsync.events.end} - when opts.silent is false or undefined
+ * @param [opts = {}] {Object} additional options
+ * @return {util.adt.StackAsync}
+ **/
+ end(opts) {
+ if(!opts.silent) this.emit(StackAsync.events.end, this._last);
+ return this._last;
+ }
+
+ /**
+ * Queue Events
+ * @static
+ * @type {Object}
+ **/
+ static events = extend(false, {}, Stack.events, {
+
+ /**
+ * @event next
+ **/
+ next: 'util:adt:stack-async:next',
+
+ /**
+ * @event end
+ **/
+ end: 'util:adt:stack-async:end'
+
+ });
+
+}
+
+export default StackAsync;
diff --git a/src/commands/util/adt/stack.es6 b/lib/util/adt/stack.es6
similarity index 67%
rename from src/commands/util/adt/stack.es6
rename to lib/util/adt/stack.es6
index 97f7484..6267d04 100644
--- a/src/commands/util/adt/stack.es6
+++ b/lib/util/adt/stack.es6
@@ -1,10 +1,12 @@
/**
-* @module commands.util.adt.Stack
+* @module util.adt.Stack
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
+'use strict';
+
import _ from 'underscore';
import extend from 'extend';
-import Collection from 'commands/util/adt/collection';
+import Collection from 'util/adt/collection';
/**
* Class Stack
@@ -18,20 +20,21 @@ import Collection from 'commands/util/adt/collection';
*
* let mystack = new Stack([{ name: 'one' }, { name: 'two' }], { interface: MyClass });
* mystack.push({ name: 3 }); // Adds one more element into the stack
-* mystack.pop(); // Outputs { name: 'one' } of MyClass, removing the element
-* @extends commands.util.adt.Collection
+* mystack.pop(); // Outputs { name: 3 } of MyClass, removing the element
+* @extends util.adt.Collection
**/
-export default class Stack extends Collection {
+class Stack extends Collection {
/**
* Constructor
* @public
* @param [initial = []] {Array} initial collection of elements on this stack
* @param [opts = {}] {Object} additional options
- * @return {commands.util.adt.Stack}
+ * @return {util.adt.Stack}
**/
constructor(initial = [], opts = {}) {
- return super(initial, opts);
+ super(initial, opts);
+ return this;
}
/**
@@ -59,13 +62,14 @@ export default class Stack extends Collection {
/**
* Retrieves and removes the head of this stack, or returns null if this stack is empty
* @public
- * @return {Object}
+ * @param [opts = {}] {Object} additional options
+ * @return {Any}
**/
- pop() {
+ pop(opts = {}) {
if(this.size() <= 0) return null;
- let removed = this.remove(0, { silent: true });
- this._fire(Stack.events.pop, {}, removed);
- return removed;
+ let popped = this.remove(this.get(this.size() - 1), { silent: true });
+ this._fire(Stack.events.pop, opts, popped);
+ return popped;
}
/**
@@ -76,7 +80,7 @@ export default class Stack extends Collection {
**/
search(element) {
if(!this._valid(element)) return -1;
- return this.findIndex((e) => _.isEqual(((this.hasInterface() && _.defined(e.toJSON)) ? e.toJSON() : e), element));
+ return this.findIndex((e) => _.isEqual(this._toJSON(e), this._toJSON(element)));
}
/**
@@ -84,26 +88,18 @@ export default class Stack extends Collection {
* @static
* @type {Object}
**/
- static events = extend(false, Collection.events, {
+ static events = extend(false, {}, Collection.events, {
/**
* @event push
**/
- push: 'commands:util:adt:stack:push',
+ push: 'util:adt:stack:push',
/**
* @event pop
**/
- pop: 'commands:util:adt:stack:pop'
+ pop: 'util:adt:stack:pop'
});
- /**
- * Static constructor
- * @static
- * @param [...args] {Any} Constructor arguments
- * @return {commands.util.adt.Stack}
- **/
- static new(...args) {
- return new this(...args);
- }
-
}
+
+export default Stack;
diff --git a/lib/util/exception/adt/queue.es6 b/lib/util/exception/adt/queue.es6
new file mode 100644
index 0000000..29e7364
--- /dev/null
+++ b/lib/util/exception/adt/queue.es6
@@ -0,0 +1,39 @@
+/**
+* @module util.exception.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Exception from 'util/exception/exception';
+
+/**
+* Class QueueException
+* @extends {util.exception.Exception}
+**/
+class QueueException extends Exception {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param [...args] {Any} constructor attribute
+ * @return {util.exception.adt.QueueException}
+ **/
+ constructor(...args) {
+ super(...args);
+ this.name = 'QueueException';
+ return this;
+ }
+
+ /**
+ * Command Exception types
+ * @public
+ * @type {Object}
+ **/
+ static type = extend(true, {}, Exception.type, {
+ capacityViolation: _.template(`Queue element's collection passed overflows the current capacity <%= capacity %>`)
+ });
+
+}
+
+export default QueueException;
diff --git a/lib/util/exception/exception.es6 b/lib/util/exception/exception.es6
new file mode 100644
index 0000000..6ce4a6d
--- /dev/null
+++ b/lib/util/exception/exception.es6
@@ -0,0 +1,52 @@
+/**
+* @module util.exception
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Logger from 'util/logger/logger';
+
+/**
+* Class Exception
+* @extends {Error}
+**/
+class Exception extends Error {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param message {Function} message template function
+ * @param [...args] {Any} constructor attribute
+ * @return {util.exception.Exception}
+ **/
+ constructor(message, ...args) {
+ super(message);
+ this.name = 'Exception';
+ Error.captureStackTrace(this, this.constructor);
+ return this;
+ }
+
+ /**
+ * Command Exception types
+ * @public
+ * @type {Object}
+ **/
+ static type = {
+ unknown: _.template('Unknown Exception')
+ };
+
+ /**
+ * Static Constructor
+ * @static
+ * @param [type = 'unknown']
+ * @param [...args] {Any} additional arguments
+ * @return {util.exception.Exception}
+ **/
+ static new(type, ...args) {
+ return new this(this.type[type](...args), ...args);
+ }
+
+}
+
+export default Exception;
diff --git a/lib/util/exception/proxy/interface.es6 b/lib/util/exception/proxy/interface.es6
new file mode 100644
index 0000000..e2cd882
--- /dev/null
+++ b/lib/util/exception/proxy/interface.es6
@@ -0,0 +1,40 @@
+/**
+* @module util.exception.proxy
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Exception from 'util/exception/exception';
+
+/**
+* Class InterfaceException
+* @extends {util.exception.Exception}
+**/
+class InterfaceException extends Exception {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param [...args] {Any} constructor attribute
+ * @return {util.exception.proxy.InterfaceException}
+ **/
+ constructor(...args) {
+ super(...args);
+ this.name = 'InterfaceException';
+ return this;
+ }
+
+ /**
+ * Command Exception types
+ * @public
+ * @type {Object}
+ **/
+ static type = extend(true, {}, Exception.type, {
+ proxy: _.template(`Proxies require a 'target' in which their interface operates on`),
+ interface: _.template(`Instance is required to implement [<%= name %>]`)
+ });
+
+}
+
+export default InterfaceException;
diff --git a/lib/util/factory/factory.es6 b/lib/util/factory/factory.es6
new file mode 100644
index 0000000..5660203
--- /dev/null
+++ b/lib/util/factory/factory.es6
@@ -0,0 +1,199 @@
+/**
+* @module util.factory
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'util/mixins';
+import fs from 'fs-extra';
+import path, { resolve } from 'path';
+import extend from 'extend';
+import { EventEmitter } from 'events';
+import logger from 'util/logger/logger';
+
+/**
+* Class Factory
+* @extends {events.EventEmitter}
+**/
+class Factory extends EventEmitter {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Object} [attrs = {}] - constructor attributes
+ * @return {util.factory.Factory}
+ **/
+ constructor(attrs = {}) {
+ super();
+ return extend(true, this, attrs, { _factories: new Map() }).basePath();
+ }
+
+ /**
+ * Resets factories
+ * @public
+ * @return {util.factoryFactory}
+ **/
+ reset() {
+ this._factories.clear();
+ return this;
+ }
+
+ /**
+ * Sets a base path
+ * @public
+ * @param {String} [ph = Factory.basePath] - base path to resolve factories filepaths
+ * @return {util.factory.Factory}
+ **/
+ basePath(ph = Factory.basePath) {
+ this.path = ph;
+ return this;
+ }
+
+ /**
+ * Resolves path to factory class file
+ * @private
+ * @param {String} ph - path to resolve
+ * @return {String}
+ **/
+ _resolve(ph) {
+ return resolve(this.path, ph);
+ }
+
+ /**
+ * Validates factory path to file
+ * @private
+ * @param {String} ph - path to registered factory
+ * @return {Boolean}
+ **/
+ _validate(ph) {
+ try {
+ return (_.defined(ph) && _.isString(ph) && ph.length > 0 && _.defined(require.resolve(this._resolve(ph))));
+ } catch(ex) {
+ logger(`${this.toString()} > [ERROR-CODE] ${ex.code}`).out({}, logger.white);
+ logger(ex.message).warn(logger);
+ return false;
+ }
+ }
+
+ /**
+ * Resolves which factory class constructor to use for instanciation
+ * If a static new method is detected on the factory, this will be used.
+ * If no static new method is detected and the constructor is a function, operator new will be used.
+ * Finally, if the factory is not a Function, this method will return it verbatim (no arguments will be passed).
+ * @private
+ * @param {Any} o - factory object
+ * @param {Any} [...args] - constructor factory arguments
+ * @return {Any}
+ **/
+ _new(o, ...args) {
+ if(_.isFunction(o)) return _.defined(o.new) ? o.new(...args) : new o(...args);
+ return o;
+ }
+
+ /**
+ * Perform a look up and retrieves the first factory matching the path. Returns null otherwise
+ * @public
+ * @param {String} [ph = ''] - path to registered factory
+ * @return {Any}
+ **/
+ find(ph = '') {
+ for(let [k, v] of this._factories.entries())
+ if(k === ph) return v;
+ return null;
+ }
+
+ /**
+ * Returns true if the given path was registered previously, false otherwise
+ * @public
+ * @param {String} ph - path to registered factory
+ * @return {Boolean}
+ **/
+ exists(ph) {
+ if(!_.defined(ph)) return false;
+ return this._factories.has(ph);
+ }
+
+ /**
+ * Registers a single factory object with a given path, if it haven't been registered before.
+ * @public
+ * @param {String} ph - factory path location
+ * @return {util.factory.Factory}
+ **/
+ register(ph) {
+ if(this._validate(ph) && !this.exists(ph)) this._factories.set(ph, this._resolve(ph));
+ return this;
+ }
+
+ /**
+ * Registers a list of factory objects with a given path list, if they haven't been registered before.
+ * @public
+ * @param {Array} [paths = []] - factory path locations
+ * @return {util.factory.Factory}
+ **/
+ registerAll(paths = []) {
+ _.each(paths, this.register, this);
+ return this;
+ }
+
+ /**
+ * Unregisters a single factory object with a given path, if it have been registered before.
+ * @public
+ * @param {String} ph - factory path location
+ * @return {util.factory.Factory}
+ **/
+ unregister(ph) {
+ if(this.exists(ph)) this._factories.delete(ph);
+ return this;
+ }
+
+ /**
+ * Unregisters a list of factory objects with a given path list, if they have been registered before.
+ * @public
+ * @param {Array} [paths = []] - factory path locations
+ * @return {util.factory.Factory}
+ **/
+ unregisterAll(paths = []) {
+ _.each(paths, this.unregister, this);
+ return this;
+ }
+
+ /**
+ * Obtain a new instance of the factory, by optionally passing arguments to the constructor.
+ * If the factory is not found, returns null.
+ * @public
+ * @param {String} ph - registred factory path
+ * @param {Any} [...args] - factory constructor arguments
+ * @return {Any}
+ **/
+ get(ph, ...args) {
+ return this.exists(ph) ? this._new(require(this.find(ph)).default, ...args) : null;
+ }
+
+ /**
+ * String representation of this class
+ * @public
+ * @override
+ * @return {String}
+ **/
+ toString() {
+ return this.constructor.name;
+ }
+
+ /**
+ * Factory Defaults
+ * @static
+ * @type {Object}
+ **/
+ static basePath = process.cwd();
+
+ /**
+ * Static Constructor
+ * @static
+ * @param {Any} [...args] - constructor arguments
+ * @return {util.factory.Factory}
+ **/
+ static new(...args) {
+ return new this(...args);
+ }
+
+}
+
+export default Factory.new();
diff --git a/lib/util/logger/logger.es6 b/lib/util/logger/logger.es6
new file mode 100644
index 0000000..f48af1e
--- /dev/null
+++ b/lib/util/logger/logger.es6
@@ -0,0 +1,326 @@
+/**
+* @module util.logger
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { EventEmitter } from 'events';
+import _ from 'underscore';
+import _s from 'underscore.string';
+import extend from 'extend';
+import chalk from 'chalk';
+
+/**
+* Class Logger
+* @extends events.EventEmitter
+**/
+export class Logger extends EventEmitter {
+
+ /**
+ * Constructor
+ * @public
+ * @return {util.logger.Logger}
+ **/
+ constructor() {
+ super();
+ this.level();
+ return extend(true, this, { _buffer: [] });
+ }
+
+ /**
+ * Proxy's 'getPrototypeOf' trap override
+ * @public
+ * @param {Any} target - constructor reference
+ * @return {Object}
+ **/
+ getPrototypeOf(target) {
+ return this.constructor.prototype;
+ }
+
+ /**
+ * Proxy's 'get' trap override
+ * @public
+ * @param {util.logger.Logger} target - logger instance reference
+ * @param {String} property - property to resolve
+ * @return {util.logger.Logger}
+ **/
+ get(target, property) {
+ if(_.defined(this[property])) return this[property];
+ if(_.defined(chalk[property])) return chalk[property];
+ return null;
+ }
+
+ /**
+ * Proxy's 'set' trap override
+ * @public
+ * @param {util.logger.Logger} target - logger instance reference
+ * @param {String} property - property to resolve
+ * @param {Any} value - value to set
+ * @return {Boolean}
+ **/
+ set(target, property, value) {
+ this[property] = value;
+ return true;
+ }
+
+ /**
+ * Proxy's 'defineProperty' trap override
+ * @public
+ * @param {util.logger.Logger} target - logger instance reference
+ * @param {String} property - property to define
+ * @return {Boolean}
+ **/
+ defineProperty(target, property) {
+ this[property] = null;
+ return true;
+ }
+
+ /**
+ * Proxy's 'deleteProperty' trap override
+ * @public
+ * @param {util.logger.Logger} target - logger instance reference
+ * @param {String} property - property to delete
+ * @return {Boolean}
+ **/
+ deleteProperty(target, property) {
+ delete this[property];
+ return true;
+ }
+
+ /**
+ * Proxy's 'apply' trap override
+ * @private
+ * @param {util.logger.Logger} target - logger instance reference
+ * @param {Any} thisArg - this context reference
+ * @param {Array} args - arguments list
+ * @return
+ **/
+ apply(target, thisArg, args) {
+ this._add(...args);
+ return Logger.ref;
+ }
+
+ /**
+ * Validates level of output provided
+ * Validation Rules:
+ * - If level equals `silent` -> will output only fatal logs
+ * - If level equals `output` -> will output warn, fatal and output (except debug).
+ * - If level equals `debug` -> will print all logs (warn, fatal, output and debug).
+ * @public
+ * @param {Object} type - log's type
+ * @return {Boolean}
+ **/
+ _validate(type) {
+ const { fatal, debug } = Logger.type;
+ if(this._level === Logger.level.silent && _.isEqual(type.name, fatal.name)) return true;
+ if(this._level === Logger.level.output && !_.isEqual(type.name, debug.name)) return true;
+ if(this._level === Logger.level.debug) return true;
+ return false;
+ }
+
+ /**
+ * Appends Message to the internal buffer
+ * @public
+ * @param {String} message - message to add
+ * @return {util.logger.Logger}
+ **/
+ _add(message) {
+ this._buffer.push(message);
+ return this;
+ }
+
+ /**
+ * Flushes message buffer
+ * @private
+ * @emits {Logger.events.flush}
+ * @return {util.logger.Logger}
+ **/
+ _flush() {
+ this._buffer = [];
+ return this;
+ }
+
+ /**
+ * Fires Event
+ * @public
+ * @param {String} name - event name
+ * @param opts {Object} - additional options
+ * @return {util.logger.Logger}
+ **/
+ _fire(name, opts) {
+ if(!opts.silent) this.emit(name, this);
+ return this;
+ }
+
+ /**
+ * Outputs message to the stdout
+ * @private
+ * @param {Function} style - chalk style override
+ * @param {Object} type - Logger type
+ * @return {util.logger.Logger}
+ **/
+ _output(style, type) {
+ if(!this._validate(type)) return this._flush();
+ let format = _.defined(style) ? style : type.style;
+ return this._stdout(format(this._buffer.join('\n')))._flush();
+ }
+
+ /**
+ * Standard Out (console.log wrapper)
+ * @private
+ * @param {String} content - content to output in stdout
+ * @return {util.logger.Logger}
+ **/
+ _stdout(content) {
+ console.warn(content);
+ return this;
+ }
+
+ /**
+ * Output message
+ * @public
+ * @emits {Logger.events.debug}
+ * @param {Object} [opts = {}] - additional options
+ * @param {Object} style - chalk optional style
+ * @return {util.logger.Logger}
+ **/
+ out(opts = {}, style) {
+ return this._output(style, Logger.type.output)._fire(Logger.events.output, opts);
+ }
+
+ /**
+ * Debug message
+ * @public
+ * @emits {Logger.events.debug}
+ * @param {Object} [opts = {}] - additional options
+ * @param {Object} style - chalk optional style
+ * @return {util.logger.Logger}
+ **/
+ debug(opts = {}, style) {
+ return this._output(style, Logger.type.debug)._fire(Logger.events.debug, opts);
+ }
+
+ /**
+ * Warning message
+ * @public
+ * @emits {Logger.events.warning}
+ * @param {Object} [opts = {}] - additional options
+ * @return {util.logger.Logger}
+ **/
+ warn(opts = {}) {
+ return this._output(null, Logger.type.warning)._fire(Logger.events.warning, opts);
+ }
+
+ /**
+ * Fatal message
+ * @public
+ * @emits {Logger.events.fatal}
+ * @param {Object} [opts = {}] - additional options
+ **/
+ fatal(opts = {}) {
+ this._output(null, Logger.type.fatal)._fire(Logger.events.fatal, opts);
+ process.exit(1);
+ }
+
+ /**
+ * Sets Logger Level
+ * @public
+ * @param {String} [level = Logger.level.output] - log's level
+ * @return {Function}
+ **/
+ level(lvl = Logger.level.output) {
+ this._level = lvl;
+ return this;
+ }
+
+ /**
+ * Logger Types
+ * @static
+ * @type {Object}
+ **/
+ static type = {
+ debug: {
+ name: 'debug',
+ style: chalk.white
+ },
+ output: {
+ name: 'output',
+ style: chalk.green
+ },
+ warning: {
+ name: 'warning',
+ style: chalk.yellow
+ },
+ fatal: {
+ name: 'fatal',
+ style: chalk.red
+ }
+ }
+
+ /**
+ * Logger Levels
+ * @static
+ * @type {Object}
+ **/
+ static level = {
+ output: 'logger:output', // Default
+ debug: 'logger:debug',
+ silent: 'logger:silent'
+ }
+
+ /**
+ * Logger Events
+ * @static
+ * @type {Object}
+ **/
+ static events = {
+
+ /**
+ * @event flush
+ **/
+ flush: 'util:logger:flush',
+
+ /**
+ * @event debug
+ **/
+ debug: 'util:logger:debug',
+
+ /**
+ * @event output
+ **/
+ output: 'util:logger:output',
+
+ /**
+ * @event warning
+ **/
+ warning: 'util:logger:warning',
+
+ /**
+ * @event fatal
+ **/
+ fatal:'util:logger:fatal'
+
+ }
+
+ /**
+ * Sets an static reference of an instance of this class
+ * @private
+ * @static
+ * @param {util.logger.Logger} logger - logger instance reference
+ * @return {Any}
+ **/
+ static _instance(logger) {
+ return (this.ref = logger);
+ }
+
+ /**
+ * Static Constructor
+ * @static
+ * @param {Any} [...args] - constructor arguments
+ * @return {util.logger.Logger}
+ **/
+ static new(...args) {
+ return new this(...args);
+ }
+
+}
+
+export default Logger._instance(new Proxy(Logger, Logger.new()));
diff --git a/src/commands/util/mixins.js b/lib/util/mixins.es6
similarity index 83%
rename from src/commands/util/mixins.js
rename to lib/util/mixins.es6
index c90996e..54864a0 100644
--- a/src/commands/util/mixins.js
+++ b/lib/util/mixins.es6
@@ -1,5 +1,5 @@
/**
-* Underscore Mixins
+* @module util
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
import _ from 'underscore';
@@ -43,7 +43,7 @@ _.mixin({
if(_.isRealObject(v)) return _.parametrize(v, pf, k);
if(_.isArray(v)) v = v.join(',');
return [`${pf}${_s.ltrim(_s(dk + ' ' + k).dasherize().value(), '-')}`, v];
- }, this).flatten().compact().value();
+ }, this).flatten().filter((v, k) => _.defined(v)).value();
},
/**
@@ -54,7 +54,7 @@ _.mixin({
**/
isRealObject: function(o) {
if(!_.defined(o) || !_.defined(o.constructor)) return false;
- return ((o).constructor.toString().indexOf('Object') !== -1);
+ return ((o).constructor.toString().indexOf('function Object() {') !== -1);
},
/**
@@ -69,6 +69,18 @@ _.mixin({
return (_.isRealObject(o) || _.isArray(o));
},
+ /**
+ * Returns an array of all method names of a given object.
+ * @public
+ * @param {String} o - object to be evaluated
+ * @return {Boolean}
+ **/
+ methods: function(o) {
+ return _.filter(Object.getOwnPropertyNames(o), (name) => {
+ return (typeof(o[name]) === 'function');
+ });
+ },
+
/**
* Returns true if parameter source is an instance of parameter constructor, false otherwise.
* @public
@@ -92,3 +104,5 @@ _.mixin({
}
});
+
+export default _;
diff --git a/lib/util/visitor/visited.es6 b/lib/util/visitor/visited.es6
new file mode 100644
index 0000000..ecd64ae
--- /dev/null
+++ b/lib/util/visitor/visited.es6
@@ -0,0 +1,60 @@
+/**
+* @module util.visitor
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { EventEmitter } from 'events';
+import _ from 'underscore';
+import extend from 'extend';
+import Visitor from 'util/visitor/visitor';
+import InterfaceException from 'util/exception/proxy/interface';
+
+/**
+* Class Visited
+* @extends {events.EventEmitter}
+**/
+class Visited extends EventEmitter {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Any} [...args] - constructor arguments
+ * @return {util.visitor.Visited}
+ **/
+ constructor(...args) {
+ super();
+ return extend(true, this, ...args);
+ }
+
+ /**
+ * Returns true if a given visitor is defined and an instance of util.visitor.Visitor, false otherwise
+ * @public
+ * @param {util.visitor.Visitor} visitor - visitor to validate
+ * @return {Boolean}
+ **/
+ validate(visitor) {
+ return _.defined(visitor) && _.instanceOf(visitor, Visitor);
+ }
+
+ /**
+ * Default strategy that accepts visitor by this visited instance
+ * @public
+ * @param {util.visitor.Visitor} vi - visitor to accept
+ * @return {util.visitor.Visited}
+ **/
+ accept(visitor) {
+ return this.validate(visitor) ? visitor.visit(this) : this;
+ }
+
+ /**
+ * Static Constructor
+ * @static
+ * @param {Any} [...args] - constructor arguments
+ * @return {util.visitor.Visited}
+ **/
+ static new(...args) {
+ return new this(...args);
+ }
+
+}
+
+export default Visited;
diff --git a/lib/util/visitor/visitor.es6 b/lib/util/visitor/visitor.es6
new file mode 100644
index 0000000..36f4323
--- /dev/null
+++ b/lib/util/visitor/visitor.es6
@@ -0,0 +1,115 @@
+/**
+* @module util.visitor
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { EventEmitter } from 'events';
+import _ from 'underscore';
+import _s from 'underscore.string';
+import extend from 'extend';
+import Visited from 'util/visitor/visited';
+import InterfaceException from 'util/exception/proxy/interface';
+
+/**
+* Class Visitor
+* @extends {events.EventEmitter}
+**/
+class Visitor extends EventEmitter {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Any} [...args] - constructor arguments
+ * @return {util.visitor.Visitor}
+ **/
+ constructor(...args) {
+ super();
+ return extend(true, this, ...args);
+ }
+
+ /**
+ * Filter private methods declared on this visitor
+ * @public
+ * @return {Array}
+ **/
+ _methods() {
+ const { prototype } = this.constructor;
+ return _.filter(_.methods(prototype).concat(_.methods(this)), (func) => {
+ return !_s.startsWith(func, '_') && !_.contains(Visitor._methods, func);
+ });
+ }
+
+ /**
+ * Decorates visited instance with methods declared on this visitor
+ * @private
+ * @param {util.visitor.Visited} vi - instance to be visited by this visitor
+ * @param {Any} [...args] - arguments passed to the visitor who visit the current visited instance
+ * @return {util.visitor.Visited}
+ **/
+ _decorate(vi, ...args) {
+ return _.reduce(this._methods(), (memo, method) => {
+ if(!_.defined(memo[method])) memo[method] = _.bind(this[method], this, vi, ...args);
+ return memo;
+ }, vi);
+ }
+
+ /**
+ * Returns true if the visited instance implements Visited interface.
+ * Otherwise, this method will raise an InterfaceException.
+ * @public
+ * @throws {util.exception.proxy.InterfaceException}
+ * @param {util.visitor.Visited} vi - visited instance
+ * @return {Boolean}
+ **/
+ validate(vi) {
+ if(!_.defined(vi)) return false;
+ if(!_.defined(vi.accept))
+ throw InterfaceException.new('interface', { name: 'util.visitor.Visited' });
+ return true;
+ }
+
+ /**
+ * Default Visit Strategy will return the visited object verbatim.
+ * This method is likely to be overriden by subsclasses of this visitor when needed.
+ * @public
+ * @param {util.visitor.Visited} vi - instance to be visited by this visitor
+ * @param {Any} [...args] - arguments passed to the visitor who visit the current visited instance
+ * @return {util.visitor.Visited}
+ **/
+ visit(vi, ...args) {
+ return this.validate(vi) ? this._decorate(vi, ...args) : null;
+ }
+
+ /**
+ * Visitor Name
+ * @public
+ * @type {String}
+ **/
+ get name() {
+ return 'Visitor';
+ }
+
+ /**
+ * Visitor Methods to filter out of the proxifing visit strategy
+ * @static
+ * @type {Array}
+ **/
+ static _methods = [
+ 'visit',
+ 'validate',
+ 'name',
+ 'constructor'
+ ].concat(_.functions(EventEmitter.prototype));
+
+ /**
+ * Static Constructor
+ * @static
+ * @param {Any} [...args] - constructor arguments
+ * @return {util.visitor.Visitor}
+ **/
+ static new(...args) {
+ return new this(...args);
+ }
+
+}
+
+export default Visitor;
diff --git a/lib/visitors/async/async.es6 b/lib/visitors/async/async.es6
new file mode 100644
index 0000000..c9b7038
--- /dev/null
+++ b/lib/visitors/async/async.es6
@@ -0,0 +1,63 @@
+/**
+* @module visitors.async
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { EventEmitter } from 'events';
+import _ from 'underscore';
+import Visitor from 'util/visitor/visitor';
+
+/**
+* Class Asynchronous
+* @extends {util.visitor.Visitor}
+**/
+class Asynchronous extends Visitor {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param {Any} [...args] - constructor arguments
+ * @return {visitors.async.Asynchronous}
+ **/
+ constructor(...args) {
+ return super();
+ }
+
+ /**
+ * Default Next Strategy to always resolve the promise.
+ * Note: This method was designed (and it's most likely) to be overriden by
+ * {@link util.visitor.Visited} subclasses that use this visitor.
+ * @public
+ * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @return {visitors.async.Asynchronous}
+ **/
+ next(adt, resolve, reject) {
+ resolve();
+ return this;
+ }
+
+ /**
+ * Default strategy to perform an asynchronous operation
+ * @public
+ * @param {util.visitor.Visited} [ctx] - context reference
+ * @param {visitors.async.Asynchronous} adt - reference to adt using this interface on their elements
+ * @return {Promise}
+ **/
+ execute(ctx, adt) {
+ return new Promise((resolve, reject) => ctx.next(adt, resolve, reject));
+ }
+
+ /**
+ * Visitor Name
+ * @public
+ * @type {String}
+ **/
+ get name() {
+ return 'AsynchronousVisitor';
+ }
+
+}
+
+export default Asynchronous;
diff --git a/lib/visitors/commander.es6 b/lib/visitors/commander.es6
new file mode 100644
index 0000000..bff0ee6
--- /dev/null
+++ b/lib/visitors/commander.es6
@@ -0,0 +1,195 @@
+/**
+* @module visitors
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { resolve } from 'path';
+import _ from 'underscore';
+import extend from 'extend';
+import yargs from 'yargs';
+import chalk from 'chalk';
+import Factory from 'util/factory/factory';
+import Visitor from 'util/visitor/visitor';
+import logger from 'util/logger/logger';
+
+/**
+* Class Commander
+* @extends {util.visitor.Visitor}
+**/
+class Commander extends Visitor {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param {Command} command - command visited by this visitor
+ * @return {util.visitor.Visitor}
+ **/
+ constructor(command) {
+ return super({ command });
+ }
+
+ /**
+ * Visit Strategy
+ * @public
+ * @override
+ * @param {util.visitor.Visited} vi - instance to be visited by this visitor
+ * @param {Any} [...args] - arguments passed to the visitor
+ * @return {util.visitor.Visited}
+ **/
+ visit(vi, ...args) {
+ return this.validate(vi) ? extend(false, vi, { commander: this }) : vi;
+ }
+
+ /**
+ * Reads CLI arguments and build yargs interface
+ * @public
+ * @return {yargs}
+ **/
+ read() {
+ return _.reduce(Commander.decorators, (memo, decorator) => {
+ return Factory.get(decorator, memo, this.command, this);
+ }, yargs.reset().wrap(120));
+ }
+
+ /**
+ * Commander CLI Options Parse Handler
+ * @public
+ * @param {Error} [err] - Parse Error
+ * @param {Object} argv - parsed options
+ * @param {Object} output - yargs output
+ * @return {visitors.Commander}
+ **/
+ onParse(err, argv, output) {
+ return this.emit(Commander.events.parse, err, argv, output);
+ }
+
+ /**
+ * Commander Arguments
+ * @private
+ * @param {Array} [args = process.argv] - command line arguments verbatim
+ * @return {Array}
+ **/
+ _args(args = process.argv) {
+ return args;
+ }
+
+ /**
+ * Creates command nomenclature for yargs
+ * @private
+ * @param {Command} command - command instance
+ * @return {String}
+ **/
+ _command(command) {
+ const { name, abbr } = command;
+ return chalk.green(`${name}` + (_.defined(abbr) ? `, -${abbr}` : ''));
+ }
+
+ /**
+ * Default Strategy that returns Command's aliases
+ * @public
+ * @param {Command} command - command instance
+ * @return {Array}
+ **/
+ _aliases(command) {
+ return command.aliases;
+ }
+
+ /**
+ * Default Strategy that returns Command Description
+ * @private
+ * @param {Command} command - command instance
+ * @return {String}
+ **/
+ _desc(command) {
+ return chalk.yellow(command.description);
+ }
+
+ /**
+ * Default Strategy that returns command options
+ * @private
+ * @param {Command} command - command instance
+ * @return {Object}
+ **/
+ _builder(command) {
+ return command.options;
+ }
+
+ /**
+ * Default Strategy that returns Command Handler
+ * @private
+ * @param {Command} command - command instance
+ * @return {Function}
+ **/
+ _handler(command) {
+ return (argv) => { return this._onHandler(command, argv); };
+ }
+
+ /**
+ * Default Command Handler
+ * @private
+ * @param {Command} command - command instance
+ * @param {Object} argv - yargs parsed arguments
+ * @return {visitors.Commander}
+ **/
+ _onHandler(command, argv) {
+ Factory.register(command.path);
+ this.command.settings({ options: extend(false, _.omit(argv, Commander.ignore), { path: command.path }) });
+ return this;
+ }
+
+ /**
+ * Visitor Name
+ * @public
+ * @type {String}
+ **/
+ get name() {
+ return 'CommanderVisitor';
+ }
+
+ /**
+ * Commander decorators
+ * @static
+ * @type {Array}
+ **/
+ static decorators = [
+ 'visitors/commander/version',
+ 'visitors/commander/usage',
+ 'visitors/commander/epilogue',
+ 'visitors/commander/commands',
+ 'visitors/commander/parse'
+ ];
+
+ /**
+ * Commander Events
+ * @static
+ * @type {Object}
+ **/
+ static events = {
+ /**
+ * @event parse
+ **/
+ parse: 'visitors:commander:parse'
+ }
+
+ /**
+ * Yargs arguments to ignore
+ * @static
+ * @type {Array}
+ **/
+ static ignore = ['$0', '_', 'version'];
+
+ /**
+ * Static Constructor
+ * @static
+ * @override
+ * @param {Any} [...args] - constructor arguments
+ * @return {visitors.Commander}
+ **/
+ static new(...args) {
+ Factory.registerAll(Commander.decorators);
+ return new this(...args);
+ }
+
+}
+
+export default Commander;
diff --git a/lib/visitors/commander/commands.es6 b/lib/visitors/commander/commands.es6
new file mode 100644
index 0000000..2cd1f4c
--- /dev/null
+++ b/lib/visitors/commander/commands.es6
@@ -0,0 +1,54 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+
+/**
+* Yargs Command Builder Option Names
+* @const
+* @private
+* @type {Array}
+**/
+const Builder = ['command', 'aliases', 'desc', 'builder', 'handler'];
+
+/**
+* Creates command's handler
+* @const
+* @private
+* @param {visitors.Commander} ctx - commander visitor reference
+* @param {Object} command - command options
+* @param {Object} memo - memoized object
+* @param {String} option - Yargs command option
+* @return {Object}
+**/
+const create = (ctx, command, memo, option) => {
+ memo[option] = ctx[`_${option}`](command);
+ return memo;
+};
+
+/**
+* Create Single Yarg Command
+* @const
+* @private
+* @param {visitors.Commander} ctx - commander visitor reference
+* @param {yargs} memo - memoized yargs reference
+* @param {Object} current - current command
+* @return {yargs}
+**/
+const command = (ctx, memo, current) => {
+ return memo.command(_.reduce(Builder, _.bind(create, this, ctx, current), {}));
+};
+
+/**
+* Create Yargs Commands
+* @static
+* @param {yargs} yargs - yargs reference
+* @param {Command} c - current command
+* @param {visitors.Commander} ctx - commander visitor reference
+* @return {yargs}
+**/
+export default (yargs, c, ctx) => {
+ const { commands } = c.constructor;
+ return commands.reduce(_.bind(command, this, ctx), yargs);
+};
diff --git a/lib/visitors/commander/epilogue.es6 b/lib/visitors/commander/epilogue.es6
new file mode 100644
index 0000000..5e16c20
--- /dev/null
+++ b/lib/visitors/commander/epilogue.es6
@@ -0,0 +1,16 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import chalk from 'chalk';
+
+/**
+* Yargs Epilogue
+* @static
+* @param {yargs} yargs - yargs reference
+* @return {yargs}
+**/
+export default (yargs) => {
+ return yargs.epilogue(chalk.cyan(`For more information, please visit http://squarebox.nahuel.io/`));
+};
diff --git a/lib/visitors/commander/parse.es6 b/lib/visitors/commander/parse.es6
new file mode 100644
index 0000000..517caed
--- /dev/null
+++ b/lib/visitors/commander/parse.es6
@@ -0,0 +1,17 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+
+/**
+* Yargs Parse
+* @static
+* @param {yargs} yargs - yargs reference
+* @param {Command} c - current command
+* @param {visitors.Commander} ctx - commander visitor reference
+* @return {util.visitor.Visited}
+**/
+export default (yargs, command, ctx) => {
+ return yargs.parse(ctx._args(), _.bind(ctx.onParse, ctx));
+};
diff --git a/lib/visitors/commander/usage.es6 b/lib/visitors/commander/usage.es6
new file mode 100644
index 0000000..6e27312
--- /dev/null
+++ b/lib/visitors/commander/usage.es6
@@ -0,0 +1,24 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import chalk from 'chalk';
+
+/**
+* Usage Message
+* @const
+* @private
+* @type {String}
+**/
+const message = 'sqbox [args]';
+
+/**
+* Yargs Usage
+* @static
+* @param {yargs} yargs - yargs reference
+* @return {yargs}
+**/
+export default (yargs) => {
+ return yargs.usage(chalk.white(message));
+};
diff --git a/lib/visitors/commander/version.es6 b/lib/visitors/commander/version.es6
new file mode 100644
index 0000000..4d9da36
--- /dev/null
+++ b/lib/visitors/commander/version.es6
@@ -0,0 +1,23 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import { resolve } from 'path';
+import _ from 'underscore';
+import logger from 'util/logger/logger';
+
+/**
+* Yargs Version
+* @static
+* @param {yargs} yargs - yargs reference
+* @param {Command} command - current command reference
+* @return {yargs}
+**/
+export default (yargs, command) => {
+ try {
+ return yargs.version(require(resolve(command.dirname, '..', 'package.json')).version);
+ } catch(ex) {
+ logger(`[WARN] SquareBox Version not detected - ${ex.message}`).debug(logger.yellow);
+ }
+ return yargs;
+};
diff --git a/lib/visitors/configuration.es6 b/lib/visitors/configuration.es6
new file mode 100644
index 0000000..ef46199
--- /dev/null
+++ b/lib/visitors/configuration.es6
@@ -0,0 +1,239 @@
+/**
+* @module visitors
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Factory from 'util/factory/factory';
+import Visitor from 'util/visitor/visitor';
+import QueueAsync from 'util/adt/queue-async';
+import logger, { Logger } from 'util/logger/logger';
+
+/**
+* Class Configuration
+* @extends {util.visitor.Visitor}
+**/
+class Configuration extends Visitor {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param {Command} command - command visited by this visitor
+ * @return {visitors.Configuration}
+ **/
+ constructor(command) {
+ super({ command });
+ return extend(true, this, { queue: QueueAsync.new([], { capacity: Configuration.methods.length }) });
+ }
+
+ /**
+ * Create configuration retrieval method
+ * @private
+ * @param {String} method - configuration method path
+ * @return {util.visitor.Visitor}
+ **/
+ _create(method) {
+ this.queue.offer(Factory.get(method, this.command));
+ return this;
+ }
+
+ /**
+ * Format and overrides command options from the CLI arguments
+ * @private
+ * @param {Object} opts - cli options for source
+ * @return {visitors.Configuration}
+ **/
+ _override(opts) {
+ let toOverride = _.pick(opts, Configuration.cliOptions);
+ extend(true, this.command, _.reduce(toOverride, this._format, toOverride, this));
+ return this;
+ }
+
+ /**
+ * Format a given CLI option by evaluating the key to load the factory formatter and passing the value
+ * to the formatter.
+ * @public
+ * @param {Object} memo - options being memoized
+ * @param {Any} memo - options being memoized
+ * @param {String} memo - options being memoized
+ * @return {Object}
+ **/
+ _format(memo, value, key) {
+ let ph = this.formatterPath(key);
+ if(Factory.exists(ph)) memo[key] = Factory.get(ph, value);
+ return memo;
+ }
+
+ /**
+ * Merge configuration options (and possibly) applies overrides by CLI arguments.
+ * @private
+ * @param {Object} opts - configuration options for target
+ * @return {visitors.Configuration}
+ **/
+ _source(opts) {
+ extend(true, this.command, _.pick(opts, Configuration.cliOptions))
+ return this;
+ }
+
+ /**
+ * Merge configuration options (and possibly) applies overrides by CLI arguments.
+ * @private
+ * @param {Object} opts - configuration options for target
+ * @return {visitors.Configuration}
+ **/
+ _target(opts) {
+ extend(true, this.command, {
+ target: _.reduce(opts, (memo, cfg, name) => {
+ memo[name] = _.pick(cfg, Configuration.cliOptions);
+ return memo;
+ }, opts)
+ });
+ return this;
+ }
+
+ /**
+ * Sets Logger level
+ * @private
+ * @param {String} level - logger level
+ * @return {visitors.Configuration}
+ **/
+ _logger(level) {
+ logger.level(Logger.level[level]);
+ return this;
+ }
+
+ /**
+ * Visit Strategy
+ * @public
+ * @override
+ * @param {util.visitor.Visited} vi - instance to be visited by this visitor
+ * @param {Any} [...args] - arguments passed to the visitor
+ * @return {util.visitor.Visited}
+ **/
+ visit(vi, ...args) {
+ return this.validate(vi) ? extend(false, vi, { configuration: this }) : vi;
+ }
+
+ /**
+ * Parse Configuration based on source
+ * @public
+ * @return {Promise}
+ **/
+ parse() {
+ Configuration.methods.forEach(_.bind(this._create, this));
+ return this.queue.poll().then(_.bind(this.onParse, this), _.bind(this.onParseError, this));
+ }
+
+ /**
+ * Configuration Parse Complete Handler
+ * @public
+ * @param {Array} results - configuration results
+ * @return {visitors.Configuration}
+ **/
+ onParse(results) {
+ _.each(_.compact(results), this.onOptions, this);
+ return this;
+ }
+
+ /**
+ * Configuration options handler based on results
+ * @public
+ * @param {Object} result - result
+ * @return
+ **/
+ onOptions(result) {
+ if(_.defined(result.warn)) return this.onParseError(result.warn);
+ this._source(result.source)
+ ._target(result.target)
+ ._logger(result.logLevel)
+ ._override(this.command.options);
+ return this;
+ }
+
+ /**
+ * Configuration Parse Error Handler
+ * @public
+ * @param {String} message - error message reference
+ * @return {visitors.Configuration}
+ **/
+ onParseError(message) {
+ logger(message).warn();
+ return this;
+ }
+
+ /**
+ * Resolves and return full formatter path
+ * @public
+ * @param {String} name - formatter name
+ * @return {String}
+ **/
+ formatterPath(name) {
+ return `visitors/configuration/formatter/${name}`;
+ }
+
+ /**
+ * Visitor Name
+ * @public
+ * @type {String}
+ **/
+ get name() {
+ return 'ConfigurationVisitor';
+ }
+
+ /**
+ * Factory
+ * @static
+ * @type {util.factory.Factory}
+ **/
+ static methods = [
+ 'visitors/configuration/remote',
+ 'visitors/configuration/local'
+ ];
+
+ /**
+ * Factory Formatters for Options
+ * @static
+ * @type {Array}
+ **/
+ static formatters = [
+ 'visitors/configuration/formatter/alias',
+ 'visitors/configuration/formatter/exclude',
+ 'visitors/configuration/formatter/extensions',
+ 'visitors/configuration/formatter/target'
+ ];
+
+ /**
+ * CLI Argument Options
+ * @static
+ * @type {Array}
+ **/
+ static cliOptions = ['scan', 'exclude', 'extensions', 'alias', 'target', 'destination', 'format'];
+
+ /**
+ * Configuration Events
+ * @static
+ * @type {Object}
+ **/
+ static events = {
+ /**
+ * @event parse
+ **/
+ parse: 'visitors:configuration:parse'
+ };
+
+ /**
+ * Static Constructor
+ * @static
+ * @override
+ * @param {Any} [...args] - constructor arguments
+ * @return {visitors.Configuration}
+ **/
+ static new(...args) {
+ Factory.registerAll(Configuration.methods.concat(Configuration.formatters));
+ return new this(...args);
+ }
+
+}
+
+export default Configuration;
diff --git a/lib/visitors/configuration/formatter/alias.es6 b/lib/visitors/configuration/formatter/alias.es6
new file mode 100644
index 0000000..14cb930
--- /dev/null
+++ b/lib/visitors/configuration/formatter/alias.es6
@@ -0,0 +1,16 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+
+/**
+* Alias Formatter
+* @static
+* @param {String} [input = ''] - input to format
+* @return {Object}
+**/
+export default (input = '') => {
+ if(input.length === 0) return {};
+ return _.chain(input.split(',')).invoke('split', ':').object().value();
+};
diff --git a/lib/visitors/configuration/formatter/exclude.es6 b/lib/visitors/configuration/formatter/exclude.es6
new file mode 100644
index 0000000..6e1d260
--- /dev/null
+++ b/lib/visitors/configuration/formatter/exclude.es6
@@ -0,0 +1,15 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+
+/**
+* Exclude Formatter
+* @static
+* @param {String} [input = ''] - input to format
+* @return {Object}
+**/
+export default (input = '') => {
+ return (_.isString(input) && input.length > 0) ? input.split(',') : [];
+};
diff --git a/lib/visitors/configuration/formatter/extensions.es6 b/lib/visitors/configuration/formatter/extensions.es6
new file mode 100644
index 0000000..f639ced
--- /dev/null
+++ b/lib/visitors/configuration/formatter/extensions.es6
@@ -0,0 +1,15 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+
+/**
+* Extensions Formatter
+* @static
+* @param {String} [input = ''] - input to format
+* @return {Object}
+**/
+export default (input = '') => {
+ return (_.isString(input) && input.length > 0) ? input.split(',') : [];
+};
diff --git a/lib/visitors/configuration/formatter/target.es6 b/lib/visitors/configuration/formatter/target.es6
new file mode 100644
index 0000000..48044a7
--- /dev/null
+++ b/lib/visitors/configuration/formatter/target.es6
@@ -0,0 +1,60 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+
+/**
+* Supported Formats
+* @const
+* @private
+* @type {Array}
+**/
+const formats = ['ifie', 'umd', 'amd', 'cjs'];
+
+/**
+* Validate Format
+* @const
+* @param {String} format - module format
+* @return {Boolean}
+**/
+const valid = (format) => {
+ return _.contains(formats, format);
+};
+
+/**
+* Create Target
+* @const
+* @param {Object} m - memoized object version to export
+* @return {Object}
+**/
+const target = (m, v, ps) => {
+ m[v[0]] = { destination: ps[1], format: ps[0] };
+ return m;
+};
+
+/**
+* Create Reducer
+* @const
+* @param {Object} m - memoized object version to export
+* @return {Object}
+**/
+const create = (m, v) => {
+ if(!_.defined(v[1])) return m;
+ let ps = v[1].split(':');
+ return valid(ps[0]) ? target(m, v, ps) : m;
+};
+
+/**
+* Target Formatter
+* @static
+* @param {String} [input = ''] - input to format
+* @return {Object}
+**/
+export default (input = '') => {
+ return (_.isString(input) && input.length > 0) ?
+ _.chain(input.split(','))
+ .invoke('split', '>')
+ .reduce(create, {})
+ .value() : {};
+};
diff --git a/lib/visitors/configuration/local.es6 b/lib/visitors/configuration/local.es6
new file mode 100644
index 0000000..16e3af8
--- /dev/null
+++ b/lib/visitors/configuration/local.es6
@@ -0,0 +1,135 @@
+/**
+* @module visitors.configuration
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import fs from 'fs-extra';
+import { resolve } from 'path';
+import _ from 'util/mixins';
+import extend from 'extend';
+import Visited from 'util/visitor/visited';
+import logger from 'util/logger/logger';
+
+/**
+* Class Local
+* @extends {util.visitor.Visited}
+*
+* @uses {visitors.async.Asynchronous}
+**/
+class Local extends Visited {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Command} command - command reference
+ * @return {visitors.configuration.Local}
+ **/
+ constructor(command) {
+ return super({ command });
+ }
+
+ /**
+ * Resolves absolute path to a given file
+ * @public
+ * @param {String} file - file name to evaluate
+ * @return {String}
+ **/
+ resolvePath(file) {
+ return resolve(this.command.cwd, file);
+ }
+
+ /**
+ * Returns true if local configuration exists, false otherwise
+ * @public
+ * @param {String} file - file name to evaluate
+ * @return {Boolean}
+ **/
+ exists(file) {
+ try {
+ return _.defined(file) && _.defined(fs.statSync(file));
+ } catch(ex) {
+ logger(`LocalConfiguration: ${ex.message}`).debug({}, logger.yellow);
+ return false;
+ }
+ }
+
+ /**
+ * Will try to open configuration as a json
+ * @public
+ * @param {String} path - resolved path to configuration
+ * @return {Object}
+ **/
+ tryJson(path) {
+ try {
+ return fs.readJsonSync(path);
+ } catch(ex) {
+ logger(`LocalConfiguration: ${ex.message}`).debug({}, logger.yellow);
+ return null;
+ }
+ }
+
+ /**
+ * Will try to open configuration as javascript via module.exports
+ * @public
+ * @param {String} path - resolved path to configuration
+ * @return {Object}
+ **/
+ tryJs(path) {
+ try {
+ return require(path);
+ } catch(ex) {
+ logger(`LocalConfiguration: ${ex.message}`).debug({}, logger.yellow);
+ return null;
+ }
+ }
+
+ /**
+ * Outputs a warn message and returns the object
+ * @public
+ * @param {String} message - warning message
+ * @return {Object}
+ **/
+ warn(warn) {
+ logger(warn).debug({}, logger.yellow);
+ return { warn };
+ }
+
+ /**
+ * Load local configuration file
+ * @public
+ * @param {String} name - file name
+ * @return {Object}
+ **/
+ load(name) {
+ let file = this.resolvePath(name), output = '';
+ if(!this.exists(file)) return this.warn(Local.messages.notFound);
+ if(!(output = (this.tryJson(file) || this.tryJs(file)))) return this.warn(Local.messages.invalid);
+ return output;
+ }
+
+ /**
+ * Asynchronous next strategy
+ * @public
+ * @override
+ * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @return {Promise}
+ **/
+ next(adt, resolve, reject) {
+ const { config } = this.command.getOptions();
+ return resolve(this.load(config));
+ }
+
+ /**
+ * Local Configuration Messages
+ * @static
+ * @type {Object}
+ **/
+ static messages = {
+ notFound: `Local configuration not found`,
+ invalid: `Local configuration: sqbox file is invalid`
+ }
+
+}
+
+export default Local;
diff --git a/lib/visitors/configuration/remote.es6 b/lib/visitors/configuration/remote.es6
new file mode 100644
index 0000000..63256bb
--- /dev/null
+++ b/lib/visitors/configuration/remote.es6
@@ -0,0 +1,94 @@
+/**
+* @module visitors.configuration
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'util/mixins';
+import extend from 'extend';
+import request from 'request-promise';
+import Visited from 'util/visitor/visited';
+
+/**
+* Class Remote
+* @extends {util.visitor.Visited}
+*
+* @uses {visitors.async.Asynchronous}
+**/
+class Remote extends Visited {
+
+ /**
+ * Constructor
+ * @public
+ * @param {Command} command - command reference
+ * @return {visitors.configuration.Remote}
+ **/
+ constructor(command) {
+ return super({ command });
+ }
+
+ /**
+ * Load remote configuration file from url
+ * @public
+ * @param {String} url - url to load remote configuration from
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @return {Promise}
+ **/
+ load(url, resolve, reject) {
+ return request.get(url, { json: true })
+ .then(_.bind(this.onResponse, this, resolve, reject))
+ .catch(_.bind(this.onResponseError, this, resolve, reject));
+ }
+
+ /**
+ * Remote Response Success
+ * @public
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @param {Object} response - request response
+ * @return {visitors.configuration.Remote}
+ **/
+ onResponse(resolve, reject, response) {
+ return resolve(response);
+ }
+
+ /**
+ * Remote Response Error
+ * @public
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @param {Object} err - response error
+ * @return {Object}
+ **/
+ onResponseError(resolve, reject, err) {
+ return resolve({ warn: Remote.messages.error({ err }) });
+ }
+
+ /**
+ * Asynchronous next strategy
+ * @public
+ * @override
+ * @param adt {util.proxy.Asynchronous} adt used for asynchronous operations
+ * @param resolve {Function} asynchronous promise's resolve
+ * @param reject {Function} asynchronous promise's reject
+ * @return {Promise}
+ **/
+ next(adt, resolve, reject) {
+ const { url } = this.command.getOptions();
+ return _.defined(url) ?
+ this.load(url, resolve, reject) :
+ resolve({ warn: Remote.messages.noUrl });
+ }
+
+ /**
+ * Remote Configuration Messages
+ * @static
+ * @type {Object}
+ **/
+ static messages = {
+ noUrl: `No Url specified`,
+ error: _.template(`Remote Url Error - <%= err %>`)
+ }
+
+}
+
+export default Remote;
diff --git a/lib/visitors/formatter/json.es6 b/lib/visitors/formatter/json.es6
new file mode 100644
index 0000000..b6b7a90
--- /dev/null
+++ b/lib/visitors/formatter/json.es6
@@ -0,0 +1,76 @@
+/**
+* @module visitors.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import Visitor from 'util/visitor/visitor';
+import InterfaceException from 'util/exception/proxy/interface';
+
+/**
+* Class JSON
+* @extends {util.visitor.Visitor}
+**/
+class Json extends Visitor {
+
+ /**
+ * Constructor
+ * @public
+ * @override
+ * @param {Any} [...args] - constructor arguments
+ * @return {visitors.formatter.Json}
+ **/
+ constructor(...args) {
+ return super();
+ }
+
+ /**
+ * Reducer Strategy to iterate over properties
+ * @public
+ * @param {Object} m - memoized object reference
+ * @param {Any} v - current object's value
+ * @param {String} k - current object's key
+ * @return {Object}
+ **/
+ _filterObject(m, v, k) {
+ if(_.isAdt(v)) return this._clean(v, v);
+ if(!_.isFunction(v)) { m[k] = v; return m; }
+ if(_.isArray(m)) m.splice(k, 1)
+ if(_.isRealObject(m)) delete m[k];
+ return m;
+ }
+
+ /**
+ * Clean Functions from JSON representation
+ * @public
+ * @param {Any} current - current object
+ * @param {Object} [memo = {}] - memoized object
+ * @return {Object}
+ **/
+ _clean(current, memo = {}) {
+ let keys = Object.getOwnPropertyNames(current);
+ return _.reduce(keys, (m, k) => this._filterObject(m, current[k], k), memo, this);
+ }
+
+ /**
+ * Returns a json representation of the instance of this class
+ * This method uses recursion
+ * @public
+ * @param {visitors.formatter.Json} [ctx] - context reference
+ * @return {Object}
+ **/
+ toJSON(ctx) {
+ return JSON.parse(JSON.stringify(this._clean(ctx)));
+ }
+
+ /**
+ * Visitor Name
+ * @public
+ * @type {String}
+ **/
+ get name() {
+ return 'JsonVisitor';
+ }
+
+}
+
+export default Json;
diff --git a/lib/visualize/graph.es6 b/lib/visualize/graph.es6
new file mode 100644
index 0000000..22b6811
--- /dev/null
+++ b/lib/visualize/graph.es6
@@ -0,0 +1,29 @@
+/**
+* @module visualize
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import extend from 'extend';
+import Command from 'command';
+
+/**
+* Class Graph
+* @extends {Command}
+**/
+class Graph extends Command {
+
+ /**
+ * Run
+ * @public
+ * @override
+ * @return {visualize.Graph}
+ **/
+ run() {
+ // TODO
+ console.log('Graph.run()...');
+ return super.run();
+ }
+
+}
+
+export default Graph;
diff --git a/mocha.opts b/mocha.opts
index 8a086db..a9837db 100644
--- a/mocha.opts
+++ b/mocha.opts
@@ -1,6 +1,8 @@
--require ./test/global
+--require babel-register
+--require babel-polyfill
--recursive
--ui bdd
---timeout 500
+--timeout 2000
--reporter spec
--compilers es6:babel-register
diff --git a/package.json b/package.json
index 7ac680d..16e4d6c 100644
--- a/package.json
+++ b/package.json
@@ -7,12 +7,18 @@
"sqbox": "bin/sqbox"
},
"scripts": {
- "test": "_mocha --opts mocha.opts --watch test/commands",
+ "test": "_mocha --opts mocha.opts --watch test/lib",
"it": "_mocha --opts mocha.opts test/integration",
- "coverage": "istanbul cover ./node_modules/.bin/_mocha -- --opts mocha.opts test/commands",
+ "coverage": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=html --reporter=text mocha --opts mocha.opts test/lib",
"coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls",
- "docs": "esdoc",
- "build": "node register"
+ "docs": "esdoc"
+ },
+ "nyc": {
+ "require": [
+ "babel-register"
+ ],
+ "sourceMap": true,
+ "instrument": false
},
"files": [
"bin",
@@ -30,24 +36,27 @@
"license": "MIT",
"devDependencies": {
"babel-cli": "6.23.0",
- "babel-core": "6.23.1",
- "babel-plugin-module-resolver": "2.5.0",
- "babel-preset-es2015": "6.22.0",
- "babel-preset-stage-2": "6.22.0",
- "babel-register": "6.23.0",
- "chai": "^3.5.0",
- "coveralls": "^2.11.16",
+ "babel-plugin-istanbul": "4.0.0",
+ "chai": "3.5.0",
+ "coveralls": "2.11.16",
+ "cross-env": "3.1.4",
"esdoc": "0.5.2",
"esdoc-es7-plugin": "0.0.3",
- "istanbul": "1.1.0-alpha.1",
"mocha": "2.5.3",
"mocha-lcov-reporter": "1.3.0",
- "sinon": "1.17.7"
+ "nyc": "10.1.2",
+ "sinon": "1.17.7",
+ "sinon-stub-promise": "4.0.0"
},
"dependencies": {
- "async": "^2.1.5",
+ "acorn": "4.0.11",
+ "babel-plugin-module-resolver": "2.5.0",
+ "babel-polyfill": "6.23.0",
+ "babel-preset-es2015": "6.22.0",
+ "babel-preset-stage-2": "6.22.0",
+ "babel-register": "6.23.0",
"chalk": "1.1.3",
- "esprima": "3.1.3",
+ "d3": "4.7.3",
"extend": "3.0.0",
"fs-extra": "2.0.0",
"glob": "7.1.1",
@@ -55,6 +64,7 @@
"minimatch": "3.0.3",
"node-watch": "0.4.1",
"request": "2.79.0",
+ "request-promise": "4.2.0",
"underscore": "1.8.3",
"underscore.string": "3.3.4",
"yargs": "6.6.0"
diff --git a/release.js b/release.js
deleted file mode 100644
index 2bfe03a..0000000
--- a/release.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
-* Release Squarebox
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-require('babel-register')();
-require('./src/build/build');
diff --git a/src/build/build.es6 b/src/build/build.es6
deleted file mode 100644
index a8613cc..0000000
--- a/src/build/build.es6
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
-* @module build
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import Package from '../../../package.json';
-import Command from '../commands/command';
-import _ from 'underscore';
-import extend from 'extend';
-import yargs from 'yargs';
-
-/**
-* Class Build
-* @extends command.Command
-**/
-class Build extends Command {
-
- /**
- * Build Run
- * @public
- * @override
- * @return build.Build
- **/
- run() {
- return this;
- }
-
- /**
- * Setup Yargs
- * @static
- * @override
- * @return build.Build
- **/
- static setup() {
- Command.setup();
- return this;
- }
-
- /**
- * Static Run
- * @static
- * @return build.Build
- **/
- static run() {
- return this.setup().new(this.args());
- }
-
-}
-
-export default Build.run();
diff --git a/src/commands/bin/sqbox.es6 b/src/commands/bin/sqbox.es6
deleted file mode 100644
index 7761fd5..0000000
--- a/src/commands/bin/sqbox.es6
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
-* @module commands.bin
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import Command from 'commands/command';
-import extend from 'extend';
-import yargs from 'yargs';
-
-/**
-* Class SquareBox
-* @extends commands.Command
-**/
-class SquareBox extends Command {
-
- /**
- * Command Defaults
- * @static
- * @type {Object}
- **/
- static defaults = extend(true, Command.defaults, {
- 'config': '.squarebox.json',
- 'source-scan': './src',
- 'source-extensions': ['.js', '.es6', '.es'],
- 'source-alias': ,
- 'target-destination': './dist',
- 'target-format': 'ifie'
- });
-
- /**
- * Command options
- * @static
- * @type {Array}
- **/
- static options = Command.options.concat([
- 'config',
- 'source-scan',
- 'source-extensions'
- 'source-alias',
- 'target-destination',
- 'target-format'
- ]);
-
- /**
- * Setup Yargs
- * @static
- * @override
- * @return
- **/
- static setup() {
- Command.setup();
- return this;
- }
-
- /**
- * Static Run
- * @static
- * @return commands.bin.SquareBox
- **/
- static run() {
- return this.setup().new(this.args());
- }
-
-}
-
-export default SquareBox.run();
diff --git a/src/commands/bundle/bundle.es6 b/src/commands/bundle/bundle.es6
deleted file mode 100644
index e69de29..0000000
diff --git a/src/commands/clean/clean.es6 b/src/commands/clean/clean.es6
deleted file mode 100644
index e69de29..0000000
diff --git a/src/commands/command.es6 b/src/commands/command.es6
deleted file mode 100644
index 2e7a94f..0000000
--- a/src/commands/command.es6
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
-* @module commands
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import { EventEmitter } from 'events';
-import './util/mixins';
-import _ from 'underscore';
-import extend from 'extend';
-import yargs from 'yargs';
-import JSON from './util/proxy/json';
-import Collection from './util/adt/collection';
-import CommandException from './util/exception/command/command';
-
-/**
-* Class Command
-* @extends {events.EventEmitter}
-*
-* @uses {commands.util.proxy.JSON}
-**/
-export default class Command extends EventEmitter {
-
- /**
- * Commands Collection
- * @private
- * @type {commands.util.adt.Collection}
- **/
- _commands = new Collection([], { interface: Command })
-
- /**
- * Constructor
- * @public
- * @param [args = {}] {Object} Constructor arguments
- * @return {commands.Command}
- **/
- constructor(args) {
- super();
- return JSON.proxy(extend(true, this, this.defaults, _.pick(args, this.constructor.options)));
- }
-
- /**
- * Command Chainning
- * @public
- * @throws {commands.util.exceptions.CommandException}
- * @param command {commands.Command} command used to chain
- * @return {commands.Command}
- **/
- chain(command) {
- if(!_.defined(command) || !_.instanceOf(command, Command))
- throw CommandException.new({ type: 'chain', level: CommandException.fatal });
- this._commands.add(command);
- return this;
- }
-
- /**
- * Default Command Run
- * @public
- * @return {commands.Command}
- **/
- run() {
- return this;
- }
-
- /**
- * Retrieves source
- * @public
- * @return {Object}
- **/
- source() {
- return this.source;
- }
-
- /**
- * Retrieves and resolves scan directory (glob)
- * @public
- * @return {String}
- **/
- scan() {
- return this.source().scan;
- }
-
- /**
- * Retrieves list of extensions to scan
- * @public
- * @return {Array}
- **/
- extensions() {
- return this.source().extensions;
- }
-
- /**
- * Retrieves aliases for modules
- * @public
- * @return {Object}
- **/
- alias() {
- return this.source().alias;
- }
-
- /**
- * Retrieves target
- * @public
- * @return {Object}
- **/
- target() {
- return this.target;
- }
-
- /**
- * Retrieves and resolves destination
- * @public
- * @return {String}
- **/
- destination() {
- return this.target().destination;
- }
-
- /**
- * Retrieves export format
- * @public
- * @return {String}
- **/
- format() {
- return this.target().format;
- }
-
- /**
- * Retrieves Yargs Arguments
- * @public
- * @return {Object}
- **/
- args() {
- return yargs.argv;
- }
-
- /**
- * Command Defaults
- * @static
- * @type {Object}
- **/
- static defaults = {
- env: 'development'
- };
-
- /**
- * Command options
- * @static
- * @type {Array}
- **/
- static options = [
- 'env'
- ];
-
- /**
- * Command Events
- * @static
- * @type {Object}
- **/
- static events = {
- start: 'commands:command:start',
- end: 'commands:command:end'
- };
-
- /**
- * Default Yargs setup
- * @static
- * @return {commands.Command}
- **/
- static setup() {
- return this;
- }
-
- /**
- * Static Constructor
- * @static
- * @param [...agrs] {Any} constructor arguments
- * @return {commands.Command}
- **/
- static new(...args) {
- return new this(...args);
- }
-
-}
diff --git a/src/commands/help/help.es6 b/src/commands/help/help.es6
deleted file mode 100644
index e69de29..0000000
diff --git a/src/commands/util/exception/adt/queue.es6 b/src/commands/util/exception/adt/queue.es6
deleted file mode 100644
index 89a2536..0000000
--- a/src/commands/util/exception/adt/queue.es6
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
-* @module commands.util.adt.exception
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import _ from 'underscore';
-import extend from 'extend';
-import Exception from '../exception';
-
-/**
-* Class QueueException
-* @extends {commands.util.exception.Exception}
-**/
-export default class QueueException extends Exception {
-
- /**
- * Command Exception types
- * @public
- * @type {Object}
- **/
- static type = extend(true, Exception.type, {
- capacityUndefined: _.template(`Queue requires a 'capacity' property in order to be instanciate it`),
- capacityViolation: _.template(`Queue element's collection passed overflows the current capacity
- <%= capacity %>`)
- });
-
-}
diff --git a/src/commands/util/exception/command/command.es6 b/src/commands/util/exception/command/command.es6
deleted file mode 100644
index f51be4e..0000000
--- a/src/commands/util/exception/command/command.es6
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
-* @module commands.util.exception.command
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import _ from 'underscore';
-import extend from 'extend';
-import Exception from '../exception';
-
-/**
-* Class CommandException
-* @extends {commands.util.exception.Exception}
-**/
-export default class CommandException extends Exception {
-
- /**
- * Command Exception types
- * @public
- * @type {Object}
- **/
- static type = extend(true, Exception.type, {
- chain: _.template('Required parameter `command` to be an instance of `commands.Command`')
- });
-
-}
diff --git a/src/commands/util/exception/exception.es6 b/src/commands/util/exception/exception.es6
deleted file mode 100644
index e9ec61b..0000000
--- a/src/commands/util/exception/exception.es6
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
-* @module commands.util.exception
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import _ from 'underscore';
-import extend from 'extend';
-import Logger from '../logger/logger';
-
-/**
-* Class Exception
-* @extends {Error}
-**/
-export default class Exception extends Error {
-
- /**
- * Constructor
- * @public
- * @param [args = { type: 'unknown' }] {Object} constructor attribute
- **/
- constructor(args = { type: 'unknown' }) {
- super({ message: Command.type[args.type] });
- return extend(true, this, _.pick(args, 'level'));
- }
-
- /**
- * Exception Message
- * @public
- * @type {String}
- **/
- get message() {
- // TODO
- }
-
- /**
- * Command Exception types
- * @public
- * @type {Object}
- **/
- static type = {
- unknown: _.template('Unknown Exception')
- }
-
- /**
- * @static
- * @param [...args] {Any}
- **/
- static new(...args) {
- return new this(...args);
- }
-
-}
diff --git a/src/commands/util/exception/proxy/interface.es6 b/src/commands/util/exception/proxy/interface.es6
deleted file mode 100644
index c3d34b7..0000000
--- a/src/commands/util/exception/proxy/interface.es6
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
-* @module commands.util.exception.proxy
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import _ from 'underscore';
-import extend from 'extend';
-import Exception from '../exception';
-
-/**
-* Class InterfaceException
-* @extends {commands.util.exception.Exception}
-**/
-export default class InterfaceException extends Exception {
-
- /**
- * Command Exception types
- * @public
- * @type {Object}
- **/
- static type = extend(true, Exception.type, {
- proxy: _.template(`Proxies require a 'target' in which their interface operates on`)
- });
-
-}
diff --git a/src/commands/util/logger/logger.es6 b/src/commands/util/logger/logger.es6
deleted file mode 100644
index e69de29..0000000
diff --git a/src/commands/util/proxy/json.es6 b/src/commands/util/proxy/json.es6
deleted file mode 100644
index 9d80bf8..0000000
--- a/src/commands/util/proxy/json.es6
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
-* @module commands.util.interface
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import _ from 'underscore';
-
-/**
-* Interface JSON
-**/
-class Json {
-
- /**
- * Proxy's `getPrototypeOf` trap strategy
- * @public
- * @param target {Any} proxy's target
- * @return {Object}
- **/
- getPrototypeOf(target) {
- return target.constructor.prototype;
- }
-
- /**
- * Proxy's `get` trap strategy
- * @public
- * @param target {Any} proxy target
- * @param property {String} property name
- * @param receiver {Any} proxy receiver
- * @return {Any}
- **/
- get(target, property, receiver) {
- let value = _.defined(this[property]) ? this[property] : target[property];
- return _.isFunction(value) ? _.bind(value, target, this) : value;
- }
-
- /**
- * Reducer Strategy to iterate over properties
- * @public
- * @param m {Object} memoized object reference
- * @param v {Any} current object's value
- * @param k {String} current object's key
- * @return {Object}
- **/
- _reduce(m, v, k) {
- if(_.isAdt(v)) this._clean(v, v);
- if(!_.isFunction(v)) { m[k] = v; return m; }
- if(_.isArray(m)) m.splice(k, 1)
- if(_.isRealObject(m)) delete m[k];
- return m;
- }
-
- /**
- * Clean Functions from JSON representation
- * @public
- * @param current {Any} current object
- * @param [memo = {}] {Object} memoized object
- * @return {Object}
- **/
- _clean(current, memo = {}) {
- return _.reduce(current, this._reduce, memo, this);
- }
-
- /**
- * Returns a json representation of the instance of this class
- * This method uses recursion
- * @public
- * @param ctx {commands.util.proxy.Json} reference to the instance of this class
- * @return {Object}
- **/
- toJSON(ctx) {
- return JSON.parse(JSON.stringify(ctx._clean(this)));
- }
-
- /**
- * Proxifies a given target with an instance of this class
- * @static
- * @throws {commands.util.exception.proxy.InterfaceException}
- * @param target {Any} instance to proxify
- * @param [...args] {Any} constructor arguments
- * @return {commands.util.proxy.Json}
- **/
- static proxy(target, ...args) {
- if(!_.defined(target))
- throw InterfaceException.new({ type: 'proxy', level: InterfaceException.fatal });
- return new Proxy(target, new this(...args));
- }
-
-}
-
-export default Json;
diff --git a/src/commands/visualize/visualize.es6 b/src/commands/visualize/visualize.es6
deleted file mode 100644
index e69de29..0000000
diff --git a/test/commands/util/adt/collection.spec.es6 b/test/commands/util/adt/collection.spec.es6
deleted file mode 100644
index ffebec3..0000000
--- a/test/commands/util/adt/collection.spec.es6
+++ /dev/null
@@ -1,233 +0,0 @@
-/**
-* @module commands.util.adt
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import Collection from 'commands/util/adt/collection';
-import Command from 'commands/command';
-
-describe('commands.util.adt.Collection', function() {
-
- before(() => {
- this.sandbox = sinon.sandbox.create();
- });
-
- beforeEach(() => {
- this.mockCollection = this.sandbox.mock(Collection.prototype);
- });
-
- afterEach(() => {
- this.sandbox.restore();
- delete this.mockCollection;
- });
-
- after(() => {
- delete this.sandbox;
- });
-
- describe('constructor()', () => {
-
- it('Should get a new instance', () => {
- assert.instanceOf(Collection.new(), Collection);
- });
-
- it('Should get a new instance with elements', () => {
- const exp = Collection.new([{ option: 1 }, { option: 2}]);
- assert.isFalse(exp.isEmpty());
- assert.equal(2, exp.size());
- });
-
- it('Should get a new instance with elements (interface)', () => {
- const exp = Collection.new([{ option: true }, { option: false }], { interface: Command });
- assert.instanceOf(exp.get(0), Command);
- });
-
- });
-
- describe('set()', () => {
-
- it('Should set new elements (with Event)', () => {
- const exp = Collection.new();
- const toSet = [1, 2, 3];
- const expReset = this.mockCollection.expects('reset')
- .once()
- .withArgs({ silent: true })
- .returns(exp);
-
- const expEmit = this.mockCollection.expects('emit')
- .once()
- .withArgs(Collection.events.set, exp, toSet)
- .returns(exp);
-
- exp.set(toSet);
-
- assert.equal(3, exp.size());
- assert.equal(toSet[1], exp.get(1));
-
- this.mockCollection.verify();
- });
-
- it('Should set new elements (without Event)', () => {
- const exp = Collection.new();
- const toSet = [1, 2, 3];
- const expReset = this.mockCollection.expects('reset')
- .once()
- .withArgs({ silent: true })
- .returns(exp);
-
- const expEmit = this.mockCollection.expects('emit').never();
-
- exp.set(toSet, { silent: true });
-
- assert.equal(3, exp.size());
- assert.equal(toSet[1], exp.get(1));
-
- this.mockCollection.verify();
- });
-
- it('Should NOT set new elements', () => {
- const exp = Collection.new();
- const expReset = this.mockCollection.expects('reset').never();
-
- assert.instanceOf(exp.set(), Collection); // undefined
- assert.instanceOf(exp.set({ option: 'notAnArray' }), Collection); // not an array
-
- this.mockCollection.verify();
- });
-
- });
-
- describe('add()', () => {
-
- it('Should add a new element (with Event)', () => {
- const toAdd = { env: 'production' };
- const exp = Collection.new([], { interface: Command });
- const expEmit = this.mockCollection.expects('emit')
- .once()
- .withArgs(Collection.events.add, exp, toAdd)
- .returns(toAdd);
-
- exp.add(toAdd);
- assert.isFalse(exp.isEmpty());
- assert.instanceOf(exp.get(0), Command);
-
- this.mockCollection.verify();
- });
-
- it('Should add a new element (without Event)', () => {
- const toAdd = { env: 'production' };
- const exp = Collection.new([], { interface: Command });
- const expEmit = this.mockCollection.expects('emit').never();
-
- exp.add(toAdd, { silent: true });
- assert.isFalse(exp.isEmpty());
- assert.instanceOf(exp.get(0), Command);
-
- this.mockCollection.verify();
- });
-
- it('Should NOT add a new element', () => {});
-
- });
-
- describe('addAll()', () => {
-
- it('Should add new elements', () => {});
- it('Should NOT add new elements', () => {});
-
- });
-
- describe('get()', () => {
-
- it('Should get an element', () => {});
- it('Should NOT get an element', () => {});
-
- });
-
- describe('contains()', () => {
-
- it('Should contain an element', () => {});
- it('Should NOT contain an element', () => {});
-
- });
-
- describe('containsAl()', () => {
-
- it('Should contain all elements', () => {});
- it('Should NOT contain at least one element', () => {});
-
- });
-
- describe('containsWhere()', () => {
-
- it('Should contain an element with condition', () => {});
- it('Should NOT contain an element with condition', () => {});
-
- });
-
- describe('remove()', () => {
-
- it('Should remove an element', () => {});
- it('Should NOT remove an element', () => {});
-
- });
-
- describe('removeAll()', () => {
-
- it('Should remove all the elements', () => {});
- it('Should NOT remove all the elements', () => {});
-
- });
-
- describe('removeBy()', () => {
-
- it('Should remove elments by predicate', () => {});
- it('Should NOT remove elements by predicate', () => {});
-
- });
-
- describe('sort()', () => {
-
- it('Should sort by comparator', () => {});
- it('Should NOT sort', () => {});
-
- });
-
- describe('iterator()', () => {
-
- it('Should get an iterator from collection', () => {});
-
- });
-
- describe('reset()', () => {
-
- it('Should reset the collection', () => {});
-
- });
-
- describe('size()', () => {
-
- it('Should get the size of the collection', () => {});
-
- });
-
- describe('isEmpty()', () => {
-
- it('Should be empty', () => {});
- it('Should NOT be empty', () => {});
-
- });
-
- describe('hasInterface()', () => {
-
- it('Should have interface defined', () => {});
- it('Should NOT have interface defined', () => {});
-
- });
-
- describe('toJSON()', () => {
-
- it('Should get a json representation', () => {});
-
- });
-
-});
diff --git a/test/commands/util/adt/queue.spec.es6 b/test/commands/util/adt/queue.spec.es6
deleted file mode 100644
index e69de29..0000000
diff --git a/test/commands/util/proxy/json.spec.es6 b/test/commands/util/proxy/json.spec.es6
deleted file mode 100644
index 24458c6..0000000
--- a/test/commands/util/proxy/json.spec.es6
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
-* @module commands.util.proxy
-* @author Patricio Ferreira <3dimentionar@gmail.com>
-**/
-import _ from 'underscore';
-import Json from 'commands/util/proxy/json';
-import Command from 'commands/command';
-
-describe('commands.util.proxy.Json', function() {
-
- before(() => {
- this.sandbox = sinon.sandbox.create();
- });
-
- beforeEach(() => {
- this.target = () => {
- return {
- a: 1,
- b: true,
- regexp: new RegExp(),
- func: () => {},
- obj: { o_a: 1, o_b: true, func: () => {}, arr: [1, true, () => {}] },
- arr: [{
- a_o_a: 1,
- a_o_b: true,
- func: () => {},
- command: Command.new()
- }, 1, true, () => {}]
- };
- };
- this.mockJson = this.sandbox.mock(Json.prototype);
- });
-
- afterEach(() => {
- this.sandbox.restore();
- delete this.target;
- delete this.mockJson;
- });
-
- after(() => {
- delete this.sandbox;
- });
-
- describe('#constructor', () => {
-
- it('Should get a new instance', () => {
- assert.instanceOf(Json.proxy(this.target), Function);
- });
-
- });
-
- describe('#toJSON()', () => {
-
- it('Should return a json representation', () => {
- const o = this.target();
- const exp = Json.proxy(o);
- const out = exp.toJSON();
- assert.notEqual(o, out);
- });
-
- });
-
-});
diff --git a/test/global.js b/test/global.js
index 4e205ff..e9b957f 100644
--- a/test/global.js
+++ b/test/global.js
@@ -4,6 +4,8 @@
**/
global.fs = require('fs-extra');
global.path = require('path');
+global._ = require('underscore');
global.sinon = require('sinon');
global.assert = require('chai').assert;
-global.basepath = path.join(path.resolve(__dirname, '..'), 'src');
+global.basepath = path.join(path.resolve(__dirname, '..'), 'lib');
+require('sinon-stub-promise')(sinon);
diff --git a/test/lib/bin/sqbox.spec.es6 b/test/lib/bin/sqbox.spec.es6
new file mode 100644
index 0000000..9b16533
--- /dev/null
+++ b/test/lib/bin/sqbox.spec.es6
@@ -0,0 +1,69 @@
+/**
+* @module bin
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import SquareBox from 'bin/sqbox';
+import Commander from 'visitors/commander';
+
+describe('bin.SquareBox', function() {
+
+ before(() => {
+ this.cwd = path.resolve(process.cwd());
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ if(this.sqbox) this.mockProto = this.sandbox.mock(this.sqbox);
+ this.mockCommander = this.sandbox.mock(Commander.prototype);
+ this.input = [process.argv[0], this.cwd];
+ });
+
+ afterEach(() => {
+ if(this.mockProto) this.mockProto.verify();
+ this.mockCommander.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockCommander;
+ delete this.mockProto;
+ delete this.input;
+ });
+
+ after(() => {
+ delete this.cwd;
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get an instance', () => {
+ this.sqbox = require('bin/sqbox').default.new();
+ assert.instanceOf(this.sqbox, SquareBox);
+ });
+
+ });
+
+ describe('static->run()', () => {
+
+ it('Should run the command', () => {
+ this.input = this.input.concat([
+ 'sqbox',
+ 'bundle',
+ '--config', 'test/specs/.sqboxrc',
+ '--s', './source/**',
+ '--x', './source/dependencies/**,./source/package/**',
+ '--e', '.js,.es6',
+ '--a', 'common:./path/common',
+ '--t', 'add>umd:./dist/umd,other>cjs:./dist/cjs'
+ ]);
+
+ this.mockCommander.expects('_args')
+ .once()
+ .returns(this.input);
+
+ assert.instanceOf(this.sqbox.run(), SquareBox);
+ });
+
+ });
+
+});
diff --git a/test/commands/command.spec.es6 b/test/lib/command.spec.es6
similarity index 68%
rename from test/commands/command.spec.es6
rename to test/lib/command.spec.es6
index 5135c3c..c94b1bb 100644
--- a/test/commands/command.spec.es6
+++ b/test/lib/command.spec.es6
@@ -1,10 +1,10 @@
/**
-* @module commands
+* @module
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
-import Command from 'commands/command';
+import Command from 'command';
-describe('commands.Command', function() {
+describe('Command', function() {
before(() => {
this.sandbox = sinon.sandbox.create();
@@ -23,7 +23,7 @@ describe('commands.Command', function() {
delete this.sandbox;
});
- describe('#constructor', () => {
+ describe('constructor()', () => {
it('Should get a new instance', () => {
assert.instanceOf(Command.new(), Command);
@@ -31,10 +31,11 @@ describe('commands.Command', function() {
});
- describe('#toJSON()', () => {
+ describe('toJSON()', () => {
it('Should return a json representation', () => {
- const exp = Command.new({ env: 'production' });
+ const exp = Command.new({ env: 'production' }).toJSON();
+ assert.property(exp, 'env');
});
});
diff --git a/test/lib/util/adt/collection.spec.es6 b/test/lib/util/adt/collection.spec.es6
new file mode 100644
index 0000000..f2e2d3e
--- /dev/null
+++ b/test/lib/util/adt/collection.spec.es6
@@ -0,0 +1,555 @@
+/**
+* @module util.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Collection from 'util/adt/collection';
+import Command from 'command';
+import Iterator from 'util/adt/iterator';
+
+describe('util.adt.Collection', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockStatic = this.sandbox.mock(Collection);
+ this.mockCollection = this.sandbox.mock(Collection.prototype);
+ });
+
+ afterEach(() => {
+ this.mockStatic.verify();
+ this.mockCollection.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockStatic;
+ delete this.mockCollection;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ assert.instanceOf(Collection.new(), Collection);
+ });
+
+ it('Should get a new instance with elements', () => {
+ const exp = Collection.new([{ option: 1 }, { option: 2}]);
+ assert.isFalse(exp.isEmpty());
+ assert.equal(2, exp.size());
+ });
+
+ it('Should get a new instance with elements (interface)', () => {
+ const exp = Collection.new([{ option: true }, { option: false }], { interface: Command });
+ assert.instanceOf(exp.get(0), Command);
+ });
+
+ });
+
+ describe('set()', () => {
+
+ it('Should set new elements (with Event)', () => {
+ const exp = Collection.new();
+ const toSet = [1, 2, 3];
+ const expReset = this.mockCollection.expects('reset')
+ .once()
+ .withArgs({ silent: true })
+ .returns(exp);
+
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.set, exp, toSet)
+ .returns(exp);
+
+ exp.set(toSet);
+
+ assert.equal(3, exp.size());
+ assert.equal(toSet[1], exp.get(1));
+ });
+
+ it('Should set new elements (without Event)', () => {
+ const exp = Collection.new();
+ const toSet = [1, 2, 3];
+ const expReset = this.mockCollection.expects('reset')
+ .once()
+ .withArgs({ silent: true })
+ .returns(exp);
+
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ exp.set(toSet, { silent: true });
+
+ assert.equal(3, exp.size());
+ assert.equal(toSet[1], exp.get(1));
+ });
+
+ it('Should NOT set new elements', () => {
+ const exp = Collection.new();
+ const expReset = this.mockCollection.expects('reset').never();
+
+ assert.instanceOf(exp.set(), Collection); // undefined
+ assert.instanceOf(exp.set({ option: 'notAnArray' }), Collection); // not an array
+ });
+
+ });
+
+ describe('add()', () => {
+
+ it('Should add a new element (with Event)', () => {
+ const toAdd = { env: 'production' };
+ const exp = Collection.new([], { interface: Command });
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.add, exp, sinon.match.instanceOf(Command))
+ .returns(sinon.match.instanceOf(Command));
+
+ exp.add(toAdd);
+ assert.isFalse(exp.isEmpty());
+ assert.instanceOf(exp.get(0), Command);
+ });
+
+ it('Should add a new element (without Event)', () => {
+ const toAdd = { env: 'production' };
+ const exp = Collection.new([], { interface: Command });
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ exp.add(toAdd, { silent: true });
+ assert.isFalse(exp.isEmpty());
+ assert.instanceOf(exp.get(0), Command);
+ });
+
+ it('Should add a new element (with custom new instanciation)', () => {
+ const toAdd = { env: 'production' };
+ const _new = (attrs) => new Command({ env: 'prod' });
+ const spyNew = this.sandbox.spy(_new);
+ const exp = Collection.new([], { interface: Command });
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.add, exp, sinon.match.instanceOf(Command))
+ .returns(sinon.match.instanceOf(Command));
+
+ assert.instanceOf(exp.add(toAdd, { new: spyNew }), Command);
+ assert.equal(true, spyNew.calledOnce);
+ });
+
+ it('Should NOT add a new element', () => {
+ const exp = Collection.new();
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ assert.isNull(exp.add());
+ });
+
+ });
+
+ describe('addAll()', () => {
+
+ it('Should add new elements', () => {
+ const toAdd = [4,5];
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.addall, exp, toAdd)
+ .returns(exp);
+
+ assert.instanceOf(exp.addAll(toAdd), Collection);
+ assert.isFalse(exp.isEmpty());
+ assert.equal(5, exp.size());
+ assert.equal(4, exp.get(3));
+ });
+
+ it('Should NOT add new elements', () => {
+ const exp = Collection.new();
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ assert.instanceOf(exp.addAll(), Collection);
+ assert.instanceOf(exp.addAll({ invalid: true }), Collection);
+ });
+
+ });
+
+ describe('get()', () => {
+
+ it('Should get an element', () => {
+ const exp = Collection.new(['hello', 'world']);
+ assert.equal('world', exp.get(1));
+ assert.equal('hello', exp.get());
+ });
+
+ it('Should NOT get an element', () => {
+ const exp = Collection.new(['hello', 'world']);
+ assert.isUndefined(exp.get(3));
+ });
+
+ });
+
+ describe('contains()', () => {
+
+ it('Should contain an element (without interface)', () => {
+ const exp = Collection.new([{ option: 1 }, { option: 2 }]);
+ assert.isTrue(exp.contains({ option: 2 }));
+ });
+
+ it('Should contain an element (with interface)', () => {
+ const exp = Collection.new([{ env: 'production' }, { env: 'staging' }], { interface: Command });
+ assert.isTrue(exp.contains(exp.get(1).toJSON()));
+ });
+
+ it('Should NOT contain an element', () => {
+ const exp = Collection.new([{ option: 1 }, { option: 2 }]);
+ assert.isFalse(exp.contains({ option: 3 }));
+ assert.isFalse(exp.contains());
+ });
+
+ });
+
+ describe('containsAll()', () => {
+
+ it('Should contain all elements', () => {
+ const exp = Collection.new([{ option: 1 }, { option: 2 }, { option: 3 }]);
+
+ assert.isTrue(exp.containsAll([{ option: 1 }, { option: 3 }]));
+ assert.isFalse(exp.containsAll([{ option: 1 }, { option: 2 }, { option: 3 }, { option: 4 }]));
+ });
+
+ it('Should NOT contain at least one element', () => {
+ const exp = Collection.new([{ option: 1 }, { option: 2 }, { option: 3 }]);
+
+ assert.isFalse(exp.containsAll([{ option: 1 }, { value: 2 }]));
+ assert.isFalse(exp.containsAll([]));
+ assert.isFalse(exp.containsAll());
+ });
+
+ });
+
+ describe('containsWhere()', () => {
+
+ it('Should contain an element with condition', () => {
+ const exp = Collection.new([{ prop: 'A' }, { prop: 'B' }]);
+ assert.isTrue(exp.containsWhere({ prop: 'B' }));
+ assert.isTrue(exp.containsWhere());
+ });
+
+ it('Should NOT contain an element with condition', () => {
+ const exp = Collection.new([{ prop: 'A' }, { prop: 'B' }]);
+ assert.isFalse(exp.containsWhere({ prop: 'C' }));
+ });
+
+ });
+
+ describe('removeAt()', () => {
+
+ it('Should remove an element at index (without interface)', () => {
+ const toRemove = 1;
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.remove, exp, toRemove)
+ .returns(exp);
+
+ assert.equal(1, exp.removeAt(toRemove));
+ assert.equal(2, exp.size());
+ assert.equal(3, exp.get(1));
+ });
+
+ it('Should remove an element at index (with interface)', () => {
+ const exp = Collection.new([{ env: 'staging' }, { env: 'production' }], { interface: Command });
+ const toRemove = 1;
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.remove, exp, toRemove)
+ .returns(exp);
+
+ assert.equal(toRemove, exp.removeAt(1));
+ assert.equal(1, exp.size());
+ });
+
+ it('Should NOT remove an element at index (default index)', () => {
+ const exp = Collection.new();
+ const expEmit = this.mockCollection.expects('emit').never();
+ assert.isNull(exp.removeAt());
+ });
+
+ it('Should NOT remove an element at index (index not a number)', () => {
+ const exp = Collection.new();
+ const expEmit = this.mockCollection.expects('emit').never();
+ assert.isNull(exp.removeAt('hello'));
+ });
+
+ it('Should NOT remove an element at index (index is greater that size - 1)', () => {
+ const exp = Collection.new([1,2]);
+ const expEmit = this.mockCollection.expects('emit').never();
+ assert.isNull(exp.removeAt(2));
+ });
+
+ });
+
+ describe('remove()', () => {
+
+ it('Should remove an element (without interface)', () => {
+ const toRemove = 2;
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.remove, exp, [toRemove])
+ .returns(exp);
+
+ assert.equal(toRemove, exp.remove(toRemove));
+ assert.equal(2, exp.size());
+ assert.equal(3, exp.get(1));
+ });
+
+ it('Should remove an element (with interface)', () => {
+ const exp = Collection.new([{ env: 'staging' }, { env: 'production' }], { interface: Command });
+ const toRemove = exp.get(1);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.remove, exp, [toRemove])
+ .returns(exp);
+
+ assert.equal(toRemove, exp.remove(toRemove));
+ assert.equal(1, exp.size());
+ assert.notEqual(toRemove, exp.get(0));
+ });
+
+ it('Should NOT remove an element (element is invalid)', () => {
+ const exp = Collection.new([1,2]);
+ const expEmit = this.mockCollection.expects('emit').never();
+ assert.isNull(exp.remove());
+ });
+
+
+ });
+
+ describe('removeAll()', () => {
+
+ it('Should remove all the elements', () => {
+ const toRemove = [1,2];
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.removeall, exp, toRemove)
+ .returns(exp);
+
+ assert.instanceOf(exp.removeAll(toRemove), Collection);
+ assert.isFalse(exp.isEmpty());
+ assert.equal(1, exp.size());
+ assert.equal(3, exp.get(0));
+ });
+
+ it('Should remove a few elements (matching)', () => {
+ const toRemove = [1,5,2];
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.removeall, exp, [1,2])
+ .returns(exp);
+
+ assert.instanceOf(exp.removeAll(toRemove), Collection);
+ assert.isFalse(exp.isEmpty());
+ assert.equal(1, exp.size());
+ assert.equal(3, exp.get(0));
+ });
+
+ it('Should NOT remove all the elements', () => {
+ const toRemove = [4,5];
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.removeall, exp, [])
+ .returns(exp);
+
+ assert.instanceOf(exp.removeAll(toRemove), Collection);
+ assert.isFalse(exp.isEmpty());
+ assert.equal(3, exp.size());
+ assert.equal(1, exp.get(0));
+ });
+
+ it('Should NOT remove all the elements (elements not an array)', () => {
+ const toRemove = { invalid: 1 };
+ const exp = Collection.new([1,2,3]);
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ assert.instanceOf(exp.removeAll(toRemove), Collection);
+ assert.instanceOf(exp.removeAll(), Collection);
+
+ assert.isFalse(exp.isEmpty());
+ assert.equal(3, exp.size());
+ });
+
+ });
+
+ describe('removeBy()', () => {
+
+ it('Should remove elments by predicate', () => {
+ const predicate = (e) => (e.env === 'stage' || e.env === 'dev');
+ let spyPredicate = this.sandbox.spy(predicate);
+ const exp = Collection.new([{ env: 'stage' }, { env: 'dev' }, { env: 'prod' }], { interface: Command });
+ const expEmit = this.mockCollection.expects('emit')
+ .exactly(2)
+ .withArgs(Collection.events.remove, exp, sinon.match(0).or(sinon.match(1)))
+ .returns(sinon.match(0).or(sinon.match(1)));
+
+ assert.instanceOf(exp.removeBy(spyPredicate), Collection);
+ assert.equal(1, exp.size());
+ assert.equal('prod', exp.get(0).env);
+ assert.equal(true, spyPredicate.calledThrice);
+ });
+
+ it('Should NOT remove elements by predicate', () => {
+ const predicate = (v) => v === 3;
+ let spyPredicate = this.sandbox.spy(predicate);
+ const exp = Collection.new([1,2]);
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ assert.instanceOf(exp.removeBy(spyPredicate), Collection);
+ assert.equal(2, exp.size());
+ assert.equal(true, spyPredicate.calledTwice);
+ });
+
+ it('Should NOT remove elements by predicate (no predicate)', () => {
+ const exp = Collection.new([1,2]);
+ const expEmit = this.mockCollection.expects('emit').never();
+
+ assert.instanceOf(exp.removeBy(), Collection);
+ assert.equal(2, exp.size());
+ });
+
+ });
+
+ describe('sort()', () => {
+
+ it('Should sort by comparator', () => {
+ const exp = Collection.new([{ v: 3 }, { v: 1 }, { v: 2 }]);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.sort, exp)
+ .returns(exp);
+
+ exp.sort((a, b) => (a.v - b.v));
+
+ assert.equal(1, exp.get(0).v);
+ assert.equal(2, exp.get(1).v);
+ assert.equal(3, exp.get(2).v);
+ });
+
+ it('Should sort using default', () => {
+ const exp = Collection.new(['Hello', '1 Hello', '2 World', 'World']);
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.sort, exp)
+ .returns(exp);
+
+ exp.sort();
+
+ assert.equal('1 Hello', exp.get(0));
+ assert.equal('2 World', exp.get(1));
+ assert.equal('Hello', exp.get(2));
+ assert.equal('World', exp.get(3));
+ });
+
+ });
+
+ describe('iterator()', () => {
+
+ it('Should get an iterator from collection', () => {
+ assert.instanceOf(Collection.new([1,2,3]).iterator(), Iterator);
+ });
+
+ });
+
+ describe('reset()', () => {
+
+ it('Should reset the collection', () => {
+ const exp = Collection.new();
+ const expEmit = this.mockCollection.expects('emit')
+ .once()
+ .withArgs(Collection.events.reset, exp)
+ .returns(exp);
+
+ exp.addAll([1,2], { silent: true });
+ assert.equal(2, exp.size());
+
+ exp.reset();
+ assert.equal(0, exp.size());
+ });
+
+ });
+
+ describe('size()', () => {
+
+ it('Should get the size of the collection', () => {
+ assert.equal(0, Collection.new().size());
+ assert.equal(2, Collection.new([1,2]).size());
+ });
+
+ });
+
+ describe('isEmpty()', () => {
+
+ it('Should be empty', () => {
+ assert.isTrue(Collection.new().isEmpty());
+ });
+
+ it('Should NOT be empty', () => {
+ assert.isFalse(Collection.new([1]).isEmpty());
+ });
+
+ });
+
+ describe('hasInterface()', () => {
+
+ it('Should have interface defined', () => {
+ const exp = Collection.new([{ env: 'staging' }, { env: 'production' }], { interface: Command });
+ assert.isTrue(exp.hasInterface());
+ });
+
+ it('Should NOT have interface defined', () => {
+ const exp = Collection.new([]);
+ assert.isFalse(exp.hasInterface());
+ });
+
+ });
+
+ describe('toJSON()', () => {
+
+ it('Should get a json representation (without interface)', () => {
+ const exp = Collection.new([1,2,'value']).toJSON();
+ assert.isArray(exp);
+ assert.equal(3, exp.length);
+ assert.equal('value', exp[2]);
+ });
+
+ it('Should get a json representation (with interface)', () => {
+ const exp = Collection.new([{ env: 'staging' }, { env: 'production' }], { interface: Command }).toJSON();
+ assert.isArray(exp);
+ assert.equal(2, exp.length);
+ assert.equal('production', exp[1].env);
+ });
+
+ });
+
+ describe('static->_aggregate()', () => {
+
+ it('Should NOT aggregate underscore methods (already implemented)', () => {
+ const expUNDERSCORE = this.mockStatic.expects('UNDERSCORE')
+ .once()
+ .returns(['remove']);
+
+ assert.typeOf(Collection._aggregate(), 'Function');
+ });
+
+ it('Should NOT aggregate underscore methods (not as part of underscore)', () => {
+ const expUNDERSCORE = this.mockStatic.expects('UNDERSCORE')
+ .once()
+ .returns(['unexistent']);
+
+ assert.typeOf(Collection._aggregate(), 'Function');
+ });
+
+ });
+
+});
diff --git a/test/commands/util/adt/iterator.spec.es6 b/test/lib/util/adt/iterator.spec.es6
similarity index 89%
rename from test/commands/util/adt/iterator.spec.es6
rename to test/lib/util/adt/iterator.spec.es6
index b23b6e3..9cfc77d 100644
--- a/test/commands/util/adt/iterator.spec.es6
+++ b/test/lib/util/adt/iterator.spec.es6
@@ -1,10 +1,10 @@
/**
-* @module commands.util.adt
+* @module util.adt
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
-import Iterator from 'commands/util/adt/iterator';
+import Iterator from 'util/adt/iterator';
-describe('commands.util.adt.Iterator', function() {
+describe('util.adt.Iterator', function() {
before(() => {
this.sandbox = sinon.sandbox.create();
@@ -23,7 +23,7 @@ describe('commands.util.adt.Iterator', function() {
delete this.sandbox;
});
- describe('#constructor', () => {
+ describe('constructor()', () => {
it('Should get a new instance', () => {
assert.instanceOf(Iterator.new(), Iterator);
@@ -38,7 +38,7 @@ describe('commands.util.adt.Iterator', function() {
});
- describe('#_valid()', () => {
+ describe('_valid()', () => {
it('Should return true (valid element)', () => {
const exp = Iterator.new();
@@ -53,7 +53,7 @@ describe('commands.util.adt.Iterator', function() {
});
- describe('#set()', () => {
+ describe('set()', () => {
it('Should set the iterator', () => {
const exp = Iterator.new();
@@ -78,7 +78,7 @@ describe('commands.util.adt.Iterator', function() {
});
- describe('#hasNext()', () => {
+ describe('hasNext()', () => {
it('Should return true and false', () => {
const exp = Iterator.new([1,2]);
@@ -91,7 +91,7 @@ describe('commands.util.adt.Iterator', function() {
});
- describe('#next()', () => {
+ describe('next()', () => {
it('Should return next element', () => {
const exp = Iterator.new([1,2]);
@@ -102,7 +102,7 @@ describe('commands.util.adt.Iterator', function() {
});
- describe('#rewind()', () => {
+ describe('rewind()', () => {
it('Should rewind the iterator (pointer to the begin of the iterator)', () => {
const exp = Iterator.new([1,2]);
@@ -117,7 +117,7 @@ describe('commands.util.adt.Iterator', function() {
});
- describe('#remove()', () => {
+ describe('remove()', () => {
it('Should remove the current element by the pointer', () => {
const exp = Iterator.new([1,2,3]);
diff --git a/test/lib/util/adt/queue-async.spec.es6 b/test/lib/util/adt/queue-async.spec.es6
new file mode 100644
index 0000000..507dec4
--- /dev/null
+++ b/test/lib/util/adt/queue-async.spec.es6
@@ -0,0 +1,93 @@
+/**
+* @module util.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import QueueAsync from 'util/adt/queue-async';
+import Command from 'command';
+import Asynchronous from 'visitors/async/async';
+import InterfaceException from 'util/exception/proxy/interface';
+
+describe('util.adt.QueueAsync', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockStatic = this.sandbox.mock(QueueAsync);
+ this.mockProto = this.sandbox.mock(QueueAsync.prototype);
+ });
+
+ afterEach(() => {
+ this.mockStatic.verify();
+ this.mockProto.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockStatic;
+ delete this.mockProto;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ const exp = QueueAsync.new([], { capacity: 3 });
+ assert.instanceOf(exp, QueueAsync);
+ assert.instanceOf(exp._visitor, Asynchronous);
+ });
+
+ it('Should get a new instance (no initial and no opts)', () => {
+ assert.instanceOf(QueueAsync.new(), QueueAsync);
+ });
+
+ });
+
+ describe('async->poll()', () => {
+
+ it('Should poll all elements asynchronously from queue (with Events)', (done) => {
+ const exp = QueueAsync.new([{ env: 'dev' }, { env: 'stage' }], { capacity: 2, interface: Command });
+
+ exp.on(QueueAsync.events.next, (element) => {
+ assert.instanceOf(element, Command);
+ });
+
+ exp.on(QueueAsync.events.end, (result) => {
+ assert.isArray(result);
+ assert.lengthOf(result, 2);
+ assert.instanceOf(result[0], Command);
+
+ done();
+ });
+
+ assert.instanceOf(exp.poll(), Promise);
+ });
+
+ it('Should poll all elements asynchronously from queue (without Events)', (done) => {
+ const exp = QueueAsync.new([], { capacity: 2, interface: Command });
+ const expEmit = this.mockProto.expects('emit').never();
+
+ assert.isTrue(exp.offer({ env: 'dev' }, { silent: true }));
+ assert.isTrue(exp.offer({ env: 'stage' }, { silent: true }));
+
+ const result = exp.poll({ silent: true }).then((results) => {
+ assert.instanceOf(results, Array);
+ assert.lengthOf(results, 2);
+ assert.instanceOf(results[0], Command);
+ assert.isTrue(exp.isEmpty());
+
+ done();
+ }).catch((err) => console.log(err));
+ });
+
+ it('Should Error: Element doesn\'t implement interface util.visitor.Visited', () => {
+ const message = InterfaceException.type.interface({ name: 'util.visitor.Visited' });
+ assert.throws(() => QueueAsync.new([{ simple: true }], { capacity: 1 }), message);
+ });
+
+ });
+
+});
diff --git a/test/lib/util/adt/queue.spec.es6 b/test/lib/util/adt/queue.spec.es6
new file mode 100644
index 0000000..14f893d
--- /dev/null
+++ b/test/lib/util/adt/queue.spec.es6
@@ -0,0 +1,158 @@
+/**
+* @module util.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Queue from 'util/adt/queue';
+import Command from 'command';
+import QueueException from 'util/exception/adt/queue';
+
+describe('util.adt.Queue', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockStatic = this.sandbox.mock(Queue);
+ this.mockQueue = this.sandbox.mock(Queue.prototype);
+ });
+
+ afterEach(() => {
+ this.mockStatic.verify();
+ this.mockQueue.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockStatic;
+ delete this.mockQueue;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ assert.instanceOf(Queue.new(), Queue);
+ });
+
+ });
+
+ describe('_valid()', () => {
+
+ it('Should ERROR: elements is array and length is >= than capacity', () => {
+ const exp = Queue.new([], { capacity: 2 });
+ assert.throws(() => exp._valid([1,2,3]), QueueException.type.capacityViolation({ capacity: 2 }));
+ });
+
+ it('Should NOT be valid - element is null', () => {
+ const exp = Queue.new([], { capacity: 2 });
+ assert.isFalse(exp._valid());
+ });
+
+ });
+
+ describe('_validCapacity()', () => {
+
+ it('Should validate capacity to true - current size is <= than capacity', () => {
+ const exp = Queue.new([], { capacity: 2 });
+ const expSize = this.mockQueue.expects('size').once().returns(2);
+ assert.isFalse(exp._validCapacity());
+ });
+
+ });
+
+ describe('set()', () => {
+
+ it('Should set elements', () => {
+ const toSet = [1, 2];
+ const exp = Queue.new([], { capacity: 2 });
+ const expEmit = this.mockQueue.expects('emit')
+ .once()
+ .withArgs(Queue.events.set, exp, toSet)
+ .returns(exp);
+
+ assert.instanceOf(exp.set(toSet), Queue);
+ assert.equal(2, exp.size());
+ });
+
+ it('Should NOT set elements (not valid)', () => {
+ const exp = Queue.new([], { capacity: 2 });
+ const expEmit = this.mockQueue.expects('emit').never();
+ assert.instanceOf(exp.set(), Queue);
+ assert.instanceOf(exp.set({}), Queue);
+ });
+
+ });
+
+ describe('offer()', () => {
+
+ it('Should push elements into the queue', () => {
+ const toOffer = { env: 'stage' };
+ const exp = Queue.new([{ env: 'dev' }], { capacity: 3, interface: Command, silent: true });
+ const expEmit = this.mockQueue.expects('emit')
+ .once()
+ .withArgs(Queue.events.offer, exp, sinon.match.instanceOf(Command))
+ .returns(exp);
+
+ assert.isTrue(exp.offer(toOffer));
+ assert.equal(2, exp.size());
+ });
+
+ it('Should NOT push elements into the queue', () => {
+ const toOffer = { env: 'stage' };
+ const exp = Queue.new([{ env: 'dev' }], { capacity: 1, interface: Command, silent: true });
+ const expEmit = this.mockQueue.expects('emit').never();
+
+ assert.isFalse(exp.offer(toOffer));
+ assert.equal(1, exp.size());
+ });
+
+ });
+
+ describe('peek()', () => {
+
+ it('Should retrieve, but not remove the next element in the queue', () => {
+ const exp = Queue.new([{ env: 'dev' }, { env: 'stage' }], { capacity: 2, interface: Command, silent: true });
+ let next = exp.peek();
+ assert.instanceOf(next, Command);
+ assert.equal('dev', next.env);
+ assert.equal(2, exp.size());
+ });
+
+ it('Should NOT retrieve the next element in the queue', () => {
+ const exp = Queue.new([], { capacity: 2, interface: Command, silent: true });
+ let next = exp.peek();
+ assert.isNull(next);
+ assert.isTrue(exp.isEmpty());
+ });
+
+ });
+
+ describe('poll()', () => {
+
+ it('Should retrieve and remove the next element in the queue', () => {
+ const exp = Queue.new([{ env: 'dev' }, { env: 'stage' }], { capacity: 3, interface: Command, silent: true });
+ const expEmit = this.mockQueue.expects('emit')
+ .once()
+ .withArgs(Queue.events.poll, exp, sinon.match.instanceOf(Command))
+ .returns(exp);
+
+ let next = exp.poll();
+ assert.instanceOf(next, Command);
+ assert.equal('dev', next.env);
+ assert.equal(1, exp.size());
+ });
+
+ it('Should NOT retrieve (nor remove) the next element in the queue', () => {
+ const exp = Queue.new([], { capacity: 2, interface: Command, silent: true });
+ const expEmit = this.mockQueue.expects('emit').never();
+
+ assert.isNull(exp.poll());
+ assert.isTrue(exp.isEmpty());
+ });
+
+ });
+
+});
diff --git a/test/lib/util/adt/stack-async.spec.es6 b/test/lib/util/adt/stack-async.spec.es6
new file mode 100644
index 0000000..0a76575
--- /dev/null
+++ b/test/lib/util/adt/stack-async.spec.es6
@@ -0,0 +1,93 @@
+/**
+* @module util.adt
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import StackAsync from 'util/adt/stack-async';
+import Command from 'command';
+import Asynchronous from 'visitors/async/async';
+import InterfaceException from 'util/exception/proxy/interface';
+
+describe('util.adt.StackAsync', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockStatic = this.sandbox.mock(StackAsync);
+ this.mockProto = this.sandbox.mock(StackAsync.prototype);
+ });
+
+ afterEach(() => {
+ this.mockStatic.verify();
+ this.mockProto.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockStatic;
+ delete this.mockProto;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ const exp = StackAsync.new([]);
+ assert.instanceOf(exp, StackAsync);
+ assert.instanceOf(exp._visitor, Asynchronous);
+ });
+
+ it('Should get a new instance (no initial and no opts)', () => {
+ assert.instanceOf(StackAsync.new(), StackAsync);
+ });
+
+ });
+
+ describe('async->pop()', () => {
+
+ it('Should pop all elements asynchronously from the stack (with Events)', (done) => {
+ const exp = StackAsync.new([{ env: 'dev' }, { env: 'stage' }], { interface: Command });
+
+ exp.on(StackAsync.events.next, (element) => {
+ assert.instanceOf(element, Command);
+ });
+
+ exp.on(StackAsync.events.end, (result) => {
+ assert.isArray(result);
+ assert.lengthOf(result, 2);
+ assert.instanceOf(result[0], Command);
+
+ done();
+ });
+
+ assert.instanceOf(exp.pop(), Promise);
+ });
+
+ it('Should pop all elements asynchronously from the stack (without Events)', (done) => {
+ const exp = StackAsync.new([], { interface: Command });
+ const expEmit = this.mockProto.expects('emit').never();
+
+ assert.isTrue(exp.push({ env: 'dev' }, { silent: true }));
+ assert.isTrue(exp.push({ env: 'stage' }, { silent: true }));
+
+ const result = exp.pop({ silent: true }).then((results) => {
+ assert.instanceOf(results, Array);
+ assert.lengthOf(results, 2);
+ assert.instanceOf(results[0], Command);
+ assert.isTrue(exp.isEmpty());
+
+ done();
+ }).catch((err) => console.log(err));
+ });
+
+ it('Should Error: Element doesn\'t implement interface util.visitor.Visited', () => {
+ const message = InterfaceException.type.interface({ name: 'util.visitor.Visited' });
+ assert.throws(() => StackAsync.new([{ simple: true }]), message);
+ });
+
+ });
+
+});
diff --git a/test/commands/util/adt/stack.spec.es6 b/test/lib/util/adt/stack.spec.es6
similarity index 74%
rename from test/commands/util/adt/stack.spec.es6
rename to test/lib/util/adt/stack.spec.es6
index 0876ebc..aaaebae 100644
--- a/test/commands/util/adt/stack.spec.es6
+++ b/test/lib/util/adt/stack.spec.es6
@@ -1,11 +1,11 @@
/**
-* @module commands.util.adt
+* @module util.adt
* @author Patricio Ferreira <3dimentionar@gmail.com>
**/
-import Stack from 'commands/util/adt/stack';
-import Command from 'commands/command';
+import Stack from 'util/adt/stack';
+import Command from 'command';
-describe('commands.util.adt.Stack', function() {
+describe('util.adt.Stack', function() {
before(() => {
this.sandbox = sinon.sandbox.create();
@@ -16,6 +16,7 @@ describe('commands.util.adt.Stack', function() {
});
afterEach(() => {
+ this.mockStack.verify();
this.sandbox.restore();
delete this.mockStack;
});
@@ -24,7 +25,7 @@ describe('commands.util.adt.Stack', function() {
delete this.sandbox;
});
- describe('#constructor()', () => {
+ describe('constructor()', () => {
it('Should get an instance (initial emtpy elements)', () => {
const exp = Stack.new();
@@ -40,37 +41,31 @@ describe('commands.util.adt.Stack', function() {
});
- describe('#push()', () => {
+ describe('push()', () => {
it('Should push a new element', () => {
const toPush = { option: true };
const exp = Stack.new([], { interface: Command });
const expEmit = this.mockStack.expects('emit')
.once()
- .withArgs(Stack.events.push, exp, toPush)
+ .withArgs(Stack.events.push, exp, sinon.match.instanceOf(Command))
.returns(exp);
- exp.push(toPush);
-
+ assert.isTrue(exp.push(toPush));
assert.isFalse(exp.isEmpty());
-
- this.mockStack.verify();
});
it('Should NOT push a new element', () => {
const exp = Stack.new();
const expEmit = this.mockStack.expects('emit').never();
- exp.push();
-
+ assert.isFalse(exp.push());
assert.isTrue(exp.isEmpty());
-
- this.mockStack.verify();
});
});
- describe('#peek()', () => {
+ describe('peek()', () => {
it('Should get the first element', () => {
const exp = Stack.new([1,2,3]);
@@ -85,20 +80,19 @@ describe('commands.util.adt.Stack', function() {
});
- describe('#pop()', () => {
+ describe('pop()', () => {
it('Should remove and get the first element', () => {
- const exp = Stack.new([{ option: true }, { option: false }], { interface: Command });
+ const exp = Stack.new([{ env: 'dev' }, { env: 'stage' }], { interface: Command });
+ const expPop = exp.last();
const expEmit = this.mockStack.expects('emit')
.once()
- .withArgs(Stack.events.pop, exp)
+ .withArgs(Stack.events.pop, exp, expPop)
.returns(exp);
- assert.instanceOf(exp.pop(), Stack);
+ assert.equal(expPop, exp.pop());
assert.instanceOf(exp.peek(), Command);
assert.equal(1, exp.size());
-
- this.mockStack.verify();
});
it('Should NOT remove and get the first element', () => {
@@ -109,7 +103,7 @@ describe('commands.util.adt.Stack', function() {
});
- describe('#search()', () => {
+ describe('search()', () => {
it('Should search and retrieve the position of an element', () => {
const exp = Stack.new(['a','b','c']);
@@ -117,6 +111,12 @@ describe('commands.util.adt.Stack', function() {
assert.equal(1, expPos);
});
+ it('Should NOT search (invalid element)', () => {
+ const exp = Stack.new(['a','b','c']);
+ const expPos = exp.search();
+ assert.equal(-1, expPos);
+ });
+
it('Should search and retrieve -1 position (element not found)', () => {
const newCommand = Command.new({ env: 'production' });
const exp = Stack.new([{ env: 'staging' }, newCommand], { interface: Command });
diff --git a/test/lib/util/factory/factory.spec.es6 b/test/lib/util/factory/factory.spec.es6
new file mode 100644
index 0000000..7695568
--- /dev/null
+++ b/test/lib/util/factory/factory.spec.es6
@@ -0,0 +1,311 @@
+/**
+* @module util.factory
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import factory from 'util/factory/factory';
+import logger from 'util/logger/logger';
+import Command from 'command';
+import Collection from 'util/adt/collection';
+
+describe('util.factory.Factory', function() {
+
+ before(() => {
+ factory.reset();
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockCommand = this.sandbox.mock(Command);
+ this.mockLogger = this.sandbox.mock(logger);
+ this.mockProto = this.sandbox.mock(factory);
+ });
+
+ afterEach(() => {
+ this.mockCommand.verify();
+ this.mockLogger.verify();
+ this.mockProto.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockCommand;
+ delete this.mockLogger;
+ delete this.mockProto;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should not be a constructor', () => {
+ assert.instanceOf(factory, Object);
+ assert.isNotNull(factory._factories);
+ assert.instanceOf(factory._factories, Map);
+ assert.isNotNull(factory.register);
+ });
+
+ });
+
+ describe('basePath()', () => {
+
+ it('Should set a basePath', () => {
+ const input = './lib';
+ assert.typeOf(factory.basePath(input), 'object');
+ assert.equal(input, factory.path);
+ });
+
+ it('Should be same basePath when importing multiple times (singleton check)', () => {
+ const singleton = require('util/factory/factory').default;
+ assert.deepEqual(singleton, factory);
+ singleton.basePath('./lib/util/adt');
+ assert.equal(singleton.path, factory.path);
+ });
+
+ });
+
+ describe('_resolve()', () => {
+
+ it('Should resolve a given path with the basepath', () => {
+ assert.equal(factory._resolve('collection'), path.resolve(factory.path, 'collection'));
+ });
+
+ });
+
+ describe('_validate()', () => {
+
+ it('Should return true: path is valid', () => {
+ assert.isTrue(factory._validate('collection'));
+ });
+
+ it('Should return false: path is not defined', () => {
+ assert.isFalse(factory._validate());
+ });
+
+ it('Should return false: path is not a string', () => {
+ assert.isFalse(factory._validate(1));
+ });
+
+ it('Should return false: path is not an empty string', () => {
+ assert.isFalse(factory._validate(''));
+ });
+
+ it('Should return false: resolved path doesn\'t exists (fs.statSync)', () => {
+ const expLoggerOutput = this.mockLogger.expects('_stdout').atLeast(1).returns(logger);
+ assert.isFalse(factory._validate('file-unexistent'));
+ });
+
+ });
+
+ describe('_new()', () => {
+
+ it('Should return factory verbatim', () => {
+ const verbatimFactory = { option: true }
+ assert.equal(factory._new(verbatimFactory), verbatimFactory);
+ });
+
+ it('Should instanciate factory using static constructor', () => {
+ const expNew = this.mockCommand.expects('new').returns('Command');
+ assert.equal(factory._new(Command), 'Command');
+ });
+
+ it('Should instanciate factory using operator `new`', () => {
+ const FakeFactory = function() {};
+ assert.instanceOf(factory._new(FakeFactory), FakeFactory);
+ });
+
+ });
+
+ describe('register()', () => {
+
+ it('Should register a factory', () => {
+ const input = 'collection';
+ const expValidate = this.mockProto.expects('_validate')
+ .once()
+ .withArgs(input)
+ .returns(true);
+
+ const expExists = this.mockProto.expects('exists')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ assert.instanceOf(factory.register(input), Object);
+ });
+
+ it('Should NOT register a factory (not valid)', () => {
+ const input = 'non-existent';
+ const expValidate = this.mockProto.expects('_validate')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ const expExists = this.mockProto.expects('exists').never();
+
+ assert.instanceOf(factory.register(input), Object);
+ assert.equal(1, factory._factories.size);
+ });
+
+ it('Should NOT register a factory (already registered)', () => {
+ const input = 'non-existent';
+ const expValidate = this.mockProto.expects('_validate')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ const expExists = this.mockProto.expects('exists').never();
+
+ assert.instanceOf(factory.register(input), Object);
+ assert.equal(1, factory._factories.size);
+ });
+
+ });
+
+ describe('registerAll()', () => {
+
+ it('Should register a list of factories', () => {
+ const input = ['stack', 'queue'];
+ const expValidate = this.mockProto.expects('_validate')
+ .twice()
+ .withArgs(sinon.match('stack').or(sinon.match('queue')))
+ .returns(true);
+
+ const expExists = this.mockProto.expects('exists')
+ .twice()
+ .withArgs(sinon.match('stack').or(sinon.match('queue')))
+ .returns(false);
+
+ assert.instanceOf(factory.registerAll(input), Object);
+ assert.equal(3, factory._factories.size);
+ });
+
+ it('Should register some out of a list of factories', () => {
+ const input = ['stack', 'queue-async'];
+ const expValidate = this.mockProto.expects('_validate')
+ .twice()
+ .withArgs(sinon.match('stack').or(sinon.match('queue')))
+ .returns(true);
+
+ const expExists = this.mockProto.expects('exists')
+ .twice()
+ .withArgs(sinon.match('stack').or(sinon.match('queue')));
+
+ expExists.onFirstCall().returns(true); // with stack
+ expExists.onSecondCall().returns(false); // with queue-async
+
+ assert.instanceOf(factory.registerAll(input), Object);
+ assert.equal(4, factory._factories.size);
+ });
+
+ it('Should NOT register an empty list of factory paths', () => {
+ assert.instanceOf(factory.registerAll(), Object);
+ assert.equal(4, factory._factories.size);
+ });
+
+ });
+
+ describe('unregister()', () => {
+
+ it('Should unregister a factory', () => {
+ const input = 'queue-async';
+ const expExists = this.mockProto.expects('exists')
+ .once()
+ .withArgs(input)
+ .returns(true);
+
+ assert.instanceOf(factory.unregister(input), Object);
+ assert.equal(3, factory._factories.size);
+ });
+
+ it('Should NOT unregister a factory (doesn\'t exists)', () => {
+ const input = 'non-existent';
+ const expExists = this.mockProto.expects('exists')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ assert.instanceOf(factory.unregister(input), Object);
+ assert.equal(3, factory._factories.size);
+ });
+
+ });
+
+ describe('unregisterAll()', () => {
+
+ it('Should unregister a list of factories', () => {
+ const input = ['stack', 'queue'];
+ const expExists = this.mockProto.expects('exists')
+ .twice()
+ .withArgs(sinon.match('stack').or(sinon.match('queue')))
+ .returns(true);
+
+ assert.instanceOf(factory.unregisterAll(input), Object);
+ assert.equal(1, factory._factories.size);
+ });
+
+ it('Should unregister some, out of a list of factories', () => {
+ const input = ['collection', 'non-existent'];
+ const expExists = this.mockProto.expects('exists')
+ .twice()
+ .withArgs(sinon.match('collection').or(sinon.match('non-existent')));
+
+ expExists.onFirstCall().returns(true); // with collection
+ expExists.onSecondCall().returns(false); // with non-existent
+
+ assert.instanceOf(factory.unregisterAll(input), Object);
+ assert.equal(0, factory._factories.size);
+ });
+
+ it('Should NOT unregister an empty list of factory paths', () => {
+ assert.instanceOf(factory.unregisterAll(), Object);
+ assert.equal(0, factory._factories.size);
+ });
+
+ });
+
+ describe('find()', () => {
+
+ it('Should return the factory (factory found)', () => {
+ factory.registerAll(['collection', 'stack', 'queue']); // Setup a few factories
+ const input = 'stack';
+
+ const exp = factory.find(input);
+ assert.isNotNull(exp);
+ assert.equal(factory._resolve(input), exp);
+ });
+
+ it('Should return null (factory not found)', () => {
+ const input = 'non-existent';
+ const exp = factory.find(input);
+ assert.isNull(exp);
+ assert.isNull(factory.find());
+ });
+
+ });
+
+ describe('exists()', () => {
+
+ it('Should return true (factory exists)', () => {
+ assert.isTrue(factory.exists('collection'));
+ });
+
+ it('Should return false (factory doesn\'t exists)', () => {
+ assert.isFalse(factory.exists('non-existent'));
+ assert.isFalse(factory.exists());
+ });
+
+ });
+
+ describe('get()', () => {
+
+ it('Should get a new instance', () => {
+ assert.instanceOf(factory.get('collection'), Collection);
+ });
+
+ it('Should NOT get a new instance (factory is not registered)', () => {
+ assert.isNull(factory.get('non-existent'));
+ });
+
+ });
+
+});
diff --git a/test/lib/util/logger/logger.spec.es6 b/test/lib/util/logger/logger.spec.es6
new file mode 100644
index 0000000..6c71bcf
--- /dev/null
+++ b/test/lib/util/logger/logger.spec.es6
@@ -0,0 +1,231 @@
+/**
+* @module util.logger
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import logger, { Logger } from 'util/logger/logger';
+
+describe('util.logger.Logger', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(logger);
+ this.mockProcess = this.sandbox.mock(process);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.mockProcess.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockProto;
+ delete this.mockProcess;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should check the singleton instance', () => {
+ assert.instanceOf(logger, Logger);
+ });
+
+ it('Should adds to the buffer by using constructor function', () => {
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ logger('hello')('world').out(logger.magenta);
+ });
+
+ });
+
+ describe('_stdout()', () => {
+
+ it('Should use console.warn to output', () => {
+ // ISOLATED: To avoid dangerous issues with unit tests (stubbing `console.warn`)
+ const stubConsoleWarn = this.sandbox.stub(console, 'warn', () => {});
+ assert.instanceOf(logger._stdout('Hello'), Logger);
+ stubConsoleWarn.restore();
+ });
+
+ });
+
+ describe('_validate()', () => {
+
+ it('Should validate to true, if level = silent and type is fatal', () => {
+ logger.level(Logger.level.silent);
+ assert.isTrue(logger._validate(Logger.type.fatal));
+ });
+
+ it('Should validate to true, if level = output and type is fatal, warn or output only', () => {
+ logger.level(Logger.level.output);
+ assert.isTrue(logger._validate(Logger.type.fatal));
+ assert.isTrue(logger._validate(Logger.type.warning));
+ assert.isTrue(logger._validate(Logger.type.output));
+ assert.isFalse(logger._validate(Logger.type.debug));
+ });
+
+ it('Should validate to true, if level = debug with all types', () => {
+ logger.level(Logger.level.debug);
+ assert.isTrue(logger._validate(Logger.type.fatal));
+ assert.isTrue(logger._validate(Logger.type.warning));
+ assert.isTrue(logger._validate(Logger.type.output));
+ assert.isTrue(logger._validate(Logger.type.debug));
+ });
+
+ it('Should validate to false, if level = silent with type is other than fatal', () => {
+ logger.level(Logger.level.silent);
+ assert.isFalse(logger._validate(Logger.type.warning));
+ assert.isFalse(logger._validate(Logger.type.output));
+ assert.isFalse(logger._validate(Logger.type.debug));
+ });
+
+ });
+
+ describe('out()', () => {
+
+ it('Should write to standard output (out - with event)', () => {
+ logger.level(Logger.level.output);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expEmit = this.mockProto.expects('emit')
+ .once()
+ .withArgs(Logger.events.output, logger);
+
+ assert.instanceOf(logger('hello').out(), Logger);
+ });
+
+ it('Should write to standard out (out - without event)', () => {
+ logger.level(Logger.level.output);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expEmit = this.mockProto.expects('emit').never();
+
+ assert.instanceOf(logger('hello').out({ silent: true }, logger.magenta), Logger);
+ });
+
+ it('Should NOT write to standard out (due to validation)', () => {
+ logger.level(Logger.level.silent);
+ const expOut = this.mockProto.expects('_stdout').never();
+
+ const expEmit = this.mockProto.expects('emit')
+ .once()
+ .withArgs(Logger.events.output, logger);
+
+ assert.instanceOf(logger('Not Logged').out(), Logger);
+ });
+
+ });
+
+ describe('debug()', () => {
+
+ it('Should write to standard output (debug - with event)', () => {
+ logger.level(Logger.level.debug);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expEmit = this.mockProto.expects('emit')
+ .once()
+ .withArgs(Logger.events.debug, logger);
+
+ assert.instanceOf(logger('hello').debug(), Logger);
+ });
+
+ it('Should write to standard out (debug - without event)', () => {
+ logger.level(Logger.level.debug);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expEmit = this.mockProto.expects('emit').never();
+
+ assert.instanceOf(logger('hello').debug({ silent: true }), Logger);
+ });
+
+ });
+
+ describe('warn()', () => {
+
+ it('Should write to standard output (warning - with event)', () => {
+ logger.level(Logger.level.output);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expEmit = this.mockProto.expects('emit')
+ .once()
+ .withArgs(Logger.events.warning, logger);
+
+ assert.instanceOf(logger('hello').warn(), Logger);
+ });
+
+ it('Should write to standard out (warning - without event)', () => {
+ logger.level(Logger.level.output);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expEmit = this.mockProto.expects('emit').never();
+
+ assert.instanceOf(logger('hello').warn({ silent: true }), Logger);
+ });
+
+ });
+
+ describe('fatal()', () => {
+
+ it('Should write to standard output (fatal - with event) and process.exit(1)', () => {
+ logger.level(Logger.level.output);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expExit = this.mockProcess.expects('exit')
+ .once()
+ .withArgs(1);
+
+ const expEmit = this.mockProto.expects('emit')
+ .once()
+ .withArgs(Logger.events.fatal, logger);
+
+ assert.isUndefined(logger('hello').fatal());
+ });
+
+ it('Should write to standard out (fatal - without event) and process.exit(1)', () => {
+ logger.level(Logger.level.output);
+ const expOut = this.mockProto.expects('_stdout')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expExit = this.mockProcess.expects('exit')
+ .once()
+ .withArgs(1);
+
+ const expEmit = this.mockProto.expects('emit').never();
+
+ assert.isUndefined(logger('hello').fatal({ silent: true }));
+ });
+
+ });
+
+});
diff --git a/test/lib/util/mixins.spec.es6 b/test/lib/util/mixins.spec.es6
new file mode 100644
index 0000000..557eeef
--- /dev/null
+++ b/test/lib/util/mixins.spec.es6
@@ -0,0 +1,130 @@
+/**
+* @module util
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'util/mixins';
+
+describe('util.mixins', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ this.test = {
+ str: 'one',
+ num: 1,
+ obj: {
+ 'obj-str': 'two',
+ '-obj-num': 2,
+ bool: false
+ },
+ arr: ['one', 1, true],
+ bool: true,
+ nul: null
+ };
+ });
+
+ beforeEach(() => {
+ this.mockStatic = this.sandbox.mock(_);
+ });
+
+ afterEach(() => {
+ this.mockStatic.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockStatic;
+ });
+
+ after(() => {
+ delete this.test;
+ delete this.sandbox;
+ });
+
+ describe('parametrize()', () => {
+
+ it('Should parametrized object structure', () => {
+ const exp = _.parametrize(this.test);
+ assert.include(exp, '--str');
+ assert.include(exp, '--num');
+ assert.include(exp, '--obj-obj-str');
+ assert.include(exp, '--obj-obj-num');
+ assert.include(exp, '--obj-bool');
+ assert.include(exp, '--arr');
+ assert.include(exp, '--bool');
+ assert.include(exp, '--nul');
+ });
+
+ it('Should return empty array (empty object)', () => {
+ assert.lengthOf(_.parametrize(), 0);
+ });
+
+ });
+
+ describe('isRealObject()', () => {
+
+ it('Should return true', () => {
+ assert.isTrue(_.isRealObject({}));
+ });
+
+ it('Should return false', () => {
+ assert.isFalse(_.isRealObject(new RegExp()));
+ assert.isFalse(_.isRealObject([]));
+ assert.isFalse(_.isRealObject("String"));
+ assert.isFalse(_.isRealObject(1));
+ });
+
+ });
+
+ describe('isAdt()', () => {
+
+ it('Should return true', () => {
+ assert.isTrue(_.isAdt({}));
+ assert.isTrue(_.isAdt([]));
+ });
+
+ it('Should return false', () => {
+ assert.isFalse(_.isAdt());
+ assert.isFalse(_.isAdt(1));
+ assert.isFalse(_.isAdt("string"));
+ assert.isFalse(_.isAdt(new RegExp()));
+ });
+
+ });
+
+ describe('instanceOf()', () => {
+
+ it('Should return true', () => {
+ assert.isTrue(_.instanceOf({}, Object));
+ assert.isTrue(_.instanceOf([], Array));
+ assert.isTrue(_.instanceOf([], Object));
+ assert.isTrue(_.instanceOf(new Number(1), Number));
+ assert.isTrue(_.instanceOf(new String("string"), String));
+ });
+
+ it('Should return false', () => {
+ assert.isFalse(_.instanceOf());
+ assert.isFalse(_.instanceOf({}, Array));
+ assert.isFalse(_.instanceOf([], String));
+ assert.isFalse(_.instanceOf(new Number(1), String));
+ assert.isFalse(_.instanceOf(new String("string"), RegExp));
+ });
+
+ });
+
+ describe('defined()', () => {
+
+ it('Should return true', () => {
+ assert.isTrue(_.defined(-1));
+ assert.isTrue(_.defined(0));
+ assert.isTrue(_.defined(false));
+ assert.isTrue(_.defined(""));
+ assert.isTrue(_.defined(NaN));
+ });
+
+ it('Should return false', () => {
+ assert.isFalse(_.defined(null));
+ assert.isFalse(_.defined(undefined));
+ });
+
+ });
+
+});
diff --git a/test/lib/util/visitor/visited.spec.es6 b/test/lib/util/visitor/visited.spec.es6
new file mode 100644
index 0000000..01bb71f
--- /dev/null
+++ b/test/lib/util/visitor/visited.spec.es6
@@ -0,0 +1,98 @@
+/**
+* @module util.visitor
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Visited from 'util/visitor/visited';
+import Visitor from 'util/visitor/visitor';
+import InterfaceException from 'util/exception/proxy/interface';
+
+describe('util.visitor.Visited', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(Visited.prototype);
+ this.mockVisitor = this.sandbox.mock(Visitor.prototype);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.mockVisitor.verify();
+ this.sandbox.restore();
+ delete this.mockProto;
+ delete this.mockVisitor;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ const exp = Visited.new({ property: 'Visited' });
+ assert.instanceOf(exp, Visited);
+ assert.equal('Visited', exp.property);
+ });
+
+ });
+
+ describe('validate()', () => {
+
+ it('Should return true', () => {
+ const inputVisitor = Visitor.new();
+ const exp = Visited.new({});
+ assert.isTrue(exp.validate(inputVisitor));
+ });
+
+ it('Should return false: visitor not defined', () => {
+ const exp = Visited.new({});
+ assert.isFalse(exp.validate());
+ });
+
+ it('Should return false: visitor not implementing Visited', () => {
+ const exp = Visited.new({});
+ assert.isFalse(exp.validate({}));
+ });
+
+ });
+
+ describe('accept()', () => {
+
+ it('Should accept visitor', () => {
+ const inputVisitor = Visitor.new();
+ const exp = Visited.new({});
+
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(inputVisitor)
+ .returns(true);
+
+ const expVisit = this.mockVisitor.expects('visit')
+ .once()
+ .withArgs(exp)
+ .returns(exp);
+
+ assert.instanceOf(exp.accept(inputVisitor), Object);
+ assert.isTrue(expValidate.calledBefore(expVisit));
+ });
+
+ it('Should NOT accept visitor', () => {
+ const inputVisitor = Visitor.new();
+ const exp = Visited.new({});
+
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(inputVisitor)
+ .returns(false);
+
+ const expVisit = this.mockVisitor.expects('visit').never();
+
+ assert.instanceOf(exp.accept(inputVisitor), Object);
+ });
+
+ });
+
+});
diff --git a/test/lib/util/visitor/visitor.spec.es6 b/test/lib/util/visitor/visitor.spec.es6
new file mode 100644
index 0000000..a10d885
--- /dev/null
+++ b/test/lib/util/visitor/visitor.spec.es6
@@ -0,0 +1,138 @@
+/**
+* @module util.visitor
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Visitor from 'util/visitor/visitor';
+import Visited from 'util/visitor/visited';
+import InterfaceException from 'util/exception/proxy/interface';
+
+describe('util.visitor.Visitor', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(Visitor.prototype);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.sandbox.restore();
+ delete this.mockProto;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ const exp = Visitor.new();
+ assert.instanceOf(exp, Visitor);
+ assert.equal(exp.name, 'Visitor');
+ });
+
+ });
+
+ describe('validate()', () => {
+
+ it('Should return true', () => {
+ const exp = Visitor.new();
+ const input = Visited.new({});
+ assert.isTrue(exp.validate(input));
+ });
+
+ it('Should return false: visited not defined', () => {
+ const exp = Visitor.new();
+ assert.isFalse(exp.validate());
+ });
+
+ it('Should throw InterfaceException: visited doesn\'t implement his interface', () => {
+ const exp = Visitor.new();
+ assert.throws(() => exp.validate({}),
+ InterfaceException.type.interface({ name: 'util.visitor.Visited' }));
+ });
+
+ });
+
+ describe('_methods()', () => {
+
+ it('Should filter visitor internal methods', () => {
+ const vi = Visitor.new({ someMethod: () => {} });
+ const exp = vi._methods();
+ assert.lengthOf(exp, 1);
+ assert.equal('someMethod', exp[0]);
+ });
+
+ });
+
+ describe('_decorate()', () => {
+
+ it('Should decorate visited object', () => {
+ const arg = { prop: 'one' };
+ const exp = Visitor.new({ func: this.sandbox.spy() });
+ const input = Visited.new();
+
+ const spyWith = exp.func.withArgs(input, arg);
+
+ const expMethods = this.mockProto.expects('_methods')
+ .once()
+ .returns(['func']);
+
+ assert.instanceOf(exp._decorate(input), Visited);
+
+ input.func(arg);
+
+ assert.isTrue(spyWith.calledOnce);
+ assert.isTrue(spyWith.calledOn(exp));
+ assert.isTrue(spyWith.calledWith(input, arg));
+ });
+
+ it('Should skip methods already defined in visited', () => {
+ const exp = Visitor.new({ func: () => {}, existent: this.sandbox.spy() });
+ const input = Visited.new({ existent: () => {} });
+
+ assert.instanceOf(exp._decorate(input), Visited);
+ input.existent();
+ assert.isFalse(exp.existent.calledOnce);
+ });
+
+ });
+
+ describe('visit()', () => {
+
+ it('Should visit the visted instance', () => {
+ const exp = Visitor.new();
+ const input = Visited.new({});
+ const expDecorate = this.mockProto.expects('_decorate')
+ .once()
+ .withArgs(input)
+ .returns(input);
+
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(input)
+ .returns(true);
+
+ assert.instanceOf(exp.visit(input), Object);
+ assert.isTrue(expValidate.calledBefore(expDecorate));
+ });
+
+ it('Should NOT visit the visted instance', () => {
+ const exp = Visitor.new();
+ const input = {};
+ const expDecorate = this.mockProto.expects('_decorate').never();
+
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ assert.isNull(exp.visit(input));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/async/async.spec.es6 b/test/lib/visitors/async/async.spec.es6
new file mode 100644
index 0000000..979e671
--- /dev/null
+++ b/test/lib/visitors/async/async.spec.es6
@@ -0,0 +1,79 @@
+/**
+* @module visitors.async
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import Asynchronous from 'visitors/async/async';
+import Visited from 'util/visitor/visited';
+import InterfaceException from 'util/exception/proxy/interface';
+
+describe('visitors.async.Asynchronous', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.target = () => { return { next: (adt, resolve, reject) => {} }; };
+ this.mockAsync = this.sandbox.mock(Asynchronous.prototype);
+ });
+
+ afterEach(() => {
+ this.sandbox.restore();
+ delete this.target;
+ delete this.mockAsync;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ const exp = Asynchronous.new(this.target());
+ assert.instanceOf(exp, Asynchronous);
+ assert.equal('AsynchronousVisitor', exp.name);
+ });
+
+ });
+
+ describe('next()', () => {
+
+ it('Should call promise\'s resolve as a default strategy', () => {
+ const resolveSpy = this.sandbox.spy();
+ const exp = Asynchronous.new({});
+ assert.instanceOf(exp.next({}, resolveSpy), Asynchronous);
+ assert.isTrue(resolveSpy.calledOnce);
+ });
+
+ });
+
+ describe('visit()', () => {
+
+ it('Should visit the target', () => {
+ const input = Visited.new({ property: 'target', next: this.sandbox.spy() });
+ const exp = Asynchronous.new();
+ assert.instanceOf(exp.visit(input), Visited);
+ });
+
+ it('Should NOT visit the target: super.validate returns false', () => {
+ const exp = Asynchronous.new();
+ assert.isNull(exp.visit());
+ });
+
+ });
+
+ describe('execute()', () => {
+
+ it('Should return a decorated target with asynchronous capabilities', () => {
+ const input = this.target();
+ const visited = Visited.new(input);
+ const exp = Asynchronous.new().visit(visited);
+ assert.isNotNull(exp.execute);
+ assert.instanceOf(exp.execute(), Promise);
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/commander.spec.es6 b/test/lib/visitors/commander.spec.es6
new file mode 100644
index 0000000..84538c9
--- /dev/null
+++ b/test/lib/visitors/commander.spec.es6
@@ -0,0 +1,217 @@
+/**
+* @module visitors
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Commander from 'visitors/commander';
+import Factory from 'util/factory/factory';
+import yargs from 'yargs';
+import Command from 'command';
+import chalk from 'chalk';
+
+describe('visitors.Commander', function() {
+
+ before(() => {
+ this.command = Command.new();
+ if(!this.commander) this.commander = Commander.new(this.command);
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(Commander.prototype);
+ this.mockYargs = this.sandbox.mock(yargs);
+ this.mockCommand = this.sandbox.mock(Command.prototype);
+ this.mockFactory = this.sandbox.mock(Factory);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.mockYargs.verify();
+ this.mockFactory.verify();
+ this.mockCommand.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockProto;
+ delete this.mockYargs;
+ delete this.mockFactory;
+ delete this.mockCommand;
+ });
+
+ after(() => {
+ delete this.command;
+ delete this.commander;
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get an instance', () => {
+ assert.instanceOf(this.commander, Commander);
+ assert.instanceOf(this.commander.command, Command);
+ assert.equal('CommanderVisitor', this.commander.name);
+ });
+
+ });
+
+ describe('_args()', () => {
+
+ it('Should return default arguments', () => {
+ const res = this.commander._args();
+ assert.isArray(res);
+ });
+
+ it('Should return custom arguments', () => {
+ const res = this.commander._args(['param', '--value']);
+ assert.isArray(res);
+ assert.lengthOf(res, 2);
+ });
+
+ });
+
+ describe('_command()', () => {
+
+ it('Should return string representation of a command for yargs (with abbr)', () => {
+ const input = { name: 'bundle', abbr: 'be' };
+ const exp = chalk.green(`${input.name}, -${input.abbr}`);
+ const res = this.commander._command(input);
+ assert.equal(exp, res);
+ });
+
+ it('Should return string representation of a command for yargs (without abbr)', () => {
+ const input = { name: 'bundle' };
+ const exp = chalk.green(`${input.name}`);
+ const res = this.commander._command(input);
+ assert.equal(exp, res);
+ });
+
+ });
+
+ describe('_aliases()', () => {
+
+ it('Should return aliases', () => {
+ const input = { aliases: ['b'] };
+ assert.equal(this.commander._aliases(input), input.aliases);
+ });
+
+ });
+
+ describe('_desc()', () => {
+
+ it('Should return command description', () => {
+ const input = { description: 'my description' };
+ assert.equal(this.commander._desc(input), chalk.yellow(input.description));
+ });
+
+ });
+
+ describe('_builder()', () => {
+
+ it('Should return options (defaults)', () => {
+ const input = { options: { url: { default: 'http://squarebox.nahuel.io/' } } };
+ assert.deepEqual(this.commander._builder(input), input.options);
+ });
+
+ });
+
+ describe('_handler()', () => {
+
+ it('Should return a function handler for the command', () => {
+ const input = { option: 1, option: 2 };
+ const command = {};
+ const expHandler = this.mockProto.expects('_onHandler')
+ .once()
+ .withArgs(command, input)
+ .returns(this.commander);
+
+ const exp = this.commander._handler(command, {});
+ assert.isFunction(exp);
+ exp(input);
+ });
+
+ });
+
+ describe('_onHandler()', () => {
+
+ it('Should execute command handler for yargs', () => {
+ const command = { path: 'bundle/bundle' };
+ const inputArgv = { $0: '/path/to/script.js', url: 'http://squarebox.nahuel.io' };
+ const expCommand = { options: { path: command.path, url: inputArgv.url } };
+ const exp = Commander.new(this.command);
+
+ const expRegister = this.mockFactory.expects('register')
+ .once()
+ .withArgs(command.path)
+ .returns(Factory);
+
+ const expSettings = this.mockCommand.expects('settings')
+ .once()
+ .withArgs(expCommand)
+ .returns(this.command);
+
+ assert.instanceOf(this.commander._onHandler(command, inputArgv), Commander);
+ assert.isTrue(expRegister.calledBefore(expSettings));
+ });
+
+ });
+
+ describe('visit()', () => {
+
+ it('Should visit commander visitor', () => {
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(this.command)
+ .returns(true);
+
+ const exp = this.commander.visit(this.command);
+ assert.instanceOf(exp, Command);
+ assert.property(exp, 'commander');
+ });
+
+ it('Should NOT visit commander visitor (invalid)', () => {
+ const input = Command.new();
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ const exp = this.commander.visit(input);
+ assert.instanceOf(exp, Command);
+ assert.notProperty(exp, 'commander');
+ });
+
+ });
+
+ describe('onParse()', () => {
+
+ it('Should execute onParse handler', () => {
+ const err = null;
+ const inputArgv = { $0: '/path/to/script.js', url: 'http://squarebox.nahuel.io' };
+ const output = {};
+
+ const expEmit = this.mockProto.expects('emit')
+ .once()
+ .withArgs(Commander.events.parse, err, inputArgv, output)
+ .returns(true);
+
+ assert.isTrue(this.commander.onParse(err, inputArgv, output));
+ });
+
+ });
+
+ describe('read()', () => {
+
+ it('Should read CLI arguments via this visitor', () => {
+ const expReset = this.mockYargs.expects('reset').once().returns(yargs);
+ const expWrap = this.mockYargs.expects('wrap').once().withArgs(120).returns(yargs);
+
+ const expFactoryGet = this.mockFactory.expects('get')
+ .exactly(5)
+ .withArgs(sinon.match((v) => _.contains(Commander.decorators, v)), yargs, this.command, this.commander)
+ .returns(yargs);
+
+ assert.typeOf(this.commander.read(), 'Function');
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/commander/commands.spec.es6 b/test/lib/visitors/commander/commands.spec.es6
new file mode 100644
index 0000000..11ab10d
--- /dev/null
+++ b/test/lib/visitors/commander/commands.spec.es6
@@ -0,0 +1,103 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import helper from 'visitors/commander/commands';
+import Collection from 'util/adt/collection';
+import yargs from 'yargs';
+
+describe('visitors.commander.Commands', function() {
+
+ before(() => {
+ this.command = {
+ constructor: {
+ commands: Collection.new([{
+ name: 'one',
+ aliases: ['uno'],
+ description: 'Description One',
+ options: { optionOne: {} }
+ }, {
+ name: 'two',
+ aliases: ['dos'],
+ description: 'Description Two',
+ options: { optionTwo: {} }
+ }])
+ }
+ };
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockYargs = this.sandbox.mock(yargs);
+ });
+
+ afterEach(() => {
+ this.mockYargs.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockYargs;
+ });
+
+ after(() => {
+ delete this.command;
+ delete this.sandbox;
+ });
+
+ describe('commands()', () => {
+
+ it('Should build and apply a list of commands to yargs', () => {
+ const { commands } = this.command.constructor;
+ const matcher = sinon.match((value) => {
+ const { command } = value;
+ return (_.has(command, 'name') &&
+ _.has(command, 'aliases') &&
+ _.has(command, 'description') &&
+ _.has(command, 'options'));
+ });
+
+ const fake = {
+ _command: this.sandbox.stub(),
+ _aliases: this.sandbox.stub(),
+ _desc: this.sandbox.stub(),
+ _builder: this.sandbox.stub(),
+ _handler: this.sandbox.stub()
+ };
+
+ fake._command.withArgs(sinon.match.object)
+ .onFirstCall().returns(commands.get(0))
+ .onSecondCall().returns(commands.get(1));
+
+ fake._aliases.withArgs(sinon.match.object)
+ .onFirstCall().returns(commands.get(0))
+ .onSecondCall().returns(commands.get(1));
+
+ fake._desc.withArgs(sinon.match.object)
+ .onFirstCall().returns(commands.get(0))
+ .onSecondCall().returns(commands.get(1));
+
+ fake._builder.withArgs(sinon.match.object)
+ .onFirstCall().returns(commands.get(0))
+ .onSecondCall().returns(commands.get(1));
+
+ fake._handler.withArgs(sinon.match.object)
+ .onFirstCall().returns(commands.get(0))
+ .onSecondCall().returns(commands.get(1));
+
+ const expYargsCommand = this.mockYargs.expects('command')
+ .twice()
+ .withArgs(matcher)
+ .returns(yargs);
+
+ assert.equal(yargs, helper(yargs, this.command, fake));
+
+ assert.isTrue(fake._command.calledWith(commands.get(0)));
+ assert.isTrue(fake._aliases.calledWith(commands.get(0)));
+ assert.isTrue(fake._desc.calledWith(commands.get(0)));
+ assert.isTrue(fake._builder.calledWith(commands.get(0)));
+ assert.isTrue(fake._handler.calledWith(commands.get(0)));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/commander/epilogue.spec.es6 b/test/lib/visitors/commander/epilogue.spec.es6
new file mode 100644
index 0000000..9261d9e
--- /dev/null
+++ b/test/lib/visitors/commander/epilogue.spec.es6
@@ -0,0 +1,44 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import epilogue from 'visitors/commander/epilogue';
+import chalk from 'chalk';
+import yargs from 'yargs';
+
+describe('visitors.commander.Epilogue', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockYargs = this.sandbox.mock(yargs);
+ });
+
+ afterEach(() => {
+ this.mockYargs.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockYargs;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('epilogue()', () => {
+
+ it('Should apply epilogue directive to yargs', () => {
+ const expEpilogue = this.mockYargs.expects('epilogue')
+ .once()
+ .withArgs(chalk.cyan('For more information, please visit http://squarebox.nahuel.io/'))
+ .returns(yargs);
+
+ assert.equal(yargs, epilogue(yargs));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/commander/parse.spec.es6 b/test/lib/visitors/commander/parse.spec.es6
new file mode 100644
index 0000000..512ecdd
--- /dev/null
+++ b/test/lib/visitors/commander/parse.spec.es6
@@ -0,0 +1,55 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import parse from 'visitors/commander/parse';
+import Command from 'command';
+import yargs from 'yargs';
+
+describe('visitors.commander.Parse', function() {
+
+ before(() => {
+ this.command = Command.new();
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockYargs = this.sandbox.mock(yargs);
+ });
+
+ afterEach(() => {
+ this.mockYargs.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockYargs;
+ });
+
+ after(() => {
+ delete this.command;
+ delete this.sandbox;
+ });
+
+ describe('parse()', () => {
+
+ it('Should call yargs parse', () => {
+ const input = [process.argv[0], this.cwd].concat(['sqbox', 'bundle']);
+ const ctx = {
+ _args: this.sandbox.stub().returns(input),
+ onParse: this.sandbox.spy()
+ };
+ const expParse = this.mockYargs.expects('parse')
+ .once()
+ .withArgs(input, sinon.match.func)
+ .returns(yargs);
+
+ assert.equal(yargs, parse(yargs, this.command, ctx));
+ expParse.callArg(1);
+
+ assert.isTrue(ctx._args.called);
+ assert.isTrue(ctx.onParse.called);
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/commander/usage.spec.es6 b/test/lib/visitors/commander/usage.spec.es6
new file mode 100644
index 0000000..d55e1f0
--- /dev/null
+++ b/test/lib/visitors/commander/usage.spec.es6
@@ -0,0 +1,44 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import usage from 'visitors/commander/usage';
+import yargs from 'yargs';
+import chalk from 'chalk';
+
+describe('visitors.commander.Usage', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockYargs = this.sandbox.mock(yargs);
+ });
+
+ afterEach(() => {
+ this.mockYargs.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockYargs;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('usage()', () => {
+
+ it('Should apply usage directive to yargs', () => {
+ const expUsage = this.mockYargs.expects('usage')
+ .once()
+ .withArgs(chalk.white('sqbox [args]'))
+ .returns(yargs);
+
+ assert.equal(yargs, usage(yargs));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/commander/version.spec.es6 b/test/lib/visitors/commander/version.spec.es6
new file mode 100644
index 0000000..8b4566a
--- /dev/null
+++ b/test/lib/visitors/commander/version.spec.es6
@@ -0,0 +1,66 @@
+/**
+* @module visitors.commander
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import version from 'visitors/commander/version';
+import Command from 'command';
+import yargs from 'yargs';
+import logger, { Logger } from 'util/logger/logger';
+
+describe('visitors.commander.Version', function() {
+
+ before(() => {
+ this.command = Command.new();
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockYargs = this.sandbox.mock(yargs);
+ this.mockLogger = this.sandbox.mock(Logger.prototype);
+ });
+
+ afterEach(() => {
+ this.mockYargs.verify();
+ this.mockLogger.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockYargs;
+ delete this.mockLogger;
+ });
+
+ after(() => {
+ delete this.command;
+ delete this.sandbox;
+ });
+
+ describe('version()', () => {
+
+ it('Should apply version directive to yargs', () => {
+ const expVersion = this.mockYargs.expects('version')
+ .once()
+ .withArgs(sinon.match(/^[0-9]\.[0-9]\.[0-9]$/))
+ .returns(yargs);
+
+ assert.equal(yargs, version(yargs, this.command));
+ });
+
+ it('Should NOT apply version directive to yargs', () => {
+ this.command.dirname = './notexistent';
+ const expVersion = this.mockYargs.expects('version').never();
+ const expLogger = this.mockLogger.expects('_add')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(logger);
+
+ const expLoggerDebug = this.mockLogger.expects('debug')
+ .once()
+ .withArgs(sinon.match.func)
+ .returns(logger);
+
+ assert.equal(yargs, version(yargs, this.command));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration.spec.es6 b/test/lib/visitors/configuration.spec.es6
new file mode 100644
index 0000000..eadaa94
--- /dev/null
+++ b/test/lib/visitors/configuration.spec.es6
@@ -0,0 +1,357 @@
+/**
+* @module visitors
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Configuration from 'visitors/configuration';
+import Command from 'command';
+import Factory from 'util/factory/factory';
+import QueueAsync from 'util/adt/queue-async';
+import logger, { Logger } from 'util/logger/logger';
+
+describe('visitors.Configuration', function() {
+
+ before(() => {
+ this.command = Command.new();
+ if(!this.configuration) this.configuration = Configuration.new(this.command);
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(Configuration.prototype);
+ this.mockQueueAsync = this.sandbox.mock(QueueAsync.prototype);
+ this.mockFactory = this.sandbox.mock(Factory);
+ this.mockCommand = this.sandbox.mock(Command.prototype);
+ this.mockLogger = this.sandbox.mock(Logger.prototype);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.mockQueueAsync.verify();
+ this.mockFactory.verify();
+ this.mockCommand.verify();
+ this.mockLogger.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockProto;
+ delete this.mockQueueAsync;
+ delete this.mockFactory;
+ delete this.mockCommand;
+ delete this.mockLogger;
+ });
+
+ after(() => {
+ delete this.command;
+ delete this.configuration;
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ assert.instanceOf(this.configuration, Configuration);
+ assert.equal('ConfigurationVisitor', this.configuration.name);
+ assert.property(this.configuration, 'queue');
+ assert.instanceOf(this.configuration.queue, QueueAsync);
+ });
+
+ });
+
+ describe('visit()', () => {
+
+ it('Should visit configuration visitor', () => {
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(this.command)
+ .returns(true);
+
+ const exp = this.configuration.visit(this.command);
+ assert.instanceOf(exp, Command);
+ assert.property(exp, 'configuration');
+ });
+
+ it('Should NOT visit configuration visitor (invalid)', () => {
+ const input = Command.new();
+ const expValidate = this.mockProto.expects('validate')
+ .once()
+ .withArgs(input)
+ .returns(false);
+
+ const exp = this.configuration.visit(input);
+ assert.instanceOf(exp, Command);
+ assert.notProperty(exp, 'configuration');
+ });
+
+ });
+
+ describe('_create()', () => {
+
+ it('Should enqueue configuration decorator methods', () => {
+ const options = { option: true };
+ const expGet = this.mockFactory.expects('get')
+ .once()
+ .withArgs('visitors/configuration/remote')
+ .returns(options);
+
+ const expOffer = this.mockQueueAsync.expects('offer')
+ .once()
+ .withArgs(options)
+ .returns(this.configuration.queue);
+
+ const exp = this.configuration._create('visitors/configuration/remote');
+ assert.instanceOf(exp, Configuration);
+ });
+
+ });
+
+ describe('_format()', () => {
+
+ it('Should apply formatter to the current configuration option', () => {
+ const options = { scan: './src/**', extensions: '.js,.es6' };
+ const expTransform = ['.js', '.es6'];
+ const formatterPath = 'visitors/configuration/formatter/extensions';
+ const expFormatterPath = this.mockProto.expects('formatterPath')
+ .once()
+ .withArgs('extensions')
+ .returns(formatterPath);
+
+ const expGet = this.mockFactory.expects('get')
+ .once()
+ .withArgs(formatterPath, options.extensions)
+ .returns(expTransform);
+
+ const expExists = this.mockFactory.expects('exists')
+ .once()
+ .withArgs(formatterPath)
+ .returns(true);
+
+ const exp = this.configuration._format(options, options.extensions, 'extensions');
+ assert.isTrue(expFormatterPath.calledBefore(expExists));
+ assert.isTrue(expExists.calledBefore(expGet));
+ assert.isObject(exp);
+ assert.property(exp, 'scan');
+ assert.property(exp, 'extensions');
+ assert.equal(expTransform, exp.extensions);
+ });
+
+ it('Should NOT apply formatter to the current configuration option', () => {
+ const options = { scan: './src/**' };
+ const formatterPath = 'visitors/configuration/formatter/scan';
+ const expFormatterPath = this.mockProto.expects('formatterPath')
+ .once()
+ .withArgs('scan')
+ .returns(formatterPath);
+
+ const expGet = this.mockFactory.expects('get').never();
+
+ const expExists = this.mockFactory.expects('exists')
+ .once()
+ .withArgs(formatterPath)
+ .returns(false);
+
+ const exp = this.configuration._format(options, options.scan, 'scan');
+ assert.isTrue(expFormatterPath.calledBefore(expExists));
+ assert.isObject(exp);
+ assert.property(exp, 'scan');
+ assert.equal(options.scan, exp.scan);
+ });
+
+ });
+
+ describe('_source()', () => {
+
+ it('Should apply source configuration options to the current command', () => {
+ const options = { scan: './src/**', extensions: ['.js', '.es6'], unrecognized: true };
+ assert.instanceOf(this.configuration._source(options), Configuration);
+
+ assert.property(this.command, 'scan');
+ assert.property(this.command, 'extensions');
+ assert.notProperty(this.command, 'unrecognized');
+
+ delete this.command.scan;
+ delete this.command.extensions;
+ });
+
+ });
+
+ describe('_target()', () => {
+
+ it('Should apply target configuration options to the current command', () => {
+ const options = { cjs: { destination: './dist', format: 'cjs', unrecognized: false }, unrecognized: true };
+ assert.instanceOf(this.configuration._target(options), Configuration);
+
+ assert.property(this.command, 'target');
+ assert.property(this.command.target, 'cjs');
+ assert.notProperty(this.command.target.cjs, 'unrecognized');
+ });
+
+ });
+
+ describe('_logger()', () => {
+
+ it('Should apply logger configuration option to the singleton logger', () => {
+ assert.instanceOf(this.configuration._logger('silent'), Configuration);
+ logger.level(Logger.level.output);
+ });
+
+ });
+
+ describe('_override()', () => {
+
+ it('Should override configuration options with cli options', () => {
+ const options = { scan: './src/**', extensions: ['.js', '.es6'], unrecognized: true };
+ const filtered = _.omit(options, 'unrecognized');
+ const expFormat = this.mockProto.expects('_format')
+ .exactly(2)
+ .withArgs(filtered, sinon.match.any, sinon.match.string)
+ .returns(filtered);
+
+ assert.instanceOf(this.configuration._override(options), Configuration);
+ assert.property(this.command, 'scan');
+ assert.property(this.command, 'extensions');
+ assert.notProperty(this.command, 'unrecognized');
+
+ delete this.command.scan;
+ delete this.command.extensions;
+ });
+
+ });
+
+ describe('formatterPath()', () => {
+
+ it('Should resolve formatter path to the factory', () => {
+ const exp = 'visitors/configuration/formatter/target';
+ assert.equal(exp, this.configuration.formatterPath('target'));
+ });
+
+ });
+
+ describe('onParse()', () => {
+
+ it('Should iterate over results gather from commander to perform transformations', () => {
+ const expResults = [{ option: 1 }, { option: 2 }, null];
+ const expOnOptions = this.mockProto.expects('onOptions')
+ .exactly(2)
+ .withArgs(sinon.match.object)
+ .returns(this.configuration);
+
+ assert.instanceOf(this.configuration.onParse(expResults), Configuration);
+ });
+
+ });
+
+ describe('onParseError()', () => {
+
+ it('Should output possible warning', () => {
+ const expMessage = 'Warning Message';
+ const expLogger = this.mockLogger.expects('_add')
+ .once()
+ .withArgs(expMessage)
+ .returns(logger);
+
+ const expWarn = this.mockLogger.expects('warn')
+ .once()
+ .returns(logger);
+
+ assert.instanceOf(this.configuration.onParseError(expMessage), Configuration);
+ });
+
+ });
+
+ describe('onOptions()', () => {
+
+ it('Should perform options transformations', () => {
+ const expResult = {
+ source: { scan: './src/**' },
+ target: { cjs: { destination: './dist', format: 'cjs' } },
+ logLevel: 'silent'
+ };
+ const expSource = this.mockProto.expects('_source')
+ .once()
+ .withArgs(expResult.source)
+ .returns(this.configuration);
+ const expTarget = this.mockProto.expects('_target')
+ .once()
+ .withArgs(expResult.target)
+ .returns(this.configuration);
+ const expLogger = this.mockProto.expects('_logger')
+ .once()
+ .withArgs(expResult.logLevel)
+ .returns(this.configuration);
+ const expOverride = this.mockProto.expects('_override')
+ .once()
+ .withArgs(this.command.options)
+ .returns(this.configuration);
+
+ assert.instanceOf(this.configuration.onOptions(expResult), Configuration);
+ });
+
+ it('Should NOT perform option transformations (Parse Error)', () => {
+ const expResult = { warn: 'Warning Message' };
+
+ const expParseError = this.mockProto.expects('onParseError')
+ .once()
+ .withArgs(expResult.warn)
+ .returns(this.configuration);
+ const expSource = this.mockProto.expects('_source').never();
+ const expTarget = this.mockProto.expects('_target').never();
+ const expLogger = this.mockProto.expects('_logger').never();
+ const expOverride = this.mockProto.expects('_override').never();
+
+ assert.instanceOf(this.configuration.onOptions(expResult), Configuration);
+ });
+
+ });
+
+ describe('parse()', () => {
+
+ it('Should create methods and resolve configuration option source', () => {
+ const expResult = { source: { scan: './src/**' } };
+ const queueStubPromise = this.sandbox.stub().returnsPromise();
+ const expCreate = this.mockProto.expects('_create')
+ .exactly(2)
+ .withArgs(sinon.match.string)
+ .returns(this.configuration);
+
+ const expOnParse = this.mockProto.expects('onParse')
+ .once()
+ .withArgs(expResult)
+ .returns(this.configuration);
+
+ const expOnParseError = this.mockProto.expects('onParseError').never();
+
+ const expPoll = this.mockQueueAsync.expects('poll')
+ .once()
+ .returns(queueStubPromise.resolves(expResult)());
+
+ const exp = this.configuration.parse();
+ assert.isTrue(exp.resolved);
+ assert.instanceOf(exp.resolveValue, Configuration);
+ });
+
+ it('Should create methods and reject configuration option (warn message from metthod)', () => {
+ const expResult = { warn: 'Warning Message' };
+ let queueStubPromise = this.sandbox.stub().returnsPromise();
+ const expCreate = this.mockProto.expects('_create')
+ .exactly(2)
+ .withArgs(sinon.match.string)
+ .returns(this.configuration);
+
+ const expOnParse = this.mockProto.expects('onParse').never();
+
+ const expOnParseError = this.mockProto.expects('onParseError')
+ .once()
+ .withArgs(expResult.warn)
+ .returns(this.configuration);
+
+ const expPoll = this.mockQueueAsync.expects('poll')
+ .once()
+ .returns(queueStubPromise.rejects(expResult.warn)());
+
+ const exp = this.configuration.parse();
+ assert.instanceOf(exp.resolveValue, Configuration);
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration/formatter/alias.spec.es6 b/test/lib/visitors/configuration/formatter/alias.spec.es6
new file mode 100644
index 0000000..3cb2e9c
--- /dev/null
+++ b/test/lib/visitors/configuration/formatter/alias.spec.es6
@@ -0,0 +1,29 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import alias from 'visitors/configuration/formatter/alias';
+
+describe('visitors.configuration.formatter.Alias', function() {
+
+ describe('alias()', () => {
+
+ it('Should transform parameter alias', () => {
+ const input = 'one:./path/one,two:./path/two';
+ const exp = alias(input);
+
+ assert.isObject(exp);
+ assert.property(exp, 'one');
+ assert.property(exp, 'two');
+ assert.propertyVal(exp, 'one', './path/one');
+ assert.propertyVal(exp, 'two', './path/two');
+ });
+
+ it('Should NOT transform parameter alias', () => {
+ const exp = alias();
+ assert.isTrue(_.isEmpty(exp));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration/formatter/exclude.spec.es6 b/test/lib/visitors/configuration/formatter/exclude.spec.es6
new file mode 100644
index 0000000..5f2fa80
--- /dev/null
+++ b/test/lib/visitors/configuration/formatter/exclude.spec.es6
@@ -0,0 +1,30 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import exclude from 'visitors/configuration/formatter/exclude';
+
+describe('visitors.configuration.formatter.Exclude', function() {
+
+ describe('exclude()', () => {
+
+ it('Should transform parameter exclude', () => {
+ const input = './path/one,./path/two';
+ const exp = exclude(input);
+
+ assert.isArray(exp);
+ assert.oneOf('./path/one', exp);
+ assert.oneOf('./path/two', exp);
+ });
+
+ it('Should NOT transform parameter exclude', () => {
+ let exp = exclude();
+ assert.isTrue(_.isEmpty(exp));
+
+ exp = exclude({});
+ assert.isTrue(_.isEmpty(exp));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration/formatter/extensions.spec.es6 b/test/lib/visitors/configuration/formatter/extensions.spec.es6
new file mode 100644
index 0000000..6290b28
--- /dev/null
+++ b/test/lib/visitors/configuration/formatter/extensions.spec.es6
@@ -0,0 +1,31 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import extensions from 'visitors/configuration/formatter/extensions';
+
+describe('visitors.configuration.formatter.Extensions', function() {
+
+ describe('extensions()', () => {
+
+ it('Should transform parameter extensions', () => {
+ const input = '.js,.es6,.jsx';
+ const exp = extensions(input);
+
+ assert.isArray(exp);
+ assert.oneOf('.js', exp);
+ assert.oneOf('.es6', exp);
+ assert.oneOf('.jsx', exp);
+ });
+
+ it('Should NOT transform parameter extensions', () => {
+ let exp = extensions();
+ assert.isTrue(_.isEmpty(exp));
+
+ exp = extensions({});
+ assert.isTrue(_.isEmpty(exp));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration/formatter/target.spec.es6 b/test/lib/visitors/configuration/formatter/target.spec.es6
new file mode 100644
index 0000000..8de6fde
--- /dev/null
+++ b/test/lib/visitors/configuration/formatter/target.spec.es6
@@ -0,0 +1,50 @@
+/**
+* @module visitors.configuration.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import target from 'visitors/configuration/formatter/target';
+
+describe('visitors.configuration.formatter.Target', function() {
+
+ describe('target()', () => {
+
+ it('Should transform parameter target', () => {
+ const input = 't1>umd:./dist/t1,t2>cjs:./dist/t2,t3>inv:./dist/t3';
+ const exp = target(input);
+
+ assert.isObject(exp);
+ assert.property(exp, 't1');
+ assert.property(exp, 't2');
+ assert.notProperty(exp, 't3');
+
+ assert.property(exp.t1, 'destination');
+ assert.property(exp.t1, 'format');
+
+ assert.property(exp.t2, 'destination');
+ assert.property(exp.t2, 'format');
+ });
+
+ it('Should NOT transform ALL parameter targets (invalid nomenclature)', () => {
+ let input = 't1>umd:./dist/t1,t2:./dist/t2';
+ let exp = target(input);
+
+ assert.isObject(exp);
+ assert.property(exp, 't1');
+ assert.notProperty(exp, 't2');
+
+ input = 't1,t2>cjs:./dist/t2';
+ exp = target(input);
+
+ assert.isObject(exp);
+ assert.property(exp, 't2');
+ assert.notProperty(exp, 't1');
+ });
+
+ it('Should NOT transform parameter target', () => {
+ const exp = target();
+ assert.isTrue(_.isEmpty(exp));
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration/local.spec.es6 b/test/lib/visitors/configuration/local.spec.es6
new file mode 100644
index 0000000..1d58c44
--- /dev/null
+++ b/test/lib/visitors/configuration/local.spec.es6
@@ -0,0 +1,214 @@
+/**
+* @module visitors.configuration
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Local from 'visitors/configuration/local';
+import Command from 'command';
+import QueueAsync from 'util/adt/queue-async';
+import logger, { Logger } from 'util/logger/logger';
+
+describe('visitors.configuration.Local', function() {
+
+ before(() => {
+ this.local = null;
+ this.options = { config: 'test/specs/.sqboxrc' };
+ this.command = Command.new();
+ this.configPath = path.resolve(this.command.cwd, this.options.config);
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(Local.prototype);
+ this.mockCommand = this.sandbox.mock(Command.prototype);
+ this.mockLogger = this.sandbox.mock(Logger.prototype);
+ this.mockFs = this.sandbox.mock(fs);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.mockCommand.verify();
+ this.mockLogger.verify();
+ this.mockFs.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockProto;
+ delete this.mockCommand;
+ delete this.mockLogger;
+ delete this.mockFs;
+ });
+
+ after(() => {
+ delete this.local;
+ delete this.options;
+ delete this.command;
+ delete this.configPath;
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ this.local = Local.new(this.command);
+ assert.instanceOf(this.local, Local);
+ assert.property(this.local, 'command');
+ });
+
+ });
+
+ describe('resolvePath()', () => {
+
+ it('Should resolve absolute path to a file', () => {
+ const exp = this.local.resolvePath(this.options.config);
+ assert.equal(this.configPath, exp);
+ });
+
+ });
+
+ describe('exists()', () => {
+
+ it('Should return true: file exists', () => {
+ const expStatSync = this.mockFs.expects('statSync')
+ .once()
+ .withArgs(this.configPath)
+ .returns({});
+
+ assert.isTrue(this.local.exists(this.configPath));
+ });
+
+ it('Should return false: file doesn\'t exists (file is undefined)', () => {
+ const expStatSync = this.mockFs.expects('statSync').never();
+ assert.isFalse(this.local.exists());
+ });
+
+ it('Should return false: file doesn\'t exists (file not found in path)', () => {
+ const expStatSync = this.mockFs.expects('statSync')
+ .once()
+ .withArgs(this.configPath)
+ .throws(new Error('Not Found'));
+
+ assert.isFalse(this.local.exists(this.configPath));
+ });
+
+ });
+
+ describe('tryJson()', () => {
+
+ it('Should return an object: valid json', () => {
+ const expReadJsonSync = this.mockFs.expects('readJsonSync')
+ .once()
+ .withArgs(this.configPath)
+ .returns({ valid: true });
+ assert.isObject(this.local.tryJson(this.configPath));
+ });
+
+ it('Should return null: invalid json', () => {
+ const expReadJsonSync = this.mockFs.expects('readJsonSync')
+ .once()
+ .withArgs(this.configPath)
+ .throws(new Error('Invalid JSON'));
+ assert.isNull(this.local.tryJson(this.configPath));
+ });
+
+ });
+
+ describe('tryJs()', () => {
+
+ it('Should return an object: valid javascript', () => {
+ const jspath = path.resolve(this.command.cwd, 'test/specs/.sqbox.js');
+ assert.isObject(this.local.tryJs(jspath));
+ });
+
+ it('Should return null: invalid javascript (file not found)', () => {
+ const jspath = path.resolve(this.command.cwd, 'test/specs/.sqbox.ext');
+ assert.isNull(this.local.tryJs(jspath));
+ });
+
+ });
+
+ describe('load()', () => {
+
+ it('Should load configuration file', () => {
+ const input = { source: { scan: './src/**' } };
+ const expExists = this.mockProto.expects('exists')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(true);
+
+ const expTryJson = this.mockProto.expects('tryJson')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(input);
+
+ const exp = this.local.load(this.options.config);
+ assert.isObject(exp);
+ assert.deepEqual(input, exp);
+ });
+
+ it('Should NOT load configuration file (file doesn\'t exists)', () => {
+ const input = { warn: Local.messages.notFound };
+ const expExists = this.mockProto.expects('exists')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(false);
+
+ const expTryJson = this.mockProto.expects('tryJson').never();
+ const expTryJs = this.mockProto.expects('tryJs').never();
+
+ const exp = this.local.load(this.options.config);
+ assert.isObject(exp);
+ assert.deepEqual(input, exp);
+ });
+
+ it('Should NOT load configuration file (Invalid Json && Javascript)', () => {
+ const input = { warn: Local.messages.invalid };
+ const expExists = this.mockProto.expects('exists')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(true);
+
+ const expTryJson = this.mockProto.expects('tryJson')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(null);
+
+ const expTryJs = this.mockProto.expects('tryJs')
+ .once()
+ .withArgs(sinon.match.string)
+ .returns(null);
+
+ const exp = this.local.load(this.options.config);
+ assert.isObject(exp);
+ assert.deepEqual(input, exp);
+ });
+
+ });
+
+ describe('next()', () => {
+
+ it('Should resolve promise for asynchronous operation', (done) => {
+ const expResolved = { result: true };
+ const resolvePromise = this.sandbox.stub().returnsPromise();
+ const spyResolve = this.sandbox.spy();
+
+ const expGetOptions = this.mockCommand.expects('getOptions')
+ .once()
+ .returns(this.options);
+
+ const expLoad = this.mockProto.expects('load')
+ .once()
+ .withArgs(this.options.config)
+ .returns(expResolved);
+
+ const exp = this.local.next({}, _.bind(resolvePromise.resolves, resolvePromise), spyResolve)();
+ exp.then((result) => {
+ assert.isTrue(exp.resolved);
+ assert.deepEqual(expResolved, exp.resolveValue);
+ assert.isTrue(spyResolve.notCalled);
+ done();
+ });
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/configuration/remote.spec.es6 b/test/lib/visitors/configuration/remote.spec.es6
new file mode 100644
index 0000000..bd0a22d
--- /dev/null
+++ b/test/lib/visitors/configuration/remote.spec.es6
@@ -0,0 +1,174 @@
+/**
+* @module visitors.configuration
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import Remote from 'visitors/configuration/remote';
+import Command from 'command';
+import QueueAsync from 'util/adt/queue-async';
+import request from 'request-promise';
+import logger, { Logger } from 'util/logger/logger';
+
+describe('visitors.configuration.Remote', function() {
+
+ before(() => {
+ this.remote = null;
+ this.options = { url: 'http://squarebox.nahuel.io/.sqboxrc' };
+ this.command = Command.new();
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.mockProto = this.sandbox.mock(Remote.prototype);
+ this.mockCommand = this.sandbox.mock(Command.prototype);
+ this.mockLogger = this.sandbox.mock(Logger.prototype);
+ });
+
+ afterEach(() => {
+ this.mockProto.verify();
+ this.mockCommand.verify();
+ this.mockLogger.verify();
+
+ this.sandbox.restore();
+
+ delete this.mockProto;
+ delete this.mockCommand;
+ delete this.mockLogger;
+ });
+
+ after(() => {
+ delete this.remote;
+ delete this.options;
+ delete this.command;
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ this.remote = Remote.new(this.command);
+ assert.instanceOf(this.remote, Remote);
+ assert.property(this.remote, 'command');
+ });
+
+ });
+
+ describe('onResponse()', () => {
+
+ it('Should resolve promise successfuly', (done) => {
+ const output = { option: true };
+ const expPromise = this.sandbox.stub().returnsPromise();
+ const bindResolves = _.bind(expPromise.resolves, expPromise);
+ const bindRejects = _.bind(expPromise.rejects, expPromise);
+
+ const exp = this.remote.onResponse(bindResolves, bindRejects, output)();
+ exp.then((result) => {
+ assert.equal(output, result);
+ done();
+ });
+ });
+
+ });
+
+ describe('onResponseError()', () => {
+
+ it('Should resolve promise with Error', () => {
+ const err = 'Remote Error';
+ const output = { warn: Remote.messages.error({ err }) };
+ const expPromise = this.sandbox.stub().returnsPromise();
+ const bindResolves = _.bind(expPromise.resolves, expPromise);
+ const bindRejects = _.bind(expPromise.rejects, expPromise);
+
+ const exp = this.remote.onResponseError(bindResolves, bindRejects, err)();
+ exp.then((result) => {
+ assert.deepEqual(output, result);
+ done();
+ });
+ });
+
+ });
+
+ describe('load()', () => {
+
+ it('Should execute a remote request (resolves)', () => {
+ const output = { option: true };
+ const expPromise = this.sandbox.stub(request, 'get').returnsPromise();
+
+ const spyInnerResolve = this.sandbox.spy();
+ const spyInnerReject = this.sandbox.spy();
+
+ const expOnResponseError = this.mockProto.expects('onResponseError').never();
+ const expOnResponse = this.mockProto.expects('onResponse')
+ .once()
+ .withArgs(spyInnerResolve, spyInnerReject, output)
+ .returns(spyInnerResolve(output));
+
+ const exp = this.remote.load(this.options.url, spyInnerResolve, spyInnerReject);
+ expPromise.resolves(output);
+
+ assert.isTrue(spyInnerResolve.called);
+ assert.isFalse(spyInnerReject.called);
+ });
+
+ it('Should execute a remote request (rejects)', () => {
+ const output = 'Remote Error';
+ const expPromise = this.sandbox.stub(request, 'get').returnsPromise();
+
+ const spyInnerResolve = this.sandbox.spy();
+ const spyInnerReject = this.sandbox.spy();
+
+ const expOnResponse = this.mockProto.expects('onResponse').never();
+ const expOnResponseError = this.mockProto.expects('onResponseError')
+ .once()
+ .withArgs(spyInnerResolve, spyInnerReject, output)
+ .returns(spyInnerResolve(output));
+
+ const exp = this.remote.load(this.options.url, spyInnerResolve, spyInnerReject);
+ expPromise.rejects(output);
+
+ assert.isTrue(spyInnerResolve.called);
+ assert.isFalse(spyInnerReject.called);
+ });
+
+ });
+
+ describe('next()', () => {
+
+ it('Should resolve promise with not warning (Url available)', () => {
+ const output = { option: true };
+ const spyResolve = this.sandbox.spy();
+ const spyReject = this.sandbox.spy();
+
+ const expGetOptions = this.mockCommand.expects('getOptions')
+ .once()
+ .returns(this.options);
+
+ const expLoad = this.mockProto.expects('load')
+ .once()
+ .withArgs(this.options.url, spyResolve, spyReject)
+ .returns(spyResolve(output));
+
+ const exp = this.remote.next({}, spyResolve, spyReject);
+
+ assert.equal(output.option, spyResolve.firstCall.args[0].option);
+ assert.isTrue(spyResolve.called);
+ assert.isFalse(spyReject.called);
+ });
+
+ it('Should resolve promise with warning (No Url available)', () => {
+ const spyResolve = this.sandbox.spy();
+ const spyReject = this.sandbox.spy();
+
+ const expGetOptions = this.mockCommand.expects('getOptions')
+ .once()
+ .returns({});
+
+ const exp = this.remote.next({}, spyResolve, spyReject);
+
+ assert.equal(Remote.messages.noUrl, spyResolve.firstCall.args[0].warn);
+ assert.isTrue(spyResolve.called);
+ assert.isFalse(spyReject.called);
+ });
+
+ });
+
+});
diff --git a/test/lib/visitors/formatter/json.spec.es6 b/test/lib/visitors/formatter/json.spec.es6
new file mode 100644
index 0000000..c45594e
--- /dev/null
+++ b/test/lib/visitors/formatter/json.spec.es6
@@ -0,0 +1,86 @@
+/**
+* @module visitors.formatter
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+import _ from 'underscore';
+import Json from 'visitors/formatter/json';
+import Visited from 'util/visitor/visited';
+import Command from 'command';
+import InterfaceException from 'util/exception/proxy/interface';
+
+describe('visitors.formatter.Json', function() {
+
+ before(() => {
+ this.sandbox = sinon.sandbox.create();
+ });
+
+ beforeEach(() => {
+ this.target = () => {
+ return {
+ a: 1,
+ b: true,
+ regexp: new RegExp(),
+ func: () => {},
+ obj: { o_a: 1, o_b: true, func: () => {}, arr: [1, true, () => {}] },
+ arr: [{
+ a_o_a: 1,
+ a_o_b: true,
+ func: () => {},
+ command: Command.new()
+ }, 1, true, () => {}]
+ };
+ };
+ this.mockJson = this.sandbox.mock(Json.prototype);
+ });
+
+ afterEach(() => {
+ this.mockJson.verify();
+ this.sandbox.restore();
+ delete this.target;
+ delete this.mockJson;
+ });
+
+ after(() => {
+ delete this.sandbox;
+ });
+
+ describe('constructor()', () => {
+
+ it('Should get a new instance', () => {
+ const exp = Json.new();
+ assert.instanceOf(exp, Json);
+ assert.equal('JsonVisitor', exp.name);
+ });
+
+ });
+
+ describe('visit()', () => {
+
+ it('Should visit the target', () => {
+ const input = Visited.new({ property: 'target' });
+ const exp = Json.new();
+ assert.instanceOf(exp.visit(input), Visited);
+ });
+
+ it('Should NOT visit the target', () => {
+ const input = { property: 'target' };
+ const exp = Json.new();
+ assert.throws(() => exp.visit(input), InterfaceException.type
+ .interface({ name: 'util.visitor.Visited' }));
+ });
+
+ });
+
+ describe('toJSON()', () => {
+
+ it('Should return a json representation', () => {
+ const input = this.target();
+ const visited = Visited.new(input);
+ const exp = Json.new().visit(visited);
+ const out = exp.toJSON();
+ assert.notDeepEqual(input, out);
+ });
+
+ });
+
+});
diff --git a/test/specs/.sqbox.js b/test/specs/.sqbox.js
new file mode 100644
index 0000000..d78e20d
--- /dev/null
+++ b/test/specs/.sqbox.js
@@ -0,0 +1,34 @@
+/**
+* Config Example using module.exports
+* @author Patricio Ferreira <3dimentionar@gmail.com>
+**/
+module.exports = {
+ source: {
+ scan: './src/**',
+ exclude: ['./src/dependencies/**'],
+ extensions: ['.js', '.es6', '.es'],
+ alias: {
+ common: 'shared/common',
+ libraries: 'libs'
+ }
+ },
+ target: {
+ global: {
+ destination: './dist/global',
+ format: 'ifie'
+ },
+ umd: {
+ destination: './dist/umd',
+ format: 'umd'
+ },
+ cjs: {
+ destination: './dist/cjs',
+ format: 'cjs'
+ },
+ amd: {
+ destination: './dist/amd',
+ format: 'amd'
+ }
+ },
+ logLevel: 'debug'
+};
diff --git a/test/specs/.sqboxrc b/test/specs/.sqboxrc
new file mode 100644
index 0000000..2d6e97e
--- /dev/null
+++ b/test/specs/.sqboxrc
@@ -0,0 +1,30 @@
+{
+ "source": {
+ "scan": "./src/**",
+ "exclude": ["./src/dependencies/**"],
+ "extensions": [".js", ".es6"],
+ "alias": {
+ "common": "shared/common",
+ "libraries": "libs"
+ }
+ },
+ "target": {
+ "global": {
+ "destination": "./dist/global",
+ "format": "ifie"
+ },
+ "umd": {
+ "destination": "./dist/umd",
+ "format": "umd"
+ },
+ "cjs": {
+ "destination": "./dist/cjs",
+ "format": "cjs"
+ },
+ "amd": {
+ "destination": "./dist/amd",
+ "format": "amd"
+ }
+ },
+ "logLevel": "debug"
+}
diff --git a/test/specs/config-basic.json b/test/specs/config-basic.json
deleted file mode 100644
index 06b89cc..0000000
--- a/test/specs/config-basic.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "source": {
- "scan": "./src/**",
- "extensions": [".js", ".es6"],
- "alias": {
- "common": "shared/common",
- "libraries": "libs"
- }
- },
- "target": {
- "destination": "./dist",
- "format": "ifie"
- }
-}