mirror of
https://github.com/SomboChea/ui
synced 2024-11-24 06:54:27 +07:00
* chore: refactoring version page * refactor: migrate version page to hooks * refactor: Version page better imports * fix: #100 render not found on click item * test: add test for version page * chore: update mocks * test: add scenario for not found package * chore: fix wrong mock path * chore: update mock * chore: add todo list
This commit is contained in:
parent
e7d3c461cd
commit
97e8448098
@ -1,36 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
name: 'verdaccio-ui-jest',
|
|
||||||
verbose: true,
|
|
||||||
collectCoverage: true,
|
|
||||||
testEnvironment: 'jest-environment-jsdom-global',
|
|
||||||
moduleFileExtensions: ['js', 'ts', 'tsx'],
|
|
||||||
testURL: 'http://localhost',
|
|
||||||
rootDir: '..',
|
|
||||||
setupFiles: ['<rootDir>/test/setup.js'],
|
|
||||||
transformIgnorePatterns: ['<rootDir>/node_modules/(?!react-syntax-highlighter)'],
|
|
||||||
modulePathIgnorePatterns: ['<rootDir>/coverage', '<rootDir>/scripts', '<rootDir>/.circleci', '<rootDir>/tools', '<rootDir>/build', '<rootDir>/.vscode/'],
|
|
||||||
snapshotSerializers: ['enzyme-to-json/serializer', 'jest-emotion'],
|
|
||||||
moduleNameMapper: {
|
|
||||||
'\\.(s?css)$': '<rootDir>/node_modules/identity-obj-proxy',
|
|
||||||
'github-markdown-css': '<rootDir>/node_modules/identity-obj-proxy',
|
|
||||||
'\\.(png)$': '<rootDir>/node_modules/identity-obj-proxy',
|
|
||||||
'\\.(svg)$': '<rootDir>/test/unit/empty.ts',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// module.exports = {
|
|
||||||
// name: 'verdaccio-unit-jest',
|
|
||||||
// verbose: true,
|
|
||||||
// collectCoverage: true,
|
|
||||||
// testEnvironment: 'jest-environment-jsdom-global',
|
|
||||||
// testURL: 'http://localhost',
|
|
||||||
// testRegex: '../src/components/CopyToClipBoard/CopyToClipBoard.test.tsx',
|
|
||||||
// setupFiles: ['./setup.ts'],
|
|
||||||
// // Some unit tests rely on data folders that look like packages. This confuses jest-hast-map
|
|
||||||
// // when it tries to scan for package.json files.
|
|
||||||
// modulePathIgnorePatterns: ['<rootDir>/coverage', '<rootDir>/scripts', '<rootDir>/.circleci', '<rootDir>/tools', '<rootDir>/build', '<rootDir>/.vscode/'],
|
|
||||||
// // testPathIgnorePatterns: ['__snapshots__', '<rootDir>/build'],
|
|
||||||
// snapshotSerializers: ['enzyme-to-json/serializer', 'jest-emotion'],
|
|
||||||
// // coveragePathIgnorePatterns: ['node_modules', 'fixtures', '<rootDir>/src/api/debug', '<rootDir>/test'],
|
|
||||||
// // transformIgnorePatterns: ['<rootDir>/node_modules/(?!react-syntax-highlighter)'],
|
|
||||||
// };
|
|
@ -3,6 +3,7 @@ const { defaults } = require('jest-config');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'verdaccio-ui-jest',
|
name: 'verdaccio-ui-jest',
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
automock: false,
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
testEnvironment: 'jest-environment-jsdom-global',
|
testEnvironment: 'jest-environment-jsdom-global',
|
||||||
moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'],
|
moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'],
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'raf/polyfill';
|
import 'raf/polyfill';
|
||||||
import { configure } from 'enzyme';
|
import { configure } from 'enzyme';
|
||||||
import Adapter from 'enzyme-adapter-react-16';
|
import Adapter from 'enzyme-adapter-react-16';
|
||||||
|
import { GlobalWithFetchMock } from 'jest-fetch-mock';
|
||||||
|
|
||||||
// @ts-ignore : Only a void function can be called with the 'new' keyword
|
// @ts-ignore : Only a void function can be called with the 'new' keyword
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
@ -14,6 +15,10 @@ global.__APP_VERSION__ = '1.0.0';
|
|||||||
// @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'.
|
// @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'.
|
||||||
global.__VERDACCIO_BASENAME_UI_OPTIONS = {};
|
global.__VERDACCIO_BASENAME_UI_OPTIONS = {};
|
||||||
|
|
||||||
|
const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock;
|
||||||
|
customGlobal.fetch = require('jest-fetch-mock');
|
||||||
|
customGlobal.fetchMock = customGlobal.fetch;
|
||||||
|
|
||||||
// mocking few DOM methods
|
// mocking few DOM methods
|
||||||
// @ts-ignore : Property 'document' does not exist on type 'Global'.
|
// @ts-ignore : Property 'document' does not exist on type 'Global'.
|
||||||
if (global.document) {
|
if (global.document) {
|
||||||
|
32
package.json
32
package.json
@ -16,24 +16,25 @@
|
|||||||
"@material-ui/core": "3.9.3",
|
"@material-ui/core": "3.9.3",
|
||||||
"@material-ui/icons": "3.0.2",
|
"@material-ui/icons": "3.0.2",
|
||||||
"@octokit/rest": "16.28.7",
|
"@octokit/rest": "16.28.7",
|
||||||
"@testing-library/react": "9.1.0",
|
"@testing-library/react": "9.1.3",
|
||||||
"@types/enzyme": "3.10.3",
|
"@types/enzyme": "3.10.3",
|
||||||
"@types/lodash": "4.14.136",
|
"@types/jest": "24.0.18",
|
||||||
|
"@types/lodash": "4.14.137",
|
||||||
"@types/material-ui": "0.21.6",
|
"@types/material-ui": "0.21.6",
|
||||||
"@types/node": "12.7.1",
|
"@types/node": "12.7.2",
|
||||||
"@types/react": "16.9.1",
|
"@types/react": "16.9.2",
|
||||||
"@types/react-dom": "16.8.5",
|
"@types/react-dom": "16.9.0",
|
||||||
"@types/react-router-dom": "4.3.4",
|
"@types/react-router-dom": "4.3.5",
|
||||||
"@types/validator": "10.11.2",
|
"@types/validator": "10.11.3",
|
||||||
"@verdaccio/babel-preset": "2.0.0",
|
"@verdaccio/babel-preset": "2.0.0",
|
||||||
"@verdaccio/eslint-config": "2.0.0",
|
"@verdaccio/eslint-config": "2.0.0",
|
||||||
"@verdaccio/types": "8.0.0-next.2",
|
"@verdaccio/types": "8.0.0",
|
||||||
"autosuggest-highlight": "3.1.1",
|
"autosuggest-highlight": "3.1.1",
|
||||||
"babel-loader": "8.0.6",
|
"babel-loader": "8.0.6",
|
||||||
"bundlesize": "0.18.0",
|
"bundlesize": "0.18.0",
|
||||||
"codeceptjs": "2.2.1",
|
"codeceptjs": "2.2.1",
|
||||||
"codecov": "3.5.0",
|
"codecov": "3.5.0",
|
||||||
"concurrently": "4.1.1",
|
"concurrently": "4.1.2",
|
||||||
"cross-env": "5.2.0",
|
"cross-env": "5.2.0",
|
||||||
"css-loader": "3.2.0",
|
"css-loader": "3.2.0",
|
||||||
"date-fns": "1.30.1",
|
"date-fns": "1.30.1",
|
||||||
@ -56,11 +57,12 @@
|
|||||||
"husky": "3.0.3",
|
"husky": "3.0.3",
|
||||||
"identity-obj-proxy": "3.0.0",
|
"identity-obj-proxy": "3.0.0",
|
||||||
"in-publish": "2.0.0",
|
"in-publish": "2.0.0",
|
||||||
"jest": "24.8.0",
|
"jest": "24.9.0",
|
||||||
"jest-emotion": "10.0.14",
|
"jest-emotion": "10.0.14",
|
||||||
"jest-environment-jsdom": "24.8.0",
|
"jest-environment-jsdom": "24.9.0",
|
||||||
"jest-environment-jsdom-global": "1.2.0",
|
"jest-environment-jsdom-global": "1.2.0",
|
||||||
"jest-environment-node": "24.8.0",
|
"jest-environment-node": "24.9.0",
|
||||||
|
"jest-fetch-mock": "2.1.2",
|
||||||
"js-base64": "2.5.1",
|
"js-base64": "2.5.1",
|
||||||
"js-yaml": "3.13.1",
|
"js-yaml": "3.13.1",
|
||||||
"localstorage-memory": "1.0.3",
|
"localstorage-memory": "1.0.3",
|
||||||
@ -76,7 +78,7 @@
|
|||||||
"react-autosuggest": "9.4.3",
|
"react-autosuggest": "9.4.3",
|
||||||
"react-dom": "16.9.0",
|
"react-dom": "16.9.0",
|
||||||
"react-emotion": "9.2.12",
|
"react-emotion": "9.2.12",
|
||||||
"react-hot-loader": "4.12.10",
|
"react-hot-loader": "4.12.11",
|
||||||
"react-router": "5.0.1",
|
"react-router": "5.0.1",
|
||||||
"react-router-dom": "5.0.1",
|
"react-router-dom": "5.0.1",
|
||||||
"resolve-url-loader": "3.1.0",
|
"resolve-url-loader": "3.1.0",
|
||||||
@ -98,10 +100,10 @@
|
|||||||
"verdaccio": "4.2.1",
|
"verdaccio": "4.2.1",
|
||||||
"verdaccio-auth-memory": "1.1.5",
|
"verdaccio-auth-memory": "1.1.5",
|
||||||
"verdaccio-memory": "2.0.0",
|
"verdaccio-memory": "2.0.0",
|
||||||
"webpack": "4.39.1",
|
"webpack": "4.39.2",
|
||||||
"webpack-bundle-analyzer": "3.4.1",
|
"webpack-bundle-analyzer": "3.4.1",
|
||||||
"webpack-bundle-size-analyzer": "3.0.0",
|
"webpack-bundle-size-analyzer": "3.0.0",
|
||||||
"webpack-cli": "3.3.6",
|
"webpack-cli": "3.3.7",
|
||||||
"webpack-dev-server": "3.8.0",
|
"webpack-dev-server": "3.8.0",
|
||||||
"webpack-merge": "4.2.1",
|
"webpack-merge": "4.2.1",
|
||||||
"whatwg-fetch": "3.0.0",
|
"whatwg-fetch": "3.0.0",
|
||||||
|
38
src/App/AppError.tsx
Normal file
38
src/App/AppError.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export interface ErrorProps {
|
||||||
|
children: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ErrorAppState {
|
||||||
|
hasError: boolean;
|
||||||
|
error: any;
|
||||||
|
info: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ErrorBoundary extends Component<ErrorProps, ErrorAppState> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { hasError: false, error: null, info: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, info) {
|
||||||
|
this.setState({ hasError: true, error, info });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { hasError, error, info } = this.state;
|
||||||
|
const { children } = this.props;
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>{'Something went wrong.'}</h1>
|
||||||
|
<p>{`error: ${error}`}</p>
|
||||||
|
<p>{`info: ${JSON.stringify(info)}`}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ describe('<ActionBar /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -35,7 +35,7 @@ describe('<ActionBar /> component', () => {
|
|||||||
latest: {},
|
latest: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -57,7 +57,7 @@ describe('<ActionBar /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,7 @@ import HomeIcon from '@material-ui/icons/Home';
|
|||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
|
||||||
import { Fab, ActionListItem } from './styles';
|
import { Fab, ActionListItem } from './styles';
|
||||||
import { isURL, extractFileName, downloadFile } from '../../utils/url';
|
import { isURL, extractFileName, downloadFile } from '../../utils/url';
|
||||||
import api from '../../utils/api';
|
import api from '../../utils/api';
|
||||||
|
@ -20,7 +20,7 @@ describe('<Author /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -39,7 +39,7 @@ describe('<Author /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -63,7 +63,7 @@ describe('<Author /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ import Avatar from '@material-ui/core/Avatar';
|
|||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
|
||||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
import { DetailContextConsumer } from '../../pages/Version';
|
||||||
import { Heading, AuthorListItem } from './styles';
|
import { Heading, AuthorListItem } from './styles';
|
||||||
import { isEmail } from '../../utils/url';
|
import { isEmail } from '../../utils/url';
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import React, { Component, Fragment, ReactElement } from 'react';
|
|||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
import CardContent from '@material-ui/core/CardContent';
|
import CardContent from '@material-ui/core/CardContent';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
|
||||||
|
|
||||||
import { CardWrap, Heading, Tags, Tag } from './styles';
|
import { CardWrap, Heading, Tags, Tag } from './styles';
|
||||||
import NoItems from '../NoItems';
|
import NoItems from '../NoItems';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { Component, ReactElement, Fragment } from 'react';
|
import React, { Component, ReactElement, Fragment } from 'react';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
|
||||||
import Readme from '../Readme';
|
import Readme from '../Readme';
|
||||||
import Versions from '../Versions';
|
import Versions from '../Versions';
|
||||||
import { preventXSS } from '../../utils/sec-utils';
|
import { preventXSS } from '../../utils/sec-utils';
|
||||||
@ -14,6 +14,11 @@ interface DetailContainerState {
|
|||||||
tabPosition: number;
|
tabPosition: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const README_LABEL = 'Readme';
|
||||||
|
export const DEPS_LABEL = 'Dependencies';
|
||||||
|
export const VERSION_LABEL = 'Versions';
|
||||||
|
export const UPLINKS_LABEL = 'Uplinks';
|
||||||
|
|
||||||
class DetailContainer<P> extends Component<P, DetailContainerState> {
|
class DetailContainer<P> extends Component<P, DetailContainerState> {
|
||||||
public state = {
|
public state = {
|
||||||
tabPosition: 0,
|
tabPosition: 0,
|
||||||
@ -37,10 +42,10 @@ class DetailContainer<P> extends Component<P, DetailContainerState> {
|
|||||||
private renderListTabs(tabPosition: number): React.ReactElement<HTMLElement> {
|
private renderListTabs(tabPosition: number): React.ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<Tabs indicatorColor={'primary'} onChange={this.handleChange} textColor={'primary'} value={tabPosition} variant={'fullWidth'}>
|
<Tabs indicatorColor={'primary'} onChange={this.handleChange} textColor={'primary'} value={tabPosition} variant={'fullWidth'}>
|
||||||
<Tab id={'readme-tab'} label={'Readme'} />
|
<Tab data-testid={'readme-tab'} id={'readme-tab'} label={README_LABEL} />
|
||||||
<Tab id={'dependencies-tab'} label={'Dependencies'} />
|
<Tab data-testid={'dependencies-tab'} id={'dependencies-tab'} label={DEPS_LABEL} />
|
||||||
<Tab id={'versions-tab'} label={'Versions'} />
|
<Tab data-testid={'versions-tab'} id={'versions-tab'} label={VERSION_LABEL} />
|
||||||
<Tab id={'uplinks-tab'} label={'Uplinks'} />
|
<Tab data-testid={'uplinks-tab'} id={'uplinks-tab'} label={UPLINKS_LABEL} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component, ReactElement } from 'react';
|
import React, { ReactElement } from 'react';
|
||||||
|
|
||||||
import Card from '@material-ui/core/Card';
|
import Card from '@material-ui/core/Card';
|
||||||
import CardContent from '@material-ui/core/CardContent';
|
import CardContent from '@material-ui/core/CardContent';
|
||||||
@ -12,76 +12,52 @@ import Engine from '../Engines/Engines';
|
|||||||
import Install from '../Install';
|
import Install from '../Install';
|
||||||
import Repository from '../Repository/Repository';
|
import Repository from '../Repository/Repository';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { DetailContext } from '../../pages/Version';
|
||||||
|
|
||||||
import { TitleListItem, TitleListItemText } from './styles';
|
import { TitleListItem, TitleListItemText } from './styles';
|
||||||
|
|
||||||
class DetailSidebar extends Component {
|
const renderCopyCLI = () => <Install />;
|
||||||
public render(): ReactElement<HTMLElement> {
|
const renderMaintainers = () => <Developers type="maintainers" />;
|
||||||
return <DetailContextConsumer>{context => this.renderSideBar(context as VersionPageConsumerProps)}</DetailContextConsumer>;
|
const renderContributors = () => <Developers type="contributors" />;
|
||||||
}
|
const renderRepository = () => <Repository />;
|
||||||
|
const renderAuthor = () => <Author />;
|
||||||
|
const renderEngine = () => <Engine />;
|
||||||
|
const renderDist = () => <Dist />;
|
||||||
|
const renderActionBar = () => <ActionBar />;
|
||||||
|
const renderTitle = (packageName, packageMeta) => {
|
||||||
|
return (
|
||||||
|
<List className="detail-info">
|
||||||
|
<TitleListItem alignItems="flex-start">
|
||||||
|
<TitleListItemText primary={<b>{packageName}</b>} secondary={packageMeta.latest.description} />
|
||||||
|
</TitleListItem>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
private renderSideBar = ({ packageName, packageMeta }): ReactElement<HTMLElement> => {
|
function renderSideBar(packageName, packageMeta): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<div className={'sidebar-info'}>
|
<div className={'sidebar-info'}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{this.renderTitle(packageName, packageMeta)}
|
{renderTitle(packageName, packageMeta)}
|
||||||
{this.renderActionBar()}
|
{renderActionBar()}
|
||||||
{this.renderCopyCLI()}
|
{renderCopyCLI()}
|
||||||
{this.renderRepository()}
|
{renderRepository()}
|
||||||
{this.renderEngine()}
|
{renderEngine()}
|
||||||
{this.renderDist()}
|
{renderDist()}
|
||||||
{this.renderAuthor()}
|
{renderAuthor()}
|
||||||
{this.renderMaintainers()}
|
{renderMaintainers()}
|
||||||
{this.renderContributors()}
|
{renderContributors()}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
private renderTitle = (packageName, packageMeta) => {
|
|
||||||
return (
|
|
||||||
<List className="detail-info">
|
|
||||||
<TitleListItem alignItems="flex-start">
|
|
||||||
<TitleListItemText primary={<b>{packageName}</b>} secondary={packageMeta.latest.description} />
|
|
||||||
</TitleListItem>
|
|
||||||
</List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderCopyCLI = () => {
|
|
||||||
return <Install />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderMaintainers = () => {
|
|
||||||
return <Developers type="maintainers" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderContributors = () => {
|
|
||||||
return <Developers type="contributors" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderRepository = () => {
|
|
||||||
return <Repository />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderAuthor = () => {
|
|
||||||
return <Author />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderEngine = () => {
|
|
||||||
return <Engine />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderDist = () => {
|
|
||||||
return <Dist />;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderActionBar = () => {
|
|
||||||
return <ActionBar />;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DetailSidebar = () => {
|
||||||
|
const { packageName, packageMeta } = React.useContext(DetailContext);
|
||||||
|
|
||||||
|
return renderSideBar(packageName, packageMeta);
|
||||||
|
};
|
||||||
|
|
||||||
export default DetailSidebar;
|
export default DetailSidebar;
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { mount } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
import Developers, { DevelopersType } from './Developers';
|
import Developers, { DevelopersType } from './Developers';
|
||||||
import { Fab } from './styles';
|
import { Fab } from './styles';
|
||||||
import { DetailContextProvider } from '../../pages/version/Version';
|
import { DetailContextProvider } from '../../pages/Version';
|
||||||
|
|
||||||
describe('test Developers', () => {
|
describe('test Developers', () => {
|
||||||
const packageMeta = {
|
const packageMeta = {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { FC, Fragment } from 'react';
|
import React, { FC, Fragment } from 'react';
|
||||||
import Add from '@material-ui/icons/Add';
|
import Add from '@material-ui/icons/Add';
|
||||||
|
|
||||||
import { DetailContext } from '../../pages/version/Version';
|
import { DetailContext } from '../../pages/Version';
|
||||||
import { AvatarTooltip } from '../AvatarTooltip';
|
import { AvatarTooltip } from '../AvatarTooltip';
|
||||||
import { Details, Heading, Content, Fab } from './styles';
|
import { Details, Heading, Content, Fab } from './styles';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ describe('<Dist /> component', () => {
|
|||||||
license: '',
|
license: '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -41,7 +41,7 @@ describe('<Dist /> component', () => {
|
|||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -67,7 +67,7 @@ describe('<Dist /> component', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||||||
|
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { VersionPageConsumerProps, DetailContextConsumer } from '../../pages/Version';
|
||||||
import { Heading, DistListItem, DistChips } from './styles';
|
import { Heading, DistListItem, DistChips } from './styles';
|
||||||
import fileSizeSI from '../../utils/file-size';
|
import fileSizeSI from '../../utils/file-size';
|
||||||
import { PackageMetaInterface } from 'types/packageMeta';
|
import { PackageMetaInterface } from 'types/packageMeta';
|
||||||
|
@ -19,7 +19,7 @@ describe('<Engines /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -35,7 +35,7 @@ describe('<Engines /> component', () => {
|
|||||||
latest: {},
|
latest: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -53,7 +53,7 @@ describe('<Engines /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@ import Grid from '@material-ui/core/Grid';
|
|||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { VersionPageConsumerProps, DetailContextConsumer } from '../../pages/Version';
|
||||||
import { Heading, EngineListItem } from './styles';
|
import { Heading, EngineListItem } from './styles';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import node from './img/node.png';
|
import node from './img/node.png';
|
||||||
|
@ -2,8 +2,8 @@ import List from '@material-ui/core/List';
|
|||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
// @ts-ignore
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
|
||||||
import CopyToClipBoard from '../CopyToClipBoard';
|
import CopyToClipBoard from '../CopyToClipBoard';
|
||||||
|
|
||||||
// logos of package managers
|
// logos of package managers
|
||||||
|
@ -6,7 +6,7 @@ import Spinner from '../Spinner';
|
|||||||
import { Wrapper, Badge } from './styles';
|
import { Wrapper, Badge } from './styles';
|
||||||
|
|
||||||
const Loading: React.FC = () => (
|
const Loading: React.FC = () => (
|
||||||
<Wrapper>
|
<Wrapper data-testid="loading">
|
||||||
<Badge>
|
<Badge>
|
||||||
<Logo size={Size.Big} />
|
<Logo size={Size.Big} />
|
||||||
</Badge>
|
</Badge>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<Loading /> component should render the component in default state 1`] = `"<div class=\\"css-1221txa eimgwje0\\"><div class=\\"css-bxochs eimgwje1\\"><div class=\\"css-ge0nak em793ed0\\"></div></div><div class=\\"css-vqrgi e1ag4h8b0\\"><div class=\\"MuiCircularProgress-root-1 MuiCircularProgress-colorPrimary-4 MuiCircularProgress-indeterminate-3 css-15gl0ho e1ag4h8b1\\" style=\\"width:50px;height:50px\\" role=\\"progressbar\\"><svg class=\\"MuiCircularProgress-svg-6\\" viewBox=\\"22 22 44 44\\"><circle class=\\"MuiCircularProgress-circle-7 MuiCircularProgress-circleIndeterminate-9\\" cx=\\"44\\" cy=\\"44\\" r=\\"20.2\\" fill=\\"none\\" stroke-width=\\"3.6\\"></circle></svg></div></div></div>"`;
|
exports[`<Loading /> component should render the component in default state 1`] = `"<div data-testid=\\"loading\\" class=\\"css-1221txa eimgwje0\\"><div class=\\"css-bxochs eimgwje1\\"><div class=\\"css-ge0nak em793ed0\\"></div></div><div class=\\"css-vqrgi e1ag4h8b0\\"><div class=\\"MuiCircularProgress-root-1 MuiCircularProgress-colorPrimary-4 MuiCircularProgress-indeterminate-3 css-15gl0ho e1ag4h8b1\\" style=\\"width:50px;height:50px\\" role=\\"progressbar\\"><svg class=\\"MuiCircularProgress-svg-6\\" viewBox=\\"22 22 44 44\\"><circle class=\\"MuiCircularProgress-circle-7 MuiCircularProgress-circleIndeterminate-9\\" cx=\\"44\\" cy=\\"44\\" r=\\"20.2\\" fill=\\"none\\" stroke-width=\\"3.6\\"></circle></svg></div></div></div>"`;
|
||||||
|
@ -1,46 +1,43 @@
|
|||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth';
|
import withWidth, { isWidthUp } from '@material-ui/core/withWidth';
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import PackageImg from './img/package.svg';
|
import PackageImg from './img/package.svg';
|
||||||
import { Card, EmptyPackage, Heading, Inner, List, Wrapper } from './styles';
|
import { Card, EmptyPackage, Heading, Inner, List, Wrapper } from './styles';
|
||||||
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||||
|
|
||||||
export const NOT_FOUND_TEXT = "Sorry, we couldn't find it...";
|
export const NOT_FOUND_TEXT = `Sorry, we couldn't find it...`;
|
||||||
|
export const LABEL_NOT_FOUND = `The page you're looking for doesn't exist.`;
|
||||||
|
export const LABEL_FOOTER_NOT_FOUND = 'Perhaps these links will help find what you are looking for:';
|
||||||
|
|
||||||
export type NotFoundProps = RouteComponentProps & { width: Breakpoint; history };
|
export type NotFoundProps = RouteComponentProps & { width: Breakpoint; history };
|
||||||
|
|
||||||
const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
const HOME_LABEL = 'Home';
|
||||||
const handleGoTo = (to: string): (() => void | undefined) => () => {
|
|
||||||
history.push(to);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGoBack = (): ((e: React.MouseEvent<HTMLElement, MouseEvent>) => void | undefined) => () => {
|
const renderSubTitle = (): JSX.Element => (
|
||||||
history.goBack();
|
<Typography variant="subtitle1">
|
||||||
};
|
<div>{LABEL_NOT_FOUND}</div>
|
||||||
|
<div>{LABEL_FOOTER_NOT_FOUND}</div>
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
|
||||||
|
const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
||||||
|
const handleGomHome = useCallback(() => {
|
||||||
|
history.push('/');
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
const renderList = (): JSX.Element => (
|
const renderList = (): JSX.Element => (
|
||||||
<List>
|
<List>
|
||||||
<ListItem button={true} divider={true} onClick={handleGoTo('/')}>
|
<ListItem button={true} divider={true} onClick={handleGomHome}>
|
||||||
{'Home'}
|
{HOME_LABEL}
|
||||||
</ListItem>
|
|
||||||
<ListItem button={true} divider={true} onClick={handleGoBack()}>
|
|
||||||
{'Back'}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderSubTitle = (): JSX.Element => (
|
|
||||||
<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 (
|
return (
|
||||||
<Wrapper>
|
<Wrapper data-testid="404">
|
||||||
<Inner>
|
<Inner>
|
||||||
<EmptyPackage alt="404 - Page not found" src={PackageImg} />
|
<EmptyPackage alt="404 - Page not found" src={PackageImg} />
|
||||||
<Heading className="not-found-text" variant={isWidthUp('sm', width) ? 'h2' : 'h4'}>
|
<Heading className="not-found-text" variant={isWidthUp('sm', width) ? 'h2' : 'h4'}>
|
||||||
|
@ -18,7 +18,7 @@ describe('<Repository /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -34,7 +34,7 @@ describe('<Repository /> component', () => {
|
|||||||
latest: {},
|
latest: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
@ -55,7 +55,7 @@ describe('<Repository /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@ import Avatar from '@material-ui/core/Avatar';
|
|||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
|
||||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
import { DetailContextConsumer } from '../../pages/Version';
|
||||||
import CopyToClipBoard from '../CopyToClipBoard';
|
import CopyToClipBoard from '../CopyToClipBoard';
|
||||||
|
|
||||||
import { Heading, GithubLink, RepositoryListItem } from './styles';
|
import { Heading, GithubLink, RepositoryListItem } from './styles';
|
||||||
|
@ -2,7 +2,7 @@ import React, { ReactElement } from 'react';
|
|||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
|
|
||||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
import { DetailContextConsumer } from '../../pages/Version';
|
||||||
import NoItems from '../NoItems';
|
import NoItems from '../NoItems';
|
||||||
import { formatDateDistance } from '../../utils/package';
|
import { formatDateDistance } from '../../utils/package';
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ describe('<Version /> component', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.doMock('../../pages/version/Version', () => ({
|
jest.doMock('../../pages/Version', () => ({
|
||||||
DetailContextConsumer: component => {
|
DetailContextConsumer: component => {
|
||||||
return component.children({ packageMeta });
|
return component.children({ packageMeta });
|
||||||
},
|
},
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
import React, { ReactElement } from 'react';
|
||||||
import { formatDateDistance } from '../../utils/package';
|
|
||||||
import { Heading, Spacer, ListItemText } from './styles';
|
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
import React, { ReactElement } from 'react';
|
|
||||||
|
import { DetailContextConsumer } from '../../pages/Version';
|
||||||
|
import { formatDateDistance } from '../../utils/package';
|
||||||
import { DIST_TAGS } from '../../../lib/constants';
|
import { DIST_TAGS } from '../../../lib/constants';
|
||||||
|
import { Heading, Spacer, ListItemText } from './styles';
|
||||||
|
|
||||||
const NOT_AVAILABLE = 'Not available';
|
const NOT_AVAILABLE = 'Not available';
|
||||||
|
|
||||||
|
27
src/pages/Version/Layout.tsx
Normal file
27
src/pages/Version/Layout.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React, { FC, ReactElement } from 'react';
|
||||||
|
import Grid from '@material-ui/core/Grid';
|
||||||
|
import DetailContainer from '../../components/DetailContainer';
|
||||||
|
import DetailSidebar from '../../components/DetailSidebar';
|
||||||
|
|
||||||
|
function renderDetail(): ReactElement<HTMLElement> {
|
||||||
|
return <DetailContainer />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSidebar(): ReactElement<HTMLElement> {
|
||||||
|
return <DetailSidebar />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout: FC<{}> = () => {
|
||||||
|
return (
|
||||||
|
<Grid className={'container content'} container={true} data-testid="version-layout" spacing={0}>
|
||||||
|
<Grid item={true} xs={8}>
|
||||||
|
{renderDetail()}
|
||||||
|
</Grid>
|
||||||
|
<Grid item={true} xs={4}>
|
||||||
|
{renderSidebar()}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Layout };
|
96
src/pages/Version/Version.test.tsx
Normal file
96
src/pages/Version/Version.test.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, cleanup } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { MemoryRouter } from 'react-router';
|
||||||
|
|
||||||
|
import vueMetadata from '../../../test/fixtures/metadata/vue.json';
|
||||||
|
|
||||||
|
import Version from './Version';
|
||||||
|
import { waitForElement } from '@testing-library/dom';
|
||||||
|
import ErrorBoundary from '../../App/AppError';
|
||||||
|
import { LABEL_NOT_FOUND } from '../../components/NotFound/NotFound';
|
||||||
|
// import { NOT_FOUND_TEXT } from '../../components/NotFound/NotFound';
|
||||||
|
|
||||||
|
// :-) we mock this otherways fails on render, some weird issue on material-ui
|
||||||
|
jest.mock('@material-ui/core/Avatar');
|
||||||
|
|
||||||
|
describe('test Version page', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
// FIXME: a better way to mock this
|
||||||
|
// @ts-ignore
|
||||||
|
global.window.VERDACCIO_API_URL = 'http://test';
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render the version page', async () => {
|
||||||
|
const readmeText = 'test';
|
||||||
|
// @ts-ignore
|
||||||
|
fetch.mockResponses(
|
||||||
|
[[JSON.stringify(vueMetadata)], { status: 200, headers: { 'content-type': 'application/json' } }],
|
||||||
|
[[`<p align="center">${readmeText}</p>`], { status: 200, headers: { 'content-type': 'text/html' } }]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { getByTestId, getByText } = render(
|
||||||
|
<ErrorBoundary>
|
||||||
|
<MemoryRouter>
|
||||||
|
<Version match={{ params: { ['package']: 'vue' } }}></Version>
|
||||||
|
</MemoryRouter>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
|
||||||
|
// first it display loading
|
||||||
|
const hasLoading = getByTestId('loading');
|
||||||
|
expect(hasLoading).toBeTruthy();
|
||||||
|
|
||||||
|
// we wait fetch response (mocked above)
|
||||||
|
await waitForElement(() => getByTestId('version-layout'));
|
||||||
|
|
||||||
|
// check whether readme was loaded
|
||||||
|
const hasReadme = getByText(readmeText);
|
||||||
|
|
||||||
|
expect(hasReadme).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render 404 page if the resources are not found', async () => {
|
||||||
|
// @ts-ignore
|
||||||
|
fetch.mockResponses(
|
||||||
|
[[JSON.stringify({})], { status: 404, headers: { 'content-type': 'application/json' } }],
|
||||||
|
[[``], { status: 404, headers: { 'content-type': 'text/html' } }]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { getByTestId, getByText } = render(
|
||||||
|
<ErrorBoundary>
|
||||||
|
<MemoryRouter>
|
||||||
|
<Version match={{ params: { ['package']: 'vue' } }}></Version>
|
||||||
|
</MemoryRouter>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
|
||||||
|
// first it display loading
|
||||||
|
const hasLoading = getByTestId('loading');
|
||||||
|
expect(hasLoading).toBeTruthy();
|
||||||
|
|
||||||
|
// we wait fetch response (mocked above)
|
||||||
|
await waitForElement(() => getByTestId('404'));
|
||||||
|
|
||||||
|
// check whether readme was loaded
|
||||||
|
const hasReadme = getByText(LABEL_NOT_FOUND);
|
||||||
|
|
||||||
|
expect(hasReadme).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wanna contribute? Here we some scenarios we need to test
|
||||||
|
|
||||||
|
test.todo('should test click on tabs');
|
||||||
|
test.todo('should check what is rendered int he sidebar is correct');
|
||||||
|
test.todo('should test click back home on 404');
|
||||||
|
test.todo('should test click on elements in the sidebar');
|
||||||
|
test.todo('should test other not consider scenarios');
|
||||||
|
});
|
69
src/pages/Version/Version.tsx
Normal file
69
src/pages/Version/Version.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { callDetailPage, callReadme } from '../../utils/calls';
|
||||||
|
import { buildScopePackage } from '../../utils/package';
|
||||||
|
import Loading from '../../components/Loading/Loading';
|
||||||
|
import NotFound from '../../components/NotFound';
|
||||||
|
|
||||||
|
import { Layout } from './Layout';
|
||||||
|
import { DetailContextProvider } from './context';
|
||||||
|
import { StateInterface } from './types';
|
||||||
|
|
||||||
|
export function getRouterPackageName(params): string {
|
||||||
|
const packageName = params.package;
|
||||||
|
const { scope } = params;
|
||||||
|
if (scope) {
|
||||||
|
return buildScopePackage(scope, packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Version = ({ match: { params } }) => {
|
||||||
|
const pkgName = getRouterPackageName(params);
|
||||||
|
const [readMe, setReadme] = useState();
|
||||||
|
const [packageName, setPackageName] = useState(pkgName);
|
||||||
|
const [packageMeta, setPackageMeta] = useState();
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [notFound, setNotFound] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const packageMeta = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
||||||
|
const readMe = (await callReadme(packageName)) as Partial<StateInterface>;
|
||||||
|
setReadme(readMe);
|
||||||
|
setPackageMeta(packageMeta);
|
||||||
|
setIsLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
setNotFound(true);
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [packageName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = `Verdaccio - ${packageName}`;
|
||||||
|
}, [packageName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const pkgName = getRouterPackageName(params);
|
||||||
|
|
||||||
|
setPackageName(pkgName);
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
const isNotFound = notFound || typeof packageMeta === 'undefined' || typeof packageName === 'undefined' || typeof readMe === 'undefined';
|
||||||
|
const renderContent = (): React.ReactElement<HTMLElement> => {
|
||||||
|
if (isLoading) {
|
||||||
|
return <Loading />;
|
||||||
|
} else if (isNotFound) {
|
||||||
|
return <NotFound />;
|
||||||
|
} else {
|
||||||
|
return <Layout />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <DetailContextProvider value={{ packageMeta, readMe, packageName, enableLoading: setIsLoading }}>{renderContent()}</DetailContextProvider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Version;
|
7
src/pages/Version/context.ts
Normal file
7
src/pages/Version/context.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React, { Consumer, Provider } from 'react';
|
||||||
|
import { DetailContextProps, VersionPageConsumerProps } from './types';
|
||||||
|
|
||||||
|
export const DetailContext = React.createContext<Partial<DetailContextProps>>({});
|
||||||
|
|
||||||
|
export const DetailContextProvider: Provider<Partial<VersionPageConsumerProps>> = DetailContext.Provider;
|
||||||
|
export const DetailContextConsumer: Consumer<Partial<VersionPageConsumerProps>> = DetailContext.Consumer;
|
3
src/pages/Version/index.ts
Normal file
3
src/pages/Version/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { DetailContextProps, VersionPageConsumerProps } from './types';
|
||||||
|
export { DetailContext, DetailContextConsumer, DetailContextProvider } from './context';
|
||||||
|
export { default } from './Version';
|
28
src/pages/Version/types.ts
Normal file
28
src/pages/Version/types.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { PackageMetaInterface } from '../../../types/packageMeta';
|
||||||
|
|
||||||
|
export interface DetailContextProps {
|
||||||
|
packageMeta: PackageMetaInterface;
|
||||||
|
readMe: string;
|
||||||
|
packageName: string;
|
||||||
|
enableLoading: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VersionPageConsumerProps {
|
||||||
|
packageMeta: PackageMetaInterface;
|
||||||
|
readMe: string;
|
||||||
|
packageName: string;
|
||||||
|
// FIXME: looking for the appropiated type here
|
||||||
|
enableLoading: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PropsInterface {
|
||||||
|
match: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StateInterface {
|
||||||
|
readMe: string;
|
||||||
|
packageName: string;
|
||||||
|
packageMeta?: PackageMetaInterface;
|
||||||
|
isLoading: boolean;
|
||||||
|
notFound: boolean;
|
||||||
|
}
|
@ -1,160 +0,0 @@
|
|||||||
import React, { Component, ReactElement, Consumer, Provider } from 'react';
|
|
||||||
import Grid from '@material-ui/core/Grid';
|
|
||||||
import Loading from '../../components/Loading/Loading';
|
|
||||||
import DetailContainer from '../../components/DetailContainer/DetailContainer';
|
|
||||||
import DetailSidebar from '../../components/DetailSidebar/DetailSidebar';
|
|
||||||
import { callDetailPage } from '../../utils/calls';
|
|
||||||
import { getRouterPackageName } from '../../utils/package';
|
|
||||||
import NotFound from '../../components/NotFound';
|
|
||||||
import { PackageMetaInterface } from '../../../types/packageMeta';
|
|
||||||
|
|
||||||
export interface DetailContextProps {
|
|
||||||
packageMeta: PackageMetaInterface;
|
|
||||||
readMe: string;
|
|
||||||
packageName: string;
|
|
||||||
enableLoading: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DetailContext = React.createContext<Partial<DetailContextProps>>({});
|
|
||||||
|
|
||||||
export interface VersionPageConsumerProps {
|
|
||||||
packageMeta: PackageMetaInterface;
|
|
||||||
readMe: string;
|
|
||||||
packageName: string;
|
|
||||||
enableLoading: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DetailContextProvider: Provider<Partial<VersionPageConsumerProps>> = DetailContext.Provider;
|
|
||||||
export const DetailContextConsumer: Consumer<Partial<VersionPageConsumerProps>> = DetailContext.Consumer;
|
|
||||||
|
|
||||||
interface PropsInterface {
|
|
||||||
match: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StateInterface {
|
|
||||||
readMe: string;
|
|
||||||
packageName: string;
|
|
||||||
packageMeta: PackageMetaInterface | null;
|
|
||||||
isLoading: boolean;
|
|
||||||
notFound: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class VersionPage extends Component<PropsInterface, Partial<StateInterface>> {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
readMe: '',
|
|
||||||
packageName: getRouterPackageName(props.match),
|
|
||||||
packageMeta: null,
|
|
||||||
isLoading: true,
|
|
||||||
notFound: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getDerivedStateFromProps(nextProps, prevState): { packageName?: string; isLoading: boolean; notFound?: boolean } | null {
|
|
||||||
const { match } = nextProps;
|
|
||||||
const packageName = getRouterPackageName(match);
|
|
||||||
|
|
||||||
if (packageName !== prevState.packageName) {
|
|
||||||
try {
|
|
||||||
return {
|
|
||||||
packageName,
|
|
||||||
isLoading: false,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
return {
|
|
||||||
notFound: true,
|
|
||||||
isLoading: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async componentDidMount(): Promise<void> {
|
|
||||||
await this.loadPackageInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint no-unused-vars: 0 */
|
|
||||||
public async componentDidUpdate(nextProps, prevState: StateInterface): Promise<void> {
|
|
||||||
const { packageName } = this.state;
|
|
||||||
if (packageName !== prevState.packageName) {
|
|
||||||
const { readMe, packageMeta } = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
|
||||||
this.setState({
|
|
||||||
readMe,
|
|
||||||
packageMeta,
|
|
||||||
packageName,
|
|
||||||
notFound: false,
|
|
||||||
isLoading: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): ReactElement<HTMLElement> {
|
|
||||||
const { isLoading, packageMeta, readMe, packageName } = this.state;
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <Loading />;
|
|
||||||
} else if (!packageMeta) {
|
|
||||||
return <NotFound />;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<DetailContextProvider value={{ packageMeta, readMe, packageName, enableLoading: this.enableLoading }}>
|
|
||||||
<Grid className={'container content'} container={true} spacing={0}>
|
|
||||||
<Grid item={true} xs={8}>
|
|
||||||
{this.renderDetail()}
|
|
||||||
</Grid>
|
|
||||||
<Grid item={true} xs={4}>
|
|
||||||
{this.renderSidebar()}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</DetailContextProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async loadPackageInfo(): Promise<void> {
|
|
||||||
const { packageName } = this.state;
|
|
||||||
// FIXME: use utility
|
|
||||||
document.title = `Verdaccio - ${packageName}`;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
readMe: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { readMe, packageMeta } = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
|
||||||
this.setState({
|
|
||||||
readMe,
|
|
||||||
packageMeta,
|
|
||||||
packageName,
|
|
||||||
notFound: false,
|
|
||||||
isLoading: false,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
this.setState({
|
|
||||||
notFound: true,
|
|
||||||
packageName,
|
|
||||||
isLoading: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enableLoading = () => {
|
|
||||||
this.setState({
|
|
||||||
isLoading: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
public renderDetail(): ReactElement<HTMLElement> {
|
|
||||||
return <DetailContainer />;
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderSidebar(): ReactElement<HTMLElement> {
|
|
||||||
return <DetailSidebar />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default VersionPage;
|
|
@ -1 +0,0 @@
|
|||||||
export { default, DetailContextProps } from './Version';
|
|
@ -13,7 +13,7 @@ const history = createBrowserHistory({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const NotFound = asyncComponent(() => import('./components/NotFound'));
|
const NotFound = asyncComponent(() => import('./components/NotFound'));
|
||||||
const VersionPackage = asyncComponent(() => import('./pages/version/Version'));
|
const VersionPackage = asyncComponent(() => import('./pages/Version'));
|
||||||
const HomePage = asyncComponent(() => import('./pages/home'));
|
const HomePage = asyncComponent(() => import('./pages/home'));
|
||||||
|
|
||||||
interface RouterAppProps {
|
interface RouterAppProps {
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import API from './api';
|
import API from './api';
|
||||||
import { PackageMetaInterface } from 'types/packageMeta';
|
import { PackageMetaInterface } from 'types/packageMeta';
|
||||||
|
|
||||||
export interface DetailPage {
|
export async function callReadme(packageName): Promise<string | {}> {
|
||||||
readMe: string | {};
|
return await API.request<string | {}>(`package/readme/${packageName}`, 'GET');
|
||||||
packageMeta: PackageMetaInterface | {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function callDetailPage(packageName): Promise<DetailPage> {
|
export async function callDetailPage(packageName): Promise<PackageMetaInterface | {}> {
|
||||||
const readMe = await API.request<string | {}>(`package/readme/${packageName}`, 'GET');
|
|
||||||
const packageMeta = await API.request<PackageMetaInterface | {}>(`sidebar/${packageName}`, 'GET');
|
const packageMeta = await API.request<PackageMetaInterface | {}>(`sidebar/${packageName}`, 'GET');
|
||||||
|
|
||||||
return { readMe, packageMeta };
|
return packageMeta;
|
||||||
}
|
}
|
||||||
|
@ -56,14 +56,8 @@ export function formatDateDistance(lastUpdate): string {
|
|||||||
return distanceInWordsToNow(new Date(lastUpdate));
|
return distanceInWordsToNow(new Date(lastUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRouterPackageName(match): string {
|
export function buildScopePackage(scope: string, packageName: string) {
|
||||||
const packageName = match.params.package;
|
return `@${scope}/${packageName}`;
|
||||||
const scope = match.params.scope;
|
|
||||||
if (scope) {
|
|
||||||
return `@${scope}/${packageName}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return packageName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
24204
test/fixtures/metadata/vue.json
vendored
Normal file
24204
test/fixtures/metadata/vue.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
"checkJs": false,
|
"checkJs": false,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
@ -18,6 +19,6 @@
|
|||||||
"types/*.d.ts", "scripts/lib", "node_modules/config"
|
"types/*.d.ts", "scripts/lib", "node_modules/config"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user