add tests for new manage users UI and improve checkbox accessibility (#536)

* e2e test for manage user page, and updating styles for ui components

* make checkbox more accessible and create e2e test around creating user

* add react testing library and use it for radio testing

* clean up comments

* update docs and clean up
This commit is contained in:
Gabe Hernandez 2021-03-31 13:33:00 +01:00 committed by Zach Wasserman
parent d0ded91d0b
commit be77b0de59
15 changed files with 237 additions and 99 deletions

View File

@ -210,22 +210,24 @@ xp-fleetctl: .pre-binary-bundle .pre-fleetctl generate-go
binary-bundle: xp-fleet xp-fleetctl binary-bundle: xp-fleet xp-fleetctl
cd build/binary-bundle && zip -r fleet.zip darwin/ linux/ windows/ cd build/binary-bundle && zip -r fleet.zip darwin/ linux/ windows/
cd build/binary-bundle && mkdir fleetctl-macos && cp darwin/fleetctl fleetctl-macos && tar -czf fleetctl-macos.tar.gz fleetctl-macos cd build/binary-bundle && mkdir fleetctl-macos && cp darwin/fleetctl fleetctl-macos && tar -czf fleetctl-macos.tar.gz fleetctl-macos
cd build/binary-bundle && mkdir fleetctl-linux && cp linux/fleetctl fleetctl-linux && tar -czf fleetctl-linux.tar.gz fleetctl-linux cd build/binary-bundle && mkdir fleetctl-linux && cp linux/fleetctl fleetctl-linux && tar -czf fleetctl-linux.tar.gz fleetctl-linux
cd build/binary-bundle && mkdir fleetctl-windows && cp windows/fleetctl.exe fleetctl-windows && tar -czf fleetctl-windows.tar.gz fleetctl-windows cd build/binary-bundle && mkdir fleetctl-windows && cp windows/fleetctl.exe fleetctl-windows && tar -czf fleetctl-windows.tar.gz fleetctl-windows
cd build/binary-bundle && cp windows/fleetctl.exe . && zip fleetctl.exe.zip fleetctl.exe cd build/binary-bundle && cp windows/fleetctl.exe . && zip fleetctl.exe.zip fleetctl.exe
cd build/binary-bundle && shasum -a 256 fleet.zip fleetctl.exe.zip fleetctl-macos.tar.gz fleetctl-windows.tar.gz fleetctl-linux.tar.gz cd build/binary-bundle && shasum -a 256 fleet.zip fleetctl.exe.zip fleetctl-macos.tar.gz fleetctl-windows.tar.gz fleetctl-linux.tar.gz
# Drop, create, and migrate the e2e test database # Drop, create, and migrate the e2e test database
e2e-reset-db: e2e-reset-db:
docker-compose exec -T mysql_test bash -c 'echo "drop database if exists e2e; create database e2e;" | mysql -uroot -ptoor' docker-compose exec -T mysql_test bash -c 'echo "drop database if exists e2e; create database e2e;" | mysql -uroot -ptoor'
./build/fleet prepare db --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e ./build/fleet prepare db --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e
e2e-setup: e2e-setup:
./build/fleetctl config set --context e2e --address https://localhost:8642 ./build/fleetctl config set --context e2e --address https://localhost:8642
./build/fleetctl config set --context e2e --tls-skip-verify true ./build/fleetctl config set --context e2e --tls-skip-verify true
./build/fleetctl setup --context e2e --email=test@fleetdm.com --username=test --password=admin123# --org-name='Fleet Test' ./build/fleetctl setup --context e2e --email=test@fleetdm.com --username=test --password=admin123# --org-name='Fleet Test'
./build/fleetctl user create --context e2e --username=user1 --email=user1@example.com --sso=true ./build/fleetctl user create --context e2e --username=user1 --email=user1@example.com --sso=true
./build/fleetctl user create --context e2e --email=test+1@fleetdm.com --username=test1 --password=admin123#
./build/fleetctl user create --context e2e --email=test+2@fleetdm.com --username=test2 --password=admin123#
e2e-serve: e2e-serve:
./build/fleet serve --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e --server_address=localhost:8642 ./build/fleet serve --mysql_address=localhost:3307 --mysql_username=root --mysql_password=toor --auth_jwt_key=insecure --mysql_database=e2e --server_address=localhost:8642

View File

@ -0,0 +1,55 @@
describe('Manage Users', () => {
beforeEach(() => {
cy.setup();
cy.login();
cy.setupSMTP();
});
it('Searching for a user', () => {
cy.intercept({
method: 'GET',
url: '/api/v1/fleet/users',
}).as('getUsers');
cy.visit('/settings/users');
cy.url().should('match', /\/settings\/users$/i);
cy.wait('@getUsers');
cy.findByText('test@fleetdm.com')
.should('exist');
cy.findByText('test+1@fleetdm.com')
.should('exist');
cy.findByText('test+2@fleetdm.com')
.should('exist');
cy.findByPlaceholderText('Search')
.type('test@fleetdm.com');
cy.wait('@getUsers');
cy.findByText('test@fleetdm.com')
.should('exist');
cy.findByText('test+1@fleetdm.com')
.should('not.exist');
cy.findByText('test+2@fleetdm.com')
.should('not.exist');
});
it('Creating a user', () => {
cy.visit('/settings/users');
cy.url().should('match', /\/settings\/users$/i);
cy.contains('button:enabled', /create user/i)
.click();
cy.findByPlaceholderText('Full Name')
.type('New User');
cy.findByPlaceholderText('Email')
.type('new-user@fleetdm.com');
cy.findByRole('checkbox', { name: 'Test Team' })
.click({ force: true }); // we use `force` as the checkbox button is not fully accessible yet.
});
});

View File

@ -30,35 +30,14 @@ Cypress.Commands.add('setup', () => {
cy.exec('make e2e-reset-db e2e-setup', { timeout: 10000 }); cy.exec('make e2e-reset-db e2e-setup', { timeout: 10000 });
}); });
Cypress.Commands.add('login', (username, password) => { Cypress.Commands.add('setupSMTP', () => {
username ||= 'test';
password ||= 'admin123#';
cy.request('POST', '/api/v1/fleet/login', { username, password })
.then((resp) => {
window.localStorage.setItem('KOLIDE::auth_token', resp.body.token);
});
});
Cypress.Commands.add('logout', () => {
cy.request({
url: '/api/v1/fleet/logout',
method: 'POST',
body: {},
auth: {
bearer: window.localStorage.getItem('KOLIDE::auth_token'),
},
});
});
Cypress.Commands.add('setupSSO', (enable_idp_login = false) => {
const body = { const body = {
sso_settings: { smtp_settings: {
enable_sso: true, authentication_type: 'authtype_none',
enable_sso_idp_login: enable_idp_login, enable_smtp: true,
entity_id: 'https://localhost:8080', port: 1025,
idp_name: 'SimpleSAML', sender_address: 'gabriel+dev@fleetdm.com',
issuer_uri: 'http://localhost:8080/simplesaml/saml2/idp/SSOService.php', server: 'localhost',
metadata_url: 'http://localhost:9080/simplesaml/saml2/idp/metadata.php',
}, },
}; };
@ -72,44 +51,11 @@ Cypress.Commands.add('setupSSO', (enable_idp_login = false) => {
}); });
}); });
Cypress.Commands.add('loginSSO', () => { Cypress.Commands.add('login', (username, password) => {
// Note these requests set cookies that are required for the SSO flow to username ||= 'test';
// work properly. This is handled automatically by the browser. password ||= 'admin123#';
cy.request({ cy.request('POST', '/api/v1/fleet/login', { username, password })
method: 'GET', .then((resp) => {
url: 'http://localhost:9080/simplesaml/saml2/idp/SSOService.php?spentityid=https://localhost:8080', window.localStorage.setItem('KOLIDE::auth_token', resp.body.token);
followRedirect: false,
}).then((firstResponse) => {
const redirect = firstResponse.headers.location;
cy.request({
method: 'GET',
url: redirect,
followRedirect: false,
}).then((secondResponse) => {
const el = document.createElement('html');
el.innerHTML = secondResponse.body;
const authState = el.getElementsByTagName('input').namedItem('AuthState').defaultValue;
cy.request({
method: 'POST',
url: redirect,
body: `username=user1&password=user1pass&AuthState=${authState}`,
form: true,
followRedirect: false,
}).then((finalResponse) => {
el.innerHTML = finalResponse.body;
const saml = el.getElementsByTagName('input').namedItem('SAMLResponse').defaultValue;
// Load the callback URL with the response from the IdP
cy.visit({
url: '/api/v1/fleet/sso/callback',
method: 'POST',
body: {
SAMLResponse: saml,
},
});
});
}); });
});
}); });

View File

@ -20,6 +20,9 @@ declare namespace Cypress {
/** /**
* Custom command to setup the SMTP configuration for this testing environment. * Custom command to setup the SMTP configuration for this testing environment.
*
* NOTE: login() command is required before this, as it will make authenticated
* requests to set up SMTP
*/ */
setupSMTP(): Chainable<Element>; setupSMTP(): Chainable<Element>;

View File

@ -15,6 +15,3 @@
// Import commands.js using ES2015 syntax: // Import commands.js using ES2015 syntax:
import './commands'; import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -33,7 +33,6 @@ class Checkbox extends Component {
const { handleChange } = this; const { handleChange } = this;
const { children, className, disabled, name, value, wrapperClassName } = this.props; const { children, className, disabled, name, value, wrapperClassName } = this.props;
const checkBoxClass = classnames(baseClass, className); const checkBoxClass = classnames(baseClass, className);
const formFieldProps = pick(this.props, ['hint', 'label', 'error', 'name']); const formFieldProps = pick(this.props, ['hint', 'label', 'error', 'name']);
const checkBoxTickClass = classnames(`${checkBoxClass}__tick`, { const checkBoxTickClass = classnames(`${checkBoxClass}__tick`, {

View File

@ -4,15 +4,19 @@
display: inline-block; display: inline-block;
&__input { &__input {
visibility: hidden; opacity: 0;
margin: 0; margin: 0;
position: absolute;
z-index: -1; &:focus + .kolide-checkbox__tick {
&::after {
border-color: $core-blue;
}
}
&:checked + .kolide-checkbox__tick { &:checked + .kolide-checkbox__tick {
&::after { &::after {
background-color: $core-blue; background-color: $core-blue;
border: solid 1px $core-blue; border: solid 2px $core-blue;
} }
&::before { &::before {
@ -41,7 +45,7 @@
@include size(20px); @include size(20px);
transition: border 75ms ease-in-out, background 75ms ease-in-out; transition: border 75ms ease-in-out, background 75ms ease-in-out;
border-radius: 2px; border-radius: 2px;
border: solid 1px $ui-borders; border: solid 2px $ui-borders;
content: ''; content: '';
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
@ -62,6 +66,6 @@
line-height: 20px; line-height: 20px;
letter-spacing: 0.5px; letter-spacing: 0.5px;
color: $core-dark-blue-grey; color: $core-dark-blue-grey;
padding-left: 25px; padding-left: $pad-small;
} }
} }

View File

@ -1,7 +1,7 @@
.dropdown { .dropdown {
&__label { &__label {
display: block; display: block;
font-size: 16px; font-size: $x-small;
font-weight: $regular; font-weight: $regular;
color: $core-dark-blue-grey; color: $core-dark-blue-grey;
margin-bottom: $pad-xsmall; margin-bottom: $pad-xsmall;
@ -53,7 +53,7 @@
} }
.Select-value { .Select-value {
font-size: $small; font-size: $x-small;
border-radius: 4px; border-radius: 4px;
background-color: $ui-light-grey; background-color: $ui-light-grey;
border: solid 1px $ui-borders; border: solid 1px $ui-borders;
@ -77,7 +77,7 @@
top: 50%; top: 50%;
left: 50%; left: 50%;
visibility: visible; visibility: visible;
font-size: 16px; font-size: $x-small;
color: $ui-medium-grey; color: $ui-medium-grey;
text-indent: 0; text-indent: 0;
} }
@ -90,7 +90,7 @@
} }
.Select-value-label { .Select-value-label {
font-size: 16px; font-size: $x-small;
font-weight: $regular; font-weight: $regular;
color: $core-dark-blue-grey; color: $core-dark-blue-grey;
line-height: 28px; line-height: 28px;
@ -127,7 +127,7 @@
.Select-value { .Select-value {
.Select-value-label { .Select-value-label {
color: $core-dark-blue-grey; color: $core-dark-blue-grey;
font-size: 16px; font-size: $x-small;
} }
} }
} }
@ -177,7 +177,7 @@
.Select-placeholder { .Select-placeholder {
color: $core-medium-blue-grey; color: $core-medium-blue-grey;
font-size: $small; font-size: $x-small;
line-height: 40px; line-height: 40px;
padding: 0 12px; padding: 0 12px;
box-sizing: border-box; box-sizing: border-box;

View File

@ -0,0 +1,34 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import Radio from './Radio';
describe('Radio - component', () => {
it('renders the correct selected state', () => {
render(<Radio
checked
label={'Test Radio'}
value={'Test Radio'}
id={'test-radio'}
onChange={() => { return null; }}
name={'Test Radio'}
/>);
const radio = screen.getByRole('radio', { name: 'Test Radio' });
expect(radio).toBeChecked();
});
it('renders the correct disabled state', () => {
render(<Radio
disabled
label={'Test Radio'}
value={'Test Radio'}
id={'test-radio'}
onChange={() => { return null; }}
name={'Test Radio'}
/>);
const radio = screen.getByRole('radio', { name: 'Test Radio' });
expect(radio).toBeDisabled();
});
});

View File

@ -26,11 +26,6 @@ enum UserTeamType {
} }
const globalUserRoles = [ const globalUserRoles = [
{
disabled: false,
label: 'admin',
value: 'admin',
},
{ {
disabled: false, disabled: false,
label: 'observer', label: 'observer',
@ -41,6 +36,11 @@ const globalUserRoles = [
label: 'maintainer', label: 'maintainer',
value: 'maintainer', value: 'maintainer',
}, },
{
disabled: false,
label: 'admin',
value: 'admin',
},
]; ];
interface IFormData { interface IFormData {
@ -216,7 +216,7 @@ class CreateUserForm extends Component <ICreateUserFormProps, ICreateUserFormSta
</InfoBanner> </InfoBanner>
<p className={`${baseClass}__label`}>Role</p> <p className={`${baseClass}__label`}>Role</p>
<Dropdown <Dropdown
value={global_role || 'admin'} value={global_role || 'observer'}
className={`${baseClass}__global-role-dropdown`} className={`${baseClass}__global-role-dropdown`}
options={globalUserRoles} options={globalUserRoles}
searchable={false} searchable={false}

View File

@ -110,6 +110,7 @@ const SelectedTeamsForm = (props: ISelectedTeamsFormProps): JSX.Element => {
value={isChecked} value={isChecked}
name={name} name={name}
onChange={(newValue: boolean) => updateSelectedTeams(teamItem.id, newValue, 'checkbox')} onChange={(newValue: boolean) => updateSelectedTeams(teamItem.id, newValue, 'checkbox')}
testId={`${name}-checkbox`}
> >
{name} {name}
</Checkbox> </Checkbox>
@ -119,6 +120,7 @@ const SelectedTeamsForm = (props: ISelectedTeamsFormProps): JSX.Element => {
options={roles} options={roles}
searchable={false} searchable={false}
onChange={(newValue: string) => updateSelectedTeams(teamItem.id, newValue, 'dropdown')} onChange={(newValue: string) => updateSelectedTeams(teamItem.id, newValue, 'dropdown')}
testId={`${name}-checkbox`}
/> />
</div> </div>
); );

View File

@ -6,6 +6,9 @@ import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16'; import Adapter from 'enzyme-adapter-react-16';
import nock from 'nock'; import nock from 'nock';
// for testing-library utils
import '@testing-library/jest-dom';
// Uncomment for verbose unhandled promise rejection warnings // Uncomment for verbose unhandled promise rejection warnings
// process.on('unhandledRejection', (reason) => { // process.on('unhandledRejection', (reason) => {
// console.error('REJECTION', reason); // console.error('REJECTION', reason);

View File

@ -139,6 +139,8 @@
"@babel/preset-react": "^7.12.7", "@babel/preset-react": "^7.12.7",
"@babel/preset-typescript": "^7.12.7", "@babel/preset-typescript": "^7.12.7",
"@testing-library/cypress": "^7.0.4", "@testing-library/cypress": "^7.0.4",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@tsconfig/recommended": "^1.0.1", "@tsconfig/recommended": "^1.0.1",
"@types/classnames": "0.0.32", "@types/classnames": "0.0.32",
"@types/cypress": "^1.1.3", "@types/cypress": "^1.1.3",

View File

@ -4,7 +4,6 @@
"baseUrl": "./frontend", "baseUrl": "./frontend",
"sourceMap": true, "sourceMap": true,
"jsx": "react", "jsx": "react",
"types": ["cypress", "@testing-library/cypress"]
}, },
"include": [ "include": [
"./frontend/**/*" "./frontend/**/*"

102
yarn.lock
View File

@ -1046,7 +1046,7 @@
core-js-pure "^3.0.0" core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5": "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.9.2":
version "7.13.10" version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
@ -1441,6 +1441,28 @@
lz-string "^1.4.4" lz-string "^1.4.4"
pretty-format "^26.6.2" pretty-format "^26.6.2"
"@testing-library/jest-dom@^5.11.9":
version "5.11.9"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz#e6b3cd687021f89f261bd53cbe367041fbd3e975"
integrity sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
aria-query "^4.2.2"
chalk "^3.0.0"
css "^3.0.0"
css.escape "^1.5.1"
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react@^11.2.5":
version "11.2.5"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.5.tgz#ae1c36a66c7790ddb6662c416c27863d87818eb9"
integrity sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==
dependencies:
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^7.28.1"
"@tsconfig/recommended@^1.0.1": "@tsconfig/recommended@^1.0.1":
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.1.tgz#7619bad397e06ead1c5182926c944e0ca6177f52" resolved "https://registry.yarnpkg.com/@tsconfig/recommended/-/recommended-1.0.1.tgz#7619bad397e06ead1c5182926c944e0ca6177f52"
@ -1540,6 +1562,14 @@
dependencies: dependencies:
"@types/istanbul-lib-report" "*" "@types/istanbul-lib-report" "*"
"@types/jest@*":
version "26.0.21"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.21.tgz#3a73c2731e7e4f0fbaea56ce7ff8c79cf812bd24"
integrity sha512-ab9TyM/69yg7eew9eOwKMUmvIZAKEGZYlq/dhe5/0IMUd/QLJv5ldRMdddSn+u22N13FP3s5jYyktxuBwY0kDA==
dependencies:
jest-diff "^26.0.0"
pretty-format "^26.0.0"
"@types/js-md5@^0.4.2": "@types/js-md5@^0.4.2":
version "0.4.2" version "0.4.2"
resolved "https://registry.yarnpkg.com/@types/js-md5/-/js-md5-0.4.2.tgz#95b39911e2081bf2915436e61cc345e12459e5bb" resolved "https://registry.yarnpkg.com/@types/js-md5/-/js-md5-0.4.2.tgz#95b39911e2081bf2915436e61cc345e12459e5bb"
@ -1672,6 +1702,13 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
"@types/testing-library__jest-dom@^5.9.1":
version "5.9.5"
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0"
integrity sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==
dependencies:
"@types/jest" "*"
"@types/yargs-parser@*": "@types/yargs-parser@*":
version "15.0.0" version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@ -2419,7 +2456,7 @@ at-least-node@^1.0.0:
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
atob@^2.1.1: atob@^2.1.1, atob@^2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
@ -3774,6 +3811,14 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0" strip-ansi "^3.0.0"
supports-color "^2.0.0" supports-color "^2.0.0"
chalk@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.1.0: chalk@^4.0.0, chalk@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
@ -4415,6 +4460,20 @@ css-what@2.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
css@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
dependencies:
inherits "^2.0.4"
source-map "^0.6.1"
source-map-resolve "^0.6.0"
cssesc@^0.1.0: cssesc@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
@ -7026,6 +7085,11 @@ indent-string@^3.0.0:
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
indexes-of@^1.0.1: indexes-of@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
@ -7039,7 +7103,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -7712,7 +7776,7 @@ jest-config@^26.6.3:
micromatch "^4.0.2" micromatch "^4.0.2"
pretty-format "^26.6.2" pretty-format "^26.6.2"
jest-diff@^26.6.2: jest-diff@^26.0.0, jest-diff@^26.6.2:
version "26.6.2" version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
@ -8873,6 +8937,11 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
mini-css-extract-plugin@^0.7.0: mini-css-extract-plugin@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0"
@ -10340,7 +10409,7 @@ pretty-error@^2.0.2:
renderkid "^2.0.1" renderkid "^2.0.1"
utila "~0.4" utila "~0.4"
pretty-format@^26.6.2: pretty-format@^26.0.0, pretty-format@^26.6.2:
version "26.6.2" version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
@ -10907,6 +10976,14 @@ redent@^1.0.0:
indent-string "^2.1.0" indent-string "^2.1.0"
strip-indent "^1.0.1" strip-indent "^1.0.1"
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
dependencies:
indent-string "^4.0.0"
strip-indent "^3.0.0"
reduce-css-calc@^1.2.6: reduce-css-calc@^1.2.6:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
@ -11818,6 +11895,14 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0" source-map-url "^0.4.0"
urix "^0.1.0" urix "^0.1.0"
source-map-resolve@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
dependencies:
atob "^2.1.2"
decode-uri-component "^0.2.0"
source-map-support@^0.4.0, source-map-support@^0.4.15: source-map-support@^0.4.0, source-map-support@^0.4.15:
version "0.4.18" version "0.4.18"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
@ -12186,6 +12271,13 @@ strip-indent@^1.0.1:
dependencies: dependencies:
get-stdin "^4.0.1" get-stdin "^4.0.1"
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
dependencies:
min-indent "^1.0.0"
strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"