0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

chore: add ui tests (#4512)

* chore: ui test coverage

* ui test coverage

* add tests

* add dep
This commit is contained in:
Juan Picado 2024-02-18 21:08:17 +01:00 committed by GitHub
parent 82ae08e3c8
commit 1bae2c431a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 36159 additions and 352 deletions

View file

@ -4,7 +4,7 @@ on: [pull_request]
permissions:
contents: read
concurrency:
group: e2e
group: e2e-ci-${{ github.ref }}
cancel-in-progress: true
jobs:
prepare:

View file

@ -4,7 +4,7 @@ on: [pull_request]
permissions:
contents: read
concurrency:
group: e2e
group: e2e-ui-${{ github.ref }}
cancel-in-progress: true
jobs:
test:

View file

@ -47,8 +47,10 @@
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
"@testing-library/dom": "9.3.4",
"@testing-library/jest-dom": "6.3.0",
"@testing-library/react": "14.1.2",
"@testing-library/jest-dom": "6.4.2",
"@testing-library/user-event": "14.5.2",
"aria-query": "5.1.3",
"@testing-library/react": "14.2.1",
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@types/body-parser": "1.19.5",
"@types/connect": "3.4.38",

View file

@ -58,7 +58,7 @@
"raw-loader": "4.0.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.49.3",
"react-hook-form": "7.50.1",
"react-hot-loader": "4.13.1",
"react-i18next": "13.5.0",
"react-json-view": "1.21.3",

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -34,10 +34,10 @@ module.exports = Object.assign({}, config, {
coverageReporters: ['text', 'html'],
coverageThreshold: {
global: {
branches: 66,
functions: 65,
lines: 73,
statements: 73,
branches: 70,
functions: 76,
lines: 80,
statements: 82,
},
},
});

View file

@ -31,7 +31,18 @@ export const handlers = [
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/jquery', (req, res, ctx) => {
return res(ctx.json(require('./api/jquery-sidebar.json')));
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/glob', (req, res, ctx) => {
return res(ctx.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'));
}),
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/got', (req, res, ctx) => {
return res(ctx.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'));
}),
rest.get('http://localhost:9000/-/verdaccio/data/package/readme/jquery', (req, res, ctx) => {
return res(ctx.text(require('./api/jquery-readme')()));
}),

View file

@ -6,7 +6,6 @@ import Dependencies from './Dependencies';
describe('<Dependencies /> component', () => {
test('Renders a message when there are no dependencies', () => {
// Given
const packageMeta = {
latest: {
name: 'verdaccio',
@ -25,15 +24,12 @@ describe('<Dependencies /> component', () => {
_uplinks: {},
};
// When
const { getByText } = render(<Dependencies packageMeta={packageMeta} />);
// Then
expect(getByText('dependencies.has-no-dependencies')).toBeDefined();
});
test('Renders a link to each dependency', () => {
// Given
test('renders a link to each dependency', () => {
const packageMeta = {
latest: {
name: 'verdaccio',
@ -59,18 +55,12 @@ describe('<Dependencies /> component', () => {
_uplinks: {},
};
// When
const { getByText } = render(
<HashRouter>
<Dependencies packageMeta={packageMeta} />
</HashRouter>
);
// Then
// FIXME: currently MaterialUI chips do not support the children
// prop, therefore it is impossible to use proper links for
// dependencies. Those are for now clickable spans
expect(getByText('dependencies (2)')).toBeDefined();
expect(getByText('devDependencies (1)')).toBeDefined();
expect(getByText('peerDependencies (1)')).toBeDefined();

View file

@ -1,63 +1,21 @@
import Box from '@mui/material/Box';
import styled from '@emotion/styled';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import { useTheme } from '@mui/styles';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { PackageDependencies } from '../../types/packageMeta';
import { Theme } from '../../Theme';
import NoItems from '../NoItems';
import { CardWrap, StyledText, Tag, Tags } from './styles';
import { DependencyBlock } from './DependencyBlock';
import { hasKeys } from './utits';
interface DependencyBlockProps {
title: string;
dependencies: PackageDependencies;
}
const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => {
const history = useHistory();
const { t } = useTranslation();
const theme = useTheme();
const deps = Object.entries(dependencies);
function handleClick(name: string): void {
history.push(`/-/web/detail/${name}`);
}
return (
<Box data-testid={title} sx={{ margin: theme.spacing(2) }}>
<StyledText sx={{ marginBottom: theme.spacing(1) }} variant="subtitle1">
{`${title} (${deps.length})`}
</StyledText>
<Tags>
{deps.map(([name, version]) => (
<Tag
className={'dep-tag'}
clickable={true}
data-testid={name}
key={name}
label={t('dependencies.dependency-block', { package: name, version })}
// eslint-disable-next-line
onClick={() => handleClick(name)}
/>
))}
</Tags>
</Box>
);
};
function hasKeys(object?: { [key: string]: any }): boolean {
return !!object && Object.keys(object).length > 0;
}
export const CardWrap = styled(Card)<{ theme?: Theme }>((props) => ({
marginBottom: props.theme.spacing(2),
}));
const Dependencies: React.FC<{ packageMeta: any }> = ({ packageMeta }) => {
const { t } = useTranslation();
if (!packageMeta) {
throw new Error(t('error.package-meta-is-required-at-detail-context'));
}
const { latest } = packageMeta;
// FIXME: add dependencies to package meta type
// @ts-ignore

View file

@ -0,0 +1,33 @@
import userEvent from '@testing-library/user-event';
import React from 'react';
import { MemoryRouter, Route } from 'react-router';
import { render, screen } from '../../test/test-react-testing-library';
import { DependencyBlock } from './DependencyBlock';
describe('<DependencyBlock /> component', () => {
test('renders dependency block', () => {
render(<DependencyBlock dependencies={{ jquery: '1.0.0' }} title="foo" />);
expect(screen.getByText('foo (1)')).toBeInTheDocument();
expect(screen.getByText('dependencies.dependency-block')).toBeInTheDocument();
userEvent.click(screen.getByText('dependencies.dependency-block'));
});
test.todo('test the click event');
test.skip('handle change route handler', () => {
render(
<MemoryRouter
initialEntries={[`/-/web/detail/some-dep`, `/-/web/detail/jquery`]}
initialIndex={0}
>
<Route exact={true} path="/-/web/detail/:package">
<DependencyBlock dependencies={{ jquery: '1.0.0' }} title="foo" />
</Route>
</MemoryRouter>
);
userEvent.click(screen.getByTestId('jquery'));
});
});

View file

@ -0,0 +1,69 @@
import styled from '@emotion/styled';
import Box from '@mui/material/Box';
import Chip from '@mui/material/Chip';
import Typography from '@mui/material/Typography';
import { useTheme } from '@mui/styles';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { Theme } from '../../Theme';
import { PackageDependencies } from '../../types/packageMeta';
interface DependencyBlockProps {
title: string;
dependencies: PackageDependencies;
}
export const StyledText = styled(Typography)<{ theme?: Theme }>((props) => ({
fontWeight: props.theme && props.theme.fontWeight.bold,
textTransform: 'capitalize',
}));
export const Tags = styled('div')<{ theme?: Theme }>((props) => ({
display: 'flex',
justifyContent: 'start',
flexWrap: 'wrap',
// force title to be on the same line as the title
// could be better to avoid margin on the first element
// but it is a bit tricky to do with flexbox
marginLeft: props.theme.spacing(-0.6),
}));
export const Tag = styled(Chip)<{ theme?: Theme }>((props) => ({
margin: props.theme.spacing(0.6),
}));
export const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => {
const history = useHistory();
const { t } = useTranslation();
const theme = useTheme();
const deps = Object.entries(dependencies);
function handleClick(name: string): void {
history.push(`/-/web/detail/${name}`);
}
return (
<Box data-testid={title} sx={{ margin: theme.spacing(2) }}>
<StyledText sx={{ marginBottom: theme.spacing(1) }} variant="subtitle1">
{`${title} (${deps.length})`}
</StyledText>
<Tags>
{deps.map(([name, version]) => (
<Tag
className={'dep-tag'}
clickable={true}
data-testid={name}
key={name}
label={t('dependencies.dependency-block', { package: name, version })}
// eslint-disable-next-line
onClick={() => {
handleClick(name);
}}
/>
))}
</Tags>
</Box>
);
};

View file

@ -1,26 +0,0 @@
import styled from '@emotion/styled';
import Card from '@mui/material/Card';
import Chip from '@mui/material/Chip';
import Typography from '@mui/material/Typography';
import { Theme } from '../../Theme';
export const CardWrap = styled(Card)({
margin: '0 0 16px',
});
export const StyledText = styled(Typography)<{ theme?: Theme }>((props) => ({
fontWeight: props.theme && props.theme.fontWeight.bold,
textTransform: 'capitalize',
}));
export const Tags = styled('div')({
display: 'flex',
justifyContent: 'start',
flexWrap: 'wrap',
margin: '0 -5px',
});
export const Tag = styled(Chip)({
margin: '5px',
});

View file

@ -0,0 +1,3 @@
export function hasKeys(object?: { [key: string]: any }): boolean {
return !!object && Object.keys(object).length > 0;
}

View file

@ -1,11 +1,12 @@
import React from 'react';
import { render } from '../../test/test-react-testing-library';
import { render, screen } from '../../test/test-react-testing-library';
import Loading from './Loading';
describe('<Loading /> component', () => {
test('should render the component in default state', () => {
const { container } = render(<Loading />);
expect(container.firstChild).toMatchSnapshot();
render(<Loading />);
screen.debug();
expect(screen.getByTestId('loading')).toBeInTheDocument();
});
});

View file

@ -1,138 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Loading /> component should render the component in default state 1`] = `
@keyframes animation-0 {
0% {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes animation-1 {
0% {
stroke-dasharray: 1px,200px;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 100px,200px;
stroke-dashoffset: -15px;
}
100% {
stroke-dasharray: 100px,200px;
stroke-dashoffset: -125px;
}
}
.emotion-0 {
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
position: absolute;
}
.emotion-2 {
margin: 0 0 30px 0;
border-radius: 25px;
box-shadow: 0 10px 20px 0 rgba(69, 58, 100, 0.2);
background: #f7f8f6;
}
.emotion-4 {
display: inline-block;
vertical-align: middle;
box-sizing: border-box;
-webkit-background-position: center;
background-position: center;
-webkit-background-size: contain;
background-size: contain;
background-image: url([object Object]);
background-repeat: no-repeat;
width: 90px;
height: 90px;
}
.emotion-6 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
}
.emotion-9 {
display: inline-block;
color: #4b5e40;
-webkit-animation: animation-0 1.4s linear infinite;
animation: animation-0 1.4s linear infinite;
color: #4b5e40;
}
.emotion-10 {
display: block;
}
.emotion-11 {
stroke: currentColor;
stroke-dasharray: 80px,200px;
stroke-dashoffset: 0;
-webkit-animation: animation-1 1.4s ease-in-out infinite;
animation: animation-1 1.4s ease-in-out infinite;
}
<div
class="emotion-0 emotion-1"
data-testid="loading"
>
<div
class="emotion-2 emotion-3"
>
<div
class="emotion-4 emotion-5"
/>
</div>
<div
class="emotion-6 emotion-7"
>
<span
class="MuiCircularProgress-root MuiCircularProgress-indeterminate MuiCircularProgress-colorPrimary emotion-8 emotion-9"
role="progressbar"
style="width: 50px; height: 50px;"
>
<svg
class="MuiCircularProgress-svg emotion-10"
viewBox="22 22 44 44"
>
<circle
class="MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate emotion-11"
cx="44"
cy="44"
fill="none"
r="20.2"
stroke-width="3.6"
/>
</svg>
</span>
</div>
</div>
`;

View file

@ -0,0 +1 @@
export { default } from './SideBarTitle';

View file

@ -1 +0,0 @@
export { default } from './SideBarTittle';

View file

@ -14,7 +14,7 @@ export * as Icons from './components/Icons';
export { default as Install } from './components/Install';
export { default as RawViewer } from './components/RawViewer';
export { default as Readme } from './components/Readme';
export { default as SideBarTitle } from './components/SideBarTittle';
export { default as SideBarTitle } from './components/SideBarTitle';
export { default as UpLinks } from './components/UpLinks';
export { default as Versions } from './components/Versions';
export { default as TextField } from './components/TextField';

View file

@ -0,0 +1,29 @@
import React from 'react';
import { MemoryRouter } from 'react-router';
import { store } from '../../store';
import { renderWithStore, screen, waitFor } from '../../test/test-react-testing-library';
import Home from './Home';
// force the windows to expand to display items
// https://github.com/bvaughn/react-virtualized/issues/493#issuecomment-640084107
jest.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockReturnValue(600);
jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(600);
const ComponentSideBar: React.FC = () => (
<MemoryRouter>
<Home />
</MemoryRouter>
);
describe('Home', () => {
test('should render titles', async () => {
renderWithStore(<ComponentSideBar />, store);
await waitFor(() => expect(screen.getAllByTestId('package-item-list')).toHaveLength(5));
});
test('should render loading', async () => {
renderWithStore(<ComponentSideBar />, store);
await waitFor(() => expect(screen.getByTestId('loading')).toBeInTheDocument());
});
});

View file

@ -11,7 +11,6 @@ const Home: React.FC = () => {
// @ts-ignore
dispatch.packages.getPackages();
}, [dispatch]);
return (
<div className="container content" data-testid="home-page-container">
{isLoading ? <Loading /> : <PackageList packages={packages} />}

View file

@ -1,110 +1,67 @@
import { rest } from 'msw';
import React from 'react';
import { MemoryRouter } from 'react-router';
import { server } from '../../../jest/server';
import { VersionProvider } from '../../providers';
import { store } from '../../store';
import { renderWithStore, screen, waitFor } from '../../test/test-react-testing-library';
import DetailSidebar from './Sidebar';
import Sidebar from './Sidebar';
jest.mock('marked');
jest.mock('marked');
jest.mock('marked-highlight');
const ComponentToBeRendered: React.FC = () => (
const ComponentSideBar: React.FC = () => (
<MemoryRouter>
<VersionProvider>
<DetailSidebar />
<Sidebar />
</VersionProvider>
</MemoryRouter>
);
// https://stackoverflow.com/a/54010619/308341
jest.mock('react', () => {
const React = jest.requireActual('react');
React.Suspense = ({ children }) => children;
return React;
});
const packageMeta = {
_uplinks: {},
latest: {
name: 'verdaccio-ui/local-storage',
version: '8.0.1-next.1',
dist: {
fileCount: 0,
unpackedSize: 0,
tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz',
},
homepage: 'https://verdaccio.org',
bugs: {
url: 'https://github.com/verdaccio/monorepo/issues',
},
},
};
// const detailContextValue = {
// packageName: 'foo',
// readMe: 'test',
// isLoading: false,
// hasNotBeenFound: false,
// packageMeta: ,
// };
const mockPkgName = jest.fn().mockReturnValue('jquery');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'), // use actual for all non-hook parts
useParams: () => ({
package: 'jquery',
package: mockPkgName(),
}),
}));
server.use(
...[
rest.get('http://localhost:9000/-/verdaccio/data/sidebar/jquery', (req, res, ctx) => {
return res(ctx.json(packageMeta));
}),
rest.get('http://localhost:9000/-/verdaccio/data/package/readme/jquery', (req, res, ctx) => {
return res(ctx.text('foo readme'));
}),
]
);
// describe('DetailSidebar', () => {
test.skip('should render commonjs module icon', async () => {
const { getByText } = renderWithStore(<ComponentToBeRendered />, store);
screen.debug();
await waitFor(() => expect(getByText('jquery')).toBeInTheDocument());
describe('Sidebar', () => {
afterEach(() => {
jest.clearAllMocks();
});
test('should render titles', async () => {
renderWithStore(<ComponentSideBar />, store);
await waitFor(() => expect(screen.getByText('jquery')).toBeInTheDocument());
expect(screen.getByText(`jquery`)).toBeInTheDocument();
expect(screen.getByText(`sidebar.detail.latest-version`, { exact: false })).toBeInTheDocument();
expect(
screen.getByText(`sidebar.detail.published a year ago`, { exact: false })
).toBeInTheDocument();
expect(screen.getByText(`sidebar.installation.title`, { exact: false })).toBeInTheDocument();
});
test('should render commonJS', async () => {
renderWithStore(<ComponentSideBar />, store);
await waitFor(() => expect(screen.getByText('jquery')).toBeInTheDocument());
expect(screen.getByAltText('commonjs')).toBeInTheDocument();
});
test('should render typescript', async () => {
mockPkgName.mockReturnValue('glob');
renderWithStore(<ComponentSideBar />, store);
await waitFor(() => expect(screen.getByText('glob')).toBeInTheDocument());
expect(screen.getByAltText('typescript')).toBeInTheDocument();
});
test('should render es modules', async () => {
mockPkgName.mockReturnValue('got');
renderWithStore(<ComponentSideBar />, store);
await waitFor(() => expect(screen.getByText('got')).toBeInTheDocument());
expect(screen.getByAltText('es6 modules')).toBeInTheDocument();
});
});
// test('should render ts module icon', () => {
// renderWithStore(
// <ComponentToBeRendered
// contextValue={_.merge(detailContextValue, {
// packageMeta: {
// latest: {
// types: './src/index.d.ts',
// },
// },
// })}
// />,
// store
// );
// expect(screen.getByAltText('typescript')).toBeInTheDocument();
// });
// test('should render es6 module icon', () => {
// renderWithStore(
// <ComponentToBeRendered
// contextValue={_.merge(detailContextValue, {
// packageMeta: {
// latest: {
// type: 'module',
// },
// },
// })}
// />,
// store
// );
// expect(screen.getByAltText('es6 modules')).toBeInTheDocument();
// });
// });

View file

@ -6,25 +6,15 @@ import { Theme } from '../../Theme';
import ActionBar from '../../components/ActionBar';
import Author from '../../components/Author';
import Developers, { DeveloperType } from '../../components/Developers';
import Dist from '../../components/Distribution';
import Engines from '../../components/Engines';
import FundButton from '../../components/FundButton';
import SideBarTittle from '../../components/SideBarTittle';
import Install from '../../components/Install';
import Repository from '../../components/Repository';
import SideBarTitle from '../../components/SideBarTitle';
import { useConfig } from '../../providers';
import { useVersion } from '../../providers';
import { PackageMetaInterface } from '../../types/packageMeta';
import loadable from '../../utils/loadable';
const Engines = loadable(
() => import(/* webpackChunkName: "Engines" */ '../../components/Engines')
);
const Dist = loadable(
() => import(/* webpackChunkName: "Distribution" */ '../../components/Distribution')
);
const Install = loadable(
() => import(/* webpackChunkName: "Install" */ '../../components/Install')
);
const Repository = loadable(
() => import(/* webpackChunkName: "Repository" */ '../../components/Repository')
);
const getModuleType = (manifest: PackageMetaInterface) => {
if (manifest.latest.main) {
@ -40,14 +30,13 @@ const DetailSidebar: React.FC = () => {
const { configOptions } = useConfig();
const version = packageVersion || packageMeta?.latest.version || '';
const time = packageMeta?.time ? packageMeta.time[version] : '';
if (!packageMeta || !packageName) {
return null;
}
return (
<StyledPaper sx={{ position: 'sticky', top: 0 }}>
<SideBarTittle
<SideBarTitle
description={packageMeta.latest?.description}
hasTypes={typeof packageMeta.latest?.types === 'string'}
isLatest={typeof packageVersion === 'undefined'}

View file

@ -105,11 +105,14 @@ importers:
specifier: 9.3.4
version: 9.3.4
'@testing-library/jest-dom':
specifier: 6.3.0
version: 6.3.0(@types/jest@29.5.11)(jest@29.7.0)(vitest@0.34.6)
specifier: 6.4.2
version: 6.4.2(@types/jest@29.5.11)(jest@29.7.0)(vitest@0.34.6)
'@testing-library/react':
specifier: 14.1.2
version: 14.1.2(react-dom@18.2.0)(react@18.2.0)
specifier: 14.2.1
version: 14.2.1(react-dom@18.2.0)(react@18.2.0)
'@testing-library/user-event':
specifier: 14.5.2
version: 14.5.2(@testing-library/dom@9.3.4)
'@trivago/prettier-plugin-sort-imports':
specifier: 4.3.0
version: 4.3.0(prettier@3.2.2)
@ -224,6 +227,9 @@ importers:
'@vitest/coverage-v8':
specifier: 0.34.6
version: 0.34.6(vitest@0.34.6)
aria-query:
specifier: 5.1.3
version: 5.1.3
babel-core:
specifier: 7.0.0-bridge.0
version: 7.0.0-bridge.0(@babel/core@7.23.9)
@ -1305,8 +1311,8 @@ importers:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
react-hook-form:
specifier: 7.49.3
version: 7.49.3(react@18.2.0)
specifier: 7.50.1
version: 7.50.1(react@18.2.0)
react-hot-loader:
specifier: 4.13.1
version: 4.13.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
@ -11472,6 +11478,40 @@ packages:
vitest: 0.34.6
dev: true
/@testing-library/jest-dom@6.4.2(@types/jest@29.5.11)(jest@29.7.0)(vitest@0.34.6):
resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==}
engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
peerDependencies:
'@jest/globals': '>= 28'
'@types/bun': latest
'@types/jest': '>= 28'
jest: '>= 28'
vitest: '>= 0.32'
peerDependenciesMeta:
'@jest/globals':
optional: true
'@types/bun':
optional: true
'@types/jest':
optional: true
jest:
optional: true
vitest:
optional: true
dependencies:
'@adobe/css-tools': 4.3.2
'@babel/runtime': 7.23.9
'@types/jest': 29.5.11
aria-query: 5.1.3
chalk: 3.0.0
css.escape: 1.5.1
dom-accessibility-api: 0.6.3
jest: 29.7.0(@types/node@20.11.7)(ts-node@10.9.2)
lodash: 4.17.21
redent: 3.0.0
vitest: 0.34.6
dev: true
/@testing-library/react@14.1.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-z4p7DVBTPjKM5qDZ0t5ZjzkpSNb+fZy1u6bzO7kk8oeGagpPCAtgh4cx1syrfp7a+QWkM021jGqjJaxJJnXAZg==}
engines: {node: '>=14'}
@ -11486,6 +11526,20 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
/@testing-library/react@14.2.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==}
engines: {node: '>=14'}
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
dependencies:
'@babel/runtime': 7.23.9
'@testing-library/dom': 9.3.4
'@types/react-dom': 18.2.18
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
/@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4):
resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
engines: {node: '>=12', npm: '>=6'}
@ -12097,7 +12151,7 @@ packages:
resolution: {integrity: sha512-bnreXCgus6IIadyHNlN/oI5FfX4dWgvGhOPvpr7zzCYDGAPIfvyIoAozMBINmhmsVuqV0cncejF2y5KC7ScqOg==}
deprecated: This is a stub types definition. @testing-library/jest-dom provides its own type definitions, so you do not need this installed.
dependencies:
'@testing-library/jest-dom': 6.3.0(@types/jest@29.5.11)(jest@29.7.0)(vitest@0.34.6)
'@testing-library/jest-dom': 6.4.2(@types/jest@29.5.11)(jest@29.7.0)(vitest@0.34.6)
transitivePeerDependencies:
- '@jest/globals'
- '@types/bun'
@ -25721,6 +25775,16 @@ packages:
react: ^16.8.0 || ^17 || ^18
dependencies:
react: 18.2.0
dev: false
/react-hook-form@7.50.1(react@18.2.0):
resolution: {integrity: sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
dependencies:
react: 18.2.0
dev: true
/react-hot-loader@4.13.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-ZlqCfVRqDJmMXTulUGic4lN7Ic1SXgHAFw7y/Jb7t25GBgTR0fYAJ8uY4mrpxjRyWGWmqw77qJQGnYbzCvBU7g==}
@ -25982,7 +26046,7 @@ packages:
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.23.9
'@babel/runtime': 7.21.0
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0