chore: migrate to cypress for ui e2e (#3434)

This commit is contained in:
Juan Picado 2022-10-15 07:43:42 +02:00 committed by GitHub
parent 852324c321
commit 2f835ac09e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 688 additions and 448 deletions

4
.gitignore vendored
View File

@ -53,3 +53,7 @@ website/docs/api/**/*.md
website/docs/api/**/*.yml
!website/docs/api/index.md
packages/**/docs
# cypress
e2e/ui/cypress/videos/**/*
e2e/ui/cypress/screenshots/**/*

View File

@ -3,4 +3,4 @@
This folder is composed of two strategies:
- E2E for CLI
- E2E for the UI (puppeteer)
- E2E for the UI (cypress)

View File

@ -1,9 +0,0 @@
{
"globals": {
"VERDACCIO_API_URL": true,
"__DEBUG__": true
},
"env": {
"browser": true
}
}

View File

@ -1,29 +0,0 @@
# @verdaccio/e2e-ui
## 2.0.0-6-next.3
### Major Changes
- 000d4374: feat: upgrade to material ui 5
## 1.1.0-6-next.2
### Minor Changes
- 154b2ecd: refactor: remove @verdaccio/commons-api in favor @verdaccio/core and remove duplications
## 1.1.0-6-next.1
### Minor Changes
- 1b217fd3: Some verdaccio modules depend on 'mkdirp' library which provides recursive directory creation functionality.
NodeJS can do this out of the box since v.10.12. The last commit in 'mkdirp' was made in early 2016, and it's mid 2021 now.
Time to stick with a built-in library solution!
- All 'mkdirp' calls are replaced with appropriate 'fs' calls.
## 1.0.1-alpha.0
### Patch Changes
- fecbb9be: chore: add release step to private regisry on merge changeset pr

View File

@ -7,3 +7,7 @@
- Check navigation
- Check sidebar
- Check protected packages works
## Contribute
More tests could be added to verify UI works as expected.

View File

@ -1,64 +0,0 @@
const { join } = require('path');
const { fileUtils } = require('@verdaccio/core');
const { parseConfigFile } = require('@verdaccio/config');
const { Registry, ServerQuery } = require('verdaccio');
describe('basic functionality', () => {
let registry1;
let page;
beforeAll(async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath);
await registry1.init();
const query1 = new ServerQuery(registry1.getRegistryUrl());
await query1.createUser('test', 'test');
page = await global.__BROWSER__.newPage();
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
// eslint-disable-next-line no-console
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
});
afterAll(async () => {
await page.close();
registry1.stop();
});
// this might be increased based on the delays included in all test
jest.setTimeout(20000);
test('should display title', async () => {
const text = await page.title();
await page.waitForTimeout(1000);
expect(text).toContain('verdaccio-server-e2e');
});
test('should match title with no packages published', async () => {
const text = await page.evaluate(() => document.querySelector('#help-card__title').textContent);
expect(text).toMatch('No Package Published Yet.');
});
test('should match title with first step', async () => {
const text = await page.evaluate(() => document.querySelector('#help-card').textContent);
expect(text).toContain(`npm adduser --registry http://0.0.0.0:${registry1.getPort()}`);
});
test('should match title with second step', async () => {
const text = await page.evaluate(() => document.querySelector('#help-card').textContent);
expect(text).toContain(`npm publish --registry http://0.0.0.0:${registry1.getPort()}`);
});
test('should go to 404 page', async () => {
await page.goto(`http://0.0.0.0:${registry1.getPort()}/-/web/detail/@verdaccio/not-found`);
await page.waitForTimeout(500);
const text = await page.evaluate(() => document.querySelector('.not-found-text').textContent);
expect(text).toMatch("Sorry, we couldn't find it...");
});
});

60
e2e/ui/cypress.config.ts Normal file
View File

@ -0,0 +1,60 @@
import { defineConfig } from 'cypress';
import { join } from 'path';
import { Registry, ServerQuery } from 'verdaccio';
import { parseConfigFile } from '@verdaccio/config';
import { HEADERS, fileUtils } from '@verdaccio/core';
import { generatePackageMetadata } from '@verdaccio/test-helper';
let registry1;
export default defineConfig({
e2e: {
setupNodeEvents(on) {
on('before:run', async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath, {
createUser: true,
credentials: { user: 'test', password: 'test' },
});
await registry1.init();
});
on('after:run', async () => {
registry1.stop();
});
on('task', {
publishScoped() {
const scopedPackageMetadata = generatePackageMetadata('pkg-scoped', '1.0.6');
const server = new ServerQuery(registry1.getRegistryUrl());
server
.putPackage(scopedPackageMetadata.name, scopedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
})
.then(() => {});
return null;
},
publishProtected({ pkgName }) {
const protectedPackageMetadata = generatePackageMetadata(pkgName, '5.0.5');
const server = new ServerQuery(registry1.getRegistryUrl());
server
.putPackage(protectedPackageMetadata.name, protectedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
})
.then(() => {});
},
registry() {
return {
registryUrl: registry1.getRegistryUrl(),
port: registry1.getPort(),
};
},
});
},
},
});

View File

@ -0,0 +1,30 @@
describe('home spec', () => {
let ctx: any = {};
beforeEach(async () => {
// @ts-expect-error
const registry = await cy.task('registry');
ctx.url = registry.registryUrl;
});
it('title should be correct', () => {
cy.visit(ctx.url);
cy.location('pathname').should('include', '/');
cy.title().should('eq', 'verdaccio-server-e2e');
});
it('should match title with no packages published', () => {
cy.visit(ctx.url);
cy.getByTestId('help-card').contains('No Package Published Yet.');
});
it('should display instructions on help card', () => {
cy.visit(ctx.url);
cy.getByTestId('help-card').contains(`npm adduser --registry ${ctx.url}`);
cy.getByTestId('help-card').contains(`npm publish --registry ${ctx.url}`);
});
it('should go to 404 page', () => {
cy.visit(`${ctx.url}/-/web/detail/@verdaccio/not-found`);
cy.getByTestId('404').contains(`Sorry, we couldn't find it.`);
});
});

View File

@ -0,0 +1,88 @@
describe('publish spec', () => {
let ctx: any = {};
const credentials = { user: 'test', password: 'test' };
beforeEach(async () => {
// @ts-expect-error
const registry = await cy.task('registry');
ctx.url = registry.registryUrl;
cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');
cy.intercept('GET', '/-/verdaccio/data/packages').as('pkgs');
cy.intercept('GET', '/-/verdaccio/data/sidebar/pkg-scoped').as('sidebar');
cy.intercept('GET', '/-/verdaccio/data/package/readme/pkg-scoped').as('readme');
cy.task('publishScoped', { pkgName: 'pkg-protected' });
});
it('should have one published package', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.getByTestId('package-title').should('have.length', 1);
});
it('should navigate to page detail', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.wait(300);
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
});
it('should have readme content', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.get('.markdown-body').should('have.length', 1);
cy.contains('.markdown-body', /test/);
});
it('should click on dependencies tab', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.getByTestId('dependencies-tab').click();
cy.wait(100);
cy.getByTestId('dependencies').should('have.length', 1);
cy.getByTestId('verdaccio')
.children()
.invoke('text')
.should('match', /verdaccio/);
cy.screenshot();
});
it('should click on versions tab', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.getByTestId('versions-tab').click();
cy.getByTestId('tag-latest').children().invoke('text').should('match', /1.0.6/);
cy.screenshot();
});
it('should click on uplinks tab', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.getByTestId('uplinks-tab').click();
cy.getByTestId('no-uplinks').should('be.visible');
cy.screenshot();
});
});

View File

@ -0,0 +1,31 @@
describe('sign spec', () => {
let ctx: any = {};
const credentials = { user: 'test', password: 'test' };
beforeEach(async () => {
// @ts-expect-error
const registry = await cy.task('registry');
ctx.url = registry.registryUrl;
cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');
});
it('should login user', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.getByTestId('logInDialogIcon').click();
cy.wait(100);
cy.getByTestId('greetings-label').contains(credentials.user);
});
it('should logout an user', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.getByTestId('logInDialogIcon').click();
cy.wait(100);
cy.getByTestId('logOutDialogIcon').click();
cy.screenshot();
cy.getByTestId('header--button-login').contains('Login');
cy.screenshot();
});
});

View File

@ -0,0 +1,37 @@
/* eslint-disable no-undef */
// / <reference types="cypress" />;
Cypress.Commands.add('getByTestId', (selector, ...args) => {
return cy.get(`[data-testid=${selector}]`, ...args);
});
// -- This is a parent command --
Cypress.Commands.add('login', (user, password) => {
cy.getByTestId('header--button-login').click();
cy.get('#login--dialog-username').type(user);
cy.get('#login--dialog-password').type(password);
cy.get('#login--dialog-button-submit').click();
});
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }

View File

@ -0,0 +1,19 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -1,36 +0,0 @@
export const credentials = { user: 'test', password: 'test' };
export const clickElement = async function (page, selector, options = { delay: 100 }) {
const button = await page.$(selector);
await button.focus();
await button.click(options);
};
export const evaluateSignIn = async function (page, matchText = 'Login') {
const text = await page.evaluate(() => {
return document.querySelector('button[data-testid="header--button-login"]')?.textContent;
});
expect(text).toMatch(matchText);
};
export const getPackages = async function (page) {
return await page.$$('.package-title');
};
export const logIn = async function (page) {
await clickElement(page, 'div[data-testid="dialogContentLogin"]');
const userInput = await page.$('#login--dialog-username');
expect(userInput).not.toBeNull();
const passInput = await page.$('#login--dialog-password');
expect(passInput).not.toBeNull();
await userInput.type(credentials.user, { delay: 100 });
await passInput.type(credentials.password, { delay: 100 });
await passInput.dispose();
// click on log in
const loginButton = await page.$('#login--dialog-button-submit');
expect(loginButton).toBeDefined();
await loginButton.focus();
await loginButton.click({ delay: 100 });
await page.waitForTimeout(500);
};

View File

@ -1,8 +0,0 @@
const config = require('../../jest/config');
module.exports = Object.assign({}, config, {
verbose: false,
collectCoverage: false,
globalSetup: './pre-setup.js',
globalTeardown: './teardown.js',
});

View File

@ -8,12 +8,10 @@
"@verdaccio/config": "workspace:6.0.0-6-next.48",
"@verdaccio/test-helper": "workspace:2.0.0-6-next.5",
"debug": "4.3.4",
"colorette": "2.0.19",
"lodash": "^4.17.21",
"puppeteer": "17.1.3",
"rimraf": "3.0.2"
"cypress": "10.10.0"
},
"scripts": {
"test": "jest --runInBand"
"cypress:open": "cypress open",
"test": "cypress run"
}
}

View File

@ -1,118 +0,0 @@
const { join } = require('path');
const { generatePackageMetadata } = require('@verdaccio/test-helper');
const { fileUtils, HEADERS } = require('@verdaccio/core');
const { parseConfigFile } = require('@verdaccio/config');
const { Registry, ServerQuery } = require('verdaccio');
const { getPackages } = require('./helper');
const protectedPackageMetadata = generatePackageMetadata('pkg-protected', '5.0.5');
const scopedPackageMetadata = generatePackageMetadata('pkg-scoped', '1.0.6');
describe('publish package', () => {
let registry1;
let page;
beforeAll(async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath, { createUser: true });
await registry1.init();
const query1 = new ServerQuery(registry1.getRegistryUrl());
await query1.createUser('test', 'test');
page = await global.__BROWSER__.newPage();
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
// eslint-disable-next-line no-console
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
});
afterAll(async () => {
await page.close();
registry1.stop();
});
// this might be increased based on the delays included in all test
jest.setTimeout(20000);
test('should publish a package', async () => {
const server = new ServerQuery(registry1.getRegistryUrl());
await server.putPackage(scopedPackageMetadata.name, scopedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
});
await page.waitForTimeout(1000);
await page.reload();
await page.waitForTimeout(1000);
const packagesList = await getPackages(page);
expect(packagesList).toHaveLength(1);
});
//
test('should navigate to the package detail', async () => {
const packagesList = await getPackages(page);
const firstPackage = packagesList[0];
await firstPackage.click({ delay: 200 });
await page.waitForTimeout(1000);
const readmeText = await page.evaluate(
() => document.querySelector('.markdown-body').textContent
);
expect(readmeText).toMatch('test');
});
test('should contains last sync information', async () => {
const versionList = await page.$$('.sidebar-info .detail-info');
expect(versionList).toHaveLength(1);
});
test('should display dependencies tab', async () => {
const dependenciesTab = await page.$$('#dependencies-tab');
expect(dependenciesTab).toHaveLength(1);
await dependenciesTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
const tags = await page.$$('.dep-tag');
const tag = tags[0];
const label = await page.evaluate((el) => el.innerText, tag);
expect(label).toMatch('verdaccio@');
});
test('should display version tab', async () => {
const versionsTab = await page.$$('#versions-tab');
expect(versionsTab).toHaveLength(1);
await versionsTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
const versionItems = await page.$$('.version-item');
expect(versionItems).toHaveLength(2);
});
test('should display uplinks tab', async () => {
const upLinksTab = await page.$$('#uplinks-tab');
expect(upLinksTab).toHaveLength(1);
await upLinksTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
});
test('should display readme tab', async () => {
const readmeTab = await page.$$('#readme-tab');
expect(readmeTab).toHaveLength(1);
await readmeTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
});
test('should publish a second package', async () => {
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
await page.waitForTimeout(500);
const server = new ServerQuery(registry1.getRegistryUrl());
await server.putPackage(protectedPackageMetadata.name, protectedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
});
await page.waitForTimeout(500);
await page.reload();
await page.waitForTimeout(500);
const packagesList = await getPackages(page);
expect(packagesList).toHaveLength(2);
});
});

View File

@ -1,80 +0,0 @@
const { join } = require('path');
const { fileUtils } = require('@verdaccio/core');
const { parseConfigFile } = require('@verdaccio/config');
const { Registry, ServerQuery } = require('verdaccio');
const { clickElement, logIn } = require('./helper');
describe('sign in user', () => {
let registry1;
let page;
beforeAll(async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath);
await registry1.init();
const query1 = new ServerQuery(registry1.getRegistryUrl());
await query1.createUser('test', 'test');
page = await global.__BROWSER__.newPage();
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
// eslint-disable-next-line no-console
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
});
afterAll(async () => {
await page.close();
registry1.stop();
});
// this might be increased based on the delays included in all test
jest.setTimeout(20000);
const evaluateSignIn = async function (matchText = 'Login') {
const text = await page.evaluate(() => {
return document.querySelector('button[data-testid="header--button-login"]').textContent;
});
expect(text).toMatch(matchText);
};
test('should match button Login to sign in', async () => {
await evaluateSignIn();
});
test('should click on sign in button', async () => {
const signInButton = await page.$('button[data-testid="header--button-login"]');
await signInButton.click();
await page.waitForTimeout(1000);
const signInDialog = await page.$('#login--dialog');
expect(signInDialog).not.toBeNull();
const closeButton = await page.$('button[data-testid="close-login-dialog-button"]');
await closeButton.click();
await page.waitForTimeout(500);
});
test('should log in an user', async () => {
// we open the dialog
await logIn(page);
// verify if logged in
const accountButton = await page.$('#header--button-account');
expect(accountButton).toBeDefined();
// check whether user is logged
const buttonLogout = await page.$('#logOutDialogIcon');
expect(buttonLogout).toBeDefined();
});
test('should logout an user', async () => {
await page.waitForTimeout(10000);
// we assume the user is logged already
await clickElement(page, '#header--button-account', { delay: 500 });
await page.waitForTimeout(1000);
await clickElement(page, '#logOutDialogIcon > span', { delay: 500 });
await page.waitForTimeout(1000);
await evaluateSignIn();
});
});

18
e2e/ui/tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.reference.json",
"include": ["./cypress/**/*.ts", "./*.ts"],
"references": [
{
"path": "../../packages/core/core"
},
{
"path": "../../packages/verdaccio"
},
{
"path": "../../packages/config"
},
{
"path": "../../packages/tools/helpers"
}
]
}

View File

@ -27,7 +27,7 @@ const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }
}
return (
<CardWrap>
<CardWrap data-testid={title}>
<CardContent>
<StyledText variant="subtitle1">{`${title} (${deps.length})`}</StyledText>
<Tags>
@ -35,6 +35,7 @@ const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }
<Tag
className={'dep-tag'}
clickable={true}
data-testid={name}
key={name}
label={t('dependencies.dependency-block', { package: name, version })}
// eslint-disable-next-line

View File

@ -6,8 +6,9 @@ interface Props {
className?: string;
}
const NoItems: React.FC<Props> = ({ className, text }) => (
<Text className={className} gutterBottom={true} variant="subtitle1">
const NoItems: React.FC<Props> = ({ className, text, ...props }) => (
// eslint-disable-next-line verdaccio/jsx-spread
<Text {...props} className={className} gutterBottom={true} variant="subtitle1">
{text}
</Text>
);

View File

@ -19,7 +19,7 @@ const UpLinks: React.FC = () => {
const { _uplinks: uplinks, latest } = packageMeta;
if (Object.keys(uplinks).length === 0) {
return <NoItems text={t('uplinks.no-items', { name: latest.name })} />;
return <NoItems data-testid="no-uplinks" text={t('uplinks.no-items', { name: latest.name })} />;
}
return (

View File

@ -16,6 +16,7 @@ exports[`<UpLinks /> component should render the component when there is no upli
<div>
<h6
class="MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom emotion-0"
data-testid="no-uplinks"
>
verdaccio has no uplinks.
</h6>
@ -33,6 +34,7 @@ exports[`<UpLinks /> component should render the component when there is no upli
<div>
<h6
class="MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom emotion-0"
data-testid="no-uplinks"
>
verdaccio has no uplinks.
</h6>

View File

@ -1,7 +1,6 @@
import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { DIST_TAGS } from '../../../../../lib/constants';
import { DetailContext } from '../../context';
import VersionsHistoryList from './VersionsHistoryList';
import VersionsTagList from './VersionsTagList';
@ -17,7 +16,7 @@ const Versions: React.FC = () => {
return null;
}
const { versions = {}, time = {}, [DIST_TAGS]: distTags = {} } = packageMeta;
const { versions = {}, time = {}, ['dist-tags']: distTags = {} } = packageMeta;
return (
<>

View File

@ -21,7 +21,7 @@ const VersionsHistoryList: React.FC<Props> = ({ versions, packageName, time }) =
{Object.keys(versions)
.reverse()
.map((version) => (
<ListItem className="version-item" key={version}>
<ListItem className="version-item" data-testid={`version-${version}`} key={version}>
<StyledLink to={`/-/web/detail/${packageName}/v/${version}`}>
<ListItemText>{version}</ListItemText>
</StyledLink>

View File

@ -19,7 +19,7 @@ const VersionsTagList: React.FC<Props> = ({ tags, packageName, time }) => (
return time[tags[a]] < time[tags[b]] ? 1 : time[tags[a]] > time[tags[b]] ? -1 : 0;
})
.map((tag) => (
<ListItem className="version-item" key={tag}>
<ListItem className="version-item" data-testid={`tag-${tag}`} key={tag}>
<StyledLink to={`/-/web/detail/${packageName}/v/${tags[tag]}`}>
<ListItemText>{tag}</ListItemText>
</StyledLink>

View File

@ -25,7 +25,7 @@ const Help: React.FC = () => {
const { t } = useTranslation();
return (
<Card id="help-card">
<Card data-testid="help-card" id="help-card">
<CardContent>
<Typography gutterBottom={true} id="help-card__title" variant="h5">
{t('help.title')}

View File

@ -284,6 +284,7 @@ exports[`<Help /> component should load the component in default state 1`] = `
<div
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root emotion-0 emotion-1"
data-testid="help-card"
id="help-card"
>
<div

View File

@ -179,7 +179,9 @@ const Package: React.FC<PackageInterface> = ({
<Grid container={true} item={true} xs={12}>
<Grid item={true} xs={11}>
<WrapperLink to={`/-/web/detail/${packageName}`}>
<PackageTitle className="package-title">{packageName}</PackageTitle>
<PackageTitle className="package-title" data-testid="package-title">
{packageName}
</PackageTitle>
</WrapperLink>
</Grid>
<GridRightAligned

View File

@ -20,6 +20,7 @@
"eslint-plugin-jsx-a11y": "6.6.1",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react": "7.31.8",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-simple-import-sort": "7.0.0",
"eslint-plugin-verdaccio": "10.0.0"

View File

@ -1,5 +1,11 @@
module.exports = {
extends: ['./rules/base.js', './rules/prettier.js', './rules/react.js', './rules/jest.js'],
extends: [
'./rules/base.js',
'./rules/prettier.js',
'./rules/react.js',
'./rules/jest.js',
'./rules/cypress.js',
],
env: {
es6: true,
node: true,
@ -7,6 +13,7 @@ module.exports = {
globals: {
__APP_VERSION__: true,
NodeJS: true,
cy: true,
},
parserOptions: {
allowImportExportEverywhere: true,

View File

@ -0,0 +1,3 @@
module.exports = {
plugins: ['cypress'],
};

View File

@ -299,21 +299,15 @@ importers:
'@verdaccio/config': workspace:6.0.0-6-next.48
'@verdaccio/core': workspace:6.0.0-6-next.48
'@verdaccio/test-helper': workspace:2.0.0-6-next.5
colorette: 2.0.19
cypress: 10.10.0
debug: 4.3.4
lodash: ^4.17.21
puppeteer: 17.1.3
rimraf: 3.0.2
verdaccio: workspace:6.0.0-6-next.48
devDependencies:
'@verdaccio/config': link:../../packages/config
'@verdaccio/core': link:../../packages/core/core
'@verdaccio/test-helper': link:../../packages/tools/helpers
colorette: 2.0.19
cypress: 10.10.0
debug: 4.3.4
lodash: 4.17.21
puppeteer: 17.1.3
rimraf: 3.0.2
verdaccio: link:../../packages/verdaccio
packages/api:
@ -1175,6 +1169,7 @@ importers:
eslint-config-google: 0.14.0
eslint-config-prettier: 8.5.0
eslint-plugin-babel: 5.3.1
eslint-plugin-cypress: 2.12.1
eslint-plugin-import: 2.26.0
eslint-plugin-jest: 26.9.0
eslint-plugin-jsx-a11y: 6.6.1
@ -1187,6 +1182,7 @@ importers:
eslint-config-google: 0.14.0_eslint@8.23.1
eslint-config-prettier: 8.5.0_eslint@8.23.1
eslint-plugin-babel: 5.3.1_eslint@8.23.1
eslint-plugin-cypress: 2.12.1_eslint@8.23.1
eslint-plugin-import: 2.26.0_a64a5b0f4abe31e3590db284fd772ccf
eslint-plugin-jest: 26.9.0_2ee4cf2b6e0a1b502665284cca313d44
eslint-plugin-jsx-a11y: 6.6.1_eslint@8.23.1
@ -5758,6 +5754,39 @@ packages:
postcss-selector-parser: 6.0.10
dev: true
/@cypress/request/2.88.10:
resolution: {integrity: sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==}
engines: {node: '>= 6'}
dependencies:
aws-sign2: 0.7.0
aws4: 1.11.0
caseless: 0.12.0
combined-stream: 1.0.8
extend: 3.0.2
forever-agent: 0.6.1
form-data: 2.3.3
http-signature: 1.3.6
is-typedarray: 1.0.0
isstream: 0.1.2
json-stringify-safe: 5.0.1
mime-types: 2.1.34
performance-now: 2.1.0
qs: 6.5.3
safe-buffer: 5.2.1
tough-cookie: 2.5.0
tunnel-agent: 0.6.0
uuid: 8.3.2
dev: true
/@cypress/xvfb/1.2.4_supports-color@8.1.1:
resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
dependencies:
debug: 3.2.7_supports-color@8.1.1
lodash.once: 4.1.1
transitivePeerDependencies:
- supports-color
dev: true
/@dianmora/contributors/5.0.0:
resolution: {integrity: sha512-QhvpeVXjK3/q//9vxUtgK5y0chzIaT5c7aUJxR1mI86K0Kyy7rI9oWAiKyOmtO36H9xytODpq9ouoLZFdUfZBQ==}
dependencies:
@ -9234,6 +9263,14 @@ packages:
'@types/node': 17.0.21
dev: true
/@types/sinonjs__fake-timers/8.1.1:
resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
dev: true
/@types/sizzle/2.3.3:
resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
dev: true
/@types/sockjs/0.3.33:
resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==}
dependencies:
@ -10079,6 +10116,10 @@ packages:
engines: {node: '>=8'}
dev: false
/arch/2.2.0:
resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
dev: true
/archy/1.0.0:
resolution: {integrity: sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=}
@ -10199,6 +10240,17 @@ packages:
/asap/2.0.6:
resolution: {integrity: sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=}
/asn1/0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
dependencies:
safer-buffer: 2.1.2
dev: true
/assert-plus/1.0.0:
resolution: {integrity: sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=}
engines: {node: '>=0.8'}
dev: true
/assign-symbols/1.0.0:
resolution: {integrity: sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=}
engines: {node: '>=0.10.0'}
@ -10223,6 +10275,10 @@ packages:
dependencies:
lodash: 4.17.21
/async/3.2.4:
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
dev: true
/asynckit/0.4.0:
resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=}
@ -10298,6 +10354,14 @@ packages:
transitivePeerDependencies:
- supports-color
/aws-sign2/0.7.0:
resolution: {integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=}
dev: true
/aws4/1.11.0:
resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==}
dev: true
/axe-core/4.4.3:
resolution: {integrity: sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==}
engines: {node: '>=4'}
@ -10668,6 +10732,12 @@ packages:
/batch/0.6.1:
resolution: {integrity: sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=}
/bcrypt-pbkdf/1.0.2:
resolution: {integrity: sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=}
dependencies:
tweetnacl: 0.14.5
dev: true
/bcryptjs/2.4.3:
resolution: {integrity: sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=}
dev: false
@ -10709,6 +10779,10 @@ packages:
readable-stream: 3.6.0
dev: true
/blob-util/2.0.2:
resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==}
dev: true
/bluebird/3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
dev: true
@ -10975,6 +11049,11 @@ packages:
normalize-url: 6.1.0
responselike: 2.0.0
/cachedir/2.3.0:
resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==}
engines: {node: '>=6'}
dev: true
/call-bind/1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
@ -11026,6 +11105,10 @@ packages:
/caniuse-lite/1.0.30001410:
resolution: {integrity: sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==}
/caseless/0.12.0:
resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=}
dev: true
/ccount/1.1.0:
resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==}
@ -11100,6 +11183,11 @@ packages:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
/check-more-types/2.24.0:
resolution: {integrity: sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=}
engines: {node: '>= 0.8.0'}
dev: true
/cheerio-select/2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
dependencies:
@ -11433,6 +11521,11 @@ packages:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
/common-tags/1.8.2:
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
engines: {node: '>=4.0.0'}
dev: true
/commondir/1.0.1:
resolution: {integrity: sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=}
@ -12286,10 +12379,67 @@ packages:
array-find-index: 1.0.2
dev: true
/cypress/10.10.0:
resolution: {integrity: sha512-bU8r44x1NIYAUNNXt3CwJpLOVth7HUv2hUhYCxZmgZ1IugowDvuHNpevnoZRQx1KKOEisLvIJW+Xen5Pjn41pg==}
engines: {node: '>=12.0.0'}
hasBin: true
requiresBuild: true
dependencies:
'@cypress/request': 2.88.10
'@cypress/xvfb': 1.2.4_supports-color@8.1.1
'@types/node': 14.17.3
'@types/sinonjs__fake-timers': 8.1.1
'@types/sizzle': 2.3.3
arch: 2.2.0
blob-util: 2.0.2
bluebird: 3.7.2
buffer: 5.7.1
cachedir: 2.3.0
chalk: 4.1.2
check-more-types: 2.24.0
cli-cursor: 3.1.0
cli-table3: 0.6.2
commander: 5.1.0
common-tags: 1.8.2
dayjs: 1.11.5
debug: 4.3.4_supports-color@8.1.1
enquirer: 2.3.6
eventemitter2: 6.4.7
execa: 4.1.0
executable: 4.1.1
extract-zip: 2.0.1_supports-color@8.1.1
figures: 3.2.0
fs-extra: 9.1.0
getos: 3.2.1
is-ci: 3.0.1
is-installed-globally: 0.4.0
lazy-ass: 1.6.0
listr2: 3.13.5_enquirer@2.3.6
lodash: 4.17.21
log-symbols: 4.1.0
minimist: 1.2.6
ospath: 1.2.2
pretty-bytes: 5.6.0
proxy-from-env: 1.0.0
request-progress: 3.0.0
semver: 7.3.7
supports-color: 8.1.1
tmp: 0.2.1
untildify: 4.0.0
yauzl: 2.10.0
dev: true
/damerau-levenshtein/1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
dev: false
/dashdash/1.14.1:
resolution: {integrity: sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=}
engines: {node: '>=0.10'}
dependencies:
assert-plus: 1.0.0
dev: true
/data-urls/3.0.0:
resolution: {integrity: sha512-4AefxbTTdFtxDUdh0BuMBs2qJVL25Mow2zlcuuePegQwgD6GEmQao42LLEeksOui8nL4RcNEugIpFP7eRd33xg==}
engines: {node: '>=12'}
@ -12375,6 +12525,18 @@ packages:
ms: 2.1.3
supports-color: 6.1.0
/debug/3.2.7_supports-color@8.1.1:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.3
supports-color: 8.1.1
dev: true
/debug/4.3.3:
resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==}
engines: {node: '>=6.0'}
@ -12649,10 +12811,6 @@ packages:
- supports-color
dev: true
/devtools-protocol/0.0.1036444:
resolution: {integrity: sha512-0y4f/T8H9lsESV9kKP1HDUXgHxCdniFeJh6Erq+FbdOEvp/Ydp9t8kcAAM5gOd17pMrTDlFWntoHtzzeTUWKNw==}
dev: true
/dezalgo/1.0.3:
resolution: {integrity: sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=}
dependencies:
@ -12891,6 +13049,13 @@ packages:
/eastasianwidth/0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
/ecc-jsbn/0.1.2:
resolution: {integrity: sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=}
dependencies:
jsbn: 0.1.1
safer-buffer: 2.1.2
dev: true
/ecdsa-sig-formatter/1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
dependencies:
@ -13470,6 +13635,15 @@ packages:
eslint-rule-composer: 0.3.0
dev: false
/eslint-plugin-cypress/2.12.1_eslint@8.23.1:
resolution: {integrity: sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==}
peerDependencies:
eslint: '>= 3.2.1'
dependencies:
eslint: 8.23.1
globals: 11.12.0
dev: false
/eslint-plugin-import/2.26.0_a64a5b0f4abe31e3590db284fd772ccf:
resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
engines: {node: '>=4'}
@ -13757,6 +13931,10 @@ packages:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
/eventemitter2/6.4.7:
resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
dev: true
/eventemitter3/4.0.6:
resolution: {integrity: sha512-s3GJL04SQoM+gn2c14oyqxvZ3Pcq7cduSDqy3sBFXx6UPSUmgVYwQM9zwkTn9je0lrfg0gHEwR42pF3Q2dCQkQ==}
@ -13787,6 +13965,21 @@ packages:
signal-exit: 3.0.7
strip-eof: 1.0.0
/execa/4.1.0:
resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
engines: {node: '>=10'}
dependencies:
cross-spawn: 7.0.3
get-stream: 5.2.0
human-signals: 1.1.1
is-stream: 2.0.1
merge-stream: 2.0.0
npm-run-path: 4.0.1
onetime: 5.1.2
signal-exit: 3.0.7
strip-final-newline: 2.0.0
dev: true
/execa/5.0.0:
resolution: {integrity: sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==}
engines: {node: '>=10'}
@ -13815,6 +14008,13 @@ packages:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
/executable/4.1.1:
resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
engines: {node: '>=4'}
dependencies:
pify: 2.3.0
dev: true
/exit/0.1.2:
resolution: {integrity: sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=}
engines: {node: '>= 0.8.0'}
@ -13997,12 +14197,12 @@ packages:
transitivePeerDependencies:
- supports-color
/extract-zip/2.0.1:
/extract-zip/2.0.1_supports-color@8.1.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
hasBin: true
dependencies:
debug: 4.3.4
debug: 4.3.4_supports-color@8.1.1
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
@ -14011,6 +14211,11 @@ packages:
- supports-color
dev: true
/extsprintf/1.3.0:
resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=}
engines: {'0': node >=0.6.0}
dev: true
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@ -14366,6 +14571,10 @@ packages:
resolution: {integrity: sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=}
engines: {node: '>=0.10.0'}
/forever-agent/0.6.1:
resolution: {integrity: sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=}
dev: true
/fork-ts-checker-webpack-plugin/4.1.6_a4613122169baab55b2b1552a44f40da:
resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==}
engines: {node: '>=6.11.5', yarn: '>=1.0.0'}
@ -14425,6 +14634,15 @@ packages:
typescript: 4.8.4
webpack: 5.74.0_esbuild@0.14.10
/form-data/2.3.3:
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
engines: {node: '>= 0.12'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.34
dev: true
/form-data/2.5.1:
resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==}
engines: {node: '>= 0.12'}
@ -14488,10 +14706,6 @@ packages:
webpack: 5.74.0_webpack-cli@4.7.2
dev: true
/fs-constants/1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
dev: true
/fs-extra/10.1.0:
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
engines: {node: '>=12'}
@ -14638,6 +14852,18 @@ packages:
resolution: {integrity: sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=}
engines: {node: '>=0.10.0'}
/getos/3.2.1:
resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
dependencies:
async: 3.2.4
dev: true
/getpass/0.1.7:
resolution: {integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=}
dependencies:
assert-plus: 1.0.0
dev: true
/github-markdown-css/4.0.0:
resolution: {integrity: sha512-mH0bcIKv4XAN0mQVokfTdKo2OD5K8WJE9+lbMdM32/q0Ie5tXgVN/2o+zvToRMxSTUuiTRcLg5hzkFfOyBYreg==}
dev: true
@ -15299,6 +15525,15 @@ packages:
transitivePeerDependencies:
- debug
/http-signature/1.3.6:
resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==}
engines: {node: '>=0.10'}
dependencies:
assert-plus: 1.0.0
jsprim: 2.0.2
sshpk: 1.17.0
dev: true
/http-status-codes/2.2.0:
resolution: {integrity: sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==}
@ -15332,6 +15567,11 @@ packages:
resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==}
dev: true
/human-signals/1.1.1:
resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
engines: {node: '>=8.12.0'}
dev: true
/human-signals/2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@ -15971,6 +16211,10 @@ packages:
resolution: {integrity: sha1-TkMekrEalzFjaqH5yNHMvP2reN8=}
engines: {node: '>=0.10.0'}
/isstream/0.1.2:
resolution: {integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=}
dev: true
/istanbul-lib-coverage/3.2.0:
resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==}
engines: {node: '>=8'}
@ -16568,6 +16812,10 @@ packages:
dependencies:
argparse: 2.0.1
/jsbn/0.1.1:
resolution: {integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM=}
dev: true
/jsdom/17.0.0:
resolution: {integrity: sha512-MUq4XdqwtNurZDVeKScENMPHnkgmdIvMzZ1r1NSwHkDuaqI6BouPjr+17COo4/19oLNnmdpFDPOHVpgIZmZ+VA==}
engines: {node: '>=12'}
@ -16677,6 +16925,10 @@ packages:
/json-schema-traverse/1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
/json-schema/0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
dev: true
/json-stable-stringify-without-jsonify/1.0.1:
resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=}
@ -16752,6 +17004,16 @@ packages:
ms: 2.1.3
semver: 5.7.1
/jsprim/2.0.2:
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
engines: {'0': node >=0.6.0}
dependencies:
assert-plus: 1.0.0
extsprintf: 1.3.0
json-schema: 0.4.0
verror: 1.10.0
dev: true
/jss-plugin-camel-case/10.9.2:
resolution: {integrity: sha512-wgBPlL3WS0WDJ1lPJcgjux/SHnDuu7opmgQKSraKs4z8dCCyYMx9IDPFKBXQ8Q5dVYij1FFV0WdxyhuOOAXuTg==}
dependencies:
@ -16915,6 +17177,11 @@ packages:
dependencies:
package-json: 6.5.0
/lazy-ass/1.6.0:
resolution: {integrity: sha1-eZllXoZGwX8In90YfRUNMyTVRRM=}
engines: {node: '> 0.8'}
dev: true
/leven/3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
@ -18024,10 +18291,6 @@ packages:
engines: {node: '>= 8.0.0'}
dev: true
/mkdirp-classic/0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
dev: true
/mkdirp/0.5.5:
resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==}
hasBin: true
@ -18986,6 +19249,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/ospath/1.2.2:
resolution: {integrity: sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=}
dev: true
/outdent/0.5.0:
resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
dev: true
@ -19238,6 +19505,10 @@ packages:
resolution: {integrity: sha1-elfrVQpng/kRUzH89GY9XI4AelA=}
dev: true
/performance-now/2.1.0:
resolution: {integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=}
dev: true
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@ -20310,8 +20581,8 @@ packages:
forwarded: 0.2.0
ipaddr.js: 1.9.1
/proxy-from-env/1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
/proxy-from-env/1.0.0:
resolution: {integrity: sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=}
dev: true
/prr/1.0.1:
@ -20350,29 +20621,6 @@ packages:
dependencies:
escape-goat: 2.1.1
/puppeteer/17.1.3:
resolution: {integrity: sha512-tVtvNSOOqlq75rUgwLeDAEQoLIiBqmRg0/zedpI6fuqIocIkuxG23A7FIl1oVSkuSMMLgcOP5kVhNETmsmjvPw==}
engines: {node: '>=14.1.0'}
requiresBuild: true
dependencies:
cross-fetch: 3.1.5
debug: 4.3.4
devtools-protocol: 0.0.1036444
extract-zip: 2.0.1
https-proxy-agent: 5.0.1
progress: 2.0.3
proxy-from-env: 1.1.0
rimraf: 3.0.2
tar-fs: 2.1.1
unbzip2-stream: 1.4.3
ws: 8.8.1
transitivePeerDependencies:
- bufferutil
- encoding
- supports-color
- utf-8-validate
dev: true
/pure-color/1.3.0:
resolution: {integrity: sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=}
@ -20387,6 +20635,11 @@ packages:
dependencies:
side-channel: 1.0.4
/qs/6.5.3:
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
engines: {node: '>=0.6'}
dev: true
/qs/6.7.0:
resolution: {integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==}
engines: {node: '>=0.6'}
@ -21232,6 +21485,12 @@ packages:
resolution: {integrity: sha1-jcrkcOHIirwtYA//Sndihtp15jc=}
engines: {node: '>=0.10'}
/request-progress/3.0.0:
resolution: {integrity: sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=}
dependencies:
throttleit: 1.0.0
dev: true
/require-directory/2.1.1:
resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=}
engines: {node: '>=0.10.0'}
@ -22135,6 +22394,22 @@ packages:
/sprintf-js/1.0.3:
resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=}
/sshpk/1.17.0:
resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
asn1: 0.2.6
assert-plus: 1.0.0
bcrypt-pbkdf: 1.0.2
dashdash: 1.14.1
ecc-jsbn: 0.1.2
getpass: 0.1.7
jsbn: 0.1.1
safer-buffer: 2.1.2
tweetnacl: 0.14.5
dev: true
/stable/0.1.8:
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
@ -22636,26 +22911,6 @@ packages:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
/tar-fs/2.1.1:
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
dependencies:
chownr: 1.1.4
mkdirp-classic: 0.5.3
pump: 3.0.0
tar-stream: 2.2.0
dev: true
/tar-stream/2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
dependencies:
bl: 4.1.0
end-of-stream: 1.4.4
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.0
dev: true
/tar/4.4.19:
resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==}
engines: {node: '>=4.5'}
@ -22854,6 +23109,10 @@ packages:
dependencies:
real-require: 0.2.0
/throttleit/1.0.0:
resolution: {integrity: sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=}
dev: true
/through/2.3.8:
resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=}
@ -22885,6 +23144,13 @@ packages:
os-tmpdir: 1.0.2
dev: true
/tmp/0.2.1:
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
engines: {node: '>=8.17.0'}
dependencies:
rimraf: 3.0.2
dev: true
/tmpl/1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
@ -22943,6 +23209,14 @@ packages:
nopt: 1.0.10
dev: true
/tough-cookie/2.5.0:
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
engines: {node: '>=0.8'}
dependencies:
psl: 1.8.0
punycode: 2.1.1
dev: true
/tough-cookie/4.0.0:
resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==}
engines: {node: '>=6'}
@ -23078,6 +23352,16 @@ packages:
yargs: 17.3.1
dev: true
/tunnel-agent/0.6.0:
resolution: {integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=}
dependencies:
safe-buffer: 5.2.1
dev: true
/tweetnacl/0.14.5:
resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=}
dev: true
/typanion/3.3.1:
resolution: {integrity: sha512-VogBiMj3ZQuWaHkbhXwSgd9jXE4s7EMaaV7VSEiKTNYnKJs/bPjvcOGbD7rTM9aPqTABvgLVEZ+iFP6ab12HtQ==}
dev: false
@ -23228,13 +23512,6 @@ packages:
has-symbols: 1.0.3
which-boxed-primitive: 1.0.2
/unbzip2-stream/1.4.3:
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
dependencies:
buffer: 5.7.1
through: 2.3.8
dev: true
/undefsafe/2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
dev: true
@ -23447,6 +23724,11 @@ packages:
has-value: 0.3.1
isobject: 3.0.1
/untildify/4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
dev: true
/upath/1.2.0:
resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
engines: {node: '>=4'}
@ -23808,6 +24090,15 @@ packages:
resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
engines: {node: '>= 0.8'}
/verror/1.10.0:
resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=}
engines: {'0': node >=0.6.0}
dependencies:
assert-plus: 1.0.0
core-util-is: 1.0.2
extsprintf: 1.3.0
dev: true
/vfile-location/3.2.0:
resolution: {integrity: sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==}
@ -24743,19 +25034,6 @@ packages:
utf-8-validate:
optional: true
/ws/8.8.1:
resolution: {integrity: sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: true
/xdg-basedir/4.0.0:
resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==}
engines: {node: '>=8'}