couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robertkowal...@apache.org
Subject [2/2] fauxton commit: updated refs/heads/master to deaf53c
Date Wed, 13 May 2015 09:12:34 GMT
tests: move tests to subcomponents

some subcomponents already have this, this removes the remaining
tests to their subcomponents

PR: #405
PR-URL: https://github.com/apache/couchdb-fauxton/pull/405
Reviewed-By: Benjamin Keen <ben.keen@gmail.com>
Reviewed-By: Michelle Phung <michellephung@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/deaf53c9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/deaf53c9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/deaf53c9

Branch: refs/heads/master
Commit: deaf53c9f60ea9047eb203c9d60b3a7cd6d5b1f0
Parents: 2c9723a
Author: Robert Kowalski <robertkowalski@apache.org>
Authored: Tue May 12 12:28:06 2015 +0200
Committer: Robert Kowalski <robertkowalski@apache.org>
Committed: Wed May 13 11:14:41 2015 +0200

----------------------------------------------------------------------
 .../tests/changes.componentsSpec.react.jsx      | 375 +++++++++++++++++++
 .../changes/tests/changes.storesSpec.js         | 107 ++++++
 .../documents/header/tests/headerSpec.react.jsx | 112 ++++++
 .../documents/index-editor/tests/actionsSpec.js | 348 +++++++++++++++++
 .../documents/index-editor/tests/storesSpec.js  | 311 +++++++++++++++
 .../tests/viewIndex.componentsSpec.react.jsx    | 268 +++++++++++++
 app/addons/documents/tests/actionsSpec.js       | 348 -----------------
 .../tests/changes.componentsSpec.react.jsx      | 375 -------------------
 .../documents/tests/changes.storesSpec.js       | 107 ------
 app/addons/documents/tests/headerSpec.react.jsx | 112 ------
 app/addons/documents/tests/storesSpec.js        | 311 ---------------
 .../tests/viewIndex.componentsSpec.react.jsx    | 268 -------------
 .../documents/tests/views-advancedoptsSpec.js   |  32 --
 13 files changed, 1521 insertions(+), 1553 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/changes/tests/changes.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/changes/tests/changes.componentsSpec.react.jsx b/app/addons/documents/changes/tests/changes.componentsSpec.react.jsx
new file mode 100644
index 0000000..ecf3f35
--- /dev/null
+++ b/app/addons/documents/changes/tests/changes.componentsSpec.react.jsx
@@ -0,0 +1,375 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  'app',
+  'api',
+  'react',
+  'addons/documents/changes/components.react',
+  'addons/documents/changes/stores',
+  'addons/documents/changes/actions',
+  'testUtils'
+], function (app, FauxtonAPI, React, Changes, Stores, Actions, utils) {
+  FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+
+
+  // suppresses unwanted console.log()'s on missing URLs
+  FauxtonAPI.registerUrls('document', {
+    server: function (database, doc) { return app.host + '/' + database + '/' + doc; },
+    app: function (database, doc) { return '/database/' + database + '/' + doc; },
+    apiurl: function (database, doc) { return window.location.origin + '/' + database + '/' + doc; },
+    'web-index': function (database, doc) { return '/database/' + database + '/' + doc; }
+  });
+
+
+  describe('ChangesHeader', function () {
+    var container, tab, spy;
+
+    describe('Testing DOM', function () {
+      beforeEach(function () {
+        spy = sinon.spy(Actions, 'toggleTabVisibility');
+        container = document.createElement('div');
+        tab = TestUtils.renderIntoDocument(<Changes.ChangesHeaderController />, container);
+      });
+
+      afterEach(function () {
+        Stores.changesStore.reset();
+        React.unmountComponentAtNode(container);
+      });
+
+      // similar as previous, except it confirms that the action gets fired, not the custom toggle func
+      it('calls toggleTabVisibility action on selecting a tab', function () {
+        TestUtils.Simulate.click($(tab.getDOMNode()).find('a')[0]);
+        assert.ok(spy.calledOnce);
+      });
+    });
+  });
+
+  describe('ChangesHeaderTab', function () {
+    var container, tab, toggleTabVisibility;
+
+    beforeEach(function () {
+      toggleTabVisibility = sinon.spy();
+      container = document.createElement('div');
+      tab = TestUtils.renderIntoDocument(<Changes.ChangesHeaderTab onToggle={toggleTabVisibility} />, container);
+    });
+
+    afterEach(function () {
+      Stores.changesStore.reset();
+      React.unmountComponentAtNode(container);
+    });
+
+    it('should call toggle function on clicking tab', function () {
+      TestUtils.Simulate.click($(tab.getDOMNode()).find('a')[0]);
+      assert.ok(toggleTabVisibility.calledOnce);
+    });
+  });
+
+
+  describe('ChangesTabContent', function () {
+    var container, changesFilterEl;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+      changesFilterEl = TestUtils.renderIntoDocument(<Changes.ChangesTabContent />, container);
+    });
+
+    afterEach(function () {
+      Stores.changesStore.reset();
+      React.unmountComponentAtNode(container);
+    });
+
+    it('should add filter markup', function () {
+      var $el = $(changesFilterEl.getDOMNode()),
+          submitBtn = $el.find('[type="submit"]')[0],
+          addItemField = $el.find('.js-changes-filter-field')[0];
+
+      addItemField.value = 'I wandered lonely as a filter';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      addItemField.value = 'A second filter';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      assert.equal(2, $el.find('.js-remove-filter').length);
+    });
+
+    it('should call addFilter action on click', function () {
+      var $el = $(changesFilterEl.getDOMNode()),
+        submitBtn = $el.find('[type="submit"]')[0],
+        addItemField = $el.find('.js-changes-filter-field')[0];
+
+      var spy = sinon.spy(Actions, 'addFilter');
+
+      addItemField.value = 'I wandered lonely as a filter';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      assert.ok(spy.calledOnce);
+    });
+
+    it('should remove filter markup', function () {
+      var $el = $(changesFilterEl.getDOMNode()),
+        submitBtn = $el.find('[type="submit"]')[0],
+        addItemField = $el.find('.js-changes-filter-field')[0];
+
+      addItemField.value = 'Bloop';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      addItemField.value = 'Flibble';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      // clicks ALL 'remove' elements
+      TestUtils.Simulate.click($el.find('.js-remove-filter')[0]);
+      TestUtils.Simulate.click($el.find('.js-remove-filter')[0]);
+
+      assert.equal(0, $el.find('.js-remove-filter').length);
+    });
+
+    it('should call removeFilter action on click', function () {
+      var $el = $(changesFilterEl.getDOMNode()),
+        submitBtn = $el.find('[type="submit"]')[0],
+        addItemField = $el.find('.js-changes-filter-field')[0];
+
+      var spy = sinon.spy(Actions, 'removeFilter');
+
+      addItemField.value = 'I wandered lonely as a filter';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+      TestUtils.Simulate.click($el.find('.js-remove-filter')[0]);
+
+      assert.ok(spy.calledOnce);
+    });
+
+    it('should not add empty filters', function () {
+      var $el = $(changesFilterEl.getDOMNode()),
+        submitBtn = $el.find('[type="submit"]')[0],
+        addItemField = $el.find('.js-changes-filter-field')[0];
+
+      addItemField.value = '';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      assert.equal(0, $el.find('.js-remove-filter').length);
+    });
+
+    it('should not add tooltips by default', function () {
+      assert.equal(0, $(changesFilterEl.getDOMNode()).find('.js-remove-filter').length);
+    });
+
+    it('should not add the same filter twice', function () {
+      var $el = $(changesFilterEl.getDOMNode()),
+        submitBtn = $el.find('[type="submit"]')[0],
+        addItemField = $el.find('.js-changes-filter-field')[0];
+
+      var filter = 'I am unique in the whole wide world';
+      addItemField.value = filter;
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      addItemField.value = filter;
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      assert.equal(1, $el.find('.js-remove-filter').length);
+    });
+  });
+
+
+  // tests Changes Controller; includes tests in conjunction with ChangesHeaderController
+  describe('ChangesController', function () {
+    var containerEl, headerEl, $headerEl, changesEl, $changesEl;
+
+    var results = [
+      { id: 'doc_1', seq: 4, deleted: false, changes: { code: 'here' } },
+      { id: 'doc_2', seq: 1, deleted: false, changes: { code: 'here' } },
+      { id: 'doc_3', seq: 6, deleted: true, changes: { code: 'here' } },
+      { id: 'doc_4', seq: 7, deleted: false, changes: { code: 'here' } },
+      { id: 'doc_5', seq: 1, deleted: true, changes: { code: 'here' } }
+    ];
+    var changesResponse = JSON.stringify({
+      last_seq: 123,
+      'results': results
+    });
+
+    beforeEach(function () {
+      Actions.initChanges({ databaseName: 'testDatabase' });
+      headerEl  = TestUtils.renderIntoDocument(<Changes.ChangesHeaderController />, containerEl);
+      $headerEl = $(headerEl.getDOMNode());
+      changesEl = TestUtils.renderIntoDocument(<Changes.ChangesController />, containerEl);
+      $changesEl = $(changesEl.getDOMNode());
+      Actions.updateChanges(changesResponse);
+    });
+
+    afterEach(function () {
+      Stores.changesStore.reset();
+      React.unmountComponentAtNode(containerEl);
+    });
+
+
+    it('should list the right number of changes', function () {
+      assert.equal(results.length, $changesEl.find('.change-box').length);
+    });
+
+
+    it('"false"/"true" filter strings should apply to change deleted status', function () {
+      // expand the header
+      TestUtils.Simulate.click($headerEl.find('a')[0]);
+
+      // add a filter
+      var addItemField = $headerEl.find('.js-changes-filter-field')[0];
+      var submitBtn = $headerEl.find('[type="submit"]')[0];
+      addItemField.value = 'true';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      // confirm only the two deleted items shows up and the IDs maps to the deleted rows
+      assert.equal(2, $changesEl.find('.change-box').length);
+      assert.equal('doc_3', $($changesEl.find('.js-doc-id').get(0)).html());
+      assert.equal('doc_5', $($changesEl.find('.js-doc-id').get(1)).html());
+    });
+
+
+    it('confirms that a filter affects the actual search results', function () {
+      // expand the header
+      TestUtils.Simulate.click($headerEl.find('a')[0]);
+
+      // add a filter
+      var addItemField = $headerEl.find('.js-changes-filter-field')[0];
+      var submitBtn = $headerEl.find('[type="submit"]')[0];
+      addItemField.value = '6'; // should match doc_3's sequence ID
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      // confirm only one item shows up and the ID maps to what we'd expect
+      assert.equal(1, $changesEl.find('.change-box').length);
+      assert.equal('doc_3', $($changesEl.find('.js-doc-id').get(0)).html());
+    });
+
+
+    // confirms that if there are multiple filters, ALL are applied to return the subset of results that match
+    // all filters
+    it('multiple filters should all be applied to results', function () {
+      TestUtils.Simulate.click($headerEl.find('a')[0]);
+
+      // add the filters
+      var addItemField = $headerEl.find('.js-changes-filter-field')[0];
+      var submitBtn = $headerEl.find('[type="submit"]')[0];
+
+      // *** should match doc_1, doc_2 and doc_5
+      addItemField.value = '1';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      // *** should match doc_3 and doc_5
+      addItemField.value = 'true';
+      TestUtils.Simulate.change(addItemField);
+      TestUtils.Simulate.submit(submitBtn);
+
+      // confirm only one item shows up and that it's doc_5
+      assert.equal(1, $changesEl.find('.change-box').length);
+      assert.equal('doc_5', $($changesEl.find('.js-doc-id').get(0)).html());
+    });
+  });
+
+
+  describe('ChangesController max results', function () {
+    var containerEl, changesEl;
+    var maxChanges = 10;
+
+    beforeEach(function () {
+
+      var changes = [];
+      _.times(maxChanges + 10, function (i) {
+        changes.push({ id: 'doc_' + i, seq: 1, changes: { code: 'here' } });
+      });
+
+      var response = JSON.stringify({
+        last_seq: 1,
+        results: changes
+      });
+
+      Actions.initChanges({ databaseName: 'test' });
+
+      // to keep the test speedy, override the default value (1000)
+      Stores.changesStore.setMaxChanges(maxChanges);
+
+      Actions.updateChanges(response);
+      changesEl = TestUtils.renderIntoDocument(<Changes.ChangesController />, containerEl);
+    });
+
+    afterEach(function () {
+      Stores.changesStore.reset();
+      React.unmountComponentAtNode(containerEl);
+    });
+
+    it('should truncate the number of results with very large # of changes', function () {
+      // check there's no more than maxChanges results
+      assert.equal(maxChanges, $(changesEl.getDOMNode()).find('.change-box').length);
+    });
+
+    it('should show a message if the results are truncated', function () {
+      assert.equal(1, $(changesEl.getDOMNode()).find('.changes-result-limit').length);
+    });
+
+  });
+
+
+  describe('ChangeRow', function () {
+    var container;
+    var change = {
+      id: '123',
+      seq: 5,
+      deleted: false,
+      changes: { code: 'here' }
+    };
+
+    beforeEach(function () {
+      container = document.createElement('div');
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+
+    it('clicking the toggle-json button shows the code section', function () {
+      var changeRow = TestUtils.renderIntoDocument(<Changes.ChangeRow change={change} databaseName="testDatabase" />, container);
+
+      // confirm it's hidden by default
+      assert.equal(0, $(changeRow.getDOMNode()).find('.prettyprint').length);
+
+      // confirm clicking it shows the element
+      TestUtils.Simulate.click($(changeRow.getDOMNode()).find('button.btn')[0]);
+      assert.equal(1, $(changeRow.getDOMNode()).find('.prettyprint').length);
+    });
+
+    it('deleted docs should not be clickable', function () {
+      change.deleted = true;
+      var changeRow = TestUtils.renderIntoDocument(<Changes.ChangeRow change={change} databaseName="testDatabase" />, container);
+      assert.equal(0, $(changeRow.getDOMNode()).find('a.js-doc-link').length);
+    });
+
+    it('non-deleted docs should be clickable', function () {
+      change.deleted = false;
+      var changeRow = TestUtils.renderIntoDocument(<Changes.ChangeRow change={change} databaseName="testDatabase" />, container);
+      assert.equal(1, $(changeRow.getDOMNode()).find('a.js-doc-link').length);
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/changes/tests/changes.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/changes/tests/changes.storesSpec.js b/app/addons/documents/changes/tests/changes.storesSpec.js
new file mode 100644
index 0000000..fedfb13
--- /dev/null
+++ b/app/addons/documents/changes/tests/changes.storesSpec.js
@@ -0,0 +1,107 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  'app',
+  'api',
+  'addons/documents/changes/stores',
+  'testUtils'
+], function (app, FauxtonAPI, Stores, utils) {
+  FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+  var assert = utils.assert;
+
+
+  describe('ChangesStore', function () {
+
+    afterEach(function () {
+      Stores.changesStore.reset();
+    });
+
+    it('toggleTabVisibility() changes state in store', function () {
+      assert.ok(Stores.changesStore.isTabVisible() === false);
+      Stores.changesStore.toggleTabVisibility();
+      assert.ok(Stores.changesStore.isTabVisible() === true);
+    });
+
+    it('reset() changes tab visibility to hidden', function () {
+      Stores.changesStore.toggleTabVisibility();
+      Stores.changesStore.reset();
+      assert.ok(Stores.changesStore.isTabVisible() === false);
+    });
+
+    it('addFilter() adds item in store', function () {
+      var filter = 'My filter';
+      Stores.changesStore.addFilter(filter);
+      var filters = Stores.changesStore.getFilters();
+      assert.ok(filters.length === 1);
+      assert.ok(filters[0] === filter);
+    });
+
+    it('removeFilter() removes item from store', function () {
+      var filter1 = 'My filter 1';
+      var filter2 = 'My filter 2';
+      Stores.changesStore.addFilter(filter1);
+      Stores.changesStore.addFilter(filter2);
+      Stores.changesStore.removeFilter(filter1);
+
+      var filters = Stores.changesStore.getFilters();
+      assert.ok(filters.length === 1);
+      assert.ok(filters[0] === filter2);
+    });
+
+    it('hasFilter() finds item in store', function () {
+      var filter = 'My filter';
+      Stores.changesStore.addFilter(filter);
+      assert.ok(Stores.changesStore.hasFilter(filter) === true);
+    });
+
+    it('getDatabaseName() returns database name', function () {
+      var dbName = 'hoopoes';
+      Stores.changesStore.initChanges({ databaseName: dbName });
+      assert.equal(Stores.changesStore.getDatabaseName(), dbName);
+
+      Stores.changesStore.reset();
+      assert.equal(Stores.changesStore.getDatabaseName(), '');
+    });
+
+    it("getChanges() should return a subset if there are a lot of changes", function () {
+
+      // to keep the test speedy, we override the default max value
+      var maxChanges = 10;
+      var changes = [];
+      _.times(maxChanges + 10, function (i) {
+        changes.push({ id: 'doc_' + i, seq: 1, changes: {}});
+      });
+      Stores.changesStore.initChanges({ databaseName: "test" });
+      Stores.changesStore.setMaxChanges(maxChanges);
+
+      var seqNum = 123;
+      Stores.changesStore.updateChanges(seqNum, changes);
+
+      var results = Stores.changesStore.getChanges();
+      assert.equal(maxChanges, results.length);
+    });
+
+    it("tracks last sequence number", function () {
+      assert.equal(null, Stores.changesStore.getLastSeqNum());
+
+      var seqNum = 123;
+      Stores.changesStore.updateChanges(seqNum, []);
+
+      // confirm it's been stored
+      assert.equal(seqNum, Stores.changesStore.getLastSeqNum());
+    });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/header/tests/headerSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/header/tests/headerSpec.react.jsx b/app/addons/documents/header/tests/headerSpec.react.jsx
new file mode 100644
index 0000000..420aebe
--- /dev/null
+++ b/app/addons/documents/header/tests/headerSpec.react.jsx
@@ -0,0 +1,112 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  'api',
+  'addons/documents/header/header.react',
+  'addons/documents/header/header.stores',
+  'addons/documents/header/header.actions',
+
+  'addons/databases/base',
+  'addons/documents/resources',
+  'addons/documents/index-results/actions',
+  'addons/documents/index-results/stores',
+
+  'testUtils',
+  'react'
+], function (FauxtonAPI, Views, Stores, Actions, Databases, Resources,
+             IndexResultsActions, IndexResultsStore, utils, React) {
+
+  var assert = utils.assert;
+  var restore = utils.restore;
+  var TestUtils = React.addons.TestUtils;
+
+  describe('Header Controller', function () {
+    var container, toggleEl;
+    beforeEach(function () {
+      container = document.createElement('div');
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('should not include invalid calssname', function () {
+      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+      assert.equal($(toggleEl.getDOMNode()).find('.undefined').length, 0);
+    });
+
+    it('should use the passed classname', function () {
+      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+      TestUtils.Simulate.click($el[0]);
+      assert.ok($el.hasClass('js-headerbar-togglebutton-selected'));
+    });
+
+    it('should not render the alternative header if the button is not clicked', function () {
+      Actions.resetHeaderController();
+      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+      assert.equal($(toggleEl.getDOMNode()).find('.alternative-header').length, 0);
+    });
+
+    it('should render the alternative header', function () {
+      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+      TestUtils.Simulate.click($el[0]);
+      assert.equal($(toggleEl.getDOMNode()).find('.alternative-header').length, 1);
+    });
+  });
+
+  describe('Bulkdocument Headerbar Controller', function () {
+    var container, header;
+    beforeEach(function () {
+      var database = new Databases.Model({id: 'registry'});
+
+      database.allDocs = new Resources.AllDocs({_id: "ente"}, {
+        database: database,
+        viewMeta: {update_seq: 1},
+        params: {}
+      });
+
+      //override reset so we don't loose the added doc needed for testing
+      database.allDocs.reset = function () {};
+
+      IndexResultsActions.newResultsList({
+        collection: database.allDocs,
+        isListDeletable: false
+      });
+
+
+      container = document.createElement('div');
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+      restore(Actions.collapseDocuments);
+    });
+
+    it('should trigger action', function () {
+      var spy = sinon.spy(Actions, 'collapseDocuments');
+      header = TestUtils.renderIntoDocument(<Views.BulkDocumentHeaderController />, container);
+      TestUtils.Simulate.click($(header.getDOMNode()).find('.control-collapse')[0]);
+
+      assert.ok(spy.calledOnce);
+    });
+
+    it('pressing SelectAll should fill the selected items list', function () {
+      TestUtils.Simulate.click($(header.getDOMNode()).find('.control-select-all')[0]);
+
+      assert.equal(IndexResultsStore.indexResultsStore.getSelectedItemsLength(), 1);
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/index-editor/tests/actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/actionsSpec.js b/app/addons/documents/index-editor/tests/actionsSpec.js
new file mode 100644
index 0000000..dec71fb
--- /dev/null
+++ b/app/addons/documents/index-editor/tests/actionsSpec.js
@@ -0,0 +1,348 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  'api',
+  'addons/documents/index-editor/actions',
+  'addons/documents/resources',
+  'addons/documents/index-editor/actiontypes',
+  'addons/documents/index-editor/stores',
+  'testUtils',
+  'addons/documents/index-results/actions',
+  'addons/documents/base'
+], function (FauxtonAPI, Actions, Documents, ActionTypes, Stores, testUtils, IndexResultsActions) {
+  var assert = testUtils.assert;
+  var restore = testUtils.restore;
+
+  FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+  describe('Index Editor Actions', function () {
+    var database = {
+      safeID: function () { return 'id';}
+    };
+
+    describe('save view', function () {
+      var designDoc, designDocs;
+      beforeEach(function () {
+        designDoc = {
+          _id: '_design/test-doc',
+          _rev: '1-231313',
+          views: {
+            'test-view': {
+              map: 'function () {};',
+            }
+          }
+        };
+        var doc = new Documents.Doc(designDoc, {database: database});
+        designDocs = new Documents.AllDocs([doc], {
+          params: { limit: 10 },
+          database: database
+        });
+
+        designDocs = designDocs.models;
+      });
+
+      afterEach(function () {
+        restore(FauxtonAPI.navigate);
+        restore(FauxtonAPI.triggerRouteEvent);
+        restore(IndexResultsActions.reloadResultsList);
+        restore(Actions.updateDesignDoc);
+      });
+
+      it('shows a notification if no design doc id given', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+
+        var viewInfo = {
+          database: database,
+          viewName: 'new-doc',
+          designDocId: undefined,
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: true,
+          newView: true,
+          designDocs: designDocs
+        };
+
+        Actions.saveView(viewInfo);
+        assert.ok(spy.calledOnce);
+        FauxtonAPI.addNotification.restore();
+      });
+
+      it('creates new design Doc for new design doc', function () {
+        var spy = sinon.spy(Actions.helpers, 'createNewDesignDoc');
+
+        var viewInfo = {
+          database: database,
+          viewName: 'new-doc',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: true,
+          newView: true,
+          designDocs: designDocs
+        };
+
+        Actions.saveView(viewInfo);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('sets the design doc with updated view', function () {
+        var viewInfo = {
+          viewName: 'test-view',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: false,
+          newView: true,
+          designDocs: designDocs
+        };
+
+        Actions.saveView(viewInfo);
+
+        var updatedDesignDoc = _.first(designDocs).dDocModel();
+        assert.equal(updatedDesignDoc.get('views')['test-view'].reduce, '_sum');
+      });
+
+      it('saves doc', function () {
+        var viewInfo = {
+          viewName: 'test-view',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: false,
+          newView: true,
+          designDocs: designDocs
+        };
+
+        var updatedDesignDoc = _.first(designDocs).dDocModel();
+        var spy = sinon.spy(updatedDesignDoc, 'save');
+        Actions.saveView(viewInfo);
+
+        assert.ok(spy.calledOnce);
+      });
+
+      it('updates design doc', function () {
+        var viewInfo = {
+          viewName: 'test-view',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: false,
+          newView: false,
+          designDocs: designDocs,
+          database: {
+            safeID: function () { return '1';}
+          }
+        };
+
+        designDocs.find = function () {};
+        designDocs.add = function () {};
+        designDocs.dDocModel = function () {};
+
+        Actions.editIndex({
+          database: {id: 'rockos-db'},
+          newView: true,
+          viewName: 'test-view',
+          designDocs: designDocs,
+          designDocId: designDocs[0]._id
+        });
+
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        var updatedDesignDoc = _.first(designDocs).dDocModel();
+        var stub = sinon.stub(updatedDesignDoc, 'save');
+        stub.returns(promise);
+
+        var spy = sinon.spy(Actions, 'updateDesignDoc');
+        Actions.saveView(viewInfo);
+
+        assert.ok(spy.calledOnce);
+      });
+
+      it('navigates to new url for new view', function () {
+        var spy = sinon.spy(FauxtonAPI, 'navigate');
+
+        var viewInfo = {
+          database: database,
+          viewName: 'test-view',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: false,
+          newView: true,
+          designDocs: designDocs
+        };
+        var designDoc = _.first(designDocs);
+
+        designDoc.save = function () {
+          var promise = $.Deferred();
+          promise.resolve();
+          return promise;
+        };
+
+        Actions.saveView(viewInfo);
+        assert.ok(spy.calledOnce);
+        assert.ok(spy.getCall(0).args[0].match(/_view\/test-view/));
+      });
+
+      it('triggers reload results list', function () {
+        var spy = sinon.spy(IndexResultsActions, 'reloadResultsList');
+
+        var viewInfo = {
+          viewName: 'test-view',
+          designDocId: '_design/test-doc',
+          map: 'map',
+          reduce: '_sum',
+          newDesignDoc: false,
+          newView: false,
+          designDocs: designDocs,
+          database: {
+            safeID: function () {
+              return 'foo';
+            }
+          }
+        };
+        var designDoc = _.first(designDocs);
+
+        designDoc.save = function () {
+          var promise = $.Deferred();
+          promise.resolve();
+          return promise;
+        };
+
+        var stub = sinon.stub(Actions, 'updateDesignDoc');
+        stub.returns(true);
+
+        Actions.saveView(viewInfo);
+        assert.ok(spy.calledOnce);
+      });
+    });
+
+    describe('delete view', function () {
+      var designDocs, database, designDoc, designDocId, viewName;
+      beforeEach(function () {
+        database = {
+          safeID: function () { return 'safeid';}
+        };
+
+        viewName = 'test-view';
+        designDocId = '_design/test-doc';
+        designDocs = new Documents.AllDocs([{
+          _id: designDocId ,
+          _rev: '1-231',
+          views: {
+              'test-view': {
+                map: 'function () {};',
+              },
+              'test-view2': {
+                map: 'function () {};',
+              }
+            }
+          }], {
+          params: { limit: 10 },
+          database: database
+        });
+        designDocs = designDocs.models;
+        designDoc = _.first(designDocs);
+
+      });
+
+      afterEach(function () {
+        restore(FauxtonAPI.navigate);
+        restore(FauxtonAPI.triggerRouteEvent);
+      });
+
+      it('removes view from design doc', function () {
+
+        Actions.deleteView({
+          viewName: viewName,
+          designDocId: designDocId,
+          database: database,
+          designDocs: designDocs
+        });
+
+        assert.ok(_.isUndefined(designDoc.getDdocView(viewName)));
+      });
+
+      it('saves design doc if has other views', function () {
+        var spy = sinon.spy(designDoc, 'save');
+
+        Actions.deleteView({
+          viewName: viewName,
+          designDocId: designDocId,
+          database: database,
+          designDocs: designDocs
+        });
+
+        assert.ok(spy.calledOnce);
+      });
+
+      it('deletes design doc if has no other views', function () {
+        var spy = sinon.spy(designDoc, 'destroy');
+        designDoc.removeDdocView('test-view2');
+
+        Actions.deleteView({
+          viewName: viewName,
+          designDocId: designDocId,
+          database: database,
+          designDocs: designDocs
+        });
+
+        assert.ok(spy.calledOnce);
+
+      });
+
+      it('navigates to all docs', function () {
+        var spy = sinon.spy(FauxtonAPI, 'navigate');
+
+        designDoc.save = function () {
+          var promise = $.Deferred();
+          promise.resolve();
+          return promise;
+        };
+
+        Actions.deleteView({
+          viewName: viewName,
+          designDocId: designDocId,
+          database: database,
+          designDocs: designDocs
+        });
+
+
+        assert.ok(spy.getCall(0).args[0].match(/_all_docs/));
+        assert.ok(spy.calledOnce);
+      });
+
+      it('triggers design doc reload', function () {
+        var spy = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
+
+        designDoc.save = function () {
+          var promise = $.Deferred();
+          promise.resolve();
+          return promise;
+        };
+
+        Actions.deleteView({
+          viewName: viewName,
+          designDocId: designDocId,
+          database: database,
+          designDocs: designDocs
+        });
+
+        assert.ok(spy.calledOnce);
+        assert.equal(spy.getCall(0).args[0], 'reloadDesignDocs');
+      });
+
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/index-editor/tests/storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/storesSpec.js b/app/addons/documents/index-editor/tests/storesSpec.js
new file mode 100644
index 0000000..c60295c
--- /dev/null
+++ b/app/addons/documents/index-editor/tests/storesSpec.js
@@ -0,0 +1,311 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+define([
+  'api',
+  'addons/documents/index-editor/stores',
+  'addons/documents/index-editor/actiontypes',
+  'addons/documents/resources',
+  'testUtils'
+], function (FauxtonAPI, Stores, ActionTypes, Documents, testUtils) {
+  var assert = testUtils.assert;
+  var store;
+  var dispatchToken;
+
+
+  describe('IndexEditorStore', function () {
+
+    beforeEach(function () {
+      store = new Stores.IndexEditorStore();
+      dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch);
+    });
+
+    afterEach(function () {
+      FauxtonAPI.dispatcher.unregister(dispatchToken);
+    });
+
+    describe('map editor', function () {
+
+      describe('new view', function () {
+
+        beforeEach(function () {
+
+          FauxtonAPI.dispatch({
+            type: ActionTypes.EDIT_NEW_INDEX,
+            options: {
+              newView: true
+            }
+          });
+        });
+
+        it('returns default map', function () {
+          assert.equal(store.getMap(), 'function (doc) {\n  emit(doc._id, 1);\n}');
+        });
+      });
+
+    });
+
+    describe('reduce editor', function () {
+
+      describe('has custom reduce', function () {
+
+        it('is false for no reduce', function () {
+          var designDoc = {
+            _id: '_design/test-doc',
+            views: {
+              'test-view': {
+                map: 'function () {};'
+              }
+            }
+          };
+
+          var designDocs = new Documents.AllDocs([designDoc], {
+            params: { limit: 10 },
+            database: {
+              safeID: function () { return 'id';}
+            }
+          });
+
+          FauxtonAPI.dispatch({
+            type: ActionTypes.EDIT_NEW_INDEX,
+            options: {
+              newView: false,
+              viewName: 'test-view',
+              designDocs: designDocs,
+              designDocId: designDoc._id
+            }
+          });
+
+          assert.notOk(store.hasCustomReduce());
+        });
+
+        it('is false for built in reduce', function () {
+          var designDoc = {
+            _id: '_design/test-doc',
+            views: {
+              'test-view': {
+                map: 'function () {};',
+                reduce: '_sum'
+              }
+            }
+          };
+
+          var designDocs = new Documents.AllDocs([designDoc], {
+            params: { limit: 10 },
+            database: {
+              safeID: function () { return 'id';}
+            }
+          });
+          FauxtonAPI.dispatch({
+            type: ActionTypes.EDIT_NEW_INDEX,
+            options: {
+              newView: false,
+              viewName: 'test-view',
+              designDocs: designDocs,
+              designDocId: designDoc._id
+            }
+          });
+
+          assert.notOk(store.hasCustomReduce());
+        });
+
+        it('is true for custom reduce', function () {
+          var designDoc = {
+            _id: '_design/test-doc',
+            views: {
+              'test-view': {
+                map: 'function () {};',
+                reduce: 'function (reduce) { reduce(); }'
+              }
+            }
+          };
+
+          var designDocs = new Documents.AllDocs([designDoc], {
+            params: { limit: 10 },
+            database: {
+              safeID: function () { return 'id';}
+            }
+          });
+
+          FauxtonAPI.dispatch({
+            type: ActionTypes.EDIT_NEW_INDEX,
+            options: {
+              newView: false,
+              viewName: 'test-view',
+              designDocs: designDocs,
+              designDocId: designDoc._id
+            }
+          });
+
+          assert.ok(store.hasCustomReduce());
+        });
+
+      });
+
+      //show default reduce
+      describe('SELECT_REDUCE_CHANGE', function () {
+
+        beforeEach(function () {
+          var designDoc = {
+            _id: '_design/test-doc',
+            views: {
+              'test-view': {
+                map: 'function () {};'
+              }
+            }
+          };
+
+          var designDocs = new Documents.AllDocs([designDoc], {
+            params: { limit: 10 },
+            database: {
+              safeID: function () { return 'id';}
+            }
+          });
+
+          FauxtonAPI.dispatch({
+            type: ActionTypes.EDIT_NEW_INDEX,
+            options: {
+              newView: false,
+              viewName: 'test-view',
+              designDocs: designDocs,
+              designDocId: designDoc._id
+            }
+          });
+        });
+
+        it('NONE returns null reduce', function () {
+          FauxtonAPI.dispatch({
+            type: ActionTypes.SELECT_REDUCE_CHANGE,
+            reduceSelectedOption: 'NONE'
+          });
+          assert.ok(_.isNull(store.getReduce()));
+        });
+
+        it('builtin returns bultin reduce', function () {
+          FauxtonAPI.dispatch({
+            type: ActionTypes.SELECT_REDUCE_CHANGE,
+            reduceSelectedOption: '_sum'
+          });
+          assert.equal(store.getReduce(), '_sum');
+        });
+
+        it('custom returns custom reduce', function () {
+          FauxtonAPI.dispatch({
+            type: ActionTypes.SELECT_REDUCE_CHANGE,
+            reduceSelectedOption: 'CUSTOM'
+          });
+          assert.equal(store.getReduce(), 'function (keys, values, rereduce) {\n  if (rereduce) {\n    return sum(values);\n  } else {\n    return values.length;\n  }\n}');
+        });
+      });
+    });
+
+
+    describe('design doc selector', function () {
+      var designDoc;
+
+      beforeEach(function () {
+        designDoc = {
+          _id: '_design/test-doc',
+          views: {
+            'test-view': {
+              map: 'boom'
+            }
+          }
+        };
+
+        var designDocs = new Documents.AllDocs([designDoc], {
+          params: { limit: 10 },
+          database: {
+            safeID: function () { return 'id';}
+          }
+        });
+
+        FauxtonAPI.dispatch({
+          type: ActionTypes.EDIT_INDEX,
+          options: {
+            newView: false,
+            viewName: 'test-view',
+            designDocs: designDocs,
+            designDocId: designDoc._id
+          }
+        });
+      });
+
+      it('DESIGN_DOC_CHANGE changes design doc id', function () {
+        var designDocId =  'another-one';
+        FauxtonAPI.dispatch({
+          type: ActionTypes.DESIGN_DOC_CHANGE,
+          designDocId: designDocId,
+          newDesignDoc: false
+        });
+
+        assert.equal(store.getDesignDocId(), designDocId);
+        assert.notOk(store.isNewDesignDoc());
+      });
+
+      it('sets new design doc on NEW_DESIGN_DOC', function () {
+        FauxtonAPI.dispatch({
+          type: ActionTypes.NEW_DESIGN_DOC
+        });
+
+        assert.ok(store.isNewDesignDoc());
+        assert.equal(store.getDesignDocId(), '');
+      });
+    });
+
+    describe('EDIT_INDEX', function () {
+      var designDoc, designDocs;
+
+      beforeEach(function () {
+        designDoc = {
+          _id: '_design/test-doc',
+          views: {
+            'test-view': {
+              map: 'boom'
+            }
+          }
+        };
+
+        designDocs = new Documents.AllDocs([designDoc], {
+          params: { limit: 10 },
+          database: {
+            safeID: function () { return 'id';}
+          }
+        });
+
+      });
+
+      it('can set reduce for new design doc', function () {
+        FauxtonAPI.dispatch({
+          type: ActionTypes.EDIT_INDEX,
+          options: {
+            newView: true,
+            newDesignDoc: true,
+            viewName: 'test-view',
+            designDocs: designDocs,
+            designDocId: undefined
+          }
+        });
+
+        FauxtonAPI.dispatch({
+          type: ActionTypes.SELECT_REDUCE_CHANGE,
+          reduceSelectedOption: '_sum'
+        });
+
+        assert.equal(store.getReduce(), '_sum');
+      });
+
+    });
+
+  });
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
new file mode 100644
index 0000000..9cac62d
--- /dev/null
+++ b/app/addons/documents/index-editor/tests/viewIndex.componentsSpec.react.jsx
@@ -0,0 +1,268 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+define([
+  'api',
+  'addons/documents/index-editor/components.react',
+  'addons/documents/index-editor/stores',
+  'addons/documents/index-editor/actions',
+  'addons/documents/resources',
+  'testUtils',
+  "react"
+], function (FauxtonAPI, Views, Stores, Actions, Documents, utils, React) {
+  FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+  var restore = utils.restore;
+
+  var resetStore = function (designDocs) {
+    designDocs = designDocs.map(function (doc) {
+      return Documents.Doc.prototype.parse(doc);
+    });
+
+    var ddocs = new Documents.AllDocs(designDocs, {
+      params: { limit: 10 },
+      database: {
+        safeID: function () { return 'id';}
+      }
+    });
+
+    Actions.editIndex({
+      database: {id: 'rockos-db'},
+      newView: false,
+      viewName: 'test-view',
+      designDocs: ddocs,
+      designDocId: designDocs[0]._id
+    });
+  };
+
+  describe('reduce editor', function () {
+    var container, reduceEl;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    describe('getReduceValue', function () {
+      var container;
+
+      beforeEach(function () {
+        container = document.createElement('div');
+        $('body').append('<div id="reduce-function"></div>');
+      });
+
+      it('returns null for none', function () {
+        var store = Stores.indexEditorStore;
+
+        var designDoc = {
+          _id: '_design/test-doc',
+          views: {
+            'test-view': {
+              map: 'function () {};',
+              //reduce: 'function (reduce) { reduce(); }'
+            }
+          }
+        };
+
+        resetStore([designDoc]);
+
+        reduceEl = TestUtils.renderIntoDocument(<Views.ReduceEditor/>, container);
+        assert.ok(_.isNull(reduceEl.getReduceValue()));
+      });
+
+      it('returns built in for built in reduce', function () {
+        var store = Stores.indexEditorStore;
+
+        var designDoc = {
+          _id: '_design/test-doc',
+          views: {
+            'test-view': {
+              map: 'function () {};',
+              reduce: '_sum'
+            }
+          }
+        };
+
+        resetStore([designDoc]);
+
+        reduceEl = TestUtils.renderIntoDocument(<Views.ReduceEditor/>, container);
+        assert.equal(reduceEl.getReduceValue(), '_sum');
+      });
+
+    });
+  });
+
+  describe('design Doc Selector', function () {
+    var container, selectorEl;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+      $('body').append('<div id="map-function"></div>');
+      $('body').append('<div id="editor"></div>');
+      var designDoc = {
+        "id": "_design/test-doc",
+        "key": "_design/test-doc",
+        "value": {
+          "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
+        },
+        "doc": {
+          "_id": "_design/test-doc",
+          "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
+          "views": {
+            "test-view": {
+              "map": "function(doc) {\n  emit(doc._id, 2);\n}"
+            },
+            "new-view": {
+              "map": "function(doc) {\n  if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n    emit(doc._id, 1);\n}",
+              "reduce": "_sum"
+            }
+          },
+          "language": "javascript",
+          "indexes": {
+            "newSearch": {
+              "analyzer": "standard",
+              "index": "function(doc){\n index(\"default\", doc._id);\n}"
+            }
+          }
+        }
+      };
+      var mangodoc = {
+        "id": "_design/123mango",
+        "key": "_design/123mango",
+        "value": {
+          "rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80"
+        },
+        "doc": {
+          "_id": "_design/123mango",
+          "_rev": "20-9e4bc8b76fd7d752d620bbe6e0ea9a80",
+          "views": {
+            "test-view": {
+              "map": "function(doc) {\n  emit(doc._id, 2);\n}"
+            },
+            "new-view": {
+              "map": "function(doc) {\n  if (doc.class === \"mammal\" && doc.diet === \"herbivore\")\n    emit(doc._id, 1);\n}",
+              "reduce": "_sum"
+            }
+          },
+          "language": "query",
+          "indexes": {
+            "newSearch": {
+              "analyzer": "standard",
+              "index": "function(doc){\n index(\"default\", doc._id);\n}"
+            }
+          }
+        }
+      };
+      resetStore([designDoc, mangodoc]);
+      selectorEl = TestUtils.renderIntoDocument(<Views.DesignDocSelector/>, container);
+    });
+
+
+    afterEach(function () {
+      restore(Actions.newDesignDoc);
+      restore(Actions.designDocChange);
+      React.unmountComponentAtNode(container);
+    });
+
+    it('calls new design doc on new selected', function () {
+      var spy = sinon.spy(Actions, 'newDesignDoc');
+      TestUtils.Simulate.change($(selectorEl.getDOMNode()).find('#ddoc')[0], {
+        target: {
+          value: 'new'
+        }
+      });
+
+      assert.ok(spy.calledOnce);
+    });
+
+    it('calls design doc changed on a different design doc selected', function () {
+      var spy = sinon.spy(Actions, 'designDocChange');
+      TestUtils.Simulate.change($(selectorEl.getDOMNode()).find('#ddoc')[0], {
+        target: {
+          value: 'another-doc'
+        }
+      });
+
+      assert.ok(spy.calledWith('another-doc', false));
+    });
+
+    it('calls design doc changed on new design doc entered', function () {
+      var spy = sinon.spy(Actions, 'designDocChange');
+      Actions.newDesignDoc();
+      TestUtils.Simulate.change($(selectorEl.getDOMNode()).find('#new-ddoc')[0], {
+        target: {
+          value: 'new-doc-entered'
+        }
+      });
+
+      assert.ok(spy.calledWith('_design/new-doc-entered', true));
+    });
+
+    it('does not filter usual design docs', function () {
+      assert.ok(/_design\/test-doc/.test($(selectorEl.getDOMNode()).text()));
+    });
+
+    it('filters mango docs', function () {
+      selectorEl = TestUtils.renderIntoDocument(<Views.DesignDocSelector/>, container);
+      assert.notOk(/_design\/123mango/.test($(selectorEl.getDOMNode()).text()));
+    });
+  });
+
+  describe('Editor', function () {
+    var container, editorEl, reduceStub;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+      $('body').append('<div id="map-function"></div>');
+      $('body').append('<div id="editor"></div>');
+      editorEl = TestUtils.renderIntoDocument(<Views.Editor/>, container);
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('returns false on invalid map editor code', function () {
+      var stub = sinon.stub(editorEl.refs.mapEditor, 'hadValidCode');
+      stub.returns(false);
+      assert.notOk(editorEl.hasValidCode());
+    });
+
+    it('returns true on valid map editor code', function () {
+      var stub = sinon.stub(editorEl.refs.mapEditor, 'hadValidCode');
+      stub.returns(true);
+      assert.ok(editorEl.hasValidCode());
+    });
+
+    it('returns true on non-custom reduce', function () {
+      var stub = sinon.stub(Stores.indexEditorStore, 'hasCustomReduce');
+      stub.returns(false);
+      assert.ok(editorEl.hasValidCode());
+    });
+
+    it('calls changeViewName on view name change', function () {
+      var viewName = 'new-name';
+      var spy = sinon.spy(Actions, 'changeViewName');
+      var el = $(editorEl.getDOMNode()).find('#index-name')[0];
+      TestUtils.Simulate.change(el, {
+        target: {
+          value: viewName
+        }
+      });
+      assert.ok(spy.calledWith(viewName));
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/tests/actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/actionsSpec.js b/app/addons/documents/tests/actionsSpec.js
deleted file mode 100644
index dec71fb..0000000
--- a/app/addons/documents/tests/actionsSpec.js
+++ /dev/null
@@ -1,348 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
-  'api',
-  'addons/documents/index-editor/actions',
-  'addons/documents/resources',
-  'addons/documents/index-editor/actiontypes',
-  'addons/documents/index-editor/stores',
-  'testUtils',
-  'addons/documents/index-results/actions',
-  'addons/documents/base'
-], function (FauxtonAPI, Actions, Documents, ActionTypes, Stores, testUtils, IndexResultsActions) {
-  var assert = testUtils.assert;
-  var restore = testUtils.restore;
-
-  FauxtonAPI.router = new FauxtonAPI.Router([]);
-
-  describe('Index Editor Actions', function () {
-    var database = {
-      safeID: function () { return 'id';}
-    };
-
-    describe('save view', function () {
-      var designDoc, designDocs;
-      beforeEach(function () {
-        designDoc = {
-          _id: '_design/test-doc',
-          _rev: '1-231313',
-          views: {
-            'test-view': {
-              map: 'function () {};',
-            }
-          }
-        };
-        var doc = new Documents.Doc(designDoc, {database: database});
-        designDocs = new Documents.AllDocs([doc], {
-          params: { limit: 10 },
-          database: database
-        });
-
-        designDocs = designDocs.models;
-      });
-
-      afterEach(function () {
-        restore(FauxtonAPI.navigate);
-        restore(FauxtonAPI.triggerRouteEvent);
-        restore(IndexResultsActions.reloadResultsList);
-        restore(Actions.updateDesignDoc);
-      });
-
-      it('shows a notification if no design doc id given', function () {
-        var spy = sinon.spy(FauxtonAPI, 'addNotification');
-
-        var viewInfo = {
-          database: database,
-          viewName: 'new-doc',
-          designDocId: undefined,
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: true,
-          newView: true,
-          designDocs: designDocs
-        };
-
-        Actions.saveView(viewInfo);
-        assert.ok(spy.calledOnce);
-        FauxtonAPI.addNotification.restore();
-      });
-
-      it('creates new design Doc for new design doc', function () {
-        var spy = sinon.spy(Actions.helpers, 'createNewDesignDoc');
-
-        var viewInfo = {
-          database: database,
-          viewName: 'new-doc',
-          designDocId: '_design/test-doc',
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: true,
-          newView: true,
-          designDocs: designDocs
-        };
-
-        Actions.saveView(viewInfo);
-        assert.ok(spy.calledOnce);
-      });
-
-      it('sets the design doc with updated view', function () {
-        var viewInfo = {
-          viewName: 'test-view',
-          designDocId: '_design/test-doc',
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: false,
-          newView: true,
-          designDocs: designDocs
-        };
-
-        Actions.saveView(viewInfo);
-
-        var updatedDesignDoc = _.first(designDocs).dDocModel();
-        assert.equal(updatedDesignDoc.get('views')['test-view'].reduce, '_sum');
-      });
-
-      it('saves doc', function () {
-        var viewInfo = {
-          viewName: 'test-view',
-          designDocId: '_design/test-doc',
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: false,
-          newView: true,
-          designDocs: designDocs
-        };
-
-        var updatedDesignDoc = _.first(designDocs).dDocModel();
-        var spy = sinon.spy(updatedDesignDoc, 'save');
-        Actions.saveView(viewInfo);
-
-        assert.ok(spy.calledOnce);
-      });
-
-      it('updates design doc', function () {
-        var viewInfo = {
-          viewName: 'test-view',
-          designDocId: '_design/test-doc',
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: false,
-          newView: false,
-          designDocs: designDocs,
-          database: {
-            safeID: function () { return '1';}
-          }
-        };
-
-        designDocs.find = function () {};
-        designDocs.add = function () {};
-        designDocs.dDocModel = function () {};
-
-        Actions.editIndex({
-          database: {id: 'rockos-db'},
-          newView: true,
-          viewName: 'test-view',
-          designDocs: designDocs,
-          designDocId: designDocs[0]._id
-        });
-
-        var promise = FauxtonAPI.Deferred();
-        promise.resolve();
-
-        var updatedDesignDoc = _.first(designDocs).dDocModel();
-        var stub = sinon.stub(updatedDesignDoc, 'save');
-        stub.returns(promise);
-
-        var spy = sinon.spy(Actions, 'updateDesignDoc');
-        Actions.saveView(viewInfo);
-
-        assert.ok(spy.calledOnce);
-      });
-
-      it('navigates to new url for new view', function () {
-        var spy = sinon.spy(FauxtonAPI, 'navigate');
-
-        var viewInfo = {
-          database: database,
-          viewName: 'test-view',
-          designDocId: '_design/test-doc',
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: false,
-          newView: true,
-          designDocs: designDocs
-        };
-        var designDoc = _.first(designDocs);
-
-        designDoc.save = function () {
-          var promise = $.Deferred();
-          promise.resolve();
-          return promise;
-        };
-
-        Actions.saveView(viewInfo);
-        assert.ok(spy.calledOnce);
-        assert.ok(spy.getCall(0).args[0].match(/_view\/test-view/));
-      });
-
-      it('triggers reload results list', function () {
-        var spy = sinon.spy(IndexResultsActions, 'reloadResultsList');
-
-        var viewInfo = {
-          viewName: 'test-view',
-          designDocId: '_design/test-doc',
-          map: 'map',
-          reduce: '_sum',
-          newDesignDoc: false,
-          newView: false,
-          designDocs: designDocs,
-          database: {
-            safeID: function () {
-              return 'foo';
-            }
-          }
-        };
-        var designDoc = _.first(designDocs);
-
-        designDoc.save = function () {
-          var promise = $.Deferred();
-          promise.resolve();
-          return promise;
-        };
-
-        var stub = sinon.stub(Actions, 'updateDesignDoc');
-        stub.returns(true);
-
-        Actions.saveView(viewInfo);
-        assert.ok(spy.calledOnce);
-      });
-    });
-
-    describe('delete view', function () {
-      var designDocs, database, designDoc, designDocId, viewName;
-      beforeEach(function () {
-        database = {
-          safeID: function () { return 'safeid';}
-        };
-
-        viewName = 'test-view';
-        designDocId = '_design/test-doc';
-        designDocs = new Documents.AllDocs([{
-          _id: designDocId ,
-          _rev: '1-231',
-          views: {
-              'test-view': {
-                map: 'function () {};',
-              },
-              'test-view2': {
-                map: 'function () {};',
-              }
-            }
-          }], {
-          params: { limit: 10 },
-          database: database
-        });
-        designDocs = designDocs.models;
-        designDoc = _.first(designDocs);
-
-      });
-
-      afterEach(function () {
-        restore(FauxtonAPI.navigate);
-        restore(FauxtonAPI.triggerRouteEvent);
-      });
-
-      it('removes view from design doc', function () {
-
-        Actions.deleteView({
-          viewName: viewName,
-          designDocId: designDocId,
-          database: database,
-          designDocs: designDocs
-        });
-
-        assert.ok(_.isUndefined(designDoc.getDdocView(viewName)));
-      });
-
-      it('saves design doc if has other views', function () {
-        var spy = sinon.spy(designDoc, 'save');
-
-        Actions.deleteView({
-          viewName: viewName,
-          designDocId: designDocId,
-          database: database,
-          designDocs: designDocs
-        });
-
-        assert.ok(spy.calledOnce);
-      });
-
-      it('deletes design doc if has no other views', function () {
-        var spy = sinon.spy(designDoc, 'destroy');
-        designDoc.removeDdocView('test-view2');
-
-        Actions.deleteView({
-          viewName: viewName,
-          designDocId: designDocId,
-          database: database,
-          designDocs: designDocs
-        });
-
-        assert.ok(spy.calledOnce);
-
-      });
-
-      it('navigates to all docs', function () {
-        var spy = sinon.spy(FauxtonAPI, 'navigate');
-
-        designDoc.save = function () {
-          var promise = $.Deferred();
-          promise.resolve();
-          return promise;
-        };
-
-        Actions.deleteView({
-          viewName: viewName,
-          designDocId: designDocId,
-          database: database,
-          designDocs: designDocs
-        });
-
-
-        assert.ok(spy.getCall(0).args[0].match(/_all_docs/));
-        assert.ok(spy.calledOnce);
-      });
-
-      it('triggers design doc reload', function () {
-        var spy = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
-
-        designDoc.save = function () {
-          var promise = $.Deferred();
-          promise.resolve();
-          return promise;
-        };
-
-        Actions.deleteView({
-          viewName: viewName,
-          designDocId: designDocId,
-          database: database,
-          designDocs: designDocs
-        });
-
-        assert.ok(spy.calledOnce);
-        assert.equal(spy.getCall(0).args[0], 'reloadDesignDocs');
-      });
-
-    });
-  });
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/tests/changes.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/changes.componentsSpec.react.jsx b/app/addons/documents/tests/changes.componentsSpec.react.jsx
deleted file mode 100644
index ecf3f35..0000000
--- a/app/addons/documents/tests/changes.componentsSpec.react.jsx
+++ /dev/null
@@ -1,375 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
-  'app',
-  'api',
-  'react',
-  'addons/documents/changes/components.react',
-  'addons/documents/changes/stores',
-  'addons/documents/changes/actions',
-  'testUtils'
-], function (app, FauxtonAPI, React, Changes, Stores, Actions, utils) {
-  FauxtonAPI.router = new FauxtonAPI.Router([]);
-
-  var assert = utils.assert;
-  var TestUtils = React.addons.TestUtils;
-
-
-  // suppresses unwanted console.log()'s on missing URLs
-  FauxtonAPI.registerUrls('document', {
-    server: function (database, doc) { return app.host + '/' + database + '/' + doc; },
-    app: function (database, doc) { return '/database/' + database + '/' + doc; },
-    apiurl: function (database, doc) { return window.location.origin + '/' + database + '/' + doc; },
-    'web-index': function (database, doc) { return '/database/' + database + '/' + doc; }
-  });
-
-
-  describe('ChangesHeader', function () {
-    var container, tab, spy;
-
-    describe('Testing DOM', function () {
-      beforeEach(function () {
-        spy = sinon.spy(Actions, 'toggleTabVisibility');
-        container = document.createElement('div');
-        tab = TestUtils.renderIntoDocument(<Changes.ChangesHeaderController />, container);
-      });
-
-      afterEach(function () {
-        Stores.changesStore.reset();
-        React.unmountComponentAtNode(container);
-      });
-
-      // similar as previous, except it confirms that the action gets fired, not the custom toggle func
-      it('calls toggleTabVisibility action on selecting a tab', function () {
-        TestUtils.Simulate.click($(tab.getDOMNode()).find('a')[0]);
-        assert.ok(spy.calledOnce);
-      });
-    });
-  });
-
-  describe('ChangesHeaderTab', function () {
-    var container, tab, toggleTabVisibility;
-
-    beforeEach(function () {
-      toggleTabVisibility = sinon.spy();
-      container = document.createElement('div');
-      tab = TestUtils.renderIntoDocument(<Changes.ChangesHeaderTab onToggle={toggleTabVisibility} />, container);
-    });
-
-    afterEach(function () {
-      Stores.changesStore.reset();
-      React.unmountComponentAtNode(container);
-    });
-
-    it('should call toggle function on clicking tab', function () {
-      TestUtils.Simulate.click($(tab.getDOMNode()).find('a')[0]);
-      assert.ok(toggleTabVisibility.calledOnce);
-    });
-  });
-
-
-  describe('ChangesTabContent', function () {
-    var container, changesFilterEl;
-
-    beforeEach(function () {
-      container = document.createElement('div');
-      changesFilterEl = TestUtils.renderIntoDocument(<Changes.ChangesTabContent />, container);
-    });
-
-    afterEach(function () {
-      Stores.changesStore.reset();
-      React.unmountComponentAtNode(container);
-    });
-
-    it('should add filter markup', function () {
-      var $el = $(changesFilterEl.getDOMNode()),
-          submitBtn = $el.find('[type="submit"]')[0],
-          addItemField = $el.find('.js-changes-filter-field')[0];
-
-      addItemField.value = 'I wandered lonely as a filter';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      addItemField.value = 'A second filter';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      assert.equal(2, $el.find('.js-remove-filter').length);
-    });
-
-    it('should call addFilter action on click', function () {
-      var $el = $(changesFilterEl.getDOMNode()),
-        submitBtn = $el.find('[type="submit"]')[0],
-        addItemField = $el.find('.js-changes-filter-field')[0];
-
-      var spy = sinon.spy(Actions, 'addFilter');
-
-      addItemField.value = 'I wandered lonely as a filter';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      assert.ok(spy.calledOnce);
-    });
-
-    it('should remove filter markup', function () {
-      var $el = $(changesFilterEl.getDOMNode()),
-        submitBtn = $el.find('[type="submit"]')[0],
-        addItemField = $el.find('.js-changes-filter-field')[0];
-
-      addItemField.value = 'Bloop';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      addItemField.value = 'Flibble';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      // clicks ALL 'remove' elements
-      TestUtils.Simulate.click($el.find('.js-remove-filter')[0]);
-      TestUtils.Simulate.click($el.find('.js-remove-filter')[0]);
-
-      assert.equal(0, $el.find('.js-remove-filter').length);
-    });
-
-    it('should call removeFilter action on click', function () {
-      var $el = $(changesFilterEl.getDOMNode()),
-        submitBtn = $el.find('[type="submit"]')[0],
-        addItemField = $el.find('.js-changes-filter-field')[0];
-
-      var spy = sinon.spy(Actions, 'removeFilter');
-
-      addItemField.value = 'I wandered lonely as a filter';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-      TestUtils.Simulate.click($el.find('.js-remove-filter')[0]);
-
-      assert.ok(spy.calledOnce);
-    });
-
-    it('should not add empty filters', function () {
-      var $el = $(changesFilterEl.getDOMNode()),
-        submitBtn = $el.find('[type="submit"]')[0],
-        addItemField = $el.find('.js-changes-filter-field')[0];
-
-      addItemField.value = '';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      assert.equal(0, $el.find('.js-remove-filter').length);
-    });
-
-    it('should not add tooltips by default', function () {
-      assert.equal(0, $(changesFilterEl.getDOMNode()).find('.js-remove-filter').length);
-    });
-
-    it('should not add the same filter twice', function () {
-      var $el = $(changesFilterEl.getDOMNode()),
-        submitBtn = $el.find('[type="submit"]')[0],
-        addItemField = $el.find('.js-changes-filter-field')[0];
-
-      var filter = 'I am unique in the whole wide world';
-      addItemField.value = filter;
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      addItemField.value = filter;
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      assert.equal(1, $el.find('.js-remove-filter').length);
-    });
-  });
-
-
-  // tests Changes Controller; includes tests in conjunction with ChangesHeaderController
-  describe('ChangesController', function () {
-    var containerEl, headerEl, $headerEl, changesEl, $changesEl;
-
-    var results = [
-      { id: 'doc_1', seq: 4, deleted: false, changes: { code: 'here' } },
-      { id: 'doc_2', seq: 1, deleted: false, changes: { code: 'here' } },
-      { id: 'doc_3', seq: 6, deleted: true, changes: { code: 'here' } },
-      { id: 'doc_4', seq: 7, deleted: false, changes: { code: 'here' } },
-      { id: 'doc_5', seq: 1, deleted: true, changes: { code: 'here' } }
-    ];
-    var changesResponse = JSON.stringify({
-      last_seq: 123,
-      'results': results
-    });
-
-    beforeEach(function () {
-      Actions.initChanges({ databaseName: 'testDatabase' });
-      headerEl  = TestUtils.renderIntoDocument(<Changes.ChangesHeaderController />, containerEl);
-      $headerEl = $(headerEl.getDOMNode());
-      changesEl = TestUtils.renderIntoDocument(<Changes.ChangesController />, containerEl);
-      $changesEl = $(changesEl.getDOMNode());
-      Actions.updateChanges(changesResponse);
-    });
-
-    afterEach(function () {
-      Stores.changesStore.reset();
-      React.unmountComponentAtNode(containerEl);
-    });
-
-
-    it('should list the right number of changes', function () {
-      assert.equal(results.length, $changesEl.find('.change-box').length);
-    });
-
-
-    it('"false"/"true" filter strings should apply to change deleted status', function () {
-      // expand the header
-      TestUtils.Simulate.click($headerEl.find('a')[0]);
-
-      // add a filter
-      var addItemField = $headerEl.find('.js-changes-filter-field')[0];
-      var submitBtn = $headerEl.find('[type="submit"]')[0];
-      addItemField.value = 'true';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      // confirm only the two deleted items shows up and the IDs maps to the deleted rows
-      assert.equal(2, $changesEl.find('.change-box').length);
-      assert.equal('doc_3', $($changesEl.find('.js-doc-id').get(0)).html());
-      assert.equal('doc_5', $($changesEl.find('.js-doc-id').get(1)).html());
-    });
-
-
-    it('confirms that a filter affects the actual search results', function () {
-      // expand the header
-      TestUtils.Simulate.click($headerEl.find('a')[0]);
-
-      // add a filter
-      var addItemField = $headerEl.find('.js-changes-filter-field')[0];
-      var submitBtn = $headerEl.find('[type="submit"]')[0];
-      addItemField.value = '6'; // should match doc_3's sequence ID
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      // confirm only one item shows up and the ID maps to what we'd expect
-      assert.equal(1, $changesEl.find('.change-box').length);
-      assert.equal('doc_3', $($changesEl.find('.js-doc-id').get(0)).html());
-    });
-
-
-    // confirms that if there are multiple filters, ALL are applied to return the subset of results that match
-    // all filters
-    it('multiple filters should all be applied to results', function () {
-      TestUtils.Simulate.click($headerEl.find('a')[0]);
-
-      // add the filters
-      var addItemField = $headerEl.find('.js-changes-filter-field')[0];
-      var submitBtn = $headerEl.find('[type="submit"]')[0];
-
-      // *** should match doc_1, doc_2 and doc_5
-      addItemField.value = '1';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      // *** should match doc_3 and doc_5
-      addItemField.value = 'true';
-      TestUtils.Simulate.change(addItemField);
-      TestUtils.Simulate.submit(submitBtn);
-
-      // confirm only one item shows up and that it's doc_5
-      assert.equal(1, $changesEl.find('.change-box').length);
-      assert.equal('doc_5', $($changesEl.find('.js-doc-id').get(0)).html());
-    });
-  });
-
-
-  describe('ChangesController max results', function () {
-    var containerEl, changesEl;
-    var maxChanges = 10;
-
-    beforeEach(function () {
-
-      var changes = [];
-      _.times(maxChanges + 10, function (i) {
-        changes.push({ id: 'doc_' + i, seq: 1, changes: { code: 'here' } });
-      });
-
-      var response = JSON.stringify({
-        last_seq: 1,
-        results: changes
-      });
-
-      Actions.initChanges({ databaseName: 'test' });
-
-      // to keep the test speedy, override the default value (1000)
-      Stores.changesStore.setMaxChanges(maxChanges);
-
-      Actions.updateChanges(response);
-      changesEl = TestUtils.renderIntoDocument(<Changes.ChangesController />, containerEl);
-    });
-
-    afterEach(function () {
-      Stores.changesStore.reset();
-      React.unmountComponentAtNode(containerEl);
-    });
-
-    it('should truncate the number of results with very large # of changes', function () {
-      // check there's no more than maxChanges results
-      assert.equal(maxChanges, $(changesEl.getDOMNode()).find('.change-box').length);
-    });
-
-    it('should show a message if the results are truncated', function () {
-      assert.equal(1, $(changesEl.getDOMNode()).find('.changes-result-limit').length);
-    });
-
-  });
-
-
-  describe('ChangeRow', function () {
-    var container;
-    var change = {
-      id: '123',
-      seq: 5,
-      deleted: false,
-      changes: { code: 'here' }
-    };
-
-    beforeEach(function () {
-      container = document.createElement('div');
-    });
-
-    afterEach(function () {
-      React.unmountComponentAtNode(container);
-    });
-
-
-    it('clicking the toggle-json button shows the code section', function () {
-      var changeRow = TestUtils.renderIntoDocument(<Changes.ChangeRow change={change} databaseName="testDatabase" />, container);
-
-      // confirm it's hidden by default
-      assert.equal(0, $(changeRow.getDOMNode()).find('.prettyprint').length);
-
-      // confirm clicking it shows the element
-      TestUtils.Simulate.click($(changeRow.getDOMNode()).find('button.btn')[0]);
-      assert.equal(1, $(changeRow.getDOMNode()).find('.prettyprint').length);
-    });
-
-    it('deleted docs should not be clickable', function () {
-      change.deleted = true;
-      var changeRow = TestUtils.renderIntoDocument(<Changes.ChangeRow change={change} databaseName="testDatabase" />, container);
-      assert.equal(0, $(changeRow.getDOMNode()).find('a.js-doc-link').length);
-    });
-
-    it('non-deleted docs should be clickable', function () {
-      change.deleted = false;
-      var changeRow = TestUtils.renderIntoDocument(<Changes.ChangeRow change={change} databaseName="testDatabase" />, container);
-      assert.equal(1, $(changeRow.getDOMNode()).find('a.js-doc-link').length);
-    });
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/tests/changes.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/changes.storesSpec.js b/app/addons/documents/tests/changes.storesSpec.js
deleted file mode 100644
index fedfb13..0000000
--- a/app/addons/documents/tests/changes.storesSpec.js
+++ /dev/null
@@ -1,107 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-define([
-  'app',
-  'api',
-  'addons/documents/changes/stores',
-  'testUtils'
-], function (app, FauxtonAPI, Stores, utils) {
-  FauxtonAPI.router = new FauxtonAPI.Router([]);
-
-  var assert = utils.assert;
-
-
-  describe('ChangesStore', function () {
-
-    afterEach(function () {
-      Stores.changesStore.reset();
-    });
-
-    it('toggleTabVisibility() changes state in store', function () {
-      assert.ok(Stores.changesStore.isTabVisible() === false);
-      Stores.changesStore.toggleTabVisibility();
-      assert.ok(Stores.changesStore.isTabVisible() === true);
-    });
-
-    it('reset() changes tab visibility to hidden', function () {
-      Stores.changesStore.toggleTabVisibility();
-      Stores.changesStore.reset();
-      assert.ok(Stores.changesStore.isTabVisible() === false);
-    });
-
-    it('addFilter() adds item in store', function () {
-      var filter = 'My filter';
-      Stores.changesStore.addFilter(filter);
-      var filters = Stores.changesStore.getFilters();
-      assert.ok(filters.length === 1);
-      assert.ok(filters[0] === filter);
-    });
-
-    it('removeFilter() removes item from store', function () {
-      var filter1 = 'My filter 1';
-      var filter2 = 'My filter 2';
-      Stores.changesStore.addFilter(filter1);
-      Stores.changesStore.addFilter(filter2);
-      Stores.changesStore.removeFilter(filter1);
-
-      var filters = Stores.changesStore.getFilters();
-      assert.ok(filters.length === 1);
-      assert.ok(filters[0] === filter2);
-    });
-
-    it('hasFilter() finds item in store', function () {
-      var filter = 'My filter';
-      Stores.changesStore.addFilter(filter);
-      assert.ok(Stores.changesStore.hasFilter(filter) === true);
-    });
-
-    it('getDatabaseName() returns database name', function () {
-      var dbName = 'hoopoes';
-      Stores.changesStore.initChanges({ databaseName: dbName });
-      assert.equal(Stores.changesStore.getDatabaseName(), dbName);
-
-      Stores.changesStore.reset();
-      assert.equal(Stores.changesStore.getDatabaseName(), '');
-    });
-
-    it("getChanges() should return a subset if there are a lot of changes", function () {
-
-      // to keep the test speedy, we override the default max value
-      var maxChanges = 10;
-      var changes = [];
-      _.times(maxChanges + 10, function (i) {
-        changes.push({ id: 'doc_' + i, seq: 1, changes: {}});
-      });
-      Stores.changesStore.initChanges({ databaseName: "test" });
-      Stores.changesStore.setMaxChanges(maxChanges);
-
-      var seqNum = 123;
-      Stores.changesStore.updateChanges(seqNum, changes);
-
-      var results = Stores.changesStore.getChanges();
-      assert.equal(maxChanges, results.length);
-    });
-
-    it("tracks last sequence number", function () {
-      assert.equal(null, Stores.changesStore.getLastSeqNum());
-
-      var seqNum = 123;
-      Stores.changesStore.updateChanges(seqNum, []);
-
-      // confirm it's been stored
-      assert.equal(seqNum, Stores.changesStore.getLastSeqNum());
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/deaf53c9/app/addons/documents/tests/headerSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/headerSpec.react.jsx b/app/addons/documents/tests/headerSpec.react.jsx
deleted file mode 100644
index 420aebe..0000000
--- a/app/addons/documents/tests/headerSpec.react.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-define([
-  'api',
-  'addons/documents/header/header.react',
-  'addons/documents/header/header.stores',
-  'addons/documents/header/header.actions',
-
-  'addons/databases/base',
-  'addons/documents/resources',
-  'addons/documents/index-results/actions',
-  'addons/documents/index-results/stores',
-
-  'testUtils',
-  'react'
-], function (FauxtonAPI, Views, Stores, Actions, Databases, Resources,
-             IndexResultsActions, IndexResultsStore, utils, React) {
-
-  var assert = utils.assert;
-  var restore = utils.restore;
-  var TestUtils = React.addons.TestUtils;
-
-  describe('Header Controller', function () {
-    var container, toggleEl;
-    beforeEach(function () {
-      container = document.createElement('div');
-    });
-
-    afterEach(function () {
-      React.unmountComponentAtNode(container);
-    });
-
-    it('should not include invalid calssname', function () {
-      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
-      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
-      assert.equal($(toggleEl.getDOMNode()).find('.undefined').length, 0);
-    });
-
-    it('should use the passed classname', function () {
-      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
-      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
-      TestUtils.Simulate.click($el[0]);
-      assert.ok($el.hasClass('js-headerbar-togglebutton-selected'));
-    });
-
-    it('should not render the alternative header if the button is not clicked', function () {
-      Actions.resetHeaderController();
-      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
-      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
-      assert.equal($(toggleEl.getDOMNode()).find('.alternative-header').length, 0);
-    });
-
-    it('should render the alternative header', function () {
-      toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
-      var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
-      TestUtils.Simulate.click($el[0]);
-      assert.equal($(toggleEl.getDOMNode()).find('.alternative-header').length, 1);
-    });
-  });
-
-  describe('Bulkdocument Headerbar Controller', function () {
-    var container, header;
-    beforeEach(function () {
-      var database = new Databases.Model({id: 'registry'});
-
-      database.allDocs = new Resources.AllDocs({_id: "ente"}, {
-        database: database,
-        viewMeta: {update_seq: 1},
-        params: {}
-      });
-
-      //override reset so we don't loose the added doc needed for testing
-      database.allDocs.reset = function () {};
-
-      IndexResultsActions.newResultsList({
-        collection: database.allDocs,
-        isListDeletable: false
-      });
-
-
-      container = document.createElement('div');
-    });
-
-    afterEach(function () {
-      React.unmountComponentAtNode(container);
-      restore(Actions.collapseDocuments);
-    });
-
-    it('should trigger action', function () {
-      var spy = sinon.spy(Actions, 'collapseDocuments');
-      header = TestUtils.renderIntoDocument(<Views.BulkDocumentHeaderController />, container);
-      TestUtils.Simulate.click($(header.getDOMNode()).find('.control-collapse')[0]);
-
-      assert.ok(spy.calledOnce);
-    });
-
-    it('pressing SelectAll should fill the selected items list', function () {
-      TestUtils.Simulate.click($(header.getDOMNode()).find('.control-select-all')[0]);
-
-      assert.equal(IndexResultsStore.indexResultsStore.getSelectedItemsLength(), 1);
-    });
-  });
-});


Mime
View raw message