couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gar...@apache.org
Subject [1/4] fauxton commit: updated refs/heads/master to ff25441
Date Wed, 16 Nov 2016 11:32:52 GMT
Repository: couchdb-fauxton
Updated Branches:
  refs/heads/master b245c6d20 -> ff25441bc


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/components/source.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/source.js b/app/addons/replication/components/source.js
new file mode 100644
index 0000000..e73f75d
--- /dev/null
+++ b/app/addons/replication/components/source.js
@@ -0,0 +1,170 @@
+// 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.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+import ReactSelect from 'react-select';
+import RemoteExample from './remoteexample';
+
+const { StyledSelect } = Components;
+
+const RemoteSourceInput = ({onChange, value}) =>
+  <div className="replication__section">
+    <div className="replication__input-label">Database URL:</div>
+    <div className="">
+      <input
+        type="text"
+        className="replication__remote-connection-url"
+        placeholder="https://"
+        value={value}
+        onChange={(e) => onChange(e.target.value)}
+      />
+    <RemoteExample />
+    </div>
+  </div>;
+
+RemoteSourceInput.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const LocalSourceInput = ({value, onChange, databases}) => {
+  const options = databases.map(db => ({value: db, label: db}));
+  return (
+    <div className="replication__section">
+      <div className="replication__input-label">
+        Source Name:
+      </div>
+      <div className="replication__input-react-select">
+        <ReactSelect
+          name="source-name"
+          value={value}
+          placeholder="Database name"
+          options={options}
+          clearable={false}
+          onChange={({value}) => onChange(value)} />
+      </div>
+    </div>
+  );
+};
+
+LocalSourceInput.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ReplicationSourceRow = ({replicationSource, databases, localSource, remoteSource, onChangeRemote, onChangeLocal}) => {
+  if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL) {
+    return <LocalSourceInput
+      value={localSource}
+      databases={databases}
+      onChange={onChangeLocal}
+           />;
+  }
+
+  return <RemoteSourceInput value={remoteSource} onChange={onChangeRemote} />;
+};
+
+ReplicationSourceRow.propTypes = {
+  replicationSource: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  localSource: React.PropTypes.string.isRequired,
+  remoteSource: React.PropTypes.string.isRequired,
+  onChangeRemote: React.PropTypes.func.isRequired,
+  onChangeLocal: React.PropTypes.func.isRequired
+};
+
+const replicationSourceSelectOptions = () => {
+  return [
+    { value: '', label: 'Select source' },
+    { value: Constants.REPLICATION_SOURCE.LOCAL, label: 'Local database' },
+    { value: Constants.REPLICATION_SOURCE.REMOTE, label: 'Remote database' }
+  ].map((option) => {
+    return (
+      <option value={option.value} key={option.value}>{option.label}</option>
+    );
+  });
+};
+
+export const ReplicationSourceSelect = ({onChange, value}) => {
+
+  return (
+    <div className="replication__section">
+      <div className="replication__input-label">
+        Replication Source:
+      </div>
+      <div className="replication__input-select">
+        <StyledSelect
+          selectContent={replicationSourceSelectOptions()}
+          selectChange={(e) => onChange(e.target.value)}
+          selectId="replication-source"
+          selectValue={value} />
+      </div>
+    </div>
+  );
+};
+
+ReplicationSourceSelect.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+export class ReplicationSource extends React.Component {
+
+  getReplicationSourceRow () {
+    const {
+      replicationSource,
+      localSource,
+      onLocalSourceChange,
+      onRemoteSourceChange,
+      remoteSource,
+      databases
+    } = this.props;
+
+    if (!replicationSource) {
+      return null;
+    }
+
+    return <ReplicationSourceRow
+      replicationSource={replicationSource}
+      databases={databases}
+      localSource={localSource}
+      remoteSource={remoteSource}
+      onChangeLocal={onLocalSourceChange}
+      onChangeRemote={onRemoteSourceChange}
+           />;
+  }
+
+  render () {
+    const {replicationSource, onSourceSelect, localSource, remoteSource, databases} = this.props;
+    const Actions = {};
+    return (
+      <div>
+        <ReplicationSourceSelect
+          onChange={onSourceSelect}
+          value={replicationSource}
+        />
+        {this.getReplicationSourceRow()}
+      </div>
+    );
+  }
+}
+
+ReplicationSource.propTypes = {
+  replicationSource: React.PropTypes.string.isRequired,
+  localSource: React.PropTypes.string.isRequired,
+  remoteSource: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  onLocalSourceChange: React.PropTypes.func.isRequired,
+  onRemoteSourceChange: React.PropTypes.func.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/components/submit.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/submit.js b/app/addons/replication/components/submit.js
new file mode 100644
index 0000000..3c9281c
--- /dev/null
+++ b/app/addons/replication/components/submit.js
@@ -0,0 +1,43 @@
+// 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.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+
+const {ConfirmButton} = Components;
+
+export const ReplicationSubmit = ({onClear, disabled, onClick}) =>
+<div className="replication__button-row">
+  <ConfirmButton
+    id="replicate"
+    text="Start Replication"
+    onClick={onClick}
+    disabled={disabled}
+  />
+  <a
+    className="replication__clear-link"
+    href="#"
+    data-bypass="true"
+    onClick={(e) => {
+      e.preventDefault();
+      onClear();
+    }}>
+    Clear
+  </a>
+</div>;
+
+
+ReplicationSubmit.propTypes = {
+  disabled: React.PropTypes.bool.isRequired,
+  onClick: React.PropTypes.func.isRequired,
+  onClear: React.PropTypes.func.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/components/target.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/target.js b/app/addons/replication/components/target.js
new file mode 100644
index 0000000..c605ac2
--- /dev/null
+++ b/app/addons/replication/components/target.js
@@ -0,0 +1,209 @@
+// 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.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+import ReactSelect from 'react-select';
+import RemoteExample from './remoteexample';
+
+const { StyledSelect } = Components;
+
+const replicationTargetSourceOptions = () => {
+  return [
+    { value: '', label: 'Select target' },
+    { value: Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE, label: 'Existing local database' },
+    { value: Constants.REPLICATION_TARGET.EXISTING_REMOTE_DATABASE, label: 'Existing remote database' },
+    { value: Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE, label: 'New local database' },
+    { value: Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE, label: 'New remote database' }
+  ].map((option) => {
+    return (
+      <option value={option.value} key={option.value}>{option.label}</option>
+    );
+  });
+};
+
+const ReplicationTargetSelect = ({value, onChange}) => {
+  return (
+    <div className="replication__section">
+      <div className="replication__input-label">
+        Replication Target:
+      </div>
+      <div id="replication-target" className="replication__input-select">
+        <StyledSelect
+          selectContent={replicationTargetSourceOptions()}
+          selectChange={(e) => onChange(e.target.value)}
+          selectId="replication-target"
+          selectValue={value} />
+      </div>
+    </div>
+  );
+};
+
+ReplicationTargetSelect.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const RemoteTargetReplicationRow = ({onChange, value, newRemote}) => {
+  return (
+    <div>
+      <input
+        type="text"
+        className="replication__remote-connection-url"
+        placeholder="https://"
+        value={value}
+        onChange={(e) => onChange(e.target.value)}
+      />
+    <RemoteExample newRemote={newRemote} />
+    </div>
+  );
+};
+
+RemoteTargetReplicationRow.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ExistingLocalTargetReplicationRow = ({onChange, value, databases}) => {
+  const options = databases.map(db => ({value: db, label: db}));
+  return (
+    <div id="replication-target-local" className="replication__input-react-select">
+      <ReactSelect
+        value={value}
+        options={options}
+        placeholder="Database name"
+        clearable={false}
+        onChange={({value}) => onChange(value)}
+      />
+    </div>
+  );
+};
+
+ExistingLocalTargetReplicationRow.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const NewLocalTargetReplicationRow = ({onChange, value}) =>
+  <input
+    type="text"
+    className="replication__new-input"
+    placeholder="Database name"
+    value={value}
+    onChange={(e) => onChange(e.target.value)}
+  />;
+
+NewLocalTargetReplicationRow.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ReplicationTargetRow = ({
+  replicationTarget,
+  onLocalTargetChange,
+  onRemoteTargetChange,
+  localTarget,
+  remoteTarget,
+  databases
+}) => {
+  if (!replicationTarget) {
+    return null;
+  }
+  let input;
+
+  if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
+    targetLabel = 'New Database:';
+    input = <NewLocalTargetReplicationRow
+      value={localTarget}
+      onChange={onLocalTargetChange}
+            />;
+  } else if (replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE) {
+    input = <ExistingLocalTargetReplicationRow
+      onChange={onLocalTargetChange}
+      databases={databases}
+      value={localTarget}
+            />;
+  } else {
+    input = <RemoteTargetReplicationRow
+      onChange={onRemoteTargetChange}
+      value={remoteTarget}
+      newRemote={Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE === replicationTarget}
+            />;
+  }
+
+  let targetLabel = 'Target Name:';
+
+  if (replicationTarget === Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE ||
+      replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
+    targetLabel = 'New Database:';
+  }
+
+  return (
+    <div className="replication__section">
+      <div className="replication__input-label">{targetLabel}</div>
+      <div>
+        {input}
+      </div>
+    </div>
+  );
+};
+
+ReplicationTargetRow.propTypes = {
+  databases: React.PropTypes.array.isRequired,
+  onLocalTargetChange: React.PropTypes.func.isRequired,
+  onRemoteTargetChange: React.PropTypes.func.isRequired,
+  remoteTarget: React.PropTypes.string.isRequired,
+  localTarget: React.PropTypes.string.isRequired,
+  replicationTarget: React.PropTypes.string.isRequired
+};
+
+export class ReplicationTarget extends React.Component {
+
+  render () {
+    const {
+      replicationTarget,
+      onLocalTargetChange,
+      onTargetChange,
+      databases,
+      localTarget,
+      onRemoteTargetChange,
+      remoteTarget
+    } = this.props;
+    return (
+      <div>
+        <ReplicationTargetSelect
+          value={replicationTarget}
+          onChange={onTargetChange}
+        />
+        <ReplicationTargetRow
+          remoteTarget={remoteTarget}
+          replicationTarget={replicationTarget}
+          databases={databases}
+          localTarget={localTarget}
+          onRemoteTargetChange={onRemoteTargetChange}
+          onLocalTargetChange={onLocalTargetChange}
+        />
+      </div>
+    );
+  }
+}
+
+ReplicationTarget.propTypes = {
+  databases: React.PropTypes.array.isRequired,
+  onTargetChange: React.PropTypes.func.isRequired,
+  onLocalTargetChange: React.PropTypes.func.isRequired,
+  onRemoteTargetChange: React.PropTypes.func.isRequired,
+  remoteTarget: React.PropTypes.string.isRequired,
+  localTarget: React.PropTypes.string.isRequired,
+  replicationTarget: React.PropTypes.string.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/controller.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/controller.js b/app/addons/replication/controller.js
new file mode 100644
index 0000000..90e59da
--- /dev/null
+++ b/app/addons/replication/controller.js
@@ -0,0 +1,212 @@
+// 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.
+import React from 'react';
+import app from '../../app';
+import FauxtonAPI from '../../core/api';
+import Stores from './stores';
+import Actions from './actions';
+import AuthActions from '../auth/actions';
+import Constants from './constants';
+import base64 from 'base-64';
+import Components from '../components/react-components.react';
+import NewReplication from './components/newreplication';
+import Activity from './components/activity';
+import {checkReplicationDocID} from './api';
+import {OnePane, OnePaneHeader, OnePaneContent} from '../components/layouts';
+
+const {LoadLines, ConfirmButton, Polling, RefreshBtn} = Components;
+
+const store = Stores.replicationStore;
+
+export default class ReplicationController extends React.Component {
+  constructor (props) {
+    super(props);
+    this.state = this.getStoreState();
+  }
+
+  getStoreState () {
+    return {
+      loading: store.isLoading(),
+      activityLoading: store.isActivityLoading(),
+      databases: store.getDatabases(),
+      authenticated: store.isAuthenticated(),
+      password: store.getPassword(),
+
+      // source fields
+      replicationSource: store.getReplicationSource(),
+      localSource: store.getlocalSource(),
+      localSourceKnown: store.isLocalSourceKnown(),
+      remoteSource: store.getRemoteSource(),
+
+      // target fields
+      replicationTarget: store.getReplicationTarget(),
+      localTarget: store.getlocalTarget(),
+      localTargetKnown: store.isLocalTargetKnown(),
+      remoteTarget: store.getRemoteTarget(),
+
+      // other
+      passwordModalVisible: store.isPasswordModalVisible(),
+      showConflictModal: store.isConflictModalVisible(),
+      replicationType: store.getReplicationType(),
+      replicationDocName: store.getReplicationDocName(),
+      submittedNoChange: store.getSubmittedNoChange(),
+      statusDocs: store.getFilteredReplicationStatus(),
+      statusFilter: store.getStatusFilter(),
+      allDocsSelected: store.getAllDocsSelected(),
+      someDocsSelected:  store.someDocsSelected(),
+      username: store.getUsername(),
+      password: store.getPassword(),
+      activitySort: store.getActivitySort()
+    };
+  }
+
+  componentDidMount () {
+    store.on('change', this.onChange, this);
+    Actions.initReplicator(this.props.localSource);
+    Actions.getReplicationActivity();
+
+    if (this.props.replicationId) {
+      Actions.getReplicationStateFrom(this.props.replicationId);
+    }
+  }
+
+  componentWillUnmount () {
+    store.off('change', this.onChange);
+    Actions.clearReplicationForm();
+  }
+
+  onChange () {
+    this.setState(this.getStoreState());
+  }
+
+  showSection () {
+    const {
+      replicationSource, replicationTarget, replicationType, replicationDocName,
+      passwordModalVisible, databases, localSource, remoteSource, remoteTarget,
+      localTarget, selectedTab, statusDocs, statusFilter, loading, allDocsSelected,
+      someDocsSelected, showConflictModal, localSourceKnown, localTargetKnown,
+      username, password, authenticated, activityLoading, submittedNoChange, activitySort
+    } = this.state;
+
+    if (this.props.section === 'new replication') {
+      if (loading) {
+        return <LoadLines/>;
+      }
+
+      const updateFormField = (field) => {
+        return (value) => {
+          Actions.updateFormField(field, value);
+        };
+      };
+
+      return <NewReplication
+        docs={statusDocs}
+        localTargetKnown={localTargetKnown}
+        localSourceKnown={localSourceKnown}
+        clearReplicationForm={Actions.clearReplicationForm}
+        replicate={Actions.replicate}
+        showPasswordModal={AuthActions.showPasswordModal}
+        replicationSource={replicationSource}
+        replicationTarget={replicationTarget}
+        replicationType={replicationType}
+        replicationDocName={replicationDocName}
+        passwordModalVisible={passwordModalVisible}
+        databases={databases}
+        localSource={localSource}
+        remoteSource={remoteSource}
+        remoteTarget={remoteTarget}
+        localTarget={localTarget}
+        updateFormField={updateFormField}
+        conflictModalVisible={showConflictModal}
+        hideConflictModal={Actions.hideConflictModal}
+        showConflictModal={Actions.showConflictModal}
+        checkReplicationDocID={checkReplicationDocID}
+        authenticated={authenticated}
+        username={username}
+        password={password}
+        submittedNoChange={submittedNoChange}
+      />;
+    }
+
+    if (activityLoading) {
+      return <LoadLines/>;
+    }
+
+
+    return <Activity
+      docs={statusDocs}
+      filter={statusFilter}
+      onFilterChange={Actions.filterDocs}
+      selectAllDocs={Actions.selectAllDocs}
+      selectDoc={Actions.selectDoc}
+      selectAllDocs={Actions.selectAllDocs}
+      allDocsSelected={allDocsSelected}
+      someDocsSelected={someDocsSelected}
+      deleteDocs={Actions.deleteDocs}
+      activitySort={activitySort}
+      changeActivitySort={Actions.changeActivitySort}
+           />;
+  }
+
+  getCrumbs () {
+    if (this.props.section === 'new replication') {
+      return [
+        {name: 'Replication', link: 'replication'},
+        {name: 'New Replication'}
+      ];
+    }
+
+    return [
+      {name: 'Replication'}
+    ];
+  }
+
+  getHeaderComponents () {
+    if (this.props.section === 'new replication') {
+      return null;
+    }
+
+    return (
+      <div className="right-header-flex">
+        <Polling
+          min={60}
+          max={600}
+          startValue={300}
+          stepSize={60}
+          onPoll={Actions.getReplicationActivity}
+          />
+        <RefreshBtn
+          refresh={Actions.getReplicationActivity}
+          />
+      </div>
+    );
+  }
+
+  render () {
+    return (
+      <OnePane>
+        <OnePaneHeader
+          crumbs={this.getCrumbs()}
+        >
+        {this.getHeaderComponents()}
+        </OnePaneHeader>
+        <OnePaneContent>
+          <div className="template-content flex-body flex-layout flex-col">
+            <div className="replication__page flex-layout flex-col">
+              {this.showSection()}
+            </div>
+          </div>
+        </OnePaneContent>
+      </OnePane>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/helpers.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/helpers.js b/app/addons/replication/helpers.js
index fd82b45..52472fa 100644
--- a/app/addons/replication/helpers.js
+++ b/app/addons/replication/helpers.js
@@ -1,17 +1,22 @@
+// 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.
 
-function getDatabaseLabel (db) {
-  let dbString = (_.isString(db)) ? db.trim().replace(/\/$/, '') : db.url;
-  const matches = dbString.match(/[^\/]+$/, '');
-  return matches[0];
-}
+import _ from 'underscore';
 
-function getReactSelectOptions (list) {
-  return _.map(list, (item) => {
-    return { value: item, label: item };
-  });
-}
+const getDatabaseLabel = db => {
+  const dbString = (_.isString(db)) ? db.trim().replace(/\/$/, '') : db.url;
+  return (new URL(dbString)).pathname.slice(1);
+};
 
 export default {
   getDatabaseLabel,
-  getReactSelectOptions
 };

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/route.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/route.js b/app/addons/replication/route.js
index 6947045..6d4b726 100644
--- a/app/addons/replication/route.js
+++ b/app/addons/replication/route.js
@@ -10,34 +10,67 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import app from '../../app';
 import FauxtonAPI from '../../core/api';
-import Actions from './actions';
-import Components from './components.react';
+import ReplicationController from './controller';
+import ComponentActions from '../components/actions';
 
+const ReplicationRouteObject = FauxtonAPI.RouteObject.extend({
+  layout: 'empty',
+  hideNotificationCenter: true,
+  hideApiBar: true,
 
-var ReplicationRouteObject = FauxtonAPI.RouteObject.extend({
-  layout: 'one_pane',
   routes: {
-    'replication': 'defaultView',
-    'replication/:dbname': 'defaultView'
+    'replication/_create': 'defaultView',
+    'replication/:dbname': 'defaultView',
+    'replication/id/:id': 'fromId',
+    'replication': 'activityView'
   },
   selectedHeader: 'Replication',
-  apiUrl: function () {
-    return [FauxtonAPI.urls('replication', 'api'), FauxtonAPI.constants.DOC_URLS.REPLICATION];
-  },
-  crumbs: [
-    { name: 'Replication', link: 'replication' }
-  ],
+
   roles: ['fx_loggedIn'],
+
+  setActivityCrumbs () {
+    this.crumbs = [
+      {name: 'Replication'}
+    ];
+  },
+
+  setCreateReplicationCrumbs () {
+    this.crumbs = [
+      {name: 'Replication', link: 'replication'},
+      {name: 'New Replication'}
+    ];
+  },
+
   defaultView: function (databaseName) {
-    const sourceDatabase = databaseName || '';
-    Actions.initReplicator(sourceDatabase);
-    this.setComponent('#dashboard-content', Components.ReplicationController);
+    // ComponentActions.hideAPIBarButton();
+    // this.setCreateReplicationCrumbs();
+    const localSource = databaseName || '';
+    this.setComponent('.template', ReplicationController, {
+      localSource: localSource,
+      section: 'new replication'
+    });
+
+  },
+
+  fromId: function (replicationId) {
+    // ComponentActions.hideAPIBarButton();
+    // this.setCreateReplicationCrumbs();
+    this.setComponent('.template', ReplicationController, {
+      replicationId: replicationId,
+      section: 'new replication'
+    });
+  },
+
+  activityView: function () {
+    // ComponentActions.hideAPIBarButton();
+    // this.setActivityCrumbs();
+    this.setComponent('.template', ReplicationController, {
+      section: 'activity'
+    });
   }
 });
 
-var Replication = {};
-Replication.RouteObjects = [ReplicationRouteObject];
-
-export default Replication;
+export default {
+  RouteObjects: [ReplicationRouteObject]
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/stores.js b/app/addons/replication/stores.js
index 2da7c61..26376d2 100644
--- a/app/addons/replication/stores.js
+++ b/app/addons/replication/stores.js
@@ -14,14 +14,14 @@ import FauxtonAPI from '../../core/api';
 import ActionTypes from './actiontypes';
 import Constants from './constants';
 import AccountActionTypes from '../auth/actiontypes';
-
+import _ from 'lodash';
 
 const ReplicationStore = FauxtonAPI.Store.extend({
-  initialize: function () {
+  initialize () {
     this.reset();
   },
 
-  reset: function () {
+  reset () {
     this._loading = false;
     this._databases = [];
     this._authenticated = false;
@@ -29,132 +29,294 @@ const ReplicationStore = FauxtonAPI.Store.extend({
 
     // source fields
     this._replicationSource = '';
-    this._sourceDatabase = '';
+    this._localSource = '';
     this._remoteSource = '';
 
     // target fields
     this._replicationTarget = '';
-    this._targetDatabase = '';
+    this._localTarget = '';
     this._remoteTarget = '';
 
     // other
     this._isPasswordModalVisible = false;
+    this._isConflictModalVisible = false;
     this._replicationType = Constants.REPLICATION_TYPE.ONE_TIME;
     this._replicationDocName = '';
+    this._submittedNoChange = false;
+    this._statusDocs = [];
+    this._statusFilteredStatusDocs = [];
+    this._statusFilter = '';
+    this._allDocsSelected = false;
+    this._username = '';
+    this._password = '';
+    this._activityLoading = false;
+
+    this.loadActivitySort();
+  },
+
+  getActivitySort () {
+    return this._activitySort;
+  },
+
+  loadActivitySort () {
+    let sort = app.utils.localStorageGet('replication-activity-sort');
+    if (!sort) {
+      sort = {
+        descending: false,
+        column: 'statusTime'
+      };
+
+      this.setActivitySort(sort);
+    }
+
+    this._activitySort = sort;
+  },
+
+  setActivitySort (sort) {
+    app.utils.localStorageSet('replication-activity-sort', sort);
+    this._activitySort = sort;
+  },
+
+  setCredentials (username, password) {
+    this._username = username;
+    this._password = password;
+  },
+
+  getUsername () {
+    return this._username;
+  },
+
+  getPassword () {
+    return this._password;
+  },
+
+  getSubmittedNoChange () {
+    return this._submittedNoChange;
+  },
+
+  changeAfterSubmit () {
+    this._submittedNoChange = false;
   },
 
-  isLoading: function () {
+  isLoading () {
     return this._loading;
   },
 
-  isAuthenticated: function () {
+  isActivityLoading () {
+    return this._activityLoading;
+  },
+
+  isAuthenticated () {
     return this._authenticated;
   },
 
-  getReplicationSource: function () {
+  getReplicationSource () {
     return this._replicationSource;
   },
 
-  getSourceDatabase: function () {
-    return this._sourceDatabase;
+  getlocalSource () {
+    return this._localSource;
   },
 
-  isLocalSourceDatabaseKnown: function () {
-    return _.contains(this._databases, this._sourceDatabase);
+  isLocalSourceKnown () {
+    return _.contains(this._databases, this._localSource);
   },
 
-  isLocalTargetDatabaseKnown: function () {
-    return _.contains(this._databases, this._targetDatabase);
+  isLocalTargetKnown () {
+    return _.contains(this._databases, this._localTarget);
   },
 
-  getReplicationTarget: function () {
+  getReplicationTarget () {
     return this._replicationTarget;
   },
 
-  getDatabases: function () {
+  getDatabases () {
     return this._databases;
   },
 
-  setDatabases: function (databases) {
+  setDatabases (databases) {
     this._databases = databases;
   },
 
-  getReplicationType: function () {
+  getReplicationType () {
     return this._replicationType;
   },
 
-  getTargetDatabase: function () {
-    return this._targetDatabase;
+  getlocalTarget () {
+    return this._localTarget;
   },
 
-  getReplicationDocName: function () {
+  getReplicationDocName () {
     return this._replicationDocName;
   },
 
+  setReplicationStatus (docs) {
+    this._statusDocs = docs;
+  },
+
+  getReplicationStatus () {
+    return this._statusDocs;
+  },
+
+  getFilteredReplicationStatus () {
+    return this._statusDocs.filter(doc => {
+      return _.values(doc).filter(item => {
+        if (!item) {return null;}
+        return item.toString().toLowerCase().match(this._statusFilter);
+      }).length > 0;
+    });
+  },
+
+  selectDoc (id) {
+    const doc = this._statusDocs.find(doc => doc._id === id);
+    if (!doc) {
+      return;
+    }
+
+    doc.selected = !doc.selected;
+    this._allDocsSelected = false;
+  },
+
+  selectAllDocs () {
+    this._allDocsSelected = !this._allDocsSelected;
+    this.getFilteredReplicationStatus().forEach(doc => doc.selected = this._allDocsSelected);
+  },
+
+  someDocsSelected () {
+    return this.getFilteredReplicationStatus().some(doc => doc.selected);
+  },
+
+  getAllDocsSelected () {
+    return this._allDocsSelected;
+  },
+
+  setStatusFilter (filter) {
+    this._statusFilter = filter;
+  },
+
+  getStatusFilter () {
+    return this._statusFilter;
+  },
   // to cut down on boilerplate
-  updateFormField: function (fieldName, value) {
+  updateFormField (fieldName, value) {
 
     // I know this could be done by just adding the _ prefix to the passed field name, I just don't much like relying
     // on the var names like that...
     var validFieldMap = {
       remoteSource: '_remoteSource',
       remoteTarget: '_remoteTarget',
-      targetDatabase: '_targetDatabase',
+      localTarget: '_localTarget',
       replicationType: '_replicationType',
       replicationDocName: '_replicationDocName',
       replicationSource: '_replicationSource',
       replicationTarget: '_replicationTarget',
-      sourceDatabase: '_sourceDatabase'
+      localSource: '_localSource'
     };
 
     this[validFieldMap[fieldName]] = value;
   },
 
-  getRemoteSource: function () {
+  getRemoteSource () {
     return this._remoteSource;
   },
 
-  getRemoteTarget: function () {
+  getRemoteTarget () {
     return this._remoteTarget;
   },
 
-  isPasswordModalVisible: function () {
+  isPasswordModalVisible () {
     return this._isPasswordModalVisible;
   },
 
-  getPassword: function () {
+  isConflictModalVisible () {
+    return this._isConflictModalVisible;
+  },
+
+  getPassword () {
     return this._password;
   },
 
-  dispatch: function (action) {
-    switch (action.type) {
+  setStateFromDoc (doc) {
+    Object.keys(doc).forEach(key => {
+      this.updateFormField(key, doc[key]);
+    });
+  },
+
+  dispatch ({type, options}) {
+    switch (type) {
 
       case ActionTypes.INIT_REPLICATION:
         this._loading = true;
-        this._sourceDatabase = action.options.sourceDatabase;
+        this._localSource = options.localSource;
 
-        if (this._sourceDatabase) {
+        if (this._localSource) {
           this._replicationSource = Constants.REPLICATION_SOURCE.LOCAL;
           this._remoteSource = '';
           this._replicationTarget = '';
-          this._targetDatabase = '';
+          this._localTarget = '';
           this._remoteTarget = '';
         }
       break;
 
       case ActionTypes.REPLICATION_DATABASES_LOADED:
-        this.setDatabases(action.options.databases);
+        this.setDatabases(options.databases);
         this._loading = false;
       break;
 
       case ActionTypes.REPLICATION_UPDATE_FORM_FIELD:
-        this.updateFormField(action.options.fieldName, action.options.value);
+        this.changeAfterSubmit();
+        this.updateFormField(options.fieldName, options.value);
       break;
 
       case ActionTypes.REPLICATION_CLEAR_FORM:
         this.reset();
       break;
 
+      case ActionTypes.REPLICATION_STARTING:
+        this._submittedNoChange = true;
+      break;
+
+      case ActionTypes.REPLICATION_FETCHING_STATUS:
+        this._activityLoading = true;
+      break;
+
+      case ActionTypes.REPLICATION_STATUS:
+        this._activityLoading = false;
+        this.setReplicationStatus(options);
+      break;
+
+      case ActionTypes.REPLICATION_FILTER_DOCS:
+        this.setStatusFilter(options);
+      break;
+
+      case ActionTypes.REPLICATION_TOGGLE_DOC:
+        this.selectDoc(options);
+      break;
+
+      case ActionTypes.REPLICATION_TOGGLE_ALL_DOCS:
+        this.selectAllDocs();
+      break;
+
+      case ActionTypes.REPLICATION_SET_STATE_FROM_DOC:
+        this.setStateFromDoc(options);
+      break;
+
+      case ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL:
+        this._isConflictModalVisible = true;
+      break;
+
+      case ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL:
+        this._isConflictModalVisible = false;
+      break;
+
+      case ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT:
+        this.setActivitySort(options);
+      break;
+
+      case ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS:
+        this._allDocsSelected = false;
+      break;
+
       case AccountActionTypes.AUTH_SHOW_PASSWORD_MODAL:
         this._isPasswordModalVisible = true;
       break;
@@ -165,7 +327,7 @@ const ReplicationStore = FauxtonAPI.Store.extend({
 
       case AccountActionTypes.AUTH_CREDS_VALID:
         this._authenticated = true;
-        this._password = action.options.password;
+        this.setCredentials(options.username, options.password);
       break;
 
       case AccountActionTypes.AUTH_CREDS_INVALID:

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/apiSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/apiSpec.js b/app/addons/replication/tests/apiSpec.js
new file mode 100644
index 0000000..ef8bc39
--- /dev/null
+++ b/app/addons/replication/tests/apiSpec.js
@@ -0,0 +1,170 @@
+// 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.
+import utils from '../../../../test/mocha/testUtils';
+import {
+  getSource,
+  getTarget,
+  continuous,
+  createTarget,
+  addDocIdAndRev,
+  getDocUrl
+} from '../api';
+import Constants from '../constants';
+
+const assert = utils.assert;
+
+describe('Replication API', () => {
+
+  describe('getSource', () => {
+
+    it('encodes remote db', () => {
+      const remoteSource = 'http://remote-couchdb.com/my/db/here';
+      const source = getSource({
+        replicationSource: Constants.REPLICATION_SOURCE.REMOTE,
+        remoteSource
+      });
+
+      assert.deepEqual(source, 'http://remote-couchdb.com/my%2Fdb%2Fhere');
+    });
+
+    it('returns local source with auth info and encoded', () => {
+      const localSource = 'my/db';
+
+      const source = getSource({
+        replicationSource: Constants.REPLICATION_SOURCE.LOCAL,
+        localSource,
+        username: 'the-user',
+        password: 'password'
+      });
+
+      assert.deepEqual(source.headers, {Authorization:"Basic dGhlLXVzZXI6cGFzc3dvcmQ="});
+      assert.ok(/my%2Fdb/.test(source.url));
+    });
+  });
+
+  describe('getTarget', () => {
+
+    it('returns remote encoded target', () => {
+      const remoteTarget = 'http://remote-couchdb.com/my/db';
+
+      assert.deepEqual("http://remote-couchdb.com/my%2Fdb", getTarget({
+        replicationTarget: Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE,
+        remoteTarget: remoteTarget
+      }));
+    });
+
+    it('returns existing local database', () => {
+      const target = getTarget({
+        replicationTarget: Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE,
+        localTarget: 'my-existing/db',
+        username: 'the-user',
+        password: 'password'
+      });
+
+      assert.deepEqual(target.headers, {Authorization:"Basic dGhlLXVzZXI6cGFzc3dvcmQ="});
+      assert.ok(/my-existing%2Fdb/.test(target.url));
+    });
+
+    it('returns new local database', () => {
+      const target = getTarget({
+        replicationTarget: Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE,
+        replicationSource: Constants.REPLICATION_SOURCE.LOCAL,
+        localTarget: 'my-new/db',
+        username: 'the-user',
+        password: 'password'
+      });
+
+      assert.deepEqual(target.headers, {Authorization:"Basic dGhlLXVzZXI6cGFzc3dvcmQ="});
+      assert.ok(/my-new%2Fdb/.test(target.url));
+    });
+
+    it('returns new local for remote source', () => {
+      const target = getTarget({
+        replicationTarget: Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE,
+        replicationSource: Constants.REPLICATION_SOURCE.REMOTE,
+        localTarget: 'my-new/db',
+        username: 'the-user',
+        password: 'password'
+      });
+
+      assert.ok(/the-user:password@/.test(target));
+      assert.ok(/my-new%2Fdb/.test(target));
+    });
+  });
+
+  describe('continuous', () => {
+
+    it('returns true for continuous', () => {
+      assert.ok(continuous(Constants.REPLICATION_TYPE.CONTINUOUS));
+    });
+
+    it('returns false for once', () => {
+      assert.notOk(continuous(Constants.REPLICATION_TYPE.ONE_TIME));
+    });
+  });
+
+  describe('create target', () => {
+
+    it('returns true for new local', () => {
+      assert.ok(createTarget(Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE));
+    });
+
+    it('returns true for new remote', () => {
+      assert.ok(createTarget(Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE));
+    });
+
+    it('returns false for existing', () => {
+      assert.notOk(createTarget(Constants.REPLICATION_TARGET.EXISTING_REMOTE_DATABASE));
+    });
+
+  });
+
+  describe('addDocId', () => {
+
+    it('adds doc id if it exists', () => {
+      const docId = 'docId';
+
+      assert.deepEqual(
+        addDocIdAndRev(docId, null,  {}), {
+          _id: docId
+        });
+    });
+
+    it('adds doc and Rev if it exists', () => {
+      const docId = 'docId';
+      const _rev = "1-rev123";
+
+      assert.deepEqual(
+        addDocIdAndRev(docId, _rev, {}), {
+          _id: docId,
+          _rev: _rev
+        });
+    });
+
+    it('does not add doc id if it does not exists', () => {
+      const docId = 'docId';
+
+      assert.deepEqual(
+        addDocIdAndRev(null, null, {}), {});
+    });
+  });
+
+  describe("getDocUrl", () => {
+    it("scrubs passwords and decodes", () => {
+      const url = "http://userone:theirpassword@couchdb-host.com/my%2Fdb%2fhere";
+      const cleanedUrl = "http://couchdb-host.com/my/db/here";
+
+      assert.deepEqual(getDocUrl(url), cleanedUrl);
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/helpersSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/helpersSpec.js b/app/addons/replication/tests/helpersSpec.js
new file mode 100644
index 0000000..5e2b1c3
--- /dev/null
+++ b/app/addons/replication/tests/helpersSpec.js
@@ -0,0 +1,41 @@
+// 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.
+
+import utils from "../../../../test/mocha/testUtils";
+import helpers from '../helpers';
+const assert = utils.assert;
+
+describe('Replication Helpers', () => {
+
+  describe('getDatabaseLabel', () => {
+
+    it('returns database name for string', () => {
+      const db = 'http://tester:testerpass@127.0.0.1/fancy/db/name';
+
+      const dbName = helpers.getDatabaseLabel(db);
+      assert.deepEqual('fancy/db/name', dbName);
+
+    });
+
+    it('returns database name for object', () => {
+      const db = {
+        url: 'http://tester:testerpass@127.0.0.1/fancy'
+      };
+
+      const dbName = helpers.getDatabaseLabel(db);
+      assert.deepEqual('fancy', dbName);
+
+    });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/newreplicationSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/newreplicationSpec.js b/app/addons/replication/tests/newreplicationSpec.js
new file mode 100644
index 0000000..81246b9
--- /dev/null
+++ b/app/addons/replication/tests/newreplicationSpec.js
@@ -0,0 +1,225 @@
+// 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.
+import React from "react";
+import TestUtils from "react-addons-test-utils";
+import utils from "../../../../test/mocha/testUtils";
+import FauxtonAPI from '../../../core/api';
+import { mount } from 'enzyme';
+import sinon from "sinon";
+import NewReplication from '../components/newreplication';
+import Constants from '../constants';
+
+const {assert, restore}  = utils;
+
+describe('New Replication Component', () => {
+
+  describe('validation', () => {
+
+    afterEach(() => {
+      restore(FauxtonAPI.addNotification);
+    });
+
+    it('returns true for local source and target selected', () => {
+      const newreplication = mount(<NewReplication
+        databases={[]}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        localTarget={"mydb"}
+        localSource={"anotherdb"}
+        updateFormField={() => {}}
+        />);
+
+      assert.ok(newreplication.instance().validate());
+    });
+
+    it('returns true for remote source and target selected', () => {
+      const newreplication = mount(<NewReplication
+        databases={[]}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE}
+        remoteTarget={"mydb"}
+        remoteSource={"anotherdb"}
+        updateFormField={() => {}}
+        />);
+
+      assert.ok(newreplication.instance().validate());
+    });
+
+    it("warns if new local database exists", () => {
+      const spy = sinon.spy(FauxtonAPI, 'addNotification');
+
+      const newreplication = mount(<NewReplication
+        databases={["existingdb"]}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        localTarget={"existingdb"}
+        localSource={"anotherdb"}
+        updateFormField={() => {}}
+        />);
+
+      newreplication.instance().validate();
+      assert.ok(spy.calledOnce);
+
+      const notification = spy.args[0][0];
+      assert.ok(/database already exists/.test(notification.msg));
+    });
+
+    it("warns if database name is wrong", () => {
+      const spy = sinon.spy(FauxtonAPI, 'addNotification');
+
+      const newreplication = mount(<NewReplication
+        databases={[]}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        localTarget={"existing db"}
+        localSource={"anotherdb"}
+        updateFormField={() => {}}
+        />);
+
+      newreplication.instance().validate();
+      assert.ok(spy.calledOnce);
+
+      const notification = spy.args[0][0];
+      assert.ok(/may not contain any spaces/.test(notification.msg));
+    });
+
+    it("warns if database is same for local", () => {
+      const spy = sinon.spy(FauxtonAPI, 'addNotification');
+
+      const newreplication = mount(<NewReplication
+        databases={[]}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        localTarget={"samedb"}
+        localSource={"samedb"}
+        updateFormField={() => {}}
+        />);
+
+      newreplication.instance().validate();
+      assert.ok(spy.calledOnce);
+
+      const notification = spy.args[0][0];
+      assert.ok(/Cannot replicate a database to itself/.test(notification.msg));
+    });
+
+    it("warns if database is same for remote", () => {
+      const spy = sinon.spy(FauxtonAPI, 'addNotification');
+
+      const newreplication = mount(<NewReplication
+        databases={[]}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        remoteTarget={"samedb"}
+        remoteSource={"samedb"}
+        updateFormField={() => {}}
+        />);
+
+      newreplication.instance().validate();
+      assert.ok(spy.calledOnce);
+
+      const notification = spy.args[0][0];
+      assert.ok(/Cannot replicate a database to itself/.test(notification.msg));
+    });
+  });
+
+  describe('confirmButtonEnabled', () => {
+    it('returns false for default', () => {
+      const newreplication = mount(<NewReplication
+        updateFormField={() => {}}
+        />);
+
+      assert.notOk(newreplication.instance().confirmButtonEnabled());
+    });
+
+    it('returns false for empty remote source', () => {
+      const newreplication = mount(<NewReplication
+        updateFormField={() => {}}
+        submittedNoChange={false}
+        replicationSource={Constants.REPLICATION_SOURCE.REMOTE}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        locallocalTargetKnown={false}
+        locallocalSourceKnown={false}
+        />);
+
+      assert.notOk(newreplication.instance().confirmButtonEnabled());
+    });
+
+    it('returns false for empty local source', () => {
+      const newreplication = mount(<NewReplication
+        updateFormField={() => {}}
+        submittedNoChange={false}
+        replicationSource={Constants.REPLICATION_SOURCE.REMOTE}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        locallocalTargetKnown={false}
+        locallocalSourceKnown={false}
+        remoteSource={'db'}
+        />);
+
+
+      assert.notOk(newreplication.instance().confirmButtonEnabled());
+    });
+
+    it("returns true for all details filled in", () => {
+      const newreplication = mount(<NewReplication
+        updateFormField={() => {}}
+        submittedNoChange={false}
+        replicationSource={Constants.REPLICATION_SOURCE.REMOTE}
+        remoteSource={'db'}
+        replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
+        localTarget={"new-db"}
+        locallocalTargetKnown={false}
+        locallocalSourceKnown={false}
+        />);
+
+
+      assert.ok(newreplication.instance().confirmButtonEnabled());
+    });
+
+  });
+
+  describe("runReplicationChecks", () => {
+
+    it("shows conflict modal for existing replication doc", () => {
+      let called = false;
+      const showConflictModal = () => { called = true;};
+      const checkReplicationDocID = () => {
+        const promise = FauxtonAPI.Deferred();
+        promise.resolve(true);
+        return promise;
+      };
+      const newreplication = mount(<NewReplication
+        updateFormField={() => {}}
+        replicationDocName="my-doc-id"
+        checkReplicationDocID={checkReplicationDocID}
+        showConflictModal={showConflictModal}
+        />);
+
+      newreplication.instance().runReplicationChecks();
+      assert.ok(called);
+    });
+
+    it("Shows password modal", () => {
+      let called = false;
+      const showPasswordModal = () => {called = true;};
+      const checkReplicationDocID = () => {
+        const promise = FauxtonAPI.Deferred();
+        promise.resolve(false);
+        return promise;
+      };
+      const newreplication = mount(<NewReplication
+        updateFormField={() => {}}
+        replicationDocName="my-doc-id"
+        checkReplicationDocID={checkReplicationDocID}
+        />);
+
+      newreplication.instance().showPasswordModal = showPasswordModal;
+      newreplication.instance().runReplicationChecks();
+      assert.ok(called);
+    });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/nightwatch/replication.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/nightwatch/replication.js b/app/addons/replication/tests/nightwatch/replication.js
index d70356a..1f879af 100644
--- a/app/addons/replication/tests/nightwatch/replication.js
+++ b/app/addons/replication/tests/nightwatch/replication.js
@@ -18,99 +18,96 @@ const newDatabaseName2 = 'fauxton-selenium-tests-replication2';
 const replicatedDBName = 'replicated-db';
 const docName1 = 'doc-name1';
 const docName2 = 'doc-name2';
-const pwd = 'testerpass';
-const longWaitTime = 120000;
 
-const destroyDBs = (client, done) => {
+const destroyDBsAndCreateReplicator = (client, done) => {
   var nano = helpers.getNanoInstance(client.globals.test_settings.db_url);
   nano.db.destroy(newDatabaseName1, () => {
     nano.db.destroy(newDatabaseName2, () => {
       nano.db.destroy(replicatedDBName, () => {
-        done();
+        nano.db.create('_replicator', function (err, body, header) {
+          done();
+        });
       });
     });
   });
 };
 
 module.exports = {
-  before: destroyDBs, // just in case the test failed on prev execution
-  after: destroyDBs,
+  before: destroyDBsAndCreateReplicator,
 
   'Replicates existing local db to new local db' : function (client) {
-    var waitTime = client.globals.maxWaitTime,
-        baseUrl = client.globals.test_settings.launch_url;
+    const waitTime = client.globals.maxWaitTime;
+    const baseUrl = client.globals.test_settings.launch_url;
+    const password = client.globals.test_settings.password;
 
     client
       .createDatabase(newDatabaseName1)
-      .checkForDatabaseCreated(newDatabaseName1, longWaitTime)
+      .checkForDatabaseCreated(newDatabaseName1, waitTime)
       .createDocument(docName1, newDatabaseName1)
       .loginToGUI()
-      .url(baseUrl + '/#replication')
+      .url(baseUrl + '/#replication/_create')
       .waitForElementPresent('button#replicate', waitTime, true)
       .waitForElementPresent('#replication-source', waitTime, true)
 
       // select LOCAL as the source
       .click('#replication-source')
-      .click('#replication-source option[value="REPLICATION_SOURCE_LOCAL"]')
-      .waitForElementPresent('.replication-source-name-row', waitTime, true)
+      .keys(['\uE006'])
+      .waitForElementPresent('.replication__input-react-select', waitTime, true)
 
       // enter our source DB
-      .setValue('.replication-source-name-row .Select-input input', [newDatabaseName1])
-      .keys(['\uE015', '\uE015', '\uE006'])
+      .setValue('.replication__input-react-select .Select-input input', [newDatabaseName1, client.Keys.ENTER])
 
       // enter a new target name
-      .click('#replication-target')
+      .waitForElementPresent('#replication-target', waitTime, true)
       .click('option[value="REPLICATION_TARGET_NEW_LOCAL_DATABASE"]')
-      .setValue('.new-local-db', replicatedDBName)
+      .setValue('.replication__new-input', replicatedDBName)
 
       .click('#replicate')
 
       .waitForElementPresent('.enter-password-modal', waitTime, true)
-      .setValue('.enter-password-modal input[type="password"]', pwd)
+      .setValue('.enter-password-modal input[type="password"]', password)
       .click('.enter-password-modal button.save')
       .waitForElementNotPresent('.enter-password-modal', waitTime, true)
+      .waitForElementNotPresent('.global-notification .fonticon-cancel', waitTime, false)
 
       // now check the database was created
-      .checkForDatabaseCreated(replicatedDBName, longWaitTime)
+      //.checkForDatabaseCreated(replicatedDBName, waitTime)
 
-      // lastly, check the doc was replicated as well
-      .checkForDocumentCreated(docName1, longWaitTime, replicatedDBName)
       .end();
   },
 
 
   'Replicates existing local db to existing local db' : function (client) {
-    var waitTime = client.globals.maxWaitTime,
-      baseUrl = client.globals.test_settings.launch_url;
+    const waitTime = client.globals.maxWaitTime;
+    const baseUrl = client.globals.test_settings.launch_url;
+    const password = client.globals.test_settings.password;
 
     client
 
       // create two databases, each with a single (different) doc
       .createDatabase(newDatabaseName1)
-      .checkForDatabaseCreated(newDatabaseName1, longWaitTime)
+      .checkForDatabaseCreated(newDatabaseName1, waitTime)
       .createDocument(docName1, newDatabaseName1)
       .createDatabase(newDatabaseName2)
-      .checkForDatabaseCreated(newDatabaseName2, longWaitTime)
+      .checkForDatabaseCreated(newDatabaseName2, waitTime)
       .createDocument(docName2, newDatabaseName2)
 
       // now login and fill in the replication form
       .loginToGUI()
-      .url(baseUrl + '/#replication')
+      .url(baseUrl + '/#replication/_create')
       .waitForElementPresent('button#replicate', waitTime, true)
       .waitForElementPresent('#replication-source', waitTime, true)
 
       // select the LOCAL db as the source
       .click('#replication-source')
-      .click('#replication-source option[value="REPLICATION_SOURCE_LOCAL"]')
-      .waitForElementPresent('.replication-source-name-row', waitTime, true)
-      .setValue('.replication-source-name-row .Select-input input', [newDatabaseName1])
-      .keys(['\uE015', '\uE015', '\uE006'])
+      .keys(['\uE006'])
+      .waitForElementPresent('.replication__input-react-select', waitTime, true)
+      .setValue('.replication__input-react-select .Select-input input', [newDatabaseName1, client.Keys.ENTER])
 
       // select existing local as the target
-      .click('#replication-target')
+      .waitForElementPresent('#replication-target', waitTime, true)
       .click('#replication-target option[value="REPLICATION_TARGET_EXISTING_LOCAL_DATABASE"]')
-      .setValue('.replication-target-name-row .Select-input input', [newDatabaseName2])
-      .keys(['\uE015', '\uE015', '\uE006'])
+      .setValue('#replication-target-local .Select-input input', [newDatabaseName2, client.Keys.ENTER])
 
       .getAttribute('#replicate', 'disabled', function (result) {
         // confirm it's not disabled
@@ -119,11 +116,63 @@ module.exports = {
       .click('#replicate')
 
       .waitForElementPresent('.enter-password-modal', waitTime, true)
-      .setValue('.enter-password-modal input[type="password"]', pwd)
+      .setValue('.enter-password-modal input[type="password"]', password)
       .click('.enter-password-modal button.save')
+      .end();
+  },
+
+  'Replicates using existing doc id' : function (client) {
+    const waitTime = client.globals.maxWaitTime;
+    const baseUrl = client.globals.test_settings.launch_url;
+    const password = client.globals.test_settings.password;
+
+    const replicatorDoc = {
+      _id: 'existing-doc-id',
+      source: "http://source-db.com",
+      target: "http://target-db.com"
+    };
+
+    client
+
+      // create two databases, each with a single (different) doc
+      .createDatabase(newDatabaseName1)
+      .checkForDatabaseCreated(newDatabaseName1, waitTime)
+      .createDocument(docName1, newDatabaseName1)
+      .createDatabase(newDatabaseName2)
+      .checkForDatabaseCreated(newDatabaseName2, waitTime)
+      .createDocument(docName2, newDatabaseName2)
+      .deleteDocument(replicatorDoc._id, '_replicator')
+      .createDocument(replicatorDoc._id, '_replicator', replicatorDoc)
 
-      // now check the target database contains the doc from the original db
-      .checkForDocumentCreated(docName1, longWaitTime, newDatabaseName2)
+      // now login and fill in the replication form
+      .loginToGUI()
+      .url(baseUrl + '/#replication/_create')
+      .waitForElementPresent('button#replicate', waitTime, true)
+      .waitForElementPresent('#replication-source', waitTime, true)
+
+      // select the LOCAL db as the source
+      .click('#replication-source')
+      .keys(['\uE006'])
+      .waitForElementPresent('.replication__input-react-select', waitTime, true)
+      .setValue('.replication__input-react-select .Select-input input', [newDatabaseName1, client.Keys.ENTER])
+
+      // select existing local as the target
+      .waitForElementPresent('#replication-target', waitTime, true)
+      .click('#replication-target option[value="REPLICATION_TARGET_EXISTING_LOCAL_DATABASE"]')
+      .setValue('#replication-target-local .Select-input input', [newDatabaseName2, client.Keys.ENTER])
+      .setValue('.replication__doc-name-input', [replicatorDoc._id, client.Keys.ENTER])
+
+      .getAttribute('#replicate', 'disabled', function (result) {
+        // confirm it's not disabled
+        this.assert.equal(result.value, null);
+      })
+      .click('#replicate')
+
+      .waitForElementPresent('.replication__error-doc-modal .replication__error-continue', waitTime, true)
+      .click('.replication__error-doc-modal .replication__error-continue')
+      .waitForElementPresent('.enter-password-modal', waitTime, true)
+      .setValue('.enter-password-modal input[type="password"]', password)
+      .click('.enter-password-modal button.save')
       .end();
   }
 };

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/nightwatch/replicationactivity.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/nightwatch/replicationactivity.js b/app/addons/replication/tests/nightwatch/replicationactivity.js
new file mode 100644
index 0000000..6cc0f27
--- /dev/null
+++ b/app/addons/replication/tests/nightwatch/replicationactivity.js
@@ -0,0 +1,50 @@
+// 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.
+
+
+module.exports = {
+
+  'Can delete document': (client) => {
+    const waitTime = client.globals.maxWaitTime;
+    const baseUrl = client.globals.test_settings.launch_url;
+    const password = client.globals.test_settings.password;
+
+    const replicatorDoc = {
+      _id: 'existing-doc-id-2',
+      source: "http://source-db.com",
+      target: "http://target-db.com"
+    };
+    client
+      .deleteDatabase('_replicator')
+      .createDatabase('_replicator')
+      .createDocument(replicatorDoc._id, '_replicator', replicatorDoc)
+      .loginToGUI()
+      .url(baseUrl + '/#replication')
+      .waitForElementNotPresent('.load-lines', waitTime, true)
+      .waitForElementPresent('.replication__filter', waitTime, true)
+      .click('a[title="Delete document existing-doc-id-2"]')
+      .waitForElementPresent('.replication_delete-doc-modal', waitTime, true)
+      .click('.replication_delete-doc-modal button.save')
+      .waitForElementNotPresent('.replication_delete-doc-modal', waitTime, true)
+      .waitForElementPresent('.global-notification .fonticon-cancel', waitTime, false)
+      .waitForElementNotPresent('.global-notification .fonticon-cancel', waitTime, false)
+      .waitForElementNotPresent('.load-lines', waitTime, true)
+      .clickWhenVisible('#notification-center-btn', waitTime, false)
+      .waitForElementPresent('.notification-list', waitTime, true)
+      .getText('.notification-list', function (result) {
+        console.log("fonticon text", result);
+      })
+      .waitForElementNotPresent('a[title="Delete document existing-doc-id-2"]', waitTime, true)
+      .assert.elementNotPresent('a[title="Delete document existing-doc-id-2"]')
+      .end();
+  }
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/replicationSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/replicationSpec.js b/app/addons/replication/tests/replicationSpec.js
deleted file mode 100644
index 4664c4e..0000000
--- a/app/addons/replication/tests/replicationSpec.js
+++ /dev/null
@@ -1,212 +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.
-import React from 'react';
-import ReactDOM from 'react-dom';
-import FauxtonAPI from '../../../core/api';
-import TestUtils from 'react-addons-test-utils';
-import utils from '../../../../test/mocha/testUtils';
-import Components from '../components.react';
-import Constants from '../constants';
-import Actions from '../actions';
-import ActionTypes from '../actiontypes';
-import Stores from '../stores';
-
-const assert = utils.assert;
-const store = Stores.replicationStore;
-
-
-const updateField = function (fieldName, value) {
-  FauxtonAPI.dispatch({
-    type: ActionTypes.REPLICATION_UPDATE_FORM_FIELD,
-    options: {
-      fieldName: fieldName,
-      value: value
-    }
-  });
-};
-
-
-describe('Replication', () => {
-
-  describe('ReplicationTargetRow', () => {
-    let el, container;
-
-    beforeEach(() => {
-      container = document.createElement('div');
-    });
-
-    afterEach(() => {
-      ReactDOM.unmountComponentAtNode(container);
-      store.reset();
-    });
-
-    it('new remote replication target shows a URL field', () => {
-      el = TestUtils.renderIntoDocument(
-        <Components.ReplicationTargetRow
-          remoteTarget="remotetarget"
-          replicationTarget={Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE}
-          databases={['one', 'two']}
-          targetDatabase=""
-        />,
-        container
-      );
-      assert.equal($(ReactDOM.findDOMNode(el)).find('input.connection-url').length, 1);
-    });
-
-    it('existing remote replication target also shows a URL field', () => {
-      el = TestUtils.renderIntoDocument(
-        <Components.ReplicationTargetRow
-          remoteTarget="remotetarget"
-          replicationTarget={Constants.REPLICATION_TARGET.EXISTING_REMOTE_DATABASE}
-          databases={['one', 'two']}
-          targetDatabase=""
-        />,
-        container
-      );
-      assert.equal($(ReactDOM.findDOMNode(el)).find('input.connection-url').length, 1);
-    });
-
-    it('new local database fields have simple textfield', () => {
-      el = TestUtils.renderIntoDocument(
-        <Components.ReplicationTargetRow
-          remoteTarget="remotetarget"
-          replicationTarget={Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE}
-          databases={['one', 'two']}
-          targetDatabase=""
-        />,
-        container
-      );
-      assert.equal($(ReactDOM.findDOMNode(el)).find('input.connection-url').length, 0);
-      assert.equal($(ReactDOM.findDOMNode(el)).find('input.new-local-db').length, 1);
-    });
-
-    it('existing local databases fields have typeahead field', () => {
-      el = TestUtils.renderIntoDocument(
-        <Components.ReplicationTargetRow
-          remoteTarget="remotetarget"
-          replicationTarget={Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE}
-          databases={['one', 'two']}
-          targetDatabase=""
-        />,
-        container
-      );
-      assert.equal($(ReactDOM.findDOMNode(el)).find('input.connection-url').length, 0);
-      assert.equal($(ReactDOM.findDOMNode(el)).find('input.new-local-db').length, 0);
-
-      // (the typeahead field has a search icon)
-      assert.equal($(ReactDOM.findDOMNode(el)).find('.Select--single').length, 1);
-    });
-
-  });
-
-
-  describe('ReplicationController', () => {
-
-    describe('Replicate button', () => {
-      let el, container;
-
-      beforeEach(() => {
-        container = document.createElement('div');
-      });
-
-      afterEach(() => {
-        ReactDOM.unmountComponentAtNode(container);
-        store.reset();
-      });
-
-      it('shows loading spinner until databases loaded', () => {
-        el = TestUtils.renderIntoDocument(<Components.ReplicationController/>, container);
-        Actions.initReplicator('sourcedb');
-        assert.ok($(ReactDOM.findDOMNode(el)).hasClass('loading-lines'));
-
-        FauxtonAPI.dispatch({
-          type: ActionTypes.REPLICATION_DATABASES_LOADED,
-          options: { databases: ['one', 'two', 'three'] }
-        });
-        assert.notOk($(ReactDOM.findDOMNode(el)).hasClass('loading-lines'));
-      });
-
-      it('disabled by default', () => {
-        el = TestUtils.renderIntoDocument(<Components.ReplicationController/>, container);
-        Actions.initReplicator('sourcedb');
-        FauxtonAPI.dispatch({
-          type: ActionTypes.REPLICATION_DATABASES_LOADED,
-          options: { databases: ['one', 'two', 'three'] }
-        });
-        assert.ok($(ReactDOM.findDOMNode(el)).find('#replicate').is(':disabled'));
-      });
-
-      it('enabled when all fields entered', () => {
-        el = TestUtils.renderIntoDocument(<Components.ReplicationController/>, container);
-        Actions.initReplicator('sourcedb');
-        FauxtonAPI.dispatch({
-          type: ActionTypes.REPLICATION_DATABASES_LOADED,
-          options: { databases: ['one', 'two', 'three'] }
-        });
-
-        updateField('replicationSource', Constants.REPLICATION_SOURCE.LOCAL);
-        updateField('sourceDatabase', 'one');
-        updateField('replicationTarget', Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE);
-        updateField('targetDatabase', 'two');
-
-        assert.notOk($(ReactDOM.findDOMNode(el)).find('#replicate').is(':disabled'));
-      });
-
-      it('disabled when missing replication source', () => {
-        el = TestUtils.renderIntoDocument(<Components.ReplicationController/>, container);
-        Actions.initReplicator('sourcedb');
-        FauxtonAPI.dispatch({
-          type: ActionTypes.REPLICATION_DATABASES_LOADED,
-          options: { databases: ['one', 'two', 'three'] }
-        });
-
-        updateField('replicationTarget', Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE);
-        updateField('targetDatabase', 'two');
-
-        assert.ok($(ReactDOM.findDOMNode(el)).find('#replicate').is(':disabled'));
-      });
-
-      it('disabled when source is local, but not in known list of dbs', () => {
-        el = TestUtils.renderIntoDocument(<Components.ReplicationController/>, container);
-        Actions.initReplicator('sourcedb');
-        FauxtonAPI.dispatch({
-          type: ActionTypes.REPLICATION_DATABASES_LOADED,
-          options: { databases: ['one', 'two', 'three'] }
-        });
-
-        updateField('replicationSource', Constants.REPLICATION_SOURCE.LOCAL);
-        updateField('sourceDatabase', 'unknown-source-db');
-        updateField('replicationTarget', Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE);
-        updateField('targetDatabase', 'two');
-
-        assert.ok($(ReactDOM.findDOMNode(el)).find('#replicate').is(':disabled'));
-      });
-
-      it('disabled when target is local, but not in known list of dbs', () => {
-        el = TestUtils.renderIntoDocument(<Components.ReplicationController/>, container);
-        Actions.initReplicator('sourcedb');
-        FauxtonAPI.dispatch({
-          type: ActionTypes.REPLICATION_DATABASES_LOADED,
-          options: { databases: ['one', 'two', 'three'] }
-        });
-
-        updateField('replicationSource', Constants.REPLICATION_SOURCE.LOCAL);
-        updateField('sourceDatabase', 'one');
-        updateField('replicationTarget', Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE);
-        updateField('targetDatabase', 'unknown-target-db');
-
-        assert.ok($(ReactDOM.findDOMNode(el)).find('#replicate').is(':disabled'));
-      });
-    });
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/app/addons/replication/tests/storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/tests/storesSpec.js b/app/addons/replication/tests/storesSpec.js
index 04be3df..bd5fa36 100644
--- a/app/addons/replication/tests/storesSpec.js
+++ b/app/addons/replication/tests/storesSpec.js
@@ -16,7 +16,7 @@ import Constants from '../constants';
 const assert = utils.assert;
 const store = Stores.replicationStore;
 
-describe('Databases Store', function () {
+describe('Replication Store', function () {
 
   afterEach(function () {
     store.reset();
@@ -31,9 +31,9 @@ describe('Databases Store', function () {
     store.updateFormField('remoteTarget', 'TARGET');
     assert.equal(store.getRemoteTarget(), 'TARGET');
 
-    assert.equal(store.getTargetDatabase(), '');
-    store.updateFormField('targetDatabase', 'db');
-    assert.equal(store.getTargetDatabase(), 'db');
+    assert.equal(store.getlocalTarget(), '');
+    store.updateFormField('localTarget', 'db');
+    assert.equal(store.getlocalTarget(), 'db');
 
     assert.equal(store.getReplicationType(), Constants.REPLICATION_TYPE.ONE_TIME);
     store.updateFormField('replicationType', Constants.REPLICATION_TYPE.CONTINUOUS);
@@ -51,9 +51,9 @@ describe('Databases Store', function () {
     store.updateFormField('replicationTarget', 'rtarget');
     assert.equal(store.getReplicationTarget(), 'rtarget');
 
-    assert.equal(store.getSourceDatabase(), '');
-    store.updateFormField('sourceDatabase', 'source-db');
-    assert.equal(store.getSourceDatabase(), 'source-db');
+    assert.equal(store.getlocalSource(), '');
+    store.updateFormField('localSource', 'source-db');
+    assert.equal(store.getlocalSource(), 'source-db');
   });
 
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/assets/less/fauxton.less b/assets/less/fauxton.less
index 9ef3bad..fbdf0db 100644
--- a/assets/less/fauxton.less
+++ b/assets/less/fauxton.less
@@ -432,6 +432,15 @@ table.databases {
   max-height: 291px;
   background-color: #333333;
 }
+.Select-arrow-zone > .Select-arrow {
+  border-top-color: #333;
+
+}
+
+.Select-arrow-zone:hover > .Select-arrow {
+  border-top-color: #E73D34;
+}
+
 
 //----footer--///
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/i18n.json.default.json
----------------------------------------------------------------------
diff --git a/i18n.json.default.json b/i18n.json.default.json
index 2773f3f..d1e4971 100644
--- a/i18n.json.default.json
+++ b/i18n.json.default.json
@@ -10,6 +10,8 @@
     "mango-indexeditor-title": "Mango",
     "couchdb-productname": "Apache CouchDB",
     "cors-disable-cors-prompt": "Are you sure? Disabling CORS will overwrite your specific origin domains.",
-    "cors-notice": "Cross-Origin Resource Sharing (CORS) lets you connect to remote servers directly from the browser, so you can host browser-based apps on static pages and talk directly with CouchDB to load your data."
+    "cors-notice": "Cross-Origin Resource Sharing (CORS) lets you connect to remote servers directly from the browser, so you can host browser-based apps on static pages and talk directly with CouchDB to load your data.",
+    "replication-password-modal-header": "Enter Account Password.",
+    "replication-password-modal-text": "Replication requires authentication on your credentials."
   }
 }

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index a7e44e4..80a0824 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,8 @@
     "nightwatch": "~0.9.0",
     "phantomjs-prebuilt": "^2.1.7",
     "react-addons-test-utils": "~15.0.1",
-    "sinon": "git+https://github.com/sinonjs/sinon.git"
+    "sinon": "git+https://github.com/sinonjs/sinon.git",
+    "url-polyfill": "github/url-polyfill"
   },
   "dependencies": {
     "async": "~0.2.6",

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/test/dev.js
----------------------------------------------------------------------
diff --git a/test/dev.js b/test/dev.js
index 874a35e..5fa7e15 100644
--- a/test/dev.js
+++ b/test/dev.js
@@ -13,7 +13,7 @@
 
 // This will search for files ending in .test.js and require them
 // so that they are added to the webpack bundle
-var context = require.context('../app/', true, /[Ss]pec/);
+var context = require.context('../app/addons/replication', true, /[Ss]pec/);
 console.log('Testing files', context.keys());
 context.keys().forEach(context);
 module.exports = context;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/test/mocha/testUtils.js
----------------------------------------------------------------------
diff --git a/test/mocha/testUtils.js b/test/mocha/testUtils.js
index 92ccc72..ae0202e 100644
--- a/test/mocha/testUtils.js
+++ b/test/mocha/testUtils.js
@@ -11,6 +11,7 @@
 // the License.
 
 import chai from "chai";
+import _ from 'lodash';
 
 var restore = function (fn) {
   if (fn.restore) {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/test/nightwatch_tests/custom-commands/helper.js
----------------------------------------------------------------------
diff --git a/test/nightwatch_tests/custom-commands/helper.js b/test/nightwatch_tests/custom-commands/helper.js
index 1a183ef..5b1718b 100644
--- a/test/nightwatch_tests/custom-commands/helper.js
+++ b/test/nightwatch_tests/custom-commands/helper.js
@@ -9,11 +9,8 @@ exports.checkForDocumentCreated = function checkForDocumentCreated (url, timeout
   const intervalId = setInterval(() => {
 
     request(url, (er, res, body) => {
-      console.log(url, res.statusCode);
-
       if (res && /^2..$/.test(res.statusCode)) {
         clearTimeout(timeOutId);
-        console.log('check for doc created successful');
         clearInterval(intervalId);
 
         cb(null);
@@ -28,18 +25,11 @@ exports.checkForDatabaseCreated = function checkForDatabaseCreated (couchUrl, da
   }, timeout);
 
   const intervalId = setInterval(() => {
-    const rand = Math.round(Math.random() * 10000000);
-    request(couchUrl + '/_all_dbs?cachebust=' + rand, function (er, res, body) {
+    request(couchUrl + '/_all_dbs', function (er, res, body) {
       if (body) {
-        console.log(couchUrl + '/_all_dbs?cachebust=' + rand);
-        console.log('list of databases:');
-        console.log(body);
-        console.log("_______________________");
-
         const reg = new RegExp('"' + databaseName + '"', 'g');
         if (reg.test(body)) {
           clearTimeout(timeOutId);
-          console.log('database created: ' + databaseName);
           clearInterval(intervalId);
           cb(null);
         }

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/test/test.config.underscore
----------------------------------------------------------------------
diff --git a/test/test.config.underscore b/test/test.config.underscore
index d1b4804..32a16a7 100644
--- a/test/test.config.underscore
+++ b/test/test.config.underscore
@@ -14,6 +14,7 @@
 //
 
 require([
+  "url-polyfill",
   <% _.each(testFiles, function (test) {%>
   '.././<%= test %>',
  <% }) %>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ff25441b/webpack.config.test.js
----------------------------------------------------------------------
diff --git a/webpack.config.test.js b/webpack.config.test.js
index eae1b2b..5a96b8c 100644
--- a/webpack.config.test.js
+++ b/webpack.config.test.js
@@ -31,16 +31,23 @@ module.exports = {
       //loader: 'react-hot!babel'
       loader: 'babel'
     },
-    { test: require.resolve("jquery"),
+    {
+      test: require.resolve("jquery"),
       loader: "expose?$!expose?jQuery"
-     },
-    { test: require.resolve("sinon"),
+    },
+    {
+      test: require.resolve("sinon"),
       loader: "expose?sinon"
-     },
-    { test: require.resolve("backbone"),
+    },
+    {
+      test: require.resolve("backbone"),
       loader: "expose?Backbone"
     },
     {
+      test: require.resolve("url-polyfill"),
+      loader: "imports?this=>window"
+    },
+    {
       test: require.resolve("react"),
       loader: "imports?shim=es5-shim/es5-shim&sham=es5-shim/es5-sham"
     },


Mime
View raw message