fix: convert Engine component to hooks (#233)

* refactor: convert Engine component to hooks

* inline engine test data only used by one test

* remove  from engines tests

* remove confusing test abstraction

* change tests to not use mutations
This commit is contained in:
Andrew Hughson 2019-10-31 21:17:16 +00:00 committed by Juan Picado @jotadeveloper
parent b56e43846b
commit 5cb47ed49e
3 changed files with 73 additions and 101 deletions

View File

@ -1,71 +1,61 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { DetailContext } from '../../pages/Version';
import { PackageMetaInterface } from '../../../types/packageMeta';
import Engine from './Engines'; import Engine from './Engines';
jest.mock('./img/node.png', () => ''); jest.mock('./img/node.png', () => '');
jest.mock('../Install/img/npm.svg', () => ''); jest.mock('../Install/img/npm.svg', () => '');
const mockPackageMeta: jest.Mock = jest.fn(() => ({ const mockPackageMeta = (engines?: PackageMetaInterface['latest']['engines']): PackageMetaInterface => ({
latest: { latest: {
homepage: 'https://verdaccio.tld', name: 'verdaccio',
bugs: { version: '0.0.0',
url: 'https://verdaccio.tld/bugs',
},
dist: { dist: {
tarball: 'https://verdaccio.tld/download', fileCount: 1,
unpackedSize: 1,
}, },
...(engines && { engines }),
}, },
})); _uplinks: {},
});
jest.mock('../../pages/Version', () => ({
DetailContextConsumer: component => {
return component.children({ packageMeta: mockPackageMeta() });
},
}));
describe('<Engines /> component', () => { describe('<Engines /> component', () => {
beforeEach(() => {
jest.resetAllMocks();
});
test('should render the component in default state', () => { test('should render the component in default state', () => {
const packageMeta = { const packageMeta = mockPackageMeta({
latest: { node: '>= 0.1.98',
engines: { npm: '>3',
node: '>= 0.1.98', });
npm: '>3',
},
},
};
mockPackageMeta.mockImplementation(() => packageMeta); const wrapper = mount(
<DetailContext.Provider value={{ packageMeta }}>
const wrapper = mount(<Engine />); <Engine />
</DetailContext.Provider>
);
expect(wrapper.html()).toMatchSnapshot(); expect(wrapper.html()).toMatchSnapshot();
}); });
test('should render the component when there is no engine key in package meta', () => { test('should render the component when there is no engine key in package meta', () => {
const packageMeta = { const packageMeta = mockPackageMeta();
latest: {},
};
mockPackageMeta.mockImplementation(() => packageMeta); const wrapper = mount(
<DetailContext.Provider value={{ packageMeta }}>
const wrapper = mount(<Engine />); <Engine />
expect(wrapper.html()).toEqual(''); </DetailContext.Provider>
);
expect(wrapper.html()).toBeNull();
}); });
test('should render the component when there is no keys in engine in package meta', () => { test('should render the component when there is no keys in engine in package meta', () => {
const packageMeta = { const packageMeta = mockPackageMeta({});
latest: {
engines: {},
},
};
mockPackageMeta.mockImplementation(() => packageMeta); const wrapper = mount(
<DetailContext.Provider value={{ packageMeta }}>
const wrapper = mount(<Engine />); <Engine />
expect(wrapper.html()).toEqual(''); </DetailContext.Provider>
);
expect(wrapper.html()).toBeNull();
}); });
}); });

View File

@ -1,6 +1,6 @@
import React, { Component, ReactElement } from 'react'; import React, { useContext } from 'react';
import { VersionPageConsumerProps, DetailContextConsumer } from '../../pages/Version'; import { DetailContext } from '../../pages/Version';
import Avatar from '../../muiComponents/Avatar'; import Avatar from '../../muiComponents/Avatar';
import List from '../../muiComponents/List'; import List from '../../muiComponents/List';
import npm from '../Install/img/npm.svg'; import npm from '../Install/img/npm.svg';
@ -8,66 +8,44 @@ import ListItemText from '../../muiComponents/ListItemText';
import Grid from '../../muiComponents/Grid'; import Grid from '../../muiComponents/Grid';
import { StyledText, EngineListItem } from './styles'; import { StyledText, EngineListItem } from './styles';
// @ts-ignore
import node from './img/node.png'; import node from './img/node.png';
const ICONS = { const Engine: React.FC = () => {
'node-JS': <Avatar src={node} />, const { packageMeta } = useContext(DetailContext);
'NPM-version': <Avatar src={npm} />,
};
class Engine extends Component { const engines = packageMeta && packageMeta.latest && packageMeta.latest.engines;
public render(): ReactElement<HTMLElement> {
return ( if (!engines || (!engines.node && !engines.npm)) {
<DetailContextConsumer> return null;
{context => {
return this.renderEngine(context as VersionPageConsumerProps);
}}
</DetailContextConsumer>
);
} }
private renderEngine = ({ packageMeta }): ReactElement<HTMLElement> | null => { /* eslint-disable react/jsx-max-depth */
const { engines } = packageMeta.latest; return (
if (!engines) { <Grid container={true}>
return null; {engines.node && (
} <Grid item={true} xs={6}>
<List subheader={<StyledText variant={'subtitle1'}>{'node JS'}</StyledText>}>
<EngineListItem button={true}>
<Avatar src={node} />
<ListItemText primary={engines.node} />
</EngineListItem>
</List>
</Grid>
)}
const engineDict = { {engines.npm && (
'node-JS': engines.node, <Grid item={true} xs={6}>
'NPM-version': engines.npm, <List subheader={<StyledText variant={'subtitle1'}>{'NPM version'}</StyledText>}>
}; <EngineListItem button={true}>
<Avatar src={npm} />
const accumulator: React.ReactNode[] = []; <ListItemText primary={engines.npm} />
const items = Object.keys(engineDict).reduce((markup, text, key) => { </EngineListItem>
const heading = engineDict[text]; </List>
if (heading) { </Grid>
markup.push( )}
<Grid item={true} key={key} xs={6}> </Grid>
{this.renderListItems(heading, text)} );
</Grid> /* eslint-enable react/jsx-max-depth */
); };
}
return markup;
}, accumulator);
if (items.length < 1) {
return null;
}
return <Grid container={true}>{items}</Grid>;
};
private renderListItems = (heading: string, text: string) => {
return (
<List subheader={<StyledText variant={'subtitle1'}>{text.split('-').join(' ')}</StyledText>}>
<EngineListItem button={true}>
{ICONS[text]}
<ListItemText primary={heading} />
</EngineListItem>
</List>
);
};
}
export default Engine; export default Engine;

View File

@ -9,6 +9,10 @@ export interface PackageMetaInterface {
fileCount: number; fileCount: number;
unpackedSize: number; unpackedSize: number;
}; };
engines?: {
node?: string;
npm?: string;
};
license?: Partial<LicenseInterface> | string; license?: Partial<LicenseInterface> | string;
version: string; version: string;
}; };