@verdaccio/ui-components 4.0.0-next-8.4 → 4.0.0-next-8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/build/components/CopyClipboard/CopyToClipBoard.js +2 -2
- package/build/components/HeaderInfoDialog/HeaderInfoDialog.js +2 -2
- package/build/components/Heading/Heading.js +2 -2
- package/build/components/Help/Help.js +1 -1
- package/build/components/Icons/SvgIcon.js +2 -2
- package/build/components/Label/Label.js +2 -2
- package/build/components/LinkExternal/LinkExternal.js +2 -2
- package/build/components/NoItems/NoItems.js +2 -2
- package/build/components/Package/styles.js +13 -13
- package/build/components/Readme/Readme.spec.js +4 -2
- package/build/components/Readme/Readme.spec.js.map +1 -1
- package/build/components/Readme/utils.js +1 -1
- package/build/components/Search/Search.js +13 -2
- package/build/components/Search/SearchItem.js +2 -2
- package/build/components/TextField/TextField.js +2 -2
- package/build/components/Versions/HistoryList.js +6 -1
- package/build/components/Versions/__partials__/unsorted-versions.json +37 -0
- package/build/sections/Header/HeaderSettingsDialog.js +2 -2
- package/build/src/AppTest/App.stories.d.ts +9 -8
- package/build/src/components/Author/Author.stories.d.ts +1 -1
- package/build/src/components/Engines/Engines.stories.d.ts +1 -1
- package/build/src/components/Search/Search.d.ts +3 -1
- package/build/src/components/Search/Search.stories.d.ts +6 -8
- package/build/src/sections/Detail/Detail.stories.d.ts +8 -7
- package/build/src/sections/Header/Header.stories.d.ts +6 -5
- package/build/src/sections/Home/Home.stories.d.ts +6 -5
- package/build/src/sections/SideBar/Sidebar.stories.d.ts +7 -7
- package/build/src/store/api.d.ts +3 -0
- package/build/src/utils/token-generate.d.ts +4 -0
- package/build/store/api.js +1 -1
- package/build/store/models/routes.js +1 -1
- package/build/utils/token-generate.js +33 -0
- package/build/utils/url.js +19 -9
- package/package.json +37 -31
- package/src/AppTest/App.stories.tsx +81 -24
- package/src/AppTest/AppRoute.test.tsx +6 -2
- package/src/components/ActionBar/ActionBar.test.tsx +68 -29
- package/src/components/Author/Author.stories.tsx +1 -1
- package/src/components/Engines/Engines.stories.tsx +12 -12
- package/src/components/Help/Help.tsx +1 -1
- package/src/components/Package/Package.test.tsx +9 -0
- package/src/components/Package/styles.ts +1 -2
- package/src/components/Readme/Readme.spec.tsx +2 -2
- package/src/components/Readme/utils.ts +2 -2
- package/src/components/Search/Search.stories.tsx +24 -2
- package/src/components/Search/Search.tsx +7 -3
- package/src/components/Versions/HistoryList.tsx +10 -2
- package/src/components/Versions/Versions.test.tsx +11 -0
- package/src/components/Versions/__partials__/unsorted-versions.json +37 -0
- package/src/layouts/Version/Version.stories.tsx +26 -0
- package/src/sections/Detail/Detail.stories.tsx +52 -21
- package/src/sections/Header/Header.stories.tsx +33 -10
- package/src/sections/Home/Home.stories.tsx +25 -8
- package/src/sections/SideBar/Sidebar.stories.tsx +53 -28
- package/src/store/api.test.ts +4 -2
- package/src/store/api.ts +1 -1
- package/src/store/models/login.test.ts +25 -19
- package/src/utils/token.test.ts +2 -3
- package/src/utils/url.ts +25 -9
- package/tsconfig.build.json +2 -2
- package/tsconfig.json +3 -2
- package/vitest.config.mjs +2 -1
- package/vitest/api/glob-sidebar.json +0 -12520
- package/vitest/api/got-sidebar.json +0 -23346
- package/vitest/api/home-packages.json +0 -40
- package/vitest/api/jquery-readme.js +0 -3
- package/vitest/api/jquery-sidebar.json +0 -5908
- package/vitest/api/search-verdaccio.json +0 -1192
- package/vitest/api/storybook-readme.js +0 -3
- package/vitest/api/storybook-sidebar.json +0 -53782
- package/vitest/api/storybook-v.json +0 -53782
- package/vitest/identity.js +0 -29
- package/vitest/react-markdown.tsx +0 -7
- package/vitest/server-handlers.ts +0 -82
- package/vitest/server.ts +0 -6
- package/vitest/setup-env.ts +0 -33
- package/vitest/setup.ts +0 -40
- package/vitest/unit/empty-string.ts +0 -1
- package/vitest/unit/empty.ts +0 -1
- package/vitest/vitestSerializer.ts +0 -31
- /package/{vitest/unit/components/__mocks__/token.ts → src/utils/token-generate.ts} +0 -0
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
import { cleanupDownloadMocks, setupDownloadMocks } from '../../../vitest/vitestHelpers';
|
|
3
4
|
import { store } from '../../store/store';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
cleanup,
|
|
7
|
+
fireEvent,
|
|
8
|
+
renderWithStore,
|
|
9
|
+
screen,
|
|
10
|
+
waitFor,
|
|
11
|
+
} from '../../test/test-react-testing-library';
|
|
5
12
|
import ActionBar from './ActionBar';
|
|
6
13
|
|
|
7
14
|
const defaultPackageMeta = {
|
|
@@ -21,25 +28,37 @@ const defaultPackageMeta = {
|
|
|
21
28
|
},
|
|
22
29
|
};
|
|
23
30
|
|
|
31
|
+
beforeAll(() => {
|
|
32
|
+
setupDownloadMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
cleanupDownloadMocks();
|
|
37
|
+
});
|
|
38
|
+
|
|
24
39
|
describe('<ActionBar /> component', () => {
|
|
25
40
|
afterEach(() => {
|
|
26
41
|
cleanup();
|
|
27
42
|
});
|
|
28
43
|
|
|
29
|
-
test('should render the component in default state', () => {
|
|
44
|
+
test('should render the component in default state', async () => {
|
|
30
45
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} />, store);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
await waitFor(() => {
|
|
47
|
+
expect(screen.getByTestId('download-tarball-btn')).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByTestId('BugReportIcon')).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByTestId('HomeIcon')).toBeInTheDocument();
|
|
50
|
+
});
|
|
34
51
|
});
|
|
35
52
|
|
|
36
|
-
test('should not render if data is missing', () => {
|
|
53
|
+
test('should not render if data is missing', async () => {
|
|
37
54
|
// @ts-ignore - testing with missing data
|
|
38
55
|
renderWithStore(<ActionBar packageMeta={undefined} />, store);
|
|
39
|
-
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(screen.queryByTestId('HomeIcon')).toBeNull();
|
|
58
|
+
});
|
|
40
59
|
});
|
|
41
60
|
|
|
42
|
-
test('when there is no action bar data', () => {
|
|
61
|
+
test('when there is no action bar data', async () => {
|
|
43
62
|
const packageMeta = {
|
|
44
63
|
...defaultPackageMeta,
|
|
45
64
|
latest: {
|
|
@@ -54,60 +73,80 @@ describe('<ActionBar /> component', () => {
|
|
|
54
73
|
};
|
|
55
74
|
|
|
56
75
|
renderWithStore(<ActionBar packageMeta={packageMeta} />, store);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
await waitFor(() => {
|
|
77
|
+
expect(screen.queryByTestId('download-tarball-btn')).not.toBeInTheDocument();
|
|
78
|
+
expect(screen.queryByTestId('BugReportIcon')).not.toBeInTheDocument();
|
|
79
|
+
expect(screen.queryByTestId('HomeIcon')).not.toBeInTheDocument();
|
|
80
|
+
});
|
|
60
81
|
});
|
|
61
82
|
|
|
62
|
-
test('when there is a button to download a tarball', () => {
|
|
83
|
+
test('when there is a button to download a tarball', async () => {
|
|
63
84
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} />, store);
|
|
64
|
-
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(screen.getByLabelText('action-bar-action.download-tarball')).toBeTruthy();
|
|
87
|
+
});
|
|
65
88
|
});
|
|
66
89
|
|
|
67
|
-
test('when button to download is disabled', () => {
|
|
90
|
+
test('when button to download is disabled', async () => {
|
|
68
91
|
renderWithStore(
|
|
69
92
|
<ActionBar packageMeta={defaultPackageMeta} showDownloadTarball={false} />,
|
|
70
93
|
store
|
|
71
94
|
);
|
|
72
|
-
|
|
95
|
+
await waitFor(() => {
|
|
96
|
+
expect(screen.queryByTestId('download-tarball-btn')).not.toBeInTheDocument();
|
|
97
|
+
});
|
|
73
98
|
});
|
|
74
99
|
|
|
75
|
-
test('when there is a button to raw manifest', () => {
|
|
100
|
+
test('when there is a button to raw manifest', async () => {
|
|
76
101
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} showRaw={true} />, store);
|
|
77
|
-
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(screen.getByLabelText('action-bar-action.raw')).toBeTruthy();
|
|
104
|
+
});
|
|
78
105
|
});
|
|
79
106
|
|
|
80
107
|
test('when click button to raw manifest open a dialog with viewer', async () => {
|
|
81
108
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} showRaw={true} />, store);
|
|
82
|
-
|
|
109
|
+
await waitFor(() => {
|
|
110
|
+
expect(screen.queryByTestId('rawViewer--dialog')).toBeFalsy();
|
|
111
|
+
});
|
|
83
112
|
|
|
84
113
|
fireEvent.click(screen.getByLabelText('action-bar-action.raw'));
|
|
85
|
-
await
|
|
114
|
+
await waitFor(() => {
|
|
115
|
+
expect(screen.getByTestId('rawViewer--dialog')).toBeInTheDocument();
|
|
116
|
+
});
|
|
86
117
|
|
|
87
118
|
fireEvent.click(screen.getByTestId('close-raw-viewer'));
|
|
88
|
-
await
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
await waitFor(() => {
|
|
120
|
+
expect(screen.queryByTestId('rawViewer--dialog')).toBeFalsy();
|
|
121
|
+
});
|
|
91
122
|
});
|
|
92
123
|
|
|
93
|
-
test('should not display download tarball button', () => {
|
|
124
|
+
test('should not display download tarball button', async () => {
|
|
94
125
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} showRaw={false} />, store);
|
|
95
|
-
|
|
126
|
+
await waitFor(() => {
|
|
127
|
+
expect(screen.queryByLabelText('Download tarball')).toBeFalsy();
|
|
128
|
+
});
|
|
96
129
|
});
|
|
97
130
|
|
|
98
131
|
test('when click button to download ', async () => {
|
|
99
132
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} showRaw={false} />, store);
|
|
100
133
|
fireEvent.click(screen.getByTestId('download-tarball-btn'));
|
|
101
|
-
await
|
|
134
|
+
await waitFor(() => {
|
|
135
|
+
expect(store.getState().loading.models.download).toBe(true);
|
|
136
|
+
});
|
|
102
137
|
});
|
|
103
138
|
|
|
104
|
-
test('should not display show raw button', () => {
|
|
139
|
+
test('should not display show raw button', async () => {
|
|
105
140
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} showRaw={false} />, store);
|
|
106
|
-
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(screen.queryByLabelText('action-bar-action.raw')).toBeFalsy();
|
|
143
|
+
});
|
|
107
144
|
});
|
|
108
145
|
|
|
109
|
-
test('when there is a button to open an issue', () => {
|
|
146
|
+
test('when there is a button to open an issue', async () => {
|
|
110
147
|
renderWithStore(<ActionBar packageMeta={defaultPackageMeta} />, store);
|
|
111
|
-
|
|
148
|
+
await waitFor(() => {
|
|
149
|
+
expect(screen.getByTestId('BugReportIcon')).toBeInTheDocument();
|
|
150
|
+
});
|
|
112
151
|
});
|
|
113
152
|
});
|
|
@@ -6,12 +6,14 @@ export default {
|
|
|
6
6
|
title: 'Components/Sidebar/Engines',
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export const
|
|
9
|
+
export const EnginesAll: any = () => (
|
|
10
10
|
<Engines
|
|
11
11
|
packageMeta={{
|
|
12
12
|
latest: {
|
|
13
13
|
engines: {
|
|
14
|
-
node: '>=
|
|
14
|
+
node: '>= 18',
|
|
15
|
+
pnpm: '>7',
|
|
16
|
+
yarn: '>3',
|
|
15
17
|
npm: '>3',
|
|
16
18
|
},
|
|
17
19
|
},
|
|
@@ -19,51 +21,49 @@ export const EnginesNpmNode: any = () => (
|
|
|
19
21
|
/>
|
|
20
22
|
);
|
|
21
23
|
|
|
22
|
-
export const
|
|
24
|
+
export const EnginesNpmNode: any = () => (
|
|
23
25
|
<Engines
|
|
24
26
|
packageMeta={{
|
|
25
27
|
latest: {
|
|
26
28
|
engines: {
|
|
27
|
-
node: '>=
|
|
29
|
+
node: '>= 0.1.98',
|
|
30
|
+
npm: '>3',
|
|
28
31
|
},
|
|
29
32
|
},
|
|
30
33
|
}}
|
|
31
34
|
/>
|
|
32
35
|
);
|
|
33
36
|
|
|
34
|
-
export const
|
|
37
|
+
export const EnginesNode: any = () => (
|
|
35
38
|
<Engines
|
|
36
39
|
packageMeta={{
|
|
37
40
|
latest: {
|
|
38
41
|
engines: {
|
|
39
|
-
|
|
42
|
+
node: '>= 18',
|
|
40
43
|
},
|
|
41
44
|
},
|
|
42
45
|
}}
|
|
43
46
|
/>
|
|
44
47
|
);
|
|
45
48
|
|
|
46
|
-
export const
|
|
49
|
+
export const EnginesYarn: any = () => (
|
|
47
50
|
<Engines
|
|
48
51
|
packageMeta={{
|
|
49
52
|
latest: {
|
|
50
53
|
engines: {
|
|
51
|
-
|
|
54
|
+
yarn: '>3',
|
|
52
55
|
},
|
|
53
56
|
},
|
|
54
57
|
}}
|
|
55
58
|
/>
|
|
56
59
|
);
|
|
57
60
|
|
|
58
|
-
export const
|
|
61
|
+
export const EnginesPnpm: any = () => (
|
|
59
62
|
<Engines
|
|
60
63
|
packageMeta={{
|
|
61
64
|
latest: {
|
|
62
65
|
engines: {
|
|
63
|
-
node: '>= 18',
|
|
64
66
|
pnpm: '>7',
|
|
65
|
-
yarn: '>3',
|
|
66
|
-
npm: '>3',
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
69
|
}}
|
|
@@ -40,7 +40,7 @@ const Help: React.FC = () => {
|
|
|
40
40
|
t('help.second-step'),
|
|
41
41
|
t('help.second-step-command-line', { registryUrl })
|
|
42
42
|
)}
|
|
43
|
-
<Typography variant="
|
|
43
|
+
<Typography variant="body1">{t('help.third-step')}</Typography>
|
|
44
44
|
</CardContent>
|
|
45
45
|
<CardActions>
|
|
46
46
|
<Button color="primary" href="https://verdaccio.org/docs/cli-registry" size="small">
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { MemoryRouter } from 'react-router';
|
|
3
3
|
|
|
4
4
|
import { store } from '../../';
|
|
5
|
+
import { cleanupDownloadMocks, setupDownloadMocks } from '../../../vitest/vitestHelpers';
|
|
5
6
|
import {
|
|
6
7
|
cleanup,
|
|
7
8
|
fireEvent,
|
|
@@ -34,6 +35,14 @@ const props = {
|
|
|
34
35
|
keywords: ['verdaccio-core'],
|
|
35
36
|
};
|
|
36
37
|
|
|
38
|
+
beforeAll(() => {
|
|
39
|
+
setupDownloadMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterAll(() => {
|
|
43
|
+
cleanupDownloadMocks();
|
|
44
|
+
});
|
|
45
|
+
|
|
37
46
|
describe('<Package /> component', () => {
|
|
38
47
|
afterEach(() => {
|
|
39
48
|
cleanup();
|
|
@@ -71,8 +71,7 @@ export const GridRightAligned = styled(Grid)({
|
|
|
71
71
|
|
|
72
72
|
export const Wrapper = styled(List)<{ theme?: Theme }>(({ theme }) => ({
|
|
73
73
|
'&:hover': {
|
|
74
|
-
backgroundColor:
|
|
75
|
-
theme.palette.mode == 'dark' ? theme.palette.secondary.main : theme.palette.greyLight2,
|
|
74
|
+
backgroundColor: theme.palette.action.hover,
|
|
76
75
|
},
|
|
77
76
|
}));
|
|
78
77
|
|
|
@@ -7,8 +7,8 @@ describe('<Readme /> component', () => {
|
|
|
7
7
|
beforeEach(cleanup);
|
|
8
8
|
|
|
9
9
|
test('should load the component in default state', () => {
|
|
10
|
-
const
|
|
11
|
-
expect(
|
|
10
|
+
const { getByText } = render(<Readme description="test" />);
|
|
11
|
+
expect(getByText('test')).toBeInTheDocument();
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
test('should dangerously set html', () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import DOMPurify from 'dompurify';
|
|
2
2
|
import { Marked } from 'marked';
|
|
3
3
|
import { markedHighlight } from 'marked-highlight';
|
|
4
4
|
|
|
@@ -22,5 +22,5 @@ marked.setOptions({
|
|
|
22
22
|
|
|
23
23
|
export function parseReadme(readme: string): string | void {
|
|
24
24
|
const html = marked.parse(readme);
|
|
25
|
-
return DOMPurify.sanitize(html);
|
|
25
|
+
return DOMPurify.sanitize(html as string);
|
|
26
26
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
/* eslint-disable verdaccio/jsx-spread */
|
|
2
2
|
import Box from '@mui/material/Box';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
import { HttpResponse, http } from 'msw';
|
|
3
5
|
import React from 'react';
|
|
4
6
|
import { MemoryRouter } from 'react-router';
|
|
5
7
|
|
|
8
|
+
import Home from '../../sections/Home';
|
|
6
9
|
import Search from './Search';
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
type Story = StoryObj<typeof Search>;
|
|
12
|
+
const meta: Meta<typeof Search> = {
|
|
9
13
|
title: 'Components/Header/Search',
|
|
14
|
+
component: Home,
|
|
10
15
|
};
|
|
11
16
|
|
|
12
|
-
export
|
|
17
|
+
export default meta;
|
|
18
|
+
|
|
19
|
+
export const SearchByQuery: Story = {
|
|
13
20
|
render: (args) => (
|
|
14
21
|
<MemoryRouter initialEntries={[`/-/web/detail/storybook`]}>
|
|
15
22
|
<Box
|
|
@@ -27,4 +34,19 @@ export const SearchByQuery = {
|
|
|
27
34
|
</Box>
|
|
28
35
|
</MemoryRouter>
|
|
29
36
|
),
|
|
37
|
+
parameters: {
|
|
38
|
+
msw: {
|
|
39
|
+
handlers: [
|
|
40
|
+
http.get('https://my-registry.org/-/verdaccio/data/sidebar/storybook', () => {
|
|
41
|
+
return HttpResponse.json(require('../../../vitest/api/storybook-sidebar.json'));
|
|
42
|
+
}),
|
|
43
|
+
http.get('https://my-registry.org/-/verdaccio/data/package/readme/storybook', () => {
|
|
44
|
+
return HttpResponse.json(require('../../../vitest/api/storybook-readme')());
|
|
45
|
+
}),
|
|
46
|
+
http.get('https://my-registry.org/-/verdaccio/data/search/*', () => {
|
|
47
|
+
return HttpResponse.json(require('../../../vitest/api/search-verdaccio.json'));
|
|
48
|
+
}),
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
30
52
|
};
|
|
@@ -118,6 +118,8 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
const renderOption = (props, option) => {
|
|
121
|
+
const { key, ...otherProps } = props;
|
|
122
|
+
|
|
121
123
|
if (searchRemote) {
|
|
122
124
|
const item: SearchResultWeb = option.package;
|
|
123
125
|
const isPrivate = option?.verdaccioPrivate;
|
|
@@ -125,7 +127,8 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
|
|
|
125
127
|
const isRemote = !isCached && !isPrivate;
|
|
126
128
|
return (
|
|
127
129
|
<SearchItem
|
|
128
|
-
{
|
|
130
|
+
key={key}
|
|
131
|
+
{...otherProps}
|
|
129
132
|
description={item?.description}
|
|
130
133
|
isCached={isCached}
|
|
131
134
|
isPrivate={isPrivate}
|
|
@@ -137,7 +140,8 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
|
|
|
137
140
|
} else {
|
|
138
141
|
return (
|
|
139
142
|
<SearchItem
|
|
140
|
-
{
|
|
143
|
+
key={key}
|
|
144
|
+
{...otherProps}
|
|
141
145
|
description={option?.description}
|
|
142
146
|
name={option?.name}
|
|
143
147
|
version={option?.version}
|
|
@@ -161,4 +165,4 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
|
|
|
161
165
|
);
|
|
162
166
|
};
|
|
163
167
|
|
|
164
|
-
export default withRouter(Search);
|
|
168
|
+
export default withRouter(Search as any);
|
|
@@ -39,7 +39,11 @@ const VersionsHistoryList: React.FC<Props> = ({ versions, packageName, time }) =
|
|
|
39
39
|
return (
|
|
40
40
|
<List dense={true}>
|
|
41
41
|
{Object.keys(listVersions)
|
|
42
|
-
.
|
|
42
|
+
.sort((a, b) => {
|
|
43
|
+
const timeA = time[a] ? new Date(time[a]).getTime() : 0;
|
|
44
|
+
const timeB = time[b] ? new Date(time[b]).getTime() : 0;
|
|
45
|
+
return timeB - timeA;
|
|
46
|
+
})
|
|
43
47
|
.map((version) => (
|
|
44
48
|
<ListItem
|
|
45
49
|
className="version-item"
|
|
@@ -48,7 +52,11 @@ const VersionsHistoryList: React.FC<Props> = ({ versions, packageName, time }) =
|
|
|
48
52
|
sx={{ pr: 0 }}
|
|
49
53
|
>
|
|
50
54
|
<Link to={`${Route.DETAIL}${packageName}/v/${version}`} variant="outline">
|
|
51
|
-
<ListItemText
|
|
55
|
+
<ListItemText
|
|
56
|
+
data-testid={`version-list-link`}
|
|
57
|
+
disableTypography={false}
|
|
58
|
+
primary={version}
|
|
59
|
+
/>
|
|
52
60
|
</Link>
|
|
53
61
|
{typeof versions[version]?.deprecated === 'string' ? (
|
|
54
62
|
<Chip
|
|
@@ -7,6 +7,7 @@ import { fireEvent, render, screen } from '../../test/test-react-testing-library
|
|
|
7
7
|
import Versions, { Props } from './Versions';
|
|
8
8
|
import data from './__partials__/data.json';
|
|
9
9
|
import dataDeprecated from './__partials__/deprecated-versions.json';
|
|
10
|
+
import dataUnsorted from './__partials__/unsorted-versions.json';
|
|
10
11
|
|
|
11
12
|
const VersionsComponent: React.FC<Props> = (props) => (
|
|
12
13
|
<MemoryRouter>
|
|
@@ -68,5 +69,15 @@ describe('<Version /> component', () => {
|
|
|
68
69
|
expect(screen.getByText('0.0.1')).toBeInTheDocument();
|
|
69
70
|
});
|
|
70
71
|
|
|
72
|
+
test('should render versions sorted by timestamp in descending order', () => {
|
|
73
|
+
render(<VersionsComponent packageMeta={dataUnsorted} packageName={'dummy'} />);
|
|
74
|
+
|
|
75
|
+
const versionElements = screen.getAllByTestId('version-list-link');
|
|
76
|
+
const versions = versionElements.map((el) => el.textContent);
|
|
77
|
+
|
|
78
|
+
// Expected order based on timestamps in unsorted-versions.json:
|
|
79
|
+
expect(versions).toEqual(['1.0.1', '1.0.0', '0.1.1', '0.1.0']);
|
|
80
|
+
});
|
|
81
|
+
|
|
71
82
|
test.todo('should click on version link');
|
|
72
83
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"versions": {
|
|
3
|
+
"1.0.0": {
|
|
4
|
+
"name": "dummy",
|
|
5
|
+
"version": "1.0.0"
|
|
6
|
+
},
|
|
7
|
+
"0.1.0": {
|
|
8
|
+
"name": "dummy",
|
|
9
|
+
"version": "0.1.0"
|
|
10
|
+
},
|
|
11
|
+
"0.1.1": {
|
|
12
|
+
"name": "dummy",
|
|
13
|
+
"version": "0.1.1"
|
|
14
|
+
},
|
|
15
|
+
"1.0.1": {
|
|
16
|
+
"name": "dummy",
|
|
17
|
+
"version": "1.0.1"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"time": {
|
|
21
|
+
"created": "2012-02-19T21:35:53.514Z",
|
|
22
|
+
"modified": "2024-11-21T06:32:43.761Z",
|
|
23
|
+
"1.0.0": "2025-01-10T14:55:23.763Z",
|
|
24
|
+
"0.1.0": "2012-02-19T21:35:54.455Z",
|
|
25
|
+
"0.1.1": "2012-02-20T06:12:03.403Z",
|
|
26
|
+
"1.0.1": "2025-01-10T15:11:36.318Z"
|
|
27
|
+
},
|
|
28
|
+
"users": {},
|
|
29
|
+
"dist-tags": {
|
|
30
|
+
"latest": "1.0.1"
|
|
31
|
+
},
|
|
32
|
+
"_id": "dummy",
|
|
33
|
+
"latest": {
|
|
34
|
+
"name": "dummy",
|
|
35
|
+
"version": "1.0.1"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { HttpResponse, http } from 'msw';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import { MemoryRouter, Route } from 'react-router';
|
|
4
5
|
|
|
@@ -11,6 +12,7 @@ const meta: Meta<typeof VersionLayout> = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export default meta;
|
|
15
|
+
|
|
14
16
|
type Story = StoryObj<typeof VersionLayout>;
|
|
15
17
|
|
|
16
18
|
export const Primary: Story = {
|
|
@@ -26,6 +28,18 @@ export const Primary: Story = {
|
|
|
26
28
|
</MemoryRouter>
|
|
27
29
|
);
|
|
28
30
|
},
|
|
31
|
+
parameters: {
|
|
32
|
+
msw: {
|
|
33
|
+
handlers: [
|
|
34
|
+
http.get('https://my-registry.org/-/verdaccio/data/sidebar/storybook', () => {
|
|
35
|
+
return HttpResponse.json(require('../../../vitest/api/storybook-sidebar.json'));
|
|
36
|
+
}),
|
|
37
|
+
http.get('https://my-registry.org/-/verdaccio/data/package/readme/storybook', () => {
|
|
38
|
+
return HttpResponse.json(require('../../../vitest/api/storybook-readme')());
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
29
43
|
};
|
|
30
44
|
|
|
31
45
|
export const jQuery: Story = {
|
|
@@ -41,4 +55,16 @@ export const jQuery: Story = {
|
|
|
41
55
|
</MemoryRouter>
|
|
42
56
|
);
|
|
43
57
|
},
|
|
58
|
+
parameters: {
|
|
59
|
+
msw: {
|
|
60
|
+
handlers: [
|
|
61
|
+
http.get('https://my-registry.org/-/verdaccio/data/sidebar/jquery', () => {
|
|
62
|
+
return HttpResponse.json(require('../../../vitest/api/jquery-sidebar.json'));
|
|
63
|
+
}),
|
|
64
|
+
http.get('https://my-registry.org/-/verdaccio/data/package/readme/jquery', () => {
|
|
65
|
+
return HttpResponse.json(require('../../../vitest/api/jquery-readme')());
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
44
70
|
};
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { HttpResponse, http } from 'msw';
|
|
1
3
|
import React from 'react';
|
|
2
4
|
import { MemoryRouter, Route } from 'react-router';
|
|
3
5
|
|
|
4
6
|
import { VersionProvider } from '../../providers';
|
|
5
7
|
import Detail from './Detail';
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
const meta: Meta<typeof Detail> = {
|
|
8
10
|
title: 'Sections/Detail',
|
|
11
|
+
component: Detail,
|
|
9
12
|
};
|
|
10
13
|
|
|
11
|
-
export
|
|
12
|
-
<MemoryRouter initialEntries={[`/-/web/detail/storybook`]}>
|
|
13
|
-
<Route exact={true} path="/-/web/detail/:package">
|
|
14
|
-
<VersionProvider>
|
|
15
|
-
<Detail />
|
|
16
|
-
</VersionProvider>
|
|
17
|
-
</Route>
|
|
18
|
-
</MemoryRouter>
|
|
19
|
-
);
|
|
14
|
+
export default meta;
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
type Story = StoryObj<typeof Detail>;
|
|
17
|
+
const getDetailApp = (url: string) => (
|
|
18
|
+
<MemoryRouter initialEntries={[url]}>
|
|
23
19
|
<Route exact={true} path="/-/web/detail/:package">
|
|
24
20
|
<VersionProvider>
|
|
25
21
|
<Detail />
|
|
@@ -28,12 +24,47 @@ export const DetailJquery: any = () => (
|
|
|
28
24
|
</MemoryRouter>
|
|
29
25
|
);
|
|
30
26
|
|
|
31
|
-
export const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
27
|
+
export const DetailStorybook: Story = {
|
|
28
|
+
render: () => getDetailApp(`/-/web/detail/storybook`),
|
|
29
|
+
parameters: {
|
|
30
|
+
msw: {
|
|
31
|
+
handlers: [
|
|
32
|
+
http.get('https://my-registry.org/-/verdaccio/data/sidebar/storybook', () => {
|
|
33
|
+
return HttpResponse.json(require('../../../vitest/api/storybook-sidebar.json'));
|
|
34
|
+
}),
|
|
35
|
+
http.get('https://my-registry.org/-/verdaccio/data/package/readme/storybook', () => {
|
|
36
|
+
return HttpResponse.json(require('../../../vitest/api/storybook-readme')());
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const DetailJquery: Story = {
|
|
44
|
+
render: () => getDetailApp(`/-/web/detail/jquery`),
|
|
45
|
+
parameters: {
|
|
46
|
+
msw: {
|
|
47
|
+
handlers: [
|
|
48
|
+
http.get('https://my-registry.org/-/verdaccio/data/sidebar/jquery', () => {
|
|
49
|
+
return HttpResponse.json(require('../../../vitest/api/jquery-sidebar.json'));
|
|
50
|
+
}),
|
|
51
|
+
http.get('https://my-registry.org/-/verdaccio/data/package/readme/jquery', () => {
|
|
52
|
+
return HttpResponse.json(require('../../../vitest/api/jquery-readme')());
|
|
53
|
+
}),
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const DetailForbidden: Story = {
|
|
60
|
+
render: () => getDetailApp(`/-/web/detail/JSONStream`),
|
|
61
|
+
parameters: {
|
|
62
|
+
msw: {
|
|
63
|
+
handlers: [
|
|
64
|
+
http.get('https://my-registry.org/-/verdaccio/data/sidebar/JSONStream', () => {
|
|
65
|
+
return new HttpResponse('unauthorized', { status: 401 });
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|