You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by am...@apache.org on 2019/01/09 21:35:08 UTC

[couchdb-fauxton] branch master updated: Set number of databases per page to display (#1168)

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

amaranhao 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 209add2  Set number of databases per page to display (#1168)
209add2 is described below

commit 209add2137a9271fe9906aa8a2fc4e9e09e14f41
Author: Antonio Maranhao <30...@users.noreply.github.com>
AuthorDate: Wed Jan 9 16:35:01 2019 -0500

    Set number of databases per page to display (#1168)
    
    * Set number of databases per page to display
    
    * Update nano helpers to avoid Node warnings during Nightwatch tests
---
 .gitignore                                         |  2 +
 app/addons/components/actions.js                   | 25 +++----
 .../components/components/deletedatabasemodal.js   |  7 +-
 app/addons/databases/__tests__/components.test.js  | 31 ++++++++-
 .../databases/__tests__/databasepagination.test.js |  1 +
 app/addons/databases/actions.js                    | 62 ++++++++++--------
 app/addons/databases/actiontypes.js                |  1 +
 app/addons/databases/components.js                 | 76 ++++++++++++++++++----
 app/addons/databases/routes.js                     | 29 ++++++++-
 app/addons/databases/stores.js                     | 23 +++++++
 app/addons/fauxton/components.js                   | 11 +++-
 assets/less/pagination.less                        |  5 ++
 .../custom-commands/createDatabase.js              | 10 ++-
 test/nightwatch_tests/helpers/helpers.js           | 53 +++++++--------
 14 files changed, 242 insertions(+), 94 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3e9575d..7865768 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,5 @@ test/bundle.js
 test/bundle.dev.js
 test/templates.js
 test/dashboard.assets
+# test coverage dir
+coverage
diff --git a/app/addons/components/actions.js b/app/addons/components/actions.js
index 2e8ae4e..bc1783b 100644
--- a/app/addons/components/actions.js
+++ b/app/addons/components/actions.js
@@ -10,8 +10,9 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import FauxtonAPI from "../../core/api";
-import ActionTypes from "./actiontypes";
+import { deleteRequest } from '../../core/ajax';
+import FauxtonAPI from '../../core/api';
+import ActionTypes from './actiontypes';
 
 function showDeleteDatabaseModal (options) {
   FauxtonAPI.dispatch({
@@ -20,14 +21,14 @@ function showDeleteDatabaseModal (options) {
   });
 }
 
-function deleteDatabase (dbId) {
+function deleteDatabase (dbId, onDeleteSuccess) {
   const url = FauxtonAPI.urls('databaseBaseURL', 'server', dbId, '');
 
-  $.ajax({
-    url: url,
-    dataType: 'json',
-    type: 'DELETE'
-  }).then(function () {
+  deleteRequest(url).then(resp => {
+    if (!resp.ok) {
+      const msg = resp.reason || '';
+      throw new Error(msg);
+    }
     this.showDeleteDatabaseModal({ showModal: true });
 
     FauxtonAPI.addNotification({
@@ -37,10 +38,12 @@ function deleteDatabase (dbId) {
     });
 
     Backbone.history.loadUrl(FauxtonAPI.urls('allDBs', 'app'));
-
-  }.bind(this)).fail(function (rsp, error, msg) {
+    if (onDeleteSuccess) {
+      onDeleteSuccess();
+    }
+  }).catch(err => {
     FauxtonAPI.addNotification({
-      msg: 'Could not delete the database, reason ' + msg + '.',
+      msg: 'Could not delete the database. ' + err.message,
       type: 'error',
       clear: true
     });
diff --git a/app/addons/components/components/deletedatabasemodal.js b/app/addons/components/components/deletedatabasemodal.js
index 65a2f33..004ad68 100644
--- a/app/addons/components/components/deletedatabasemodal.js
+++ b/app/addons/components/components/deletedatabasemodal.js
@@ -20,7 +20,8 @@ import Actions from "../actions";
 export class DeleteDatabaseModal extends React.Component {
   static propTypes = {
     showHide: PropTypes.func.isRequired,
-    modalProps: PropTypes.object
+    modalProps: PropTypes.object,
+    onSuccess: PropTypes.func
   };
 
   state = {
@@ -64,12 +65,12 @@ export class DeleteDatabaseModal extends React.Component {
   onDeleteClick = (e) => {
     e.preventDefault();
 
-    Actions.deleteDatabase(this.getDatabaseName());
+    Actions.deleteDatabase(this.getDatabaseName(), this.props.onSuccess);
   };
 
   onInputKeypress = (e) => {
     if (e.keyCode === 13 && this.state.disableSubmit !== true) {
-      Actions.deleteDatabase(this.getDatabaseName());
+      Actions.deleteDatabase(this.getDatabaseName(), this.props.onSuccess);
     }
   };
 
diff --git a/app/addons/databases/__tests__/components.test.js b/app/addons/databases/__tests__/components.test.js
index f4d22d1..c1f83a1 100644
--- a/app/addons/databases/__tests__/components.test.js
+++ b/app/addons/databases/__tests__/components.test.js
@@ -52,16 +52,45 @@ describe('DatabasePagination', () => {
     store.reset();
   });
 
+  afterEach(() => {
+    sinon.restore();
+  });
+
   it('uses custom URL prefix on the navigation if passed through props', () => {
+    const mockNavigate = sinon.stub(FauxtonAPI, 'navigate');
     const pagination = mount(<Views.DatabasePagination linkPath="_custom_path" />);
     const links = pagination.find('a');
 
     expect(links.length).toBe(3);
     links.forEach(link => {
-      expect(link.props('href').href).toContain('_custom_path');
+      link.simulate('click', { preventDefault: () => {}});
+      sinon.assert.calledWithMatch(mockNavigate, '_custom_path');
+      mockNavigate.reset();
     });
   });
 
+  it('sets the correct page and perPage values', () => {
+    const mockNavigate = sinon.stub(FauxtonAPI, 'navigate');
+    store._fullDbList = ['db1', 'db2', 'db3', 'db4'];
+    store._page = 2;
+    store._limit = 2;
+    const pagination = mount(<Views.DatabasePagination />);
+    const links = pagination.find('a');
+    // "<<" link
+    links.at(0).simulate('click', { preventDefault: () => {}});
+    sinon.assert.calledWithMatch(mockNavigate, 'page=1&limit=2');
+    mockNavigate.reset();
+
+    // page "2" link
+    links.at(2).simulate('click', { preventDefault: () => {}});
+    sinon.assert.calledWithMatch(mockNavigate, 'page=2&limit=2');
+    mockNavigate.reset();
+
+    // ">>" link
+    links.at(3).simulate('click', { preventDefault: () => {}});
+    sinon.assert.calledWithMatch(mockNavigate, 'page=2&limit=2');
+  });
+
   it('renders the database count and range', () => {
     const controller = mount(<Views.DatabasePagination linkPath="_custom_path" />);
     const dbList = [];
diff --git a/app/addons/databases/__tests__/databasepagination.test.js b/app/addons/databases/__tests__/databasepagination.test.js
index 2777680..f29c84d 100644
--- a/app/addons/databases/__tests__/databasepagination.test.js
+++ b/app/addons/databases/__tests__/databasepagination.test.js
@@ -33,6 +33,7 @@ describe('Database Pagination', function () {
     const tempStore = {
       getTotalAmountOfDatabases: () => { return 10; },
       getPage: () => { return 1; },
+      getLimit: () => { return 20; },
       on: () => {},
       off: () => {}
     };
diff --git a/app/addons/databases/actions.js b/app/addons/databases/actions.js
index 85b1a2e..d576c7e 100644
--- a/app/addons/databases/actions.js
+++ b/app/addons/databases/actions.js
@@ -9,13 +9,13 @@
 // 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 app from "../../app";
-import Helpers from "../../helpers";
-import FauxtonAPI from "../../core/api";
-import { get } from "../../core/ajax";
+import app from '../../app';
+import Helpers from '../../helpers';
+import FauxtonAPI from '../../core/api';
+import { get } from '../../core/ajax';
 import DatabasesBase from '../databases/base';
-import Stores from "./stores";
-import ActionTypes from "./actiontypes";
+import Stores from './stores';
+import ActionTypes from './actiontypes';
 import * as API from './api';
 
 function getDatabaseDetails (dbList, fullDbList) {
@@ -75,19 +75,18 @@ function updateDatabases (options) {
 }
 
 export default {
-  getDatabaseList: getDatabaseList,
-  paginate: paginate,
-  getDatabaseDetails: getDatabaseDetails,
-  fetch: fetch,
-
-  init: function () {
-    const params = app.getParams();
-    const page = params.page ? parseInt(params.page, 10) : 1;
-    const limit = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE;
+  getDatabaseList,
+  paginate,
+  getDatabaseDetails,
+  fetch,
+  updateDatabases,
 
+  init({page, limit}) {
     this.setStartLoading();
-
     this.setPage(page);
+    if (limit) {
+      this.setLimit(limit);
+    }
 
     getDatabaseList(limit, page)
       .then((fullDbList) => {
@@ -104,30 +103,37 @@ export default {
       });
   },
 
-  updateDatabases,
-
-  setPage: function (page) {
+  setPage(page) {
     FauxtonAPI.dispatch({
       type: ActionTypes.DATABASES_SETPAGE,
       options: {
-        page: page
+        page
+      }
+    });
+  },
+
+  setLimit(limit) {
+    FauxtonAPI.dispatch({
+      type: ActionTypes.DATABASES_SETLIMIT,
+      options: {
+        limit
       }
     });
   },
 
-  setStartLoading: function () {
+  setStartLoading() {
     FauxtonAPI.dispatch({
       type: ActionTypes.DATABASES_STARTLOADING
     });
   },
 
-  setLoadComplete: function () {
+  setLoadComplete() {
     FauxtonAPI.dispatch({
       type: ActionTypes.DATABASES_LOADCOMPLETE
     });
   },
 
-  createNewDatabase: function (databaseName, partitioned) {
+  createNewDatabase(databaseName, partitioned) {
     if (_.isNull(databaseName) || databaseName.trim().length === 0) {
       FauxtonAPI.addNotification({
         msg: 'Please enter a valid database name',
@@ -147,7 +153,7 @@ export default {
 
     const db = Stores.databasesStore.obtainNewDatabaseModel(databaseName, partitioned);
     FauxtonAPI.addNotification({ msg: 'Creating database.' });
-    db.save().done(function () {
+    db.save().done(() => {
       FauxtonAPI.addNotification({
         msg: 'Database created successfully',
         type: 'success',
@@ -156,7 +162,7 @@ export default {
       const route = FauxtonAPI.urls('allDocs', 'app', app.utils.safeURLName(databaseName));
       app.router.navigate(route, { trigger: true });
     }
-    ).fail(function (xhr) {
+    ).fail((xhr) => {
       const responseText = JSON.parse(xhr.responseText).reason;
       FauxtonAPI.addNotification({
         msg: 'Create database failed: ' + responseText,
@@ -167,7 +173,7 @@ export default {
     );
   },
 
-  jumpToDatabase: function (databaseName) {
+  jumpToDatabase(databaseName) {
     if (_.isNull(databaseName) || databaseName.trim().length === 0) {
       return;
     }
@@ -179,10 +185,10 @@ export default {
     return setTimeout(() => { FauxtonAPI.navigate(url); });
   },
 
-  fetchAllDbsWithKey: (id, callback) => {
+  fetchAllDbsWithKey(id, callback) {
     const query = '?' + app.utils.queryParams({
       startkey: JSON.stringify(id),
-      endkey: JSON.stringify(id + "\u9999"),
+      endkey: JSON.stringify(id + '\u9999'),
       limit: 30
     });
 
diff --git a/app/addons/databases/actiontypes.js b/app/addons/databases/actiontypes.js
index bf7bd57..20f83a5 100644
--- a/app/addons/databases/actiontypes.js
+++ b/app/addons/databases/actiontypes.js
@@ -11,6 +11,7 @@
 // the License.
 export default {
   DATABASES_SETPAGE: 'DATABASES_SETPAGE',
+  DATABASES_SETLIMIT: 'DATABASES_SETLIMIT',
   DATABASES_SET_PROMPT_VISIBLE: 'DATABASES_SET_PROMPT_VISIBLE',
   DATABASES_STARTLOADING: 'DATABASES_STARTLOADING',
   DATABASES_LOADCOMPLETE: 'DATABASES_LOADCOMPLETE',
diff --git a/app/addons/databases/components.js b/app/addons/databases/components.js
index 203ea49..46eccb4 100644
--- a/app/addons/databases/components.js
+++ b/app/addons/databases/components.js
@@ -15,10 +15,10 @@ import FauxtonAPI from "../../core/api";
 import PropTypes from 'prop-types';
 
 import React, { Fragment } from "react";
-import ReactDOM from "react-dom";
 import Components from "../components/react-components";
 import ComponentsStore from "../components/stores";
 import ComponentsActions from "../components/actions";
+import PerPageSelector from '../documents/index-results/components/pagination/PerPageSelector';
 import FauxtonComponentsReact from "..//fauxton/components";
 import Stores from "./stores";
 import Actions from "./actions";
@@ -32,18 +32,40 @@ const {DeleteDatabaseModal, ToggleHeaderButton, TrayContents} = Components;
 
 
 class DatabasesController extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.reloadAfterDatabaseDeleted = this.reloadAfterDatabaseDeleted.bind(this);
+  }
+
   getStoreState = () => {
     return {
       dbList: databasesStore.getDbList(),
       loading: databasesStore.isLoading(),
       showDeleteDatabaseModal: deleteDbModalStore.getShowDeleteDatabaseModal(),
-      showPartitionedColumn: databasesStore.isPartitionedDatabasesAvailable()
+      showPartitionedColumn: databasesStore.isPartitionedDatabasesAvailable(),
+      page: databasesStore.getPage(),
+      limit: databasesStore.getLimit()
     };
   };
 
   componentDidMount() {
     databasesStore.on('change', this.onChange, this);
     deleteDbModalStore.on('change', this.onChange, this);
+    this.loadData();
+  }
+
+  componentDidUpdate(_, prevState) {
+    if (prevState.page !== this.state.page || prevState.limit !== this.state.limit) {
+      this.loadData();
+    }
+  }
+
+  loadData() {
+    Actions.init({
+      page: this.state.page,
+      limit: this.state.limit
+    });
   }
 
   componentWillUnmount() {
@@ -55,6 +77,18 @@ class DatabasesController extends React.Component {
     this.setState(this.getStoreState());
   };
 
+  reloadAfterDatabaseDeleted() {
+    let page = this.state.page;
+    if (page > 1 && this.state.dbList.length === 1) {
+      //show previous page if the db deleted was the only one in the page
+      page = page - 1;
+    }
+    Actions.init({
+      page,
+      limit: this.state.limit
+    });
+  }
+
   state = this.getStoreState();
 
   render() {
@@ -65,7 +99,8 @@ class DatabasesController extends React.Component {
         showDeleteDatabaseModal={this.state.showDeleteDatabaseModal}
         dbList={dbList}
         loading={loading}
-        showPartitionedColumn={this.state.showPartitionedColumn} />
+        showPartitionedColumn={this.state.showPartitionedColumn}
+        onDatabaseDeleted={this.reloadAfterDatabaseDeleted} />
     );
   }
 }
@@ -75,7 +110,8 @@ class DatabaseTable extends React.Component {
     dbList: PropTypes.array.isRequired,
     showDeleteDatabaseModal: PropTypes.object.isRequired,
     loading: PropTypes.bool.isRequired,
-    showPartitionedColumn: PropTypes.bool.isRequired
+    showPartitionedColumn: PropTypes.bool.isRequired,
+    onDatabaseDeleted: PropTypes.func
   };
 
   createRows = (dbList) => {
@@ -113,7 +149,8 @@ class DatabaseTable extends React.Component {
       <div className="view">
         <DeleteDatabaseModal
           showHide={this.showDeleteDatabaseModal}
-          modalProps={this.props.showDeleteDatabaseModal} />
+          modalProps={this.props.showDeleteDatabaseModal}
+          onSuccess={this.props.onDatabaseDeleted} />
         <table className="table table-striped fauxton-table-list databases">
           <thead>
             <tr>
@@ -400,7 +437,8 @@ class DatabasePagination extends React.Component {
 
     return {
       totalAmountOfDatabases: store.getTotalAmountOfDatabases(),
-      page: store.getPage()
+      page: store.getPage(),
+      limit: store.getLimit()
     };
   };
 
@@ -430,18 +468,32 @@ class DatabasePagination extends React.Component {
 
   state = this.getStoreState(this.props);
 
-  render() {
-    const {page, totalAmountOfDatabases} = this.state;
+  onPageLinkClick(pageNumber) {
+    const url = `#/${this.props.linkPath}?page=${pageNumber}&limit=${this.state.limit}`;
+    FauxtonAPI.navigate(url);
+  }
 
-    const urlPrefix = `#/${this.props.linkPath}?page=`;
-    var start = 1 + (page - 1) * FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE;
-    var end = Math.min(totalAmountOfDatabases, page * FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE);
+  onPerPageSelected(perPage) {
+    const url = `#/${this.props.linkPath}?page=${this.state.page}&limit=${perPage}`;
+    FauxtonAPI.navigate(url);
+  }
+
+  render() {
+    const {limit, page, totalAmountOfDatabases} = this.state;
+    const start = 1 + (page - 1) * limit;
+    const end = Math.min(totalAmountOfDatabases, page * limit);
 
     return (
       <footer className="all-db-footer pagination-footer">
         <div id="database-pagination">
-          <FauxtonComponentsReact.Pagination page={page} total={totalAmountOfDatabases} urlPrefix={urlPrefix} />
+          <FauxtonComponentsReact.Pagination
+            page={page}
+            total={totalAmountOfDatabases}
+            perPage={limit}
+            onClick={this.onPageLinkClick.bind(this)}
+          />
         </div>
+        <PerPageSelector label="Databases per page" perPage={limit} perPageChange={this.onPerPageSelected.bind(this)}/>
         <div className="current-databases">
           Showing <span className="all-db-footer__range">{start}&ndash;{end}</span>
           &nbsp;of&nbsp;<span className="all-db-footer__total-db-count">{totalAmountOfDatabases}</span>
diff --git a/app/addons/databases/routes.js b/app/addons/databases/routes.js
index 377310a..584993d 100644
--- a/app/addons/databases/routes.js
+++ b/app/addons/databases/routes.js
@@ -10,6 +10,7 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+import app from '../../app';
 import React from 'react';
 import FauxtonAPI from "../../core/api";
 import Actions from "./actions";
@@ -23,16 +24,38 @@ const AllDbsRouteObject = FauxtonAPI.RouteObject.extend({
   routes: {
     "": "allDatabases",
     "index.html": "allDatabases",
-    "_all_dbs(:params)": "allDatabases"
+    "_all_dbs(:extra)": "allDatabases"
   },
 
   roles: ['fx_loggedIn'],
 
   selectedHeader: "Databases",
 
-  allDatabases: function () {
-    Actions.init();
+  allDatabases: function (_, params) {
+    const {page, limit} = this.createParams(params);
+    Actions.setPage(page);
+    if (limit) {
+      Actions.setLimit(limit);
+    }
     return <Layout />;
+  },
+
+  createParams: function (options) {
+    let page = 1;
+    let limit = undefined;
+    if (options) {
+      const urlParams = app.getParams(options);
+      if (isFinite(parseInt(urlParams.page))) {
+        page = parseInt(urlParams.page);
+      }
+      if (isFinite(parseInt(urlParams.limit))) {
+        limit = parseInt(urlParams.limit);
+      }
+    }
+    return {
+      page,
+      limit
+    };
   }
 });
 Databases.RouteObjects = [AllDbsRouteObject];
diff --git a/app/addons/databases/stores.js b/app/addons/databases/stores.js
index 00befb0..2f77c4d 100644
--- a/app/addons/databases/stores.js
+++ b/app/addons/databases/stores.js
@@ -10,11 +10,20 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+import app from '../../app';
 import FauxtonAPI from "../../core/api";
 import ActionTypes from "./actiontypes";
 import Resources from "./resources";
 import Helpers from "../../helpers";
 
+function loadDatabasesPerPage() {
+  let dbsPerPage = app.utils.localStorageGet('fauxton:dbs_per_page');
+  if (!dbsPerPage) {
+    dbsPerPage = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE;
+  }
+  return dbsPerPage;
+}
+
 const Database = Resources.Model;
 
 const DatabasesStoreConstructor = FauxtonAPI.Store.extend({
@@ -27,6 +36,7 @@ const DatabasesStoreConstructor = FauxtonAPI.Store.extend({
     this._loading = false;
     this._promptVisible = false;
     this._page = 1;
+    this._limit = loadDatabasesPerPage();
 
     this._dbList = [];
     this._databaseDetails = [];
@@ -40,6 +50,15 @@ const DatabasesStoreConstructor = FauxtonAPI.Store.extend({
     return this._page;
   },
 
+  getLimit: function () {
+    return this._limit;
+  },
+
+  setLimit: function (limit) {
+    this._limit = limit;
+    app.utils.localStorageSet('fauxton:dbs_per_page', limit);
+  },
+
   isLoading: function () {
     return this._loading;
   },
@@ -114,6 +133,10 @@ const DatabasesStoreConstructor = FauxtonAPI.Store.extend({
         this._page = action.options.page;
         break;
 
+      case ActionTypes.DATABASES_SETLIMIT:
+        this.setLimit(action.options.limit);
+        break;
+
       case ActionTypes.DATABASES_SET_PROMPT_VISIBLE:
         this.setPromptVisible(action.options.visible);
         break;
diff --git a/app/addons/fauxton/components.js b/app/addons/fauxton/components.js
index 534fb1b..e33cf73 100644
--- a/app/addons/fauxton/components.js
+++ b/app/addons/fauxton/components.js
@@ -102,10 +102,19 @@ class Pagination extends React.Component {
     });
   };
 
+  getOnPageClick(pageNumber) {
+    return (e) => {
+      if (e) {
+        e.preventDefault();
+      }
+      this.props.onClick(pageNumber);
+    };
+  }
+
   getLink = (i, label) => {
     if (this.props.onClick) {
       return (
-        <a onClick={this.props.onClick.bind(null, i)} dangerouslySetInnerHTML={{__html: label}}></a>
+        <a onClick={this.getOnPageClick(i)} dangerouslySetInnerHTML={{__html: label}}></a>
       );
     }
     return (
diff --git a/assets/less/pagination.less b/assets/less/pagination.less
index d061bff..9a93356 100644
--- a/assets/less/pagination.less
+++ b/assets/less/pagination.less
@@ -49,7 +49,12 @@ ul.pagination > .active > a,
 ul.pagination > .active > span {
   background-color: @paginationActiveBackground;
 }
+ul.pagination > li > a:hover {
+  cursor: pointer;
+}
+
 ul.pagination > .active > a,
+ul.pagination > .active > a:hover,
 ul.pagination > .active > span {
   color: @grayLight;
   cursor: default;
diff --git a/test/nightwatch_tests/custom-commands/createDatabase.js b/test/nightwatch_tests/custom-commands/createDatabase.js
index 3eca7f2..617a84a 100644
--- a/test/nightwatch_tests/custom-commands/createDatabase.js
+++ b/test/nightwatch_tests/custom-commands/createDatabase.js
@@ -28,19 +28,17 @@ CreateDatabase.prototype.command = function (databaseName) {
   const nano = helpers.getNanoInstance(this.client.options.db_url);
 
 
-  nano.db.create(databaseName, (err, body, header) => {
-    if (err) {
-      console.log('Error in nano CreateDatabase Function: ' + databaseName, err.message);
-    }
-
+  nano.db.create(databaseName).then(() => {
     console.log('nano - created a database: ' + databaseName);
-
     const timeout = helpers.maxWaitTime;
     const couchUrl = this.client.options.db_url;
 
     checkForDatabaseCreated(couchUrl, databaseName, timeout, () => {
       this.emit('complete');
     });
+  }).catch(err => {
+    console.log('Error in CreateDatabase custom command. Db: ' + databaseName, 'Reason:', err.message);
+    this.emit('complete');
   });
 
   return this;
diff --git a/test/nightwatch_tests/helpers/helpers.js b/test/nightwatch_tests/helpers/helpers.js
index 11fd534..78e91ae 100644
--- a/test/nightwatch_tests/helpers/helpers.js
+++ b/test/nightwatch_tests/helpers/helpers.js
@@ -21,15 +21,11 @@ function getRandomInt(min, max) {
 const dbName = 'fauxton-selenium-tests-' + getRandomInt(1, 20000);
 
 function createDatabase(nano, database) {
-  return new Promise(function (resolve, reject) {
-    nano.db.create(database, function (err) {
-
-      //Tolerate database already existing
-      if (err && err.status_code != 412)
-        reject(err);
-      else
-        resolve();
-    });
+  return nano.db.create(database).catch(err => {
+    //Tolerate database already existing
+    if (err && err.statusCode !== 412) {
+      throw err;
+    }
   });
 }
 
@@ -48,29 +44,27 @@ module.exports = {
       database = module.exports.testDatabaseName;
 
     console.log('nano setting up database', database);
-
     // clean up the database we created previously
-    nano.db.destroy(database, function (err, body, header) {
-
+    nano.db.destroy(database).catch(err => {
       if (err && err.message !== 'Database does not exist.' && err.message !== 'missing') {
         console.log('Error in setting up ' + database, err.message);
       }
+    }).then(() => {
       // create a new database
-      nano.db.create(database, function (err, body, header) {
-          if (err) {
-            console.log('Error in setting up ' + database, err.message);
-          }
-          const databaseToCreate = ["_users", "_replicator", "_global_changes"];
-          const promises = databaseToCreate.map(db => createDatabase(nano, db));
-
-          Promise.all(promises).then(function () {
-            done();
-          }).catch(function (err) {
-            console.log("Unable to create required databases:" + JSON.stringify(err));
-            done();
-          });
-        }
-      );
+      nano.db.create(database).catch(err => {
+        console.log('Error in setting up ' + database, err.message);
+      }).then(() => {
+        // Create required dbs
+        const databaseToCreate = ["_users", "_replicator", "_global_changes"];
+        const promises = databaseToCreate.map(db => createDatabase(nano, db).catch(() => {}));
+        
+        Promise.all(promises).then(function () {
+          done();
+        }).catch(function (err) {
+          console.log("Unable to create required databases:" + JSON.stringify(err));
+          done();
+        });
+      });
     });
   },
 
@@ -79,10 +73,11 @@ module.exports = {
       database = module.exports.testDatabaseName;
 
     console.log('nano cleaning up', database);
-    nano.db.destroy(database, function (err, header, body) {
-      if (err) {
+    nano.db.destroy(database).catch(err => {
+      if (err && err.message !== 'Database does not exist.') {
         console.log('Error in cleaning up ' + database, err.message);
       }
+    }).then(() => {
       done();
     });
   }