mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 15:35:49 +01:00
added defaultBranch to Branches type, changed ui-bundler version for better testing experience in intellij, corrected fetchBranch functionality, wrote reducer for single branch and unit tests
This commit is contained in:
@@ -4,5 +4,6 @@ import type {Links} from "./hal";
|
||||
export type Branch = {
|
||||
name: string,
|
||||
revision: string,
|
||||
defaultBranch?: boolean,
|
||||
_links: Links
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"pre-commit": "jest && flow && eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scm-manager/ui-bundler": "^0.0.26",
|
||||
"@scm-manager/ui-bundler": "^0.0.27",
|
||||
"concat": "^1.0.3",
|
||||
"copyfiles": "^2.0.0",
|
||||
"enzyme": "^3.3.0",
|
||||
|
||||
@@ -19,7 +19,7 @@ const styles = {
|
||||
};
|
||||
|
||||
class BranchRow extends React.Component<Props> {
|
||||
renderLink(to: string, label: string, defaultBranch: boolean) {
|
||||
renderLink(to: string, label: string, defaultBranch?: boolean) {
|
||||
const { classes } = this.props;
|
||||
|
||||
let showLabel = null;
|
||||
|
||||
@@ -7,8 +7,8 @@ import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import {
|
||||
fetchBranchByName,
|
||||
getBranchByName,
|
||||
fetchBranch,
|
||||
getBranch,
|
||||
getFetchBranchFailure,
|
||||
isFetchBranchPending
|
||||
} from "../modules/branches";
|
||||
@@ -22,7 +22,7 @@ type Props = {
|
||||
branch: Branch,
|
||||
|
||||
// dispatch functions
|
||||
fetchBranchByName: (repository: Repository, branchName: string) => void,
|
||||
fetchBranch: (repository: Repository, branchName: string) => void,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
@@ -30,9 +30,9 @@ type Props = {
|
||||
|
||||
class BranchView extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchBranchByName, repository, branchName } = this.props;
|
||||
const { fetchBranch, repository, branchName } = this.props;
|
||||
|
||||
fetchBranchByName(repository, branchName);
|
||||
fetchBranch(repository, branchName);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -71,7 +71,7 @@ class BranchView extends React.Component<Props> {
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { repository } = ownProps;
|
||||
const branchName = decodeURIComponent(ownProps.match.params.branch);
|
||||
const branch = getBranchByName(state, branchName);
|
||||
const branch = getBranch(state, repository, branchName);
|
||||
const loading = isFetchBranchPending(state, branchName);
|
||||
const error = getFetchBranchFailure(state, branchName);
|
||||
return {
|
||||
@@ -85,8 +85,8 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBranchByName: (repository: Repository, branchName: string) => {
|
||||
dispatch(fetchBranchByName(repository._links.branches.href, branchName));
|
||||
fetchBranch: (repository: Repository, branchName: string) => {
|
||||
dispatch(fetchBranch(repository, branchName));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,11 +5,7 @@ import {
|
||||
SUCCESS_SUFFIX
|
||||
} from "../../../modules/types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import type {
|
||||
Action,
|
||||
Branch,
|
||||
Repository
|
||||
} from "@scm-manager/ui-types";
|
||||
import type { Action, Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
|
||||
@@ -25,61 +21,69 @@ export const FETCH_BRANCH_FAILURE = `${FETCH_BRANCH}_${FAILURE_SUFFIX}`;
|
||||
|
||||
// Fetching branches
|
||||
|
||||
export function fetchBranchByName(link: string, name: string) {
|
||||
let endsWith = "";
|
||||
if(!link.endsWith("/")) {
|
||||
endsWith = "/";
|
||||
}
|
||||
return fetchBranch(link + endsWith + encodeURIComponent(name), name);
|
||||
function createIdentifier(repository: Repository) {
|
||||
return repository.namespace + "/" + repository.name;
|
||||
}
|
||||
|
||||
export function fetchBranchPending(name: string): Action {
|
||||
export function fetchBranchPending(
|
||||
repository: Repository,
|
||||
name: string
|
||||
): Action {
|
||||
return {
|
||||
type: FETCH_BRANCH_PENDING,
|
||||
payload: name,
|
||||
itemId: name
|
||||
payload: { repository, name },
|
||||
itemId: createIdentifier(repository) + "/" + name
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBranchSuccess(repo: Repository, branch: Branch): Action {
|
||||
export function fetchBranchSuccess(
|
||||
repository: Repository,
|
||||
branch: Branch
|
||||
): Action {
|
||||
return {
|
||||
type: FETCH_BRANCH_SUCCESS,
|
||||
payload: branch,
|
||||
itemId: branch.name
|
||||
payload: { repository, branch },
|
||||
itemId: createIdentifier(repository) + "/" + branch.name
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBranchFailure(name: string, error: Error): Action {
|
||||
export function fetchBranchFailure(
|
||||
repository: Repository,
|
||||
name: string,
|
||||
error: Error
|
||||
): Action {
|
||||
return {
|
||||
type: FETCH_BRANCH_FAILURE,
|
||||
payload: name,
|
||||
itemId: name
|
||||
payload: { error, repository, name },
|
||||
itemId: createIdentifier(repository) + "/" + name
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBranch(link: string, name: string) {
|
||||
export function fetchBranch(
|
||||
repository: Repository,
|
||||
name: string
|
||||
) {
|
||||
let link = repository._links.branches.href;
|
||||
if (!link.endsWith("/")) {
|
||||
link += "/";
|
||||
}
|
||||
link += encodeURIComponent(name);
|
||||
return function(dispatch: any) {
|
||||
dispatch(fetchBranchPending(name));
|
||||
dispatch(fetchBranchPending(repository, name));
|
||||
return apiClient
|
||||
.get(link)
|
||||
.then(response => {
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
dispatch(fetchBranchSuccess(data));
|
||||
dispatch(fetchBranchSuccess(repository, data));
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch(fetchBranchFailure(name, error));
|
||||
dispatch(fetchBranchFailure(repository, name, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function getBranchByName(state: Object, name: string) {
|
||||
if (state.branches) {
|
||||
return state.branches[name];
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchBranchPending(state: Object, name: string) {
|
||||
return isPending(state, FETCH_BRANCH, name);
|
||||
}
|
||||
@@ -138,6 +142,24 @@ export function fetchBranchesFailure(repository: Repository, error: Error) {
|
||||
|
||||
// Reducers
|
||||
|
||||
function reduceBranchSuccess(state, repositoryName, newBranch) {
|
||||
const newBranches = [];
|
||||
// we do not use filter, because we try to keep the current order
|
||||
let found = false;
|
||||
for (const branch of state[repositoryName] || []) {
|
||||
if (branch.name === newBranch.name) {
|
||||
newBranches.push(newBranch);
|
||||
found = true;
|
||||
} else {
|
||||
newBranches.push(branch);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
newBranches.push(newBranch);
|
||||
}
|
||||
return newBranches;
|
||||
}
|
||||
|
||||
type State = { [string]: Branch[] };
|
||||
|
||||
export default function reducer(
|
||||
@@ -156,11 +178,15 @@ export default function reducer(
|
||||
[key]: extractBranchesFromPayload(payload.data)
|
||||
};
|
||||
case FETCH_BRANCH_SUCCESS:
|
||||
if (!action.payload.repository || !action.payload.branch) {
|
||||
return state;
|
||||
}
|
||||
const newBranch = action.payload.branch;
|
||||
const repositoryName = createIdentifier(action.payload.repository);
|
||||
return {
|
||||
...state,
|
||||
[action.payload.name]: action.payload
|
||||
[repositoryName]: reduceBranchSuccess(state, repositoryName, newBranch)
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@ import reducer, {
|
||||
FETCH_BRANCH_SUCCESS,
|
||||
FETCH_BRANCH_FAILURE,
|
||||
fetchBranches,
|
||||
fetchBranchByName,
|
||||
fetchBranchSuccess,
|
||||
fetchBranch,
|
||||
fetchBranchSuccess,
|
||||
getBranch,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
@@ -100,7 +99,7 @@ describe("branches", () => {
|
||||
fetchMock.getOnce(URL + "/branch1", branch1);
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchBranchByName(URL, "branch1")).then(() => {
|
||||
return store.dispatch(fetchBranch(repository, "branch1")).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_BRANCH_SUCCESS);
|
||||
@@ -114,7 +113,7 @@ describe("branches", () => {
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchBranchByName(URL, "branch2")).then(() => {
|
||||
return store.dispatch(fetchBranch(repository, "branch2")).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(FETCH_BRANCH_FAILURE);
|
||||
@@ -149,51 +148,60 @@ describe("branches", () => {
|
||||
const oldState = {
|
||||
"hitchhiker/heartOfGold": [branch3]
|
||||
};
|
||||
|
||||
const newState = reducer(oldState, action);
|
||||
expect(newState[key]).toContain(branch1);
|
||||
expect(newState[key]).toContain(branch2);
|
||||
|
||||
expect(newState["hitchhiker/heartOfGold"]).toContain(branch3);
|
||||
});
|
||||
|
||||
it("should update state according to FETCH_BRANCH_SUCCESS action", () => {
|
||||
const newState = reducer({}, fetchBranchSuccess(branch3));
|
||||
expect(newState["branch3"]).toBe(branch3);
|
||||
const newState = reducer({}, fetchBranchSuccess(repository, branch3));
|
||||
expect(newState["foo/bar"]).toEqual([branch3]);
|
||||
});
|
||||
|
||||
it("should not delete existing branch from state", () => {
|
||||
const oldState = {
|
||||
branch1
|
||||
"foo/bar": [branch1]
|
||||
};
|
||||
|
||||
const newState = reducer(oldState, fetchBranchSuccess(branch2));
|
||||
expect(newState["branch1"]).toBe(branch1);
|
||||
expect(newState["branch2"]).toBe(branch2);
|
||||
const newState = reducer(oldState, fetchBranchSuccess(repository, branch2));
|
||||
expect(newState["foo/bar"]).toEqual([branch1, branch2]);
|
||||
});
|
||||
|
||||
it("should update required branch from state", () => {
|
||||
const oldState = {
|
||||
branch1
|
||||
"foo/bar": [branch1]
|
||||
};
|
||||
|
||||
const newBranch1 = { name: "branch1", revision: "revision2" };
|
||||
|
||||
const newState = reducer(oldState, fetchBranchSuccess(newBranch1));
|
||||
expect(newState["branch1"]).not.toBe(branch1);
|
||||
expect(newState["branch1"]).toBe(newBranch1);
|
||||
const newState = reducer(oldState, fetchBranchSuccess(repository, newBranch1));
|
||||
expect(newState["foo/bar"]).toEqual([newBranch1]);
|
||||
});
|
||||
|
||||
it("should update required branch from state and keeps old repo", () => {
|
||||
const oldState = {
|
||||
repo1: {
|
||||
branch1
|
||||
}
|
||||
"ns/one": [branch1]
|
||||
};
|
||||
const repo2 = { repo2: { branch3 } };
|
||||
const newState = reducer(oldState, fetchBranchSuccess(repo2, branch2));
|
||||
expect(newState["repo1"]).toBe({ branch1 });
|
||||
expect(newState["repo2"]).toBe({ branch2, branch3 });
|
||||
const newState = reducer(oldState, fetchBranchSuccess(repository, branch3));
|
||||
expect(newState["ns/one"]).toEqual([branch1]);
|
||||
expect(newState["foo/bar"]).toEqual([branch3]);
|
||||
});
|
||||
|
||||
it("should return the oldState, if action has no payload", () => {
|
||||
const state = {};
|
||||
const newState = reducer(state, {type: FETCH_BRANCH_SUCCESS});
|
||||
expect(newState).toBe(state);
|
||||
});
|
||||
|
||||
it("should return the oldState, if payload has no branch", () => {
|
||||
const action = {
|
||||
type: FETCH_BRANCH_SUCCESS,
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: "foo/bar/"
|
||||
};
|
||||
const state = {};
|
||||
const newState = reducer(state, action);
|
||||
expect(newState).toBe(state);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -698,9 +698,10 @@
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||
|
||||
"@scm-manager/ui-bundler@^0.0.26":
|
||||
version "0.0.26"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.26.tgz#4676a7079b781b33fa1989c6643205c3559b1f66"
|
||||
"@scm-manager/ui-bundler@^0.0.27":
|
||||
version "0.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.27.tgz#3ed2c7826780b9a1a9ea90464332640cfb5d54b5"
|
||||
integrity sha512-cBU1xq6gDy1Vw9AGOzsR763+JmBeraTaC/KQfxT3I6XyZJ2brIfG1m5QYcAcHWvDxq3mYMogpI5rfShw14L4/w==
|
||||
dependencies:
|
||||
"@babel/core" "^7.0.0"
|
||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||
|
||||
Reference in New Issue
Block a user