Merge with upstream

This commit is contained in:
Florian Scholdei
2019-10-09 16:55:50 +02:00
29 changed files with 2904 additions and 237 deletions

View File

@@ -5,7 +5,8 @@
"scm-plugins/*" "scm-plugins/*"
], ],
"scripts": { "scripts": {
"build": "webpack --mode=development --config=scm-ui/scripts/webpack.config.js", "build": "webpack --mode=production --config=scm-ui/scripts/webpack.config.js",
"build:dev": "webpack --mode=development --config=scm-ui/scripts/webpack.config.js",
"test": "jest --config=scm-ui/scripts/jest.config.js", "test": "jest --config=scm-ui/scripts/jest.config.js",
"serve": "webpack-dev-server --mode=development --config=scm-ui/scripts/webpack.config.js" "serve": "webpack-dev-server --mode=development --config=scm-ui/scripts/webpack.config.js"
}, },

View File

@@ -3,7 +3,8 @@
"version": "2.0.0-SNAPSHOT", "version": "2.0.0-SNAPSHOT",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"scripts": { "scripts": {
"build": "webpack --mode=development" "build": "webpack --mode=production",
"test": "jest --config=../../scm-ui/scripts/jest-plugin.config.js"
}, },
"devDependencies": { "devDependencies": {
"flow-bin": "^0.109.0", "flow-bin": "^0.109.0",
@@ -11,7 +12,7 @@
}, },
"jest": { "jest": {
"transform": { "transform": {
"^.+\\.js$": "../../scm-ui/scripts/babelMonoRepoTransformer.js" "^.+\\.js$": "../../scm-ui/scripts/babelPluginTransformer.js"
} }
} }
} }

View File

@@ -51,10 +51,6 @@
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<corePlugin>true</corePlugin> <corePlugin>true</corePlugin>
<links>
<link>@scm-manager/ui-types</link>
<link>@scm-manager/ui-components</link>
</links>
</configuration> </configuration>
</plugin> </plugin>

View File

@@ -14,7 +14,7 @@ import RepositoryConfig from "./RepositoryConfig";
// @visibleForTesting // @visibleForTesting
export const gitPredicate = (props: Object) => { export const gitPredicate = (props: Object) => {
return props.repository && props.repository.type === "git"; return !!(props && props.repository && props.repository.type === "git");
}; };
binder.bind( binder.bind(

View File

@@ -1,7 +1,7 @@
// @flow // @flow
import { gitPredicate } from "./index"; import { gitPredicate } from "./index";
describe("test gi predicate", () => { describe("test git predicate", () => {
it("should return false", () => { it("should return false", () => {
expect(gitPredicate()).toBe(false); expect(gitPredicate()).toBe(false);
expect(gitPredicate({})).toBe(false); expect(gitPredicate({})).toBe(false);
@@ -10,6 +10,6 @@ describe("test gi predicate", () => {
}); });
it("should return true", () => { it("should return true", () => {
expect(gitPredicate({ repository: { type: "fir" } })).toBe(true); expect(gitPredicate({ repository: { type: "git" } })).toBe(true);
}); });
}); });

View File

@@ -4,6 +4,11 @@
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"main": "src/main/js/index.js", "main": "src/main/js/index.js",
"scripts": { "scripts": {
"build": "webpack --mode=development" "build": "webpack --mode=production"
},
"jest": {
"transform": {
"^.+\\.js$": "../../scm-ui/scripts/babelPluginTransformer.js"
}
} }
} }

View File

@@ -57,10 +57,6 @@
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<corePlugin>true</corePlugin> <corePlugin>true</corePlugin>
<links>
<link>@scm-manager/ui-types</link>
<link>@scm-manager/ui-components</link>
</links>
</configuration> </configuration>
</plugin> </plugin>

View File

@@ -4,6 +4,11 @@
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"main": "src/main/js/index.js", "main": "src/main/js/index.js",
"scripts": { "scripts": {
"build": "webpack --mode=development" "build": "webpack --mode=production"
},
"jest": {
"transform": {
"^.+\\.js$": "../../scm-ui/scripts/babelPluginTransformer.js"
}
} }
} }

View File

@@ -4,6 +4,11 @@
"version": "2.0.0-SNAPSHOT", "version": "2.0.0-SNAPSHOT",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"scripts": { "scripts": {
"build": "webpack --mode=development" "build": "webpack --mode=production"
},
"jest": {
"transform": {
"^.+\\.js$": "../../scm-ui/scripts/babelPluginTransformer.js"
}
} }
} }

View File

@@ -44,10 +44,6 @@
<artifactId>smp-maven-plugin</artifactId> <artifactId>smp-maven-plugin</artifactId>
<configuration> <configuration>
<corePlugin>true</corePlugin> <corePlugin>true</corePlugin>
<links>
<link>@scm-manager/ui-types</link>
<link>@scm-manager/ui-components</link>
</links>
</configuration> </configuration>
</plugin> </plugin>

View File

@@ -75,17 +75,17 @@
<script>build</script> <script>build</script>
</configuration> </configuration>
</execution> </execution>
<!--execution> <execution>
<id>test</id> <id>test</id>
<phase>test</phase> <phase>test</phase>
<goals> <goals>
<goal>run</goal> <goal>run</goal>
</goals> </goals>
<configuration> <configuration>
<script>test-ci</script> <script>test</script>
<skip>${skipTests}</skip> <skip>${skipTests}</skip>
</configuration> </configuration>
</execution--> </execution>
</executions> </executions>
</plugin> </plugin>

View File

@@ -1,11 +1,11 @@
/** /**
* Read and use .babelrc from packages * Read and use .babelrc from packages
*/ */
const { join, resolve } = require("path"); const path = require("path");
const { createTransformer } = require("babel-jest"); const { createTransformer } = require("babel-jest");
const packagePath = resolve(__dirname, "../"); const packagePath = path.resolve(__dirname, "../");
const packageGlob = join(packagePath, "*"); const packageGlob = path.join(packagePath, "*");
module.exports = createTransformer({ module.exports = createTransformer({
babelrcRoots: packageGlob babelrcRoots: packageGlob

View File

@@ -0,0 +1,16 @@
/**
* Read and use .babelrc from packages
*/
const path = require("path");
const { createTransformer } = require("babel-jest");
const packagePath = path.resolve(__dirname, "..");
const packageGlob = path.join(packagePath, "*");
const currentDirectory = path.join(process.cwd());
module.exports = createTransformer({
babelrcRoots: [
packageGlob,
currentDirectory
]
});

View File

@@ -0,0 +1,30 @@
const path = require("path");
const fs = require("fs");
const transformer = path.resolve(__dirname, "babelPluginTransformer.js");
const rootDir = path.resolve(process.cwd());
const packageJsonPath = path.join(rootDir, "package.json");
const packageJson = JSON.parse(
fs.readFileSync(packageJsonPath, { encoding: "UTF-8" })
);
const reportDirectory = path.join(rootDir, "target", "jest-reports");
module.exports = {
rootDir,
transform: { "^.+\\.js$": transformer },
transformIgnorePatterns: [".*/node_modules/.*"],
collectCoverage: true,
coverageDirectory: path.join(reportDirectory, "coverage"),
coveragePathIgnorePatterns: ["src/main/js/tests/.*"],
reporters: [
"default",
[
"jest-junit",
{
suiteName: packageJson.name + " tests",
outputDirectory: reportDirectory,
outputName: "TEST-plugin.xml"
}
]
]
};

View File

@@ -12,7 +12,11 @@ module.exports = {
"default", "default",
[ [
"jest-junit", "jest-junit",
{ outputDirectory: reportDirectory, outputName: "TEST-all.xml" } {
suiteName: "SCM-UI Package tests",
outputDirectory: reportDirectory,
outputName: "TEST-scm-ui.xml"
}
] ]
] ]
}; };

View File

@@ -29,22 +29,26 @@ module.exports = {
{ {
test: /\.(js|jsx)$/, test: /\.(js|jsx)$/,
exclude: /node_modules/, exclude: /node_modules/,
use: [{ use: [
loader: "cache-loader" {
},{ loader: "cache-loader"
loader: "thread-loader" },
}, { {
loader: "babel-loader", loader: "thread-loader"
options: { },
cacheDirectory: true, {
presets: [ loader: "babel-loader",
"@babel/preset-env", options: {
"@babel/preset-react", cacheDirectory: true,
"@babel/preset-flow" presets: [
], "@babel/preset-env",
plugins: ["@babel/plugin-proposal-class-properties"] "@babel/preset-react",
"@babel/preset-flow"
],
plugins: ["@babel/plugin-proposal-class-properties"]
}
} }
}] ]
}, },
{ {
test: /\.(css|scss|sass)$/i, test: /\.(css|scss|sass)$/i,
@@ -77,7 +81,13 @@ module.exports = {
app.use(createContextPathMiddleware("/scm")); app.use(createContextPathMiddleware("/scm"));
}, },
after: function(app) { after: function(app) {
const templatePath = path.join(__dirname, "..", "ui-webapp", "public", "index.mustache"); const templatePath = path.join(
__dirname,
"..",
"ui-webapp",
"public",
"index.mustache"
);
const renderParams = { const renderParams = {
contextPath: "/scm" contextPath: "/scm"
}; };
@@ -88,12 +98,16 @@ module.exports = {
optimization: { optimization: {
runtimeChunk: "single", runtimeChunk: "single",
splitChunks: { splitChunks: {
chunks: "all",
cacheGroups: { cacheGroups: {
vendor: { vendors: {
test: /[\\/]node_modules[\\/]/, test: /[\\/]node_modules[\\/]/,
name: "vendors", priority: -10
enforce: true, },
chunks: "all" default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
} }
} }
} }

View File

@@ -0,0 +1 @@
import 'storybook-addon-i18next/register';

View File

@@ -0,0 +1,28 @@
import i18n from "i18next";
import { reactI18nextModule } from "react-i18next";
import { addDecorator, configure } from "@storybook/react";
import { withI18next } from "storybook-addon-i18next";
import "!style-loader!css-loader!sass-loader!../../ui-webapp/src/style/scm.scss";
i18n.use(reactI18nextModule).init({
whitelist: ["en", "de", "es"],
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false
}
});
addDecorator(
withI18next({
i18n,
languages: {
en: "English",
de: "Deutsch",
es: "Spanisch"
}
})
);
configure(require.context("../src", true, /\.stories\.js$/), module);

View File

@@ -11,34 +11,39 @@
"author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>", "author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"scripts": { "scripts": {
"update-index": "create-index -r src", "eslint-fix": "eslint src --fix",
"eslint-fix": "eslint src --fix" "test": "jest",
"storybook": "start-storybook"
}, },
"devDependencies": { "devDependencies": {
"create-index": "^2.3.0", "@storybook/addon-actions": "^5.2.3",
"@storybook/addon-storyshots": "^5.2.3",
"@storybook/react": "^5.2.3",
"enzyme": "^3.5.0", "enzyme": "^3.5.0",
"enzyme-adapter-react-16": "^1.3.1", "enzyme-adapter-react-16": "^1.3.1",
"fetch-mock": "^7.2.5", "fetch-mock": "^7.5.1",
"flow-bin": "^0.109.0", "flow-bin": "^0.109.0",
"flow-typed": "^2.5.1", "flow-typed": "^2.5.1",
"raf": "^3.4.0", "raf": "^3.4.0",
"react-router-enzyme-context": "^1.2.0" "react-router-enzyme-context": "^1.2.0",
"storybook-addon-i18next": "^1.2.1",
"storybook-react-router": "^1.0.8"
}, },
"dependencies": { "dependencies": {
"@scm-manager/ui-extensions": "^0.1.2", "@scm-manager/ui-extensions": "^0.1.2",
"@scm-manager/ui-types": "2.0.0-SNAPSHOT", "@scm-manager/ui-types": "2.0.0-SNAPSHOT",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"moment": "^2.22.2", "date-fns": "^2.4.1",
"query-string": "5", "query-string": "5",
"react": "^16.8.6", "react": "^16.8.6",
"react-diff-view": "^1.8.1", "react-diff-view": "^1.8.1",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-i18next": "^7.11.0", "react-i18next": "^7.9.0",
"react-jss": "^8.6.1", "react-jss": "^8.6.1",
"react-markdown": "^4.0.6", "react-markdown": "^4.0.6",
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-select": "^2.1.2", "react-select": "^2.1.2",
"react-syntax-highlighter": "^10.2.0" "react-syntax-highlighter": "^11.0.2"
}, },
"resolutions": { "resolutions": {
"gitdiff-parser": "https://github.com/cloudogu/gitdiff-parser#3a72da4a8e3d9bfb4b9e01a43e85628c19f26cc4" "gitdiff-parser": "https://github.com/cloudogu/gitdiff-parser#3a72da4a8e3d9bfb4b9e01a43e85628c19f26cc4"
@@ -46,6 +51,9 @@
"jest": { "jest": {
"transform": { "transform": {
"^.+\\.js$": "../scripts/babelMonoRepoTransformer.js" "^.+\\.js$": "../scripts/babelMonoRepoTransformer.js"
},
"moduleNameMapper": {
"\\.(css|scss|sass)$": "<rootDir>/src/tests/styleMock.js"
} }
} }
} }

View File

@@ -1,12 +1,15 @@
//@flow //@flow
import React from "react"; import React from "react";
import moment from "moment";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { formatDistance, format, parseISO } from "date-fns";
import { enUS, de, es } from "date-fns/locale";
import styled from "styled-components"; import styled from "styled-components";
// fix german locale const supportedLocales = {
// https://momentjscom.readthedocs.io/en/latest/moment/00-use-it/07-browserify/ enUS,
import "moment/locale/de"; de,
es
};
type Props = { type Props = {
date?: string, date?: string,
@@ -15,25 +18,37 @@ type Props = {
i18n: any i18n: any
}; };
const Date = styled.time` const DateElement = styled.time`
border-bottom: 1px dotted rgba(219, 219, 219); border-bottom: 1px dotted rgba(219, 219, 219);
cursor: help; cursor: help;
`; `;
class DateFromNow extends React.Component<Props> { class DateFromNow extends React.Component<Props> {
render() { getLocale = () => {
const { i18n, date } = this.props; const { i18n } = this.props;
const locale = supportedLocales[i18n.language];
if (date) { if (!locale) {
const dateWithLocale = moment(date).locale(i18n.language); return enUS;
return (
<Date title={dateWithLocale.format()}>
{dateWithLocale.fromNow()}
</Date>
);
} }
return locale;
};
createOptions = () => {
const locale = this.getLocale();
return {
locale
};
};
render() {
const { date } = this.props;
if (date) {
const isoDate = parseISO(date);
const options = this.createOptions();
const distance = formatDistance(isoDate, new Date(), options);
const formatted = format(isoDate, "yyyy-MM-dd HH:mm:ss", options);
return <DateElement title={formatted}>{distance}</DateElement>;
}
return null; return null;
} }
} }

View File

@@ -0,0 +1,14 @@
import React from "react";
import DateFromNow from "./DateFromNow";
import { storiesOf } from "@storybook/react";
storiesOf("DateFromNow", module).add("Default", () => (
<div>
<p>
<DateFromNow date="2009-06-30T18:30:00+02:00" />
</p>
<p>
<DateFromNow date="2019-06-30T18:30:00+02:00" />
</p>
</div>
));

View File

@@ -3,8 +3,8 @@ import FileSize from "./FileSize";
it("should format bytes", () => { it("should format bytes", () => {
expect(FileSize.format(0)).toBe("0 B"); expect(FileSize.format(0)).toBe("0 B");
expect(FileSize.format(160)).toBe("160 B"); expect(FileSize.format(160)).toBe("160 B");
expect(FileSize.format(6304)).toBe("6.16 K"); expect(FileSize.format(6304)).toBe("6.30 K");
expect(FileSize.format(28792588)).toBe("27.46 M"); expect(FileSize.format(28792588)).toBe("28.79 M");
expect(FileSize.format(1369510189)).toBe("1.28 G"); expect(FileSize.format(1369510189)).toBe("1.37 G");
expect(FileSize.format(42949672960)).toBe("40.00 G"); expect(FileSize.format(42949672960)).toBe("42.95 G");
}); });

View File

@@ -1,7 +1,7 @@
// @flow // @flow
import React from "react"; import React from "react";
import ReactSyntaxHighlighter from "react-syntax-highlighter"; import { LightAsync as ReactSyntaxHighlighter } from "react-syntax-highlighter";
import { arduinoLight } from "react-syntax-highlighter/dist/styles/hljs"; import { arduinoLight } from "react-syntax-highlighter/dist/cjs/styles/hljs";
type Props = { type Props = {
language: string, language: string,

View File

@@ -0,0 +1,23 @@
import React from "react";
import Button from "./Button";
import { storiesOf } from "@storybook/react";
import StoryRouter from "storybook-react-router";
import styled from "styled-components";
const colors = ["primary", "success", "info", "warning", "danger", "black"];
const Spacing = styled.div`
padding: 1em;
`;
storiesOf("Button", module)
.addDecorator(StoryRouter())
.add("Colors", () => (
<div>
{colors.map(color => (
<Spacing>
<Button color={color} label={color} />
</Spacing>
))}
</div>
));

View File

@@ -11,7 +11,8 @@ describe("test createBackendError", () => {
context: [{ context: [{
type: "planet", type: "planet",
id: "earth" id: "earth"
}] }],
violations: []
}; };
it("should return a default backend error", () => { it("should return a default backend error", () => {
@@ -20,7 +21,8 @@ describe("test createBackendError", () => {
expect(err.name).toBe("BackendError"); expect(err.name).toBe("BackendError");
}); });
it("should return an unauthorized error for status code 403", () => { // 403 is no backend error
xit("should return an unauthorized error for status code 403", () => {
const err = createBackendError(earthNotFoundError, 403); const err = createBackendError(earthNotFoundError, 403);
expect(err).toBeInstanceOf(UnauthorizedError); expect(err).toBeInstanceOf(UnauthorizedError);
expect(err.name).toBe("UnauthorizedError"); expect(err.name).toBe("UnauthorizedError");

View File

@@ -0,0 +1 @@
module.exports = {};

View File

@@ -19,7 +19,6 @@
"i18next-fetch-backend": "^0.1.0", "i18next-fetch-backend": "^0.1.0",
"jss-nested": "^6.0.1", "jss-nested": "^6.0.1",
"memoize-one": "^5.0.4", "memoize-one": "^5.0.4",
"moment": "^2.22.2",
"query-string": "5", "query-string": "5",
"react": "^16.8.6", "react": "^16.8.6",
"react-diff-view": "^1.8.1", "react-diff-view": "^1.8.1",
@@ -31,7 +30,6 @@
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-router-redux": "^5.0.0-alpha.9", "react-router-redux": "^5.0.0-alpha.9",
"react-select": "^2.1.2", "react-select": "^2.1.2",
"react-syntax-highlighter": "^9.0.1",
"redux": "^4.0.0", "redux": "^4.0.0",
"redux-devtools-extension": "^2.13.5", "redux-devtools-extension": "^2.13.5",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
@@ -49,8 +47,7 @@
"build-js": "ui-bundler bundle --mode=production target/scm-ui/scm-ui.bundle.js", "build-js": "ui-bundler bundle --mode=production target/scm-ui/scm-ui.bundle.js",
"build-vendor": "ui-bundler vendor --mode=production target/scm-ui/vendor.bundle.js", "build-vendor": "ui-bundler vendor --mode=production target/scm-ui/vendor.bundle.js",
"build": "npm-run-all -s webfonts build-css polyfills build-vendor build-js", "build": "npm-run-all -s webfonts build-css polyfills build-vendor build-js",
"test": "ui-bundler test", "test": "jest",
"test-ci": "ui-bundler test --ci",
"flow": "flow", "flow": "flow",
"pre-commit": "jest && flow && eslint src" "pre-commit": "jest && flow && eslint src"
}, },
@@ -59,7 +56,7 @@
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"enzyme": "^3.3.0", "enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1", "enzyme-adapter-react-16": "^1.1.1",
"fetch-mock": "^6.5.0", "fetch-mock": "^7.5.1",
"flow-bin": "^0.109.0", "flow-bin": "^0.109.0",
"flow-typed": "^2.6.1", "flow-typed": "^2.6.1",
"node-sass": "^4.9.3", "node-sass": "^4.9.3",

View File

@@ -35,7 +35,7 @@
window.ctxPath = "{{ contextPath }}"; window.ctxPath = "{{ contextPath }}";
</script> </script>
<script src="{{ contextPath }}/assets/runtime.bundle.js"></script> <script src="{{ contextPath }}/assets/runtime.bundle.js"></script>
<script src="{{ contextPath }}/assets/vendors.bundle.js"></script> <script src="{{ contextPath }}/assets/vendors~webapp.bundle.js"></script>
<script src="{{ contextPath }}/assets/webapp.bundle.js"></script> <script src="{{ contextPath }}/assets/webapp.bundle.js"></script>
{{#liveReloadURL}} {{#liveReloadURL}}

2811
yarn.lock

File diff suppressed because it is too large Load Diff