diff --git a/scm-ui/jest.config.js b/scm-ui/jest.config.js new file mode 100644 index 0000000000..a5e135724b --- /dev/null +++ b/scm-ui/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + collectCoverage: true, + coverageFormats: ["json", "html"] +}; diff --git a/scm-ui/src/users/containers/EditUserButton.js b/scm-ui/src/users/containers/EditUserButton.js new file mode 100644 index 0000000000..37a65bbbe3 --- /dev/null +++ b/scm-ui/src/users/containers/EditUserButton.js @@ -0,0 +1,29 @@ +//@flow +import React from "react"; +import type { User } from "../types/User"; + +type Props = { + user: User, + editUser: User => void +}; + +type State = {}; + +class EditUserButton extends React.Component { + render() { + if (!this.isEditable()) { + return ""; + } + return ( + + ); + } + + isEditable = () => { + return this.props.user._links.update; + }; +} + +export default EditUserButton; diff --git a/scm-ui/src/users/containers/EditUserButton.test.js b/scm-ui/src/users/containers/EditUserButton.test.js new file mode 100644 index 0000000000..a916be12f0 --- /dev/null +++ b/scm-ui/src/users/containers/EditUserButton.test.js @@ -0,0 +1,31 @@ +import React from "react"; +import { configure, shallow } from "enzyme"; +import EditUserButton from "./EditUserButton"; +import Adapter from "enzyme-adapter-react-16"; + +import "raf/polyfill"; + +configure({ adapter: new Adapter() }); + +it("should render nothing, if the edit link is missing", () => { + const user = { + _links: {} + }; + + const button = shallow(); + expect(button.text()).toBe(""); +}); + +it("should render the button", () => { + const user = { + _links: { + update: { + href: "/users" + } + } + }; + + const button = shallow(); + expect(button.text()).not.toBe(""); +}); + diff --git a/scm-ui/src/users/modules/users.js b/scm-ui/src/users/modules/users.js index 73b82f781e..d4b7085c6a 100644 --- a/scm-ui/src/users/modules/users.js +++ b/scm-ui/src/users/modules/users.js @@ -175,13 +175,14 @@ function deleteUserSuccess(user: User) { export function deleteUserFailure(user: User, error: Error) { return { type: DELETE_USER_FAILURE, - payload: { - error, + payload: { + error, user } }; } + export function deleteUser(user: User) { return function (dispatch: any) { dispatch(requestDeleteUser(user)); @@ -241,7 +242,7 @@ export function editUser(user: User) { const reduceUsersByNames = (state: any, username: string, newUserState: any) => { const newUsersByNames = { ...state.usersByNames, - [username] : newUserState + [username]: newUserState }; return { @@ -254,12 +255,8 @@ export default function reducer(state: any = {}, action: any = {}) { switch (action.type) { case FETCH_USERS: return { - ...state, - users: { - error: null, - entries: null, - loading: true - } + loading: true, + error: null }; case DELETE_USER: return reduceUsersByNames(state, action.payload.name, { @@ -287,17 +284,12 @@ export default function reducer(state: any = {}, action: any = {}) { }; case FETCH_USERS_FAILURE: - return { - ...state, - error: action.payload, - loading: false - }; case DELETE_USER_FAILURE: - return reduceUsersByNames(state, action.payload.user.name, { - loading: false, - error: action.payload.error, - entry: action.payload.user - }); + return reduceUsersByNames(state, action.payload.user.name, { + loading: false, + error: action.payload.error, + entry: action.payload.user + }); case EDIT_USER: return { ...state, diff --git a/scm-ui/src/users/modules/users.test.js b/scm-ui/src/users/modules/users.test.js index 8104ea9051..e462491e98 100644 --- a/scm-ui/src/users/modules/users.test.js +++ b/scm-ui/src/users/modules/users.test.js @@ -11,6 +11,10 @@ import { FETCH_USERS_SUCCESS, fetchUsers, FETCH_USERS_FAILURE, + addUser, + ADD_USER, + ADD_USER_SUCCESS, + ADD_USER_FAILURE, updateUser, UPDATE_USER, UPDATE_USER_FAILURE, @@ -160,6 +164,33 @@ describe("fetch tests", () => { }); }); + test("successful user add", () => { + fetchMock.postOnce("/scm/api/rest/v2/users", { + status: 204 + }); + + const store = mockStore({}); + return store.dispatch(addUser(userZaphod)).then(() => { + const actions = store.getActions(); + expect(actions[0].type).toEqual(ADD_USER); + expect(actions[1].type).toEqual(ADD_USER_SUCCESS); + }); + }); + + test("user add failed", () => { + fetchMock.postOnce("/scm/api/rest/v2/users", { + status: 500 + }); + + const store = mockStore({}); + return store.dispatch(addUser(userZaphod)).then(() => { + const actions = store.getActions(); + expect(actions[0].type).toEqual(ADD_USER); + expect(actions[1].type).toEqual(ADD_USER_FAILURE); + expect(actions[1].payload).toBeDefined(); + }); + }); + test("successful user update", () => { fetchMock.putOnce("http://localhost:8081/scm/api/rest/v2/users/zaphod", { status: 204 diff --git a/scm-ui/src/users/types/UserEntry.js b/scm-ui/src/users/types/UserEntry.js new file mode 100644 index 0000000000..5ab609d02c --- /dev/null +++ b/scm-ui/src/users/types/UserEntry.js @@ -0,0 +1,7 @@ +type UserEntry = { + loading: boolean, + error: Error, + user: User +}; + +export type UserEntry; \ No newline at end of file