Repository: couchdb-fauxton
Updated Branches:
  refs/heads/master fbd7df2ba -> 77ba12fb7


Convert Compaction to use React.js

This converts all backbone.js views to React and Flux.


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

Branch: refs/heads/master
Commit: 77ba12fb75a0fa329735d1fcef17f8c19b567970
Parents: fbd7df2
Author: Garren Smith <[email protected]>
Authored: Mon May 11 14:32:32 2015 +0200
Committer: Garren Smith <[email protected]>
Committed: Thu May 14 11:39:06 2015 +0200

----------------------------------------------------------------------
 app/addons/compaction/actions.js                | 123 +++++++++
 app/addons/compaction/actiontypes.js            |  23 ++
 app/addons/compaction/base.js                   |   2 +-
 app/addons/compaction/components.react.jsx      | 169 +++++++++++++
 app/addons/compaction/routes.js                 |  10 +-
 app/addons/compaction/stores.js                 |  93 +++++++
 .../compaction/templates/compact_view.html      |  14 --
 app/addons/compaction/templates/layout.html     |  28 ---
 app/addons/compaction/tests/actionsSpec.js      | 247 +++++++++++++++++++
 .../compaction/tests/componentsSpec.react.jsx   | 110 +++++++++
 app/addons/compaction/views.js                  | 142 -----------
 .../documents/index-editor/components.react.jsx |  13 +
 12 files changed, 784 insertions(+), 190 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/actions.js b/app/addons/compaction/actions.js
new file mode 100644
index 0000000..15c50c2
--- /dev/null
+++ b/app/addons/compaction/actions.js
@@ -0,0 +1,123 @@
+// 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/compaction/actiontypes',
+  'addons/compaction/resources'
+],
+function (app, FauxtonAPI, ActionTypes, Compaction) {
+
+  return {
+    setCompactionFor: function (database) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_SET_UP,
+        database: database
+      });
+
+    },
+
+    compactionStarted: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_COMPACTION_STARTING
+      });
+    },
+
+    compactionFinished: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_COMPACTION_FINISHED
+      });
+    },
+
+    cleaningViewsStarted: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_CLEANUP_STARTED
+      });
+    },
+
+    cleaningViewsFinished: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_CLEANUP_FINISHED
+      });
+    },
+
+    compactViewStarted: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_VIEW_STARTED
+      });
+    },
+
+    compactViewFinished: function () {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.COMPACTION_VIEW_FINISHED
+      });
+    },
+
+    compactDatabase: function (database) {
+      this.compactionStarted();
+      Compaction.compactDB(database).then(function () {
+        FauxtonAPI.addNotification({
+          type: 'success',
+          msg: 'Database compaction has started. Visit <a 
href="#activetasks">Active Tasks</a> to view the compaction progress.',
+          escape: false // beware of possible XSS when the message changes
+        });
+      }, function (xhr, error, reason) {
+        FauxtonAPI.addNotification({
+          type: 'error',
+          msg: 'Error: ' + JSON.parse(xhr.responseText).reason
+        });
+      }).always(function () {
+        this.compactionFinished();
+      }.bind(this));
+    },
+
+    cleanupViews: function (database) {
+      this.cleaningViewsStarted();
+      Compaction.cleanupViews(database).then(function () {
+        FauxtonAPI.addNotification({
+          type: 'success',
+          msg: 'View cleanup has started. Visit <a href="#activetasks">Active 
Tasks</a> to view progress.',
+          escape: false // beware of possible XSS when the message changes
+        });
+      }, function (xhr, error, reason) {
+        FauxtonAPI.addNotification({
+          type: 'error',
+          msg: 'Error: ' + JSON.parse(xhr.responseText).reason
+        });
+      }).always(function () {
+        this.cleaningViewsFinished();
+      }.bind(this));
+    },
+
+    compactView: function (database, designDoc) {
+      this.compactViewStarted();
+
+      Compaction.compactView(database, designDoc).then(function () {
+        FauxtonAPI.addNotification({
+          type: 'success',
+          msg: 'View compaction has started. Visit <a 
href="#activetasks">Active Tasks</a> to view progress.',
+          escape: false // beware of possible XSS when the message changes
+        });
+      }, function (xhr, error, reason) {
+        FauxtonAPI.addNotification({
+          type: 'error',
+          msg: 'Error: ' + JSON.parse(xhr.responseText).reason
+        });
+      }).always(function () {
+        this.compactViewFinished();
+      }.bind(this));
+    }
+
+  };
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/actiontypes.js 
b/app/addons/compaction/actiontypes.js
new file mode 100644
index 0000000..e164f7c
--- /dev/null
+++ b/app/addons/compaction/actiontypes.js
@@ -0,0 +1,23 @@
+// 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([], function () {
+  return {
+    COMPACTION_SET_UP: 'COMPACTION_SET_UP',
+    COMPACTION_COMPACTION_STARTING: 'COMPACTION_COMPACTION_STARTING',
+    COMPACTION_COMPACTION_FINISHED: 'COMPACTION_COMPACTION_FINISHED',
+    COMPACTION_CLEANUP_STARTED: 'COMPACTION_CLEANUP_STARTED',
+    COMPACTION_CLEANUP_FINISHED: 'COMPACTION_CLEANUP_FINISHED',
+    COMPACTION_VIEW_STARTED: 'COMPACTION_VIEW_STARTED',
+    COMPACTION_VIEW_FINISHED: 'COMPACTION_VIEW_FINISHED'
+  };
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/base.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/base.js b/app/addons/compaction/base.js
index b82852a..187391c 100644
--- a/app/addons/compaction/base.js
+++ b/app/addons/compaction/base.js
@@ -25,7 +25,7 @@ function (app, FauxtonAPI, Compaction) {
       icon: "icon-cogs"
     });
 
-    FauxtonAPI.registerExtension('ViewEditor:ButtonRow', new 
Compaction.CompactView({}));
+    FauxtonAPI.registerExtension('view-editor:compaction-button', 
Compaction.ViewCompactionButton);
   };
 
   return Compaction;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/compaction/components.react.jsx 
b/app/addons/compaction/components.react.jsx
new file mode 100644
index 0000000..1aa2c0c
--- /dev/null
+++ b/app/addons/compaction/components.react.jsx
@@ -0,0 +1,169 @@
+// 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/compaction/stores',
+  'addons/compaction/actions',
+  'addons/components/react-components.react'
+],
+
+function (app, FauxtonAPI, React, Stores, Actions, Components, 
ReactComponents) {
+  var compactionStore = Stores.compactionStore;
+
+  var CompactDatabase = React.createClass({
+
+    run: function (e) {
+      this.props.compactDatabase();
+    },
+
+    render: function () {
+      var btnText = 'Run';
+
+      if (this.props.isCompacting) {
+        btnText = 'Compacting...';
+      }
+
+      return (
+        <div className="row-fluid">
+          <div className="span12 compaction-option">
+            <h3>Compact Database</h3>
+            <p>Compacting a database removes deleted documents and previous 
revisions. It is an irreversible operation and may take a while to complete for 
large databases.</p>
+            <button id="compact-db" disabled={this.props.isCompacting} 
onClick={this.run} className="btn btn-large btn-primary">{btnText}</button>
+          </div>
+        </div>
+      );
+    }
+
+  });
+
+  var CleanView = React.createClass({
+
+    run: function (e) {
+      this.props.cleanupView();
+    },
+
+    render: function () {
+      var btnText = 'Run';
+
+      if (this.props.isCleaningView) {
+        btnText = 'Cleaning Views...';
+      }
+      return (
+        <div className="row-fluid">
+          <div className="span12 compaction-option">
+            <h3>Cleanup Views</h3>
+            <p>Cleaning up views in a database removes old view files still 
stored on the filesystem. It is an irreversible operation.</p>
+            <button id="cleanup-views" onClick={this.run} className="btn 
btn-large btn-primary">{btnText}</button>
+          </div>
+        </div>
+      );
+    }
+
+  });
+
+  var CompactionController = React.createClass({
+    getStoreState: function () {
+      return {
+        database: compactionStore.getDatabase(),
+        isCompacting: compactionStore.isCompacting(),
+        isCleaningViews: compactionStore.isCleaningViews()
+      };
+    },
+
+    getInitialState: function () {
+      return this.getStoreState();
+    },
+
+    componentDidMount: function () {
+      compactionStore.on('change', this.onChange, this);
+    },
+
+    componentWillUnmount: function () {
+      compactionStore.off('change', this.onChange);
+    },
+
+    onChange: function () {
+      this.setState(this.getStoreState());
+    },
+
+    compactDatabase: function () {
+      Actions.compactDatabase(this.state.database);
+    },
+
+    cleanupView: function () {
+      Actions.cleanupViews(this.state.database);
+    },
+
+    render: function () {
+      return (
+        <div>
+          <CompactDatabase isCompacting={this.state.isCompacting} 
compactDatabase={this.compactDatabase} />
+          <CleanView isCleaningView={this.state.isCleaningViews} 
cleanupView={this.cleanupView}/>
+        </div>
+      );
+    }
+  });
+
+  var ViewCompactionButton = React.createClass({
+    onClick: function (e) {
+      e.preventDefault();
+      Actions.compactView(this.props.database, this.props.designDoc);
+    },
+
+    getStoreState: function () {
+      return {
+        isCompactingView: compactionStore.isCompactingView()
+      };
+    },
+
+    getInitialState: function () {
+      return this.getStoreState();
+    },
+
+    componentDidMount: function () {
+      compactionStore.on('change', this.onChange, this);
+    },
+
+    componentWillUnmount: function () {
+      compactionStore.off('change', this.onChange);
+    },
+
+    onChange: function () {
+      this.setState(this.getStoreState());
+    },
+
+    render: function () {
+      var btnMsg = 'Compact View';
+
+      if (this.state.isCompactingView) {
+        btnMsg = 'Compacting View';
+      }
+
+      return (
+        <button disabled={this.state.isCompactingView}
+          className="btn btn-info pull-right"
+          onClick={this.onClick}>{btnMsg}</button>
+      );
+    }
+
+  });
+
+  return {
+    CompactDatabase: CompactDatabase,
+    CleanView: CleanView,
+    CompactionController: CompactionController,
+    ViewCompactionButton: ViewCompactionButton
+  };
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/routes.js b/app/addons/compaction/routes.js
index 295765e..ea6d54f 100644
--- a/app/addons/compaction/routes.js
+++ b/app/addons/compaction/routes.js
@@ -15,12 +15,13 @@ define([
   'api',
 
   // Modules
-  'addons/compaction/views',
+  'addons/compaction/components.react',
+  'addons/compaction/actions',
   'addons/databases/resources',
   'addons/documents/shared-routes'
 ],
 
-function (app, FauxtonAPI, Compaction, Databases, BaseRoute) {
+function (app, FauxtonAPI, Compaction, Actions, Databases, BaseRoute) {
 
   var CompactionRouteObject = BaseRoute.extend({
     routes: {
@@ -55,7 +56,8 @@ function (app, FauxtonAPI, Compaction, Databases, BaseRoute) {
     },
 
     compaction: function () {
-      this.pageContent = this.setView('#dashboard-content', new 
Compaction.Layout({model: this.database}));
+      Actions.setCompactionFor(this.database);
+      this.pageContent = this.setComponent('#dashboard-content', 
Compaction.CompactionController);
     },
 
     establish: function () {
@@ -86,5 +88,3 @@ function (app, FauxtonAPI, Compaction, Databases, BaseRoute) {
   return Compaction;
 
 });
-
-

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/stores.js b/app/addons/compaction/stores.js
new file mode 100644
index 0000000..d2c5410
--- /dev/null
+++ b/app/addons/compaction/stores.js
@@ -0,0 +1,93 @@
+// 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/compaction/actiontypes'
+],
+
+function (FauxtonAPI, ActionTypes) {
+  var Stores = {};
+
+  Stores.CompactionStore = FauxtonAPI.Store.extend({
+
+    initialize: function () {
+      this._isCompacting = false;
+      this._isCleaningView = false;
+      this._isCompactingView = false;
+    },
+
+    isCompacting: function () {
+      return this._isCompacting;
+    },
+
+    isCleaningViews: function () {
+      return this._isCleaningViews;
+    },
+
+    isCompactingView: function () {
+      return this._isCompactingView;
+    },
+
+    setDatabase: function (database) {
+      this._database = database;
+    },
+
+    getDatabase: function () {
+      return this._database;
+    },
+
+    dispatch: function (action) {
+      switch (action.type) {
+        case ActionTypes.COMPACTION_SET_UP:
+          this.setDatabase(action.database);
+          this.triggerChange();
+        break;
+        case ActionTypes.COMPACTION_COMPACTION_STARTING:
+          this._isCompacting = true;
+          this.triggerChange();
+        break;
+        case ActionTypes.COMPACTION_COMPACTION_FINISHED:
+          this._isCompacting = false;
+          this.triggerChange();
+        break;
+        case ActionTypes.COMPACTION_CLEANUP_STARTED:
+          this._isCleaningViews = true;
+          this.triggerChange();
+        break;
+        case ActionTypes.COMPACTION_CLEANUP_FINISHED:
+          this._isCleaningViews = false;
+          this.triggerChange();
+        break;
+        case ActionTypes.COMPACTION_VIEW_STARTED:
+          this._isCompactingView = true;
+          this.triggerChange();
+        break;
+        case ActionTypes.COMPACTION_VIEW_FINISHED:
+          this._isCompactingView = false;
+          this.triggerChange();
+        break;
+
+        default:
+        return;
+        // do nothing
+      }
+    }
+
+  });
+
+  Stores.compactionStore = new Stores.CompactionStore();
+
+  Stores.compactionStore.dispatchToken = 
FauxtonAPI.dispatcher.register(Stores.compactionStore.dispatch);
+
+  return Stores;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/templates/compact_view.html
----------------------------------------------------------------------
diff --git a/app/addons/compaction/templates/compact_view.html 
b/app/addons/compaction/templates/compact_view.html
deleted file mode 100644
index 8a0b7ec..0000000
--- a/app/addons/compaction/templates/compact_view.html
+++ /dev/null
@@ -1,14 +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.
--->
-Compact View

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/templates/layout.html
----------------------------------------------------------------------
diff --git a/app/addons/compaction/templates/layout.html 
b/app/addons/compaction/templates/layout.html
deleted file mode 100644
index f63b7de..0000000
--- a/app/addons/compaction/templates/layout.html
+++ /dev/null
@@ -1,28 +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.
--->
-<div class="row-fluid">
-  <div class="span12 compaction-option">
-    <h3> Compact Database </h3>
-    <p>Compacting a database removes deleted documents and previous revisions. 
It is an irreversible operation and may take a while to complete for large 
databases.</p>
-    <button id="compact-db" class="btn btn-large btn-primary"> Run </button>
-  </div>
-</div>
-
-<div class="row-fluid">
-  <div class="span12 compaction-option">
-    <h3> Cleanup Views </h3>
-    <p>Cleaning up views in a database removes old view files still stored on 
the filesystem. It is an irreversible operation.</p>
-    <button id="cleanup-views" class="btn btn-large btn-primary"> Run </button>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/tests/actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/tests/actionsSpec.js 
b/app/addons/compaction/tests/actionsSpec.js
new file mode 100644
index 0000000..1aff431
--- /dev/null
+++ b/app/addons/compaction/tests/actionsSpec.js
@@ -0,0 +1,247 @@
+// 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/compaction/stores',
+  'addons/compaction/resources',
+  'addons/compaction/actions',
+  'testUtils'
+], function (FauxtonAPI, Stores, Compaction, Actions, testUtils) {
+  var assert = testUtils.assert;
+  var restore = testUtils.restore;
+  var store = Stores.compactionStore;
+
+  describe('Compaction Actions', function () {
+
+    describe('Compact Database', function () {
+      var database = {};
+
+      afterEach(function () {
+        restore(FauxtonAPI.addNotification);
+      });
+
+      it('compacts database', function () {
+        var spy = false;
+        var promise = FauxtonAPI.Deferred();
+
+        Compaction.compactDB = function () {
+          spy = true;
+          return promise;
+        };
+
+        Actions.compactDatabase(database);
+        assert.ok(spy);
+      });
+
+      it('notifies on success', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        Compaction.compactDB = function () {
+          return promise;
+        };
+
+        Actions.compactDatabase(database);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('notifies on failure', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+        var promise = FauxtonAPI.Deferred();
+        promise.reject({
+          responseText: JSON.stringify({reason: 'testing'})
+        });
+
+        Compaction.compactDB = function () {
+          return promise;
+        };
+
+        Actions.compactDatabase(database);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('sets compacting view to true on start', function () {
+        var promise = FauxtonAPI.Deferred();
+
+        Compaction.compactDB = function () {
+          return promise;
+        };
+
+        Actions.compactDatabase(database);
+        assert.ok(store.isCompacting());
+      });
+
+      it('sets compacting view to false on completion of request', function () 
{
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        Compaction.compactDB = function () {
+          return promise;
+        };
+
+        Actions.compactDatabase(database);
+        assert.notOk(store.isCompacting());
+      });
+
+    });
+
+    describe('Clean Views', function () {
+      var database = {};
+
+      afterEach(function () {
+        restore(FauxtonAPI.addNotification);
+      });
+
+      it('cleans views', function () {
+        var spy = false;
+        var promise = FauxtonAPI.Deferred();
+
+        Compaction.cleanupViews = function () {
+          spy = true;
+          return promise;
+        };
+
+        Actions.cleanupViews(database);
+        assert.ok(spy);
+      });
+
+      it('notifies on success', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        Compaction.cleanupViews = function () {
+          return promise;
+        };
+
+        Actions.cleanupViews(database);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('notifies on failure', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+        var promise = FauxtonAPI.Deferred();
+        promise.reject({
+          responseText: JSON.stringify({reason: 'testing'})
+        });
+
+        Compaction.cleanupViews = function () {
+          return promise;
+        };
+
+        Actions.cleanupViews(database);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('sets compacting view to true on start', function () {
+        var promise = FauxtonAPI.Deferred();
+
+        Compaction.cleanupViews = function () {
+          return promise;
+        };
+
+        Actions.cleanupViews(database);
+        assert.ok(store.isCleaningViews());
+      });
+
+      it('sets compacting view to false on completion of request', function () 
{
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        Compaction.cleanupViews = function () {
+          return promise;
+        };
+
+        Actions.cleanupViews(database);
+        assert.notOk(store.isCleaningViews());
+      });
+
+    });
+
+    describe('Compact View', function () {
+      var database = {};
+      var designDoc = '_design/test-doc';
+
+      afterEach(function () {
+        restore(FauxtonAPI.addNotification);
+      });
+
+      it('compacts database', function () {
+        var spy = false;
+        var promise = FauxtonAPI.Deferred();
+
+        Compaction.compactView = function () {
+          spy = true;
+          return promise;
+        };
+
+        Actions.compactView(database, designDoc);
+        assert.ok(spy);
+      });
+
+      it('notifies on success', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        Compaction.compactView = function () {
+          return promise;
+        };
+
+        Actions.compactView(database, designDoc);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('notifies on failure', function () {
+        var spy = sinon.spy(FauxtonAPI, 'addNotification');
+        var promise = FauxtonAPI.Deferred();
+        promise.reject({
+          responseText: JSON.stringify({reason: 'testing'})
+        });
+
+        Compaction.compactView = function () {
+          return promise;
+        };
+
+        Actions.compactView(database, designDoc);
+        assert.ok(spy.calledOnce);
+      });
+
+      it('sets compacting view to true on start', function () {
+        var promise = FauxtonAPI.Deferred();
+
+        Compaction.compactView = function () {
+          return promise;
+        };
+
+        Actions.compactView(database, designDoc);
+        assert.ok(store.isCompactingView());
+      });
+
+      it('sets compacting view to false on completion of request', function () 
{
+        var promise = FauxtonAPI.Deferred();
+        promise.resolve();
+
+        Compaction.compactView = function () {
+          return promise;
+        };
+
+        Actions.compactView(database, designDoc);
+        assert.notOk(store.isCompactingView());
+      });
+
+    });
+
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/tests/componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/compaction/tests/componentsSpec.react.jsx 
b/app/addons/compaction/tests/componentsSpec.react.jsx
new file mode 100644
index 0000000..5c6a18a
--- /dev/null
+++ b/app/addons/compaction/tests/componentsSpec.react.jsx
@@ -0,0 +1,110 @@
+// 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/compaction/components.react',
+  'addons/compaction/actions',
+  'testUtils',
+  "react"
+], function (FauxtonAPI, Views, Actions, utils, React) {
+  FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+
+  describe('Compaction Controller', function () {
+    var container, controllerEl;
+
+    beforeEach(function () {
+      Actions.setCompactionFor({
+        id: 'my-database'
+      });
+
+      container = document.createElement('div');
+      controllerEl = TestUtils.renderIntoDocument(
+        <Views.CompactionController />,
+        container
+      );
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('triggers compact database action', function () {
+      var spy = sinon.spy(Actions, 'compactDatabase');
+
+      controllerEl.compactDatabase();
+      assert.ok(spy.calledOnce);
+    });
+
+    it('triggers clean up view action', function () {
+      var spy = sinon.spy(Actions, 'cleanupViews');
+
+      controllerEl.cleanupView();
+      assert.ok(spy.calledOnce);
+    });
+
+  });
+
+  describe('CleanView', function () {
+    var spy, container, cleanupViewEl;
+
+    beforeEach(function () {
+      spy = sinon.spy();
+      container = document.createElement('div');
+      cleanupViewEl = TestUtils.renderIntoDocument(
+        <Views.CleanView cleanupView={spy} />,
+        container
+      );
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('calls cleanupView on button click', function () {
+      var el = $(cleanupViewEl.getDOMNode()).find('button')[0];
+      TestUtils.Simulate.click(el, {});
+
+      assert.ok(spy.calledOnce);
+
+    });
+
+  });
+
+  describe('CompactDatabase', function () {
+    var spy, container, compactViewEl;
+
+    beforeEach(function () {
+      spy = sinon.spy();
+      container = document.createElement('div');
+      compactViewEl = TestUtils.renderIntoDocument(
+        <Views.CompactDatabase compactDatabase={spy} />,
+        container
+      );
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('calls compact database on button click', function () {
+      var el = $(compactViewEl.getDOMNode()).find('button')[0];
+      TestUtils.Simulate.click(el, {});
+
+      assert.ok(spy.calledOnce);
+
+    });
+
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/compaction/views.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/views.js b/app/addons/compaction/views.js
deleted file mode 100644
index be3f129..0000000
--- a/app/addons/compaction/views.js
+++ /dev/null
@@ -1,142 +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",
-       // Modules
-       "addons/compaction/resources"
-],
-function (app, FauxtonAPI, Compaction) {
-
-  Compaction.Layout = FauxtonAPI.View.extend({
-    template: 'addons/compaction/templates/layout',
-
-    initialize: function () {
-      _.bindAll(this);
-    },
-
-    events: {
-      "click #compact-db": "compactDB",
-      "click #compact-view": "compactDB",
-      "click #cleanup-views": "cleanupViews"
-    },
-
-    disableButton: function (selector, text) {
-      this.$(selector).attr('disabled', 'disabled').text(text);
-    },
-
-    enableButton: function (selector, text) {
-      this.$(selector).removeAttr('disabled').text(text);
-    },
-
-    compactDB: function (event) {
-      var enableButton = this.enableButton;
-      event.preventDefault();
-
-      this.disableButton('#compact-db', 'Compacting...');
-
-      Compaction.compactDB(this.model).then(function () {
-        FauxtonAPI.addNotification({
-          type: 'success',
-          msg: 'Database compaction has started. Visit <a 
href="#activetasks">Active Tasks</a> to view the compaction progress.',
-          escape: false // beware of possible XSS when the message changes
-        });
-      }, function (xhr, error, reason) {
-        FauxtonAPI.addNotification({
-          type: 'error',
-          msg: 'Error: ' + JSON.parse(xhr.responseText).reason
-        });
-      }).always(function () {
-        enableButton('#compact-db', 'Run');
-      });
-    },
-
-    cleanupViews: function (event) {
-      var enableButton = this.enableButton;
-      event.preventDefault();
-
-      this.disableButton('#cleanup-view', 'Cleaning...');
-
-      Compaction.cleanupViews(this.model).then(function () {
-        FauxtonAPI.addNotification({
-          type: 'success',
-          msg: 'View cleanup has started. Visit <a href="#activetasks">Active 
Tasks</a> to view progress.',
-          escape: false // beware of possible XSS when the message changes
-        });
-      }, function (xhr, error, reason) {
-        FauxtonAPI.addNotification({
-          type: 'error',
-          msg: 'Error: ' + JSON.parse(xhr.responseText).reason
-        });
-      }).always(function () {
-        enableButton('#cleanup-views', 'Run');
-      });
-    }
-  });
-
-  Compaction.CompactView = FauxtonAPI.View.extend({
-    template: 'addons/compaction/templates/compact_view',
-    className: 'btn btn-info pull-right',
-    tagName: 'button',
-
-    initialize: function () {
-      _.bindAll(this);
-    },
-
-    events: {
-      "click": "compact"
-    },
-
-    disableButton: function () {
-      this.$el.attr('disabled', 'disabled').text('Compacting...');
-    },
-
-    enableButton: function () {
-      this.$el.removeAttr('disabled').text('Compact View');
-    },
-
-
-    update: function (database, designDoc, viewName) {
-      this.database = database;
-      this.designDoc = designDoc;
-      this.viewName = viewName;
-    },
-
-    compact: function (event) {
-      event.preventDefault();
-      var enableButton = this.enableButton;
-
-      this.disableButton();
-
-      Compaction.compactView(this.database, this.designDoc).then(function () {
-        FauxtonAPI.addNotification({
-          type: 'success',
-          msg: 'View compaction has started. Visit <a 
href="#activetasks">Active Tasks</a> to view progress.',
-          escape: false // beware of possible XSS when the message changes
-        });
-      }, function (xhr, error, reason) {
-        FauxtonAPI.addNotification({
-          type: 'error',
-          msg: 'Error: ' + JSON.parse(xhr.responseText).reason
-        });
-      }).always(function () {
-        enableButton();
-      });
-
-    }
-
-  });
-
-  return Compaction;
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/77ba12fb/app/addons/documents/index-editor/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/components.react.jsx 
b/app/addons/documents/index-editor/components.react.jsx
index 1ba8bc6..2f27464 100644
--- a/app/addons/documents/index-editor/components.react.jsx
+++ b/app/addons/documents/index-editor/components.react.jsx
@@ -365,6 +365,18 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, ReactComponents)
       Actions.updateMapCode(code);
     },
 
+    getCompactButton: function () {
+      var extension = 
FauxtonAPI.getExtensions('view-editor:compaction-button');
+
+      if (_.isEmpty(extension)) {
+        return null;
+      } else {
+        var CompactButton = extension[0];
+        return <CompactButton database={this.state.database}
+                          designDoc={this.state.designDocId}/>;
+      }
+    },
+
     render: function () {
       if (this.state.isLoading) {
         return (
@@ -423,6 +435,7 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, ReactComponents)
               <div className="control-group">
                 <ConfirmButton text="Save &amp; Build Index" />
                 <DeleteView />
+                {this.getCompactButton()}
               </div>
             </div>
           </form>

Reply via email to