Skip to content

Commit 1464806

Browse files
committed
add ProjectForge render test and authentication actions test
1 parent bdfbaad commit 1464806

7 files changed

Lines changed: 386 additions & 17 deletions

File tree

projectforge-webapp/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"bootstrap": "^4.3.1",
1010
"classnames": "^2.2.6",
1111
"eslint-config-airbnb": "^17.1.0",
12+
"fetch-mock": "^7.3.1",
1213
"node-sass": "^4.11.0",
1314
"prop-types": "^15.7.2",
1415
"react": "^16.8.3",
@@ -19,6 +20,7 @@
1920
"react-scripts": "2.1.5",
2021
"reactstrap": "^7.1.0",
2122
"redux": "^4.0.1",
23+
"redux-mock-store": "^1.5.3",
2224
"redux-thunk": "^2.3.0"
2325
},
2426
"scripts": {
@@ -30,6 +32,13 @@
3032
"eslintConfig": {
3133
"extends": "react-app"
3234
},
35+
"jest": {
36+
"collectCoverageFrom": [
37+
"src/**/*.{js,jsx}",
38+
"!src/index.js",
39+
"!src/serviceWorker.js"
40+
]
41+
},
3342
"browserslist": [
3443
">0.2%",
3544
"not dead",

projectforge-webapp/src/actions/authentication.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,26 @@ export const USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE';
1313

1414
export const USER_LOGOUT = 'USER_LOGOUT';
1515

16-
const userLoginBegin = () => ({
16+
export const userLoginBegin = () => ({
1717
type: USER_LOGIN_BEGIN,
1818
});
1919

20-
const userLoginSuccess = (userId, authenticationToken) => ({
20+
export const userLoginSuccess = (userId, authenticationToken) => ({
2121
type: USER_LOGIN_SUCCESS,
2222
payload: {
2323
userId,
2424
authenticationToken,
2525
},
2626
});
2727

28-
const userLoginFailure = error => ({
28+
export const userLoginFailure = error => ({
2929
type: USER_LOGIN_FAILURE,
3030
payload: {
3131
error,
3232
},
3333
});
3434

35-
const userLogout = () => ({
35+
export const userLogout = () => ({
3636
type: USER_LOGOUT,
3737
});
3838

@@ -47,7 +47,7 @@ const removeCookies = () => {
4747
const catchError = dispatch => (error) => {
4848
removeCookies();
4949

50-
dispatch(userLoginFailure(error));
50+
dispatch(userLoginFailure(error.message));
5151
};
5252

5353
export const login = (username, password, keepSignedIn) => (dispatch) => {
@@ -85,20 +85,19 @@ export const loadSessionIfAvailable = () => (dispatch) => {
8585
const token = cookies.load(TOKEN_COOKIE);
8686

8787
if (!userID || !token) {
88-
return;
88+
return null;
8989
}
9090

9191
dispatch(userLoginBegin());
9292

93-
fetch(
93+
return fetch(
9494
getServiceURL('authenticate/initialContact'),
9595
{
9696
method: 'GET',
9797
headers: getAuthenticationHeaders(userID, token),
9898
},
9999
)
100100
.then(handleHTTPErrors)
101-
.then(response => response.json())
102101
.then(() => dispatch(userLoginSuccess(userID, token)))
103102
.catch(catchError(dispatch));
104103
};
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
import fetchMock from 'fetch-mock/es5/client';
2+
import cookies from 'react-cookies';
3+
import configureMockStore from 'redux-mock-store';
4+
import thunk from 'redux-thunk';
5+
import {
6+
loadSessionIfAvailable,
7+
login,
8+
logout,
9+
USER_LOGIN_BEGIN,
10+
USER_LOGIN_FAILURE,
11+
USER_LOGIN_SUCCESS,
12+
USER_LOGOUT,
13+
userLoginBegin,
14+
userLoginFailure,
15+
userLoginSuccess,
16+
userLogout,
17+
} from './authentication';
18+
19+
describe('login', () => {
20+
const username = 'demo';
21+
const password = 'demo123';
22+
const token = 'ABCDEF';
23+
const userId = 123;
24+
25+
Object.freeze(username);
26+
Object.freeze(password);
27+
Object.freeze(token);
28+
Object.freeze(userId);
29+
30+
const mockStore = configureMockStore([thunk]);
31+
32+
afterEach(() => fetchMock.restore());
33+
34+
it('should create an action to start the login', () => {
35+
const expectedAction = {
36+
type: USER_LOGIN_BEGIN,
37+
};
38+
39+
expect(userLoginBegin())
40+
.toEqual(expectedAction);
41+
});
42+
43+
it('should create an action to mark the login as success', () => {
44+
const expectedAction = {
45+
type: USER_LOGIN_SUCCESS,
46+
payload: {
47+
userId,
48+
authenticationToken: token,
49+
},
50+
};
51+
52+
expect(userLoginSuccess(userId, token))
53+
.toEqual(expectedAction);
54+
});
55+
56+
it('should create an action to mark the login as success', () => {
57+
const expectedAction = {
58+
type: USER_LOGIN_FAILURE,
59+
payload: {
60+
error: 'Some uncool error message',
61+
},
62+
};
63+
64+
expect(userLoginFailure('Some uncool error message'))
65+
.toEqual(expectedAction);
66+
});
67+
68+
it('creates USER_LOGIN_SUCCESS when fetching login has been done without keepSignedIn', () => {
69+
fetchMock
70+
.getOnce('/rest/authenticate/getToken', {
71+
status: 200,
72+
body: {
73+
id: userId,
74+
authenticationToken: token,
75+
},
76+
}, {
77+
headers: {
78+
'Authentication-Username': username,
79+
'Authentication-Password': password,
80+
},
81+
})
82+
.catch(() => {
83+
throw new Error('mock failed');
84+
});
85+
86+
const expectedActions = [
87+
{ type: USER_LOGIN_BEGIN },
88+
{
89+
type: USER_LOGIN_SUCCESS,
90+
payload: {
91+
userId,
92+
authenticationToken: token,
93+
},
94+
},
95+
];
96+
97+
const store = mockStore({});
98+
99+
return store.dispatch(login(username, password, false))
100+
.then(() => {
101+
expect(store.getActions())
102+
.toEqual(expectedActions);
103+
104+
expect(cookies.loadAll())
105+
.toEqual({});
106+
});
107+
});
108+
109+
it('creates USER_LOGIN_SUCCESS when fetching login has been done with keepSignedIn', () => {
110+
fetchMock
111+
.getOnce('/rest/authenticate/getToken', {
112+
status: 200,
113+
body: {
114+
id: userId,
115+
authenticationToken: token,
116+
},
117+
}, {
118+
headers: {
119+
'Authentication-Username': username,
120+
'Authentication-Password': password,
121+
},
122+
})
123+
.catch(() => {
124+
throw new Error('mock failed');
125+
});
126+
127+
const expectedActions = [
128+
{ type: USER_LOGIN_BEGIN },
129+
{
130+
type: USER_LOGIN_SUCCESS,
131+
payload: {
132+
userId,
133+
authenticationToken: token,
134+
},
135+
},
136+
];
137+
138+
const store = mockStore({});
139+
140+
return store.dispatch(login(username, password, true))
141+
.then(() => {
142+
expect(store.getActions())
143+
.toEqual(expectedActions);
144+
145+
expect(cookies.loadAll())
146+
.toEqual({
147+
TOKEN: token,
148+
USER_ID: userId,
149+
});
150+
});
151+
});
152+
153+
it('creates USER_LOGIN_FAILURE when fetching login has been failed', () => {
154+
fetchMock
155+
.getOnce('/rest/authenticate/getToken', 401)
156+
.catch(() => {
157+
throw new Error('mock failed');
158+
});
159+
160+
const expectedActions = [
161+
{ type: USER_LOGIN_BEGIN },
162+
{
163+
type: USER_LOGIN_FAILURE,
164+
payload: {
165+
error: 'Unauthorized',
166+
},
167+
},
168+
];
169+
170+
const store = mockStore({});
171+
172+
return store.dispatch(login(username, password, false))
173+
.then(() => {
174+
expect(store.getActions())
175+
.toEqual(expectedActions);
176+
});
177+
});
178+
});
179+
180+
describe('logout', () => {
181+
const mockStore = configureMockStore([thunk]);
182+
183+
it('should create USER_LOGOUT action', () => {
184+
const expectedAction = {
185+
type: USER_LOGOUT,
186+
};
187+
188+
expect(userLogout())
189+
.toEqual(expectedAction);
190+
});
191+
192+
it('creates USER_LOGOUT during logout', () => {
193+
const expectedActions = [
194+
{ type: USER_LOGOUT },
195+
];
196+
197+
const store = mockStore({});
198+
199+
cookies.save('TOKEN', 'ABCDEF');
200+
201+
store.dispatch(logout());
202+
203+
expect(store.getActions())
204+
.toEqual(expectedActions);
205+
206+
expect(cookies.loadAll())
207+
.toEqual({});
208+
});
209+
});
210+
211+
describe('check session', () => {
212+
const userId = 123;
213+
const token = 'ABCDEF';
214+
215+
Object.freeze(userId);
216+
Object.freeze(token);
217+
218+
const mockStore = configureMockStore([thunk]);
219+
220+
afterEach(() => fetchMock.restore());
221+
222+
it('creates no action at all', () => {
223+
const store = mockStore({});
224+
225+
expect(store.dispatch(loadSessionIfAvailable()))
226+
.toEqual(null);
227+
expect(store.getActions())
228+
.toEqual([]);
229+
});
230+
231+
it('creates USER_LOGIN_SUCCESS', () => {
232+
fetchMock
233+
.getOnce('/rest/authenticate/initialContact', 200, {
234+
headers: {
235+
'Authentication-User-Id': userId,
236+
'Authentication-Token': token,
237+
},
238+
})
239+
.catch(() => {
240+
throw new Error('mock failed');
241+
});
242+
243+
const expectedActions = [
244+
{ type: USER_LOGIN_BEGIN },
245+
{
246+
type: USER_LOGIN_SUCCESS,
247+
payload: {
248+
userId,
249+
authenticationToken: token,
250+
},
251+
},
252+
];
253+
254+
cookies.save('TOKEN', token);
255+
cookies.save('USER_ID', userId);
256+
257+
const store = mockStore({});
258+
259+
return store.dispatch(loadSessionIfAvailable())
260+
.then(() => {
261+
expect(store.getActions())
262+
.toEqual(expectedActions);
263+
264+
expect(cookies.loadAll())
265+
.toEqual({
266+
TOKEN: token,
267+
USER_ID: userId,
268+
});
269+
});
270+
});
271+
});

projectforge-webapp/src/containers/ProjectForge.jsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import React, { Component } from 'react';
2-
import { connect } from 'react-redux';
31
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import { connect } from 'react-redux';
44
import { BrowserRouter as Router } from 'react-router-dom';
5-
import TopBar from '../components/base/topbar';
6-
import Footer from '../components/base/footer';
7-
import LoginView from '../components/authentication/LoginView';
85
import { loadSessionIfAvailable, loginUser, logoutUser } from '../actions';
6+
import LoginView from '../components/authentication/LoginView';
7+
import Footer from '../components/base/footer';
98
import Navigation from '../components/base/navigation';
10-
import style from './ProjectForge.module.scss';
9+
import TopBar from '../components/base/topbar';
1110
import EditPage from './page/edit';
11+
import style from './ProjectForge.module.scss';
1212

13-
class ProjectForge extends Component {
13+
class ProjectForge extends React.Component {
1414
componentDidMount() {
1515
const { loadSessionIfAvailable: loadSession } = this.props;
1616

0 commit comments

Comments
 (0)