diff --git a/scm-ui/src/types/Action.js b/scm-ui/src/types/Action.js new file mode 100644 index 0000000000..6ec4fad55b --- /dev/null +++ b/scm-ui/src/types/Action.js @@ -0,0 +1,5 @@ +// @flow +export type Action = { + type: string, + payload?: any +}; diff --git a/scm-ui/src/users/modules/users.js b/scm-ui/src/users/modules/users.js index 64a89dfacb..7fbb4c33c5 100644 --- a/scm-ui/src/users/modules/users.js +++ b/scm-ui/src/users/modules/users.js @@ -3,6 +3,7 @@ import { apiClient } from "../../apiclient"; import type { User } from "../types/User"; import type { UserEntry } from "../types/UserEntry"; import { Dispatch } from "redux"; +import { Action } from "../../types/Action"; export const FETCH_USERS_PENDING = "scm/users/FETCH_USERS_PENDING"; export const FETCH_USERS_SUCCESS = "scm/users/FETCH_USERS_SUCCESS"; @@ -25,6 +26,7 @@ export const DELETE_USER_SUCCESS = "scm/users/DELETE_SUCCESS"; export const DELETE_USER_FAILURE = "scm/users/DELETE_FAILURE"; const USERS_URL = "users"; +const USER_URL = "users/"; const CONTENT_TYPE_USER = "application/vnd.scmm-user+json;v=2"; @@ -53,20 +55,20 @@ export function fetchUsers() { }; } -export function fetchUsersPending() { +export function fetchUsersPending(): Action { return { type: FETCH_USERS_PENDING }; } -export function fetchUsersSuccess(users: any) { +export function fetchUsersSuccess(users: any): Action { return { type: FETCH_USERS_SUCCESS, payload: users }; } -export function fetchUsersFailure(url: string, error: Error) { +export function fetchUsersFailure(url: string, error: Error): Action { return { type: FETCH_USERS_FAILURE, payload: { @@ -81,7 +83,7 @@ export function fetchUsersFailure(url: string, error: Error) { export function fetchUser(name: string) { const userUrl = USERS_URL + "/" + name; return function(dispatch: any) { - dispatch(fetchUsersPending()); + dispatch(fetchUserPending(name)); return apiClient .get(userUrl) .then(response => { @@ -97,30 +99,32 @@ export function fetchUser(name: string) { }) .catch(cause => { const error = new Error(`could not fetch user: ${cause.message}`); - dispatch(fetchUsersFailure(USERS_URL, error)); + dispatch(fetchUserFailure(USERS_URL, error)); }); }; } -export function fetchUserPending(name: string) { +export function fetchUserPending(name: string): Action { return { type: FETCH_USER_PENDING, payload: { name } }; } -export function fetchUserSuccess(user: any) { +export function fetchUserSuccess(user: any): Action { return { type: FETCH_USER_SUCCESS, payload: user }; } -export function fetchUserFailure(user: User, error: Error) { +export function fetchUserFailure(username: string, error: Error): Action { return { type: FETCH_USER_FAILURE, - user, - error + payload: { + username, + error + } }; } @@ -146,20 +150,20 @@ export function createUser(user: User) { }; } -export function createUserPending(user: User) { +export function createUserPending(user: User): Action { return { type: CREATE_USER_PENDING, user }; } -export function createUserSuccess() { +export function createUserSuccess(): Action { return { type: CREATE_USER_SUCCESS }; } -export function createUserFailure(user: User, err: Error) { +export function createUserFailure(user: User, err: Error): Action { return { type: CREATE_USER_FAILURE, payload: err, @@ -184,25 +188,31 @@ export function modifyUser(user: User) { }; } -function modifyUserPending(user: User) { +function modifyUserPending(user: User): Action { return { type: MODIFY_USER_PENDING, - user + payload: { + user + } }; } -function modifyUserSuccess(user: User) { +function modifyUserSuccess(user: User): Action { return { type: MODIFY_USER_SUCCESS, - user + payload: { + user + } }; } -export function modifyUserFailure(user: User, error: Error) { +export function modifyUserFailure(user: User, error: Error): Action { return { type: MODIFY_USER_FAILURE, - payload: error, - user + payload: { + error, + user + } }; } @@ -226,21 +236,21 @@ export function deleteUser(user: User) { }; } -export function deleteUserPending(user: User) { +export function deleteUserPending(user: User): Action { return { type: DELETE_USER, payload: user }; } -export function deleteUserSuccess(user: User) { +export function deleteUserSuccess(user: User): Action { return { type: DELETE_USER_SUCCESS, payload: user }; } -export function deleteUserFailure(user: User, error: Error) { +export function deleteUserFailure(user: User, error: Error): Action { return { type: DELETE_USER_FAILURE, payload: { @@ -380,9 +390,9 @@ export default function reducer(state: any = {}, action: any = {}) { usersByNames: ubn }; case FETCH_USER_FAILURE: - return reduceUsersByNames(state, action.user.name, { + return reduceUsersByNames(state, action.payload.username, { loading: true, - error: action.error + error: action.payload.error }); // Delete single user cases case DELETE_USER: diff --git a/scm-ui/src/users/modules/users.test.js b/scm-ui/src/users/modules/users.test.js index 8683a60306..1a8a598c4c 100644 --- a/scm-ui/src/users/modules/users.test.js +++ b/scm-ui/src/users/modules/users.test.js @@ -24,6 +24,10 @@ import { deleteUser, fetchUsersFailure, fetchUsersSuccess, + fetchUser, + FETCH_USER_PENDING, + FETCH_USER_SUCCESS, + FETCH_USER_FAILURE, createUser, createUserSuccess, createUserFailure, @@ -131,6 +135,8 @@ const response = { responseBody }; +const USERS_URL = "/scm/api/rest/v2/users"; + describe("users fetch()", () => { const mockStore = configureMockStore([thunk]); afterEach(() => { @@ -139,7 +145,7 @@ describe("users fetch()", () => { }); it("should successfully fetch users", () => { - fetchMock.getOnce("/scm/api/rest/v2/users", response); + fetchMock.getOnce(USERS_URL, response); const expectedActions = [ { type: FETCH_USERS_PENDING }, @@ -157,7 +163,7 @@ describe("users fetch()", () => { }); it("should fail getting users on HTTP 500", () => { - fetchMock.getOnce("/scm/api/rest/v2/users", { + fetchMock.getOnce(USERS_URL, { status: 500 }); @@ -170,14 +176,40 @@ describe("users fetch()", () => { }); }); + it("should sucessfully fetch single user", () => { + fetchMock.getOnce(USERS_URL + "/zaphod", userZaphod); + + const store = mockStore({}); + return store.dispatch(fetchUser("zaphod")).then(() => { + const actions = store.getActions(); + expect(actions[0].type).toEqual(FETCH_USER_PENDING); + expect(actions[1].type).toEqual(FETCH_USER_SUCCESS); + expect(actions[1].payload).toBeDefined(); + }); + }); + + it("should fail fetching single user on HTTP 500", () => { + fetchMock.getOnce(USERS_URL + "/zaphod", { + status: 500 + }); + + const store = mockStore({}); + return store.dispatch(fetchUser("zaphod")).then(() => { + const actions = store.getActions(); + expect(actions[0].type).toEqual(FETCH_USER_PENDING); + expect(actions[1].type).toEqual(FETCH_USER_FAILURE); + expect(actions[1].payload).toBeDefined(); + }); + }); + it("should add a user successfully", () => { // unmatched - fetchMock.postOnce("/scm/api/rest/v2/users", { + fetchMock.postOnce(USERS_URL, { status: 204 }); // after create, the users are fetched again - fetchMock.getOnce("/scm/api/rest/v2/users", response); + fetchMock.getOnce(USERS_URL, response); const store = mockStore({}); return store.dispatch(createUser(userZaphod)).then(() => { @@ -189,7 +221,7 @@ describe("users fetch()", () => { }); it("should fail adding a user on HTTP 500", () => { - fetchMock.postOnce("/scm/api/rest/v2/users", { + fetchMock.postOnce(USERS_URL, { status: 500 }); @@ -207,7 +239,7 @@ describe("users fetch()", () => { status: 204 }); // after update, the users are fetched again - fetchMock.getOnce("/scm/api/rest/v2/users", response); + fetchMock.getOnce(USERS_URL, response); const store = mockStore({}); return store.dispatch(modifyUser(userZaphod)).then(() => { @@ -237,7 +269,7 @@ describe("users fetch()", () => { status: 204 }); // after update, the users are fetched again - fetchMock.getOnce("/scm/api/rest/v2/users", response); + fetchMock.getOnce(USERS_URL, response); const store = mockStore({}); return store.dispatch(deleteUser(userZaphod)).then(() => { @@ -411,10 +443,10 @@ describe("users reducer", () => { expect(newState.usersByNames["zaphod"].loading).toBeTruthy(); }); - it("should uppdate state according to FETCH_USER_FAILURE action", () => { + it("should update state according to FETCH_USER_FAILURE action", () => { const newState = reducer( {}, - fetchUserFailure(userFord, new Error("kaputt!")) + fetchUserFailure(userFord.name, new Error("kaputt!")) ); expect(newState.usersByNames["ford"].error).toBeTruthy; });