chore(deps): upgrade storybook to v8 (#5056)

* chore(deps): upgrade storybook to v8

* Update
This commit is contained in:
Marc Bernard
2025-01-27 15:28:22 +01:00
committed by GitHub
parent 3935e2f0a9
commit eb4a24b2fc
24 changed files with 1462 additions and 5138 deletions

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/ui-components': patch
---
chore(deps): upgrade storybook to v8

View File

@@ -32,7 +32,7 @@ jobs:
- name: Install pnpm
run: |
corepack enable
corepack prepare
corepack install
- name: Install
run: pnpm install
- name: Build storybook

View File

@@ -1,6 +1,6 @@
.storybook
public/
msw
jest
vitest
__snapshots__
*.map

View File

@@ -0,0 +1,41 @@
import * as Flags from 'country-flag-icons/react/3x2';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const DEFAULT_LANGUAGE = 'en-US';
export const listLanguages = [
{ lng: DEFAULT_LANGUAGE, icon: Flags.US, menuKey: 'lng.english' },
{ lng: 'cs-CZ', icon: Flags.CZ, menuKey: 'lng.czech' },
{ lng: 'pt-BR', icon: Flags.BR, menuKey: 'lng.portuguese' },
{ lng: 'es-ES', icon: Flags.ES, menuKey: 'lng.spanish' },
{ lng: 'de-DE', icon: Flags.DE, menuKey: 'lng.german' },
];
const whitelist = listLanguages.reduce((acc, item) => {
acc.push(item.lng);
return acc;
}, [] as string[]);
const resources = listLanguages.reduce((acc, item) => {
// Use English for all languages
acc[item.lng] = { translation: require(`./ui.json`) };
return acc;
}, {});
i18n.use(initReactI18next).init({
lng: DEFAULT_LANGUAGE,
fallbackLng: DEFAULT_LANGUAGE,
whitelist,
load: 'currentOnly',
react: {
useSuspense: false,
},
resources,
debug: false,
interpolation: {
escapeValue: false,
},
});
export default i18n;

View File

@@ -0,0 +1,206 @@
{
"copy-to-clipboard": "Copy to clipboard",
"author-anonymous": "Anonymous",
"author-unknown": "Unknown",
"action-bar-action": {
"visit-home-page": "Visit homepage",
"open-an-issue": "Open an issue",
"download-tarball": "Download tarball",
"raw": "View manifest",
"raw-title": "Manifest of {{package}}"
},
"dialog": {
"registry-info": {
"title": "Information"
},
"settings": {
"title": "Configuration"
},
"license": "License",
"totalContributors": "Total contributors"
},
"header": {
"documentation": "Documentation",
"registry-info": "Registry Information",
"registry-info-link": "Learn more",
"settings": "Settings",
"help": "Help",
"registry-no-conf": "No configurations available",
"greetings": "Hi "
},
"search": {
"packages": "Search Packages",
"isPrivate": "Private",
"isRemote": "Remote",
"isCached": "Cached"
},
"autoComplete": {
"loading": "Loading...",
"no-results-found": "No results found",
"clear": "Clear",
"expand": "Expand",
"collapse": "Collapse"
},
"tab": {
"uplinks": "Uplinks",
"versions": "Versions",
"dependencies": "Dependencies",
"readme": "Readme"
},
"uplinks": {
"title": "Uplinks",
"no-items": "{{name}} has no uplinks."
},
"versions": {
"current-tags": "Current Tags",
"version-history": "Version History",
"not-available": "Not available",
"deprecated": "Deprecated",
"search.placeholder": "Search for a version by semver range, e.g., ^1.0.0.",
"hide-deprecated": "All deprecated versions are hidden by global configuration"
},
"package": {
"published-on": "Published {{time}}",
"version": "v{{version}}",
"visit-home-page": "Visit homepage",
"homepage": "Homepage",
"open-an-issue": "Open an issue",
"bugs": "Bugs",
"download": "Download {{what}}",
"the-tar-file": "the tar file",
"tarball": "Tarball"
},
"dependencies": {
"has-no-dependencies": "{{package}} has no dependencies.",
"dependency-block": "{{package}}: {{version}}",
"dependency-block-bundle": "{{package}}"
},
"form": {
"username": "Username",
"password": "Password"
},
"form-placeholder": {
"username": "Your username",
"password": "Your strong password"
},
"form-validation": {
"required-field": "This field is required",
"required-min-length": "This field required with a minimum length of {{length}}",
"unable-to-sign-in": "Unable to sign in",
"username-or-password-cant-be-empty": "Username or password can't be empty!"
},
"help": {
"title": "No Package Published Yet.",
"sub-title": "To publish your first package just:",
"first-step": "1. Create user",
"first-step-command-line": "npm adduser --registry {{registryUrl}}",
"second-step": "2. Publish",
"second-step-command-line": "npm publish --registry {{registryUrl}}",
"third-step": "3. Refresh this page"
},
"sidebar": {
"detail": {
"latest-version": "Latest v{{version}}",
"version": "v{{version}}",
"published": "Published"
},
"installation": {
"title": "Installation",
"latest": "latest version",
"global": "global package",
"yarnModern": "yarn modern syntax"
},
"repository": {
"title": "Repository"
},
"author": {
"title": "Author"
},
"distribution": {
"title": "Distribution",
"license": "License",
"size": "Size",
"file-count": "File Count"
},
"keywords": {
"title": "Keywords"
},
"maintainers": {
"title": "Maintainers"
},
"contributors": {
"title": "Contributors"
},
"engines": {
"npm-version": "Npm Version",
"pnpm-version": "Pnpm Version",
"yarn-version": "Yarn Version",
"node-js": "Node.js"
}
},
"footer": {
"powered-by": "Powered by",
"made-with-love-on": "Made with <0>♥</0> on"
},
"button": {
"close": "Close",
"cancel": "Cancel",
"login": "Login",
"logout": "Logout",
"go-to-the-home-page": "Go to the home page",
"learn-more": "Learn More",
"fund-this-package": "<0>Fund</0> this package"
},
"error": {
"unspecific": "Something went wrong.",
"404": {
"sorry-we-could-not-find-it": "Sorry, we couldn't find it..."
},
"401": {
"sorry-no-access": "Sorry, you need credentials access to see this page."
},
"app-context-not-correct-used": "The app context was not used correctly",
"theme-context-not-correct-used": "The theme context was not used correctly",
"package-meta-is-required-at-detail-context": "packageMeta is required at DetailContext"
},
"lng": {
"english": "English",
"czech": "Czech",
"japanese": "Japanese",
"portuguese": "Portuguese",
"spanish": "Spanish",
"irish": "Irish",
"german": "German",
"italian": "Italian",
"french": "French",
"russian": "Russian",
"turkish": "Turkish",
"ukraine": "Ukraine",
"khmer": "Khmer",
"chinese": "Chinese Simplified",
"chineseTraditional": "Chinese Traditional"
},
"flag": {
"austria": "Austria",
"australia": "Australia",
"brazil": "Brazil",
"canada": "Canada",
"spain": "Spain",
"nicaragua": "Nicaragua",
"india": "India",
"china": "China",
"germany": "Germany",
"taiwan": "Taiwan"
},
"packageManagers": {
"title": "Package Managers",
"description": "This is the configuration details for the registry. Each package manager could have different configuration, expand each section for more details. If the section is disable review your configuration.",
"yarnclassicDetails": "Yarn classic configuration differs from Yarn 2+ configuration. For more details, please visit [Yarn Classic](https://verdaccio.org/docs/cli-registry#yarn-1x).",
"yarnBerryDetails": "Yarn Berry does not support the `--registry` flag, instead all configurarion should be defined on the `yarnrc.yaml` file in the root of your project. For more details, please visit [Yarn Berry](https://verdaccio.org/docs/cli-registry#yarn-berry-2x)."
},
"language": {
"title": "Translations",
"contribute": "If your language is not listed here or is not fully translated, please contribute to the translation. You can find the source code on [our main repository](https://github.com/verdaccio/verdaccio/blob/master/packages/plugins/ui-theme/src/i18n/ABOUT_TRANSLATIONS.md)",
"description": "This is the configuration details for the language. Select a language to select a client side translation."
}
}

View File

@@ -1,13 +0,0 @@
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: '@storybook/react',
layout: 'centered',
core: {
builder: 'webpack5',
},
};

View File

@@ -0,0 +1,25 @@
import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-webpack5-compiler-babel',
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
staticDirs: ['../public'],
typescript: {
reactDocgen: 'react-docgen-typescript',
},
};
export default config;

View File

@@ -1,10 +1,12 @@
<script>
window.__VERDACCIO_BASENAME_UI_OPTIONS = {
"title": "Verdaccio Local Dev",
"sort_packages": "asc", "primary_color": null,
"pkgManagers": ["npm", "yarn", "pnpm"],
"version": "1.0.0", "flags": { "searchRemote": true },
"filename": "index.html",
"base": "http://localhost:9000/"
}
</script>
window.__VERDACCIO_BASENAME_UI_OPTIONS = {
title: 'Verdaccio Local Dev',
sort_packages: 'asc',
primary_color: null,
pkgManagers: ['npm', 'yarn', 'pnpm'],
version: '1.0.0',
flags: { searchRemote: true },
filename: 'index.html',
base: 'http://localhost:9000/',
};
</script>

View File

@@ -1,55 +0,0 @@
import Flags from 'country-flag-icons/react/3x2';
import { Provider } from 'react-redux';
import config from '../../plugins/ui-theme/src/i18n/config';
import {
AppConfigurationProvider,
PersistenceSettingProvider,
StyleBaseline,
ThemeProvider,
TranslatorProvider,
store,
} from '../src/';
const DEFAULT_LANGUAGE = 'en-US';
const listLanguages = [
{ lng: DEFAULT_LANGUAGE, icon: Flags.US, menuKey: 'lng.english' },
{ lng: 'cs-CZ', icon: Flags.CZ, menuKey: 'lng.czech' },
{ lng: 'pt-BR', icon: Flags.BR, menuKey: 'lng.portuguese' },
{ lng: 'es-ES', icon: Flags.ES, menuKey: 'lng.spanish' },
{ lng: 'de-DE', icon: Flags.DE, menuKey: 'lng.german' },
];
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
// preview-head file contains the __VERDACCIO_BASENAME_UI_OPTIONS
// required by AppConfigurationProvider
export const withMuiTheme = (Story) => (
<Provider store={store}>
<TranslatorProvider onMount={() => {}} i18n={config} listLanguages={listLanguages}>
<PersistenceSettingProvider>
<AppConfigurationProvider>
<ThemeProvider>
<StyleBaseline />
<Story />
</ThemeProvider>
</AppConfigurationProvider>
</PersistenceSettingProvider>
</TranslatorProvider>
</Provider>
);
if (typeof global.process === 'undefined') {
const { worker } = require('../msw/browser');
worker.start();
}
export const decorators = [withMuiTheme];

View File

@@ -0,0 +1,55 @@
import type { Preview, StoryFn } from '@storybook/react';
import { initialize, mswLoader } from 'msw-storybook-addon';
import React from 'react';
import { Provider } from 'react-redux';
import {
AppConfigurationProvider,
PersistenceSettingProvider,
StyleBaseline,
ThemeProvider,
TranslatorProvider,
store,
} from '../src';
import i18n, { listLanguages } from './i18n';
// preview-head file contains the __VERDACCIO_BASENAME_UI_OPTIONS
// required by AppConfigurationProvider
export const withMuiTheme = (Story: StoryFn) => (
<Provider store={store}>
<TranslatorProvider onMount={() => ({})} i18n={i18n} listLanguages={listLanguages}>
<PersistenceSettingProvider>
<AppConfigurationProvider>
<ThemeProvider>
<StyleBaseline />
<Story />
</ThemeProvider>
</AppConfigurationProvider>
</PersistenceSettingProvider>
</TranslatorProvider>
</Provider>
);
/*
* Initializes MSW
*/
initialize();
/*
* Setup the preview
*/
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
decorators: [withMuiTheme],
loaders: [mswLoader],
};
export default preview;

View File

@@ -1,5 +0,0 @@
import { setupWorker } from 'msw';
import { handlers } from '../jest/server-handlers';
export const worker = setupWorker(...handlers);

View File

@@ -16,8 +16,8 @@
"watch": "babel src/ --out-dir build/ --watch --copy-files --no-copy-ignored --extensions \".ts,.tsx\" --source-maps --ignore \"src/**/*.stories.tsx\" --ignore \"src/**/*.test.tsx\" --ignore \"src/**/*.test.ts\"",
"build": "pnpm run build:js && pnpm run build:types",
"netlify:ui:deploy": "npx netlify-cli deploy --dir=./packages/ui-components/storybook-static --prod",
"start": "start-storybook -p 6006 -s ./public",
"build-storybook": "build-storybook"
"start": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@emotion/react": "11.11.4",
@@ -60,13 +60,13 @@
"devDependencies": {
"@babel/core": "7.26.0",
"@emotion/babel-plugin": "11.12.0",
"@storybook/addon-actions": "^6.5.15",
"@storybook/addon-essentials": "^6.5.15",
"@storybook/addon-interactions": "^6.5.15",
"@storybook/addon-links": "^6.5.15",
"@storybook/builder-webpack5": "^6.5.15",
"@storybook/manager-webpack5": "^6.5.15",
"@storybook/react": "^6.5.15",
"@storybook/addon-actions": "^8.4.7",
"@storybook/addon-essentials": "^8.4.7",
"@storybook/addon-interactions": "^8.4.7",
"@storybook/addon-links": "^8.4.7",
"@storybook/addon-webpack5-compiler-babel": "3.0.5",
"@storybook/react": "^8.4.7",
"@storybook/react-webpack5": "8.4.7",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.1.0",
@@ -75,16 +75,20 @@
"@types/unist": "^2.0.0",
"@verdaccio/types": "workspace:13.0.0-next-8.2",
"@vitejs/plugin-react": "^4.3.4",
"jsdom": "^25.0.0",
"babel-loader": "9.2.1",
"jsdom": "^25.0.0",
"mockdate": "3.0.5",
"msw": "2.7.0",
"msw-storybook-addon": "^2.0.4",
"mutationobserver-shim": "0.3.7",
"msw": "0.49.3",
"storybook": "8.4.7",
"typescript": "4.9.5",
"vitest": "3.0.4",
"whatwg-fetch": "3.6.20"
},
"msw": {
"workerDirectory": "public"
"workerDirectory": [
"public"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -2,13 +2,15 @@
/* tslint:disable */
/**
* Mock Service Worker (0.49.2).
* Mock Service Worker.
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.
*/
const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70';
const PACKAGE_VERSION = '2.7.0';
const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f';
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse');
const activeClientIds = new Set();
self.addEventListener('install', function () {
@@ -47,7 +49,10 @@ self.addEventListener('message', async function (event) {
case 'INTEGRITY_CHECK_REQUEST': {
sendToClient(client, {
type: 'INTEGRITY_CHECK_RESPONSE',
payload: INTEGRITY_CHECKSUM,
payload: {
packageVersion: PACKAGE_VERSION,
checksum: INTEGRITY_CHECKSUM,
},
});
break;
}
@@ -57,7 +62,12 @@ self.addEventListener('message', async function (event) {
sendToClient(client, {
type: 'MOCKING_ENABLED',
payload: true,
payload: {
client: {
id: client.id,
frameType: client.frameType,
},
},
});
break;
}
@@ -86,12 +96,6 @@ self.addEventListener('message', async function (event) {
self.addEventListener('fetch', function (event) {
const { request } = event;
const accept = request.headers.get('accept') || '';
// Bypass server-sent events.
if (accept.includes('text/event-stream')) {
return;
}
// Bypass navigation requests.
if (request.mode === 'navigate') {
@@ -112,29 +116,8 @@ self.addEventListener('fetch', function (event) {
}
// Generate unique request ID.
const requestId = Math.random().toString(16).slice(2);
event.respondWith(
handleRequest(event, requestId).catch((error) => {
if (error.name === 'NetworkError') {
console.warn(
'[MSW] Successfully emulated a network error for the "%s %s" request.',
request.method,
request.url
);
return;
}
// At this point, any exception indicates an issue with the original request/response.
console.error(
`\
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
request.method,
request.url,
`${error.name}: ${error.message}`
);
})
);
const requestId = crypto.randomUUID();
event.respondWith(handleRequest(event, requestId));
});
async function handleRequest(event, requestId) {
@@ -146,20 +129,24 @@ async function handleRequest(event, requestId) {
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
(async function () {
const clonedResponse = response.clone();
sendToClient(client, {
type: 'RESPONSE',
payload: {
requestId,
type: clonedResponse.type,
ok: clonedResponse.ok,
status: clonedResponse.status,
statusText: clonedResponse.statusText,
body: clonedResponse.body === null ? null : await clonedResponse.text(),
headers: Object.fromEntries(clonedResponse.headers.entries()),
redirected: clonedResponse.redirected,
const responseClone = response.clone();
sendToClient(
client,
{
type: 'RESPONSE',
payload: {
requestId,
isMockedResponse: IS_MOCKED_RESPONSE in response,
type: responseClone.type,
status: responseClone.status,
statusText: responseClone.statusText,
body: responseClone.body,
headers: Object.fromEntries(responseClone.headers.entries()),
},
},
});
[responseClone.body]
);
})();
}
@@ -173,6 +160,10 @@ async function handleRequest(event, requestId) {
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId);
if (activeClientIds.has(event.clientId)) {
return client;
}
if (client?.frameType === 'top-level') {
return client;
}
@@ -195,20 +186,32 @@ async function resolveMainClient(event) {
async function getResponse(event, client, requestId) {
const { request } = event;
const clonedRequest = request.clone();
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const requestClone = request.clone();
function passthrough() {
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const headers = Object.fromEntries(clonedRequest.headers.entries());
// Cast the request headers to a new Headers instance
// so the headers can be manipulated with.
const headers = new Headers(requestClone.headers);
// Remove MSW-specific request headers so the bypassed requests
// comply with the server's CORS preflight check.
// Operate with the headers as an object because request "Headers"
// are immutable.
delete headers['x-msw-bypass'];
// Remove the "accept" header value that marked this request as passthrough.
// This prevents request alteration and also keeps it compliant with the
// user-defined CORS policies.
const acceptHeader = headers.get('accept');
if (acceptHeader) {
const values = acceptHeader.split(',').map((value) => value.trim());
const filteredValues = values.filter((value) => value !== 'msw/passthrough');
return fetch(clonedRequest, { headers });
if (filteredValues.length > 0) {
headers.set('accept', filteredValues.join(', '));
} else {
headers.delete('accept');
}
}
return fetch(requestClone, { headers });
}
// Bypass mocking when the client is not active.
@@ -224,57 +227,46 @@ async function getResponse(event, client, requestId) {
return passthrough();
}
// Bypass requests with the explicit bypass header.
// Such requests can be issued by "ctx.fetch()".
if (request.headers.get('x-msw-bypass') === 'true') {
return passthrough();
}
// Notify the client that a request has been intercepted.
const clientMessage = await sendToClient(client, {
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
mode: request.mode,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: await request.text(),
bodyUsed: request.bodyUsed,
keepalive: request.keepalive,
const requestBuffer = await request.arrayBuffer();
const clientMessage = await sendToClient(
client,
{
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
mode: request.mode,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: requestBuffer,
keepalive: request.keepalive,
},
},
});
[requestBuffer]
);
switch (clientMessage.type) {
case 'MOCK_RESPONSE': {
return respondWithMock(clientMessage.data);
}
case 'MOCK_NOT_FOUND': {
case 'PASSTHROUGH': {
return passthrough();
}
case 'NETWORK_ERROR': {
const { name, message } = clientMessage.data;
const networkError = new Error(message);
networkError.name = name;
// Rejecting a "respondWith" promise emulates a network error.
throw networkError;
}
}
return passthrough();
}
function sendToClient(client, message) {
function sendToClient(client, message, transferrables = []) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel();
@@ -286,17 +278,25 @@ function sendToClient(client, message) {
resolve(event.data);
};
client.postMessage(message, [channel.port2]);
});
}
function sleep(timeMs) {
return new Promise((resolve) => {
setTimeout(resolve, timeMs);
client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean)));
});
}
async function respondWithMock(response) {
await sleep(response.delay);
return new Response(response.body, response);
// Setting response status code to 0 is a no-op.
// However, when responding with a "Response.error()", the produced Response
// instance will have status code set to 0. Since it's not possible to create
// a Response instance with status code 0, handle that use-case separately.
if (response.status === 0) {
return Response.error();
}
const mockedResponse = new Response(response.body, response);
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
value: true,
enumerable: true,
});
return mockedResponse;
}

View File

@@ -13,7 +13,7 @@ export const ApplicationStoryBook: any = () => (
</MemoryRouter>
);
export const ApplicationjQuery: any = () => (
export const ApplicationJquery: any = () => (
<MemoryRouter initialEntries={[`/-/web/detail/jquery`]}>
<AppRoute />
</MemoryRouter>

View File

@@ -35,7 +35,7 @@ export const AuthorAll: any = () => (
/>
);
export const NoDAuthor: any = () => (
export const NoAuthor: any = () => (
<Author
packageMeta={{
latest: {

View File

@@ -6,6 +6,21 @@ export default {
title: 'Components/Sidebar/Engines',
};
export const EnginesAll: any = () => (
<Engines
packageMeta={{
latest: {
engines: {
node: '>= 18',
pnpm: '>7',
yarn: '>3',
npm: '>3',
},
},
}}
/>
);
export const EnginesNpmNode: any = () => (
<Engines
packageMeta={{
@@ -54,18 +69,3 @@ export const EnginesPnpm: any = () => (
}}
/>
);
export const EnginesAll: any = () => (
<Engines
packageMeta={{
latest: {
engines: {
node: '>= 18',
pnpm: '>7',
yarn: '>3',
npm: '>3',
},
},
}}
/>
);

View File

@@ -40,7 +40,7 @@ const Help: React.FC = () => {
t('help.second-step'),
t('help.second-step-command-line', { registryUrl })
)}
<Typography variant="body2">{t('help.third-step')}</Typography>
<Typography variant="body1">{t('help.third-step')}</Typography>
</CardContent>
<CardActions>
<Button color="primary" href="https://verdaccio.org/docs/cli-registry" size="small">

View File

@@ -1,20 +1,24 @@
import { vi } from 'vitest';
// import { generateTokenWithTimeRange } from '../../../jest/unit/components/__mocks__/token';
import { generateTokenWithTimeRange } from '../../utils/token-generate';
describe('getDefaultUserState', (): void => {
const username = 'xyz';
beforeEach(() => {
vi.resetModules();
vi.resetAllMocks();
});
test('should return state with empty user', async () => {
const token = 'token-xx-xx-xx';
vi.doMock('../storage', async (importOriginal) => ({
...(await importOriginal<typeof import('../storage')>()),
getItem: (key: string) => (key === 'token' ? token : username),
vi.doMock('../storage', () => ({
default: {
getItem: (key: string) => (key === 'token' ? token : username),
},
}));
const Login = await import('./login');
const { getDefaultUserState } = Login;
const result = {
@@ -24,19 +28,21 @@ describe('getDefaultUserState', (): void => {
expect(getDefaultUserState()).toEqual(result);
});
// test('should return state with user from storage', async () => {
// const token = generateTokenWithTimeRange(24);
//
// vi.doMock('../storage', async (importOriginal) => ({
// ...(await importOriginal<typeof import('../storage')>()),
// getItem: (key: string) => (key === 'token' ? token : username),
// }));
// const Login = await import('./login');
// const { getDefaultUserState } = Login;
// const result = {
// token,
// username,
// };
// expect(getDefaultUserState()).toEqual(result);
// });
test('should return state with user from storage', async () => {
const token = generateTokenWithTimeRange(24);
vi.doMock('../storage', () => ({
default: {
getItem: (key: string) => (key === 'token' ? token : username),
},
}));
const Login = await import('./login');
const { getDefaultUserState } = Login;
const result = {
token,
username,
};
expect(getDefaultUserState()).toEqual(result);
});
});

View File

@@ -1,13 +1,12 @@
import { vi } from 'vitest';
/* eslint-disable jest/no-mocks-import */
import { isTokenExpire } from './token';
import {
generateInvalidToken,
generateTokenWithExpirationAsString,
generateTokenWithOutExpiration,
generateTokenWithTimeRange,
} from '../../vitest/unit/components/__mocks__/token';
import { isTokenExpire } from './token';
} from './token-generate';
/* eslint-disable no-console */
console.error = vi.fn();

View File

@@ -22,6 +22,7 @@ export default defineConfig({
'\\.(jpg)$': './vitest/unit/empty.ts',
'\\.(md)$': './vitest/unit/empty-string.ts',
},
testTimeout: 15000,
},
plugins: [
react({

View File

@@ -1,82 +1,74 @@
import fs from 'fs';
import { rest } from 'msw';
const packagesPayload = require('./api/home-packages.json');
import { HttpResponse, http } from 'msw';
import path from 'path';
export const handlers = [
rest.get('http://localhost:9000/-/verdaccio/data/packages', (req, res, ctx) => {
return res(ctx.json(Array(400).fill(packagesPayload)));
http.get('http://localhost:9000/-/verdaccio/data/packages', () => {
return HttpResponse.json(Array(400).fill(require('./api/home-packages.json')));
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/storybook', (req, res, ctx) => {
const { v } = req.params;
http.get('http://localhost:9000/-/verdaccio/data/sidebar/storybook', ({ params }) => {
const { v } = params;
if (v) {
return res(ctx.json(require('./api/storybook-v.json')));
return HttpResponse.json(require('./api/storybook-v.json'));
} else {
return res(ctx.json(require('./api/storybook-sidebar.json')));
return HttpResponse.json(require('./api/storybook-sidebar.json'));
}
}),
rest.get('http://localhost:9000/-/verdaccio/data/search/*', (req, res, ctx) => {
return res(ctx.json(require('./api/search-verdaccio.json')));
http.get('http://localhost:9000/-/verdaccio/data/search/*', () => {
return HttpResponse.json(require('./api/search-verdaccio.json'));
}),
rest.get('http://localhost:9000/-/verdaccio/data/package/readme/storybook', (req, res, ctx) => {
return res(ctx.text(require('./api/storybook-readme')()));
http.get('http://localhost:9000/-/verdaccio/data/package/readme/storybook', () => {
return HttpResponse.text(require('./api/storybook-readme')());
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/jquery', (req, res, ctx) => {
return res(ctx.json(require('./api/jquery-sidebar.json')));
http.get('http://localhost:9000/-/verdaccio/data/sidebar/jquery', () => {
return HttpResponse.json(require('./api/jquery-sidebar.json'));
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/JSONStream', (req, res, ctx) => {
return res(ctx.status(401)); // unauthorized
http.get('http://localhost:9000/-/verdaccio/data/sidebar/JSONStream', () => {
return new HttpResponse('unauthorized', { status: 401 });
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/semver', (req, res, ctx) => {
return res(ctx.status(500)); // internal server error
http.get('http://localhost:9000/-/verdaccio/data/sidebar/semver', () => {
return new HttpResponse('internal server error', { status: 500 });
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/kleur', (req, res, ctx) => {
return res(ctx.status(404)); // not found
http.get('http://localhost:9000/-/verdaccio/data/sidebar/kleur', () => {
return new HttpResponse('not found', { status: 404 });
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/glob', (req, res, ctx) => {
return res(ctx.json(require('./api/glob-sidebar.json')));
http.get('http://localhost:9000/-/verdaccio/data/sidebar/glob', () => {
return HttpResponse.json(require('./api/glob-sidebar.json'));
}),
rest.get('http://localhost:9000/-/verdaccio/data/package/readme/glob', (req, res, ctx) => {
return res(ctx.text('foo glob'));
http.get('http://localhost:9000/-/verdaccio/data/package/readme/glob', () => {
return HttpResponse.text('foo glob');
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/got', (req, res, ctx) => {
return res(ctx.json(require('./api/got-sidebar.json')));
http.get('http://localhost:9000/-/verdaccio/data/sidebar/got', () => {
return HttpResponse.json(require('./api/got-sidebar.json'));
}),
rest.get('http://localhost:9000/-/verdaccio/data/package/readme/got', (req, res, ctx) => {
return res(ctx.text('foo got'));
http.get('http://localhost:9000/-/verdaccio/data/package/readme/got', () => {
return HttpResponse.text('foo got');
}),
rest.get('http://localhost:9000/-/verdaccio/data/package/readme/jquery', (req, res, ctx) => {
return res(ctx.text(require('./api/jquery-readme')()));
http.get('http://localhost:9000/-/verdaccio/data/package/readme/jquery', () => {
return HttpResponse.text(require('./api/jquery-readme')());
}),
rest.get('http://localhost:9000/verdaccio/-/verdaccio-1.0.0.tgz', (req, res, ctx) => {
const fileContent = fs.readFileSync('./api/verdaccio-1.0.0.tgz');
return res(
ctx.status(200),
ctx.set('Content-Type', 'application/octet-stream'),
ctx.body(fileContent)
);
http.get('http://localhost:9000/verdaccio/-/verdaccio-1.0.0.tgz', () => {
const fileName = path.resolve(__dirname, './api/verdaccio-1.0.0.tgz');
const fileContent = fs.readFileSync(fileName);
return new HttpResponse(fileContent, {
status: 200,
headers: { 'Content-Type': 'application/octet-stream' },
});
}),
rest.post<{ username: string; password: string }, { token: string; username: string }>(
http.post<{ username: string; password: string }, { token: string; username: string }>(
'http://localhost:9000/-/verdaccio/sec/login',
// @ts-ignore
async (req, res, ctx) => {
// @ts-ignore
const body = await req.json();
async ({ request }) => {
const body = await request.json();
if (body.username === 'fail') {
return ctx.status(401);
return new HttpResponse('unauthorized', { status: 401 });
}
return res(
ctx.json({
username: body.username,
token: 'valid token',
})
);
return HttpResponse.json({ username: body.username, token: 'valid token' });
}
),
];

5763
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff