Skip to content

Commit

Permalink
Improve UX and workflow, write tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkvon committed Dec 2, 2018
1 parent 30d669f commit 0aed9f3
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 15 deletions.
33 changes: 33 additions & 0 deletions modules/references/client/components/Info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';

/**
* @TODO make these elements nicer
*/

export function Self() {
return (<div>Self</div>);
}

export function Loading() {
return (<div>Loading</div>);
}

export function Duplicate() {
return (<div>Duplicate</div>);
}

export function Submitted({ isReported, isPublic }) {
return (
<div>
<div>Submitted</div>
{(isReported) ? <div>Reported</div> : null}
<div>{(isPublic) ? '' : 'not '}public</div>
</div>
);
}

Submitted.propTypes = {
isReported: PropTypes.boolean,
isPublic: PropTypes.boolean
};
4 changes: 2 additions & 2 deletions modules/references/client/components/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
</button>
);
Expand All @@ -46,14 +46,14 @@ export default function Navigation(props) {
{/* <!-- For the last tab -->*/}
{(props.tab === props.tabs - 1) ? submit : null}
</div>

);
}

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
Expand Down
45 changes: 43 additions & 2 deletions modules/references/client/components/ReferencesNew.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand All @@ -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
Expand Down Expand Up @@ -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() {
Expand All @@ -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 <Self />;

if (this.state.isLoading) return <Loading />;

if (this.state.isDuplicate) return <Duplicate />;

if (this.state.isSubmitted) {
const isReported = this.state.reference.recommend === 'no' && this.state.report;
const isPublic = this.state.isPublic;
return <Submitted isReported={isReported} isPublic={isPublic} />;
}

return (
<div>
<nav><span>How do you know them</span> &gt; <span>Recommendation</span></nav>
Expand All @@ -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()}
Expand All @@ -119,5 +159,6 @@ export default class ReferencesNew extends React.Component {
}

ReferencesNew.propTypes = {
userFrom: PropTypes.object,
userTo: PropTypes.object
};
9 changes: 8 additions & 1 deletion modules/references/client/components/references.api.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@ Enzyme.configure({ adapter: new Adapter() });
});

describe('Navigation through 3 tabs', () => {
xit('test', () => {
const wrapper = shallow(<Navigation tab={1} tabDone={0} tabs={3} onBack={() => {}} />);
console.log(wrapper);
console.log(<Navigation tab={0} />);
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(<Navigation tab={0} tabs={3} />);

Expand Down
Original file line number Diff line number Diff line change
@@ -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('<ReferencesNew />', () => {
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(<ReferencesNew userFrom={me} userTo={me} />);
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(<ReferencesNew userFrom={userFrom} userTo={userTo} />);
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(<ReferencesNew userFrom={userFrom} userTo={userTo} />);

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(<ReferencesNew userFrom={userFrom} userTo={userTo} />);
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(<ReferencesNew userFrom={userFrom} userTo={userTo} />);

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(<ReferencesNew userFrom={userFrom} userTo={userTo} />);

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(<ReferencesNew userFrom={userFrom} userTo={userTo} />);

wrapper.setState({
isLoading: false,
isSubmitted: true
});

wrapper.update();

expect(wrapper.find(Submitted)).toExist();
});
});
}());
2 changes: 1 addition & 1 deletion modules/users/client/config/users.client.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@
}).
state('profile.references.new', {
url: '/new',
template: '<references-new userTo="profileCtrl.profile"></references-new>',
template: '<references-new userTo="profileCtrl.profile" userFrom="app.user"></references-new>',
requiresAuth: true,
noScrollingTop: true,
data: {
Expand Down

0 comments on commit 0aed9f3

Please sign in to comment.