Skip to content
This repository has been archived by the owner on Jan 16, 2022. It is now read-only.

Version Page - Replaces class by func. #171

Merged
merged 14 commits into from
Oct 17, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 32 additions & 65 deletions src/pages/Version/Version.test.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,59 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import { render } from '@testing-library/react';
import { waitForElement } from '@testing-library/dom';

import vueMetadata from '../../../test/fixtures/metadata/vue.json';
import ErrorBoundary from '../../App/AppError';
import { NOT_FOUND_TEXT } from '../../components/NotFound';

import Version from './Version';
import { DetailContext } from './context';
import data from './__partials__/data.json';

// :-) we mock this otherways fails on render, some weird issue on material-ui
jest.mock('../../muiComponents/Avatar');

// eslint-disable-next-line react/display-name
jest.mock('../../components/NotFound', () => () => <div>{'Not found'}</div>);
const detailContextValue = {
packageName: 'foo',
packageMeta: data,
readMe: 'Read me!',
enableLoading: jest.fn(),
isLoading: false,
hasNotBeenFound: false,
version: '1.0.0',
};

describe('test Version page', () => {
jest.setTimeout(40000000);
beforeAll(() => {
// FIXME: a better way to mock this
// @ts-ignore
global.window.VERDACCIO_API_URL = 'http://test';
});

afterEach(() => {
cleanup();
});

beforeEach(() => {
jest.resetAllMocks();
// @ts-ignore
fetch.resetMocks();
});

/* eslint-disable react/jsx-max-depth */
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>
<MemoryRouter>
<DetailContext.Provider value={detailContextValue}>
<Version />
</DetailContext.Provider>
</MemoryRouter>
);

// 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);

const hasReadme = getByText(detailContextValue.readMe);
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 { getByText } = render(
<MemoryRouter>
<DetailContext.Provider
value={{
...detailContextValue,
hasNotBeenFound: true,
}}>
<Version />
</DetailContext.Provider>
</MemoryRouter>
);

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(() => getByText('Not found'));

// check whether readme was loaded
const hasReadme = getByText('Not found');

expect(hasReadme).toBeTruthy();
const notFoundElement = await waitForElement(() => getByText(NOT_FOUND_TEXT));
expect(notFoundElement).toBeTruthy();
});

// Wanna contribute? Here we some scenarios we need to test
Expand Down
98 changes: 12 additions & 86 deletions src/pages/Version/Version.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,24 @@
import React, { useEffect, useState } from 'react';
import React, { useContext } 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';
import VersionLayout from './VersionLayout';
import { DetailContext } from './context';

export function getRouterPackageName(params): string {
const packageName = params.package;
const { scope } = params;
if (scope) {
return buildScopePackage(scope, packageName);
}

return packageName;
}

function fillTitle(text: string): string {
return `Verdaccio - ${text}`;
}
const Version: React.FC = () => {
const detailContext = useContext(DetailContext);
const { isLoading, hasNotBeenFound } = detailContext;

function isVersionValid(packageMeta, packageVersion): boolean {
const hasVersion = typeof packageVersion !== 'undefined';
if (!hasVersion) {
// if is undefined, that means versions does not exist, we continue
return true;
if (isLoading) {
return <Loading />;
}

const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);
return hasMatchVersion;
}

const Version = ({ match: { params } }) => {
const pkgName = getRouterPackageName(params);
const [readMe, setReadme] = useState();
const [packageName, setPackageName] = useState(pkgName);
// eslint-disable-next-line no-unused-vars
const [packageVersion, setPackageVersion] = useState(params.version);
const [packageMeta, setPackageMeta] = useState();
const [isLoading, setIsLoading] = useState(true);
const [notFound, setNotFound] = useState(false);

useEffect(() => {
(async () => {
try {
const packageMeta = (await callDetailPage(packageName, packageVersion)) as Partial<StateInterface>;
const readMe = (await callReadme(packageName, packageVersion)) as Partial<StateInterface>;
if (isVersionValid(packageMeta, packageVersion)) {
setReadme(readMe);
setPackageMeta(packageMeta);
setIsLoading(false);
} else {
setIsLoading(false);
setNotFound(true);
}
} catch (error) {
setNotFound(true);
setIsLoading(false);
}
})();
}, [packageName, packageVersion]);

useEffect(() => {
if (!packageVersion) {
document.title = fillTitle(packageName);
} else {
document.title = fillTitle(`${packageName}@${packageVersion}`);
}
}, [packageName, packageVersion]);

useEffect(() => {
const pkgName = getRouterPackageName(params);

setPackageName(pkgName);
setPackageVersion(params.version);
}, [params, setPackageName, setPackageVersion]);

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 />;
}
};
if (hasNotBeenFound) {
return <NotFound />;
}

return (
<DetailContextProvider value={{ packageMeta, packageVersion, readMe, packageName, enableLoading: setIsLoading }}>{renderContent()}</DetailContextProvider>
);
return <VersionLayout />;
};

export default Version;
69 changes: 69 additions & 0 deletions src/pages/Version/VersionContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';

import { callDetailPage, callReadme } from '../../utils/calls';

import getRouterPackageName from './get-route-package-name';
import { DetailContext } from './context';
import isPackageVersionValid from './is-package-version-valid';

interface Params {
scope?: string;
package: string;
version?: string;
}

const VersionContextProvider: React.FC = ({ children }) => {
const { version, package: pkgName, scope } = useParams<Params>();
const [packageName, setPackageName] = useState(getRouterPackageName(pkgName, scope));
const [packageVersion, setPackageVersion] = useState(version);
const [packageMeta, setPackageMeta] = useState();
const [readMe, setReadme] = useState();
const [isLoading, setIsLoading] = useState(true);
const [hasNotBeenFound, setHasNotBeenFound] = useState();

useEffect(() => {
const updatedPackageName = getRouterPackageName(pkgName, scope);
setPackageName(updatedPackageName);
}, [pkgName, scope]);

useEffect(() => {
setPackageVersion(version);
}, [version]);

useEffect(() => {
(async () => {
try {
const packageMeta = await callDetailPage(packageName, packageVersion);
const readMe = await callReadme(packageName, packageVersion);
if (isPackageVersionValid(packageMeta, packageVersion)) {
setReadme(readMe);
setPackageMeta(packageMeta);
setIsLoading(false);
} else {
setIsLoading(false);
setHasNotBeenFound(true);
}
} catch (error) {
setHasNotBeenFound(true);
setIsLoading(false);
}
})();
}, [packageName, packageVersion]);

return (
<DetailContext.Provider
value={{
packageMeta,
packageVersion,
readMe,
packageName,
isLoading,
hasNotBeenFound,
}}>
{children}
</DetailContext.Provider>
);
};

export default VersionContextProvider;
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import React, { FC, ReactElement } from 'react';
import React 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<{}> = () => {
const VersionLayout: React.FC = () => {
return (
<Grid className={'container content'} container={true} data-testid="version-layout" spacing={0}>
<Grid item={true} md={8} xs={12}>
{renderDetail()}
<DetailContainer />
</Grid>
<Grid item={true} md={4} xs={12}>
{renderSidebar()}
<DetailSidebar />
</Grid>
</Grid>
);
};

export { Layout };
export default VersionLayout;
Loading