├── .gitignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── README.md ├── build └── release.min.js ├── package.json ├── src ├── banner.txt └── core.js └── test └── core.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac. 2 | .DS_STORE 3 | 4 | # Node. 5 | node_modules 6 | npm-debug.log 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": {}, 3 | 4 | "browser": false, 5 | "couch": false, 6 | "devel": false, 7 | "dojo": false, 8 | "jquery": false, 9 | "mootools": false, 10 | "node": false, 11 | "nonstandard": false, 12 | "prototypejs": false, 13 | "rhino": false, 14 | "worker": false, 15 | "wsh": false, 16 | "yui": false, 17 | 18 | "nomen": true, 19 | "onevar": false, 20 | "passfail": false, 21 | "white": true, 22 | 23 | "asi": false, 24 | "boss" : true, 25 | "debug": false, 26 | "eqnull": false, 27 | "esnext": true, 28 | "evil": false, 29 | "expr": false, 30 | "funcscope": false, 31 | "globalstrict": false, 32 | "iterator": false, 33 | "lastsemic": false, 34 | "laxbreak": false, 35 | "laxcomma": false, 36 | "loopfunc": false, 37 | "moz": false, 38 | "multistr": false, 39 | "proto": false, 40 | "scripturl": false, 41 | "smarttabs": false, 42 | "sub": true, 43 | "supernew": false, 44 | "validthis": false, 45 | 46 | "bitwise": true, 47 | "browser": true, 48 | "camelcase": true, 49 | "curly": true, 50 | "devel": false, 51 | "eqeqeq": true, 52 | "es3": false, 53 | "forin": true, 54 | "immed": true, 55 | "indent": 4, 56 | "latedef": true, 57 | "maxcomplexity": 100, 58 | "maxdepth": 10, 59 | "maxlen": 300, 60 | "maxparams": 6, 61 | "maxstatements": 30, 62 | "newcap": true, 63 | "noarg": true, 64 | "noempty": true, 65 | "nonew": true, 66 | "plusplus": true, 67 | "quotmark": "single", 68 | "shadow": true, 69 | "strict": true, 70 | "trailing": true, 71 | "undef": true, 72 | "unused": true 73 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: "0.11" -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | module.exports = function (grunt) { 3 | 'use strict'; 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | pkg: grunt.file.readJSON('package.json'), 8 | uglify: { 9 | options: { 10 | banner: grunt.file.read('src/banner.txt') + '\n' 11 | }, 12 | build: { 13 | src: 'build/release.min.js', 14 | dest: 'build/release.min.js' 15 | } 16 | }, 17 | concat: { 18 | options: { 19 | separator: ';' 20 | }, 21 | dist: { 22 | src: ['src/**/*.js'], 23 | dest: 'build/release.min.js' 24 | } 25 | }, 26 | jshint: { 27 | files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'], 28 | options: { 29 | jshintrc: '.jshintrc' 30 | } 31 | }, 32 | simplemocha: { 33 | options: { 34 | globals: ['chai'], 35 | timeout: 3000, 36 | ignoreLeaks: false, 37 | ui: 'bdd', 38 | reporter: 'spec' 39 | }, 40 | all: { src: ['test/**/*.js'] } 41 | } 42 | }); 43 | 44 | // Load the required plugins. 45 | grunt.loadNpmTasks('grunt-contrib-uglify'); 46 | grunt.loadNpmTasks('grunt-contrib-concat'); 47 | grunt.loadNpmTasks('grunt-contrib-jshint'); 48 | grunt.loadNpmTasks('grunt-simple-mocha'); 49 | 50 | // Tasks. 51 | grunt.registerTask('default', ['simplemocha', 'jshint', 'concat', 'uglify']); 52 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Graph](https://www.github.com/ryansmith94/Graph) 2 | A micro-library that provides "jQuery-like" functionality for graph structures in JavaScript. 3 | 4 | [![Build Status](https://travis-ci.org/ryansmith94/Graph.png)](https://travis-ci.org/ryansmith94/Graph) 5 | 6 | **License** 7 | This work is licensed under a [Attribution-NonCommercial-ShareAlike 4.0 International 8 | license](https://gist.githubusercontent.com/ryansmith94/b947ee33d7bfffff9d16/raw/bcd4b00739543c4a215a1f60538d899e2c22cdfd/BY-NC-SA.txt). 9 | 10 | 11 | **Contributing** 12 | Please make contributions by [forking](https://github.com/ryansmith94/Graph/fork "/fork") the project and creating a [pull-request](https://github.com/ryansmith94/Graph/pull/new/master "/pull-request"). Other contributions include maintaining the [Wiki](https://github.com/ryansmith94/Graph/wiki "/wiki") and [issues](https://github.com/ryansmith94/Graph/issues?state=open "/issues"). 13 | 14 | # Documentation 15 | ## 1 Installation 16 | ### 1.1 Browser 17 | Reference the [raw Github version](https://raw.github.com/ryansmith94/Graph/master/build/release.min.js) of [release.min.js](https://www.github.com/ryansmith94/Graph/blob/master/build/release.min.js) in your code. 18 | 19 | Graph is compatible with requireJS and can be used by wrapping your code in the following block: 20 | ```JavaScript 21 | require(['graph'], function (graph) { 22 | // Your code. 23 | }); 24 | ``` 25 | 26 | ### 1.2 Node 27 | Graph is also available as a node package called "micro-graph". You can install it to your local repository using `npm install micro-graph --save-dev` and you can use the library with node by using `var graph = require("micro-graph").graph;` in your JavaScript file. 28 | 29 | ### 1.3 Versioning 30 | This project is maintained under the [semantic versioning guidlines](http://semver.org/). This means that releases will have the following format `..`. 31 | * Breaking backward compatibility bumps the major (and resets the minor and patch). 32 | * New additions without breaking backward compatibility bumps the minor (and resets the patch). 33 | * Bug fixes and misc changes bumps the patch. 34 | 35 | ## 2 Getting Started 36 | To create a new graph, use the global "graph" function. 37 | ```JavaScript 38 | graph(nodes[, options]); 39 | ``` 40 | 41 | **Arguments** 42 | * {Array} nodes: An array of nodes. 43 | * {Object} options: A object containing options that change the configuration of the graph. 44 | * {String} parentsKey: Sets the key to be used for finding parents. Defaults to `"parents"`. 45 | * {String} childrenKey: Sets the key to be used for finding children. Defaults to `"children"`. 46 | 47 | **Returns** 48 | {Object} graph: A structure that can be manipulated like a graph. 49 | 50 | ## 3 Methods 51 | ### 3.1 children 52 | Finds the descendants of the nodes in the graph. 53 | ```JavaScript 54 | graph(nodes, options).children([fn, generations]); 55 | ``` 56 | 57 | **Arguments** 58 | * {Function} fn: A function that returns true for nodes that should be returned. 59 | * {Number} generations: The number of generations to search through (search depth). 60 | 61 | **Returns** 62 | {Object} graph: A structure that can be manipulated like a graph. 63 | 64 | ### 3.2 parents 65 | Finds the ancestors of the nodes in the graph. 66 | ```JavaScript 67 | graph(nodes, options).parents([fn, generations]); 68 | ``` 69 | 70 | **Arguments** 71 | * {Function} fn: A function that returns true for nodes that should be returned. 72 | * {Number} generations: The number of generations to search through (search depth). 73 | 74 | **Returns** 75 | {Object} graph: A structure that can be manipulated like a graph. 76 | 77 | ### 3.3 addChildren 78 | Adds children to the nodes in the graph. 79 | ```JavaScript 80 | graph(nodes, options).addChildren(children); 81 | ``` 82 | 83 | **Arguments** 84 | * {Array} children: An array of nodes to be added as children. 85 | 86 | **Returns** 87 | {Object} graph: A structure that can be manipulated like a graph. 88 | 89 | ### 3.4 addParents 90 | Adds parents to the nodes in the graph. 91 | ```JavaScript 92 | graph(nodes, options).addParents(parents); 93 | ``` 94 | 95 | **Arguments** 96 | * {Array} parents: An array of nodes to be added as parents. 97 | 98 | **Returns** 99 | {Object} graph: A structure that can be manipulated like a graph. 100 | 101 | ### 3.5 removeChildren 102 | Removes children from the nodes in the graph. 103 | ```JavaScript 104 | graph(nodes, options).removeChildren([children]); 105 | ``` 106 | 107 | **Arguments** 108 | * {Array} children: An array of nodes to be removed as children. 109 | 110 | **Returns** 111 | {Object} graph: A structure that can be manipulated like a graph. 112 | 113 | ### 3.6 removeParents 114 | Removes parents from the nodes in the graph. 115 | ```JavaScript 116 | graph(nodes, options).removeParents([parents]); 117 | ``` 118 | 119 | **Arguments** 120 | * {Array} parents: An array of nodes to be removed as parents. 121 | 122 | **Returns** 123 | {Object} graph: A structure that can be manipulated like a graph. 124 | 125 | ### 3.7 filter 126 | Filters out nodes in graph. 127 | ```JavaScript 128 | graph(nodes, options).filter([fn]); 129 | ``` 130 | 131 | **Arguments** 132 | * {Function} fn: A function that returns true for nodes that should be returned. 133 | 134 | **Returns** 135 | {Object} graph: A structure that can be manipulated like a graph. 136 | 137 | ### 3.8 nodes 138 | Returns the array of nodes in the graph. 139 | ```JavaScript 140 | graph(nodes, options).nodes(); 141 | ``` 142 | 143 | **Arguments** 144 | None. 145 | 146 | **Returns** 147 | {Array} nodes: An array of nodes. 148 | -------------------------------------------------------------------------------- /build/release.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * micro-graph. 3 | * https://www.github.com/ryansmith94/Graph 4 | * 5 | * Copyright 2013, ryansmith94 and contributors. 6 | * Released under the MIT license. 7 | * https://github.com/ryansmith94/Graph/blob/master/license.md 8 | * 9 | * Version 1.0.3. 10 | */ 11 | !function(a){"use strict";var b={parentsKey:"parents",childrenKey:"children"},c=function(a,b,c){return a.forEach(function(a){a[b]=a[b].concat(c)}),a},d=function(a,b,c,d){return function e(a,d){var f=[],g=d>0||void 0===d?e:function(){return[]};return a.forEach(function(a){f=a[b].filter(c),f=f.concat(g(a[b],d&&d-1))}),f}(a,d)},e=function(a,b,c){return a.forEach(function(a){c.forEach(function(c){a[b].splice(a[b].indexOf(c),1)})}),a},f=function(a){return"[object Array]"!==Object.prototype.toString.call(a)?[a]:a},g=function(){return!0},h=function(a,i){var j=this;return a=f(a),i=i||b,b=i,j.children=function(b,c){return new h(d(a,i.childrenKey,b||g,c),i)},j.parents=function(b,c){return new h(d(a,i.parentsKey,b||g,c),i)},j.addChildren=function(b){return c(a,i.childrenKey,c(f(b),i.parentsKey,a)),j},j.addParents=function(b){return c(a,i.parentsKey,c(f(b),i.childrenKey,a)),j},j.removeChildren=function(b){return e(a,i.childrenKey,e(f(b),i.parentsKey,a)),j},j.removeParents=function(b){return e(a,i.parentsKey,e(f(b),i.childrenKey,a)),j},j.filter=function(b){return new h(a.filter(b||g),i)},j.nodes=function(){return a},j},i=function(a,b){return new h(a,b)};"function"==typeof a.define?a.define("graph",[],function(){return i}):a.graph=i}(this); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro-graph", 3 | "author": "ryansmith94", 4 | "homepage": "https://www.github.com/ryansmith94/Graph", 5 | "description": "A micro-library that provides \"jQuery-like\" functionality for graph structures in JavaScript.", 6 | "keywords": [ 7 | "graph", 8 | "traversal" 9 | ], 10 | "bugs": { 11 | "url": "http://github.com/ryansmith94/Graph/issues" 12 | }, 13 | "version": "1.0.3", 14 | "licence": { 15 | "type": "MIT", 16 | "url": "https://github.com/ryansmith94/Graph/blob/master/license.md" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/ryansmith94/Graph.git" 21 | }, 22 | "devDependencies": { 23 | "grunt": "~0.4.1", 24 | "grunt-contrib-jshint": "~0.5.4", 25 | "grunt-contrib-uglify": "~0.2.1", 26 | "grunt-contrib-concat": "~0.3.0", 27 | "grunt-cli": "~0.1.8", 28 | "grunt-simple-mocha": "~0.4.0", 29 | "chai": "~1.7.2", 30 | "mocha": "~1.12.1" 31 | }, 32 | "scripts": { 33 | "test": "grunt" 34 | }, 35 | "engines": { 36 | "node": ">= 0.8.0" 37 | }, 38 | "readmeFilename": "readme.md", 39 | "main": "./build/release.min.js", 40 | "directories": { 41 | "build": "build" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/banner.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * <%= pkg.name %>. 3 | * <%= pkg.homepage %> 4 | * 5 | * Copyright <%= grunt.template.today('yyyy') %>, <%= pkg.author %> and contributors. 6 | * Released under the <%= pkg.licence.type %> license. 7 | * <%= pkg.licence.url %> 8 | * 9 | * Version <%= pkg.version %>. 10 | */ -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | (function (scope) { 2 | 'use strict'; 3 | var config = { 4 | parentsKey: 'parents', 5 | childrenKey: 'children' 6 | }; 7 | 8 | var add = function (nodes, relation, relatedNodes) { 9 | nodes.forEach(function (node) { 10 | node[relation] = node[relation].concat(relatedNodes); 11 | }); 12 | return nodes; 13 | }; 14 | 15 | var get = function (nodes, relation, fn, generations) { 16 | return (function get(nodes, generations) { 17 | var filtered = []; 18 | var nextGen = (generations > 0 || generations === undefined) ? get : function () {return []; }; 19 | 20 | nodes.forEach(function (node) { 21 | filtered = node[relation].filter(fn); 22 | filtered = filtered.concat(nextGen(node[relation], generations && generations - 1)); 23 | }); 24 | 25 | return filtered; 26 | }(nodes, generations)); 27 | }; 28 | 29 | var remove = function (nodes, relation, relatedNodes) { 30 | nodes.forEach(function (node) { 31 | relatedNodes.forEach(function (relatedNode) { 32 | node[relation].splice(node[relation].indexOf(relatedNode), 1); 33 | }); 34 | }); 35 | return nodes; 36 | }; 37 | 38 | var toNodes = function (nodes) { 39 | return (Object.prototype.toString.call(nodes) !== '[object Array]') ? [nodes] : nodes; 40 | }; 41 | 42 | var trueFn = function () { 43 | return true; 44 | }; 45 | 46 | var Graph = function (nodes, options) { 47 | var self = this; 48 | 49 | nodes = toNodes(nodes); 50 | options = options || config; 51 | config = options; 52 | 53 | self.children = function (fn, generations) { 54 | return new Graph(get(nodes, options.childrenKey, fn || trueFn, generations), options); 55 | }; 56 | self.parents = function (fn, generations) { 57 | return new Graph(get(nodes, options.parentsKey, fn || trueFn, generations), options); 58 | }; 59 | self.addChildren = function (children) { 60 | add(nodes, options.childrenKey, add(toNodes(children), options.parentsKey, nodes)); 61 | return self; 62 | }; 63 | self.addParents = function (parents) { 64 | add(nodes, options.parentsKey, add(toNodes(parents), options.childrenKey, nodes)); 65 | return self; 66 | }; 67 | self.removeChildren = function (children) { 68 | remove(nodes, options.childrenKey, remove(toNodes(children), options.parentsKey, nodes)); 69 | return self; 70 | }; 71 | self.removeParents = function (parents) { 72 | remove(nodes, options.parentsKey, remove(toNodes(parents), options.childrenKey, nodes)); 73 | return self; 74 | }; 75 | self.filter = function (fn) { 76 | return new Graph(nodes.filter(fn || trueFn), options); 77 | }; 78 | self.nodes = function () { 79 | return nodes; 80 | }; 81 | 82 | return self; 83 | }; 84 | 85 | var constructor = function (nodes, options) { 86 | return new Graph(nodes, options); 87 | }; 88 | 89 | // AMD Support. 90 | if (typeof scope.define === 'function') { 91 | scope.define('graph', [], function () { 92 | return constructor; 93 | }); 94 | } else { 95 | scope.graph = constructor; 96 | } 97 | }(this)); -------------------------------------------------------------------------------- /test/core.js: -------------------------------------------------------------------------------- 1 | /* jshint maxstatements: false, node: true */ 2 | /* global describe */ 3 | /* global it */ 4 | /* global before */ 5 | (function () { 6 | 'use strict'; 7 | var expect = require('chai').expect; 8 | 9 | describe('graph()', function () { 10 | var graph; 11 | var testConfig = { 12 | parentsKey: 'a', 13 | childrenKey: 'b' 14 | }; 15 | 16 | // Tests that all graph functions are exposed. 17 | var testGraph = function (fn) { 18 | var functions = ['addChildren', 'addParents', 'removeChildren', 'removeParents', 'children', 'parents', 'filter', 'nodes']; 19 | var testFnExposure = function (name) { 20 | it('should expose a function called ' + name, function () { 21 | expect(fn()[name]).to.be.a('function'); 22 | }); 23 | }; 24 | 25 | functions.forEach(function (name) { 26 | testFnExposure(name); 27 | }); 28 | }; 29 | 30 | // Creates a new node. 31 | var createNode = function (name) { 32 | return { 33 | a: [], 34 | b: [], 35 | name: name 36 | }; 37 | }; 38 | 39 | // Prepares 2 parent and 2 child nodes. 40 | var prepare = function (parents, children, init) { 41 | parents[0] = createNode('a'); 42 | parents[1] = createNode('b'); 43 | children[0] = createNode('c'); 44 | children[1] = createNode('d'); 45 | if (init) { 46 | parents[0][testConfig['childrenKey']] = [children[0], children[1]]; 47 | parents[1][testConfig['childrenKey']] = [children[0], children[1]]; 48 | children[0][testConfig['parentsKey']] = [parents[0], parents[1]]; 49 | children[1][testConfig['parentsKey']] = [parents[0], parents[1]]; 50 | } 51 | }; 52 | 53 | before(function () { 54 | graph = require('../src/core.js').graph; 55 | expect(graph).to.be.a('function'); 56 | }); 57 | 58 | testGraph(function () { 59 | return graph(); 60 | }); 61 | 62 | // Test change functions (add/remove). 63 | (function () { 64 | var testChangeFn = function (fnName, init, toStart, toUse, checkFn) { 65 | describe(fnName + '()', function () { 66 | var myGraph; 67 | var nodes = { 68 | parents: [], 69 | children: [] 70 | }; 71 | 72 | before(function () { 73 | prepare(nodes.parents, nodes.children, init); 74 | myGraph = graph(nodes[toStart], testConfig); 75 | myGraph[fnName](nodes[toUse]); 76 | }); 77 | 78 | it('should update children', function () { 79 | checkFn(nodes.parents[0], nodes.children[0], 'childrenKey'); 80 | checkFn(nodes.parents[0], nodes.children[1], 'childrenKey'); 81 | checkFn(nodes.parents[1], nodes.children[0], 'childrenKey'); 82 | checkFn(nodes.parents[1], nodes.children[1], 'childrenKey'); 83 | }); 84 | 85 | it('should update parents', function () { 86 | checkFn(nodes.children[0], nodes.parents[0], 'parentsKey'); 87 | checkFn(nodes.children[0], nodes.parents[1], 'parentsKey'); 88 | checkFn(nodes.children[1], nodes.parents[0], 'parentsKey'); 89 | checkFn(nodes.children[1], nodes.parents[1], 'parentsKey'); 90 | }); 91 | 92 | it('should return the current graph', function () { 93 | var myGraph; 94 | var nodes = { 95 | parents: [], 96 | children: [] 97 | }; 98 | prepare(nodes.parents, nodes.children, init); 99 | myGraph = graph(nodes[toStart], testConfig); 100 | expect(myGraph[fnName](nodes[toUse])).to.equal(myGraph); 101 | }); 102 | }); 103 | }; 104 | 105 | // Test add functions. 106 | (function () { 107 | var checkFn = function (a, b, key) { 108 | expect(a[testConfig[key]].indexOf(b)).to.not.equal(-1); 109 | }; 110 | testChangeFn('addChildren', false, 'parents', 'children', checkFn); 111 | testChangeFn('addParents', false, 'children', 'parents', checkFn); 112 | }()); 113 | 114 | // Test remove functions. 115 | (function () { 116 | var checkFn = function (a, b, key) { 117 | expect(a[testConfig[key]].indexOf(b)).to.equal(-1); 118 | }; 119 | testChangeFn('removeChildren', true, 'parents', 'children', checkFn); 120 | testChangeFn('removeParents', true, 'children', 'parents', checkFn); 121 | }()); 122 | }()); 123 | 124 | // Test family functions (children/parents). 125 | (function () { 126 | var testFamilyFn = function (needle, haystack) { 127 | describe(needle + '()', function () { 128 | var myGraph; 129 | var nodes = { 130 | parents: [], 131 | children: [] 132 | }; 133 | 134 | before(function () { 135 | prepare(nodes.parents, nodes.children, true); 136 | myGraph = graph(nodes[haystack]); 137 | }); 138 | 139 | it('should contain the correct nodes', function () { 140 | var myNodes = myGraph[needle]().nodes(); 141 | expect(myNodes.indexOf(nodes[needle][0])).to.not.equal(-1); 142 | expect(myNodes.indexOf(nodes[needle][1])).to.not.equal(-1); 143 | expect(myNodes.length).to.equal(2); 144 | }); 145 | 146 | testGraph(function () { 147 | return myGraph[needle](); 148 | }); 149 | }); 150 | }; 151 | 152 | testFamilyFn('children', 'parents'); 153 | testFamilyFn('parents', 'children'); 154 | }()); 155 | 156 | describe('filter()', function () { 157 | var myGraph; 158 | var nodes = { 159 | parents: [], 160 | children: [] 161 | }; 162 | 163 | before(function () { 164 | prepare(nodes.parents, nodes.children, true); 165 | myGraph = graph(nodes['parents']); 166 | }); 167 | 168 | it('should contain the correct nodes', function () { 169 | var myNodes = myGraph.filter(function (node) { 170 | if (node.name === 'b') { 171 | return true; 172 | } 173 | return false; 174 | }).nodes(); 175 | expect(myNodes.indexOf(nodes['parents'][0])).to.equal(-1); 176 | expect(myNodes.indexOf(nodes['parents'][1])).to.not.equal(-1); 177 | expect(myNodes.indexOf(nodes['children'][0])).to.equal(-1); 178 | expect(myNodes.indexOf(nodes['children'][1])).to.equal(-1); 179 | expect(myNodes.length).to.equal(1); 180 | }); 181 | 182 | testGraph(function () { 183 | return myGraph['filter'](); 184 | }); 185 | }); 186 | 187 | describe('nodes()', function () { 188 | var myGraph; 189 | var nodes = { 190 | parents: [], 191 | children: [] 192 | }; 193 | 194 | before(function () { 195 | prepare(nodes.parents, nodes.children, true); 196 | myGraph = graph(nodes['parents']); 197 | }); 198 | 199 | it('should return the correct nodes', function () { 200 | var myNodes = myGraph.nodes(); 201 | expect(myNodes.indexOf(nodes['parents'][0])).to.not.equal(-1); 202 | expect(myNodes.indexOf(nodes['parents'][1])).to.not.equal(-1); 203 | expect(myNodes.indexOf(nodes['children'][0])).to.equal(-1); 204 | expect(myNodes.indexOf(nodes['children'][1])).to.equal(-1); 205 | }); 206 | }); 207 | }); 208 | }()); --------------------------------------------------------------------------------