mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-11-08 23:25:51 +01:00
Merge branch 'feat-new-detail-page' of github.com:verdaccio/verdaccio into feat-new-detail-page
This commit is contained in:
commit
495ec89da4
67
.babelrc
67
.babelrc
@ -1,68 +1,3 @@
|
||||
{
|
||||
"env": {
|
||||
"ui": {
|
||||
"presets": [
|
||||
"@babel/react",
|
||||
"@babel/flow",
|
||||
["@babel/env",{
|
||||
"targets": {
|
||||
"browsers": [
|
||||
"last 5 versions",
|
||||
"FireFox >= 44",
|
||||
"Safari >= 7",
|
||||
"Explorer 11",
|
||||
"last 4 Edge versions"
|
||||
]
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
"react-hot-loader/babel",
|
||||
"@babel/transform-runtime",
|
||||
"@babel/proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/syntax-dynamic-import",
|
||||
"emotion"
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"presets": [["@babel/env", {
|
||||
"targets": {
|
||||
"node": "6.10"
|
||||
}
|
||||
}], "@babel/flow",
|
||||
"@babel/react"],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"babel-plugin-dynamic-import-node",
|
||||
"emotion"
|
||||
]
|
||||
},
|
||||
"registry": {
|
||||
"presets": [
|
||||
["@babel/env", {
|
||||
"targets": {
|
||||
"node": "6.10"
|
||||
}
|
||||
}], "@babel/flow"],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/proposal-class-properties"
|
||||
]
|
||||
},
|
||||
"registry-docker": {
|
||||
"presets": [
|
||||
["@babel/env", {
|
||||
"targets": {
|
||||
"node": "10"
|
||||
}
|
||||
}],
|
||||
"@babel/flow"],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/proposal-class-properties"
|
||||
]
|
||||
}
|
||||
}
|
||||
"presets": [["@verdaccio", {"flow": true}]]
|
||||
}
|
||||
|
@ -14,3 +14,4 @@ Dockerfile
|
||||
*.scss
|
||||
*.png
|
||||
*.jpg
|
||||
test/unit/partials/
|
||||
|
36
CHANGELOG.md
36
CHANGELOG.md
@ -66,6 +66,42 @@ All notable changes to this project will be documented in this file. See [standa
|
||||
* replaced lunr by lunr-mutable ([#915](https://github.com/verdaccio/verdaccio/issues/915)) ([1602840](https://github.com/verdaccio/verdaccio/commit/1602840))
|
||||
* verdaccio update notification on CLI ([#988](https://github.com/verdaccio/verdaccio/issues/988)) ([#998](https://github.com/verdaccio/verdaccio/issues/998)) ([bc04703](https://github.com/verdaccio/verdaccio/commit/bc04703))
|
||||
|
||||
<a name="3.11.1"></a>
|
||||
## [3.11.1](https://github.com/verdaccio/verdaccio/compare/v3.11.0...v3.11.1) (2019-01-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don’t make change if `time` fields match ([#1167](https://github.com/verdaccio/verdaccio/issues/1167)) ([e62ef8d](https://github.com/verdaccio/verdaccio/commit/e62ef8d))
|
||||
* don’t packages that have no uplinks after reading ([#1204](https://github.com/verdaccio/verdaccio/issues/1204)) ([95686be](https://github.com/verdaccio/verdaccio/commit/95686be))
|
||||
|
||||
|
||||
|
||||
<a name="3.11.0"></a>
|
||||
# [3.11.0](https://github.com/verdaccio/verdaccio/compare/v3.10.2...v3.11.0) (2019-01-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* introduce server keepAliveTimeout into config files ([a359055](https://github.com/verdaccio/verdaccio/commit/a359055))
|
||||
|
||||
|
||||
|
||||
<a name="3.10.2"></a>
|
||||
## [3.10.2](https://github.com/verdaccio/verdaccio/compare/v3.10.1...v3.10.2) (2019-01-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add logic to catch clause ([#1183](https://github.com/verdaccio/verdaccio/issues/1183)) ([056d396](https://github.com/verdaccio/verdaccio/commit/056d396))
|
||||
* adds _id to normalise metadata ([#1194](https://github.com/verdaccio/verdaccio/issues/1194)) ([e2fa581](https://github.com/verdaccio/verdaccio/commit/e2fa581))
|
||||
* remove some unneeded checks ([#1182](https://github.com/verdaccio/verdaccio/issues/1182)) ([ab56d75](https://github.com/verdaccio/verdaccio/commit/ab56d75))
|
||||
* remove unused object ([#1185](https://github.com/verdaccio/verdaccio/issues/1185)) ([e9b3907](https://github.com/verdaccio/verdaccio/commit/e9b3907))
|
||||
* remove unused parameters from processBody method invocation ([#1184](https://github.com/verdaccio/verdaccio/issues/1184)) ([064f7cf](https://github.com/verdaccio/verdaccio/commit/064f7cf))
|
||||
* remove use of comma separator ([#1186](https://github.com/verdaccio/verdaccio/issues/1186)) ([f20fefa](https://github.com/verdaccio/verdaccio/commit/f20fefa))
|
||||
* remove useless assignment to local variable emailCopy ([#1181](https://github.com/verdaccio/verdaccio/issues/1181)) ([13b8347](https://github.com/verdaccio/verdaccio/commit/13b8347))
|
||||
|
||||
|
||||
<a name="3.10.1"></a>
|
||||
## [3.10.1](https://github.com/verdaccio/verdaccio/compare/v3.10.0...v3.10.1) (2018-12-20)
|
||||
|
||||
|
@ -61,6 +61,12 @@ packages:
|
||||
# if package is not available locally, proxy requests to 'npmjs' registry
|
||||
proxy: npmjs
|
||||
|
||||
# You can specify HTTP/1.1 server keep alive timeout in seconds for incomming connections.
|
||||
# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout.
|
||||
# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enought.
|
||||
server:
|
||||
keepAliveTimeout: 60
|
||||
|
||||
# To use `npm audit` uncomment the following section
|
||||
middlewares:
|
||||
audit:
|
||||
|
66
package.json
66
package.json
@ -16,77 +16,49 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@verdaccio/file-locking": "0.0.7",
|
||||
"@verdaccio/local-storage": "1.2.0",
|
||||
"@verdaccio/streams": "1.0.0",
|
||||
"JSONStream": "1.3.4",
|
||||
"@verdaccio/local-storage": "2.0.0-beta.1",
|
||||
"@verdaccio/streams": "2.0.0-beta.0",
|
||||
"JSONStream": "1.3.5",
|
||||
"async": "2.6.1",
|
||||
"body-parser": "1.18.3",
|
||||
"bunyan": "1.8.12",
|
||||
"chalk": "2.4.1",
|
||||
"commander": "2.18.0",
|
||||
"chalk": "2.4.2",
|
||||
"commander": "2.19.0",
|
||||
"compression": "1.7.3",
|
||||
"cookies": "0.7.2",
|
||||
"cors": "2.8.4",
|
||||
"date-fns": "1.29.0",
|
||||
"express": "4.16.3",
|
||||
"cookies": "0.7.3",
|
||||
"cors": "2.8.5",
|
||||
"date-fns": "1.30.1",
|
||||
"express": "4.16.4",
|
||||
"global": "4.3.2",
|
||||
"handlebars": "4.0.12",
|
||||
"http-errors": "1.7.1",
|
||||
"js-base64": "2.4.9",
|
||||
"js-base64": "2.5.1",
|
||||
"js-string-escape": "1.0.1",
|
||||
"js-yaml": "3.12.0",
|
||||
"jsonwebtoken": "8.3.0",
|
||||
"js-yaml": "3.12.1",
|
||||
"jsonwebtoken": "8.4.0",
|
||||
"lockfile": "1.0.4",
|
||||
"lodash": "4.17.11",
|
||||
"lunr-mutable-indexes": "2.3.1",
|
||||
"marked": "0.5.1",
|
||||
"mime": "2.3.1",
|
||||
"lunr-mutable-indexes": "2.3.2",
|
||||
"marked": "0.6.0",
|
||||
"mime": "2.4.0",
|
||||
"minimatch": "3.0.4",
|
||||
"mkdirp": "0.5.1",
|
||||
"mv": "2.1.1",
|
||||
"pkginfo": "0.4.1",
|
||||
"request": "2.88.0",
|
||||
"semver": "5.5.1",
|
||||
"semver": "5.6.0",
|
||||
"verdaccio-audit": "1.1.0",
|
||||
"verdaccio-htpasswd": "1.0.1",
|
||||
"verdaccio-htpasswd": "2.0.0-beta.0",
|
||||
"verror": "1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.2.3",
|
||||
"@babel/core": "7.2.2",
|
||||
"@babel/node": "7.2.2",
|
||||
"@babel/plugin-proposal-class-properties": "7.2.3",
|
||||
"@babel/plugin-proposal-decorators": "7.2.3",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.2.0",
|
||||
"@babel/plugin-proposal-function-sent": "7.2.0",
|
||||
"@babel/plugin-proposal-json-strings": "7.2.0",
|
||||
"@babel/plugin-proposal-numeric-separator": "7.2.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.2.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.2.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.2.0",
|
||||
"@babel/plugin-syntax-import-meta": "7.2.0",
|
||||
"@babel/plugin-transform-async-to-generator": "7.2.0",
|
||||
"@babel/plugin-transform-classes": "7.2.2",
|
||||
"@babel/plugin-transform-runtime": "7.2.0",
|
||||
"@babel/polyfill": "7.2.3",
|
||||
"@babel/preset-env": "7.2.3",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@babel/register": "7.0.0",
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@commitlint/cli": "7.2.1",
|
||||
"@commitlint/config-conventional": "7.1.2",
|
||||
"@material-ui/core": "3.9.0",
|
||||
"@material-ui/icons": "3.0.2",
|
||||
"@verdaccio/babel-preset": "0.0.4",
|
||||
"@verdaccio/types": "4.1.4",
|
||||
"autosuggest-highlight": "3.1.1",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-jest": "23.6.0",
|
||||
"babel-loader": "8.0.4",
|
||||
"babel-plugin-dynamic-import-node": "2.2.0",
|
||||
"babel-plugin-emotion": "9.2.10",
|
||||
"babel-plugin-flow-runtime": "0.17.0",
|
||||
"bundlesize": "0.17.0",
|
||||
"codecov": "3.1.0",
|
||||
"cross-env": "5.2.0",
|
||||
@ -154,7 +126,7 @@
|
||||
"typeface-roboto": "0.0.54",
|
||||
"url-loader": "1.1.1",
|
||||
"verdaccio-auth-memory": "0.0.4",
|
||||
"verdaccio-memory": "1.0.3",
|
||||
"verdaccio-memory": "2.0.0-beta.0",
|
||||
"webpack": "4.20.2",
|
||||
"webpack-bundle-analyzer": "3.0.2",
|
||||
"webpack-cli": "3.1.1",
|
||||
|
@ -12,6 +12,7 @@ import express from 'express';
|
||||
import { combineBaseUrl, getWebProtocol } from '../../lib/utils';
|
||||
import Search from '../../lib/search';
|
||||
import { HEADERS, HTTP_STATUS, WEB_TITLE } from '../../lib/constants';
|
||||
import { spliceURL } from '../../utils/string';
|
||||
|
||||
const { securityIframe } = require('../middleware');
|
||||
/* eslint new-cap:off */
|
||||
@ -52,9 +53,8 @@ module.exports = function(config, auth, storage) {
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/', function(req, res) {
|
||||
const installPath = _.get(config, 'url_prefix', '');
|
||||
const base = combineBaseUrl(getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol), req.get('host'), installPath);
|
||||
function renderHTML(req, res) {
|
||||
const base = combineBaseUrl(getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol), req.get('host'), config.url_prefix);
|
||||
const webPage = template
|
||||
.replace(/ToReplaceByVerdaccio/g, base)
|
||||
.replace(/ToReplaceByTitle/g, _.get(config, 'web.title') ? config.web.title : WEB_TITLE)
|
||||
@ -64,6 +64,20 @@ module.exports = function(config, auth, storage) {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
|
||||
res.send(webPage);
|
||||
}
|
||||
|
||||
router.get('/-/web/:pkg', function(req, res) {
|
||||
renderHTML(req, res);
|
||||
});
|
||||
|
||||
router.get('/-/verdaccio/logo', function(req, res) {
|
||||
const installPath = _.get(config, 'url_prefix', '');
|
||||
|
||||
res.send(_.get(config, 'web.logo') || spliceURL(installPath, '/-/static/logo.png'));
|
||||
});
|
||||
|
||||
router.get('/', function(req, res) {
|
||||
renderHTML(req, res);
|
||||
});
|
||||
|
||||
return router;
|
||||
|
5
src/lib/bootstrap.js
vendored
5
src/lib/bootstrap.js
vendored
@ -48,7 +48,10 @@ function startVerdaccio(config: any, cliListen: string, configPath: string, pkgV
|
||||
// http
|
||||
webServer = http.createServer(app);
|
||||
}
|
||||
|
||||
if (config.server && config.server.keepAliveTimeout) {
|
||||
// $FlowFixMe library definition for node is not up to date (doesn't contain recent 8.0 changes)
|
||||
webServer.keepAliveTimeout = config.server.keepAliveTimeout;
|
||||
}
|
||||
unlinkAddressPath(addr);
|
||||
|
||||
callback(webServer, addr, pkgName, pkgVersion);
|
||||
|
@ -102,8 +102,10 @@ export const API_ERROR = {
|
||||
CONFIG_BAD_FORMAT: 'config file must be an object',
|
||||
BAD_USERNAME_PASSWORD: 'bad username/password, access denied',
|
||||
NO_PACKAGE: 'no such package available',
|
||||
PACKAGE_CANNOT_BE_ADDED: 'this package cannot be added',
|
||||
BAD_DATA: 'bad data',
|
||||
NOT_ALLOWED: 'not allowed to access package',
|
||||
NOT_ALLOWED_PUBLISH: 'not allowed to publish package',
|
||||
INTERNAL_SERVER_ERROR: 'internal server error',
|
||||
UNKNOWN_ERROR: 'unknown error',
|
||||
NOT_PACKAGE_UPLINK: 'package does not exist on uplink',
|
||||
|
@ -169,7 +169,7 @@ class LocalStorage implements IStorage {
|
||||
}
|
||||
}
|
||||
|
||||
if ('time' in packageInfo) {
|
||||
if ('time' in packageInfo && !_.isEqual(packageLocalJson.time, packageInfo.time)) {
|
||||
packageLocalJson.time = packageInfo.time;
|
||||
change = true;
|
||||
}
|
||||
@ -631,29 +631,6 @@ class LocalStorage implements IStorage {
|
||||
});
|
||||
}
|
||||
|
||||
_getCustomPackageLocalStorages() {
|
||||
const storages = {};
|
||||
|
||||
// add custom storage if exist
|
||||
if (this.config.storage) {
|
||||
storages[this.config.storage] = true;
|
||||
}
|
||||
|
||||
const { packages } = this.config;
|
||||
|
||||
if (packages) {
|
||||
const listPackagesConf = Object.keys(packages);
|
||||
|
||||
listPackagesConf.map(pkg => {
|
||||
if (packages[pkg].storage) {
|
||||
storages[packages[pkg].storage] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return storages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks through each package and calls `on_package` on them.
|
||||
* @param {*} onPackage
|
||||
|
@ -45,6 +45,10 @@ export function normalizePackage(pkg: Package) {
|
||||
pkg._rev = STORAGE.DEFAULT_REVISION;
|
||||
}
|
||||
|
||||
if (_.isString(pkg._id) === false) {
|
||||
pkg._id = pkg.name;
|
||||
}
|
||||
|
||||
// normalize dist-tags
|
||||
normalizeDistTags(pkg);
|
||||
|
||||
@ -93,7 +97,7 @@ export function normalizeContributors(contributors: Array<Author>): Array<Author
|
||||
return contributors;
|
||||
}
|
||||
|
||||
export const WHITELIST = ['_rev', 'name', 'versions', 'dist-tags', 'readme', 'time'];
|
||||
export const WHITELIST = ['_rev', 'name', 'versions', 'dist-tags', 'readme', 'time', '_id'];
|
||||
|
||||
export function cleanUpLinksRef(keepUpLinkData: boolean, result: Package): Package {
|
||||
const propertyToKeep = [...WHITELIST];
|
||||
|
@ -497,6 +497,10 @@ class Storage implements IStorageHandler {
|
||||
return callback(ErrorCode.getNotFound(API_ERROR.NO_PACKAGE), null, upLinksErrors);
|
||||
}
|
||||
|
||||
if (upLinks.length === 0) {
|
||||
return callback(null, packageInfo);
|
||||
}
|
||||
|
||||
self.localStorage.updateVersions(name, packageInfo, function(err, packageJsonLocal: Package) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
|
@ -4,16 +4,16 @@ import isNil from 'lodash/isNil';
|
||||
import storage from './utils/storage';
|
||||
import { makeLogin, isTokenExpire } from './utils/login';
|
||||
|
||||
import Footer from './components/Footer';
|
||||
import Loading from './components/Loading';
|
||||
import LoginModal from './components/Login';
|
||||
import Header from './components/Header';
|
||||
import { Container, Content } from './components/Layout';
|
||||
import Route from './router';
|
||||
import RouterApp from './router';
|
||||
import API from './utils/api';
|
||||
import './styles/typeface-roboto.scss';
|
||||
import './styles/main.scss';
|
||||
import 'normalize.css';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
export const AppContext = React.createContext();
|
||||
|
||||
@ -67,7 +67,8 @@ export default class App extends Component {
|
||||
isLoading: false,
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleShowAlertDialog({
|
||||
// FIXME: add dialog
|
||||
console.error({
|
||||
title: 'Warning',
|
||||
message: `Unable to load package list: ${error.message}`,
|
||||
});
|
||||
@ -140,15 +141,14 @@ export default class App extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoading, isUserLoggedIn, packages } = this.state;
|
||||
const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state;
|
||||
return (
|
||||
<Container isLoading={isLoading}>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<Fragment>
|
||||
<AppContextProvider value={{isUserLoggedIn, packages}}>
|
||||
{this.renderHeader()}
|
||||
<AppContextProvider value={{isUserLoggedIn, packages, logoUrl, user, scope}}>
|
||||
{this.renderContent()}
|
||||
</AppContextProvider>
|
||||
</Fragment>
|
||||
@ -175,7 +175,11 @@ export default class App extends Component {
|
||||
return (
|
||||
<Fragment>
|
||||
<Content>
|
||||
<Route></Route>
|
||||
<RouterApp
|
||||
onLogout={this.handleLogout}
|
||||
onToggleLoginModal={this.handleToggleLoginModal}>
|
||||
{this.renderHeader()}
|
||||
</RouterApp>
|
||||
</Content>
|
||||
<Footer />
|
||||
</Fragment>
|
||||
|
@ -34,7 +34,7 @@ class DepDetail extends Component<any, any> {
|
||||
const { onLoading, history } = this.props;
|
||||
|
||||
onLoading();
|
||||
history.push(`/version/${name}`);
|
||||
history.push(`/-/web/version/${name}`);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import type { Node } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Button from '@material-ui/core/Button/index';
|
||||
import IconButton from '@material-ui/core/IconButton/index';
|
||||
@ -17,7 +18,7 @@ import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import { default as IconSearch } from '@material-ui/icons/Search';
|
||||
|
||||
import { getRegistryURL } from '../../utils/url';
|
||||
import Link from '../Link';
|
||||
import ExternalLink from '../Link';
|
||||
import Logo from '../Logo';
|
||||
import RegistryInfoDialog from '../RegistryInfoDialog';
|
||||
import Label from '../Label';
|
||||
@ -138,7 +139,7 @@ class Header extends Component<IProps, IState> {
|
||||
switch (type) {
|
||||
case 'help':
|
||||
content = (
|
||||
<IconButton blank={true} color={'inherit'} component={Link} to={'https://verdaccio.org/docs/en/installation'}>
|
||||
<IconButton blank={true} color={'inherit'} component={ExternalLink} to={'https://verdaccio.org/docs/en/installation'}>
|
||||
<Help />
|
||||
</IconButton>
|
||||
);
|
||||
|
@ -9,7 +9,9 @@ export const Content = styled.div`
|
||||
&& {
|
||||
background-color: #ffffff;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
|
1
src/webui/components/NotFound/img/package.svg
Normal file
1
src/webui/components/NotFound/img/package.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 495.2 495.2"><path fill="#d38c0d" d="M325.6 224.4L495.2 126 325.6 28.4l-168.8 99.2z"/><g fill="#efbb67"><path d="M170.4 224.4l168-97.6-168-98.4L0 126.8z"/><path d="M416 368.4l-168 98.4-168-98.4v-196L248 74l168 98.4z"/></g><path fill="#d38c0d" d="M248 74l168 98.4v196l-168 98.4"/><path fill="#efbb67" d="M326.4 314.8L495.2 218l-169.6-98.4L156 218z"/><path fill="#d38c0d" d="M170.4 316.4l168.8-99.2-168.8-97.6L0 218z"/><path fill="#704a0e" d="M248.8 270.8L416 172.4 248.8 74 78.4 172.4z"/><path fill="#513307" d="M248.8 270.8L416 172.4 248.8 74"/><path fill="#2d1c05" d="M248.8 270.8l36-21.6-36-20.8-36 20.8z"/><g fill="#0dd396"><path d="M368 379.6l40-23.2v-12.8l-40 23.2zM368 356.4l40-23.2v-12.8l-40 23.2zM368 333.2l40-23.2v-13.6l-40 24z"/></g></svg>
|
After Width: | Height: | Size: 802 B |
@ -1,21 +1,53 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Wrapper } from './styles';
|
||||
import { IProps } from './types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth/index';
|
||||
import ListItem from '@material-ui/core/ListItem/index';
|
||||
import Typography from '@material-ui/core/Typography/index';
|
||||
import { Wrapper, Inner, EmptyPackage, Heading, Card, List } from './styles';
|
||||
import PackageImg from './img/package.svg';
|
||||
|
||||
const NotFound = ({ pkg }: IProps) => (
|
||||
<Wrapper>
|
||||
<h1>
|
||||
{'Error 404 - '}
|
||||
{pkg}
|
||||
</h1>
|
||||
<hr />
|
||||
<p>{'Oops, The package you are trying to access does not exist.'}</p>
|
||||
</Wrapper>
|
||||
);
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const NotFound = ({ history, width }) => {
|
||||
const handleGoTo = to => () => {
|
||||
history.push(to);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
const handleGoBack = () => () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
const renderList = () => (
|
||||
<List>
|
||||
<ListItem button={true} divider={true} onClick={handleGoTo('/')}>
|
||||
{'Home'}
|
||||
</ListItem>
|
||||
<ListItem button={true} divider={true} onClick={handleGoBack()}>
|
||||
{'Back'}
|
||||
</ListItem>
|
||||
</List>
|
||||
);
|
||||
|
||||
const renderSubTitle = () => (
|
||||
<Typography variant={'subtitle1'}>
|
||||
<div>{"The page you're looking for doesn't exist."}</div>
|
||||
<div>{'Perhaps these links will help find what you are looking for:'}</div>
|
||||
</Typography>
|
||||
);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Inner>
|
||||
<EmptyPackage alt={'404 - Page not found'} src={PackageImg} />
|
||||
<Heading variant={isWidthUp('sm', width) ? 'h2' : 'h4'}>{"Sorry, we couldn't find it..."}</Heading>
|
||||
{renderSubTitle()}
|
||||
<Card>{renderList()}</Card>
|
||||
</Inner>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(withWidth()(NotFound));
|
||||
|
@ -4,16 +4,43 @@
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
import Typography from '@material-ui/core/Typography/index';
|
||||
import { default as MuiList } from '@material-ui/core/List/index';
|
||||
import { default as MuiCard } from '@material-ui/core/Card/index';
|
||||
|
||||
import { fontSize, lineHeight } from '../../utils/styles/sizes';
|
||||
export const Wrapper = styled('div')`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
`;
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
export const Inner = styled('div')`
|
||||
max-width: 650px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export const EmptyPackage = styled('img')`
|
||||
width: 150px;
|
||||
margin: 0 auto;
|
||||
`;
|
||||
|
||||
export const Heading = styled(Typography)`
|
||||
&& {
|
||||
font-size: ${fontSize.md};
|
||||
line-height: ${lineHeight.xl};
|
||||
border: none;
|
||||
outline: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #4b5e40;
|
||||
}
|
||||
`;
|
||||
|
||||
export const List = styled(MuiList)`
|
||||
&& {
|
||||
padding: 0;
|
||||
color: #4b5e40;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Card = styled(MuiCard)`
|
||||
margin-top: 24px;
|
||||
`;
|
||||
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export interface IProps {
|
||||
pkg: string;
|
||||
}
|
@ -80,7 +80,7 @@ const Package = ({ name: label, version, time, author: { name, avatar }, descrip
|
||||
);
|
||||
|
||||
return (
|
||||
<WrapperLink className={'package'} to={`version/${label}`}>
|
||||
<WrapperLink className={'package'} to={`/-/web/version/${label}`}>
|
||||
<Header>
|
||||
{renderMainInfo()}
|
||||
<Overview>
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import type { Node } from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { default as IconSearch } from '@material-ui/icons/Search';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
@ -13,7 +14,6 @@ import debounce from 'lodash/debounce';
|
||||
import API from '../../utils/api';
|
||||
import AutoComplete from '../AutoComplete';
|
||||
import colors from '../../utils/styles/colors';
|
||||
import { getDetailPageURL } from '../../utils/url';
|
||||
|
||||
import { IProps, IState } from './types';
|
||||
import type { cancelAllSearchRequests, handlePackagesClearRequested, handleSearch, handleClickSearch, handleFetchPackages, onBlur } from './types';
|
||||
@ -24,7 +24,7 @@ const CONSTANTS = {
|
||||
ABORT_ERROR: 'AbortError',
|
||||
};
|
||||
|
||||
class Search extends Component<IProps, IState> {
|
||||
export class Search extends Component<IProps, IState> {
|
||||
requestList: Array<any>;
|
||||
|
||||
constructor(props: IProps) {
|
||||
@ -92,13 +92,15 @@ class Search extends Component<IProps, IState> {
|
||||
* When an user select any package by clicking or pressing return key.
|
||||
*/
|
||||
handleClickSearch: handleClickSearch = (event, { suggestionValue, method }) => {
|
||||
const { history } = this.props;
|
||||
// stops event bubbling
|
||||
event.stopPropagation();
|
||||
switch (method) {
|
||||
case 'click':
|
||||
case 'enter':
|
||||
this.setState({ search: '' });
|
||||
window.location.assign(getDetailPageURL(suggestionValue));
|
||||
// $FlowFixMe
|
||||
history.push(`/-/web/version/${suggestionValue}`);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@ -181,4 +183,4 @@ class Search extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
export default Search;
|
||||
export default withRouter(Search);
|
||||
|
@ -3,7 +3,9 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export interface IProps {}
|
||||
export interface IProps {
|
||||
history?: any;
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
search: string;
|
||||
|
@ -2,9 +2,9 @@
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import { createHashHistory } from 'history';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
const history = createHashHistory();
|
||||
const history = createBrowserHistory();
|
||||
|
||||
// Listen for changes to the current location.
|
||||
history.listen((location, action) => {
|
||||
|
@ -9,6 +9,7 @@ import Loading from '../../components/Loading';
|
||||
import DetailContainer from '../../components/DetailContainer';
|
||||
import DetailSidebar from '../../components/DetailSidebar';
|
||||
import { callDetailPage } from '../../utils/calls';
|
||||
import { getRouterPackageName } from '../../utils/package';
|
||||
|
||||
export const DetailContext = React.createContext();
|
||||
|
||||
@ -21,7 +22,7 @@ class VersionPage extends Component<any, any> {
|
||||
|
||||
this.state = {
|
||||
readMe: '',
|
||||
packageName: this.getPackageName(props),
|
||||
packageName: getRouterPackageName(props.match),
|
||||
packageMeta: null,
|
||||
isLoading: true,
|
||||
notFound: false,
|
||||
@ -32,13 +33,6 @@ class VersionPage extends Component<any, any> {
|
||||
await this.loadPackageInfo();
|
||||
}
|
||||
|
||||
getPackageName(props: any = this.props): string {
|
||||
const { match } = props;
|
||||
const packageName = match.params.package;
|
||||
|
||||
return packageName;
|
||||
}
|
||||
|
||||
/* eslint no-unused-vars: 0 */
|
||||
async componentDidUpdate(nextProps: any, prevState: any) {
|
||||
const { packageName } = this.state;
|
||||
@ -56,7 +50,7 @@ class VersionPage extends Component<any, any> {
|
||||
|
||||
static getDerivedStateFromProps(nextProps: any, prevState: any) {
|
||||
const { match } = nextProps;
|
||||
const packageName = match.params.package;
|
||||
const packageName = getRouterPackageName(match);
|
||||
|
||||
if (packageName !== prevState.packageName) {
|
||||
try {
|
||||
|
@ -3,39 +3,52 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
/* eslint react/jsx-max-depth:0 */
|
||||
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Router, Route, Switch } from 'react-router-dom';
|
||||
import { AppContextConsumer } from './app';
|
||||
|
||||
import { asyncComponent } from './utils/asyncComponent';
|
||||
import history from './history';
|
||||
import Header from './components/Header';
|
||||
|
||||
const NotFound = asyncComponent(() => import('./components/NotFound'));
|
||||
const DetailPackage = asyncComponent(() => import('./pages/detail'));
|
||||
const VersionPackage = asyncComponent(() => import('./pages/version'));
|
||||
const HomePage = asyncComponent(() => import('./pages/home'));
|
||||
|
||||
interface IProps {
|
||||
isUserLoggedIn: boolean;
|
||||
packages: Array<Object>;
|
||||
}
|
||||
|
||||
interface IState {}
|
||||
|
||||
class RouterApp extends Component<IProps, IState> {
|
||||
class RouterApp extends Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route exact={true} path={'/'} render={this.renderHomePage} />
|
||||
<Route exact={true} path={'/detail/@:scope/:package'} render={this.renderDetailPage} />
|
||||
<Route exact={true} path={'/detail/:package'} render={this.renderDetailPage} />
|
||||
<Route exact={true} path={'/version/@:scope/:package'} render={this.renderVersionPage} />
|
||||
<Route exact={true} path={'/version/:package'} render={this.renderVersionPage} />
|
||||
</Switch>
|
||||
<Fragment>
|
||||
{this.renderHeader()}
|
||||
<Switch>
|
||||
<Route exact={true} path={'/'} render={this.renderHomePage} />
|
||||
<Route exact={true} path={'/-/web/detail/@:scope/:package'} render={this.renderDetailPage} />
|
||||
<Route exact={true} path={'/-/web/detail/:package'} render={this.renderDetailPage} />
|
||||
<Route exact={true} path={'/-/web/version/@:scope/:package'} render={this.renderVersionPage} />
|
||||
<Route exact={true} path={'/-/web/version/:package'} render={this.renderVersionPage} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</Fragment>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const { onLogout, onToggleLoginModal } = this.props;
|
||||
|
||||
return (
|
||||
<AppContextConsumer>
|
||||
{function renderConsumerVersionPage({ logoUrl, scope, user }) {
|
||||
return <Header logo={logoUrl} onLogout={onLogout} onToggleLoginModal={onToggleLoginModal} scope={scope} username={user.username} />;
|
||||
}}
|
||||
</AppContextConsumer>
|
||||
);
|
||||
};
|
||||
|
||||
renderHomePage = () => {
|
||||
return (
|
||||
<AppContextConsumer>
|
||||
|
@ -106,3 +106,13 @@ export function formatDate(lastUpdate) {
|
||||
export function formatDateDistance(lastUpdate) {
|
||||
return distanceInWordsToNow(new Date(lastUpdate));
|
||||
}
|
||||
|
||||
export function getRouterPackageName(match) {
|
||||
const packageName = match.params.package;
|
||||
const scope = match.params.scope;
|
||||
if (scope) {
|
||||
return `@${scope}/${packageName}`;
|
||||
}
|
||||
|
||||
return packageName;
|
||||
}
|
||||
|
@ -8,5 +8,5 @@ export function getRegistryURL() {
|
||||
* @param {string} packageName
|
||||
*/
|
||||
export function getDetailPageURL(packageName) {
|
||||
return `${getRegistryURL()}/#/detail/${packageName}`;
|
||||
return `${getRegistryURL()}/-/web/version/${packageName}`;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
const scopedPackageMetadata = require('./partials/pkg-scoped');
|
||||
const protectedPackageMetadata = require('./partials/pkg-protected');
|
||||
|
||||
describe('/ (Verdaccio Page)', () => {
|
||||
describe.skip('/ (Verdaccio Page)', () => {
|
||||
let page;
|
||||
// this might be increased based on the delays included in all test
|
||||
jest.setTimeout(200000);
|
||||
@ -139,7 +139,7 @@ describe('/ (Verdaccio Page)', () => {
|
||||
});
|
||||
|
||||
test('should contains last sync information', async () => {
|
||||
const versionList = await page.$$('.sidebar-info .last-sync-item');
|
||||
const versionList = await page.$$('.sidebar-info .detail-info');
|
||||
expect(versionList).toHaveLength(3);
|
||||
});
|
||||
|
||||
|
@ -13,17 +13,17 @@ export default function(server) {
|
||||
* Check whether the user is allowed to fetch packages
|
||||
* @param auth {object} disable auth
|
||||
* @param pkg {string} package name
|
||||
* @param ok {boolean}
|
||||
* @param status {boolean}
|
||||
*/
|
||||
function checkAccess(auth, pkg, ok) {
|
||||
function checkAccess(auth, pkg, status) {
|
||||
test(
|
||||
`${(ok ? 'allows' : 'forbids')} access ${auth} to ${pkg}`, () => {
|
||||
`${(status ? 'allows' : 'forbids')} access ${auth} to ${pkg}`, () => {
|
||||
server.authstr = auth ? buildAccesToken(auth) : undefined;
|
||||
const req = server.getPackage(pkg);
|
||||
|
||||
if (ok) {
|
||||
if (status === HTTP_STATUS.NOT_FOUND) {
|
||||
return req.status(HTTP_STATUS.NOT_FOUND).body_error(API_ERROR.NO_PACKAGE);
|
||||
} else {
|
||||
} else if (status === HTTP_STATUS.FORBIDDEN) {
|
||||
return req.status(HTTP_STATUS.FORBIDDEN).body_error(API_ERROR.NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
@ -34,16 +34,20 @@ export default function(server) {
|
||||
* Check whether the user is allowed to publish packages
|
||||
* @param auth {object} disable auth
|
||||
* @param pkg {string} package name
|
||||
* @param ok {boolean}
|
||||
* @param status {boolean}
|
||||
*/
|
||||
function checkPublish(auth, pkg, ok) {
|
||||
test(`${(ok ? 'allows' : 'forbids')} publish ${auth} to ${pkg}`, () => {
|
||||
function checkPublish(auth, pkg, status) {
|
||||
test(`${(status ? 'allows' : 'forbids')} publish ${auth} to ${pkg}`, () => {
|
||||
server.authstr = auth ? buildAccesToken(auth) : undefined;
|
||||
const req = server.putPackage(pkg, require('../fixtures/package')(pkg));
|
||||
if (ok) {
|
||||
return req.status(HTTP_STATUS.NOT_FOUND).body_error(/this package cannot be added/);
|
||||
} else {
|
||||
return req.status(HTTP_STATUS.FORBIDDEN).body_error(/not allowed to publish package/);
|
||||
if (status === HTTP_STATUS.NOT_FOUND) {
|
||||
return req.status(HTTP_STATUS.NOT_FOUND).body_error(API_ERROR.PACKAGE_CANNOT_BE_ADDED);
|
||||
} else if (status === HTTP_STATUS.FORBIDDEN) {
|
||||
return req.status(HTTP_STATUS.FORBIDDEN).body_error(API_ERROR.NOT_ALLOWED_PUBLISH);
|
||||
} else if (status === HTTP_STATUS.CREATED) {
|
||||
return req.status(HTTP_STATUS.CREATED);
|
||||
} else if (status === HTTP_STATUS.CONFLICT) {
|
||||
return req.status(HTTP_STATUS.CONFLICT);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -60,39 +64,39 @@ export default function(server) {
|
||||
const testOnlyAuth = 'test-only-auth';
|
||||
|
||||
describe('all are allowed to access', () => {
|
||||
checkAccess(validCredentials, testAccessOnly, true);
|
||||
checkAccess(undefined, testAccessOnly, true);
|
||||
checkAccess(badCredentials, testAccessOnly, true);
|
||||
checkPublish(validCredentials, testAccessOnly, false);
|
||||
checkPublish(undefined, testAccessOnly, false);
|
||||
checkPublish(badCredentials, testAccessOnly, false);
|
||||
checkAccess(validCredentials, testAccessOnly, HTTP_STATUS.NOT_FOUND);
|
||||
checkAccess(undefined, testAccessOnly, HTTP_STATUS.NOT_FOUND);
|
||||
checkAccess(badCredentials, testAccessOnly, HTTP_STATUS.NOT_FOUND);
|
||||
checkPublish(validCredentials, testAccessOnly, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(undefined, testAccessOnly, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(badCredentials, testAccessOnly, HTTP_STATUS.FORBIDDEN);
|
||||
});
|
||||
|
||||
describe('all are allowed to publish', () => {
|
||||
checkAccess(validCredentials, testPublishOnly, false);
|
||||
checkAccess(undefined, testPublishOnly, false);
|
||||
checkAccess(badCredentials, testPublishOnly, false);
|
||||
checkPublish(validCredentials, testPublishOnly, true);
|
||||
checkPublish(undefined, testPublishOnly, true);
|
||||
checkPublish(badCredentials, testPublishOnly, true);
|
||||
checkAccess(validCredentials, testPublishOnly, HTTP_STATUS.FORBIDDEN);
|
||||
checkAccess(undefined, testPublishOnly, HTTP_STATUS.FORBIDDEN);
|
||||
checkAccess(badCredentials, testPublishOnly, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(validCredentials, testPublishOnly, HTTP_STATUS.CREATED);
|
||||
checkPublish(undefined, testPublishOnly, HTTP_STATUS.CONFLICT);
|
||||
checkPublish(badCredentials, testPublishOnly, HTTP_STATUS.CONFLICT);
|
||||
});
|
||||
|
||||
describe('only user "test" is allowed to publish and access', () => {
|
||||
checkAccess(validCredentials, testOnlyTest, true);
|
||||
checkAccess(undefined, testOnlyTest, false);
|
||||
checkAccess(badCredentials, testOnlyTest, false);
|
||||
checkPublish(validCredentials, testOnlyTest, true);
|
||||
checkPublish(undefined, testOnlyTest, false);
|
||||
checkPublish(badCredentials, testOnlyTest, false);
|
||||
checkAccess(validCredentials, testOnlyTest, HTTP_STATUS.NOT_FOUND);
|
||||
checkAccess(undefined, testOnlyTest, HTTP_STATUS.FORBIDDEN);
|
||||
checkAccess(badCredentials, testOnlyTest, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(validCredentials, testOnlyTest, HTTP_STATUS.CREATED);
|
||||
checkPublish(undefined, testOnlyTest, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(badCredentials, testOnlyTest, HTTP_STATUS.FORBIDDEN);
|
||||
});
|
||||
|
||||
describe('only authenticated users are allowed', () => {
|
||||
checkAccess(validCredentials, testOnlyAuth, true);
|
||||
checkAccess(undefined, testOnlyAuth, false);
|
||||
checkAccess(badCredentials, testOnlyAuth, false);
|
||||
checkPublish(validCredentials, testOnlyAuth, true);
|
||||
checkPublish(undefined, testOnlyAuth, false);
|
||||
checkPublish(badCredentials, testOnlyAuth, false);
|
||||
checkAccess(validCredentials, testOnlyAuth, HTTP_STATUS.NOT_FOUND);
|
||||
checkAccess(undefined, testOnlyAuth, HTTP_STATUS.FORBIDDEN);
|
||||
checkAccess(badCredentials, testOnlyAuth, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(validCredentials, testOnlyAuth, HTTP_STATUS.CREATED);
|
||||
checkPublish(undefined, testOnlyAuth, HTTP_STATUS.FORBIDDEN);
|
||||
checkPublish(badCredentials, testOnlyAuth, HTTP_STATUS.FORBIDDEN);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ export default function (server, server2) {
|
||||
|
||||
const PKG_NAME = 'test-nullstorage2';
|
||||
const PKG_VERSION = '0.0.1';
|
||||
// const TARBALL = `${PKG_NAME}-file.name`;
|
||||
|
||||
describe('should test a scenario when tarball is being fetch from uplink', () => {
|
||||
|
||||
@ -38,8 +39,7 @@ export default function (server, server2) {
|
||||
|
||||
describe(`should succesfully publish ${PKG_NAME} package on server2`, () => {
|
||||
beforeAll(function() {
|
||||
return server2.putTarball(PKG_NAME, TARBALL, getBinary())
|
||||
.status(HTTP_STATUS.CREATED).body_ok(/.*/);
|
||||
return server2.putTarball(PKG_NAME, TARBALL, getBinary()).status(HTTP_STATUS.CREATED).body_ok(/.*/);
|
||||
});
|
||||
|
||||
beforeAll(function() {
|
||||
|
@ -19,6 +19,7 @@ uplinks:
|
||||
timeout: 100ms
|
||||
server2:
|
||||
url: http://localhost:55552/
|
||||
maxage: 0
|
||||
server3:
|
||||
url: http://localhost:55553/
|
||||
baduplink:
|
||||
@ -88,7 +89,7 @@ packages:
|
||||
access: $all
|
||||
publish: $all
|
||||
proxy: server2
|
||||
storage: false
|
||||
storage: sub_storage
|
||||
|
||||
'baduplink':
|
||||
access: $all
|
||||
@ -98,22 +99,22 @@ packages:
|
||||
'test-access-only':
|
||||
access: $all
|
||||
publish: nobody
|
||||
storage: false
|
||||
storage: sub_storage
|
||||
|
||||
'test-publish-only':
|
||||
access: nobody
|
||||
publish: $all
|
||||
storage: false
|
||||
storage: sub_storage
|
||||
|
||||
'test-only-test':
|
||||
access: test
|
||||
publish: test
|
||||
storage: false
|
||||
storage: sub_storage
|
||||
|
||||
'test-only-auth':
|
||||
access: $authenticated
|
||||
publish: $authenticated
|
||||
storage: false
|
||||
storage: sub_storage
|
||||
|
||||
'*':
|
||||
access: test $anonymous
|
||||
|
@ -25,6 +25,13 @@ describe('LocalStorage', () => {
|
||||
const pkgNameScoped = `@scope/${pkgName}-scope`;
|
||||
const tarballName: string = `${pkgName}-add-tarball-1.0.4.tgz`;
|
||||
const tarballName2: string = `${pkgName}-add-tarball-1.0.5.tgz`;
|
||||
|
||||
const getStorage = (LocalStorageClass = LocalStorage) => {
|
||||
const config: Config = new AppConfig(configExample);
|
||||
config.self_path = path.join('../partials/store');
|
||||
return new LocalStorageClass(config, Logger.logger);
|
||||
}
|
||||
|
||||
const getPackageMetadataFromStore = (pkgName: string) => {
|
||||
return new Promise((resolve) => {
|
||||
storage.getPackageMetadata(pkgName, (err, data ) => {
|
||||
@ -76,10 +83,7 @@ describe('LocalStorage', () => {
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
const config: Config = new AppConfig(configExample);
|
||||
config.self_path = path.join('../partials/store');
|
||||
|
||||
storage = new LocalStorage(config, Logger.logger);
|
||||
storage = getStorage();
|
||||
});
|
||||
|
||||
test('should be defined', () => {
|
||||
@ -249,16 +253,27 @@ describe('LocalStorage', () => {
|
||||
});
|
||||
|
||||
describe('LocalStorage::updateVersions', () => {
|
||||
test('should update versions from external source', async (done) => {
|
||||
const metadata = JSON.parse(readMetadata('metadata-update-versions-tags'));
|
||||
const pkgName = 'add-update-versions-test-1';
|
||||
const version = '1.0.2';
|
||||
await addPackageToStore(pkgName, generatePackageTemplate(pkgName));
|
||||
await addNewVersion(pkgName, '1.0.1');
|
||||
await addNewVersion(pkgName, version);
|
||||
const metadata = JSON.parse(readMetadata('metadata-update-versions-tags'));
|
||||
const pkgName = 'add-update-versions-test-1';
|
||||
const version = '1.0.2';
|
||||
let _storage;
|
||||
beforeEach(done => {
|
||||
class MockLocalStorage extends LocalStorage {}
|
||||
// $FlowFixMe
|
||||
MockLocalStorage.prototype._writePackage = jest.fn(LocalStorage.prototype._writePackage)
|
||||
_storage = getStorage(MockLocalStorage);
|
||||
rimRaf(path.join(configExample.storage, pkgName), async () => {
|
||||
await addPackageToStore(pkgName, generatePackageTemplate(pkgName));
|
||||
await addNewVersion(pkgName, '1.0.1');
|
||||
await addNewVersion(pkgName, version);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
storage.updateVersions(pkgName, metadata, (err, data) => {
|
||||
test('should update versions from external source', async (done) => {
|
||||
_storage.updateVersions(pkgName, metadata, (err, data) => {
|
||||
expect(err).toBeNull();
|
||||
expect(_storage._writePackage).toHaveBeenCalledTimes(1);
|
||||
expect(data.versions['1.0.1']).toBeDefined();
|
||||
expect(data.versions[version]).toBeDefined();
|
||||
expect(data.versions['1.0.4']).toBeDefined();
|
||||
@ -273,6 +288,17 @@ describe('LocalStorage', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should not update if the metadata match', done => {
|
||||
_storage.updateVersions(pkgName, metadata, e => {
|
||||
expect(e).toBeNull()
|
||||
_storage.updateVersions(pkgName, metadata, err => {
|
||||
expect(err).toBeNull()
|
||||
expect(_storage._writePackage).toHaveBeenCalledTimes(1);
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
describe('LocalStorage::changePackage', () => {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import rimraf from 'rimraf';
|
||||
// $FlowFixMe
|
||||
import configExample from '../partials/config/index';
|
||||
import AppConfig from '../../../src/lib/config';
|
||||
@ -16,12 +18,12 @@ import {DOMAIN_SERVERS} from '../../functional/config.functional';
|
||||
|
||||
setup(configExample.logs);
|
||||
|
||||
const storagePath = path.join(__dirname, '../partials/store/test-storage-store.spec');
|
||||
const mockServerPort: number = 55548;
|
||||
const generateStorage = async function(port = mockServerPort, configDefault = configExample) {
|
||||
const storageConfig = _.clone(configDefault);
|
||||
const storage = path.join(__dirname, '../partials/store/test-storage-store.spec');
|
||||
storageConfig.self_path = __dirname;
|
||||
storageConfig.storage = storage;
|
||||
storageConfig.storage = storagePath;
|
||||
storageConfig.uplinks = {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${port}`
|
||||
@ -37,8 +39,11 @@ const generateStorage = async function(port = mockServerPort, configDefault = co
|
||||
describe('StorageTest', () => {
|
||||
let mockRegistry;
|
||||
|
||||
beforeAll(async () => {
|
||||
mockRegistry = await mockServer(mockServerPort).init();
|
||||
beforeAll(done => {
|
||||
rimraf(storagePath, async () => {
|
||||
mockRegistry = await mockServer(mockServerPort).init();
|
||||
done()
|
||||
})
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
@ -90,5 +95,21 @@ describe('StorageTest', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should not touch if the package exists and has no uplinks', async (done) => {
|
||||
const storage: IStorageHandler = await generateStorage();
|
||||
const metadataSource = path.join(__dirname, '../partials/metadata');
|
||||
const metadataPath = path.join(storagePath, 'npm_test/package.json');
|
||||
fs.mkdirSync(path.join(storagePath, 'npm_test'));
|
||||
fs.writeFileSync(metadataPath, fs.readFileSync(metadataSource));
|
||||
const metadata = JSON.parse(fs.readFileSync(metadataPath).toString());
|
||||
// $FlowFixMe
|
||||
storage.localStorage.updateVersions = jest.fn(storage.localStorage.updateVersions);
|
||||
storage._syncUplinksMetadata('npm_test', metadata, {}, (err) => {
|
||||
expect(err).toBeNull();
|
||||
expect(storage.localStorage.updateVersions).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -82,7 +82,13 @@
|
||||
"beta": "1.0.2",
|
||||
"next": "1.0.4"
|
||||
},
|
||||
"time": {},
|
||||
"time": {
|
||||
"modified": "2019-01-29T03:20:04.000Z",
|
||||
"created": "2019-01-29T03:20:00.000Z",
|
||||
"1.0.1": "2019-01-29T03:20:01.000Z",
|
||||
"1.0.2": "2019-01-29T03:20:02.000Z",
|
||||
"1.0.4": "2019-01-29T03:20:04.000Z"
|
||||
},
|
||||
"_distfiles": {},
|
||||
"_attachments": {},
|
||||
"_uplinks": {},
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Header /> component with logged in state should load the component in logged in state 1`] = `"<div><header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-1jzwe6w e1ctrp128\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1pwdmmq e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1vacr9s e1ctrp123\\"><a href=\\"/\\" target=\\"_self\\" style=\\"margin-right: 1em;\\"><div class=\\"css-12nq0oo e18wxr160\\"></div></a></div><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-m61s5i e1ctrp122\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-85\\"></span></a><button class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-85\\"></span></button><button class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-account\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z\\"></path><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-85\\"></span></button></div></div></header></div>"`;
|
||||
exports[`<Header /> component with logged in state should load the component in logged in state 1`] = `"<div><header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-1jzwe6w e1ctrp128\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1pwdmmq e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1vacr9s e1ctrp123\\"><a style=\\"margin-right:1em\\" href=\\"/\\"><div class=\\"css-12nq0oo e18wxr160\\"></div></a></div><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-m61s5i e1ctrp122\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span></a><button class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span></button><button class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-account\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z\\"></path><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path></svg></span></button></div></div></header></div>"`;
|
||||
|
||||
exports[`<Header /> component with logged out state should load the component in logged out state 1`] = `"<div><header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-1jzwe6w e1ctrp128\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1pwdmmq e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1vacr9s e1ctrp123\\"><a href=\\"/\\" target=\\"_self\\" style=\\"margin-right: 1em;\\"><div class=\\"css-12nq0oo e18wxr160\\"></div></a></div><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-m61s5i e1ctrp122\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-85\\"></span></a><button class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-85\\"></span></button><button class=\\"MuiButtonBase-root-55 MuiButton-root-113 MuiButton-text-115 MuiButton-flat-118 MuiButton-colorInherit-134\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-login\\"><span class=\\"MuiButton-label-114\\">Login</span><span class=\\"MuiTouchRipple-root-85\\"></span></button></div></div></header></div>"`;
|
||||
exports[`<Header /> component with logged out state should load the component in logged out state 1`] = `"<div><header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-1jzwe6w e1ctrp128\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1pwdmmq e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1vacr9s e1ctrp123\\"><a style=\\"margin-right:1em\\" href=\\"/\\"><div class=\\"css-12nq0oo e18wxr160\\"></div></a></div><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-m61s5i e1ctrp122\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span></a><button class=\\"MuiButtonBase-root-55 MuiIconButton-root-49 MuiIconButton-colorInherit-50\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-54\\"><svg class=\\"MuiSvgIcon-root-58\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span></button><button class=\\"MuiButtonBase-root-55 MuiButton-root-85 MuiButton-text-87 MuiButton-flat-90 MuiButton-colorInherit-106\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-login\\"><span class=\\"MuiButton-label-86\\">Login</span></button></div></div></header></div>"`;
|
||||
|
@ -1,5 +1,163 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<NotFound /> component should load the component in default state 1`] = `"<div class=\\"css-24p7uw e1gvymku0\\"><h1>Error 404 - test</h1><hr><p>Oops, The package you are trying to access does not exist.</p></div>"`;
|
||||
|
||||
exports[`<NotFound /> component should set html from props 1`] = `"<div class=\\"css-24p7uw e1gvymku0\\"><h1>Error 404 - verdaccio</h1><hr/><p>Oops, The package you are trying to access does not exist.</p></div>"`;
|
||||
exports[`<NotFound /> component should load the component in default state 1`] = `
|
||||
ShallowWrapper {
|
||||
Symbol(enzyme.__root__): ShallowWrapper {
|
||||
Symbol(enzyme.__root__): [Circular],
|
||||
Symbol(enzyme.__unrendered__): <BrowserRouter>
|
||||
<withRouter(WithTheme(WithWidth(NotFound))) />
|
||||
</BrowserRouter>,
|
||||
Symbol(enzyme.__renderer__): Object {
|
||||
"batchedUpdates": [Function],
|
||||
"getNode": [Function],
|
||||
"render": [Function],
|
||||
"simulateError": [Function],
|
||||
"simulateEvent": [Function],
|
||||
"unmount": [Function],
|
||||
},
|
||||
Symbol(enzyme.__node__): Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"children": <withRouter(WithTheme(WithWidth(NotFound))) />,
|
||||
"history": Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"createHref": [Function],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"length": 1,
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
},
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "function",
|
||||
"props": Object {},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
"type": [Function],
|
||||
},
|
||||
Symbol(enzyme.__nodes__): Array [
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"children": <withRouter(WithTheme(WithWidth(NotFound))) />,
|
||||
"history": Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"createHref": [Function],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"length": 1,
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
},
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "function",
|
||||
"props": Object {},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
"type": [Function],
|
||||
},
|
||||
],
|
||||
Symbol(enzyme.__options__): Object {
|
||||
"adapter": ReactSixteenAdapter {
|
||||
"options": Object {
|
||||
"enableComponentDidUpdateOnSetState": true,
|
||||
"lifecycles": Object {
|
||||
"componentDidUpdate": Object {
|
||||
"onSetState": true,
|
||||
},
|
||||
"getDerivedStateFromProps": true,
|
||||
"getSnapshotBeforeUpdate": true,
|
||||
"setState": Object {
|
||||
"skipsComponentDidUpdateOnNullish": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"attachTo": undefined,
|
||||
"hydrateIn": undefined,
|
||||
},
|
||||
},
|
||||
Symbol(enzyme.__unrendered__): null,
|
||||
Symbol(enzyme.__renderer__): Object {
|
||||
"batchedUpdates": [Function],
|
||||
"getNode": [Function],
|
||||
"render": [Function],
|
||||
"simulateError": [Function],
|
||||
"simulateEvent": [Function],
|
||||
"unmount": [Function],
|
||||
},
|
||||
Symbol(enzyme.__node__): Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "function",
|
||||
"props": Object {},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
Symbol(enzyme.__nodes__): Array [
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "function",
|
||||
"props": Object {},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
],
|
||||
Symbol(enzyme.__options__): Object {
|
||||
"adapter": ReactSixteenAdapter {
|
||||
"options": Object {
|
||||
"enableComponentDidUpdateOnSetState": true,
|
||||
"lifecycles": Object {
|
||||
"componentDidUpdate": Object {
|
||||
"onSetState": true,
|
||||
},
|
||||
"getDerivedStateFromProps": true,
|
||||
"getSnapshotBeforeUpdate": true,
|
||||
"setState": Object {
|
||||
"skipsComponentDidUpdateOnNullish": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"attachTo": undefined,
|
||||
"hydrateIn": undefined,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
@ -1,3 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<PackageList /> component should load the component with packages 1`] = `"<div class=\\"package-list-items\\"><div class=\\"pkgContainer\\"><a class=\\"package css-zrrjf6 e11fsc2k16\\" href=\\"version/verdaccio\\"><div class=\\"css-esn5nr e11fsc2k0\\"><span class=\\"css-1e6w198 e11fsc2k2\\"><span class=\\"css-bxt2bt e11fsc2k1\\">verdaccio</span><span class=\\"css-17xn9wj e11fsc2k5\\">v1.0.0</span></span><span class=\\"css-1dq57rh e11fsc2k4\\"><span class=\\"css-19brcdm e11fsc2k3\\"><svg class=\\"e11fsc2k6 css-y8pkl4 ej4jd2o0\\"><title>Time</title><use xlink:href=\\"[object Object]#time\\"></use></svg><span class=\\"css-1qw5qv3 e11fsc2k7\\">Published on 21.07.2018, 22:11:12 •</span>6 months ago</span></span></div><div class=\\"css-tywa7u e11fsc2k9\\"><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-15496ft e11fsc2k12\\"><div class=\\"MuiAvatar-root-128 MuiAvatar-colorDefault-129 css-1to0t9u e11fsc2k13\\">S</div><span class=\\"css-1xj37ub e11fsc2k11\\"><div class=\\"e11fsc2k10 css-1xe0n7g e1pneb170\\">Sam</div></span></div></div><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-1b14dls e11fsc2k14\\">Private NPM repository</div></div></div></a><a class=\\"package css-zrrjf6 e11fsc2k16\\" href=\\"version/abc\\"><div class=\\"css-esn5nr e11fsc2k0\\"><span class=\\"css-1e6w198 e11fsc2k2\\"><span class=\\"css-bxt2bt e11fsc2k1\\">abc</span><span class=\\"css-17xn9wj e11fsc2k5\\">v1.0.1</span></span><span class=\\"css-1dq57rh e11fsc2k4\\"><span class=\\"css-19brcdm e11fsc2k3\\"><svg class=\\"e11fsc2k6 css-y8pkl4 ej4jd2o0\\"><title>Time</title><use xlink:href=\\"[object Object]#time\\"></use></svg><span class=\\"css-1qw5qv3 e11fsc2k7\\">Published on 21.07.2018, 22:11:12 •</span>6 months ago</span></span></div><div class=\\"css-tywa7u e11fsc2k9\\"><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-15496ft e11fsc2k12\\"><div class=\\"MuiAvatar-root-128 MuiAvatar-colorDefault-129 css-1to0t9u e11fsc2k13\\">R</div><span class=\\"css-1xj37ub e11fsc2k11\\"><div class=\\"e11fsc2k10 css-1xe0n7g e1pneb170\\">Rose</div></span></div></div><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-1b14dls e11fsc2k14\\">abc description</div></div></div></a><a class=\\"package css-zrrjf6 e11fsc2k16\\" href=\\"version/xyz\\"><div class=\\"css-esn5nr e11fsc2k0\\"><span class=\\"css-1e6w198 e11fsc2k2\\"><span class=\\"css-bxt2bt e11fsc2k1\\">xyz</span><span class=\\"css-17xn9wj e11fsc2k5\\">v1.1.0</span></span><span class=\\"css-1dq57rh e11fsc2k4\\"><span class=\\"css-19brcdm e11fsc2k3\\"><svg class=\\"e11fsc2k6 css-y8pkl4 ej4jd2o0\\"><title>Time</title><use xlink:href=\\"[object Object]#time\\"></use></svg><span class=\\"css-1qw5qv3 e11fsc2k7\\">Published on Invalid Date •</span>almost NaN years ago</span></span></div><div class=\\"css-tywa7u e11fsc2k9\\"><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-15496ft e11fsc2k12\\"><div class=\\"MuiAvatar-root-128 MuiAvatar-colorDefault-129 css-1to0t9u e11fsc2k13\\">M</div><span class=\\"css-1xj37ub e11fsc2k11\\"><div class=\\"e11fsc2k10 css-1xe0n7g e1pneb170\\">Martin</div></span></div></div><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-1b14dls e11fsc2k14\\">xyz description</div></div></div></a></div></div>"`;
|
||||
exports[`<PackageList /> component should load the component with packages 1`] = `"<div class=\\"package-list-items\\"><div class=\\"pkgContainer\\"><a class=\\"package css-zrrjf6 e11fsc2k16\\" href=\\"/-/web/version/verdaccio\\"><div class=\\"css-esn5nr e11fsc2k0\\"><span class=\\"css-1e6w198 e11fsc2k2\\"><span class=\\"css-bxt2bt e11fsc2k1\\">verdaccio</span><span class=\\"css-17xn9wj e11fsc2k5\\">v1.0.0</span></span><span class=\\"css-1dq57rh e11fsc2k4\\"><span class=\\"css-19brcdm e11fsc2k3\\"><svg class=\\"e11fsc2k6 css-y8pkl4 ej4jd2o0\\"><title>Time</title><use xlink:href=\\"[object Object]#time\\"></use></svg><span class=\\"css-1qw5qv3 e11fsc2k7\\">Published on 21.07.2018, 22:11:12 •</span>7 months ago</span></span></div><div class=\\"css-tywa7u e11fsc2k9\\"><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-15496ft e11fsc2k12\\"><div class=\\"MuiAvatar-root-128 MuiAvatar-colorDefault-129 css-1to0t9u e11fsc2k13\\">S</div><span class=\\"css-1xj37ub e11fsc2k11\\"><div class=\\"e11fsc2k10 css-1xe0n7g e1pneb170\\">Sam</div></span></div></div><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-1b14dls e11fsc2k14\\">Private NPM repository</div></div></div></a><a class=\\"package css-zrrjf6 e11fsc2k16\\" href=\\"/-/web/version/abc\\"><div class=\\"css-esn5nr e11fsc2k0\\"><span class=\\"css-1e6w198 e11fsc2k2\\"><span class=\\"css-bxt2bt e11fsc2k1\\">abc</span><span class=\\"css-17xn9wj e11fsc2k5\\">v1.0.1</span></span><span class=\\"css-1dq57rh e11fsc2k4\\"><span class=\\"css-19brcdm e11fsc2k3\\"><svg class=\\"e11fsc2k6 css-y8pkl4 ej4jd2o0\\"><title>Time</title><use xlink:href=\\"[object Object]#time\\"></use></svg><span class=\\"css-1qw5qv3 e11fsc2k7\\">Published on 21.07.2018, 22:11:12 •</span>7 months ago</span></span></div><div class=\\"css-tywa7u e11fsc2k9\\"><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-15496ft e11fsc2k12\\"><div class=\\"MuiAvatar-root-128 MuiAvatar-colorDefault-129 css-1to0t9u e11fsc2k13\\">R</div><span class=\\"css-1xj37ub e11fsc2k11\\"><div class=\\"e11fsc2k10 css-1xe0n7g e1pneb170\\">Rose</div></span></div></div><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-1b14dls e11fsc2k14\\">abc description</div></div></div></a><a class=\\"package css-zrrjf6 e11fsc2k16\\" href=\\"/-/web/version/xyz\\"><div class=\\"css-esn5nr e11fsc2k0\\"><span class=\\"css-1e6w198 e11fsc2k2\\"><span class=\\"css-bxt2bt e11fsc2k1\\">xyz</span><span class=\\"css-17xn9wj e11fsc2k5\\">v1.1.0</span></span><span class=\\"css-1dq57rh e11fsc2k4\\"><span class=\\"css-19brcdm e11fsc2k3\\"><svg class=\\"e11fsc2k6 css-y8pkl4 ej4jd2o0\\"><title>Time</title><use xlink:href=\\"[object Object]#time\\"></use></svg><span class=\\"css-1qw5qv3 e11fsc2k7\\">Published on Invalid Date •</span>almost NaN years ago</span></span></div><div class=\\"css-tywa7u e11fsc2k9\\"><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-15496ft e11fsc2k12\\"><div class=\\"MuiAvatar-root-128 MuiAvatar-colorDefault-129 css-1to0t9u e11fsc2k13\\">M</div><span class=\\"css-1xj37ub e11fsc2k11\\"><div class=\\"e11fsc2k10 css-1xe0n7g e1pneb170\\">Martin</div></span></div></div><div class=\\"css-hnjjgz e11fsc2k8\\"><div class=\\"css-1b14dls e11fsc2k14\\">xyz description</div></div></div></a></div></div>"`;
|
||||
|
@ -4,11 +4,14 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { shallow } from 'enzyme';
|
||||
import Header from '../../../../src/webui/components/Header';
|
||||
|
||||
describe('<Header /> component with logged in state', () => {
|
||||
let wrapper;
|
||||
let routerWrapper;
|
||||
let instance;
|
||||
let props;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -20,7 +23,13 @@ describe('<Header /> component with logged in state', () => {
|
||||
scope: 'test scope',
|
||||
withoutSearch: true,
|
||||
};
|
||||
wrapper = mount(<Header {...props} />);
|
||||
routerWrapper = shallow(
|
||||
<Router>
|
||||
<Header {...props} />
|
||||
</Router>
|
||||
);
|
||||
wrapper = routerWrapper.find(Header).dive();
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
test('should load the component in logged in state', () => {
|
||||
@ -30,13 +39,12 @@ describe('<Header /> component with logged in state', () => {
|
||||
registryUrl: 'http://localhost',
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
|
||||
expect(wrapper.state()).toEqual(state);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
expect(routerWrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('handleLoggedInMenu: set anchorEl to html element value in state', () => {
|
||||
const { handleLoggedInMenu } = wrapper.instance();
|
||||
|
||||
// creates a sample menu
|
||||
const div = document.createElement('div');
|
||||
const text = document.createTextNode('sample menu');
|
||||
@ -46,13 +54,15 @@ describe('<Header /> component with logged in state', () => {
|
||||
currentTarget: div,
|
||||
};
|
||||
|
||||
handleLoggedInMenu(event);
|
||||
instance.handleLoggedInMenu(event);
|
||||
expect(wrapper.state('anchorEl')).toEqual(div);
|
||||
});
|
||||
});
|
||||
|
||||
describe('<Header /> component with logged out state', () => {
|
||||
let wrapper;
|
||||
let routerWrapper;
|
||||
let instance;
|
||||
let props;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -63,7 +73,13 @@ describe('<Header /> component with logged out state', () => {
|
||||
logo: '',
|
||||
withoutSearch: true,
|
||||
};
|
||||
wrapper = mount(<Header {...props} />);
|
||||
routerWrapper = shallow(
|
||||
<Router>
|
||||
<Header {...props} />
|
||||
</Router>
|
||||
);
|
||||
wrapper = routerWrapper.find(Header).dive();
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
test('should load the component in logged out state', () => {
|
||||
@ -74,30 +90,26 @@ describe('<Header /> component with logged out state', () => {
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
expect(wrapper.state()).toEqual(state);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
expect(routerWrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('handleLoggedInMenuClose: set anchorEl value to null in state', () => {
|
||||
const { handleLoggedInMenuClose } = wrapper.instance();
|
||||
handleLoggedInMenuClose();
|
||||
instance.handleLoggedInMenuClose();
|
||||
expect(wrapper.state('anchorEl')).toBeNull();
|
||||
});
|
||||
|
||||
test('handleOpenRegistryInfoDialog: set openInfoDialog to be truthy in state', () => {
|
||||
const { handleOpenRegistryInfoDialog } = wrapper.instance();
|
||||
handleOpenRegistryInfoDialog();
|
||||
instance.handleOpenRegistryInfoDialog();
|
||||
expect(wrapper.state('openInfoDialog')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('handleCloseRegistryInfoDialog: set openInfoDialog to be falsy in state', () => {
|
||||
const { handleCloseRegistryInfoDialog } = wrapper.instance();
|
||||
handleCloseRegistryInfoDialog();
|
||||
instance.handleCloseRegistryInfoDialog();
|
||||
expect(wrapper.state('openInfoDialog')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('handleToggleLogin: close/open popover menu', () => {
|
||||
const { handleToggleLogin } = wrapper.instance();
|
||||
handleToggleLogin();
|
||||
instance.handleToggleLogin();
|
||||
expect(wrapper.state('anchorEl')).toBeNull();
|
||||
expect(props.onToggleLoginModal).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -3,26 +3,25 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { shallow } from 'enzyme';
|
||||
import NotFound from '../../../../src/webui/components/NotFound/index';
|
||||
|
||||
console.error = jest.fn();
|
||||
|
||||
describe('<NotFound /> component', () => {
|
||||
test('should load the component in default state', () => {
|
||||
const wrapper = mount(<NotFound pkg={"test"} />);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
let routerWrapper;
|
||||
beforeEach(() => {
|
||||
routerWrapper = shallow(
|
||||
<Router>
|
||||
<NotFound />
|
||||
</Router>
|
||||
);
|
||||
});
|
||||
|
||||
test('should set html from props', () => {
|
||||
const props = {
|
||||
pkg: 'verdaccio'
|
||||
};
|
||||
const wrapper = shallow(<NotFound {...props} />);
|
||||
expect(wrapper.find('h1').text()).toEqual('Error 404 - verdaccio');
|
||||
expect(wrapper.find('p').text()).toEqual(
|
||||
'Oops, The package you are trying to access does not exist.'
|
||||
);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
|
||||
test('should load the component in default state', () => {
|
||||
expect(routerWrapper.find(NotFound)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import Search from '../../../../src/webui/components/Search/index';
|
||||
import { Search } from '../../../../src/webui/components/Search/index';
|
||||
|
||||
const SEARCH_FILE_PATH = '../../../../src/webui/components/Search/index';
|
||||
const API_FILE_PATH = '../../../../src/webui/utils/api';
|
||||
@ -121,7 +121,7 @@ describe('<Search /> component: mocks specific tests ', () => {
|
||||
},
|
||||
}));
|
||||
|
||||
const Search = require(SEARCH_FILE_PATH).default;
|
||||
const Search = require(SEARCH_FILE_PATH).Search;
|
||||
const component = mount(<Search />);
|
||||
component.setState({ search: 'verdaccio' });
|
||||
const { handleFetchPackages } = component.instance();
|
||||
@ -138,7 +138,7 @@ describe('<Search /> component: mocks specific tests ', () => {
|
||||
|
||||
jest.doMock(API_FILE_PATH, () => ({ request: jest.fn(() => Promise.reject(apiResponse)) }));
|
||||
|
||||
const Search = require(SEARCH_FILE_PATH).default;
|
||||
const Search = require(SEARCH_FILE_PATH).Search;
|
||||
const component = mount(<Search />);
|
||||
component.setState({ search: 'verdaccio' });
|
||||
const { handleFetchPackages } = component.instance();
|
||||
@ -159,7 +159,7 @@ describe('<Search /> component: mocks specific tests ', () => {
|
||||
},
|
||||
}));
|
||||
|
||||
const Search = require(SEARCH_FILE_PATH).default;
|
||||
const Search = require(SEARCH_FILE_PATH).Search;
|
||||
const component = mount(<Search />);
|
||||
component.setState({ search: 'verdaccio' });
|
||||
const { handleFetchPackages } = component.instance();
|
||||
@ -175,20 +175,19 @@ describe('<Search /> component: mocks specific tests ', () => {
|
||||
jest.doMock(URL_FILE_PATH, () => ({ getDetailPageURL }));
|
||||
|
||||
const suggestionValue = [];
|
||||
const Search = require(SEARCH_FILE_PATH).default;
|
||||
const component = mount(<Search />);
|
||||
const Search = require(SEARCH_FILE_PATH).Search;
|
||||
const pushHandler = jest.fn();
|
||||
const component = mount(<Search history={{ push: pushHandler }} />);
|
||||
const { handleClickSearch } = component.instance();
|
||||
|
||||
// click
|
||||
handleClickSearch(event, { suggestionValue, method: 'click' });
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
expect(getDetailPageURL).toHaveBeenCalledWith(suggestionValue);
|
||||
expect(window.location.assign).toHaveBeenCalledWith('detail/page/url');
|
||||
expect(pushHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
// return key
|
||||
handleClickSearch(event, { suggestionValue, method: 'enter' });
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
expect(getDetailPageURL).toHaveBeenCalledWith(suggestionValue);
|
||||
expect(window.location.assign).toHaveBeenCalledWith('detail/page/url');
|
||||
expect(pushHandler).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
BIN
yarn.lock
BIN
yarn.lock
Binary file not shown.
Loading…
Reference in New Issue
Block a user