@@ -19,10 +15,5 @@ class HomeComponent extends React.Component{
}
}
-HomeComponent.contextTypes = {
- router: React.PropTypes.object.isRequired
-};
-
-export default HomeComponent;
-
+export default Home;
diff --git a/app/components/iClient/iClientComponent.js b/app/components/IClient/IClient.js
similarity index 65%
rename from app/components/iClient/iClientComponent.js
rename to app/components/IClient/IClient.js
index 16f1a07..d19fa49 100644
--- a/app/components/iClient/iClientComponent.js
+++ b/app/components/IClient/IClient.js
@@ -1,12 +1,12 @@
import React from 'react';
-import { Router, Route, IndexRoute, IndexLink, Link } from 'react-router'
-import LoginComponent from 'components/Login/LoginComponent';
+import LoginComponent from 'components/Login/Login';
import MenuComponent from 'components/Menu/Menu';
-class iClientComponent extends React.Component{
- constructor(props) {
- super(props);
+class IClient extends React.Component
+{
+ constructor(props, context) {
+ super(props, context);
this.handleView = this.handleView.bind(this);
}
@@ -20,10 +20,11 @@ class iClientComponent extends React.Component{
);
}
+
render() {
let view = this.handleView();
- if (!localStorage.token) {
+ if (!window.localStorage.getItem('token')) {
view =
;
}
return (
@@ -32,5 +33,5 @@ class iClientComponent extends React.Component{
}
}
-export default iClientComponent;
+export default IClient;
diff --git a/app/components/LinksApp/LinksApp.js b/app/components/LinksApp/LinksApp.js
new file mode 100644
index 0000000..2d0dc67
--- /dev/null
+++ b/app/components/LinksApp/LinksApp.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import { Link } from 'react-router'
+
+class LinksApp extends React.Component
+{
+ constructor(props, context) {
+ super(props, context);
+ this.generate = this.generate.bind(this);
+ this.handleClick = this.handleClick.bind(this);
+ this.state = {
+ links: this.props.links
+ };
+ }
+
+ handleClick(e) {
+ if (this.context.onClick) {
+ this.context.onClick();
+ }
+ }
+
+ generate() {
+ return this.state.links.map((link, index) => {
+ return (
+
+ {link[1]}
+
+ );
+ });
+ }
+
+ render() {
+ return (
+
+ {this.generate()}
+
+ );
+ }
+}
+
+LinksApp.propTypes = {
+ links: React.PropTypes.array.isRequired
+};
+
+LinksApp.contextTypes = {
+ onClick: React.PropTypes.func
+};
+
+export default LinksApp;
+
diff --git a/app/components/Login/LoginComponent.js b/app/components/Login/Login.js
similarity index 91%
rename from app/components/Login/LoginComponent.js
rename to app/components/Login/Login.js
index da73cc9..82783a3 100644
--- a/app/components/Login/LoginComponent.js
+++ b/app/components/Login/Login.js
@@ -4,12 +4,11 @@ import { Router } from 'react-router'
import User from 'services/User';
import ErrorComponent from 'components/Error/Error';
-class LoginComponent extends React.Component
+class Login extends React.Component
{
constructor(props, context) {
super(props, context);
this.handleSubmit = this.handleSubmit.bind(this);
- this.handleDeleteMessage = this.handleDeleteMessage.bind(this);
this.state = {
error: ''
};
@@ -23,7 +22,7 @@ class LoginComponent extends React.Component
this.refs.password.value
).then((response) => {
if (response.data.success == 200) {
- localStorage.token = response.data.token;
+ window.localStorage.setItem('token', response.data.token);
this.context.router.push("/");
}
}).catch((error) => {
@@ -34,10 +33,6 @@ class LoginComponent extends React.Component
});
}
- handleDeleteMessage() {
- this.setState({error: ''});
- }
-
render() {
return (
@@ -84,8 +79,9 @@ class LoginComponent extends React.Component
}
}
-LoginComponent.contextTypes = {
+Login.contextTypes = {
router: React.PropTypes.object.isRequired
};
-export default LoginComponent;
+export default Login;
+
diff --git a/app/components/Menu/Menu.js b/app/components/Menu/Menu.js
index 88b7d01..51edf30 100644
--- a/app/components/Menu/Menu.js
+++ b/app/components/Menu/Menu.js
@@ -1,47 +1,46 @@
import React from 'react';
-import { Router, Link } from 'react-router'
+import { Router } from 'react-router'
+
+import Nav from 'components/Nav/Nav'
+import LinksApp from 'components/LinksApp/LinksApp'
+
class Menu extends React.Component{
constructor(props, context) {
super(props, context);
this.handleLogout = this.handleLogout.bind(this);
this.handleView = this.handleView.bind(this);
+ this.state = {
+ links: [
+ [ '/', 'Home'],
+ ['/client', 'Client'],
+ ['/area', 'Area']
+ ]
+ };
}
handleLogout() {
- delete localStorage.token;
+ window.localStorage.removeItem('token');
this.context.router.push("/");
}
handleView() {
return (
-
+
);
}
render() {
let view = this.handleView();
- if (!localStorage.token) {
+ if (!window.localStorage.getItem('token')) {
view = ;
}
return (
diff --git a/app/components/Nav/Nav.js b/app/components/Nav/Nav.js
new file mode 100644
index 0000000..616512a
--- /dev/null
+++ b/app/components/Nav/Nav.js
@@ -0,0 +1,55 @@
+import React from 'react';
+
+class Nav extends React.Component
+{
+ constructor(props, context) {
+ super(props, context);
+ this.toggleNavStatus = this.toggleNavStatus.bind(this);
+ this.hide = this.hide.bind(this);
+ this.state = {
+ toggleNavStatus: ''
+ };
+ }
+
+ getChildContext() {
+ return {
+ onClick: this.toggleNavStatus
+ }
+ }
+
+ hide(e) {
+ this.setState({toggleNavStatus: ''});
+ }
+
+ toggleNavStatus() {
+ let isActive = this.state.toggleNavStatus ? '' : 'is-active';
+ this.setState({toggleNavStatus: isActive});
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+Nav.childContextTypes = {
+ onClick: React.PropTypes.func
+};
+
+export default Nav;
+
diff --git a/app/components/Visit/Visit.js b/app/components/Visit/Visit.js
new file mode 100644
index 0000000..79a9266
--- /dev/null
+++ b/app/components/Visit/Visit.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Link } from 'react-router'
+
+class Visit extends React.Component
+{
+ constructor(props, context) {
+ super(props, context);
+ this.generate = this.generate.bind(this);
+ this.state = {
+ visits: this.props.visits
+ };
+ }
+
+ generate() {
+ return this.state.visits.map((areaVisit, key) => (
+
+ { areaVisit.visit.client.name } |
+ { areaVisit.visit.visit_date } |
+
+
+
+
+ |
+
+ ));
+ }
+
+ render() {
+ return (
+
+
+
+ Name |
+ Last Visit |
+ |
+
+
+
+ {this.generate()}
+
+
+ );
+ }
+}
+
+Visit.propTypes = {
+ visits: React.PropTypes.array.isRequired
+};
+
+export default Visit;
+
diff --git a/package.json b/package.json
index e5c9beb..68810a7 100644
--- a/package.json
+++ b/package.json
@@ -13,21 +13,39 @@
},
"devDependencies": {
"babel-core": "^6.17.0",
+ "babel-jest": "^16.0.0",
"babel-loader": "^6.2.5",
- "babel-preset-es2015": "^6.16.0",
+ "babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
+ "codecov": "^1.0.1",
"css-loader": "^0.25.0",
+ "enzyme": "^2.6.0",
+ "file-loader": "^0.9.0",
+ "font-awesome-webpack": "0.0.4",
"http-server": "^0.9.0",
+ "jest": "^16.0.2",
+ "less": "^2.7.1",
+ "less-loader": "^2.2.3",
"npm-run-all": "^3.1.0",
"path": "^0.12.7",
+ "react-addons-test-utils": "^15.3.2",
+ "react-test-renderer": "^15.3.2",
"style-loader": "^0.13.1",
+ "url-loader": "^0.5.7",
"webpack": "^1.13.2"
},
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
+ "test": "jest",
"webpack": "webpack -w",
"serve": "http-server -o -c 1 -p 80",
- "dev": "npm-run-all --parallel webpack serve"
+ "dev": "npm-run-all --parallel webpack serve",
+ "coverage": "jest --coverage"
+ },
+ "jest": {
+ "modulePaths": [
+ "app"
+ ],
+ "collectCoverage": true
},
"author": "",
"license": "ISC"
diff --git a/public/index.html b/public/index.html
index aeec7b8..64cd577 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,12 +2,11 @@
+
React Client
-
-
-
+
diff --git a/tests/Area.test.js b/tests/Area.test.js
new file mode 100644
index 0000000..4c7cbb0
--- /dev/null
+++ b/tests/Area.test.js
@@ -0,0 +1,148 @@
+jest.enableAutomock();
+jest.dontMock('components/Area/Area');
+jest.dontMock('components/Error/Error');
+jest.dontMock('react');
+jest.dontMock('axios');
+jest.dontMock('../tests/__mocks__/AreasResponseMock');
+jest.dontMock('enzyme');
+
+
+describe('Test Area', () => {
+ const React = require('react');
+ const Enzyme = require('enzyme');
+ const shallow = Enzyme.shallow;
+ let Visit = require('services/Visit').default;
+
+ it('Area should show error message', (done) => {
+
+ let error = {response:{data:{error:"Not Found"}}};
+ let promises = [];
+ let Area;
+ let component;
+
+ promises.push(
+ (() => {
+ Visit.getGroupByArea = jest.genMockFunction().mockImplementation(() => {
+ return new Promise((resolve, reject) => {
+ throw error;
+ });
+ })
+ })()
+ );
+
+ promises.push(
+ (() => {
+ Area = require('components/Area/Area').default;
+ })()
+ );
+
+ promises.push(
+ (() => {
+ component = shallow(
+
+ );
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(component.state('error')).toEqual('Not Found');
+
+ let errorComponent = shallow(
+ component.instance().generateError(error.response.data.error)
+ );
+
+ expect(errorComponent.find('div').text()).toEqual('Not Found');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+ it('Area should show nothing if no data', (done) => {
+
+ let error = {data:{error:"Not Found"}};
+ let promises = [];
+ let Area;
+ let component;
+
+ promises.push(
+ (() => {
+ Visit.getGroupByArea = jest.genMockFunction().mockImplementation(() => {
+ return new Promise((resolve, reject) => {
+ throw error;
+ });
+ })
+ })()
+ );
+
+ promises.push(
+ (() => {
+ Area = require('components/Area/Area').default;
+ })()
+ );
+
+ promises.push(
+ (() => {
+ component = shallow(
+
+ );
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(component.state('error')).toEqual('');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+ it('Area should show mocked data', (done) => {
+
+ let response = {data:require('AreasResponseMock').default};
+ let promises = [];
+ let Area;
+ let component;
+
+ promises.push(
+ (() => {
+ Visit.getGroupByArea = jest.genMockFunction().mockImplementation(() => {
+ return new Promise((resolve, reject) => {
+ resolve(response);
+ });
+ })
+ })()
+ );
+
+ promises.push(
+ (() => {
+ Area = require('components/Area/Area').default;
+ })()
+ );
+
+ promises.push(
+ (() => {
+ component = shallow(
+
+ );
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(
+ shallow(
+ component.state().areas[0]
+ ).find('.title').at(0).text()
+ ).toEqual('Center');
+ expect(
+ shallow(
+ component.state().areas[1]
+ ).find('.title').at(0).text()
+ ).toEqual('South');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+});
diff --git a/tests/Client.test.js b/tests/Client.test.js
new file mode 100644
index 0000000..c2731f3
--- /dev/null
+++ b/tests/Client.test.js
@@ -0,0 +1,144 @@
+jest.enableAutomock();
+jest.dontMock('components/Client/Client');
+jest.dontMock('components/Error/Error');
+jest.dontMock('react');
+jest.dontMock('axios');
+jest.dontMock('enzyme');
+
+
+describe('Test Client', () => {
+ const React = require('react');
+ const Enzyme = require('enzyme');
+ const shallow = Enzyme.shallow;
+ let ClientService = require('services/Client').default;
+
+ it('Client should show error message', (done) => {
+
+ let error = {response:{data:{error:"Client Not Found"}}};
+ let promises = [];
+ let Client;
+ let component;
+
+ promises.push(
+ (() => {
+ ClientService.getClients = jest.genMockFunction().mockImplementation(() => {
+ return new Promise((resolve, reject) => {
+ throw error;
+ });
+ })
+ })()
+ );
+
+ promises.push(
+ (() => {
+ Client = require('components/Client/Client').default;
+ })()
+ );
+
+ promises.push(
+ (() => {
+ component = shallow(
+
+ );
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ component.update();
+ expect(component.render().text()).toEqual('Client Not Found');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+ it('Client should show default error message', (done) => {
+
+ let error = {error:"Client Not Found"};
+ let promises = [];
+ let Client;
+ let component;
+
+ promises.push(
+ (() => {
+ ClientService.getClients = jest.genMockFunction().mockImplementation(() => {
+ return new Promise((resolve, reject) => {
+ throw error;
+ });
+ })
+ })()
+ );
+
+ promises.push(
+ (() => {
+ Client = require('components/Client/Client').default;
+ })()
+ );
+
+ promises.push(
+ (() => {
+ component = shallow(
+
+ );
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ component.update();
+ expect(component.render().text()).toEqual('Error Found: Trying get client');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+ it('Client should show mocked data', (done) => {
+
+ let response = {
+ data: {
+ clients: [
+ {name: 'Jon Snow', address: '7 Street', city: 'Winterfell'}
+ ]
+ }
+ };
+ let promises = [];
+ let Client;
+ let component;
+
+ promises.push(
+ (() => {
+ ClientService.getClients = jest.genMockFunction().mockImplementation(() => {
+ return new Promise((resolve, reject) => {
+ resolve(response);
+ });
+ })
+ })()
+ );
+
+ promises.push(
+ (() => {
+ Client = require('components/Client/Client').default;
+ })()
+ );
+
+ promises.push(
+ (() => {
+ component = shallow(
+
+ );
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ component.update();
+ expect(component.find('td').at(0).text()).toEqual('Jon Snow');
+ expect(component.find('td').at(1).text()).toEqual('7 Street');
+ expect(component.find('td').at(2).text()).toEqual('Winterfell');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+});
+
diff --git a/tests/Error.test.js b/tests/Error.test.js
new file mode 100644
index 0000000..336d924
--- /dev/null
+++ b/tests/Error.test.js
@@ -0,0 +1,21 @@
+import React from 'react'
+import {shallow} from 'enzyme';
+
+import Error from 'components/Error/Error'
+
+test('Error should show null if no error', () => {
+ const errorComponent = shallow(
+
+ );
+
+ expect(errorComponent.text()).toEqual('');
+});
+
+test('Error should show error if error pass', () => {
+ const errorMessage = 'Error found';
+ const errorComponent = shallow(
+
+ );
+
+ expect(errorComponent.text()).toEqual('Error found');
+});
diff --git a/tests/Home.test.js b/tests/Home.test.js
new file mode 100644
index 0000000..cec81e6
--- /dev/null
+++ b/tests/Home.test.js
@@ -0,0 +1,17 @@
+describe('Test Home', () => {
+ const React = require('react');
+ const Enzyme = require('enzyme');
+ const shallow = Enzyme.shallow;
+
+ test('Home should show welcome message', () => {
+
+ let Home = require('components/Home/Home').default;
+
+ const component = shallow(
+
+ );
+
+ expect(component.find('h1').text()).toEqual('Welcome to IClient');
+ });
+});
+
diff --git a/tests/IClient.test.js b/tests/IClient.test.js
new file mode 100644
index 0000000..9d4bd10
--- /dev/null
+++ b/tests/IClient.test.js
@@ -0,0 +1,37 @@
+
+describe('Test iClient', () => {
+ require('../tests/__mocks__/LocalStorageMock');
+
+ const React = require('react');
+ const enzyme = require('enzyme');
+ const shallow = enzyme.shallow;
+ const mount = enzyme.mount;
+ const IClient = require('components/IClient/IClient').default;
+
+ it('iClient should show login component if not logged', (done) => {
+
+ let component = shallow(
+
+ );
+
+ expect(component.text()).toEqual('');
+
+ done();
+ });
+
+ it('iClient should show login component if not logged', (done) => {
+
+ window.localStorage.setItem('token', 'test_menu');
+
+ let component = shallow(
+
+ Test
+
+ );
+
+ expect(component.text()).toEqual('Test');
+
+ done();
+ });
+});
+
diff --git a/tests/LinksApp.test.js b/tests/LinksApp.test.js
new file mode 100644
index 0000000..6d10e90
--- /dev/null
+++ b/tests/LinksApp.test.js
@@ -0,0 +1,55 @@
+describe('Test LinksApp', () => {
+ const React = require('react');
+ const Enzyme = require('enzyme');
+ const shallow = Enzyme.shallow;
+
+ beforeEach((done) => {
+ done();
+ });
+
+ test('LinksApp should show links received from props', () => {
+
+ let LinksApp = require('components/LinksApp/LinksApp').default;
+ let links = [
+ ['/route', 'description'],
+ ['/anotherRoute', 'Another Description']
+ ];
+ const context = { onClick: () => 'foo' };
+
+ const component = shallow(
+ ,
+ { context }
+ );
+
+ component.instance().handleClick(null);
+
+ let firstLink = shallow(
+ component.instance().generate()[0]
+ );
+
+ let secondLink = shallow(
+ component.instance().generate()[1]
+ );
+
+ expect(firstLink.text()).toEqual('description');
+ expect(firstLink.instance().props.to).toEqual('/route');
+
+ expect(secondLink.text()).toEqual('Another Description');
+ expect(secondLink.instance().props.to).toEqual('/anotherRoute');
+ });
+
+ test('LinksApp should show nothing', () => {
+
+ let LinksApp = require('components/LinksApp/LinksApp').default;
+ let links = [];
+
+ const component = shallow(
+
+ );
+
+ component.instance().handleClick(null);
+
+ expect(component.instance().generate()).toEqual([]);
+ });
+});
+
diff --git a/tests/Login.test.js b/tests/Login.test.js
new file mode 100644
index 0000000..5c8a705
--- /dev/null
+++ b/tests/Login.test.js
@@ -0,0 +1,238 @@
+jest.enableAutomock();
+jest.dontMock('components/Login/Login');
+jest.dontMock('components/Error/Error');
+jest.dontMock('react');
+jest.dontMock('axios');
+jest.dontMock('enzyme');
+
+describe('Test Login', () => {
+ require('../tests/__mocks__/LocalStorageMock');
+
+ const React = require('react');
+ const enzyme = require('enzyme');
+ const shallow = enzyme.shallow;
+ const mount = enzyme.mount;
+ const Login = require('components/Login/Login').default;
+ const context = {
+ router: {
+ push: (arg) => arg
+ }
+ };
+ let User = require('services/User').default;
+
+ it('Login should show form', (done) => {
+
+ let component = shallow(
+ ,
+ { context }
+ );
+
+ expect(component.find('form').length).toEqual(1);
+
+ done();
+ });
+
+ it('Login should get login values', (done) => {
+
+ let Login = require('components/Login/Login').default;
+ let loginInformed;
+ let passwordInformed;
+
+ User.login = jest.genMockFunction().mockImplementation((login, password) => {
+ return new Promise((resolve, reject) => {
+ loginInformed = login;
+ passwordInformed = password;
+ });
+ })
+
+ let component = mount(
+ ,
+ { context }
+ );
+
+ expect(loginInformed).toEqual(undefined);
+ expect(passwordInformed).toEqual(undefined);
+
+ let inputLogin = component.find('form div p input[type="text"]');
+ let inputPassword = component.find('form div p input[type="password"]');
+
+ inputLogin.node.value = 'Astolfo';
+ inputLogin.simulate('change', inputLogin);
+
+ inputPassword.node.value = 'abcd';
+ inputPassword.simulate('change', inputPassword);
+
+ component.find('form').simulate('submit', { target: component.find('form').get(0) });
+
+ expect(loginInformed).toEqual('Astolfo');
+ expect(passwordInformed).toEqual('abcd');
+
+ done();
+ });
+
+ it('Login should not set localStorage if success is different from 200', (done) => {
+
+ let Login = require('components/Login/Login').default;
+ let promises = [];
+
+ User.login = jest.genMockFunction().mockImplementation((login, password) => {
+ return new Promise((resolve, reject) => {
+ resolve({data: {success: 201, token: 'token_test'}});
+ });
+ })
+
+ let component = mount(
+ ,
+ { context }
+ );
+
+ let inputLogin = component.find('form div p input[type="text"]');
+ let inputPassword = component.find('form div p input[type="password"]');
+
+ inputLogin.node.value = 'Astolfo';
+ inputLogin.simulate('change', inputLogin);
+
+ inputPassword.node.value = 'abcd';
+ inputPassword.simulate('change', inputPassword);
+
+ expect(window.localStorage.getItem('token')).toEqual(null);
+
+ promises.push(
+ (() => {
+ component.find('form').simulate('submit', { target: component.find('form').get(0) });
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(window.localStorage.getItem('token')).toEqual(null);
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+
+ it('Login should set localStorage', (done) => {
+
+ let Login = require('components/Login/Login').default;
+ let promises = [];
+
+ User.login = jest.genMockFunction().mockImplementation((login, password) => {
+ return new Promise((resolve, reject) => {
+ resolve({data: {success: 200, token: 'token_test'}});
+ });
+ })
+
+ let component = mount(
+ ,
+ { context }
+ );
+
+ let inputLogin = component.find('form div p input[type="text"]');
+ let inputPassword = component.find('form div p input[type="password"]');
+
+ inputLogin.node.value = 'Astolfo';
+ inputLogin.simulate('change', inputLogin);
+
+ inputPassword.node.value = 'abcd';
+ inputPassword.simulate('change', inputPassword);
+
+ expect(window.localStorage.getItem('token')).toEqual(null);
+
+ promises.push(
+ (() => {
+ component.find('form').simulate('submit', { target: component.find('form').get(0) });
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(window.localStorage.getItem('token')).toEqual('token_test');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+ it('Login should show error default on state', (done) => {
+
+ let Login = require('components/Login/Login').default;
+ let error = {response:{error:"Another error"}};
+ let promises = [];
+
+ User.login = jest.genMockFunction().mockImplementation((login, password) => {
+ return new Promise((resolve, reject) => {
+ throw error;
+ });
+ })
+
+ let component = mount(
+ ,
+ { context }
+ );
+
+ let inputLogin = component.find('form div p input[type="text"]');
+ let inputPassword = component.find('form div p input[type="password"]');
+
+ inputLogin.node.value = 'Astolfo';
+ inputLogin.simulate('change', inputLogin);
+
+ inputPassword.node.value = 'abcd';
+ inputPassword.simulate('change', inputPassword);
+
+ promises.push(
+ (() => {
+ component.find('form').simulate('submit', { target: component.find('form').get(0) });
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(component.state().error).toEqual('Authentication failed');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+
+ it('Login should set error received on state', (done) => {
+
+ let Login = require('components/Login/Login').default;
+ let error = {response:{data:{error:"User Not Found"}}};
+ let promises = [];
+
+ User.login = jest.genMockFunction().mockImplementation((login, password) => {
+ return new Promise((resolve, reject) => {
+ throw error;
+ });
+ })
+
+ let component = mount(
+ ,
+ { context }
+ );
+
+ let inputLogin = component.find('form div p input[type="text"]');
+ let inputPassword = component.find('form div p input[type="password"]');
+
+ inputLogin.node.value = 'Astolfo';
+ inputLogin.simulate('change', inputLogin);
+
+ inputPassword.node.value = 'abcd';
+ inputPassword.simulate('change', inputPassword);
+
+ promises.push(
+ (() => {
+ component.find('form').simulate('submit', { target: component.find('form').get(0) });
+ })()
+ );
+
+ Promise.all(promises).then(() => {
+ expect(component.state().error).toEqual('User Not Found');
+ done();
+ }).catch((error) => {
+ console.log(error);
+ });
+ });
+
+});
+
diff --git a/tests/Menu.test.js b/tests/Menu.test.js
new file mode 100644
index 0000000..68fe50b
--- /dev/null
+++ b/tests/Menu.test.js
@@ -0,0 +1,63 @@
+
+describe('Test Menu', () => {
+ require('../tests/__mocks__/LocalStorageMock');
+
+ const React = require('react');
+ const shallow = require('enzyme').shallow;
+ const Menu = require('components/Menu/Menu').default;
+ const context = {
+ router: {
+ push: () => null
+ }
+ };
+
+ it('Menu should to know list of links', (done) => {
+
+ let component = shallow(
+ ,
+ { context }
+ );
+
+ let expectedLinks = [
+ ['/', 'Home'],
+ ['/client', 'Client'],
+ ['/area', 'Area'],
+ ];
+
+ expect(component.state().links).toEqual(expectedLinks);
+
+ done();
+ });
+
+ it('HandleLogout should erase token in localStorage', (done) => {
+
+ let component = shallow(
+ ,
+ { context }
+ );
+
+ window.localStorage.setItem('token', 'test_menu');
+
+ expect(window.localStorage.getItem('token')).toEqual('test_menu');
+ component.instance().handleLogout();
+ expect(window.localStorage.getItem('token')).toEqual(null);
+ done();
+ });
+
+ it('Menu should render links if logged in', (done) => {
+ window.localStorage.setItem('token', 'test_menu');
+
+ let component = shallow(
+ ,
+ { context }
+ );
+
+ expect(window.localStorage.getItem('token')).toEqual('test_menu');
+
+ expect(component.childAt(0).text()).toEqual('');
+ expect(component.childAt(0).render().find('.nav-item.is-tab').length).toEqual(3);
+ expect(component.childAt(1).text()).toEqual('Logout');
+ done();
+ });
+});
+
diff --git a/tests/Nav.test.js b/tests/Nav.test.js
new file mode 100644
index 0000000..f113044
--- /dev/null
+++ b/tests/Nav.test.js
@@ -0,0 +1,61 @@
+
+describe('Test Nav', () => {
+ const React = require('react');
+ const shallow = require('enzyme').shallow;
+ const Nav = require('components/Nav/Nav').default;
+
+ it('Nav should render child', (done) => {
+
+ let component = shallow(
+
+ );
+
+ expect(component.find('directive').text()).toEqual('Child');
+
+ done();
+ });
+
+ it('toggleNavStatus function should change toggleNavStatus state from is-active to empty', (done) => {
+
+ let component = shallow(
+
+ );
+
+ component.instance().setState({ toggleNavStatus: 'is-active' });
+ expect(component.state().toggleNavStatus).toEqual('is-active');
+ component.instance().toggleNavStatus();
+ expect(component.state().toggleNavStatus).toEqual('');
+
+ done();
+ });
+
+ it('toggleNavStatus function should change toggleNavStatus state from empty to is-active', (done) => {
+
+ let component = shallow(
+
+ );
+
+ expect(component.state().toggleNavStatus).toEqual('');
+ component.instance().toggleNavStatus();
+ expect(component.state().toggleNavStatus).toEqual('is-active');
+
+ done();
+ });
+
+ it('hide should change toggleNavStatus state to empty', (done) => {
+
+ let component = shallow(
+
+ );
+
+ component.instance().setState({ toggleNavStatus: 'whatever'} );
+ expect(component.state().toggleNavStatus).toEqual('whatever');
+ component.instance().hide(null);
+ expect(component.state().toggleNavStatus).toEqual('');
+
+ done();
+ });
+});
+
diff --git a/tests/Visit.test.js b/tests/Visit.test.js
new file mode 100644
index 0000000..8d9bdd7
--- /dev/null
+++ b/tests/Visit.test.js
@@ -0,0 +1,65 @@
+
+describe('Test Visit', () => {
+ require('../tests/__mocks__/LocalStorageMock');
+
+ const React = require('react');
+ const shallow = require('enzyme').shallow;
+ const Visit = require('components/Visit/Visit').default;
+
+ it('Visit should empty table if no data', (done) => {
+
+ let component = shallow(
+
+ );
+
+
+ expect(component.find('th').length).toEqual(3);
+ expect(component.find('th').at(0).text()).toEqual('Name');
+ expect(component.find('th').at(1).text()).toEqual('Last Visit');
+ expect(component.find('th').at(2).text()).toEqual('');
+
+ expect(component.find('tbody').length).toEqual(1);
+ expect(component.find('tbody').children().length).toEqual(0);
+ done();
+ });
+
+ it('Visit should table with visits data', (done) => {
+
+ let visits = [
+ {
+ visit: {
+ client:{
+ name: 'Jon Snow'
+ },
+ visit_date: '2016-10-19T13:30:13.329Z',
+ _id: '000000000000000000000001'
+ }
+ },
+ {
+ visit: {
+ client:{
+ name: 'Cotter Pyke'
+ },
+ visit_date: '2016-10-18T13:31:14.430Z',
+ _id: '580615d5d992eb00738fab57'
+ }
+ },
+ ];
+ let component = shallow(
+
+ );
+
+ expect(component.find('tbody').children().length).toEqual(2);
+
+ expect(component.find('td').length).toEqual(6);
+ expect(component.find('td').at(0).text()).toEqual('Jon Snow');
+ expect(component.find('td').at(1).text()).toEqual('2016-10-19T13:30:13.329Z');
+ expect(component.find('td').at(2).text()).toEqual('');
+
+ expect(component.find('td').at(3).text()).toEqual('Cotter Pyke');
+ expect(component.find('td').at(4).text()).toEqual('2016-10-18T13:31:14.430Z');
+ expect(component.find('td').at(5).text()).toEqual('');
+ done();
+ });
+});
+
diff --git a/tests/__mocks__/AreasResponseMock.js b/tests/__mocks__/AreasResponseMock.js
new file mode 100644
index 0000000..7d1f673
--- /dev/null
+++ b/tests/__mocks__/AreasResponseMock.js
@@ -0,0 +1,118 @@
+const AreasResponseMock = {
+ "visits": [
+ {
+ "_id":"Center",
+ "visits": [
+ {
+ "_id":"Rodrigues",
+ "visit": {
+ "_id":"000000000000000000000001",
+ "visit_date":"2016-10-19T13:30:13.329Z",
+ "sales_quantity":100,
+ "value_received":250,
+ "client": {
+ "__v":0,
+ "updatedAt":"2016-10-18T12:30:13.337Z",
+ "createdAt":"2016-10-18T12:30:13.337Z",
+ "name":"Rodrigues",
+ "address":"Street 23",
+ "city":"London",
+ "area": {
+ "parents":"Center",
+ "_id":"Center"
+ },
+ "frequency":10,
+ "ability":200,
+ "_id":"580615d5d992eb00738fab54"
+ },
+ "user": {
+ "__v":0,
+ "updatedAt":"2016-10-18T12:30:13.340Z",
+ "createdAt":"2016-10-18T12:30:13.340Z",
+ "username":"Gabriel",
+ "email":"gabriel@teste.com",
+ "password":"12345678",
+ "_id":"580615d5d992eb00738fab55"
+ },
+ "__v":0
+ }
+ },
+ {
+ "_id":"Gabriel",
+ "visit": {
+ "_id":"580615d5d992eb00738fab58",
+ "visit_date":"2016-10-19T13:30:13.329Z",
+ "sales_quantity":100,
+ "value_received":250,
+ "client": {
+ "__v":0,
+ "updatedAt":"2016-10-18T12:30:13.333Z",
+ "createdAt":"2016-10-18T12:30:13.333Z",
+ "name":"Gabriel",
+ "address":"Street 23",
+ "city":"London",
+ "area": {
+ "parents":"Center",
+ "_id":"Center"
+ },
+ "frequency":10,
+ "ability":200,
+ "_id":"580615d5d992eb00738fab52"
+ },
+ "user": {
+ "__v":0,
+ "updatedAt":"2016-10-18T12:30:13.340Z",
+ "createdAt":"2016-10-18T12:30:13.340Z",
+ "username":"Gabriel",
+ "email":"gabriel@teste.com",
+ "password":"12345678",
+ "_id":"580615d5d992eb00738fab55"
+ },
+ "__v":0
+ }
+ }
+ ]
+ },
+ {
+ "_id":"South",
+ "visits": [
+ {
+ "_id":"Gonçalves",
+ "visit": {
+ "_id":"580615d5d992eb00738fab57",
+ "visit_date":"2016-10-18T12:30:13.329Z",
+ "sales_quantity":100,
+ "value_received":250,
+ "client": {
+ "__v":0,
+ "updatedAt":"2016-10-18T12:30:13.335Z",
+ "createdAt":"2016-10-18T12:30:13.335Z",
+ "name":"Gonçalves",
+ "address":"Street 32",
+ "city":"London",
+ "area": {
+ "parents":"Center",
+ "_id":"South"
+ },
+ "frequency":20,
+ "ability":200,
+ "_id":"580615d5d992eb00738fab53"
+ },
+ "user": {
+ "__v":0,
+ "updatedAt":"2016-10-18T12:30:13.340Z",
+ "createdAt":"2016-10-18T12:30:13.340Z",
+ "username":"Gabriel",
+ "email":"gabriel@teste.com",
+ "password":"12345678",
+ "_id":"580615d5d992eb00738fab55"
+ },
+ "__v":0
+ }
+ }
+ ]
+ }
+ ]
+};
+
+export default AreasResponseMock;
diff --git a/tests/__mocks__/LocalStorageMock.js b/tests/__mocks__/LocalStorageMock.js
new file mode 100644
index 0000000..e437090
--- /dev/null
+++ b/tests/__mocks__/LocalStorageMock.js
@@ -0,0 +1,26 @@
+function storageMock() {
+ var storage = {};
+
+ return {
+ setItem: function(key, value) {
+ storage[key] = value || '';
+ },
+ getItem: function(key) {
+ return storage[key] || null;
+ },
+ removeItem: function(key) {
+ delete storage[key];
+ },
+ getLength() {
+ return Object.keys(storage).length;
+ },
+ key: function(i) {
+ var keys = Object.keys(storage);
+ return keys[i] || null;
+ }
+ };
+}
+
+window.localStorage = storageMock();
+
+export default storageMock;
diff --git a/webpack.config.js b/webpack.config.js
index 895cbfd..022ddc6 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,13 +1,22 @@
const path = require('path');
+var webpack = require('webpack');
+
module.exports = {
entry: "./app/App.js",
output: {
- filename: "public/dist/bundle.js"
+ path: 'public/dist/',
+ publicPath: '/dist/',
+ filename: "bundle.min.js",
},
module: {
loaders: [
+ //Bulma loader
{ test: /\.css$/, loader: "style-loader!css-loader" },
+ //Font-awesome loader
+ { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
+ { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" },
+ //Jsx loader
{
test: /\.js$/,
exclude: /node_modules/,
@@ -18,6 +27,11 @@ module.exports = {
}
]
},
+ plugins: [
+ new webpack.optimize.UglifyJsPlugin({
+ compress: { warnings: false }
+ })
+ ],
resolve: {
extensions: ['', '.js', '.jsx'],
root: [