diff options
author | Garren Smith <garren.smith@gmail.com> | 2014-02-19 15:23:52 +0200 |
---|---|---|
committer | Garren Smith <garren.smith@gmail.com> | 2014-03-03 11:18:42 +0200 |
commit | f796b38ef2474bdfed5303a627136ee3ad8ea8d2 (patch) | |
tree | 54ecfbbd47c3bd4260295d6af83e844fa4014747 | |
parent | 4e10385a13e335d00f5b342e32ac6964fbb6634f (diff) | |
download | couchdb-f796b38ef2474bdfed5303a627136ee3ad8ea8d2.tar.gz |
Add new pagination algorithmn
-rw-r--r-- | src/fauxton/app/addons/documents/resources.js | 54 | ||||
-rw-r--r-- | src/fauxton/app/addons/documents/routes.js | 105 | ||||
-rw-r--r-- | src/fauxton/app/addons/documents/tests/resourcesSpec.js | 27 | ||||
-rw-r--r-- | src/fauxton/app/addons/documents/views.js | 133 | ||||
-rw-r--r-- | src/fauxton/app/addons/fauxton/base.js | 14 | ||||
-rw-r--r-- | src/fauxton/app/addons/fauxton/components.js | 38 | ||||
-rw-r--r-- | src/fauxton/app/addons/fauxton/tests/paginateSpec.js | 18 | ||||
-rw-r--r-- | src/fauxton/test/mocha/chai.js | 804 | ||||
-rw-r--r-- | src/fauxton/test/test.config.underscore | 4 |
9 files changed, 765 insertions, 432 deletions
diff --git a/src/fauxton/app/addons/documents/resources.js b/src/fauxton/app/addons/documents/resources.js index c2f9a706c..6112bab28 100644 --- a/src/fauxton/app/addons/documents/resources.js +++ b/src/fauxton/app/addons/documents/resources.js @@ -18,6 +18,51 @@ define([ function(app, FauxtonAPI) { var Documents = FauxtonAPI.addon(); + Documents.paginate = { + next: function (docs, currentParams, perPage, _isAllDocs) { + var params = {limit: perPage, skip: 1}, + doc = _.last(docs), + docId = '', + lastId = '', + isView = !!!_isAllDocs, + key; + + if (currentParams.keys) { + throw "Cannot paginate _all_docs with keys"; + } + + if (_.isUndefined(doc)) { + throw "Require docs to paginate"; + } + + params = _.reduce(['reduce', 'keys', 'endkey', 'descending', 'inclusive_end'], function (params, p) { + if (_.has(currentParams, p)) { + params[p] = currentParams[p]; + } + return params; + }, params); + + lastId = doc.id || doc._id; + + if (isView) { + key = doc.key; + docId = lastId; + } else { + docId = key = lastId; + } + + if (isView && !currentParams.keys) { + params.startkey_docid = docId; + params.startkey = key; + } else if (currentParams.startkey) { + params.startkey = key; + } else { + params.startkey_docid = docId; + } + return params; + } + }; + Documents.Doc = FauxtonAPI.Model.extend({ idAttribute: "_id", documentation: function(){ @@ -274,6 +319,7 @@ function(app, FauxtonAPI) { Documents.AllDocs = FauxtonAPI.Collection.extend({ model: Documents.Doc, + isAllDocs: true, documentation: function(){ return "docs"; }, @@ -330,6 +376,10 @@ function(app, FauxtonAPI) { this.params.limit = limit; }, + updateParams: function (params) { + this.params = params; + }, + nextPage: function (num, lastId) { if (!lastId) { var doc = this.last(); @@ -478,6 +528,10 @@ function(app, FauxtonAPI) { return this.url('app'); }, + updateParams: function (params) { + this.params = params; + }, + updateLimit: function (limit) { if (this.params.startkey_docid && this.params.startkey) { //we are paginating so set limit + 1 diff --git a/src/fauxton/app/addons/documents/routes.js b/src/fauxton/app/addons/documents/routes.js index 0d143dedc..5ce66b18d 100644 --- a/src/fauxton/app/addons/documents/routes.js +++ b/src/fauxton/app/addons/documents/routes.js @@ -160,8 +160,8 @@ function(app, FauxtonAPI, Documents, Databases) { }, initialize: function (route, masterLayout, options) { - var docOptions = app.getParams(); - docOptions.include_docs = true; + var docParams = app.getParams(); + docParams.include_docs = true; this.databaseName = options[0]; @@ -186,23 +186,32 @@ function(app, FauxtonAPI, Documents, Databases) { return this.data.designDocs.fetch(); }, + createParams: function (options) { + var urlParams = app.getParams(options); + return { + urlParams: urlParams, + docParams: _.extend(_.clone(urlParams), {limit: 20}) + }; + }, + + /* + * docParams are the options collection uses to fetch from the server + * urlParams are what are shown in the url and to the user + * They are not the same when paginating + */ allDocs: function(databaseName, options) { - var docOptions = app.getParams(options), - docLimit; + var params = this.createParams(options), + urlParams = params.urlParams, + docParams = params.docParams; if (this.eventAllDocs) { this.eventAllDocs = false; return; } - if (docOptions.limit) { - docLimit = docOptions.limit; - } - - docOptions.limit = 20; //default per page - this.data.database.buildAllDocs(docOptions); + this.data.database.buildAllDocs(docParams); - if (docOptions.startkey && docOptions.startkey.indexOf('_design') > -1) { + if (docParams.startkey && docParams.startkey.indexOf('_design') > -1) { this.sidebar.setSelectedTab('design-docs'); } else { this.sidebar.setSelectedTab('all-docs'); @@ -218,12 +227,14 @@ function(app, FauxtonAPI, Documents, Databases) { this.setView("#dashboard-upper-content", new Documents.Views.AllDocsLayout({ database: this.data.database, collection: this.data.database.allDocs, - params: docOptions + params: urlParams, + docParams: docParams })); this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({ collection: this.data.database.allDocs, - docLimit: parseInt(docLimit, 10) + docParams: docParams, + params: urlParams })); this.crumbs = [ @@ -313,43 +324,34 @@ function(app, FauxtonAPI, Documents, Databases) { updateAllDocsFromView: function (event) { var view = event.view, - docOptions = app.getParams(), + params = this.createParams(), + urlParams = params.urlParams, + docParams = params.docParams, ddoc = event.ddoc, - docLimit; + collection; - if (docOptions.limit) { - docLimit = docOptions.limit; - } + docParams.limit = this.documentsView.perPage(); - docOptions.limit = this.documentsView.perPage(); - this.documentsView && this.documentsView.remove(); + this.documentsView.forceRender(); if (event.allDocs) { this.eventAllDocs = true; // this is horrible. But I cannot get the trigger not to fire the route! - this.data.database.buildAllDocs(docOptions); - this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({ - collection: this.data.database.allDocs, - docLimit: parseInt(docLimit, 10) - })); - return; - } + this.data.database.buildAllDocs(docParams); + collection = this.data.database.allDocs; - this.data.indexedDocs = new Documents.IndexCollection(null, { - database: this.data.database, - design: ddoc, - view: view, - params: app.getParams() - }); + } else { + collection = this.data.indexedDocs = new Documents.IndexCollection(null, { + database: this.data.database, + design: ddoc, + view: view, + params: docParams + }); - this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({ - database: this.data.database, - collection: this.data.indexedDocs, - nestedView: Documents.Views.Row, - viewList: true, - docLimit: parseInt(docLimit, 10) - })); + this.apiUrl = [this.data.indexedDocs.url("apiurl"), "docs"]; + } - this.apiUrl = [this.data.indexedDocs.url("apiurl"), "docs"]; + this.documentsView.setCollection(collection); + this.documentsView.setParams(docParams, urlParams); }, updateAllDocsFromPreview: function (event) { @@ -373,24 +375,23 @@ function(app, FauxtonAPI, Documents, Databases) { }, perPageChange: function (perPage) { + this.perPage = perPage; this.documentsView.collection.updateLimit(perPage); this.documentsView.forceRender(); }, paginate: function (options) { + var params = options.params, + urlParams = app.getParams(), + collection = this.documentsView.collection; + this.documentsView.forceRender(); if (options.direction === 'next') { - this.documentsView.collection.skipFirstItem = true; - this.documentsView.collection.nextPage(options.perPage); - } else { - if (options.params && options.params.startkey) { - this.documentsView.collection.skipFirstItem = true; - } else { - this.documentsView.collection.skipFirstItem = false; - } - this.documentsView.collection.previousPage(options.perPage, options.params); + params = Documents.paginate.next(collection.map(function (item) { return item.toJSON(); }), collection.params, options.perPage, !!collection.isAllDocs); } + + collection.updateParams(params); }, reloadDesignDocs: function (event) { @@ -421,9 +422,9 @@ function(app, FauxtonAPI, Documents, Databases) { this.databaseName = options[0]; this.database = new Databases.Model({id: this.databaseName}); - var docOptions = app.getParams(); + var docParams = app.getParams(); - this.database.buildChanges(docOptions); + this.database.buildChanges(docParams); this.setView("#tabs", new Documents.Views.Tabs({ collection: this.designDocs, diff --git a/src/fauxton/app/addons/documents/tests/resourcesSpec.js b/src/fauxton/app/addons/documents/tests/resourcesSpec.js index 380a4e4fc..62506e6e4 100644 --- a/src/fauxton/app/addons/documents/tests/resourcesSpec.js +++ b/src/fauxton/app/addons/documents/tests/resourcesSpec.js @@ -32,20 +32,6 @@ define([ }); - it('Should return urlNext', function () { - var url = collection.urlNextPage(20); - - assert.equal(url, 'database/databaseId/_design/myDoc/_view/?limit=21&reduce=false&startkey_docid=myId2&startkey='); - - }); - - it('Should return urlPrevious', function () { - var url = collection.urlPreviousPage(20, {limit: 21, reduce: false, startkey_docid: "myId1",startkey:"myId1"} ); - - assert.equal(url, 'database/databaseId/_design/myDoc/_view/?limit=20&reduce=false&startkey_docid=myId1&startkey=myId1'); - - }); - }); describe('AllDocs', function () { @@ -65,19 +51,6 @@ define([ }); - it('Should return urlNext', function () { - var url = collection.urlNextPage(20); - - assert.equal(url, 'database/databaseId/_all_docs?limit=21&startkey_docid=%22myId2%22&startkey=%22myId2%22'); - - }); - - it('Should return urlPrevious', function () { - var url = collection.urlPreviousPage(20, {limit: 21, startkey_docid: "myId1",startkey:"myId1"} ); - assert.equal(url, 'database/databaseId/_all_docs?limit=20&startkey_docid=myId1&startkey=myId1'); - }); - - }); }); diff --git a/src/fauxton/app/addons/documents/views.js b/src/fauxton/app/addons/documents/views.js index c5ad1bb10..dea0bc7cc 100644 --- a/src/fauxton/app/addons/documents/views.js +++ b/src/fauxton/app/addons/documents/views.js @@ -415,8 +415,9 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum initialize: function (options) { this.newView = options.newView || false; this.pagination = options.pagination; + _.bindAll(this); - this._perPage = 20; + this._perPage = options.perPageDefault || 20; this.listenTo(this.collection, 'totalRows:decrement', this.render); }, @@ -466,6 +467,10 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum perPage: function () { return this._perPage; + }, + + setCollection: function (collection) { + this.collection = collection; } }); @@ -484,20 +489,16 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum toggleQuery: function (event) { $('#dashboard-content').scrollTop(0); - console.log('hi'); this.$('#query').toggle('slow'); }, beforeRender: function () { - this.eventer = _.extend({}, Backbone.Events); - this.advancedOptions = this.insertView('#query', new Views.AdvancedOptions({ updateViewFn: this.updateAllDocs, previewFn: this.previewView, hasReduce: false, showPreview: false, database: this.database, - eventer: this.eventer })); }, @@ -505,7 +506,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum if (this.params) { this.advancedOptions.updateFromParams(this.params); } - }, updateAllDocs: function (event, paramInfo) { @@ -574,13 +574,16 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum this.rows = {}; this.viewList = !! options.viewList; this.database = options.database; + if (options.ddocInfo) { this.designDocs = options.ddocInfo.designDocs; this.ddocID = options.ddocInfo.id; } this.newView = options.newView || false; - this.docLimit = options.docLimit; + this.docParams = options.docParams; + this.params = options.params || {}; this.expandDocs = true; + this.perPageDefault = options.perPageDefault || 20; }, establish: function() { @@ -666,13 +669,13 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum this.pagination = new Components.IndexPagination({ collection: this.collection, scrollToSelector: '#dashboard-content', - docLimit: this.docLimit + docLimit: this.params.limit }); }, cleanup: function () { - this.pagination.remove(); - this.allDocsNumber.remove(); + this.pagination && this.pagination.remove(); + this.allDocsNumber && this.allDocsNumber.remove(); _.each(this.rows, function (row) {row.remove();}); }, @@ -682,13 +685,16 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum this.addPagination(); } - this.insertView('#documents-pagination', this.pagination); + if (!this.params.keys) { //cannot paginate with keys + this.insertView('#documents-pagination', this.pagination); + } if (!this.allDocsNumber) { this.allDocsNumber = new Views.AllDocsNumber({ collection: this.collection, newView: this.newView, - pagination: this.pagination + pagination: this.pagination, + perPageDefault: this.perPageDefault }); } @@ -703,6 +709,21 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum }, this); }, + setCollection: function (collection) { + this.collection = collection; + this.pagination.setCollection(collection); + this.allDocsNumber.setCollection(collection); + }, + + setParams: function (docParams, urlParams) { + this.docParams = docParams; + this.params = urlParams; + + if (this.params.limit) { + this.pagination.docLimit = this.params.limit; + } + }, + afterRender: function(){ prettyPrint(); }, @@ -1007,73 +1028,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum } }); - Views.AdvancedOptionsMenu = FauxtonAPI.View.extend({ - template: 'addons/documents/templates/advanced_options_menu', - tagName: "div", - className: "controls-group advanced-options-menu", - events: { - "click input": "updateRows", - 'change #group-level': 'updateRows', - 'click #query-nav': 'toggleMenu' - }, - - initialize: function (options) { - this.hasReduce = options.hasReduce; - this.eventer = options.eventer; - }, - - toggleMenu: function (event) { - this.$('.checkbox').toggle(); - }, - - updateRows: function (event) { - var $groupLevel = this.$('#group-level-label'), - params = { - include_docs: false, - reduce: false, - group_level: 0 - }; - - if (this.$('#include-docs-views').prop('checked')) { - params.include_docs = true; - } - - if (this.hasReduce && this.$('#reduce').prop('checked')) { - params.reduce = true; - params.group_level = this.$('#group-level option:selected').val(); - $groupLevel.show(); - } else { - $groupLevel.hide(); - } - this.eventer.trigger('options:param_update', params); - }, - - updateFromParams: function (params) { - if (params.reduce) { - var $reduce = this.$('#reduce'); - $reduce.prop("checked", true); - this.$('#group-level-label').show(); - this.$('option[value="' + params.group_level + '"]').prop('selected', true); - - } else if (params.include_docs) { - var $include_docs = this.$('#include-docs-views'); - $include_docs.prop("checked", true); - } - }, - - serialize: function () { - return { - hasReduce: this.hasReduce - }; - }, - - setHasReduce: function (hasReduce) { - this.hasReduce = hasReduce; - } - - }); - - Views.AdvancedOptions = FauxtonAPI.View.extend({ template: "addons/documents/templates/advanced_options", className: "advanced-options well", @@ -1084,7 +1038,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum this.viewName = options.viewName; this.updateViewFn = options.updateViewFn; this.previewFn = options.previewFn; - this.eventer = options.eventer; if (typeof(options.hasReduce) === 'undefined') { this.hasReduce = true; @@ -1097,8 +1050,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum } else { this.showPreview = options.showPreview; } - - this.eventer && this.listenTo(this.eventer, 'options:param_update', this.optionsParamsUpdate); }, events: { @@ -1108,24 +1059,6 @@ function(app, FauxtonAPI, Components, Documents, Databases, pouchdb, resizeColum "click button.preview": "previewView" }, - optionsParamsUpdate: function (params) { - var $form = this.$el.find("form.view-query-update"); - - if (!params.group_level) { - this.$("select[name='group_level']").attr('disabled', 'disabled'); - } - - if (params.reduce && params.group_level) { - $form.find("select[name='group_level']").val(params.group_level).removeAttr('disabled'); - delete params.group_level; - } - - _.each(params, function(val, key) { - $form.find("input[name='"+key+"']").prop('checked', val); - }); - this.$('form.view-query-update').submit(); - }, - beforeRender: function () { if (this.viewName && this.ddocName) { var buttonViews = FauxtonAPI.getExtensions('advancedOptions:ViewButton'); diff --git a/src/fauxton/app/addons/fauxton/base.js b/src/fauxton/app/addons/fauxton/base.js index 35babb5f6..a6e462a28 100644 --- a/src/fauxton/app/addons/fauxton/base.js +++ b/src/fauxton/app/addons/fauxton/base.js @@ -45,7 +45,6 @@ function(app, FauxtonAPI, resizeColumns) { } }); - Fauxton.initialize = function () { app.footer = new Fauxton.Footer({el: "#footer-content"}), app.navBar = new Fauxton.NavBar(); @@ -93,7 +92,7 @@ function(app, FauxtonAPI, resizeColumns) { }); }; - Fauxton.Breadcrumbs = Backbone.View.extend({ + Fauxton.Breadcrumbs = FauxtonAPI.View.extend({ template: "templates/fauxton/breadcrumbs", serialize: function() { @@ -114,10 +113,7 @@ function(app, FauxtonAPI, resizeColumns) { } }); - // TODO: this View should extend from FauxtonApi.View. - // Chicken and egg problem, api.js extends fauxton/base.js. - // Need to sort the loading order. - Fauxton.Footer = Backbone.View.extend({ + Fauxton.Footer = FauxtonAPI.View.extend({ template: "templates/fauxton/footer", initialize: function() { @@ -135,7 +131,7 @@ function(app, FauxtonAPI, resizeColumns) { } }); - Fauxton.NavBar = Backbone.View.extend({ + Fauxton.NavBar = FauxtonAPI.View.extend({ className:"navbar", template: "templates/fauxton/nav_bar", // TODO: can we generate this list from the router? @@ -260,7 +256,7 @@ function(app, FauxtonAPI, resizeColumns) { // TODO: ADD ACTIVE CLASS }); - Fauxton.ApiBar = Backbone.View.extend({ + Fauxton.ApiBar = FauxtonAPI.View.extend({ template: "templates/fauxton/api_bar", endpoint: '_all_docs', @@ -304,7 +300,7 @@ function(app, FauxtonAPI, resizeColumns) { }); - Fauxton.Notification = Backbone.View.extend({ + Fauxton.Notification = FauxtonAPI.View.extend({ fadeTimer: 5000, initialize: function(options) { diff --git a/src/fauxton/app/addons/fauxton/components.js b/src/fauxton/app/addons/fauxton/components.js index f8032fee6..a9f4ef4b5 100644 --- a/src/fauxton/app/addons/fauxton/components.js +++ b/src/fauxton/app/addons/fauxton/components.js @@ -71,14 +71,21 @@ function(app, FauxtonAPI, ace, spin) { this.nextUrlfn = options.nextUrlfn; this.scrollToSelector = options.scrollToSelector; _.bindAll(this); + this.docLimit = options.docLimit || 1000000; + this.perPage = 20; + this.setDefaults(); + }, + + setDefaults: function () { this._pageNumber = []; this._pageStart = 1; - this.perPage = 20; - this.docLimit = options.docLimit || 1000000; this.paramsHistory = []; + this.enabled = true; }, canShowPreviousfn: function () { + if (!this.enabled) { return this.enabled; } + if (this._pageStart === 1) { return false; } @@ -86,6 +93,8 @@ function(app, FauxtonAPI, ace, spin) { }, canShowNextfn: function () { + if (!this.enabled) { return this.enabled; } + if (this.collection.length < (this.perPage -1)) { return false; } @@ -94,6 +103,10 @@ function(app, FauxtonAPI, ace, spin) { return false; } + if (this.collection.viewMeta && this.collection.viewMeta.total_rows <= this.pageStart() + this.perPage) { + return false; + } + return true; }, @@ -103,11 +116,12 @@ function(app, FauxtonAPI, ace, spin) { if (!this.canShowPreviousfn()) { return; } this.decPageNumber(); + var params = this.paramsHistory.pop(); FauxtonAPI.triggerRouteEvent('paginate', { direction: 'previous', perPage: this.perPage, - params: this.paramsHistory.pop() + params: params }); }, @@ -179,12 +193,22 @@ function(app, FauxtonAPI, ace, spin) { }, pageEnd: function () { - if (this.collection.length < this.perPage) { - return this.page() + this.collection.length; - } + return this.page() + this.collection.length; + }, - return this.page() + this.perPage; + disable: function () { + this.enabled = false; + }, + + enable: function () { + this.enabled = true; + }, + + setCollection: function (collection) { + this.collection = collection; + this.setDefaults(); } + }); //TODO allow more of the typeahead options. diff --git a/src/fauxton/app/addons/fauxton/tests/paginateSpec.js b/src/fauxton/app/addons/fauxton/tests/paginateSpec.js index 535e26f4a..8fc409a5d 100644 --- a/src/fauxton/app/addons/fauxton/tests/paginateSpec.js +++ b/src/fauxton/app/addons/fauxton/tests/paginateSpec.js @@ -58,15 +58,6 @@ define([ //FauxtonAPI.navigate.restore && FauxtonAPI.navigate.restore(); }); - it('Should navigate', function () { - var navigateMock = sinon.spy(FauxtonAPI, 'navigate'); - - paginate.$('a#next').click(); - - assert.ok(navigateMock.calledOnce); - FauxtonAPI.navigate.restore(); - }); - it('Should trigger routeEvent', function () { var navigateMock = sinon.spy(FauxtonAPI, 'triggerRouteEvent'); @@ -81,15 +72,6 @@ define([ describe('#previous', function () { - it('Should navigate', function () { - var navigateMock = sinon.spy(FauxtonAPI, 'navigate'); - - paginate.$('a#previous').click(); - - assert.ok(navigateMock.calledOnce); - FauxtonAPI.navigate.restore(); - }); - it('Should trigger routeEvent', function () { var navigateMock = sinon.spy(FauxtonAPI, 'triggerRouteEvent'); diff --git a/src/fauxton/test/mocha/chai.js b/src/fauxton/test/mocha/chai.js index 2a67f982b..9dd7b0a78 100644 --- a/src/fauxton/test/mocha/chai.js +++ b/src/fauxton/test/mocha/chai.js @@ -27,10 +27,14 @@ function require(path, parent, orig) { // perform real require() // by invoking the module's // registered function - if (!module.exports) { - module.exports = {}; - module.client = module.component = true; - module.call(this, module.exports, require.relative(resolved), module); + if (!module._resolving && !module.exports) { + var mod = {}; + mod.exports = {}; + mod.client = mod.component = true; + module._resolving = true; + module.call(this, mod.exports, require.relative(resolved), mod); + delete module._resolving; + module.exports = mod.exports; } return module.exports; @@ -309,6 +313,411 @@ AssertionError.prototype.toJSON = function (stack) { }; }); +require.register("chaijs-type-detect/lib/type.js", function(exports, require, module){ +/*! + * type-detect + * Copyright(c) 2013 jake luer <jake@alogicalparadox.com> + * MIT Licensed + */ + +/*! + * Primary Exports + */ + +var exports = module.exports = getType; + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Array]': 'array' + , '[object RegExp]': 'regexp' + , '[object Function]': 'function' + , '[object Arguments]': 'arguments' + , '[object Date]': 'date' +}; + +/** + * ### typeOf (obj) + * + * Use several different techniques to determine + * the type of object being tested. + * + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ + +function getType (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +} + +exports.Library = Library; + +/** + * ### Library + * + * Create a repository for custom type detection. + * + * ```js + * var lib = new type.Library; + * ``` + * + */ + +function Library () { + this.tests = {}; +} + +/** + * #### .of (obj) + * + * Expose replacement `typeof` detection to the library. + * + * ```js + * if ('string' === lib.of('hello world')) { + * // ... + * } + * ``` + * + * @param {Mixed} object to test + * @return {String} type + */ + +Library.prototype.of = getType; + +/** + * #### .define (type, test) + * + * Add a test to for the `.test()` assertion. + * + * Can be defined as a regular expression: + * + * ```js + * lib.define('int', /^[0-9]+$/); + * ``` + * + * ... or as a function: + * + * ```js + * lib.define('bln', function (obj) { + * if ('boolean' === lib.of(obj)) return true; + * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; + * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); + * return !! ~blns.indexOf(obj); + * }); + * ``` + * + * @param {String} type + * @param {RegExp|Function} test + * @api public + */ + +Library.prototype.define = function (type, test) { + if (arguments.length === 1) return this.tests[type]; + this.tests[type] = test; + return this; +}; + +/** + * #### .test (obj, test) + * + * Assert that an object is of type. Will first + * check natives, and if that does not pass it will + * use the user defined custom tests. + * + * ```js + * assert(lib.test('1', 'int')); + * assert(lib.test('yes', 'bln')); + * ``` + * + * @param {Mixed} object + * @param {String} type + * @return {Boolean} result + * @api public + */ + +Library.prototype.test = function (obj, type) { + if (type === getType(obj)) return true; + var test = this.tests[type]; + + if (test && 'regexp' === getType(test)) { + return test.test(obj); + } else if (test && 'function' === getType(test)) { + return test(obj); + } else { + throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); + } +}; + +}); +require.register("chaijs-deep-eql/lib/eql.js", function(exports, require, module){ +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com> + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var type = require('type-detect'); + +/*! + * Buffer.isBuffer browser shim + */ + +var Buffer; +try { Buffer = require('buffer').Buffer; } +catch(ex) { + Buffer = {}; + Buffer.isBuffer = function() { return false; } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; + +/** + * Assert super-strict (egal) equality between + * two objects of any type. + * + * @param {Mixed} a + * @param {Mixed} b + * @param {Array} memoised (optional) + * @return {Boolean} equal match + */ + +function deepEqual(a, b, m) { + if (sameValue(a, b)) { + return true; + } else if ('date' === type(a)) { + return dateEqual(a, b); + } else if ('regexp' === type(a)) { + return regexpEqual(a, b); + } else if (Buffer.isBuffer(a)) { + return bufferEqual(a, b); + } else if ('arguments' === type(a)) { + return argumentsEqual(a, b, m); + } else if (!typeEqual(a, b)) { + return false; + } else if (('object' !== type(a) && 'object' !== type(b)) + && ('array' !== type(a) && 'array' !== type(b))) { + return sameValue(a, b); + } else { + return objectEqual(a, b, m); + } +} + +/*! + * Strict (egal) equality test. Ensures that NaN always + * equals NaN and `-0` does not equal `+0`. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} equal match + */ + +function sameValue(a, b) { + if (a === b) return a !== 0 || 1 / a === 1 / b; + return a !== a && b !== b; +} + +/*! + * Compare the types of two given objects and + * return if they are equal. Note that an Array + * has a type of `array` (not `object`) and arguments + * have a type of `arguments` (not `array`/`object`). + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function typeEqual(a, b) { + return type(a) === type(b); +} + +/*! + * Compare two Date objects by asserting that + * the time values are equal using `saveValue`. + * + * @param {Date} a + * @param {Date} b + * @return {Boolean} result + */ + +function dateEqual(a, b) { + if ('date' !== type(b)) return false; + return sameValue(a.getTime(), b.getTime()); +} + +/*! + * Compare two regular expressions by converting them + * to string and checking for `sameValue`. + * + * @param {RegExp} a + * @param {RegExp} b + * @return {Boolean} result + */ + +function regexpEqual(a, b) { + if ('regexp' !== type(b)) return false; + return sameValue(a.toString(), b.toString()); +} + +/*! + * Assert deep equality of two `arguments` objects. + * Unfortunately, these must be sliced to arrays + * prior to test to ensure no bad behavior. + * + * @param {Arguments} a + * @param {Arguments} b + * @param {Array} memoize (optional) + * @return {Boolean} result + */ + +function argumentsEqual(a, b, m) { + if ('arguments' !== type(b)) return false; + a = [].slice.call(a); + b = [].slice.call(b); + return deepEqual(a, b, m); +} + +/*! + * Get enumerable properties of a given object. + * + * @param {Object} a + * @return {Array} property names + */ + +function enumerable(a) { + var res = []; + for (var key in a) res.push(key); + return res; +} + +/*! + * Simple equality for flat iterable objects + * such as Arrays or Node.js buffers. + * + * @param {Iterable} a + * @param {Iterable} b + * @return {Boolean} result + */ + +function iterableEqual(a, b) { + if (a.length !== b.length) return false; + + var i = 0; + var match = true; + + for (; i < a.length; i++) { + if (a[i] !== b[i]) { + match = false; + break; + } + } + + return match; +} + +/*! + * Extension to `iterableEqual` specifically + * for Node.js Buffers. + * + * @param {Buffer} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function bufferEqual(a, b) { + if (!Buffer.isBuffer(b)) return false; + return iterableEqual(a, b); +} + +/*! + * Block for `objectEqual` ensuring non-existing + * values don't get in. + * + * @param {Mixed} object + * @return {Boolean} result + */ + +function isValue(a) { + return a !== null && a !== undefined; +} + +/*! + * Recursively check the equality of two objects. + * Once basic sameness has been established it will + * defer to `deepEqual` for each enumerable key + * in the object. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function objectEqual(a, b, m) { + if (!isValue(a) || !isValue(b)) { + return false; + } + + if (a.prototype !== b.prototype) { + return false; + } + + var i; + if (m) { + for (i = 0; i < m.length; i++) { + if ((m[i][0] === a && m[i][1] === b) + || (m[i][0] === b && m[i][1] === a)) { + return true; + } + } + } else { + m = []; + } + + try { + var ka = enumerable(a); + var kb = enumerable(b); + } catch (ex) { + return false; + } + + ka.sort(); + kb.sort(); + + if (!iterableEqual(ka, kb)) { + return false; + } + + m.push([ a, b ]); + + var key; + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], m)) { + return false; + } + } + + return true; +} + +}); require.register("chai/index.js", function(exports, require, module){ module.exports = require('./lib/chai'); @@ -316,7 +725,7 @@ module.exports = require('./lib/chai'); require.register("chai/lib/chai.js", function(exports, require, module){ /*! * chai - * Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -327,7 +736,7 @@ var used = [] * Chai version */ -exports.version = '1.7.2'; +exports.version = '1.8.1'; /*! * Assertion Error @@ -400,7 +809,7 @@ require.register("chai/lib/chai/assertion.js", function(exports, require, module /*! * chai * http://chaijs.com - * Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -480,6 +889,10 @@ module.exports = function (_chai, util) { util.overwriteMethod(this.prototype, name, fn); }; + Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { + util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + /*! * ### .assert(expression, message, negateMessage, expected, actual) * @@ -533,7 +946,7 @@ require.register("chai/lib/chai/core/assertions.js", function(exports, require, /*! * chai * http://chaijs.com - * Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -545,7 +958,7 @@ module.exports = function (chai, _) { /** * ### Language Chains * - * The following are provide as chainable getters to + * The following are provided as chainable getters to * improve the readability of your assertions. They * do not provide an testing capability unless they * have been overwritten by a plugin. @@ -558,6 +971,7 @@ module.exports = function (chai, _) { * - is * - that * - and + * - has * - have * - with * - at @@ -569,7 +983,7 @@ module.exports = function (chai, _) { */ [ 'to', 'be', 'been' - , 'is', 'and', 'have' + , 'is', 'and', 'has', 'have' , 'with', 'that', 'at' , 'of', 'same' ].forEach(function (chain) { Assertion.addProperty(chain, function () { @@ -677,9 +1091,21 @@ module.exports = function (chai, _) { function include (val, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object') + var obj = flag(this, 'object'); + + if (_.type(val) === 'object') { + if (!flag(this, 'negate')) { + for (var k in val) new Assertion(obj).property(k, val[k]); + return; + } + var subset = {} + for (var k in val) subset[k] = obj[k] + var expected = _.eql(subset, val); + } else { + var expected = obj && ~obj.indexOf(val) + } this.assert( - ~obj.indexOf(val) + expected , 'expected #{this} to include ' + _.inspect(val) , 'expected #{this} to not include ' + _.inspect(val)); } @@ -776,8 +1202,8 @@ module.exports = function (chai, _) { * * Asserts that the target is `undefined`. * - * expect(undefined).to.be.undefined; - * expect(null).to.not.be.undefined; + * expect(undefined).to.be.undefined; + * expect(null).to.not.be.undefined; * * @name undefined * @api public @@ -1534,6 +1960,7 @@ module.exports = function (chai, _) { * @param {String|RegExp} expected error message * @param {String} message _optional_ * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @returns error for chaining (null if no error) * @api public */ @@ -1558,7 +1985,10 @@ module.exports = function (chai, _) { constructor = null; errMsg = null; } else if (typeof constructor === 'function') { - name = (new constructor()).name; + name = constructor.prototype.name || constructor.name; + if (name === 'Error' && constructor !== Error) { + name = (new constructor()).name; + } } else { constructor = null; } @@ -1572,12 +2002,14 @@ module.exports = function (chai, _) { err === desiredError , 'expected #{this} to throw #{exp} but #{act} was thrown' , 'expected #{this} to not throw #{exp}' - , desiredError - , err + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (err instanceof Error ? err.toString() : err) ); + flag(this, 'object', err); return this; } + // next, check constructor if (constructor) { this.assert( @@ -1585,11 +2017,15 @@ module.exports = function (chai, _) { , 'expected #{this} to throw #{exp} but #{act} was thrown' , 'expected #{this} to not throw #{exp} but #{act} was thrown' , name - , err + , (err instanceof Error ? err.toString() : err) ); - if (!errMsg) return this; + if (!errMsg) { + flag(this, 'object', err); + return this; + } } + // next, check message var message = 'object' === _.type(err) && "message" in err ? err.message @@ -1604,6 +2040,7 @@ module.exports = function (chai, _) { , message ); + flag(this, 'object', err); return this; } else if ((message != null) && errMsg && 'string' === typeof errMsg) { this.assert( @@ -1614,6 +2051,7 @@ module.exports = function (chai, _) { , message ); + flag(this, 'object', err); return this; } else { thrown = true; @@ -1636,9 +2074,11 @@ module.exports = function (chai, _) { thrown === true , 'expected #{this} to throw ' + expectedThrown + actuallyGot , 'expected #{this} to not throw ' + expectedThrown + actuallyGot - , desiredError - , thrownError + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (thrownError instanceof Error ? thrownError.toString() : thrownError) ); + + flag(this, 'object', thrownError); }; Assertion.addMethod('throw', assertThrows); @@ -1657,8 +2097,8 @@ module.exports = function (chai, _) { * To check if a constructor will respond to a static function, * set the `itself` flag. * - * Klass.baz = function(){}; - * expect(Klass).itself.to.respondTo('baz'); + * Klass.baz = function(){}; + * expect(Klass).itself.to.respondTo('baz'); * * @name respondTo * @param {String} method @@ -1686,12 +2126,12 @@ module.exports = function (chai, _) { * * Sets the `itself` flag, later used by the `respondTo` assertion. * - * function Foo() {} - * Foo.bar = function() {} - * Foo.prototype.baz = function() {} + * function Foo() {} + * Foo.bar = function() {} + * Foo.prototype.baz = function() {} * - * expect(Foo).itself.to.respondTo('bar'); - * expect(Foo).itself.not.to.respondTo('baz'); + * expect(Foo).itself.to.respondTo('bar'); + * expect(Foo).itself.not.to.respondTo('baz'); * * @name itself * @api public @@ -1805,7 +2245,7 @@ module.exports = function (chai, _) { require.register("chai/lib/chai/interface/assert.js", function(exports, require, module){ /*! * chai - * Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -1860,13 +2300,12 @@ module.exports = function (chai, util) { */ assert.fail = function (actual, expected, message, operator) { - throw new chai.AssertionError({ + message = message || 'assert.fail()'; + throw new chai.AssertionError(message, { actual: actual , expected: expected - , message: message , operator: operator - , stackStartFunction: assert.fail - }); + }, assert.fail); }; /** @@ -2462,19 +2901,7 @@ module.exports = function (chai, util) { */ assert.include = function (exp, inc, msg) { - var obj = new Assertion(exp, msg); - - if (Array.isArray(exp)) { - obj.to.include(inc); - } else if ('string' === typeof exp) { - obj.to.contain.string(inc); - } else { - throw new chai.AssertionError( - 'expected an array or string' - , null - , assert.include - ); - } + new Assertion(exp, msg).include(inc); }; /** @@ -2494,19 +2921,7 @@ module.exports = function (chai, util) { */ assert.notInclude = function (exp, inc, msg) { - var obj = new Assertion(exp, msg); - - if (Array.isArray(exp)) { - obj.to.not.include(inc); - } else if ('string' === typeof exp) { - obj.to.not.contain.string(inc); - } else { - throw new chai.AssertionError( - 'expected an array or string' - , null - , assert.notInclude - ); - } + new Assertion(exp, msg).not.include(inc); }; /** @@ -2750,7 +3165,8 @@ module.exports = function (chai, util) { errt = null; } - new Assertion(fn, msg).to.Throw(errt, errs); + var assertErr = new Assertion(fn, msg).to.Throw(errt, errs); + return flag(assertErr, 'object'); }; /** @@ -2888,7 +3304,7 @@ module.exports = function (chai, util) { require.register("chai/lib/chai/interface/expect.js", function(exports, require, module){ /*! * chai - * Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -2903,7 +3319,7 @@ module.exports = function (chai, util) { require.register("chai/lib/chai/interface/should.js", function(exports, require, module){ /*! * chai - * Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2011-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -2982,7 +3398,7 @@ module.exports = function (chai, util) { require.register("chai/lib/chai/utils/addChainableMethod.js", function(exports, require, module){ /*! * Chai - addChainingMethod utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3037,15 +3453,27 @@ var call = Function.prototype.call, */ module.exports = function (ctx, name, method, chainingBehavior) { - if (typeof chainingBehavior !== 'function') + if (typeof chainingBehavior !== 'function') { chainingBehavior = function () { }; + } + + var chainableBehavior = { + method: method + , chainingBehavior: chainingBehavior + }; + + // save the methods so we can overwrite them later, if we need to. + if (!ctx.__methods) { + ctx.__methods = {}; + } + ctx.__methods[name] = chainableBehavior; Object.defineProperty(ctx, name, { get: function () { - chainingBehavior.call(this); + chainableBehavior.chainingBehavior.call(this); var assert = function () { - var result = method.apply(this, arguments); + var result = chainableBehavior.method.apply(this, arguments); return result === undefined ? this : result; }; @@ -3079,7 +3507,7 @@ module.exports = function (ctx, name, method, chainingBehavior) { require.register("chai/lib/chai/utils/addMethod.js", function(exports, require, module){ /*! * Chai - addMethod utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3119,7 +3547,7 @@ module.exports = function (ctx, name, method) { require.register("chai/lib/chai/utils/addProperty.js", function(exports, require, module){ /*! * Chai - addProperty utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3159,142 +3587,10 @@ module.exports = function (ctx, name, getter) { }; }); -require.register("chai/lib/chai/utils/eql.js", function(exports, require, module){ -// This is (almost) directly from Node.js assert -// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/assert.js - -module.exports = _deepEqual; - -var getEnumerableProperties = require('./getEnumerableProperties'); - -// for the browser -var Buffer; -try { - Buffer = require('buffer').Buffer; -} catch (ex) { - Buffer = { - isBuffer: function () { return false; } - }; -} - -function _deepEqual(actual, expected, memos) { - - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - - } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { - if (actual.length != expected.length) return false; - - for (var i = 0; i < actual.length; i++) { - if (actual[i] !== expected[i]) return false; - } - - return true; - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (expected instanceof Date) { - if (!(actual instanceof Date)) return false; - return actual.getTime() === expected.getTime(); - - // 7.3. Other pairs that do not both pass typeof value == 'object', - // equivalence is determined by ==. - } else if (typeof actual != 'object' && typeof expected != 'object') { - return actual === expected; - - } else if (expected instanceof RegExp) { - if (!(actual instanceof RegExp)) return false; - return actual.toString() === expected.toString(); - - // 7.4. For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical 'prototype' property. Note: this - // accounts for both named and indexed properties on Arrays. - } else { - return objEquiv(actual, expected, memos); - } -} - -function isUndefinedOrNull(value) { - return value === null || value === undefined; -} - -function isArguments(object) { - return Object.prototype.toString.call(object) == '[object Arguments]'; -} - -function objEquiv(a, b, memos) { - if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) - return false; - - // an identical 'prototype' property. - if (a.prototype !== b.prototype) return false; - - // check if we have already compared a and b - var i; - if (memos) { - for(i = 0; i < memos.length; i++) { - if ((memos[i][0] === a && memos[i][1] === b) || - (memos[i][0] === b && memos[i][1] === a)) - return true; - } - } else { - memos = []; - } - - //~~~I've managed to break Object.keys through screwy arguments passing. - // Converting to array solves the problem. - if (isArguments(a)) { - if (!isArguments(b)) { - return false; - } - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b, memos); - } - try { - var ka = getEnumerableProperties(a), - kb = getEnumerableProperties(b), - key; - } catch (e) {//happens when one is a string literal and the other isn't - return false; - } - - // having the same number of owned properties (keys incorporates - // hasOwnProperty) - if (ka.length != kb.length) - return false; - - //the same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - //~~~cheap key test - for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] != kb[i]) - return false; - } - - // remember objects we have compared to guard against circular references - memos.push([ a, b ]); - - //equivalent values for every corresponding key, and - //~~~possibly expensive deep test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!_deepEqual(a[key], b[key], memos)) return false; - } - - return true; -} - -}); require.register("chai/lib/chai/utils/flag.js", function(exports, require, module){ /*! * Chai - flag utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3329,7 +3625,7 @@ module.exports = function (obj, key, value) { require.register("chai/lib/chai/utils/getActual.js", function(exports, require, module){ /*! * Chai - getActual utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3351,7 +3647,7 @@ module.exports = function (obj, args) { require.register("chai/lib/chai/utils/getEnumerableProperties.js", function(exports, require, module){ /*! * Chai - getEnumerableProperties utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3379,7 +3675,7 @@ module.exports = function getEnumerableProperties(object) { require.register("chai/lib/chai/utils/getMessage.js", function(exports, require, module){ /*! * Chai - message composition utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3431,7 +3727,7 @@ module.exports = function (obj, args) { require.register("chai/lib/chai/utils/getName.js", function(exports, require, module){ /*! * Chai - getName utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3454,7 +3750,7 @@ module.exports = function (func) { require.register("chai/lib/chai/utils/getPathValue.js", function(exports, require, module){ /*! * Chai - getPathValue utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * @see https://github.com/logicalparadox/filtr * MIT Licensed */ @@ -3559,7 +3855,7 @@ function _getPathValue (parsed, obj) { require.register("chai/lib/chai/utils/getProperties.js", function(exports, require, module){ /*! * Chai - getProperties utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -3659,7 +3955,7 @@ exports.transferFlags = require('./transferFlags'); * Deep equal utility */ -exports.eql = require('./eql'); +exports.eql = require('deep-eql'); /*! * Deep path value @@ -3703,6 +3999,12 @@ exports.overwriteMethod = require('./overwriteMethod'); exports.addChainableMethod = require('./addChainableMethod'); +/*! + * Overwrite chainable method + */ + +exports.overwriteChainableMethod = require('./overwriteChainableMethod'); + }); require.register("chai/lib/chai/utils/inspect.js", function(exports, require, module){ @@ -4031,7 +4333,7 @@ function objectToString(o) { require.register("chai/lib/chai/utils/objDisplay.js", function(exports, require, module){ /*! * Chai - flag utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -4082,7 +4384,7 @@ module.exports = function (obj) { require.register("chai/lib/chai/utils/overwriteMethod.js", function(exports, require, module){ /*! * Chai - overwriteMethod utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -4136,7 +4438,7 @@ module.exports = function (ctx, name, method) { require.register("chai/lib/chai/utils/overwriteProperty.js", function(exports, require, module){ /*! * Chai - overwriteProperty utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -4190,10 +4492,66 @@ module.exports = function (ctx, name, getter) { }; }); +require.register("chai/lib/chai/utils/overwriteChainableMethod.js", function(exports, require, module){ +/*! + * Chai - overwriteChainableMethod utility + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> + * MIT Licensed + */ + +/** + * ### overwriteChainableMethod (ctx, name, fn) + * + * Overwites an already existing chainable method + * and provides access to the previous function or + * property. Must return functions to be used for + * name. + * + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'length', + * function (_super) { + * } + * , function (_super) { + * } + * ); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteChainableMethod('foo', fn, fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.have.length(3); + * expect(myFoo).to.have.length.above(3); + * + * @param {Object} ctx object whose method / property is to be overwritten + * @param {String} name of method / property to overwrite + * @param {Function} method function that returns a function to be used for name + * @param {Function} chainingBehavior function that returns a function to be used for property + * @name overwriteChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + var chainableBehavior = ctx.__methods[name]; + + var _chainingBehavior = chainableBehavior.chainingBehavior; + chainableBehavior.chainingBehavior = function () { + var result = chainingBehavior(_chainingBehavior).call(this); + return result === undefined ? this : result; + }; + + var _method = chainableBehavior.method; + chainableBehavior.method = function () { + var result = method(_method).apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); require.register("chai/lib/chai/utils/test.js", function(exports, require, module){ /*! * Chai - test utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -4222,7 +4580,7 @@ module.exports = function (obj, args) { require.register("chai/lib/chai/utils/transferFlags.js", function(exports, require, module){ /*! * Chai - transferFlags utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -4269,7 +4627,7 @@ module.exports = function (assertion, object, includeAll) { require.register("chai/lib/chai/utils/type.js", function(exports, require, module){ /*! * Chai - type utility - * Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> + * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ @@ -4314,17 +4672,25 @@ module.exports = function (obj) { }; }); + + + + require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js"); require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js"); require.alias("chaijs-assertion-error/index.js", "assertion-error/index.js"); require.alias("chaijs-assertion-error/index.js", "chaijs-assertion-error/index.js"); - -require.alias("chai/index.js", "chai/index.js"); - -if (typeof exports == "object") { +require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/lib/eql.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/index.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "deep-eql/index.js"); +require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/lib/type.js"); +require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/index.js"); +require.alias("chaijs-type-detect/lib/type.js", "chaijs-type-detect/index.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "chaijs-deep-eql/index.js"); +require.alias("chai/index.js", "chai/index.js");if (typeof exports == "object") { module.exports = require("chai"); } else if (typeof define == "function" && define.amd) { - define(function(){ return require("chai"); }); + define([], function(){ return require("chai"); }); } else { this["chai"] = require("chai"); }})(); diff --git a/src/fauxton/test/test.config.underscore b/src/fauxton/test/test.config.underscore index 5cebe7837..95494a4ed 100644 --- a/src/fauxton/test/test.config.underscore +++ b/src/fauxton/test/test.config.underscore @@ -7,7 +7,11 @@ require.config( require([ "app", <% _.each(testFiles, function (test) {%> + <% if (test[0] === '.') { %> '../<%= test %>', + <% } else { %> + '<%= test %>', + <% } %> <% }) %> ], function() { if (window.mochaPhantomJS) { mochaPhantomJS.run(); } |