diff --git a/modules/references/client/components/Info.js b/modules/references/client/components/Info.js
new file mode 100644
index 0000000000..37a5e23277
--- /dev/null
+++ b/modules/references/client/components/Info.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+/**
+ * @TODO make these elements nicer
+ */
+
+export function Self() {
+ return (
Self
);
+}
+
+export function Loading() {
+ return (Loading
);
+}
+
+export function Duplicate() {
+ return (Duplicate
);
+}
+
+export function Submitted({ isReported, isPublic }) {
+ return (
+
+
Submitted
+ {(isReported) ?
Reported
: null}
+
{(isPublic) ? '' : 'not '}public
+
+ );
+}
+
+Submitted.propTypes = {
+ isReported: PropTypes.boolean,
+ isPublic: PropTypes.boolean
+};
diff --git a/modules/references/client/components/Navigation.js b/modules/references/client/components/Navigation.js
index 09936cbc55..061a81b1d2 100644
--- a/modules/references/client/components/Navigation.js
+++ b/modules/references/client/components/Navigation.js
@@ -34,7 +34,7 @@ export default function Navigation(props) {
className="btn btn-action btn-primary"
aria-label="Submit reference"
onClick={props.onSubmit}
- disabled={props.tabDone < props.tabs - 1}>
+ disabled={props.tabDone < props.tabs - 1 || props.disabled}>
Submit
);
@@ -46,7 +46,6 @@ export default function Navigation(props) {
{/* */}
{(props.tab === props.tabs - 1) ? submit : null}
-
);
}
@@ -54,6 +53,7 @@ Navigation.propTypes = {
onBack: PropTypes.func,
onNext: PropTypes.func,
onSubmit: PropTypes.func,
+ disabled: PropTypes.boolean,
tab: PropTypes.number, // current tab index - indexed from 0
tabs: PropTypes.number, // amount of tabs to display
tabDone: PropTypes.number // which tab is already filled
diff --git a/modules/references/client/components/ReferencesNew.component.js b/modules/references/client/components/ReferencesNew.component.js
index 4b9044981b..f096ba8178 100644
--- a/modules/references/client/components/ReferencesNew.component.js
+++ b/modules/references/client/components/ReferencesNew.component.js
@@ -4,6 +4,7 @@ import * as references from './references.api';
import Navigation from './Navigation';
import Interaction from './Interaction';
import Recommend from './Recommend';
+import { Self, Loading, Duplicate, Submitted } from './Info';
const api = { references };
@@ -22,10 +23,26 @@ export default class ReferencesNew extends React.Component {
recommend: null
},
report: false,
- reportMessage: ''
+ reportMessage: '',
+ isSelf: props.userFrom._id === props.userTo._id,
+ isLoading: true,
+ isSubmitting: false,
+ isDuplicate: false,
+ isSubmitted: false,
+ isPublic: false
};
}
+ async componentDidMount() {
+ const reference = await api.references.read({ userFrom: this.props.userFrom._id, userTo: this.props.userTo._id });
+
+ const newState = { isLoading: false };
+
+ if (reference.length === 1) newState.isDuplicate = true;
+
+ this.setState(newState);
+ }
+
handleTabSwitch(move) {
this.setState({
tab: this.state.tab + move
@@ -74,11 +91,21 @@ export default class ReferencesNew extends React.Component {
reportMessage: this.state.reportMessage
};
- await api.references.create({ ...data.reference, userTo: this.props.userTo._id });
+ this.setState({
+ isSubmitting: true
+ });
+
+ const savedReference = await api.references.create({ ...data.reference, userTo: this.props.userTo._id });
if (data.reference.recommend === 'no' && data.report) {
await api.references.report(this.props.userTo, data.reportMessage);
}
+
+ this.setState({
+ isSubmitting: false,
+ isSubmitted: true,
+ isPublic: savedReference.public
+ });
}
render() {
@@ -100,6 +127,18 @@ export default class ReferencesNew extends React.Component {
const tabDone = (recommend) ? 1 :
(hostedMe || hostedThem || met) ? 0 : -1;
+ if (this.state.isSelf) return ;
+
+ if (this.state.isLoading) return ;
+
+ if (this.state.isDuplicate) return ;
+
+ if (this.state.isSubmitted) {
+ const isReported = this.state.reference.recommend === 'no' && this.state.report;
+ const isPublic = this.state.isPublic;
+ return ;
+ }
+
return (
@@ -109,6 +148,7 @@ export default class ReferencesNew extends React.Component {
tab={this.state.tab}
tabDone={tabDone}
tabs={tabs.length}
+ disabled={this.state.isSubmitting}
onBack={() => this.handleTabSwitch(-1)}
onNext={() => this.handleTabSwitch(+1)}
onSubmit={() => this.handleSubmit()}
@@ -119,5 +159,6 @@ export default class ReferencesNew extends React.Component {
}
ReferencesNew.propTypes = {
+ userFrom: PropTypes.object,
userTo: PropTypes.object
};
diff --git a/modules/references/client/components/references.api.js b/modules/references/client/components/references.api.js
index 7330ad4389..84d7fb0499 100644
--- a/modules/references/client/components/references.api.js
+++ b/modules/references/client/components/references.api.js
@@ -1,11 +1,18 @@
export async function create(reference) {
- await fetch('/api/references', {
+ const response = await fetch('/api/references', {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify(reference) // eslint-disable-line angular/json-functions
});
+
+ return await response.json();
+}
+
+export async function read({ userFrom, userTo }) {
+ const response = await fetch(`/api/references?userFrom=${userFrom}&userTo=${userTo}`);
+ return await response.json();
}
export async function report(user, message) {
diff --git a/modules/references/tests/client/Navigation.client.component.tests.js b/modules/references/tests/client/Navigation.client.component.tests.js
index 05e57b9b9e..597b608718 100644
--- a/modules/references/tests/client/Navigation.client.component.tests.js
+++ b/modules/references/tests/client/Navigation.client.component.tests.js
@@ -22,15 +22,6 @@ Enzyme.configure({ adapter: new Adapter() });
});
describe('Navigation through 3 tabs', () => {
- xit('test', () => {
- const wrapper = shallow( {}} />);
- console.log(wrapper);
- console.log();
- console.log(wrapper.find('button'), wrapper.exists(), wrapper.name());
- expect(wrapper.find('button')).toBeDefined();
- expect(wrapper.props().tab).toBe(0);
- });
-
it('when tab is 0, there is no Back button and there is Next button', () => {
const wrapper = shallow();
diff --git a/modules/references/tests/client/ReferencesNew.client.component.tests.js b/modules/references/tests/client/ReferencesNew.client.component.tests.js
index e69de29bb2..d2524ebbbf 100644
--- a/modules/references/tests/client/ReferencesNew.client.component.tests.js
+++ b/modules/references/tests/client/ReferencesNew.client.component.tests.js
@@ -0,0 +1,192 @@
+'use strict';
+
+import ReferencesNew from '../../client/components/ReferencesNew.component';
+import { Self, Loading, Duplicate, Submitted } from '../../client/components/Info';
+import Interaction from '../../client/components/Interaction';
+import Navigation from '../../client/components/Navigation';
+import Enzyme from 'enzyme';
+import { shallow } from 'enzyme';
+import React from 'react';
+import sinon from 'sinon';
+import Adapter from 'enzyme-adapter-react-16';
+import jasmineEnzyme from 'jasmine-enzyme';
+import * as api from '../../client/components/references.api';
+
+Enzyme.configure({ adapter: new Adapter() });
+
+/**
+ * This is a first React test suite with enzyme.
+ * The enzyme configuration can be moved elsewhere (before all tests ever).
+ */
+
+(function () {
+
+ describe('', () => {
+ beforeEach(() => {
+ jasmineEnzyme();
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it('should not be possible to leave a reference to self', () => {
+ const me = {
+ _id: '123456',
+ username: 'username'
+ };
+
+ const wrapper = shallow();
+ expect(wrapper.find(Self)).toExist();
+ });
+
+ it('check whether the reference exists at the beginning', () => {
+ const userFrom = { _id: '111111', username: 'userfrom' };
+ const userTo = { _id: '222222', username: 'userto' };
+ const stub = sinon.stub(api, 'read');
+ stub.withArgs({ userFrom: userFrom._id, userTo: userTo._id }).returns(new Promise(() => {}));
+
+ expect(stub.callCount).toBe(0);
+ const wrapper = shallow();
+ expect(stub.callCount).toBe(1);
+ expect(wrapper.find(Loading)).toExist();
+ });
+
+ it('can not leave a second reference', async () => {
+ const userFrom = { _id: '111111', username: 'userfrom' };
+ const userTo = { _id: '222222', username: 'userto' };
+ const stub = sinon.stub(api, 'read');
+ stub.withArgs({ userFrom: userFrom._id, userTo: userTo._id }).resolves([{
+ userFrom, userTo, public: false
+ }]);
+
+ const wrapper = shallow();
+
+ expect(wrapper.find(Duplicate)).not.toExist();
+ await null;
+ expect(wrapper.find(Duplicate)).toExist();
+ expect(wrapper.find(Interaction)).not.toExist();
+ });
+
+ it('can leave a reference (reference form is available)', async () => {
+ const userFrom = { _id: '111111', username: 'userfrom' };
+ const userTo = { _id: '222222', username: 'userto' };
+ const stub = sinon.stub(api, 'read');
+ stub.withArgs({ userFrom: userFrom._id, userTo: userTo._id }).resolves([]);
+
+ const wrapper = shallow();
+ expect(wrapper.find(Interaction)).not.toExist();
+ await null;
+ expect(wrapper.find(Interaction)).toExist();
+ });
+
+ it('submit a reference', async () => {
+ const userFrom = { _id: '111111', username: 'userfrom' };
+ const userTo = { _id: '222222', username: 'userto' };
+ const spyCreate = sinon.spy(api, 'create');
+ const stubRead = sinon.stub(api, 'read');
+ stubRead.withArgs({ userFrom: userFrom._id, userTo: userTo._id }).resolves([]);
+
+ const wrapper = shallow();
+
+ await null;
+
+ expect(spyCreate.callCount).toBe(0);
+ wrapper.setState({
+ reference: {
+ interactions: {
+ met: false,
+ hostedMe: true,
+ hostedThem: false
+ },
+ recommend: 'yes'
+ }
+ });
+
+ const nav = wrapper.find(Navigation);
+ nav.props().onSubmit();
+
+ expect(spyCreate.callCount).toBe(1);
+
+ expect(spyCreate.calledOnceWith({
+ interactions: {
+ met: false,
+ hostedMe: true,
+ hostedThem: false
+ },
+ recommend: 'yes',
+ userTo: userTo._id
+ })).toBe(true);
+ });
+
+ it('submit a report when recommend is no and user wants to send a report', async () => {
+ const userFrom = { _id: '111111', username: 'userfrom' };
+ const userTo = { _id: '222222', username: 'userto' };
+ const spyReport = sinon.spy(api, 'report');
+ const stubRead = sinon.stub(api, 'read');
+ const stubCreate = sinon.stub(api, 'create');
+ stubRead.withArgs({ userFrom: userFrom._id, userTo: userTo._id }).resolves([]);
+ stubCreate.resolves();
+
+ const wrapper = shallow();
+
+ await null;
+
+ expect(spyReport.callCount).toBe(0);
+ wrapper.setState({
+ reference: {
+ interactions: {
+ met: false,
+ hostedMe: true,
+ hostedThem: false
+ },
+ recommend: 'unknown'
+ },
+ report: true,
+ reportMessage: 'asdf'
+ });
+
+ const nav = wrapper.find(Navigation);
+
+ nav.props().onSubmit();
+
+ await null;
+
+ expect(spyReport.callCount).toBe(0);
+
+ wrapper.setState({
+ reference: {
+ interactions: {
+ met: true,
+ hostedMe: true,
+ hostedThem: true
+ },
+ recommend: 'no'
+ }
+ });
+
+ nav.props().onSubmit();
+
+ await null;
+
+ expect(spyReport.callCount).toBe(1);
+ expect(spyReport.calledOnceWith(userTo, 'asdf')).toBe(true);
+ });
+
+ it('give the information that the reference was submitted', async () => {
+ const userFrom = { _id: '111111', username: 'userfrom' };
+ const userTo = { _id: '222222', username: 'userto' };
+
+ const wrapper = shallow();
+
+ wrapper.setState({
+ isLoading: false,
+ isSubmitted: true
+ });
+
+ wrapper.update();
+
+ expect(wrapper.find(Submitted)).toExist();
+ });
+ });
+}());
diff --git a/modules/users/client/config/users.client.routes.js b/modules/users/client/config/users.client.routes.js
index 7d32a7eb45..9c6cd3e870 100644
--- a/modules/users/client/config/users.client.routes.js
+++ b/modules/users/client/config/users.client.routes.js
@@ -246,7 +246,7 @@
}).
state('profile.references.new', {
url: '/new',
- template: '',
+ template: '',
requiresAuth: true,
noScrollingTop: true,
data: {