mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 01:15:44 +01:00
implemented ui for sources root
This commit is contained in:
27
scm-ui-components/packages/ui-types/src/Sources.js
Normal file
27
scm-ui-components/packages/ui-types/src/Sources.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// @flow
|
||||
|
||||
import type { Collection, Links } from "./hal";
|
||||
|
||||
// TODO ?? check ?? links
|
||||
export type SubRepository = {
|
||||
repositoryUrl: string,
|
||||
browserUrl: string,
|
||||
revision: string
|
||||
};
|
||||
|
||||
export type File = {
|
||||
name: string,
|
||||
path: string,
|
||||
directory: boolean,
|
||||
description?: string,
|
||||
length: number,
|
||||
lastModified?: string,
|
||||
subRepository?: SubRepository, // TODO
|
||||
_links: Links
|
||||
};
|
||||
|
||||
export type SourcesCollection = Collection & {
|
||||
_embedded: {
|
||||
files: File[]
|
||||
}
|
||||
};
|
||||
@@ -10,3 +10,5 @@ export type { Repository, RepositoryCollection, RepositoryGroup } from "./Reposi
|
||||
export type { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes";
|
||||
|
||||
export type { Config } from "./Config";
|
||||
|
||||
export type { SubRepository, File, SourcesCollection } from "./Sources";
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"actions-label": "Actions",
|
||||
"back-label": "Back",
|
||||
"navigation-label": "Navigation",
|
||||
"information": "Information"
|
||||
"information": "Information",
|
||||
"sources": "Sources"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create Repository",
|
||||
@@ -42,5 +43,13 @@
|
||||
"submit": "Yes",
|
||||
"cancel": "No"
|
||||
}
|
||||
},
|
||||
"sources": {
|
||||
"file-tree": {
|
||||
"name": "Name",
|
||||
"length": "Length",
|
||||
"lastModified": "Last modified",
|
||||
"description": "Description"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { routerReducer, routerMiddleware } from "react-router-redux";
|
||||
import users from "./users/modules/users";
|
||||
import repos from "./repos/modules/repos";
|
||||
import repositoryTypes from "./repos/modules/repositoryTypes";
|
||||
import sources from "./repos/sources/modules/sources";
|
||||
import groups from "./groups/modules/groups";
|
||||
import auth from "./modules/auth";
|
||||
import pending from "./modules/pending";
|
||||
@@ -28,7 +29,8 @@ function createReduxStore(history: BrowserHistory) {
|
||||
repositoryTypes,
|
||||
groups,
|
||||
auth,
|
||||
config
|
||||
config,
|
||||
sources
|
||||
});
|
||||
|
||||
return createStore(
|
||||
|
||||
28
scm-ui/src/repos/components/RepositoryNavLink.js
Normal file
28
scm-ui/src/repos/components/RepositoryNavLink.js
Normal file
@@ -0,0 +1,28 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
to: string,
|
||||
label: string,
|
||||
linkName: string
|
||||
};
|
||||
|
||||
/**
|
||||
* Component renders only if the repository contains the link with the given name.
|
||||
*/
|
||||
class RepositoryNavLink extends React.Component<Props> {
|
||||
render() {
|
||||
const { linkName, to, label, repository } = this.props;
|
||||
|
||||
if (!repository._links[linkName]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <NavLink to={to} label={label} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default RepositoryNavLink;
|
||||
49
scm-ui/src/repos/components/RepositoryNavLink.test.js
Normal file
49
scm-ui/src/repos/components/RepositoryNavLink.test.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { shallow, mount } from "enzyme";
|
||||
import "../../tests/enzyme";
|
||||
import "../../tests/i18n";
|
||||
import ReactRouterEnzymeContext from "react-router-enzyme-context";
|
||||
import RepositoryNavLink from "./RepositoryNavLink";
|
||||
|
||||
describe("RepositoryNavLink", () => {
|
||||
const options = new ReactRouterEnzymeContext();
|
||||
|
||||
it("should render nothing, if the sources link is missing", () => {
|
||||
const repository = {
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const navLink = shallow(
|
||||
<RepositoryNavLink
|
||||
repository={repository}
|
||||
linkName="sources"
|
||||
to="/sources"
|
||||
label="Sources"
|
||||
/>,
|
||||
options.get()
|
||||
);
|
||||
expect(navLink.text()).toBe("");
|
||||
});
|
||||
|
||||
it("should render the navLink", () => {
|
||||
const repository = {
|
||||
_links: {
|
||||
sources: {
|
||||
href: "/sources"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const navLink = mount(
|
||||
<RepositoryNavLink
|
||||
repository={repository}
|
||||
linkName="sources"
|
||||
to="/sources"
|
||||
label="Sources"
|
||||
/>,
|
||||
options.get()
|
||||
);
|
||||
expect(navLink.text()).toBe("Sources");
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,8 @@ import Edit from "../containers/Edit";
|
||||
|
||||
import type { History } from "history";
|
||||
import EditNavLink from "../components/EditNavLink";
|
||||
import Sources from "../sources/containers/Sources";
|
||||
import RepositoryNavLink from "../components/RepositoryNavLink";
|
||||
|
||||
type Props = {
|
||||
namespace: string,
|
||||
@@ -101,12 +103,22 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
path={`${url}/edit`}
|
||||
component={() => <Edit repository={repository} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/sources`}
|
||||
component={() => <Sources repository={repository} />}
|
||||
/>
|
||||
</div>
|
||||
<div className="column">
|
||||
<Navigation>
|
||||
<Section label={t("repository-root.navigation-label")}>
|
||||
<NavLink to={url} label={t("repository-root.information")} />
|
||||
<EditNavLink repository={repository} editUrl={`${url}/edit`} />
|
||||
<RepositoryNavLink
|
||||
repository={repository}
|
||||
linkName="sources"
|
||||
to={`${url}/sources`}
|
||||
label={t("repository-root.sources")}
|
||||
/>
|
||||
</Section>
|
||||
<Section label={t("repository-root.actions-label")}>
|
||||
<DeleteNavAction repository={repository} delete={this.delete} />
|
||||
|
||||
20
scm-ui/src/repos/sources/components/FileIcon.js
Normal file
20
scm-ui/src/repos/sources/components/FileIcon.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
file: File
|
||||
};
|
||||
|
||||
class FileIcon extends React.Component<Props> {
|
||||
render() {
|
||||
const { file } = this.props;
|
||||
let icon = "file";
|
||||
if (file.directory) {
|
||||
icon = "folder";
|
||||
}
|
||||
return <i className={`fa fa-${icon}`} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default FileIcon;
|
||||
27
scm-ui/src/repos/sources/components/FileSize.js
Normal file
27
scm-ui/src/repos/sources/components/FileSize.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
bytes: number
|
||||
};
|
||||
|
||||
class FileSize extends React.Component<Props> {
|
||||
static format(bytes) {
|
||||
if (!bytes) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const units = ["B", "K", "M", "G", "T", "P", "E", "Z", "Y"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
|
||||
const size = (bytes / 1024 ** i).toFixed(2);
|
||||
return `${size} ${units[i]}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
const formattedBytes = FileSize.format(this.props.bytes);
|
||||
return <span>{formattedBytes}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
export default FileSize;
|
||||
9
scm-ui/src/repos/sources/components/FileSize.test.js
Normal file
9
scm-ui/src/repos/sources/components/FileSize.test.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import FileSize from "./FileSize";
|
||||
|
||||
it("should format bytes", () => {
|
||||
expect(FileSize.format(160)).toBe("160.00 B");
|
||||
expect(FileSize.format(6304)).toBe("6.16 K");
|
||||
expect(FileSize.format(28792588)).toBe("27.46 M");
|
||||
expect(FileSize.format(1369510189)).toBe("1.28 G");
|
||||
expect(FileSize.format(42949672960)).toBe("40.00 G");
|
||||
});
|
||||
48
scm-ui/src/repos/sources/components/FileTree.js
Normal file
48
scm-ui/src/repos/sources/components/FileTree.js
Normal file
@@ -0,0 +1,48 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import injectSheet from "react-jss";
|
||||
import FileTreeLeaf from "./FileTreeLeaf";
|
||||
import type { SourcesCollection } from "@scm-manager/ui-types";
|
||||
|
||||
const styles = {
|
||||
iconColumn: {
|
||||
width: "16px"
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
tree: SourcesCollection,
|
||||
|
||||
// context props
|
||||
classes: any,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class FileTree extends React.Component<Props> {
|
||||
render() {
|
||||
const { tree, classes, t } = this.props;
|
||||
|
||||
const files = tree._embedded.files;
|
||||
|
||||
return (
|
||||
<table className="table table-hover table-sm is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={classes.iconColumn} />
|
||||
<th>{t("sources.file-tree.name")}</th>
|
||||
<th>{t("sources.file-tree.length")}</th>
|
||||
<th>{t("sources.file-tree.lastModified")}</th>
|
||||
<th>{t("sources.file-tree.description")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{files.map(file => (
|
||||
<FileTreeLeaf key={file.name} file={file} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default injectSheet(styles)(translate("repos")(FileTree));
|
||||
49
scm-ui/src/repos/sources/components/FileTreeLeaf.js
Normal file
49
scm-ui/src/repos/sources/components/FileTreeLeaf.js
Normal file
@@ -0,0 +1,49 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import { DateFromNow } from "@scm-manager/ui-components";
|
||||
import FileSize from "./FileSize";
|
||||
import FileIcon from "./FileIcon";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
|
||||
const styles = {
|
||||
iconColumn: {
|
||||
width: "16px"
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
file: File,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
};
|
||||
|
||||
class FileTreeLeaf extends React.Component<Props> {
|
||||
render() {
|
||||
const { file, classes } = this.props;
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td className={classes.iconColumn}>
|
||||
<Link to="#todo">
|
||||
<FileIcon file={file} />
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<Link to="#todo">{file.name}</Link>
|
||||
</td>
|
||||
<td>
|
||||
<FileSize bytes={file.length} />
|
||||
</td>
|
||||
<td>
|
||||
<DateFromNow date={file.lastModified} />
|
||||
</td>
|
||||
<td>{file.description}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(FileTreeLeaf);
|
||||
72
scm-ui/src/repos/sources/containers/Sources.js
Normal file
72
scm-ui/src/repos/sources/containers/Sources.js
Normal file
@@ -0,0 +1,72 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import type { Repository, SourcesCollection } from "@scm-manager/ui-types";
|
||||
import FileTree from "../components/FileTree";
|
||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchSources,
|
||||
getFetchSourcesFailure,
|
||||
getSources,
|
||||
isFetchSourcesPending
|
||||
} from "../modules/sources";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
sources: SourcesCollection,
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
|
||||
// dispatch props
|
||||
fetchSources: (repository: Repository) => void
|
||||
};
|
||||
|
||||
class Sources extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchSources, repository } = this.props;
|
||||
|
||||
fetchSources(repository);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { sources, loading, error } = this.props;
|
||||
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
|
||||
if (!sources || loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return <FileTree tree={sources} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { repository } = ownProps;
|
||||
const loading = isFetchSourcesPending(state, repository);
|
||||
const error = getFetchSourcesFailure(state, repository);
|
||||
const sources = getSources(state, repository);
|
||||
|
||||
console.log(sources);
|
||||
|
||||
return {
|
||||
loading,
|
||||
error,
|
||||
sources
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchSources: (repository: Repository) => {
|
||||
dispatch(fetchSources(repository));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Sources);
|
||||
106
scm-ui/src/repos/sources/modules/sources.js
Normal file
106
scm-ui/src/repos/sources/modules/sources.js
Normal file
@@ -0,0 +1,106 @@
|
||||
// @flow
|
||||
|
||||
import * as types from "../../../modules/types";
|
||||
import type {
|
||||
Repository,
|
||||
SourcesCollection,
|
||||
Action
|
||||
} from "@scm-manager/ui-types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
|
||||
export const FETCH_SOURCES = "scm/repos/FETCH_SOURCES";
|
||||
export const FETCH_SOURCES_PENDING = `${FETCH_SOURCES}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_SOURCES_SUCCESS = `${FETCH_SOURCES}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_SOURCES_FAILURE = `${FETCH_SOURCES}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export function fetchSources(repository: Repository) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(fetchSourcesPending(repository));
|
||||
return apiClient
|
||||
.get(repository._links.sources.href)
|
||||
.then(response => response.json())
|
||||
.then(sources => {
|
||||
dispatch(fetchSourcesSuccess(repository, sources));
|
||||
})
|
||||
.catch(err => {
|
||||
const error = new Error(`failed to fetch sources: ${err.message}`);
|
||||
dispatch(fetchSourcesFailure(repository, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSourcesPending(repository: Repository): Action {
|
||||
return {
|
||||
type: FETCH_SOURCES_PENDING,
|
||||
itemId: createItemId(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSourcesSuccess(
|
||||
repository: Repository,
|
||||
sources: SourcesCollection
|
||||
) {
|
||||
return {
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
payload: sources,
|
||||
itemId: createItemId(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSourcesFailure(
|
||||
repository: Repository,
|
||||
error: Error
|
||||
): Action {
|
||||
return {
|
||||
type: FETCH_SOURCES_FAILURE,
|
||||
payload: error,
|
||||
itemId: createItemId(repository)
|
||||
};
|
||||
}
|
||||
|
||||
function createItemId(repository: Repository) {
|
||||
return `${repository.namespace}/${repository.name}`;
|
||||
}
|
||||
|
||||
// reducer
|
||||
|
||||
export default function reducer(
|
||||
state: any = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): any {
|
||||
if (action.type === FETCH_SOURCES_SUCCESS) {
|
||||
return {
|
||||
[action.itemId]: action.payload,
|
||||
...state
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
// selectors
|
||||
|
||||
export function getSources(
|
||||
state: any,
|
||||
repository: Repository
|
||||
): ?SourcesCollection {
|
||||
if (state.sources) {
|
||||
return state.sources[createItemId(repository)];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isFetchSourcesPending(
|
||||
state: any,
|
||||
repository: Repository
|
||||
): boolean {
|
||||
return isPending(state, FETCH_SOURCES, createItemId(repository));
|
||||
}
|
||||
|
||||
export function getFetchSourcesFailure(
|
||||
state: any,
|
||||
repository: Repository
|
||||
): ?Error {
|
||||
return getFailure(state, FETCH_SOURCES, createItemId(repository));
|
||||
}
|
||||
178
scm-ui/src/repos/sources/modules/sources.test.js
Normal file
178
scm-ui/src/repos/sources/modules/sources.test.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// @flow
|
||||
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
import {
|
||||
FETCH_SOURCES,
|
||||
FETCH_SOURCES_FAILURE,
|
||||
FETCH_SOURCES_PENDING,
|
||||
FETCH_SOURCES_SUCCESS,
|
||||
fetchSources,
|
||||
getFetchSourcesFailure,
|
||||
isFetchSourcesPending,
|
||||
default as reducer,
|
||||
getSources
|
||||
} from "./sources";
|
||||
|
||||
const sourcesUrl =
|
||||
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources";
|
||||
|
||||
const repository: Repository = {
|
||||
name: "core",
|
||||
namespace: "scm",
|
||||
type: "git",
|
||||
_links: {
|
||||
sources: {
|
||||
href: sourcesUrl
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const collection = {
|
||||
revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/"
|
||||
}
|
||||
},
|
||||
_embedded: {
|
||||
files: [
|
||||
{
|
||||
name: "src",
|
||||
path: "src",
|
||||
directory: true,
|
||||
description: null,
|
||||
length: 176,
|
||||
lastModified: null,
|
||||
subRepository: null,
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/src"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "package.json",
|
||||
path: "package.json",
|
||||
directory: false,
|
||||
description: "bump version",
|
||||
length: 780,
|
||||
lastModified: "2017-07-31T11:17:19Z",
|
||||
subRepository: null,
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/content/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/package.json"
|
||||
},
|
||||
history: {
|
||||
href:
|
||||
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/history/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
describe("sources fetch", () => {
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it("should fetch the sources of the repository", () => {
|
||||
fetchMock.getOnce(sourcesUrl, collection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_SOURCES_PENDING, itemId: "scm/core" },
|
||||
{
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
itemId: "scm/core",
|
||||
payload: collection
|
||||
}
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchSources(repository)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch FETCH_SOURCES_FAILURE on server error", () => {
|
||||
fetchMock.getOnce(sourcesUrl, {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchSources(repository)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toBe(FETCH_SOURCES_PENDING);
|
||||
expect(actions[1].type).toBe(FETCH_SOURCES_FAILURE);
|
||||
expect(actions[1].itemId).toBe("scm/core");
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("reducer tests", () => {
|
||||
it("should return unmodified state on unknown action", () => {
|
||||
const state = {};
|
||||
expect(reducer(state)).toBe(state);
|
||||
});
|
||||
|
||||
it("should store the collection", () => {
|
||||
const expectedState = {
|
||||
"scm/core": repository
|
||||
};
|
||||
expect(reducer({}, fetchSources(repository))).toEqual(expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe("selector tests", () => {
|
||||
it("should return null", () => {
|
||||
expect(getSources({}, repository)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should return the source collection", () => {
|
||||
const state = {
|
||||
sources: {
|
||||
"scm/core": collection
|
||||
}
|
||||
};
|
||||
expect(getSources(state, repository)).toBe(collection);
|
||||
});
|
||||
|
||||
it("should return true, when fetch sources is pending", () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[FETCH_SOURCES + "/scm/core"]: true
|
||||
}
|
||||
};
|
||||
expect(isFetchSourcesPending(state, repository)).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false, when fetch sources is not pending", () => {
|
||||
expect(isFetchSourcesPending({}, repository)).toEqual(false);
|
||||
});
|
||||
|
||||
const error = new Error("incredible error from hell");
|
||||
|
||||
it("should return error when fetch sources did fail", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_SOURCES + "/scm/core"]: error
|
||||
}
|
||||
};
|
||||
expect(getFetchSourcesFailure(state, repository)).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when fetch sources did not fail", () => {
|
||||
expect(getFetchSourcesFailure({}, repository)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user