You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2018/03/19 10:44:16 UTC

[couchdb-fauxton] branch master updated: Remove JQuery uses from documents addon (#1065)

This is an automated email from the ASF dual-hosted git repository.

garren pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git


The following commit(s) were added to refs/heads/master by this push:
     new 14aa1ef  Remove JQuery uses from documents addon (#1065)
14aa1ef is described below

commit 14aa1efd1b789dde293e1f726080d8f96978e892
Author: Antonio Maranhao <30...@users.noreply.github.com>
AuthorDate: Mon Mar 19 06:44:14 2018 -0400

    Remove JQuery uses from documents addon (#1065)
    
    * Remove JQuery uses from documents addon
---
 app/addons/documents/__tests__/resources.test.js   |  21 ++-
 app/addons/documents/changes/actions.js            |  15 +-
 app/addons/documents/components/actions.js         |  22 ++-
 .../__tests__/doc-editor.actions.test.js           |  21 ++-
 app/addons/documents/doc-editor/actions.js         | 176 +++++++++++----------
 app/addons/documents/doc-editor/components.js      |  22 +--
 app/addons/documents/resources.js                  |  83 ++++------
 app/addons/documents/shared-resources.js           |  12 +-
 app/addons/documents/sidebar/actions.js            |   7 +-
 .../documents/tests/nightwatch/deletesDocuments.js |   1 +
 app/helpers.js                                     |   7 +
 11 files changed, 205 insertions(+), 182 deletions(-)

diff --git a/app/addons/documents/__tests__/resources.test.js b/app/addons/documents/__tests__/resources.test.js
index c6951ea..4505015 100644
--- a/app/addons/documents/__tests__/resources.test.js
+++ b/app/addons/documents/__tests__/resources.test.js
@@ -181,6 +181,8 @@ describe('Bulk Delete', () => {
   let databaseId = 'ente',
       collection,
       promise,
+      resolve,
+      reject,
       values;
 
   values = [{
@@ -204,7 +206,10 @@ describe('Bulk Delete', () => {
       databaseId: databaseId
     });
 
-    promise = FauxtonAPI.Deferred();
+    promise = new FauxtonAPI.Promise((res, rej) => {
+      resolve = res;
+      reject = rej;
+    });
   });
 
   it('contains the models', () => {
@@ -219,7 +224,7 @@ describe('Bulk Delete', () => {
     collection.handleResponse([
       {'ok': true, 'id': '1', 'rev': '10-72cd2edbcc0d197ce96188a229a7af01'},
       {'ok': true, 'id': '2', 'rev': '6-da537822b9672a4b2f42adb1be04a5b1'}
-    ], promise);
+    ], resolve, reject);
 
     return promise.then(() => {
       assert.equal(collection.length, 1);
@@ -235,7 +240,7 @@ describe('Bulk Delete', () => {
     collection.handleResponse([
       {'ok': true, 'id': 'Deferred', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'},
       {'ok': true, 'id': 'DeskSet', 'rev':'6-da537822b9672a4b2f42adb1be04a5b1'}
-    ], promise);
+    ], resolve, reject);
   });
 
   it('triggers a error event with all errored ids', (done) => {
@@ -247,7 +252,7 @@ describe('Bulk Delete', () => {
     collection.handleResponse([
       {'error':'conflict', 'id':'Deferred', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'},
       {'ok':true, 'id':'DeskSet', 'rev':'6-da537822b9672a4b2f42adb1be04a5b1'}
-    ], promise);
+    ], resolve, reject);
   });
 
   it('removes successfull deleted from the collection but keeps one with errors', () => {
@@ -255,7 +260,7 @@ describe('Bulk Delete', () => {
       {'error':'conflict', 'id':'1', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'},
       {'ok':true, 'id':'2', 'rev':'6-da537822b9672a4b2f42adb1be04a5b1'},
       {'error':'conflict', 'id':'3', 'rev':'6-da537822b9672a4b2f42adb1be04a5b1'}
-    ], promise);
+    ], resolve, reject);
 
     return promise.then(() => {
       assert.ok(collection.get('1'));
@@ -271,7 +276,7 @@ describe('Bulk Delete', () => {
     collection.handleResponse([
       {'ok':true, 'id':'Deferred', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'},
       {'ok':true, 'id':'DeskSet', 'rev':'6-da537822b9672a4b2f42adb1be04a5b1'}
-    ], promise);
+    ], resolve, reject);
 
     return promise.then(() => {
       assert.ok(spy.calledOnce);
@@ -290,7 +295,7 @@ describe('Bulk Delete', () => {
       {'ok':true, 'id':'Deferred', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'},
       {'ok':true, 'id':'DeskSet', 'rev':'6-da537822b9672a4b2f42adb1be04a5b1'},
       {'error':'conflict', 'id':'1', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'},
-    ], promise);
+    ], resolve, reject);
 
     return promise.then(() => {
       assert.ok(spy.calledWith(ids));
@@ -300,7 +305,7 @@ describe('Bulk Delete', () => {
   it('triggers reject for failed delete', () => {
     collection.handleResponse([
       {'error':'conflict', 'id':'1', 'rev':'10-72cd2edbcc0d197ce96188a229a7af01'}
-    ], promise);
+    ], resolve, reject);
 
     return promise.catch((errors) => {
       assert.deepEqual(errors, ['1']);
diff --git a/app/addons/documents/changes/actions.js b/app/addons/documents/changes/actions.js
index a414c47..224bd12 100644
--- a/app/addons/documents/changes/actions.js
+++ b/app/addons/documents/changes/actions.js
@@ -13,6 +13,7 @@
 
 import app from "../../../app";
 import FauxtonAPI from "../../../core/api";
+import { get } from "../../../core/ajax";
 import ActionTypes from "./actiontypes";
 import Stores from "./stores";
 import Helpers from "../helpers";
@@ -61,8 +62,18 @@ export default {
     const query = app.utils.queryParams(params);
     const db = app.utils.safeURLName(changesStore.getDatabaseName());
     const endpoint = FauxtonAPI.urls('changes', 'server', db, '?' + query);
-    currentRequest = $.getJSON(endpoint);
-    currentRequest.then(this.updateChanges.bind(this));
+    get(endpoint).then((res) => {
+      if (res.error) {
+        throw new Error(res.reason || res.error);
+      }
+      this.updateChanges(res);
+    }).catch((err) => {
+      FauxtonAPI.addNotification({
+        msg: 'Error loading list of changes. Reason: ' + err.message,
+        type: 'error',
+        clear: true
+      });
+    });
   },
 
   updateChanges: function (json) {
diff --git a/app/addons/documents/components/actions.js b/app/addons/documents/components/actions.js
index b0f9e5f..765c7cb 100644
--- a/app/addons/documents/components/actions.js
+++ b/app/addons/documents/components/actions.js
@@ -9,9 +9,9 @@
 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 // License for the specific language governing permissions and limitations under
 // the License.
-import $ from 'jquery';
 import app from "../../../app";
 import FauxtonAPI from "../../../core/api";
+import { get } from "../../../core/ajax";
 
 export default {
   fetchAllDocsWithKey: (database) => {
@@ -23,17 +23,15 @@ export default {
       });
 
       const url = FauxtonAPI.urls('allDocs', 'server', database.safeID(), query);
-      $.ajax({
-        cache: false,
-        url: url,
-        dataType: 'json'
-      }).then(({rows}) => {
-        const options = rows.map(row => {
-          return { value: row.id, label: row.id};
-        });
-        callback(null, {
-          options: options
-        });
+      get(url).then(res => {
+        let options = [];
+        if (!res.error) {
+          const {rows} = res;
+          options = rows.map(row => {
+            return { value: row.id, label: row.id};
+          });
+        }
+        callback(null, { options: options });
       });
     };
   }
diff --git a/app/addons/documents/doc-editor/__tests__/doc-editor.actions.test.js b/app/addons/documents/doc-editor/__tests__/doc-editor.actions.test.js
index 85527f0..603a0e6 100644
--- a/app/addons/documents/doc-editor/__tests__/doc-editor.actions.test.js
+++ b/app/addons/documents/doc-editor/__tests__/doc-editor.actions.test.js
@@ -22,11 +22,23 @@ const { restore } = utils;
 describe('DocEditorActions', () => {
   const database = new Databases.Model({ id: 'db1' });
   const doc = new Documents.Doc({ _id: 'foo' }, { database: database });
+  let fakeXMLHttpRequest, fakeOpen;
+
+  beforeEach(() => {
+    fakeXMLHttpRequest = sinon.stub(global, 'XMLHttpRequest');
+    fakeOpen = sinon.stub();
+    fakeXMLHttpRequest.returns({
+      send: sinon.stub(),
+      setRequestHeader: sinon.stub(),
+      open: fakeOpen
+    });
+  });
 
   afterEach(() => {
     restore(FauxtonAPI.addNotification);
     restore(FauxtonAPI.navigate);
     restore(FauxtonAPI.urls);
+    fakeXMLHttpRequest.restore();
   });
 
   it('uploadAttachment handles filenames with special chars', () => {
@@ -34,7 +46,6 @@ describe('DocEditorActions', () => {
     sinon.stub(FauxtonAPI, 'urls').callsFake((p1, p2, p3, p4, p5, p6) => {
       return [p1, p2, p3, p4, p5, p6].join('/');
     });
-    const stub = sinon.stub($, 'ajax');
     const params = {
       rev: 'rev-num',
       doc: doc,
@@ -47,10 +58,10 @@ describe('DocEditorActions', () => {
     };
 
     Actions.uploadAttachment(params);
-    sinon.assert.calledWithMatch(stub,
-      {
-        url: 'document/attachment/db1/foo/' + encodeURIComponent(params.files[0].name) + '/?rev=rev-num'
-      }
+    sinon.assert.calledWithExactly(
+      fakeOpen,
+      'PUT',
+      'document/attachment/db1/foo/' + encodeURIComponent(params.files[0].name) + '/?rev=rev-num'
     );
   });
 
diff --git a/app/addons/documents/doc-editor/actions.js b/app/addons/documents/doc-editor/actions.js
index 2c0da69..6d0abc1 100644
--- a/app/addons/documents/doc-editor/actions.js
+++ b/app/addons/documents/doc-editor/actions.js
@@ -13,9 +13,10 @@
 /* global FormData */
 
 import FauxtonAPI from "../../../core/api";
+import { deleteRequest } from "../../../core/ajax";
 import ActionTypes from "./actiontypes";
 
-var xhr;
+var currentUploadHttpRequest;
 
 function initDocEditor (params) {
   var doc = params.doc;
@@ -79,33 +80,25 @@ function hideDeleteDocModal () {
 }
 
 function deleteDoc (doc) {
-  var databaseName = doc.database.safeID();
-  var query = '?rev=' + doc.get('_rev');
-
-  $.ajax({
-    url: FauxtonAPI.urls('document', 'server', databaseName, doc.safeID(), query),
-    type: 'DELETE',
-    headers: {
-      'Accept': 'application/json',
-      'Content-Type': 'application/json',
-    },
-    xhrFields: {
-      withCredentials: true
-    },
-    success: function () {
-      FauxtonAPI.addNotification({
-        msg: 'Your document has been successfully deleted.',
-        clear: true
-      });
-      FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', databaseName, ''));
-    },
-    error: function () {
-      FauxtonAPI.addNotification({
-        msg: 'Failed to delete your document!',
-        type: 'error',
-        clear: true
-      });
+  const databaseName = doc.database.safeID();
+  const query = '?rev=' + doc.get('_rev');
+  const url = FauxtonAPI.urls('document', 'server', databaseName, doc.safeID(), query);
+  deleteRequest(url).then(res => {
+    if (res.error) {
+      throw new Error(res.reason || res.error);
     }
+    FauxtonAPI.addNotification({
+      msg: 'Your document has been successfully deleted.',
+      type: 'success',
+      clear: true
+    });
+    FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', databaseName, ''));
+  }).catch(err => {
+    FauxtonAPI.addNotification({
+      msg: 'Failed to delete your document. Reason: ' + err.message,
+      type: 'error',
+      clear: true
+    });
   });
 }
 
@@ -156,75 +149,88 @@ function uploadAttachment (params) {
     });
     return;
   }
-
   FauxtonAPI.dispatch({ type: ActionTypes.START_FILE_UPLOAD });
 
-  // store the xhr in parent scope to allow us to cancel any uploads if the user closes the modal
-  xhr = $.ajaxSettings.xhr();
-
-  var query = '?rev=' + params.rev;
-  var db = params.doc.getDatabase().safeID();
-  var docId = params.doc.safeID();
-  var file = params.files[0];
-
-  $.ajax({
-    url: FauxtonAPI.urls('document', 'attachment', db, docId, encodeURIComponent(file.name), query),
-    type: 'PUT',
-    data: file,
-    contentType: file.type,
-    headers: {
-      Accept: "application/json; charset=utf-8"
-    },
-    processData: false,
-    xhrFields: {
-      withCredentials: true
-    },
-    xhr: function () {
-      xhr.upload.onprogress = function (evt) {
-        var percentComplete = evt.loaded / evt.total * 100;
-        FauxtonAPI.dispatch({
-          type: ActionTypes.SET_FILE_UPLOAD_PERCENTAGE,
-          options: {
-            percent: percentComplete
-          }
-        });
-      };
-      return xhr;
-    },
-    success: function () {
-
-      // re-initialize the document editor. Only announce it's been updated when
-      initDocEditor({
-        doc: params.doc,
-        onLoaded: function () {
-          FauxtonAPI.dispatch({ type: ActionTypes.FILE_UPLOAD_SUCCESS });
-          FauxtonAPI.addNotification({
-            msg: 'Document saved successfully.',
-            type: 'success',
-            clear: true
-          });
-        }.bind(this)
-      });
+  const query = '?rev=' + params.rev;
+  const db = params.doc.getDatabase().safeID();
+  const docId = params.doc.safeID();
+  const file = params.files[0];
+  const url = FauxtonAPI.urls('document', 'attachment', db, docId, encodeURIComponent(file.name), query);
 
-    },
-    error: function (resp) {
-      // cancelled uploads throw an ajax error but they don't contain a response. We don't want to publish an error
-      // event in those cases
-      if (_.isEmpty(resp.responseText)) {
-        return;
-      }
+  const onProgress = (evt) => {
+    if (evt.lengthComputable) {
+      const percentComplete = evt.loaded / evt.total * 100;
       FauxtonAPI.dispatch({
-        type: ActionTypes.FILE_UPLOAD_ERROR,
+        type: ActionTypes.SET_FILE_UPLOAD_PERCENTAGE,
         options: {
-          error: resp.responseJSON ? resp.responseJSON.reason : 'Error uploading file: (' + resp.statusText + ')'
+          percent: percentComplete
         }
       });
     }
-  });
+  };
+  const onSuccess = (doc) => {
+    // re-initialize the document editor. Only announce it's been updated when
+    initDocEditor({
+      doc: doc,
+      onLoaded: () => {
+        FauxtonAPI.dispatch({ type: ActionTypes.FILE_UPLOAD_SUCCESS });
+        FauxtonAPI.addNotification({
+          msg: 'Document saved successfully.',
+          type: 'success',
+          clear: true
+        });
+      }
+    });
+  };
+  const onError = (msg) => {
+    FauxtonAPI.dispatch({
+      type: ActionTypes.FILE_UPLOAD_ERROR,
+      options: {
+        error: msg
+      }
+    });
+  };
+  const httpRequest = new XMLHttpRequest();
+  currentUploadHttpRequest = httpRequest;
+  httpRequest.withCredentials = true;
+  if (httpRequest.upload) {
+    httpRequest.upload.onprogress = onProgress;
+  }
+  httpRequest.onloadend = () => {
+    currentUploadHttpRequest = undefined;
+  };
+  httpRequest.onerror = () => {
+    onError('Error uploading file');
+  };
+  httpRequest.onload = (e) => {
+    if (httpRequest.status >= 200 && httpRequest.status < 300) {
+      onSuccess(params.doc);
+    } else {
+      let errorMsg = 'Error uploading file. ';
+      if (e.responseText) {
+        try {
+          const json = JSON.parse(e.responseText);
+          if (json.error) {
+            errorMsg += 'Reason: ' + (json.reason || json.error);
+          }
+        } catch (err) {
+          //ignore parsing error
+        }
+      }
+      onError(errorMsg);
+    }
+  };
+  httpRequest.open('PUT', url);
+  httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+  httpRequest.setRequestHeader('Content-Type', file.type || `application/octet-stream`);
+  httpRequest.setRequestHeader('Accept', 'application/json');
+  httpRequest.send(file);
 }
 
 function cancelUpload () {
-  xhr.abort();
+  if (currentUploadHttpRequest) {
+    currentUploadHttpRequest.abort();
+  }
 }
 
 function resetUploadModal () {
diff --git a/app/addons/documents/doc-editor/components.js b/app/addons/documents/doc-editor/components.js
index 72441e2..3e3aba9 100644
--- a/app/addons/documents/doc-editor/components.js
+++ b/app/addons/documents/doc-editor/components.js
@@ -22,8 +22,6 @@ import GeneralComponents from "../../components/react-components";
 import { Modal } from "react-bootstrap";
 import Helpers from "../../../helpers";
 
-import DocumentResources from '../resources';
-
 var store = Stores.docEditorStore;
 
 class DocEditorController extends React.Component {
@@ -328,13 +326,15 @@ class UploadModal extends React.Component {
   state = this.getStoreState();
 
   render() {
-    var errorClasses = 'alert alert-error';
+    let errorClasses = 'alert alert-error';
     if (this.state.errorMessage === '') {
       errorClasses += ' hide';
     }
-    var loadIndicatorClasses = 'progress progress-info';
+    let loadIndicatorClasses = 'progress progress-info';
+    let disabledAttribute = {disabled: 'disabled'};
     if (!this.state.inProgress) {
       loadIndicatorClasses += ' hide';
+      disabledAttribute = {};
     }
 
     return (
@@ -350,7 +350,7 @@ class UploadModal extends React.Component {
                 Select a file to upload as an attachment to this document. Uploading a file saves the document as a new
                 revision.
               </p>
-              <input ref={el => this.attachments = el} type="file" name="_attachments" />
+              <input ref={el => this.attachments = el} type="file" name="_attachments" {...disabledAttribute}/>
               <br />
             </form>
 
@@ -361,7 +361,7 @@ class UploadModal extends React.Component {
         </Modal.Body>
         <Modal.Footer>
           <a href="#" data-bypass="true" className="cancel-link" onClick={this.closeModal}>Cancel</a>
-          <button href="#" id="upload-btn" data-bypass="true" className="btn btn-primary save" onClick={this.upload}>
+          <button href="#" id="upload-btn" data-bypass="true" className="btn btn-primary save" onClick={this.upload} {...disabledAttribute}>
             <i className="icon icon-upload" /> Upload Attachment
           </button>
         </Modal.Footer>
@@ -391,12 +391,12 @@ class CloneDocModal extends React.Component {
   };
 
   componentDidUpdate() {
-    //XXX model-code in component
     if (this.state.uuid === null) {
-      var uuid = new DocumentResources.UUID();
-      uuid.fetch().then(function () {
-        this.setState({ uuid: uuid.next() });
-      }.bind(this));
+      Helpers.getUUID().then((res) => {
+        if (res.uuids) {
+          this.setState({ uuid: res.uuids[0] });
+        }
+      });
     }
   }
 
diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js
index 5ae57a2..4fda5f7 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -11,25 +11,11 @@
 // the License.
 
 import app from "../../app";
+import Helpers from '../../helpers';
 import FauxtonAPI from "../../core/api";
+import { post } from "../../core/ajax";
 import Documents from "./shared-resources";
 
-Documents.UUID = FauxtonAPI.Model.extend({
-  initialize: function (options) {
-    options = _.extend({count: 1}, options);
-    this.count = options.count;
-  },
-
-  url: function () {
-    return app.host + "/_uuids?count=" + this.count;
-  },
-
-  next: function () {
-    return this.get("uuids").pop();
-  }
-});
-
-
 Documents.QueryParams = (function () {
   var _eachParams = function (params, action) {
     // clone to avoid in-place modification
@@ -85,18 +71,19 @@ Documents.DdocInfo = FauxtonAPI.Model.extend({
 
 Documents.NewDoc = Documents.Doc.extend({
   fetch: function () {
-    var uuid = new Documents.UUID();
-    var deferred = this.deferred = $.Deferred();
-    var that = this;
-
-    uuid.fetch().done(function () {
-      that.set("_id", uuid.next());
-      deferred.resolve();
+    return Helpers.getUUID().then((res) => {
+      if (res.uuids) {
+        this.set("_id", res.uuids[0]);
+      } else {
+        this.set("_id", 'enter_document_id');
+      }
+      return res;
+    }).catch(() => {
+      // Don't throw error so the user is still able
+      // to edit the new doc
+      this.set("_id", 'enter_document_id');
     });
-
-    return deferred.promise();
   }
-
 });
 
 Documents.BulkDeleteDoc = FauxtonAPI.Model.extend({
@@ -120,31 +107,25 @@ Documents.BulkDeleteDocCollection = FauxtonAPI.Collection.extend({
   },
 
   bulkDelete: function () {
-    var payload = this.createPayload(this.toJSON()),
-        promise = FauxtonAPI.Deferred(),
-        that = this;
-
-    $.ajax({
-      type: 'POST',
-      url: this.url(),
-      contentType: 'application/json',
-      dataType: 'json',
-      data: JSON.stringify(payload),
-    }).then(function (res) {
-      that.handleResponse(res, promise);
-    }).fail(function () {
-      var ids = _.reduce(that.toArray(), function (acc, doc) {
-        acc.push(doc.id);
-        return acc;
-      }, []);
-      that.trigger('error', ids);
-      promise.reject(ids);
+    const payload = this.createPayload(this.toJSON());
+    return new FauxtonAPI.Promise((resolve, reject) => {
+      post(this.url(), payload).then(res => {
+        if (res.error) {
+          throw new Error(res.reason || res.error);
+        }
+        this.handleResponse(res, resolve, reject);
+      }).catch(() => {
+        const ids = _.reduce(this.toArray(), (acc, doc) => {
+          acc.push(doc.id);
+          return acc;
+        }, []);
+        this.trigger('error', ids);
+        reject(ids);
+      });
     });
-
-    return promise;
   },
 
-  handleResponse: function (res, promise) {
+  handleResponse: function (res, resolve, reject) {
     var ids = _.reduce(res, function (ids, doc) {
       if (doc.error) {
         ids.errorIds.push(doc.id);
@@ -155,7 +136,7 @@ Documents.BulkDeleteDocCollection = FauxtonAPI.Collection.extend({
       }
 
       return ids;
-    }, {errorIds: [], successIds: []});
+    }, { errorIds: [], successIds: [] });
 
     this.removeDocuments(ids.successIds);
 
@@ -166,9 +147,9 @@ Documents.BulkDeleteDocCollection = FauxtonAPI.Collection.extend({
     // This is kind of tricky. If there are no documents deleted then rejects
     // otherwise resolve with list of successful and failed documents
     if (!_.isEmpty(ids.successIds)) {
-      promise.resolve(ids);
+      resolve(ids);
     } else {
-      promise.reject(ids.errorIds);
+      reject(ids.errorIds);
     }
 
     this.trigger('updated');
diff --git a/app/addons/documents/shared-resources.js b/app/addons/documents/shared-resources.js
index 807dea8..c59bd2e 100644
--- a/app/addons/documents/shared-resources.js
+++ b/app/addons/documents/shared-resources.js
@@ -12,6 +12,7 @@
 
 import app from "../../app";
 import FauxtonAPI from "../../core/api";
+import { deleteRequest } from "../../core/ajax";
 import PagingCollection from "../../../assets/js/plugins/cloudant.pagingcollection";
 
 // defined here because this is contains the base resources used throughout the addon and outside,
@@ -149,11 +150,12 @@ Documents.Doc = FauxtonAPI.Model.extend({
   },
 
   destroy: function () {
-    var url = this.url() + "?rev=" + this.get('_rev');
-    return $.ajax({
-      url: url,
-      dataType: 'json',
-      type: 'DELETE'
+    const url = this.url() + "?rev=" + this.get('_rev');
+    return deleteRequest(url).then(res => {
+      if (res.error) {
+        throw new Error(res.reason || res.error);
+      }
+      return res;
     });
   },
 
diff --git a/app/addons/documents/sidebar/actions.js b/app/addons/documents/sidebar/actions.js
index 168305d..ab93c2c 100644
--- a/app/addons/documents/sidebar/actions.js
+++ b/app/addons/documents/sidebar/actions.js
@@ -74,11 +74,12 @@ function toggleContent (designDoc, indexGroup) {
 //       Actions.selectNavItem('designDoc', { designDocName: 'my-design-doc', section: 'metadata' });
 //       Actions.selectNavItem('designDoc', { designDocName: 'my-design-doc', section: 'Views', indexName: 'my-view' });
 function selectNavItem (navItem, params) {
-  var settings = $.extend(true, {}, {
+  const settings = {
     designDocName: '',
     designDocSection: '',
-    indexName: ''
-  }, params);
+    indexName: '',
+    ...params
+  };
   settings.navItem = navItem;
 
   FauxtonAPI.dispatch({
diff --git a/app/addons/documents/tests/nightwatch/deletesDocuments.js b/app/addons/documents/tests/nightwatch/deletesDocuments.js
index 1435461..ec34cba 100644
--- a/app/addons/documents/tests/nightwatch/deletesDocuments.js
+++ b/app/addons/documents/tests/nightwatch/deletesDocuments.js
@@ -149,6 +149,7 @@ module.exports = {
       .url(baseUrl + '#/database/' + newDatabaseName + '/' + newDocumentName)
       .waitForElementPresent('#editor-container', waitTime, false)
       .clickWhenVisible('#doc-editor-actions-panel button[title="Delete"]')
+      .waitForElementVisible('.confirmation-modal', waitTime, false)
       .clickWhenVisible('.confirmation-modal button.btn.btn-primary')
       .waitForElementPresent('.jump-to-doc', waitTime, false)
 
diff --git a/app/helpers.js b/app/helpers.js
index 2e65228..0b49194 100644
--- a/app/helpers.js
+++ b/app/helpers.js
@@ -17,7 +17,9 @@
 // want to change this later, but for now this should be thought of as a
 // "purely functional" helper system.
 
+import app from "./app";
 import constants from "./constants";
+import { get } from "./core/ajax";
 import utils from "./core/utils";
 import moment from "moment";
 import _ from 'lodash';
@@ -69,4 +71,9 @@ Helpers.isIE1X = function() {
   return document.documentMode == 11 || document.documentMode == 10;
 };
 
+Helpers.getUUID = function (count = 1) {
+  const url = `${app.host}/_uuids?count=${count}`;
+  return get(url);
+};
+
 export default Helpers;

-- 
To stop receiving notification emails like this one, please contact
garren@apache.org.