implemented repository create form

This commit is contained in:
Sebastian Sdorra
2018-08-02 16:09:58 +02:00
parent e675bcc0fd
commit 9b62d19df5
23 changed files with 838 additions and 129 deletions

View File

@@ -0,0 +1,107 @@
// @flow
import * as types from "../../modules/types";
import type { Action } from "../../types/Action";
import type {
RepositoryType,
RepositoryTypeCollection
} from "../types/RepositoryTypes";
import { apiClient } from "../../apiclient";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
export const FETCH_REPOSITORY_TYPES = "scm/repos/FETCH_REPOSITORY_TYPES";
export const FETCH_REPOSITORY_TYPES_PENDING = `${FETCH_REPOSITORY_TYPES}_${
types.PENDING_SUFFIX
}`;
export const FETCH_REPOSITORY_TYPES_SUCCESS = `${FETCH_REPOSITORY_TYPES}_${
types.SUCCESS_SUFFIX
}`;
export const FETCH_REPOSITORY_TYPES_FAILURE = `${FETCH_REPOSITORY_TYPES}_${
types.FAILURE_SUFFIX
}`;
export function fetchRepositoryTypesIfNeeded() {
return function(dispatch: any, getState: () => Object) {
if (shouldFetchRepositoryTypes(getState())) {
return fetchRepositoryTypes(dispatch);
}
};
}
function fetchRepositoryTypes(dispatch: any) {
dispatch(fetchRepositoryTypesPending());
return apiClient
.get("repository-types")
.then(response => response.json())
.then(repositoryTypes => {
dispatch(fetchRepositoryTypesSuccess(repositoryTypes));
})
.catch(err => {
dispatch(fetchRepositoryTypesFailure(err));
});
}
export function shouldFetchRepositoryTypes(state: Object) {
if (
isFetchRepositoryTypesPending(state) ||
getFetchRepositoryTypesFailure(state)
) {
return false;
}
if (state.repositoryTypes && state.repositoryTypes.length > 0) {
return false;
}
return true;
}
export function fetchRepositoryTypesPending(): Action {
return {
type: FETCH_REPOSITORY_TYPES_PENDING
};
}
export function fetchRepositoryTypesSuccess(
repositoryTypes: RepositoryTypeCollection
): Action {
return {
type: FETCH_REPOSITORY_TYPES_SUCCESS,
payload: repositoryTypes
};
}
export function fetchRepositoryTypesFailure(error: Error): Action {
return {
type: FETCH_REPOSITORY_TYPES_FAILURE,
payload: error
};
}
// reducers
export default function reducer(
state: RepositoryType[] = [],
action: Action = { type: "UNKNOWN" }
): RepositoryType[] {
if (action.type === FETCH_REPOSITORY_TYPES_SUCCESS && action.payload) {
return action.payload._embedded["repository-types"];
}
return state;
}
// selectors
export function getRepositoryTypes(state: Object) {
if (state.repositoryTypes) {
return state.repositoryTypes;
}
return [];
}
export function isFetchRepositoryTypesPending(state: Object) {
return isPending(state, FETCH_REPOSITORY_TYPES);
}
export function getFetchRepositoryTypesFailure(state: Object) {
return getFailure(state, FETCH_REPOSITORY_TYPES);
}

View File

@@ -0,0 +1,198 @@
// @flow
import fetchMock from "fetch-mock";
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import {
FETCH_REPOSITORY_TYPES,
FETCH_REPOSITORY_TYPES_FAILURE,
FETCH_REPOSITORY_TYPES_PENDING,
FETCH_REPOSITORY_TYPES_SUCCESS,
fetchRepositoryTypesIfNeeded,
fetchRepositoryTypesSuccess,
getFetchRepositoryTypesFailure,
getRepositoryTypes,
isFetchRepositoryTypesPending,
shouldFetchRepositoryTypes
} from "./repository-types";
import reducer from "./repository-types";
const git = {
name: "git",
displayName: "Git",
_links: {
self: {
href: "http://localhost:8081/scm/api/rest/v2/repository-types/git"
}
}
};
const hg = {
name: "hg",
displayName: "Mercurial",
_links: {
self: {
href: "http://localhost:8081/scm/api/rest/v2/repository-types/hg"
}
}
};
const svn = {
name: "svn",
displayName: "Subversion",
_links: {
self: {
href: "http://localhost:8081/scm/api/rest/v2/repository-types/svn"
}
}
};
const collection = {
_embedded: {
"repository-types": [git, hg, svn]
},
_links: {
self: {
href: "http://localhost:8081/scm/api/rest/v2/repository-types"
}
}
};
describe("repository types caching", () => {
it("should fetch repository types, on empty state", () => {
expect(shouldFetchRepositoryTypes({})).toBe(true);
});
it("should fetch repository types, if the state contains an empty array", () => {
const state = {
repositoryTypes: []
};
expect(shouldFetchRepositoryTypes(state)).toBe(true);
});
it("should not fetch repository types, on pending state", () => {
const state = {
pending: {
[FETCH_REPOSITORY_TYPES]: true
}
};
expect(shouldFetchRepositoryTypes(state)).toBe(false);
});
it("should not fetch repository types, on failure state", () => {
const state = {
failure: {
[FETCH_REPOSITORY_TYPES]: new Error("no...")
}
};
expect(shouldFetchRepositoryTypes(state)).toBe(false);
});
it("should not fetch repository types, if they are already fetched", () => {
const state = {
repositoryTypes: [git, hg, svn]
};
expect(shouldFetchRepositoryTypes(state)).toBe(false);
});
});
describe("repository types fetch", () => {
const URL = "/scm/api/rest/v2/repository-types";
const mockStore = configureMockStore([thunk]);
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it("should successfully fetch repository types", () => {
fetchMock.getOnce(URL, collection);
const expectedActions = [
{ type: FETCH_REPOSITORY_TYPES_PENDING },
{
type: FETCH_REPOSITORY_TYPES_SUCCESS,
payload: collection
}
];
const store = mockStore({});
return store.dispatch(fetchRepositoryTypesIfNeeded()).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it("should dispatch FETCH_REPOSITORY_TYPES_FAILURE on server error", () => {
fetchMock.getOnce(URL, {
status: 500
});
const store = mockStore({});
return store.dispatch(fetchRepositoryTypesIfNeeded()).then(() => {
const actions = store.getActions();
expect(actions[0].type).toBe(FETCH_REPOSITORY_TYPES_PENDING);
expect(actions[1].type).toBe(FETCH_REPOSITORY_TYPES_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
it("should dispatch not dispatch any action, if the repository types are already fetched", () => {
const store = mockStore({
repositoryTypes: [git, hg, svn]
});
store.dispatch(fetchRepositoryTypesIfNeeded());
expect(store.getActions().length).toBe(0);
});
});
describe("repository types reducer", () => {
it("should return unmodified state on unknown action", () => {
const state = [];
expect(reducer(state)).toBe(state);
});
it("should store the repository types on FETCH_REPOSITORY_TYPES_SUCCESS", () => {
const newState = reducer([], fetchRepositoryTypesSuccess(collection));
expect(newState).toEqual([git, hg, svn]);
});
});
describe("repository types selectors", () => {
const error = new Error("The end of the universe");
it("should return an emtpy array", () => {
expect(getRepositoryTypes({})).toEqual([]);
});
it("should return the repository types", () => {
const state = {
repositoryTypes: [git, hg, svn]
};
expect(getRepositoryTypes(state)).toEqual([git, hg, svn]);
});
it("should return true, when fetch repository types is pending", () => {
const state = {
pending: {
[FETCH_REPOSITORY_TYPES]: true
}
};
expect(isFetchRepositoryTypesPending(state)).toEqual(true);
});
it("should return false, when fetch repos is not pending", () => {
expect(isFetchRepositoryTypesPending({})).toEqual(false);
});
it("should return error when fetch repository types did fail", () => {
const state = {
failure: {
[FETCH_REPOSITORY_TYPES]: error
}
};
expect(getFetchRepositoryTypesFailure(state)).toEqual(error);
});
it("should return undefined when fetch repos did not fail", () => {
expect(getFetchRepositoryTypesFailure({})).toBe(undefined);
});
});