From 9a2ed152d800a905bd6d3bf5ba2ebcb32c41e73f Mon Sep 17 00:00:00 2001 From: Siarhei Lunski Date: Mon, 23 Sep 2024 10:55:39 +0200 Subject: [PATCH] chore(tests): Migrate from Enzyme to RTL (1st batch) (#1535) * chore(tests): First batch of tests conversion * chore(tests): Conver Settings-test to RTL * chore(tests): Convert SliderControl test to RTL * chore(tests): Convert ArchiveExplorer test to RTL * chore(tests): The next batch of tests conversion * chore(tests): Bump types * chore(tests): Apply user-events where possible * chore(tests): Apply suggestions and clean up * chore(tests): Update button focus test --- .eslintrc.js | 6 + build/jest/envGlobals.js | 19 + build/jest/envWindow.js | 17 - build/jest/react-intl-mock.js | 3 + jest.config.js | 1 + package.json | 13 +- src/lib/types/jest-global.d.ts | 15 + src/lib/viewers/archive/ArchiveExplorer.js | 2 +- .../__tests__/ArchiveExplorer-test-react.js | 376 ++--- .../__tests__/Breadcrumbs-test-react.js | 85 +- .../__tests__/Image360Controls-test.tsx | 36 +- .../__tests__/Model3DControlsNew-test.tsx | 152 +- .../annotations/AnnotationsControls.tsx | 4 +- .../__tests__/AnnotationsButton-test.tsx | 47 +- .../__tests__/AnnotationsControls-test.tsx | 150 +- .../AnnotationsTargetedTooltip-test.tsx | 38 +- .../__tests__/DrawingControls-test.tsx | 14 +- .../controls/box3d/RotateAxisControl.tsx | 8 +- .../controls/box3d/RotateAxisControls.tsx | 2 +- .../__tests__/AnimationClipsControl-test.tsx | 83 +- .../__tests__/AnimationControls-test.tsx | 61 +- .../box3d/__tests__/Model3DSettings-test.tsx | 139 +- .../__tests__/RotateAxisControl-test.tsx | 48 +- .../__tests__/RotateAxisControls-test.tsx | 39 +- .../box3d/__tests__/VrToggleControl-test.tsx | 35 +- .../color-picker/ColorPickerControl.tsx | 2 +- .../__tests__/ControlsBar-test.tsx | 15 +- .../viewers/controls/media/TimeControls.tsx | 2 +- .../viewers/controls/media/VolumeControls.tsx | 2 +- .../media/__tests__/MediaSettings-test.tsx | 269 +++- .../media/__tests__/TimeControls-test.tsx | 148 +- .../page/__tests__/PageControlsForm-test.tsx | 110 +- .../viewers/controls/settings/Settings.tsx | 1 + .../controls/settings/SettingsDropdown.tsx | 2 +- .../controls/settings/SettingsToggle.tsx | 7 +- .../settings/__tests__/Settings-test.tsx | 234 ++-- .../__tests__/SettingsDropdown-test.tsx | 282 ++-- .../settings/__tests__/SettingsList-test.tsx | 113 +- .../viewers/controls/slider/SliderControl.tsx | 4 +- .../slider/__tests__/SliderControl-test.tsx | 285 ++-- .../media/__tests__/DashControls-test.tsx | 255 ++-- tsconfig.json | 3 + yarn.lock | 1245 ++++++++++++++--- 43 files changed, 2581 insertions(+), 1791 deletions(-) create mode 100644 src/lib/types/jest-global.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 3a5155445..de1698629 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,6 +28,12 @@ module.exports = { '@typescript-eslint/no-unused-vars': 'off', }, }, + { + files: ['**/*test.ts', '**/*test.tsx'], + rules: { + '@typescript-eslint/no-non-null-assertion': 'off', + }, + }, ], parser: '@typescript-eslint/parser', rules: { diff --git a/build/jest/envGlobals.js b/build/jest/envGlobals.js index 5ec981223..1e058085d 100644 --- a/build/jest/envGlobals.js +++ b/build/jest/envGlobals.js @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ const i18n = require('../../src/i18n/json/en-US.json'); const fixture = require('./fixtureLoader'); const Worker = require('./workerMock'); @@ -13,3 +14,21 @@ global.fixture = fixture; global.URL.createObjectURL = () => ''; global.URL.revokeObjectURL = () => ''; global.Worker = Worker; + +/** + * Older versions of JSDOM (<21.1.2) don't support pageX and pageY attributes in MouseEvent which breaks mouse move simulation in unit tests. + * Jest v30 comes with JSDOM version 22 which has this issue resolved. After upgrade to Jest v30 this patch can be removed. + * More details: https://github.com/jestjs/jest/pull/13825#issuecomment-1452037295 + */ +class MouseEventExtended extends MouseEvent { + constructor(type, values) { + const { pageX, pageY, ...mouseValues } = values; + super(type, mouseValues); + Object.assign(this, { + pageX: pageX || 0, + pageY: pageY || 0, + }); + } +} + +global.MouseEventExtended = MouseEventExtended; diff --git a/build/jest/envWindow.js b/build/jest/envWindow.js index cb649ba9d..945a8f46b 100644 --- a/build/jest/envWindow.js +++ b/build/jest/envWindow.js @@ -4,23 +4,6 @@ Object.defineProperty(global.navigator, 'mimeTypes', { writable: true, }); -Object.defineProperty(document, 'createRange', { - value: () => ({ - commonAncestorContainer: { - nodeName: 'BODY', - ownerDocument: document, - }, - createContextualFragment: fragment => { - const el = document.createElement('div'); - el.innerHTML = fragment; - return el.children[0]; - }, - selectNode: () => {}, - setStart: () => {}, - setEnd: () => {}, - }), -}); - Object.defineProperty(HTMLElement.prototype, 'offsetParent', { get() { return this.parentNode; diff --git a/build/jest/react-intl-mock.js b/build/jest/react-intl-mock.js index 14ea21308..8d2763b88 100644 --- a/build/jest/react-intl-mock.js +++ b/build/jest/react-intl-mock.js @@ -26,3 +26,6 @@ export const injectIntl = Component => { WrapperComponent.displayName = Component.displayName || Component.name || 'Component'; return WrapperComponent; }; + +// eslint-disable-next-line react/prop-types +export const IntlProvider = ({ children }) =>
{children}
; diff --git a/jest.config.js b/jest.config.js index 32b5b0f30..1810cd42a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,6 +18,7 @@ module.exports = { restoreMocks: true, roots: ['src'], setupFiles: [ + '/src/lib/types/jest-global.d.ts', '/build/jest/envGlobals.js', '/build/jest/envWindow.js', 'jest-canvas-mock', diff --git a/package.json b/package.json index ad5524d3c..e323c7476 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,15 @@ "@commitlint/cli": "^8.2.0", "@commitlint/config-conventional": "^8.2.0", "@commitlint/travis-cli": "^8.2.0", - "@testing-library/jest-dom": "^5.11.4", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^14.5.2", "@types/classnames": "^2.2.11", "@types/enzyme": "^3.10.8", + "@types/jest": "^29.5.13", "@types/lodash": "^4.14.149", - "@types/react": "^17.0.2", + "@types/react": "^17.0.38", "@types/react-dom": "^17.0.1", "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", @@ -44,7 +48,7 @@ "create-react-class": "^15.6.2", "css-loader": "^3.1.0", "cypress": "^12.15.0", - "enzyme": "^3.10.0", + "enzyme": "^3.11.0", "eslint": "^8.43.0", "eslint-config-airbnb": "^18.0.1", "eslint-config-prettier": "^6.7.0", @@ -153,6 +157,7 @@ }, "resolutions": { "mojito-rb-gen/merge": "1.2.1", - "**/react-intl/**/@types/react": "^17.0.2" + "**/react-intl/**/@types/react": "^17.0.2", + "cheerio": "1.0.0-rc.12" } } diff --git a/src/lib/types/jest-global.d.ts b/src/lib/types/jest-global.d.ts new file mode 100644 index 000000000..3d5b01889 --- /dev/null +++ b/src/lib/types/jest-global.d.ts @@ -0,0 +1,15 @@ +declare global { + // eslint-disable-next-line vars-on-top, no-var + var MouseEventExtended: { + new ( + type: string, + eventInitDict?: MouseEventInit & { + pageX?: number; + pageY?: number; + }, + ): MouseEvent; + prototype: MouseEvent; + }; +} + +export {}; diff --git a/src/lib/viewers/archive/ArchiveExplorer.js b/src/lib/viewers/archive/ArchiveExplorer.js index ce074d579..514cf5641 100644 --- a/src/lib/viewers/archive/ArchiveExplorer.js +++ b/src/lib/viewers/archive/ArchiveExplorer.js @@ -210,7 +210,7 @@ class ArchiveExplorer extends React.Component { return ( -
+
shallow(); // eslint-disable-line describe('lib/viewers/archive/ArchiveExplorer', () => { - beforeEach(() => { - filename = 'test.zip'; - data = [ - { - type: 'folder', - absolute_path: 'test/', - name: 'test', - modified_at: '19-Dec-02 16:43', - size: 0, - item_collection: ['test/csv-level-1.csv', 'test/pdf-level-1.pdf', 'test/subfolder/'], - }, - { - type: 'file', - absolute_path: 'test/csv-level-1.csv', - name: 'csv-level-1.csv', - modified_at: '19-Nov-04 16:11', - size: 133, - item_collection: null, - }, - { - type: 'folder', - absolute_path: 'test/subfolder/', - name: 'subfolder', - modified_at: '19-Dec-02 16:43', - size: 0, - item_collection: ['test/subfolder/test-level-2.jpg'], - }, - { - type: 'file', - absolute_path: 'test/subfolder/test-level-2.jpg', - name: 'test-level-1.jpg', - modified_at: '19-Nov-08 15:08', - size: 57379, - item_collection: null, - }, - { - type: 'file', - absolute_path: 'level-0.txt', - name: 'level-0.txt', - modified_at: '19-Nov-04 16:11', - size: 1, - item_collection: null, - }, - { - type: 'file', - absolute_path: 'test/pdf-level-1.pdf', - name: 'pdf-level-1.pdf', - modified_at: '19-Nov-04 16:11', - size: 233, - item_collection: null, - }, - ]; + const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight'); + const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth'); + const filename = 'test.zip'; + const data = [ + { + type: 'folder', + absolute_path: 'test/', + name: 'test', + modified_at: '19-Dec-02 16:43', + size: 0, + item_collection: ['test/csv-level-1.csv', 'test/pdf-level-1.pdf', 'test/subfolder/'], + }, + { + type: 'file', + absolute_path: 'test/csv-level-1.csv', + name: 'csv-level-1.csv', + modified_at: '19-Nov-04 16:11', + size: 133, + item_collection: null, + }, + { + type: 'folder', + absolute_path: 'test/subfolder/', + name: 'subfolder', + modified_at: '19-Dec-02 16:43', + size: 0, + item_collection: ['test/subfolder/test-level-2.jpg'], + }, + { + type: 'file', + absolute_path: 'test/subfolder/test-level-2.jpg', + name: 'test-level-2.jpg', + modified_at: '19-Nov-08 15:08', + size: 57379, + item_collection: null, + }, + { + type: 'file', + absolute_path: 'level-0.txt', + name: 'level-0.txt', + modified_at: '19-Nov-04 16:11', + size: 1, + item_collection: null, + }, + { + type: 'file', + absolute_path: 'test/pdf-level-1.pdf', + name: 'pdf-level-1.pdf', + modified_at: '19-Nov-04 16:11', + size: 233, + item_collection: null, + }, + ]; + + beforeAll(() => { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 1000 }); + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 100 }); + }); + + afterAll(() => { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight); + Object.defineProperty(HTMLElement.prototype, 'offsetWidth', originalOffsetWidth); }); describe('render()', () => { test('should render correct components', () => { - const component = getComponent({ filename, itemCollection: data }); + render(); - expect(component.find('.bp-ArchiveExplorer').length).toBe(1); - expect(component.find('SearchBar').length).toBe(1); - expect(component.find('Breadcrumbs').length).toBe(1); - expect(component.find('Internationalize').length).toBe(1); + expect(screen.getByTestId('bp-archive-explorer')).toBeInTheDocument(); + expect(screen.getByRole('searchbox')).toBeInTheDocument(); + expect(screen.getByLabelText('Breadcrumb')).toBeInTheDocument(); }); }); - describe('handleItemClick()', () => { - test('should set state when handleItemClick() is called', () => { - const component = getComponent({ filename, itemCollection: data }); - - component.instance().handleItemClick({ fullPath: 'test/subfolder/' }); - - expect(component.state().fullPath).toBe('test/subfolder/'); - expect(component.state().view).toBe(VIEWS.VIEW_FOLDER); - expect(component.state().searchQuery).toBe(''); - }); - }); + describe('Subfolders', () => { + test('should render nested content on folder item click', async () => { + const user = userEvent.setup(); + render(); - describe('handleBreadcrumbClick()', () => { - test('should set state when handleBreadcrumbClick() is called', () => { - const component = getComponent({ filename, itemCollection: data }); + await user.click(screen.getByText('test')); - component.instance().handleBreadcrumbClick('test/subfolder/'); + expect(screen.getByText('subfolder')).toBeInTheDocument(); + expect(screen.getByText('pdf-level-1.pdf')).toBeInTheDocument(); - expect(component.state().fullPath).toBe('test/subfolder/'); - }); - }); + await user.click(screen.getByText('subfolder')); - describe('getRowData()', () => { - test('should return correct row data', () => { - const component = getComponent({ filename, itemCollection: data }); - - const rowData = component.instance().getRowData(data)({ index: 0 }); - - const { KEY_NAME, KEY_MODIFIED_AT, KEY_SIZE } = TABLE_COLUMNS; - const { absolute_path: fullPath, modified_at: modifiedAt, name, size, type, ...rest } = data[0]; - - expect(rowData).toEqual({ - [KEY_NAME]: { - fullPath, - isExternal: false, - name, - type, - dataAttributes: { - 'data-resin-target': type, - }, - }, - [KEY_MODIFIED_AT]: modifiedAt, - [KEY_SIZE]: type === 'folder' ? null : size, - ...rest, - }); + expect(screen.getByText('test-level-2.jpg')).toBeInTheDocument(); }); }); - describe('getItemList()', () => { - test('should return correct item list', () => { - const component = getComponent({ filename, itemCollection: data }); + describe('Breadcrumbs', () => { + test('should render breadcrumbs correctly', async () => { + const user = userEvent.setup(); + render(); - let itemList = component.instance().getItemList(data, ROOT_FOLDER); + await user.click(screen.getByText('test')); - expect(itemList).toEqual([data[0], data[4]]); + expect(within(screen.getByLabelText('Breadcrumb')).getByText('test.zip')).toBeInTheDocument(); + expect(within(screen.getByLabelText('Breadcrumb')).getByText('test')).toBeInTheDocument(); - itemList = component.instance().getItemList(data, 'test/'); + await user.click(within(screen.getByLabelText('Breadcrumb')).getByText('test.zip')); - expect(itemList).toEqual([data[1], data[2], data[5]]); + expect(within(screen.getByLabelText('Breadcrumb')).getByText('test.zip')).toBeInTheDocument(); + expect(within(screen.queryByLabelText('Breadcrumb')).queryByText('test')).not.toBeInTheDocument(); }); }); - describe('handleSearch()', () => { - test('should set correct state when search query longer than 1 letter', () => { - const component = getComponent({ filename, itemCollection: data }); - - component.instance().handleSearch('test'); - expect(component.state().searchQuery).toEqual('test'); - expect(component.state().view).toEqual(VIEWS.VIEW_SEARCH); - - component.instance().handleSearch(''); - expect(component.state().searchQuery).toEqual(''); - expect(component.state().view).toEqual(VIEWS.VIEW_FOLDER); - - component.instance().handleSearch(' '); - expect(component.state().searchQuery).toEqual(' '); - expect(component.state().view).toEqual(VIEWS.VIEW_FOLDER); - - component.instance().handleSearch('a'); - expect(component.state().searchQuery).toEqual('a'); - expect(component.state().view).toEqual(VIEWS.VIEW_FOLDER); + describe('Items list', () => { + test('should render items correctly', () => { + render(); + + expect(screen.getByRole('gridcell', { name: 'test' })).toHaveAttribute('aria-colindex', '1'); + expect( + screen.getAllByRole('gridcell', { name: '{time, date, medium} at {time, time, short}' }).at(0), + ).toHaveAttribute('aria-colindex', '2'); + expect(screen.getByRole('gridcell', { name: '--' })).toHaveAttribute('aria-colindex', '3'); + + expect(screen.getByRole('gridcell', { name: 'level-0.txt' })).toHaveAttribute('aria-colindex', '1'); + expect( + screen.getAllByRole('gridcell', { name: '{time, date, medium} at {time, time, short}' }).at(0), + ).toHaveAttribute('aria-colindex', '2'); + expect(screen.getByRole('gridcell', { name: '1 Bytes' })).toHaveAttribute('aria-colindex', '3'); }); }); - describe('getSearchResult()', () => { - test('should return correct item list', () => { - const component = getComponent({ filename, itemCollection: data }); + describe('Search', () => { + test('should correctly search for query longer than 1 letter', async () => { + const user = userEvent.setup(); + render(); - const itemList = component.instance().getSearchResult(data, 'level-1'); - const fuzzyList = component.instance().getSearchResult(data, 'leel1'); + await user.type(screen.getByRole('searchbox'), 'test'); - expect(itemList).toEqual([data[1], data[3], data[5]]); - expect(fuzzyList).toEqual([data[1], data[3], data[5]]); - }); - }); - - describe('handleSort()', () => { - test('should set the sort direction and type', () => { - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); + expect(screen.getByText('Search Results')).toBeInTheDocument(); + expect(screen.getByRole('gridcell', { name: 'test' })).toBeInTheDocument(); + expect(screen.getByRole('gridcell', { name: 'test-level-2.jpg' })).toBeInTheDocument(); + expect(screen.queryByRole('gridcell', { name: 'level-0.txt' })).not.toBeInTheDocument(); - instance.handleSort({ sortBy: 'name', sortDirection: 'DESC' }); + await user.clear(screen.getByRole('searchbox')); - expect(component.state().sortBy).toEqual('name'); - expect(component.state().sortDirection).toEqual('DESC'); + expect(screen.queryByText('Search Results')).not.toBeInTheDocument(); + expect(screen.getByRole('gridcell', { name: 'test' })).toBeInTheDocument(); + expect(screen.getByRole('gridcell', { name: 'level-0.txt' })).toBeInTheDocument(); + expect(screen.queryByRole('gridcell', { name: 'test-level-2.jpg' })).not.toBeInTheDocument(); }); }); - describe('sortItemList()', () => { - test('should sort itemList by size and be in DESC order', () => { - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); - const itemList = instance.getItemList(data, 'test/'); - - instance.handleSort({ sortBy: 'size', sortDirection: 'DESC' }); - const sortedList = instance.sortItemList(itemList); - - // folders come before files - expect(sortedList[0]).toEqual(data[2]); - expect(sortedList[1]).toEqual(data[5]); - }); - - test('should sort itemList by name and be in ASC order', () => { - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); - const itemList = instance.getItemList(data, 'test/'); - - instance.handleSort({ sortBy: 'name', sortDirection: 'ASC' }); - const sortedList = instance.sortItemList(itemList); + describe('Sorting', () => { + test('should set the sort direction and type', () => { + render(); - // folders come before files - expect(sortedList[0]).toEqual(data[2]); - expect(sortedList[1]).toEqual(data[1]); + expect(screen.getByTitle('Name').querySelector('svg')).toHaveClass('bdl-icon-sort-chevron'); + expect(screen.queryByTitle('Modified').querySelector('svg')).not.toBeInTheDocument(); + expect(screen.queryByTitle('Size').querySelector('svg')).not.toBeInTheDocument(); }); - test('should sort itemList by name and be in DESC order', () => { - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); - const itemList = instance.getItemList(data, 'test/'); + test('should sort itemList by clicking on the Size column header', async () => { + const user = userEvent.setup(); + render(); - instance.handleSort({ sortBy: 'name', sortDirection: 'DESC' }); - const sortedList = instance.sortItemList(itemList); + await user.click(within(screen.getByTitle('test')).getByRole('button')); + await user.click(screen.getByRole('columnheader', { name: 'Size' })); - // folders come before files - expect(sortedList[0]).toEqual(data[2]); - expect(sortedList[1]).toEqual(data[5]); - }); + expect(screen.getAllByRole('row').at(2).textContent).toContain('subfolder'); // folders come before files + expect(screen.getAllByRole('row').at(3).textContent).toContain('csv-level-1.csv'); + expect(screen.getAllByRole('row').at(4).textContent).toContain('pdf-level-1.pdf'); - test('should not sort itemList', () => { - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); - const itemList = instance.getItemList(data, 'test/'); + await user.click(screen.getByRole('columnheader', { name: 'Size' })); - const sortedList = instance.sortItemList(itemList); - - expect(sortedList).toEqual(itemList); + expect(screen.getAllByRole('row').at(2).textContent).toContain('subfolder'); + expect(screen.getAllByRole('row').at(3).textContent).toContain('pdf-level-1.pdf'); + expect(screen.getAllByRole('row').at(4).textContent).toContain('csv-level-1.csv'); }); - test('should sort item list with string values and null', () => { - data[1].modified_at = null; - data[2].modified_at = null; - - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); - const itemList = instance.getItemList(data, 'test/'); + test('should sort itemList by clicking on the Name column header', async () => { + const user = userEvent.setup(); + render(); - instance.handleSort({ sortBy: 'modified_at' }); + await user.click(within(screen.queryByTitle('test')).getByRole('button')); + await user.click(screen.getByRole('columnheader', { name: 'Name' })); - const sortedList = instance.sortItemList(itemList); + expect(screen.getAllByRole('row').at(2).textContent).toContain('subfolder'); // folders come before files + expect(screen.getAllByRole('row').at(3).textContent).toContain('pdf-level-1.pdf'); + expect(screen.getAllByRole('row').at(4).textContent).toContain('csv-level-1.csv'); - // folders come before files - expect(sortedList[0]).toEqual(data[2]); - // item with not-null value comes first - expect(sortedList[1]).toEqual(data[5]); - }); + await user.click(screen.getByRole('columnheader', { name: 'Name' })); - test('should sort items with number values and null', () => { - const mockData = [ - { size: 1 }, - { size: 1 }, - { size: 130 }, - { size: null }, - { size: 100 }, - { size: undefined }, - ]; - const sortedMockData = [ - { size: null }, - { size: undefined }, - { size: 130 }, - { size: 100 }, - { size: 1 }, - { size: 1 }, - ]; - const component = getComponent({ filename, itemCollection: data }); - const instance = component.instance(); - - instance.handleSort({ sortBy: 'size' }); - - expect(instance.sortItemList(mockData).toString()).toEqual(sortedMockData.toString()); - - instance.handleSort({ sortBy: 'size', sortDirection: 'ASC' }); - - expect(instance.sortItemList(mockData).toString()).toEqual(sortedMockData.reverse().toString()); + expect(screen.getAllByRole('row').at(2).textContent).toContain('subfolder'); + expect(screen.getAllByRole('row').at(3).textContent).toContain('csv-level-1.csv'); + expect(screen.getAllByRole('row').at(4).textContent).toContain('pdf-level-1.pdf'); }); }); }); diff --git a/src/lib/viewers/archive/__tests__/Breadcrumbs-test-react.js b/src/lib/viewers/archive/__tests__/Breadcrumbs-test-react.js index c6ed4669f..9f0a04984 100644 --- a/src/lib/viewers/archive/__tests__/Breadcrumbs-test-react.js +++ b/src/lib/viewers/archive/__tests__/Breadcrumbs-test-react.js @@ -1,71 +1,38 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import Breadcrumbs from '../Breadcrumbs'; -import { ROOT_FOLDER, VIEWS } from '../constants'; +import { VIEWS } from '../constants'; describe('lib/viewers/archive/Breadcrumbs', () => { - const getComponent = props => shallow(); - - let filename; - let fullPath; - let onClick; - let view; - - beforeEach(() => { - filename = 'test.zip'; - fullPath = 'test/subfolder/'; - onClick = jest.fn(); - view = VIEWS.VIEW_FOLDER; - }); - describe('render()', () => { test('should render correct components', () => { - const component = getComponent({ filename, fullPath, onClick, view }); - - expect(component.find('.bp-Breadcrumbs').length).toBe(1); - expect(component.find('Breadcrumb').length).toBe(1); - expect(component.find('PlainButton').length).toBe(3); + render( + , + ); + + expect(screen.getByLabelText('Breadcrumb')).toBeInTheDocument(); + expect(screen.getAllByRole('button')).toHaveLength(3); + expect(screen.getAllByRole('button').at(0).textContent).toContain('test.zip'); + expect(screen.getAllByRole('button').at(1).textContent).toContain('test'); + expect(screen.getAllByRole('button').at(2).textContent).toContain('subfolder'); }); test('should render search result if view is search', () => { - const component = getComponent({ filename, fullPath, onClick, view: VIEWS.VIEW_SEARCH }); - - expect(component.find('span').text()).toBe(__('search_results')); - }); - }); - - describe('getPathItems()', () => { - test('should return root folder', () => { - const component = getComponent({ filename, fullPath, onClick, view }); - const pathItems = component.instance().getPathItems(ROOT_FOLDER); - - expect(pathItems).toEqual([ - { - name: filename, - path: ROOT_FOLDER, - }, - ]); - }); - - test('should return correct path items', () => { - const component = getComponent({ filename, fullPath, onClick, view }); - - const pathItems = component.instance().getPathItems(fullPath); - - expect(pathItems).toEqual([ - { - name: filename, - path: ROOT_FOLDER, - }, - { - name: 'test', - path: 'test/', - }, - { - name: 'subfolder', - path: 'test/subfolder/', - }, - ]); + render( + , + ); + + expect(screen.getByText('Search Results')).toBeInTheDocument(); }); }); }); diff --git a/src/lib/viewers/box3d/image360/__tests__/Image360Controls-test.tsx b/src/lib/viewers/box3d/image360/__tests__/Image360Controls-test.tsx index 9fd06bce3..aeadb0d15 100644 --- a/src/lib/viewers/box3d/image360/__tests__/Image360Controls-test.tsx +++ b/src/lib/viewers/box3d/image360/__tests__/Image360Controls-test.tsx @@ -1,34 +1,22 @@ import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import FullscreenToggle from '../../../controls/fullscreen'; -import Image360Controls, { Props } from '../Image360Controls'; -import VrToggleControl from '../../../controls/box3d/VrToggleControl'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import Image360Controls from '../Image360Controls'; describe('lib/viewers/box3d/image360/Image360Controls', () => { - const getDefaults = (): Props => ({ - isVrShown: false, - onFullscreenToggle: jest.fn(), - onVrToggle: jest.fn(), - }); - - const getWrapper = (props: Partial): ShallowWrapper => - shallow(); - describe('render()', () => { - test('should return a valid wrapper', () => { + test('should return a valid wrapper', async () => { + const user = userEvent.setup(); const onFullscreenToggle = jest.fn(); - const onVrToggle = jest.fn(); + render( + , + ); + + expect(screen.queryByTitle('Toggle VR display')).not.toBeInTheDocument(); - const wrapper = getWrapper({ - onFullscreenToggle, - onVrToggle, - }); + await user.click(screen.getByTitle('Enter fullscreen')); - expect(wrapper.find(VrToggleControl).props()).toMatchObject({ - isVrShown: false, - onVrToggle, - }); - expect(wrapper.find(FullscreenToggle).prop('onFullscreenToggle')).toEqual(onFullscreenToggle); + expect(onFullscreenToggle).toHaveBeenCalled(); }); }); }); diff --git a/src/lib/viewers/box3d/model3d/__tests__/Model3DControlsNew-test.tsx b/src/lib/viewers/box3d/model3d/__tests__/Model3DControlsNew-test.tsx index fbb367d99..4ad9f6422 100644 --- a/src/lib/viewers/box3d/model3d/__tests__/Model3DControlsNew-test.tsx +++ b/src/lib/viewers/box3d/model3d/__tests__/Model3DControlsNew-test.tsx @@ -1,103 +1,73 @@ import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import AnimationControls from '../../../controls/box3d/AnimationControls'; -import FullscreenToggle from '../../../controls/fullscreen'; -import Model3DControls, { Props } from '../Model3DControlsNew'; -import Model3DSettings, { CameraProjection, RenderMode } from '../../../controls/box3d/Model3DSettings'; -import ResetControl from '../../../controls/box3d/ResetControl'; -import VrToggleControl from '../../../controls/box3d/VrToggleControl'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import Model3DControls from '../Model3DControlsNew'; +import { CameraProjection, RenderMode } from '../../../controls/box3d/Model3DSettings'; describe('lib/viewers/box3d/model3d/Model3DControlsNew', () => { - const getDefaults = (): Props => ({ - animationClips: [], - cameraProjection: CameraProjection.PERSPECTIVE, - currentAnimationClipId: '123', - isPlaying: false, - isVrShown: false, - onAnimationClipSelect: jest.fn(), - onCameraProjectionChange: jest.fn(), - onFullscreenToggle: jest.fn(), - onPlayPause: jest.fn(), - onRenderModeChange: jest.fn(), - onRotateOnAxisChange: jest.fn(), - onReset: jest.fn(), - onSettingsClose: jest.fn(), - onSettingsOpen: jest.fn(), - onShowGridToggle: jest.fn(), - onShowSkeletonsToggle: jest.fn(), - onShowWireframesToggle: jest.fn(), - onVrToggle: jest.fn(), - renderMode: RenderMode.LIT, - showGrid: true, - showSkeletons: false, - showWireframes: false, - }); - - const getWrapper = (props: Partial): ShallowWrapper => - shallow(); - describe('render()', () => { - test('should return a valid wrapper', () => { + test('should render valid output', async () => { + const user = userEvent.setup(); + const onReset = jest.fn(); + const onPlayPause = jest.fn(); const onAnimationClipSelect = jest.fn(); - const onCameraProjectionChange = jest.fn(); const onFullscreenToggle = jest.fn(); - const onPlayPause = jest.fn(); - const onRenderModeChange = jest.fn(); - const onRotateOnAxisChange = jest.fn(); - const onReset = jest.fn(); - const onSettingsClose = jest.fn(); - const onSettingsOpen = jest.fn(); - const onShowGridToggle = jest.fn(); - const onShowSkeletonsToggle = jest.fn(); - const onShowWireframesToggle = jest.fn(); const onVrToggle = jest.fn(); + const onSettingsOpen = jest.fn(); + render( + , + ); + + await user.click(screen.getByTitle('Reset')); + expect(onReset).toHaveBeenCalledTimes(1); + + await user.click(screen.getByTitle('Play')); + expect(onPlayPause).toHaveBeenCalledTimes(1); + + await user.click(screen.getByTitle('Animation clips')); + const animationClip = screen.getByRole('menuitemradio'); + expect(animationClip).toHaveTextContent('00:00:02 foo'); + + await user.click(animationClip); + expect(onAnimationClipSelect).toHaveBeenCalledTimes(1); + + await user.click(screen.getByTitle('Enter fullscreen')); + expect(onFullscreenToggle).toHaveBeenCalledTimes(1); - const wrapper = getWrapper({ - onAnimationClipSelect, - onCameraProjectionChange, - onFullscreenToggle, - onPlayPause, - onRenderModeChange, - onRotateOnAxisChange, - onReset, - onSettingsClose, - onSettingsOpen, - onShowGridToggle, - onShowSkeletonsToggle, - onShowWireframesToggle, - onVrToggle, - }); + await user.click(screen.getByTitle('Toggle VR display')); + expect(onVrToggle).toHaveBeenCalledTimes(1); - expect(wrapper.find(ResetControl).props()).toMatchObject({ - onReset, - }); - expect(wrapper.find(AnimationControls).props()).toMatchObject({ - animationClips: [], - currentAnimationClipId: '123', - isPlaying: false, - onAnimationClipSelect, - onPlayPause, - }); - expect(wrapper.find(VrToggleControl).props()).toMatchObject({ - isVrShown: false, - onVrToggle, - }); - expect(wrapper.find(Model3DSettings).props()).toMatchObject({ - cameraProjection: CameraProjection.PERSPECTIVE, - onCameraProjectionChange, - onClose: onSettingsClose, - onOpen: onSettingsOpen, - onRenderModeChange, - onRotateOnAxisChange, - onShowGridToggle, - onShowSkeletonsToggle, - onShowWireframesToggle, - renderMode: RenderMode.LIT, - showGrid: true, - showSkeletons: false, - showWireframes: false, - }); - expect(wrapper.find(FullscreenToggle).prop('onFullscreenToggle')).toEqual(onFullscreenToggle); + await user.click(screen.getByTitle('Settings')); + expect(onSettingsOpen).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/lib/viewers/controls/annotations/AnnotationsControls.tsx b/src/lib/viewers/controls/annotations/AnnotationsControls.tsx index 60bc6f8cb..ebcd4311d 100644 --- a/src/lib/viewers/controls/annotations/AnnotationsControls.tsx +++ b/src/lib/viewers/controls/annotations/AnnotationsControls.tsx @@ -85,12 +85,12 @@ export default function AnnotationsControls({ const isDrawingActive = annotationMode === AnnotationMode.DRAWING; return ( -
+
{ - const getWrapper = (props = {}): ShallowWrapper => - shallow( - - Test - , - ); - describe('event handlers', () => { - test('should call the onClick callback with the given mode', () => { - const mode = AnnotationMode.HIGHLIGHT; + test('should call the onClick callback with the given mode', async () => { + const user = userEvent.setup(); const onClick = jest.fn(); - const wrapper = getWrapper({ mode, onClick }); + render( + + Test + , + ); - wrapper.simulate('click'); + await user.click(screen.getByRole('button', { name: 'Test' })); - expect(onClick).toBeCalledWith(mode); + expect(onClick).toHaveBeenCalledWith(AnnotationMode.HIGHLIGHT); }); }); describe('render', () => { - test('should return nothing if not enabled', () => { - const wrapper = getWrapper({ isEnabled: false }); - expect(wrapper.isEmptyRender()).toBe(true); + test('should render nothing if not enabled', () => { + render( + + Test + , + ); + + expect(screen.queryByRole('button', { name: 'Test' })).not.toBeInTheDocument(); }); - test('should return a valid wrapper', () => { - const wrapper = getWrapper(); + test('should render a valid output', () => { + render( + + Test + , + ); - expect(wrapper.hasClass('bp-AnnotationsButton')).toBe(true); - expect(wrapper.hasClass('bp-is-active')).toBe(false); // Default - expect(wrapper.text()).toBe('Test'); + expect(screen.getByRole('button', { name: 'Test' })).toBeInTheDocument(); }); }); }); diff --git a/src/lib/viewers/controls/annotations/__tests__/AnnotationsControls-test.tsx b/src/lib/viewers/controls/annotations/__tests__/AnnotationsControls-test.tsx index 1317ce931..d30367c27 100644 --- a/src/lib/viewers/controls/annotations/__tests__/AnnotationsControls-test.tsx +++ b/src/lib/viewers/controls/annotations/__tests__/AnnotationsControls-test.tsx @@ -1,18 +1,11 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; import { bdlBoxBlue } from 'box-ui-elements/es/styles/variables'; -import { ReactWrapper, mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import AnnotationsControls from '../AnnotationsControls'; import { AnnotationMode } from '../../../../types'; describe('AnnotationsControls', () => { - const getWrapper = (props = {}): ReactWrapper => { - const parentEl = document.createElement('div'); - document.body.appendChild(parentEl); - return mount(, { attachTo: parentEl }); - }; - const getElement = (props = {}): ReactWrapper => getWrapper(props).childAt(0); - beforeEach(() => { jest.spyOn(document, 'addEventListener'); jest.spyOn(document, 'removeEventListener'); @@ -34,23 +27,21 @@ describe('AnnotationsControls', () => { }); test('should add and remove its event handlers on mount and unmount', () => { - getWrapper({ - annotationMode: AnnotationMode.REGION, - hasHighlight: true, - hasRegion: true, - }); - expect(document.addEventListener).toBeCalledWith('keydown', expect.any(Function)); + render(); + + expect(document.addEventListener).toHaveBeenCalledWith('keydown', expect.any(Function)); unmount(); - expect(document.removeEventListener).toBeCalledWith('keydown', expect.any(Function)); + expect(document.removeEventListener).toHaveBeenCalledWith('keydown', expect.any(Function)); }); test('should not add a handler if the annotation mode is set to none', () => { - getWrapper({ hasHighlight: true, hasRegion: true }); - expect(document.addEventListener).not.toBeCalledWith('keydown', expect.any(Function)); + render(); + expect(document.addEventListener).not.toHaveBeenCalledWith('keydown', expect.any(Function)); unmount(); - expect(document.removeEventListener).toBeCalledWith('keydown', expect.any(Function)); + + expect(document.removeEventListener).toHaveBeenCalledWith('keydown', expect.any(Function)); }); }); @@ -63,53 +54,56 @@ describe('AnnotationsControls', () => { ${AnnotationMode.NONE} | ${'bp-AnnotationsControls-highlightBtn'} | ${AnnotationMode.HIGHLIGHT} ${AnnotationMode.NONE} | ${'bp-AnnotationsControls-drawBtn'} | ${AnnotationMode.DRAWING} ${AnnotationMode.DRAWING} | ${'bp-AnnotationsControls-drawBtn'} | ${AnnotationMode.NONE} - `('in $current mode returns $result when $selector is clicked', ({ current, result, selector }) => { + `('in $current mode returns $result when $selector is clicked', async ({ current, result, selector }) => { + const user = userEvent.setup(); const onClick = jest.fn(); - const element = getElement({ - annotationMode: current, - hasDrawing: true, - hasHighlight: true, - hasRegion: true, - onAnnotationModeClick: onClick, - }); - - element.find(`button[data-testid="${selector}"]`).simulate('click'); - - expect(onClick).toBeCalledWith({ mode: result }); + render( + , + ); + + await user.click(screen.getByTestId(selector)); + + expect(onClick).toHaveBeenCalledWith({ mode: result }); }); - test('should invoke the escape callback if the escape key is pressed while in a mode', () => { + test('should invoke the escape callback if the escape key is pressed while in a mode', async () => { + const user = userEvent.setup(); const onEscape = jest.fn(); - - getWrapper({ - annotationMode: AnnotationMode.REGION, - hasHighlight: true, - hasRegion: true, - onAnnotationModeEscape: onEscape, - }); - - act(() => { - document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); - }); - - expect(onEscape).toBeCalled(); + render( + , + ); + + await user.keyboard('{Escape}'); + + expect(onEscape).toHaveBeenCalled(); }); - test('should not invoke the escape callback if any key other than escape is pressed', () => { + test('should not invoke the escape callback if any key other than escape is pressed', async () => { + const user = userEvent.setup(); const onEscape = jest.fn(); - - getWrapper({ - annotationMode: AnnotationMode.REGION, - hasHighlight: true, - hasRegion: true, - onAnnotationModeEscape: onEscape, - }); - - act(() => { - document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); - }); - - expect(onEscape).not.toBeCalled(); + render( + , + ); + + await user.keyboard('{Enter}'); + + expect(onEscape).not.toHaveBeenCalled(); }); test.each` @@ -119,34 +113,28 @@ describe('AnnotationsControls', () => { ${AnnotationMode.DRAWING} | ${'bp-AnnotationsControls-drawBtn'} `( 'while in $current mode, should focus on $current button when exit button is clicked', - ({ current, selector }) => { - const wrapper = getWrapper({ - annotationMode: current, - hasDrawing: true, - hasHighlight: true, - hasRegion: true, - }); - - wrapper.find('button[data-testid="bp-AnnotationsControls-exitBtn"]').simulate('click'); - - expect(wrapper.find(`button[data-testid="${selector}"]`).getDOMNode() === document.activeElement).toBe( - true, - ); + async ({ current, selector }) => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByTestId('bp-annotations-controls-exit-btn')); + + expect(screen.getByTestId(selector) === document.activeElement).toBe(true); }, ); }); describe('render', () => { - test('should return nothing if no mode is enabled', () => { - const wrapper = getWrapper(); + test('should render nothing if no mode is enabled', () => { + render(); - expect(wrapper.isEmptyRender()).toBe(true); + expect(screen.queryByTestId('bp-annotations-controls')).not.toBeInTheDocument(); }); - test('should return a valid wrapper', () => { - const element = getElement({ hasHighlight: true, hasRegion: true }); + test('should render a valid output', () => { + render(); - expect(element.hasClass('bp-AnnotationsControls')).toBe(true); + expect(screen.getByTestId('bp-annotations-controls')).toBeInTheDocument(); }); test.each` @@ -154,9 +142,11 @@ describe('AnnotationsControls', () => { ${bdlBoxBlue} | ${AnnotationMode.DRAWING} ${'#fff'} | ${AnnotationMode.NONE} `('should return an IconDrawing24 with the fill set as $fill if annotationMode is $mode', ({ fill, mode }) => { - const wrapper = getWrapper({ annotationMode: mode, hasDrawing: true }); + render(); + + const icon = screen.getByTitle('Markup').querySelector('svg'); - expect(wrapper.find('IconDrawing24').props().fill).toBe(fill); + expect(icon?.children[0]).toHaveAttribute('fill', fill); }); }); }); diff --git a/src/lib/viewers/controls/annotations/__tests__/AnnotationsTargetedTooltip-test.tsx b/src/lib/viewers/controls/annotations/__tests__/AnnotationsTargetedTooltip-test.tsx index 87e1c8d9d..78d412f77 100644 --- a/src/lib/viewers/controls/annotations/__tests__/AnnotationsTargetedTooltip-test.tsx +++ b/src/lib/viewers/controls/annotations/__tests__/AnnotationsTargetedTooltip-test.tsx @@ -1,15 +1,9 @@ import React from 'react'; -import { ReactWrapper, mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; + import AnnotationsTargetedTooltip from '../AnnotationsTargetedTooltip'; describe('AnnotationsTargetedTooltip', () => { - const getWrapper = (props = {}): ReactWrapper => - mount( - -
Child
-
, - ); - describe('render', () => { beforeEach(() => { jest.spyOn(React, 'useContext').mockImplementation(() => ({ @@ -25,24 +19,24 @@ describe('AnnotationsTargetedTooltip', () => { })); }); - test('should return tooltip when is enabled', () => { - const wrapper = getWrapper({ - isEnabled: true, - }); + test('should render tooltip when enabled', () => { + render( + +
Child
+
, + ); - expect(wrapper.children().text()).not.toBe('Child'); - expect(wrapper.children().prop('shouldTarget')).toBe(true); - expect(wrapper.children().prop('theme')).toBe('callout'); - expect(wrapper.children().prop('useTargetingApi')().canShow).toBe(true); + expect(screen.getByRole('tooltip')).toBeInTheDocument(); }); - test('should return children when tooltip is disabled', () => { - const wrapper = getWrapper({ - isEnabled: false, - }); + test('should not render tooltip when disabled', () => { + render( + +
Child
+
, + ); - expect(wrapper.children().text()).toBe('Child'); - expect(wrapper.children().prop('shouldTarget')).toBe(false); + expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); }); }); }); diff --git a/src/lib/viewers/controls/annotations/__tests__/DrawingControls-test.tsx b/src/lib/viewers/controls/annotations/__tests__/DrawingControls-test.tsx index 359eb55e6..58cf9d62c 100644 --- a/src/lib/viewers/controls/annotations/__tests__/DrawingControls-test.tsx +++ b/src/lib/viewers/controls/annotations/__tests__/DrawingControls-test.tsx @@ -1,22 +1,20 @@ import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import DrawingControls from '../DrawingControls'; import { AnnotationMode } from '../../../../types'; describe('DrawingControls', () => { - const getWrapper = (props = {}): ShallowWrapper => - shallow(); - describe('render', () => { test('should return nothing if annotationMode is not DRAWING', () => { - const wrapper = getWrapper(); - expect(wrapper.isEmptyRender()).toBe(true); + render(); + + expect(screen.queryByTestId('bp-color-picker-control')).not.toBeInTheDocument(); }); test('should return ColorPickerControl if annotationMode is DRAWING', () => { - const wrapper = getWrapper({ annotationMode: AnnotationMode.DRAWING }); + render(); - expect(wrapper.exists('ColorPickerControl')).toBe(true); + expect(screen.getByTestId('bp-color-picker-control')).toBeInTheDocument(); }); }); }); diff --git a/src/lib/viewers/controls/box3d/RotateAxisControl.tsx b/src/lib/viewers/controls/box3d/RotateAxisControl.tsx index b43ddaec7..1b0e540e4 100644 --- a/src/lib/viewers/controls/box3d/RotateAxisControl.tsx +++ b/src/lib/viewers/controls/box3d/RotateAxisControl.tsx @@ -21,19 +21,19 @@ export default function RotateAxisControl({ axis, className, onRotateOnAxisChang const handleClickRight = (): void => onRotateOnAxisChange({ [axis]: ROTATION_STEP }); return ( -
+
; + const CustomToggle = forwardRef((): JSX.Element => ); describe('render', () => { test('should return a valid wrapper', () => { - const wrapper = getWrapper(); + render(); - expect(wrapper.exists(Settings)).toBe(true); - expect(wrapper.exists(SettingsMenu)).toBe(true); - expect(wrapper.exists(SettingsMenuItem)).toBe(true); + expect(screen.queryByTitle('Settings')).toBeInTheDocument(); + expect(screen.getByTestId('bp-settings-toggle-icon')).toBeInTheDocument(); + expect(screen.getByTestId('bp-settings-flyout')).toBeInTheDocument(); }); - test('should pass optional props to Settings', () => { - const badge =
custom
; - const wrapper = getWrapper({ badge, toggle: CustomToggle }); - const settings = wrapper.find(Settings); + test('should render custom badge', () => { + render( + custom
} + onAutoplayChange={jest.fn()} + onRateChange={jest.fn()} + rate="1.0" + toggle={CustomToggle} + />, + ); + + expect(screen.getByRole('button', { name: 'custom button' })); + }); + + test.each` + value | displayValue + ${true} | ${'Enabled'} + ${false} | ${'Disabled'} + `( + 'should display $displayValue as selected for the $menuItem value $value', + async ({ displayValue, value }) => { + const user = userEvent.setup(); + render( + , + ); + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitemradio', { name: displayValue })).toHaveAttribute( + 'aria-checked', + 'true', + ); + }, + ); + + test.each` + value | displayValue + ${'1.0'} | ${__('media_speed_normal')} + ${'2.0'} | ${'2.0'} + `('should display $displayValue for the $menuItem value $value', async ({ displayValue, value }) => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); - expect(settings.prop('badge')).toEqual(badge); - expect(settings.prop('toggle')).toEqual(CustomToggle); + expect(screen.getByRole('menuitemradio', { name: displayValue })).toHaveAttribute('aria-checked', 'true'); }); test.each` - menuItem | value | displayValue - ${'autoplay'} | ${true} | ${__('media_autoplay_enabled')} - ${'autoplay'} | ${false} | ${__('media_autoplay_disabled')} - ${'rate'} | ${'1.0'} | ${__('media_speed_normal')} - ${'rate'} | ${'2.0'} | ${'2.0'} - ${'quality'} | ${'auto'} | ${__('media_quality_auto')} - ${'quality'} | ${'sd'} | ${'480p'} - ${'quality'} | ${'hd'} | ${'1080p'} - `('should display $displayValue for the $menuItem value $value', ({ displayValue, menuItem, value }) => { - const wrapper = getWrapper({ [menuItem]: value }); - - expect(wrapper.find({ target: menuItem }).prop('value')).toBe(displayValue); + value | displayValue + ${'auto'} | ${__('media_quality_auto')} + ${'sd'} | ${'480p'} + ${'hd'} | ${'1080p'} + `('should display $displayValue for the $menuItem value $value', async ({ displayValue, value }) => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitem', { name: `Quality ${displayValue}` })).toBeInTheDocument(); }); describe('audiotracks menu', () => { - test('should render the audio menu if > 1 audio tracks are present', () => { - const wrapper = getWrapper({ audioTracks }); - expect(wrapper.exists({ target: 'audio' })).toBe(true); - expect(wrapper.exists(MediaSettingsMenuAudioTracks)).toBe(true); + test('should render the audio menu if > 1 audio tracks are present', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getAllByRole('menuitem', { name: 'Audio' })).toHaveLength(2); // Menu + Back button }); - test('should display the generated track label for the selected audio track', () => { - const wrapper = getWrapper({ audioTrack: 1, audioTracks }); - const expectedLabel = `${__('track')} 2 (English)`; - expect(wrapper.find({ target: 'audio' }).prop('value')).toBe(expectedLabel); + test('should display the generated track label for the selected audio track', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitemradio', { name: 'Track 1' })).toBeInTheDocument(); + expect(screen.getByRole('menuitemradio', { name: 'Track 2 (English)' })).toBeInTheDocument(); }); }); describe('quality menu', () => { - test('should render the quality menu if the quality is provided', () => { - const wrapper = getWrapper({ quality: 'auto', onQualityChange: jest.fn() }); - expect(wrapper.exists(MediaSettingsMenuQuality)).toBe(true); + test('should render the quality menu if the quality is provided', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitem', { name: 'Quality Auto' })).toBeInTheDocument(); + expect(screen.getByRole('menuitem', { name: 'Quality Auto' })).toHaveAttribute('aria-disabled', 'true'); }); - test('should render with isDisabled based on isHDSupported prop', () => { - const wrapper = getWrapper({ isHDSupported: false, quality: 'auto', onQualityChange: jest.fn() }); - expect(wrapper.find({ target: 'quality' }).prop('isDisabled')).toBe(true); + test('should render the quality menu disabled based on isHDSupported prop', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitem', { name: 'Quality Auto' })).toBeInTheDocument(); + expect(screen.getByRole('menuitem', { name: 'Quality Auto' })).toHaveAttribute('aria-disabled', 'true'); }); }); describe('subtitles menu', () => { - test('should render the subtitles menu item if only 1 subtitles track is present', () => { - const onSubtitleChange = jest.fn(); - const singleSubtitle = [{ id: 0, displayLanguage: 'English' }]; - const wrapper = getWrapper({ onSubtitleChange, subtitles: singleSubtitle }); - expect(wrapper.exists({ target: 'subtitles' })).toBe(true); - expect(wrapper.exists(MediaSettingsMenuSubtitles)).toBe(true); + test('should render the subtitles menu item if only 1 subtitles track is present', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitem', { name: 'Subtitles/CC Off' })).toBeInTheDocument(); + expect(screen.getByRole('menuitemradio', { name: 'English' })).toBeInTheDocument(); + expect(screen.queryByRole('menuitemradio', { name: 'Spanish' })).not.toBeInTheDocument(); }); - test('should render the subtitle menu if > 1 subtitles are present', () => { - const onSubtitleChange = jest.fn(); - const wrapper = getWrapper({ onSubtitleChange, subtitles }); - expect(wrapper.exists({ target: 'subtitles' })).toBe(true); - expect(wrapper.exists(MediaSettingsMenuSubtitles)).toBe(true); + test('should render the subtitle menu if > 1 subtitles are present', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitem', { name: 'Subtitles/CC Off' })).toBeInTheDocument(); + expect(screen.getByRole('menuitemradio', { name: 'English' })).toBeInTheDocument(); + expect(screen.getByRole('menuitemradio', { name: 'Spanish' })).toBeInTheDocument(); + expect(screen.getByRole('menuitemradio', { name: 'English' })).toHaveAttribute('aria-checked', 'false'); + expect(screen.getByRole('menuitemradio', { name: 'Spanish' })).toHaveAttribute('aria-checked', 'false'); }); - test('should display the subtitle language for the selected audio track', () => { - const onSubtitleChange = jest.fn(); - const wrapper = getWrapper({ onSubtitleChange, subtitle: 1, subtitles }); - expect(wrapper.find({ target: 'subtitles' }).prop('value')).toBe('Spanish'); + test('should display the subtitle language for the selected audio track', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByRole('menuitemradio', { name: 'English' })).toHaveAttribute('aria-checked', 'false'); + expect(screen.getByRole('menuitemradio', { name: 'Spanish' })).toHaveAttribute('aria-checked', 'true'); }); }); }); diff --git a/src/lib/viewers/controls/media/__tests__/TimeControls-test.tsx b/src/lib/viewers/controls/media/__tests__/TimeControls-test.tsx index 314b889c3..c546be60c 100644 --- a/src/lib/viewers/controls/media/__tests__/TimeControls-test.tsx +++ b/src/lib/viewers/controls/media/__tests__/TimeControls-test.tsx @@ -1,99 +1,91 @@ import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import Filmstrip from '../Filmstrip'; -import SliderControl from '../../slider'; +import { fireEvent, render, screen } from '@testing-library/react'; import TimeControls from '../TimeControls'; describe('TimeControls', () => { - const getBuffer = (end = 1000, start = 0): TimeRanges => ({ - length: end - start, - end: jest.fn().mockReturnValue(end), - start: jest.fn().mockReturnValue(start), + const mouseEventOptions = { + bubbles: true, + pageX: 250, // The center of the slider + pageY: 478, + }; + + beforeEach(() => { + jest.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation( + (): DOMRect => ({ + toJSON: jest.fn(), + bottom: 1000, + height: 930, + left: 0, // Values are reduced by the left offset of the slider + right: 500, + top: 60, + width: 500, // Values are calculated based on width of the slider + x: 0, + y: 60, + }), + ); }); - const getWrapper = (props = {}): ShallowWrapper => - shallow(); - describe('event handlers', () => { - test('should update the slider hover state on mousemove', () => { - const wrapper = getWrapper({ filmstripInterval: 1 }); + test('should update the slider on mousemove', () => { + render( + , + ); - wrapper.find(SliderControl).simulate('move', 100, 1000, 10000); // Time, position, max position + const slider = screen.getByLabelText('Media Slider'); - expect(wrapper.find(Filmstrip).props()).toMatchObject({ - position: 1000, - positionMax: 10000, - time: 100, - }); - }); - - test('should update the slider hover state on mouseover and mouseout', () => { - const wrapper = getWrapper({ filmstripInterval: 1 }); + expect(screen.getByTestId('bp-Filmstrip-time')).toHaveTextContent('0:00'); - wrapper.find(SliderControl).simulate('mouseover'); - expect(wrapper.find(Filmstrip).prop('isShown')).toBe(true); + fireEvent(slider, new MouseEventExtended('mouseenter', mouseEventOptions)); + fireEvent(slider, new MouseEventExtended('mouseover', mouseEventOptions)); + fireEvent(slider, new MouseEventExtended('mousedown', mouseEventOptions)); + fireEvent(slider, new MouseEventExtended('mousemove', mouseEventOptions)); + fireEvent(slider, new MouseEventExtended('mouseup', mouseEventOptions)); - wrapper.find(SliderControl).simulate('mouseout'); - expect(wrapper.find(Filmstrip).prop('isShown')).toBe(false); - }); + expect(screen.getByTestId('bp-Filmstrip-time')).toHaveTextContent('1:23:20'); // 1:23:20 = 5000 seconds, half of 10000 }); - describe('render', () => { - test('should return a valid wrapper', () => { - const wrapper = getWrapper(); + test('should update the slider hover state on mouseover and mouseout', () => { + render( + , + ); - expect(wrapper.hasClass('bp-TimeControls')).toBe(true); - expect(wrapper.find(SliderControl).props()).toMatchObject({ - max: 10000, - min: 0, - step: 5, - value: 0, - }); - }); + const slider = screen.getByLabelText('Media Slider'); - test('should not default to zero for invalid currentTime value', () => { - const wrapper = getWrapper({ currentTime: NaN }); - expect(wrapper.find(SliderControl).prop('value')).toBe(0); - }); + fireEvent(slider, new MouseEventExtended('mouseenter', mouseEventOptions)); + fireEvent(slider, new MouseEventExtended('mouseover', mouseEventOptions)); - test('should default to zero for invalid durationTime value', () => { - const wrapper = getWrapper({ durationTime: NaN }); - expect(wrapper.find(SliderControl).prop('max')).toBe(0); - }); + expect(screen.getByTestId('bp-time-controls').firstChild).toHaveClass('bp-is-shown'); - test.each` - currentTime | track - ${0} | ${'linear-gradient(to right, #0061d5 0%, #fff 0%, #fff 10%, #6f6f6f 10%, #6f6f6f 100%)'} - ${50} | ${'linear-gradient(to right, #0061d5 0.5%, #fff 0.5%, #fff 10%, #6f6f6f 10%, #6f6f6f 100%)'} - ${1000} | ${'linear-gradient(to right, #0061d5 10%, #fff 10%, #fff 10%, #6f6f6f 10%, #6f6f6f 100%)'} - ${2500} | ${'linear-gradient(to right, #0061d5 25%, #fff 25%, #fff 10%, #6f6f6f 10%, #6f6f6f 100%)'} - ${10000} | ${'linear-gradient(to right, #0061d5 100%, #fff 100%, #fff 10%, #6f6f6f 10%, #6f6f6f 100%)'} - `('should render the correct track for currentTime $currentTime', ({ currentTime, track }) => { - const buffer = getBuffer(1000, 0); // 10% buffered - const wrapper = getWrapper({ bufferedRange: buffer, currentTime }); - expect(wrapper.find(SliderControl).prop('track')).toEqual(track); - }); - - test('should render the filmstrip with the correct props', () => { - const wrapper = getWrapper({ - aspectRatio: 1.5, - filmstripInterval: 2, - filmstripUrl: 'https://app.box.com', - }); + fireEvent(slider, new MouseEventExtended('mouseout', mouseEventOptions)); - expect(wrapper.find(Filmstrip).props()).toMatchObject({ - aspectRatio: 1.5, - imageUrl: 'https://app.box.com', - interval: 2, - }); - }); - - test('should not render the filmstrip if the interval is missing', () => { - const wrapper = getWrapper({ - aspectRatio: 1.5, - imageUrl: 'https://app.box.com', - }); + expect(screen.getByTestId('bp-time-controls').firstChild).not.toHaveClass('bp-is-shown'); + }); - expect(wrapper.exists(Filmstrip)).toBe(false); + test('should render the filmstrip with the correct props', () => { + render( + , + ); + + expect(screen.getByTestId('bp-Filmstrip-frame')).toHaveStyle({ + backgroundImage: 'url(https://app.box.com)', }); }); }); diff --git a/src/lib/viewers/controls/page/__tests__/PageControlsForm-test.tsx b/src/lib/viewers/controls/page/__tests__/PageControlsForm-test.tsx index 2c8bd970e..37b0d6e32 100644 --- a/src/lib/viewers/controls/page/__tests__/PageControlsForm-test.tsx +++ b/src/lib/viewers/controls/page/__tests__/PageControlsForm-test.tsx @@ -1,115 +1,89 @@ import React from 'react'; -import noop from 'lodash/noop'; -import { shallow, ShallowWrapper } from 'enzyme'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; import PageControlsForm, { ENTER, ESCAPE } from '../PageControlsForm'; describe('PageControlsForm', () => { - const getWrapper = (props = {}): ShallowWrapper => - shallow(); - const getFormButton = (wrapper: ShallowWrapper): ShallowWrapper => - wrapper.find('[data-testid="bp-PageControlsForm-button"]'); - const getFormInput = (wrapper: ShallowWrapper): ShallowWrapper => - wrapper.find('[data-testid="bp-PageControlsForm-input"]'); - describe('event handlers', () => { test.each` newPageNumber | onPageSubmitCallCount ${2} | ${1} ${''} | ${0} - `('should call onPageSubmit when input blurs', ({ newPageNumber, onPageSubmitCallCount }) => { + `('should call onPageSubmit when input blurs', async ({ newPageNumber, onPageSubmitCallCount }) => { + const user = userEvent.setup(); const onPageSubmit = jest.fn(); - const wrapper = getWrapper({ onPageSubmit, pageCount: 3, pageNumber: 1 }); + render(); + + expect(screen.getByTitle('Click to enter page number')).toBe(screen.getByRole('button', { name: '1 / 3' })); + + await user.click(screen.getByRole('button', { name: '1 / 3' })); - getFormButton(wrapper).simulate('click'); - getFormInput(wrapper).simulate('change', { target: { value: newPageNumber } }); - getFormInput(wrapper).simulate('blur'); + expect(screen.getByTitle('Click to enter page number')).toBe(screen.getByRole('spinbutton')); - expect(onPageSubmit).toBeCalledTimes(onPageSubmitCallCount); + fireEvent.change(screen.getByRole('spinbutton'), { target: { value: newPageNumber } }); + fireEvent.blur(screen.getByRole('spinbutton')); + + expect(onPageSubmit).toHaveBeenCalledTimes(onPageSubmitCallCount); }); test.each` newPageNumber | onPageSubmitCallCount ${2} | ${1} ${''} | ${0} - `('should handle when Enter key is pressed on input', ({ onPageSubmitCallCount, newPageNumber }) => { + `('should handle when Enter key is pressed on input', async ({ onPageSubmitCallCount, newPageNumber }) => { + const user = userEvent.setup(); const onPageSubmit = jest.fn(); - const preventDefault = jest.fn(); - const stopPropagation = jest.fn(); - const wrapper = getWrapper({ onPageSubmit, pageCount: 3, pageNumber: 1 }); - - getFormButton(wrapper).simulate('click'); - - expect(getFormButton(wrapper).exists()).toBe(false); - expect(getFormInput(wrapper).exists()).toBe(true); + render(); - getFormInput(wrapper).simulate('change', { - target: { value: newPageNumber }, - }); - getFormInput(wrapper).simulate('keydown', { - preventDefault, - stopPropagation, + await user.click(screen.getByRole('button', { name: '1 / 3' })); + fireEvent.change(screen.getByRole('spinbutton'), { target: { value: newPageNumber } }); + fireEvent.keyDown(screen.getByRole('spinbutton'), { key: ENTER, }); - expect(preventDefault).toHaveBeenCalled(); - expect(stopPropagation).toHaveBeenCalled(); - expect(onPageSubmit).toBeCalledTimes(onPageSubmitCallCount); - expect(getFormButton(wrapper).exists()).toBe(true); - expect(getFormInput(wrapper).exists()).toBe(false); + expect(onPageSubmit).toHaveBeenCalledTimes(onPageSubmitCallCount); + expect(screen.getByRole('button', { name: '1 / 3' })).toBeInTheDocument(); + expect(screen.queryByRole('spinbutton')).not.toBeInTheDocument(); }); - test('should handle Escape key when pressed on input', () => { - const preventDefault = jest.fn(); - const stopPropagation = jest.fn(); - const wrapper = getWrapper(); - - expect(getFormButton(wrapper).exists()).toBe(true); - expect(getFormInput(wrapper).exists()).toBe(false); - - getFormButton(wrapper).simulate('click'); + test('should handle Escape key when pressed on input', async () => { + const user = userEvent.setup(); + render(); - expect(getFormButton(wrapper).exists()).toBe(false); - expect(getFormInput(wrapper).exists()).toBe(true); - - getFormInput(wrapper).simulate('keydown', { - preventDefault, - stopPropagation, + await user.click(screen.getByRole('button', { name: '1 / 3' })); + fireEvent.keyDown(screen.getByRole('spinbutton'), { key: ESCAPE, }); - expect(preventDefault).toHaveBeenCalled(); - expect(stopPropagation).toHaveBeenCalled(); - expect(getFormButton(wrapper).exists()).toBe(true); - expect(getFormInput(wrapper).exists()).toBe(false); + expect(screen.getByRole('button', { name: '1 / 3' })).toBeInTheDocument(); + expect(screen.queryByRole('spinbutton')).not.toBeInTheDocument(); }); }); describe('render', () => { test('should render button with correct page number', () => { - const pageCount = 3; - const pageNumber = 2; - - const wrapper = getWrapper({ pageCount, pageNumber }); + render(); - expect(getFormButton(wrapper).text()).toEqual(`${pageNumber} / ${pageCount}`); + expect(screen.getByRole('button', { name: '2 / 3' })).toBeInTheDocument(); }); test('should render disabled button when pageCount is 1', () => { - const wrapper = getWrapper({ pageCount: 1, pageNumber: 1 }); + render(); - expect(getFormButton(wrapper).prop('disabled')).toBe(true); + expect(screen.getByRole('button', { name: '1 / 1' })).toHaveAttribute('disabled'); }); - test('should render input when button is clicked', () => { - const wrapper = getWrapper(); + test('should render input when button is clicked', async () => { + const user = userEvent.setup(); + render(); - expect(getFormButton(wrapper).exists()).toBe(true); - expect(getFormInput(wrapper).exists()).toBe(false); + expect(screen.getByRole('button', { name: '1 / 3' })).toBeInTheDocument(); + expect(screen.queryByRole('spinbutton')).not.toBeInTheDocument(); - getFormButton(wrapper).simulate('click'); + await user.click(screen.getByRole('button', { name: '1 / 3' })); - expect(getFormButton(wrapper).exists()).toBe(false); - expect(getFormInput(wrapper).exists()).toBe(true); + expect(screen.queryByRole('button', { name: '1 / 3' })).not.toBeInTheDocument(); + expect(screen.getByRole('spinbutton')).toBeInTheDocument(); }); }); }); diff --git a/src/lib/viewers/controls/settings/Settings.tsx b/src/lib/viewers/controls/settings/Settings.tsx index 59e29e3d8..95485e8c3 100644 --- a/src/lib/viewers/controls/settings/Settings.tsx +++ b/src/lib/viewers/controls/settings/Settings.tsx @@ -82,6 +82,7 @@ export default function Settings({
(props: Props, ref: React. React.useImperativeHandle(ref, () => buttonElRef.current, []); return ( -
+
{label}
diff --git a/src/lib/viewers/controls/settings/SettingsToggle.tsx b/src/lib/viewers/controls/settings/SettingsToggle.tsx index 5f68a7a9a..cc263f8ea 100644 --- a/src/lib/viewers/controls/settings/SettingsToggle.tsx +++ b/src/lib/viewers/controls/settings/SettingsToggle.tsx @@ -13,7 +13,10 @@ export type Ref = HTMLButtonElement; function SettingsToggle({ badge, isOpen, onClick }: Props, ref: React.Ref): JSX.Element { return ( -
+
diff --git a/src/lib/viewers/controls/settings/__tests__/Settings-test.tsx b/src/lib/viewers/controls/settings/__tests__/Settings-test.tsx index c55a999a2..d95e5fb0b 100644 --- a/src/lib/viewers/controls/settings/__tests__/Settings-test.tsx +++ b/src/lib/viewers/controls/settings/__tests__/Settings-test.tsx @@ -1,192 +1,158 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mount, ReactWrapper } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; import Settings from '../Settings'; -import SettingsGearToggle from '../SettingsToggle'; -import SettingsFlyout from '../SettingsFlyout'; describe('Settings', () => { - const getHostNode = (): HTMLDivElement => { - return document.body.appendChild(document.createElement('div')); - }; - const getWrapper = (props = {}): ReactWrapper => mount(, { attachTo: getHostNode() }); - describe('event handlers', () => { - test('should update the flyout and toggle button isOpen prop when clicked', () => { - const wrapper = getWrapper(); + test('should update the flyout and toggle button isOpen prop when clicked', async () => { + const user = userEvent.setup(); + render(); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(false); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(false); + expect(screen.getByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); + expect(screen.getByTestId('bp-settings-toggle-container')).not.toHaveClass('bp-is-open'); - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(true); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(true); + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); + expect(screen.getByTestId('bp-settings-toggle-container')).toHaveClass('bp-is-open'); }); + // TODO: Investigate why "Enter" press causes container to unfocus test.each` key | isFocused ${'1'} | ${false} ${'A'} | ${false} + ${'Enter'} | ${false} ${'ArrowDown'} | ${true} ${'ArrowLeft'} | ${true} ${'ArrowRight'} | ${true} ${'ArrowUp'} | ${true} - ${'Enter'} | ${true} ${'Space'} | ${true} ${'Tab'} | ${true} - `('should update the focused state to $isFocused if $key is pressed', ({ key, isFocused }) => { - const wrapper = getWrapper(); + `( + 'should update the focused state to $isFocused if $key is pressed', + async ({ key, isFocused }: { key: string; isFocused: boolean }) => { + const user = userEvent.setup(); + render(); - expect(wrapper.childAt(0).hasClass('bp-is-focused')).toBe(false); + await user.click(screen.getByTitle('Settings')); - act(() => { - wrapper.simulate('keydown', { key }); - }); - wrapper.update(); + await user.keyboard(`{${key}}`); - expect(wrapper.childAt(0).hasClass('bp-is-focused')).toBe(isFocused); - }); + expect(screen.getByTestId('bp-settings').className.includes('bp-is-focused')).toBe(isFocused); + }, + ); - test('should reset the parent context when a click is detected outside the controls', () => { - const wrapper = getWrapper(); - const getEvent = (target: HTMLElement): MouseEvent => { - const event = new MouseEvent('click'); - Object.defineProperty(event, 'target', { enumerable: true, value: target }); - return event; - }; + test('should reset the parent context when a click is detected outside the controls', async () => { + const user = userEvent.setup(); + render(); - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(true); + await user.click(screen.getByTitle('Settings')); - act(() => { - document.dispatchEvent(getEvent(document.body)); // Click outside the controls - }); - wrapper.update(); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(false); + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); + + await user.click(document.body); // Click outside the controls + + expect(screen.getByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); // Re-open the controls - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(true); + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); + + await user.click(screen.getByTestId('bp-settings-flyout')); // Click within the controls - wrapper.find(SettingsFlyout).simulate('click'); // Click within the controls - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(true); + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); }); - test('should stop propagation on all keydown events to prevent triggering global event listeners', () => { - const wrapper = getWrapper(); - const event = { stopPropagation: jest.fn() }; // Key is not relevant + test('should stop propagation on all keydown events to prevent triggering global event listeners', async () => { + const user = userEvent.setup(); + const mockGlobalOnClick = jest.fn(); + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions + document.addEventListener('keydown', mockGlobalOnClick); + render(); - act(() => { - wrapper.simulate('keydown', event); - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(event.stopPropagation).toBeCalled(); + expect(mockGlobalOnClick).not.toHaveBeenCalled(); + document.removeEventListener('keydown', mockGlobalOnClick); }); }); describe('open/close callbacks', () => { - test('should call the onOpen callback when the flyout opens', () => { + test('should call the onOpen callback when the flyout opens', async () => { + const user = userEvent.setup(); const onClose = jest.fn(); const onOpen = jest.fn(); - const wrapper = getWrapper({ onClose, onOpen }); + render(); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(false); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(false); + expect(screen.getByTestId('bp-settings-toggle-container')).not.toHaveClass('bp-is-open'); + expect(screen.getByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(true); - expect(onOpen).toBeCalledTimes(1); - expect(onClose).not.toBeCalled(); + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); + expect(onOpen).toHaveBeenCalledTimes(1); + expect(onClose).not.toHaveBeenCalled(); }); - test('should call the onClose callback when the flyout closes', () => { + test('should call the onClose callback when the flyout closes', async () => { + const user = userEvent.setup(); const onClose = jest.fn(); const onOpen = jest.fn(); - const wrapper = getWrapper({ onClose, onOpen }); + render(); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(false); - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(false); + expect(screen.getByTestId('bp-settings-toggle-container')).not.toHaveClass('bp-is-open'); + expect(screen.getByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(true); - expect(onOpen).toBeCalledTimes(1); - expect(onClose).not.toBeCalled(); + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); + expect(onOpen).toHaveBeenCalledTimes(1); + expect(onClose).not.toHaveBeenCalled(); - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(false); - expect(onOpen).toBeCalledTimes(1); - expect(onClose).toBeCalledTimes(1); + expect(screen.getByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); + expect(onOpen).toHaveBeenCalledTimes(1); + expect(onClose).toHaveBeenCalledTimes(1); }); - test('should call the onClose callback when the flyout is closed by clicking outside', () => { + test('should call the onClose callback when the flyout is closed by clicking outside', async () => { + const user = userEvent.setup(); const onClose = jest.fn(); const onOpen = jest.fn(); - const wrapper = getWrapper({ onClose, onOpen }); - const getEvent = (target: HTMLElement): MouseEvent => { - const event = new MouseEvent('click'); - Object.defineProperty(event, 'target', { enumerable: true, value: target }); - return event; - }; - - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); // Open the controls - expect(wrapper.find(SettingsGearToggle).prop('isOpen')).toBe(true); + render(); - act(() => { - document.dispatchEvent(getEvent(document.body)); // Click outside the controls - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(onOpen).toBeCalledTimes(1); - expect(onClose).toBeCalledTimes(1); + expect(screen.getByTestId('bp-settings-toggle-container')).toHaveClass('bp-is-open'); + + await user.click(document.body); + + expect(onOpen).toHaveBeenCalledTimes(1); + expect(onClose).toHaveBeenCalledTimes(1); }); }); describe('render', () => { test('should return a valid wrapper', () => { - const wrapper = getWrapper(); + render(); - expect(wrapper.getDOMNode()).toHaveClass('bp-Settings'); - expect(wrapper.exists(SettingsFlyout)).toBe(true); - expect(wrapper.exists(SettingsGearToggle)).toBe(true); + expect(screen.getByTestId('bp-settings-flyout')).toBeInTheDocument(); + expect(screen.getByTestId('bp-settings-toggle-container')).toBeInTheDocument(); }); describe('flyout dimensions', () => { - test('should apply activeRect dimensions if present', () => { - const wrapper = getWrapper(); + test('should apply activeRect dimensions if present', async () => { + const user = userEvent.setup(); + render(); - act(() => { - wrapper.find(SettingsGearToggle).prop('onClick')(); - }); - wrapper.update(); + await user.click(screen.getByTitle('Settings')); - expect(wrapper.find(SettingsFlyout).prop('height')).toBe('auto'); - expect(wrapper.find(SettingsFlyout).prop('width')).toBe('auto'); + expect(screen.getByTestId('bp-settings-flyout')).toHaveStyle('height: auto'); + expect(screen.getByTestId('bp-settings-flyout')).toHaveStyle('width: auto'); }); }); @@ -196,40 +162,36 @@ describe('Settings', () => { { isOpen, ...rest }: { isOpen: boolean; onClick: () => void }, ref: React.Ref, ): JSX.Element { - return
} />); + + expect(screen.getByTestId('custom-badge')).toBeInTheDocument(); }); }); }); diff --git a/src/lib/viewers/controls/settings/__tests__/SettingsDropdown-test.tsx b/src/lib/viewers/controls/settings/__tests__/SettingsDropdown-test.tsx index 97bed2284..85ddbb607 100644 --- a/src/lib/viewers/controls/settings/__tests__/SettingsDropdown-test.tsx +++ b/src/lib/viewers/controls/settings/__tests__/SettingsDropdown-test.tsx @@ -1,9 +1,7 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mount, ReactWrapper } from 'enzyme'; -import SettingsDropdown, { Props, Ref as SettingsDropdownRef } from '../SettingsDropdown'; -import SettingsFlyout from '../SettingsFlyout'; -import SettingsList from '../SettingsList'; +import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; +import SettingsDropdown from '../SettingsDropdown'; describe('SettingsDropdown', () => { const listItems = [ @@ -11,212 +9,138 @@ describe('SettingsDropdown', () => { { label: 'second', value: 'second' }, { label: 'third', value: 'third' }, ]; - const getDefaults = (): Props => ({ - label: 'Dropdown Label', - listItems, - onSelect: jest.fn(), - value: 'first', - }); - const getHostNode = (): HTMLDivElement => { - return document.body.appendChild(document.createElement('div')); - }; - const getWrapper = (props = {}): ReactWrapper => - mount(, { - attachTo: getHostNode(), - }); describe('toggling', () => { - test('should toggle open the flyout and render the list', () => { - const wrapper = getWrapper(); - - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); - - const renderedItems = wrapper.find('.bp-SettingsDropdown-listitem'); - - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(true); - expect(renderedItems.length).toBe(listItems.length); + test('should toggle open the flyout and render the list', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByLabelText('Dropdown Label first')); + + expect(screen.getByTestId('bp-settings-flyout')).toHaveClass('bp-is-open'); + expect(screen.getAllByRole('option')).toHaveLength(listItems.length); + expect(screen.getAllByRole('option').at(0)?.textContent).toContain('first'); + expect(screen.getAllByRole('option').at(1)?.textContent).toContain('second'); + expect(screen.getAllByRole('option').at(2)?.textContent).toContain('third'); }); - test('should select the specified value', () => { - const wrapper = getWrapper({ value: 'second' }); + test('should select the specified value', async () => { + const user = userEvent.setup(); + render( + , + ); - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); + await user.click(screen.getByLabelText('Dropdown Label second')); - const renderedItems = wrapper.find('.bp-SettingsDropdown-listitem'); - - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(true); - expect(renderedItems.length).toBe(listItems.length); - expect(renderedItems.get(0).props['aria-selected']).toBe(false); - expect(renderedItems.get(1).props['aria-selected']).toBe(true); - expect(renderedItems.get(2).props['aria-selected']).toBe(false); + expect(screen.getByRole('option', { name: 'first' })).toHaveAttribute('aria-selected', 'false'); + expect(screen.getByRole('option', { name: 'second' })).toHaveAttribute('aria-selected', 'true'); + expect(screen.getByRole('option', { name: 'third' })).toHaveAttribute('aria-selected', 'false'); }); }); describe('events', () => { - test('should call onSelect with the list item value when clicked', () => { - const mockEvent = { stopPropagation: jest.fn() }; + test('should call onSelect with the list item value when clicked', async () => { + const user = userEvent.setup(); const onSelect = jest.fn(); - const wrapper = getWrapper({ onSelect }); - - // Open the flyout - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); - - act(() => { - wrapper - .find('.bp-SettingsDropdown-listitem') - .get(1) - .props.onClick(mockEvent); - }); - wrapper.update(); - - expect(onSelect).toBeCalledWith('second'); - expect(mockEvent.stopPropagation).toHaveBeenCalled(); - }); + render(); - test.each(['Space', 'Enter'])('should call onSelect with the list item value when keydown %s', key => { - const mockEvent = { key }; - const onSelect = jest.fn(); - const wrapper = getWrapper({ onSelect }); - - // Open the flyout - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); - - act(() => { - wrapper - .find('.bp-SettingsDropdown-listitem') - .get(1) - .props.onKeyDown(mockEvent); - }); - wrapper.update(); - - expect(onSelect).toBeCalledWith('second'); - }); + await user.click(screen.getByLabelText('Dropdown Label first')); + await user.click(screen.getByRole('option', { name: 'second' })); - test.each(['Escape', 'ArrowLeft'])('should not call onSelect with the list item value when keydown %s', key => { - const mockEvent = { key }; - const onSelect = jest.fn(); - const wrapper = getWrapper({ onSelect }); - - // Open the flyout - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); - - act(() => { - wrapper - .find('.bp-SettingsDropdown-listitem') - .get(1) - .props.onKeyDown(mockEvent); - }); - wrapper.update(); - - expect(onSelect).not.toBeCalled(); + expect(onSelect).toHaveBeenCalledWith('second'); }); - test('should close dropdown after making selection', () => { - const mockEvent = { stopPropagation: jest.fn() }; + test.each(['Space', 'Enter'])('should call onSelect with the list item value when keydown %s', async key => { + const user = userEvent.setup(); const onSelect = jest.fn(); - const wrapper = getWrapper({ onSelect }); - - // Open the flyout - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); - - act(() => { - wrapper - .find('.bp-SettingsDropdown-listitem') - .get(1) - .props.onClick(mockEvent); - }); - wrapper.update(); - - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(false); - }); + render(); - test('should close dropdown if Escape is pressed', () => { - const mockEvent = { key: 'Escape', stopPropagation: jest.fn() }; - const wrapper = getWrapper(); + await user.click(screen.getByLabelText('Dropdown Label first')); + fireEvent.keyDown( + within(screen.getByRole('listbox')) + .getAllByRole('option') + .at(1)!, + { key }, + ); - // Open the flyout - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); + expect(onSelect).toHaveBeenCalledWith('second'); + }); - act(() => { - wrapper.find(SettingsList).simulate('keydown', mockEvent); - }); - wrapper.update(); + test.each(['Escape', 'ArrowLeft'])( + 'should not call onSelect with the list item value when keydown %s', + async key => { + const user = userEvent.setup(); + const onSelect = jest.fn(); + render( + , + ); - expect(wrapper.find(SettingsFlyout).prop('isOpen')).toBe(false); - expect(mockEvent.stopPropagation).toHaveBeenCalled(); - }); + await user.click(screen.getByLabelText('Dropdown Label first')); + await user.keyboard(`{${key}}`); - test.each(['ArrowUp', 'ArrowDown', 'Escape'])('should prevent propagation of keydown events for %s', key => { - const mockEvent = { key, stopPropagation: jest.fn() }; - const wrapper = getWrapper(); + expect(onSelect).not.toHaveBeenCalled(); + }, + ); - // Open the flyout - act(() => { - wrapper.find('.bp-SettingsDropdown-button').simulate('click'); - }); - wrapper.update(); + test('should close dropdown after making selection', async () => { + const user = userEvent.setup(); + const onSelect = jest.fn(); + render(); - act(() => { - wrapper.find(SettingsList).simulate('keydown', mockEvent); - }); - wrapper.update(); + await user.click(screen.getByLabelText('Dropdown Label first')); + await user.click(within(screen.getByRole('listbox')).getByText('second')); - expect(mockEvent.stopPropagation).toHaveBeenCalled(); + expect(screen.queryByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); }); - }); - - describe('ref', () => { - const TestComponent = (): JSX.Element => { - const ref = React.useRef(null); - React.useEffect(() => { - if (ref.current) { - ref.current.focus(); - } - }, []); - return ; - }; + test('should close dropdown if Escape is pressed', async () => { + const user = userEvent.setup(); + render( + , + ); - test('should be able to focus on the dropdown button', () => { - const wrapper = mount(, { - attachTo: getHostNode(), - }); + await user.click(screen.getByLabelText('Dropdown Label first')); + await user.keyboard(`{Escape}`); - expect(wrapper.find('.bp-SettingsDropdown-button').getDOMNode()).toHaveFocus(); + expect(screen.queryByTestId('bp-settings-flyout')).not.toHaveClass('bp-is-open'); }); + + test.each(['ArrowUp', 'ArrowDown', 'Escape'])( + 'should prevent propagation of keydown events for %s', + async key => { + const user = userEvent.setup(); + const mockGlobalOnPress = jest.fn(); + document.addEventListener('keydown', mockGlobalOnPress); + render( + , + ); + + await user.click(screen.getByLabelText('Dropdown Label first')); + await user.keyboard(`{${key}}`); + + expect(mockGlobalOnPress).not.toHaveBeenCalled(); + document.removeEventListener('keydown', mockGlobalOnPress); + }, + ); }); - describe('render', () => { - test('should return a valid wrapper', () => { - const wrapper = getWrapper(); - const element = wrapper.getDOMNode(); + describe('focus', () => { + test('should be able to focus on the dropdown button', async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.tab(); - expect(element).toHaveClass('bp-SettingsDropdown'); - expect(wrapper.find('.bp-SettingsDropdown-label').text()).toBe('Dropdown Label'); - expect(wrapper.find('.bp-SettingsDropdown-button').text()).toBe('first'); - expect(wrapper.exists(SettingsFlyout)); - expect(wrapper.exists(SettingsList)); + expect(screen.getByRole('button', { name: /first/i })).toHaveFocus(); }); }); }); diff --git a/src/lib/viewers/controls/settings/__tests__/SettingsList-test.tsx b/src/lib/viewers/controls/settings/__tests__/SettingsList-test.tsx index 14ad2edf1..fe3e8efbf 100644 --- a/src/lib/viewers/controls/settings/__tests__/SettingsList-test.tsx +++ b/src/lib/viewers/controls/settings/__tests__/SettingsList-test.tsx @@ -1,104 +1,39 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mount, ReactWrapper } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import SettingsList from '../SettingsList'; describe('SettingsList', () => { - const getHostNode = (): HTMLDivElement => { - return document.body.appendChild(document.createElement('div')); - }; - const getWrapper = (props = {}): ReactWrapper => - mount( - -
-
-
- , - { - attachTo: getHostNode(), - }, - ); - describe('Event handling', () => { - test('should handle navigating the list and setting focus on the active item', () => { - const wrapper = getWrapper(); - - // index 0 has focus - expect(document.querySelector('[data-testid="test1"]')).toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).not.toHaveFocus(); - - // index 1 has focus - act(() => { - wrapper.find('.bp-SettingsList').simulate('keydown', { key: 'ArrowDown' }); - }); - wrapper.update(); - - expect(document.querySelector('[data-testid="test1"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).not.toHaveFocus(); - - // index 2 has focus - act(() => { - wrapper.find('.bp-SettingsList').simulate('keydown', { key: 'ArrowDown' }); - }); - wrapper.update(); - - expect(document.querySelector('[data-testid="test1"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).toHaveFocus(); + test('should handle navigating the list and setting focus on the active item', async () => { + const user = userEvent.setup(); + render( + +
+
+
+ , + ); - // index 2 should keep focus because we are at the end of the list - act(() => { - wrapper.find('.bp-SettingsList').simulate('keydown', { key: 'ArrowDown' }); - }); - wrapper.update(); + expect(screen.getByTestId('test1')).toHaveFocus(); - expect(document.querySelector('[data-testid="test1"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).toHaveFocus(); + await user.keyboard('{ArrowDown}'); + expect(screen.getByTestId('test2')).toHaveFocus(); - // index 1 has focus - act(() => { - wrapper.find('.bp-SettingsList').simulate('keydown', { key: 'ArrowUp' }); - }); - wrapper.update(); + await user.keyboard('{ArrowDown}'); + expect(screen.getByTestId('test3')).toHaveFocus(); - expect(document.querySelector('[data-testid="test1"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).not.toHaveFocus(); + await user.keyboard('{ArrowDown}'); + expect(screen.getByTestId('test3')).toHaveFocus(); - // index 0 has focus - act(() => { - wrapper.find('.bp-SettingsList').simulate('keydown', { key: 'ArrowUp' }); - }); - wrapper.update(); - - expect(document.querySelector('[data-testid="test1"]')).toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).not.toHaveFocus(); - - // index 0 should keep focus because we are at the top of the list - act(() => { - wrapper.find('.bp-SettingsList').simulate('keydown', { key: 'ArrowUp' }); - }); - wrapper.update(); - - expect(document.querySelector('[data-testid="test1"]')).toHaveFocus(); - expect(document.querySelector('[data-testid="test2"]')).not.toHaveFocus(); - expect(document.querySelector('[data-testid="test3"]')).not.toHaveFocus(); - }); - }); + await user.keyboard('{ArrowUp}'); + expect(screen.getByTestId('test2')).toHaveFocus(); - describe('render', () => { - test('should return a valid wrapper', () => { - const onKeyDown = jest.fn(); - const wrapper = getWrapper({ onKeyDown }); - const element = wrapper.getDOMNode(); + await user.keyboard('{ArrowUp}'); + expect(screen.getByTestId('test1')).toHaveFocus(); - expect(element).toHaveClass('bp-SettingsList'); - expect(element).toHaveAttribute('role', 'listbox'); - expect(element).toHaveAttribute('tabIndex', '0'); + await user.keyboard('{ArrowUp}'); + expect(screen.getByTestId('test1')).toHaveFocus(); }); }); }); diff --git a/src/lib/viewers/controls/slider/SliderControl.tsx b/src/lib/viewers/controls/slider/SliderControl.tsx index 7cd7b4667..1e5208d9d 100644 --- a/src/lib/viewers/controls/slider/SliderControl.tsx +++ b/src/lib/viewers/controls/slider/SliderControl.tsx @@ -136,12 +136,12 @@ export default function SliderControl({ >
{ - const getWrapper = (props = {}): ReactWrapper => - mount(); +const getTouchEventDefaults = () => ({ + clientX: 0, + clientY: 0, + force: 0, + identifier: 0, + pageX: 0, + pageY: 0, + radiusX: 0, + radiusY: 0, + rotationAngle: 0, + screenX: 0, + screenY: 0, + target: new EventTarget(), +}); +describe('SliderControl', () => { beforeEach(() => { jest.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation( (): DOMRect => ({ @@ -32,29 +44,55 @@ describe('SliderControl', () => { ${999} | ${99.9} ${1500} | ${100} `('should handle mousedown and update the value for pageX value $pageX', ({ pageX, result }) => { - const event = { button: 1, pageX }; const onUpdate = jest.fn(); - const wrapper = getWrapper({ onUpdate, value: 0 }); - - act(() => { - wrapper.simulate('mousedown', event); - }); - wrapper.update(); - - expect(onUpdate).toBeCalledWith(result); + render( + , + ); + + fireEvent( + screen.getByRole('slider')!, + new MouseEventExtended('mousedown', { + button: 1, + bubbles: true, + pageX, + }), + ); + + expect(onUpdate).toHaveBeenCalledWith(result); }); test('should handle mousemove by calling onMove with the value, position, and width', () => { - const event = { pageX: 100 }; const onMove = jest.fn(); - const wrapper = getWrapper({ onMove }); - - act(() => { - wrapper.simulate('mousemove', event); - }); - wrapper.update(); - - expect(onMove).toBeCalledWith(10, 100, 1000); // Value, position, width + render( + , + ); + + fireEvent( + screen.getByRole('slider')!, + new MouseEventExtended('mousemove', { + button: 1, + bubbles: true, + pageX: 100, + }), + ); + + expect(onMove).toHaveBeenCalledWith(10, 100, 1000); // Value, position, width }); test.each` @@ -62,18 +100,23 @@ describe('SliderControl', () => { ${0} | ${0} ${10} | ${9} ${100} | ${99} - `('should handle keydown and decrement the value $initial to $result ', ({ initial, result }) => { - const event = { key: 'ArrowLeft', stopPropagation: jest.fn() }; + `('should handle keydown and decrement the value $initial to $result ', async ({ initial, result }) => { const onUpdate = jest.fn(); - const wrapper = getWrapper({ onUpdate, value: initial }); - - act(() => { - wrapper.simulate('keydown', event); - }); - wrapper.update(); - - expect(event.stopPropagation).toBeCalled(); - expect(onUpdate).toBeCalledWith(result); + render( + , + ); + + fireEvent.keyDown(screen.getByRole('slider')!, { key: 'ArrowLeft' }); + + expect(onUpdate).toHaveBeenCalledWith(result); }); test.each` @@ -82,14 +125,22 @@ describe('SliderControl', () => { ${10} | ${11} ${100} | ${100} `('should handle keydown and increment the value $initial to $result ', ({ initial, result }) => { - const event = { key: 'ArrowRight', stopPropagation: jest.fn() }; const onUpdate = jest.fn(); - const wrapper = getWrapper({ onUpdate, value: initial }); - - wrapper.simulate('keydown', event); - - expect(event.stopPropagation).toBeCalled(); - expect(onUpdate).toBeCalledWith(result); + render( + , + ); + + fireEvent.keyDown(screen.getByRole('slider')!, { key: 'ArrowRight' }); + + expect(onUpdate).toHaveBeenCalledWith(result); }); }); @@ -100,95 +151,125 @@ describe('SliderControl', () => { }); test('should add document-level event handlers when scrubbing starts', () => { - const wrapper = getWrapper(); + render(); - act(() => { - wrapper.simulate('mousedown', { button: 1 }); - }); - wrapper.update(); + fireEvent.mouseDown(screen.getByRole('slider')!); - expect(document.addEventListener).toBeCalledWith('mousemove', expect.any(Function)); - expect(document.addEventListener).toBeCalledWith('mouseup', expect.any(Function)); - expect(document.addEventListener).toBeCalledWith('touchend', expect.any(Function)); - expect(document.addEventListener).toBeCalledWith('touchmove', expect.any(Function)); + expect(document.addEventListener).toHaveBeenCalledWith('mousemove', expect.any(Function)); + expect(document.addEventListener).toHaveBeenCalledWith('mouseup', expect.any(Function)); + expect(document.addEventListener).toHaveBeenCalledWith('touchend', expect.any(Function)); + expect(document.addEventListener).toHaveBeenCalledWith('touchmove', expect.any(Function)); }); - test('should remove document-level event handlers when scrubbing stops', () => { - const wrapper = getWrapper(); + test('should remove document-level event handlers when scrubbing stops', async () => { + const user = userEvent.setup(); + render(); - act(() => { - wrapper.simulate('mousedown', { button: 1 }); - }); - wrapper.update(); - - act(() => { - document.dispatchEvent(new Event('mouseup')); - }); - wrapper.update(); + fireEvent.mouseDown(screen.getByRole('slider')!); + await user.click(document.body); - expect(document.removeEventListener).toBeCalledWith('mousemove', expect.any(Function)); - expect(document.removeEventListener).toBeCalledWith('mouseup', expect.any(Function)); - expect(document.removeEventListener).toBeCalledWith('touchend', expect.any(Function)); - expect(document.removeEventListener).toBeCalledWith('touchmove', expect.any(Function)); + expect(document.removeEventListener).toHaveBeenCalledWith('mousemove', expect.any(Function)); + expect(document.removeEventListener).toHaveBeenCalledWith('mouseup', expect.any(Function)); + expect(document.removeEventListener).toHaveBeenCalledWith('touchend', expect.any(Function)); + expect(document.removeEventListener).toHaveBeenCalledWith('touchmove', expect.any(Function)); }); test('should handle document-level mousemove events and call onUpdate', () => { const onUpdate = jest.fn(); - const wrapper = getWrapper({ onUpdate }); - - act(() => { - wrapper.simulate('mousedown', { button: 1 }); - }); - wrapper.update(); - - act(() => { - const event = new MouseEvent('mousemove'); - Object.assign(event, { pageX: 100 }); - document.dispatchEvent(event); - }); - wrapper.update(); - - expect(onUpdate).toBeCalledWith(10); + render( + , + ); + + fireEvent.mouseDown(screen.getByRole('slider')!); + fireEvent( + document, + new MouseEventExtended('mousemove', { + bubbles: true, + pageX: 100, + }), + ); + + expect(onUpdate).toHaveBeenCalledWith(10); }); test('should handle document-level touchmove events and call onUpdate', () => { const onUpdate = jest.fn(); - const wrapper = getWrapper({ onUpdate }); - - act(() => { - wrapper.simulate('touchstart', { touches: [{ pageX: 0 }] }); - }); - wrapper.update(); - - act(() => { - const event = new MouseEvent('touchmove'); - Object.assign(event, { touches: [{ pageX: 100 }] }); - document.dispatchEvent(event); - }); - wrapper.update(); - - expect(onUpdate).toBeCalledWith(10); + render( + , + ); + + fireEvent( + screen.getByRole('slider')!, + new TouchEvent('touchstart', { + touches: [ + { + ...getTouchEventDefaults(), + pageX: 100, + }, + ], + bubbles: true, + }), + ); + fireEvent( + document, + new TouchEvent('touchmove', { + touches: [ + { + ...getTouchEventDefaults(), + pageX: 0, + }, + ], + }), + ); + + expect(onUpdate).toHaveBeenCalledWith(10); }); }); describe('render', () => { test('should return a valid wrapper', () => { - const wrapper = getWrapper(); + render(); - expect(wrapper.childAt(0).hasClass('bp-SliderControl')).toBe(true); - expect(wrapper.find('[data-testid="bp-SliderControl-thumb"]').prop('style')).toEqual({ + expect(screen.getByRole('slider')).toHaveClass('bp-SliderControl'); + expect(screen.getByTestId('bp-slider-control-thumb')).toHaveStyle({ left: '0%', }); }); test('should forward the track and value properties properly', () => { const track = 'linear-gradient(#fff %20, #000 100%'; - const wrapper = getWrapper({ track, value: 20 }); - - expect(wrapper.find('[data-testid="bp-SliderControl-thumb"]').prop('style')).toEqual({ + render( + , + ); + + expect(screen.getByTestId('bp-slider-control-thumb')).toHaveStyle({ left: '20%', }); - expect(wrapper.find('[data-testid="bp-SliderControl-track"]').prop('style')).toEqual({ + expect(screen.getByTestId('bp-slider-control-track')).toHaveStyle({ backgroundImage: track, }); }); diff --git a/src/lib/viewers/media/__tests__/DashControls-test.tsx b/src/lib/viewers/media/__tests__/DashControls-test.tsx index 0ec57dd54..496f9b777 100644 --- a/src/lib/viewers/media/__tests__/DashControls-test.tsx +++ b/src/lib/viewers/media/__tests__/DashControls-test.tsx @@ -1,44 +1,15 @@ import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import DashControls, { Props } from '../DashControls'; -import HDBadge from '../../controls/media/HDBadge'; -import MediaFullscreenToggle from '../../controls/media/MediaFullscreenToggle'; -import MediaSettings from '../../controls/media/MediaSettings'; -import PlayPauseToggle from '../../controls/media/PlayPauseToggle'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; +import DashControls from '../DashControls'; import subtitles from '../../controls/media/__mocks__/subtitles'; -import SubtitlesToggle from '../../controls/media/SubtitlesToggle'; -import TimeControls from '../../controls/media/TimeControls'; -import VolumeControls from '../../controls/media/VolumeControls'; import { Quality } from '../../controls/media/MediaSettingsMenuQuality'; import { SUBTITLES_OFF } from '../../../constants'; describe('DashControls', () => { describe('render', () => { - function CustomBadge(): JSX.Element { - return
custom
; - } - - const getDefaults = (): Props => ({ - audioTrack: 1, - audioTracks: [], - autoplay: false, - isPlaying: false, - isPlayingHD: false, - onAudioTrackChange: jest.fn(), - onAutoplayChange: jest.fn(), - onFullscreenToggle: jest.fn(), - onMuteChange: jest.fn(), - onPlayPause: jest.fn(), - onQualityChange: jest.fn(), - onRateChange: jest.fn(), - onTimeChange: jest.fn(), - onVolumeChange: jest.fn(), - quality: Quality.AUTO, - rate: '1.0', - }); - const getWrapper = (props = {}): ShallowWrapper => shallow(); - - test('should return a valid wrapper', () => { + test('should return a valid wrapper', async () => { + const user = userEvent.setup(); const onAudioTrackChange = jest.fn(); const onAutoplayChange = jest.fn(); const onFullscreenToggle = jest.fn(); @@ -48,67 +19,189 @@ describe('DashControls', () => { const onPlayPause = jest.fn(); const onTimeChange = jest.fn(); const onVolumeChange = jest.fn(); - const wrapper = getWrapper({ - onAudioTrackChange, - onAutoplayChange, - onFullscreenToggle, - onMuteChange, - onPlayPause, - onQualityChange, - onRateChange, - onTimeChange, - onVolumeChange, - }); - - expect(wrapper.hasClass('bp-DashControls')).toBe(true); - expect(wrapper.find(MediaFullscreenToggle).prop('onFullscreenToggle')).toEqual(onFullscreenToggle); - expect(wrapper.find(MediaSettings).props()).toMatchObject({ - audioTrack: 1, - audioTracks: [], - autoplay: false, - onAudioTrackChange, - onAutoplayChange, - onQualityChange, - onRateChange, - quality: 'auto', - rate: '1.0', - }); - expect(wrapper.find(PlayPauseToggle).prop('onPlayPause')).toEqual(onPlayPause); - expect(wrapper.find(TimeControls).prop('onTimeChange')).toEqual(onTimeChange); - expect(wrapper.find(VolumeControls).prop('onMuteChange')).toEqual(onMuteChange); - expect(wrapper.find(VolumeControls).prop('onVolumeChange')).toEqual(onVolumeChange); + render( + , + ); + + await user.click(screen.getByTitle('Enter fullscreen')); + expect(onFullscreenToggle).toHaveBeenCalledTimes(1); + + await user.click(screen.getByTitle('Play')); + expect(onPlayPause).toHaveBeenCalledTimes(1); + + fireEvent.mouseDown(screen.getByLabelText('Media Slider')); + expect(onTimeChange).toHaveBeenCalledTimes(1); + + await user.click(screen.getByTitle('Mute')); + expect(onMuteChange).toHaveBeenCalledTimes(1); + + fireEvent.mouseDown(screen.getByLabelText('Volume Slider')); + expect(onVolumeChange).toHaveBeenCalledTimes(1); }); - test.each([true, false])('should set isHDSupported prop on MediaSettings as %s', isHDSupported => { - const wrapper = getWrapper({ isHDSupported }); - expect(wrapper.find(MediaSettings).prop('isHDSupported')).toBe(isHDSupported); + test.each([true, false])('should set isHDSupported prop on MediaSettings as %s', async isHDSupported => { + const user = userEvent.setup(); + render( + , + ); + + await user.click(screen.getByTitle('Settings')); + + expect(screen.getByTestId('bp-media-settings-quality')).toHaveAttribute( + 'aria-disabled', + `${!isHDSupported}`, + ); }); test('should not pass along badge if not playing HD', () => { - const wrapper = getWrapper({ badge: }); - expect(wrapper.find(MediaSettings).prop('badge')).toBeUndefined(); + render( + custom
} + isPlaying={false} + isPlayingHD={false} + onAudioTrackChange={jest.fn()} + onAutoplayChange={jest.fn()} + onFullscreenToggle={jest.fn()} + onMuteChange={jest.fn()} + onPlayPause={jest.fn()} + onQualityChange={jest.fn()} + onRateChange={jest.fn()} + onTimeChange={jest.fn()} + onVolumeChange={jest.fn()} + quality={Quality.AUTO} + rate="1.0" + />, + ); + + expect(screen.queryByTestId('bp-media-controls-hd')).not.toBeInTheDocument(); }); test('should pass along badge if playing HD', () => { - const wrapper = getWrapper({ isPlayingHD: true }); - expect(wrapper.find(MediaSettings).prop('badge')).toEqual(); + render( + , + ); + + expect(screen.getByTestId('bp-media-controls-hd')).toBeInTheDocument(); }); - test('should render SubtitlesToggle if subtitles exist', () => { + test('should render SubtitlesToggle if subtitles exist', async () => { + const user = userEvent.setup(); const onSubtitlesToggle = jest.fn(); - const wrapper = getWrapper({ isAutoGeneratedSubtitles: true, onSubtitlesToggle, subtitles }); - expect(wrapper.find(SubtitlesToggle).props()).toMatchObject({ - isAutoGeneratedSubtitles: true, - isShowingSubtitles: true, - onSubtitlesToggle, - }); + render( + , + ); + + expect(screen.queryByTitle('Subtitles/Closed Captions')).not.toBeInTheDocument(); + expect(screen.getByTitle('Auto-Generated Captions')).toBeInTheDocument(); + expect(screen.getByTitle('Auto-Generated Captions')).toHaveAttribute('aria-pressed', 'true'); + + await user.click(screen.getByTitle('Auto-Generated Captions')); + + expect(onSubtitlesToggle).toHaveBeenCalledTimes(1); }); test('should render with isShowingSubtitles as false if subtitle is SUBTITLES_OFF', () => { - const wrapper = getWrapper({ subtitle: SUBTITLES_OFF, subtitles }); - expect(wrapper.find(SubtitlesToggle).props()).toMatchObject({ - isShowingSubtitles: false, - }); + render( + , + ); + + expect(screen.queryByTitle('Auto-Generated Captions')).not.toBeInTheDocument(); + expect(screen.getByTitle('Subtitles/Closed Captions')).toBeInTheDocument(); + expect(screen.getByTitle('Subtitles/Closed Captions')).toHaveAttribute('aria-pressed', 'false'); }); }); }); diff --git a/tsconfig.json b/tsconfig.json index fbd6700ba..78cc6671c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,7 @@ { + "compilerOptions": { + "types": ["node", "jest", "@testing-library/jest-dom"] + }, "extends": "@box/frontend/ts/tsconfig.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "exclude": ["node_modules", "dist"] diff --git a/yarn.lock b/yarn.lock index 8caf2153c..e1cf62b1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@adobe/css-tools@^4.0.1": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.2.0.tgz#e1a84fca468f4b337816fcb7f0964beb620ba855" - integrity sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA== +"@adobe/css-tools@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" + integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== "@ampproject/remapping@^2.2.0": version "2.2.1" @@ -20,13 +20,21 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== dependencies: "@babel/highlight" "^7.22.5" +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + "@babel/code-frame@^7.24.6": version "7.24.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.6.tgz#ab88da19344445c3d8889af2216606d3329f3ef2" @@ -344,10 +352,10 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz#28583c28b15f2a3339cfafafeaad42f9a0e828df" integrity sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q== -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-identifier@^7.22.5", "@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== "@babel/helper-validator-identifier@^7.24.6": version "7.24.6" @@ -391,14 +399,15 @@ "@babel/template" "^7.24.6" "@babel/types" "^7.24.6" -"@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== +"@babel/highlight@^7.22.5", "@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" js-tokens "^4.0.0" + picocolors "^1.0.0" "@babel/highlight@^7.24.6": version "7.24.6" @@ -1179,7 +1188,14 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.20.7", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.12.5": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2" + integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/runtime@^7.20.7", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== @@ -1690,12 +1706,12 @@ "@types/node" "*" jest-mock "^26.6.2" -"@jest/expect-utils@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" - integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - jest-get-type "^29.4.3" + jest-get-type "^29.6.3" "@jest/fake-timers@^26.6.2": version "26.6.2" @@ -1750,12 +1766,12 @@ optionalDependencies: node-notifier "^8.0.0" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" "@jest/source-map@^26.6.2": version "26.6.2" @@ -1819,12 +1835,12 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@jest/types@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" - integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1970,10 +1986,10 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@sinclair/typebox@^0.25.16": - version "0.25.24" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" - integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sindresorhus/is@^0.7.0": version "0.7.0" @@ -2008,26 +2024,71 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== -"@testing-library/jest-dom@^5.11.4": - version "5.16.5" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" - integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== +"@testing-library/dom@^10.4.0": + version "10.4.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== dependencies: - "@adobe/css-tools" "^4.0.1" - "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/dom@^8.0.0": + version "8.20.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" + integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz#50484da3f80fb222a853479f618a9ce5c47bfe54" + integrity sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA== + dependencies: + "@adobe/css-tools" "^4.4.0" aria-query "^5.0.0" chalk "^3.0.0" css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" - lodash "^4.17.15" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" redent "^3.0.0" +"@testing-library/react@^12.1.2": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" + integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.0.0" + "@types/react-dom" "<18.0.0" + +"@testing-library/user-event@^14.5.2": + version "14.5.2" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd" + integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.20.1" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" @@ -2062,9 +2123,9 @@ "@babel/types" "^7.20.7" "@types/cheerio@*": - version "0.22.31" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.31.tgz#b8538100653d6bb1b08a1e46dec75b4f2a5d5eb6" - integrity sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw== + version "0.22.35" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.35.tgz#0d16dc1f24d426231c181b9c31847f673867595f" + integrity sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA== dependencies: "@types/node" "*" @@ -2081,9 +2142,9 @@ integrity sha512-iacbaYN9IWWrGWTwlYLVOeUtN/e4cjN9Uh6v7Yo1Qa/vJzeSQeh10L/erBBSl53BTmbnQ07vsWp8mmNHGI4WbQ== "@types/enzyme@^3.10.8": - version "3.10.13" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.13.tgz#332c0ed59b01f7b1c398c532a1c21a5feefabeb1" - integrity sha512-FCtoUhmFsud0Yx9fmZk179GkdZ4U9B0GFte64/Md+W/agx0L5SxsIIbhLBOxIb9y2UfBA4WQnaG1Od/UsUQs9Q== + version "3.10.18" + resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.18.tgz#86010e7cb56cf1450dd391b8cc3a788f6a6fadef" + integrity sha512-RaO/TyyHZvXkpzinbMTZmd/S5biU4zxkvDsn22ujC29t9FMSzq8tnn8f2MxQ2P8GVhFRG5jTAL05DXKyTtpEQQ== dependencies: "@types/cheerio" "*" "@types/react" "^16" @@ -2111,29 +2172,34 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*": - version "29.5.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" - integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg== +"@types/jest@^29.5.13": + version "29.5.13" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" + integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -2164,9 +2230,11 @@ integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": - version "20.3.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.2.tgz#fa6a90f2600e052a03c18b8cb3fd83dd4e599898" - integrity sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw== + version "22.5.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8" + integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg== + dependencies: + undici-types "~6.19.2" "@types/node@^14.14.31": version "14.18.52" @@ -2198,6 +2266,13 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== +"@types/react-dom@<18.0.0": + version "17.0.25" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" + integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== + dependencies: + "@types/react" "^17" + "@types/react-dom@^17.0.1": version "17.0.20" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.20.tgz#e0c8901469d732b36d8473b40b679ad899da1b53" @@ -2215,12 +2290,21 @@ csstype "^3.0.2" "@types/react@^16": - version "16.14.43" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.43.tgz#bc6e7a0e99826809591d38ddf1193955de32c446" - integrity sha512-7zdjv7jvoLLQg1tTvpQsm+hyNUMT2mPlNV1+d0I8fbGhkJl82spopMyBlu4wb1dviZAxpGdk5eHu/muacknnfw== + version "16.14.61" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.61.tgz#ce498029a046d17908001e6e563f3febbaf41e7f" + integrity sha512-CK3zd17pDWAEMnN5TdzwQJQlto2dK/lb0WZsI74owWgQ8PR60WRk0sCeBxLWuSuuqqDZKqeUcxod/8yECqrP/g== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" + "@types/scheduler" "^0.16" + csstype "^3.0.2" + +"@types/react@^17.0.38": + version "17.0.82" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.82.tgz#eb84c38ee1023cd61be1b909fde083ac83fc163f" + integrity sha512-wTW8Lu/PARGPFE8tOZqCvprOKg5sen/2uS03yKn2xbCDFP9oLncm7vMDQ2+dEQXHVIXrOpW6u72xUXEXO0ypSw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "^0.16" csstype "^3.0.2" "@types/scheduler@*": @@ -2228,6 +2312,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== +"@types/scheduler@^0.16": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + "@types/semver@^7.3.12": version "7.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" @@ -2244,16 +2333,9 @@ integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/testing-library__jest-dom@^5.9.1": - version "5.14.6" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz#4887f6e1af11215428ab02777873bcede98a53b0" - integrity sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA== - dependencies: - "@types/jest" "*" + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/unist@*", "@types/unist@^2.0.0": version "2.0.6" @@ -2277,9 +2359,9 @@ "@types/vfile-message" "*" "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^15.0.0": version "15.0.15" @@ -2289,9 +2371,9 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" @@ -2812,7 +2894,14 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0, aria-query@^5.1.3: +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +aria-query@5.3.0, aria-query@^5.0.0, aria-query@^5.1.3: version "5.3.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== @@ -2842,6 +2931,14 @@ array-buffer-byte-length@^1.0.0: call-bind "^1.0.2" is-array-buffer "^3.0.1" +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -2901,27 +2998,39 @@ array-unique@^0.3.2: integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== array.prototype.filter@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.2.tgz#5f90ca6e3d01c31ea8db24c147665541db28bb4c" - integrity sha512-us+UrmGOilqttSOgoWZTpOvHu68vZT2YCjc/H4vhu56vzZpaDFBhB+Se2UwqWzMKbDv7Myq5M5pcZLAtUvTQdQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.4.tgz#bef83fde8a36a14d3de988c43563e0f5249962bf" + integrity sha512-r+mCJ7zXgXElgR4IRC+fkvNCeoaavWBs6EdCso5Tbcf+iEMKzBU/His60lt34WEZ9vlb8wDkZvQGcVI5GwkfoQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" es-array-method-boxes-properly "^1.0.0" + es-object-atoms "^1.0.0" is-string "^1.0.7" array.prototype.find@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.1.tgz#769b8182a0b535c3d76ac025abab98ba1e12467b" - integrity sha512-I2ri5Z9uMpMvnsNrHre9l3PaX+z9D0/z6F7Yt2u15q7wt0I62g5kX6xUKR1SJiefgG+u2/gJUmM8B47XRvQR6w== + version "2.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.3.tgz#675a233dbcd9b65ecf1fb3f915741aebc45461e6" + integrity sha512-fO/ORdOELvjbbeIfZfzrXFMhYHGofRGqd+am9zm3tZ4GlJINj/pA2eITyfd65Vg6+ZbHd/Cys7stpoRSWtQFdA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.2.3: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flat@^1.2.3, array.prototype.flat@^1.3.1: +array.prototype.flat@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== @@ -2963,6 +3072,20 @@ array.prototype.tosorted@^1.1.1: es-shim-unscopables "^1.0.0" get-intrinsic "^1.1.3" +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3078,6 +3201,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -3343,6 +3473,24 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" @@ -3394,7 +3542,14 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2, braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -3613,6 +3768,17 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + call-me-maybe@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" @@ -3738,7 +3904,7 @@ chai@^4.2.0: pathval "^1.1.1" type-detect "^4.0.5" -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3826,7 +3992,7 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" -cheerio@^1.0.0-rc.3: +cheerio@1.0.0-rc.12, cheerio@^1.0.0-rc.3: version "1.0.0-rc.12" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== @@ -3889,9 +4055,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -4228,7 +4394,7 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-type@~1.0.4: +content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -4509,6 +4675,11 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -4939,6 +5110,33 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + date-fns@^1.27.2: version "1.30.1" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" @@ -5029,6 +5227,30 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -5047,6 +5269,15 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -5055,6 +5286,15 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -5157,10 +5397,10 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.2: version "4.0.2" @@ -5229,11 +5469,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.6: +dom-accessibility-api@^0.5.9: version "0.5.16" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-helpers@^5.1.3: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -5427,6 +5672,11 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -5475,27 +5725,27 @@ entities@^4.2.0, entities@^4.4.0: integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== enzyme-adapter-utils@^1.14.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.1.tgz#f30db15dafc22e0ccd44f5acc8d93be29218cdcf" - integrity sha512-JZgMPF1QOI7IzBj24EZoDpaeG/p8Os7WeBZWTJydpsH7JRStc7jYbHE4CmNQaLqazaGFyLM8ALWA3IIZvxW3PQ== + version "1.14.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.2.tgz#1d012e6261accbe7d406db098bb4d8dfdce8c003" + integrity sha512-1ZC++RlsYRaiOWE5NRaF5OgsMt7F5rn/VuaJIgc7eW/fmgg8eS1/Ut7EugSPPi7VMdWMLcymRnMF+mJUJ4B8KA== dependencies: airbnb-prop-types "^2.16.0" - function.prototype.name "^1.1.5" - has "^1.0.3" - object.assign "^4.1.4" - object.fromentries "^2.0.5" + function.prototype.name "^1.1.6" + hasown "^2.0.0" + object.assign "^4.1.5" + object.fromentries "^2.0.7" prop-types "^15.8.1" - semver "^5.7.1" + semver "^6.3.1" enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.5.tgz#5528a897a6ad2bdc417c7221a7db682cd01711ba" - integrity sha512-i6cwm7hN630JXenxxJFBKzgLC3hMTafFQXflvzHgPmDhOBhxUWDe8AeRv1qp2/uWJ2Y8z5yLWMzmAfkTOiOCZg== + version "1.0.7" + resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.7.tgz#4e3aa678022387a68e6c47aff200587851885b5e" + integrity sha512-/um0GFqUXnpM9SvKtje+9Tjoz3f1fpBC3eXRFrNs8kpYn69JljciYP7KZTqM/YQbUY9KUjvKB4jo/q+L6WGGvg== dependencies: - has "^1.0.3" + hasown "^2.0.0" object-is "^1.1.5" -enzyme@^3.10.0: +enzyme@^3.11.0: version "3.11.0" resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28" integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw== @@ -5577,11 +5827,97 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21 unbox-primitive "^1.0.2" which-typed-array "^1.1.9" +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -5591,6 +5927,15 @@ es-set-tostringtag@^2.0.1: has "^1.0.3" has-tostringtag "^1.0.0" +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -5598,6 +5943,13 @@ es-shim-unscopables@^1.0.0: dependencies: has "^1.0.3" +es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -6118,17 +6470,54 @@ expect@^26.6.2: jest-regex-util "^26.0.0" expect@^29.0.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" - integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/expect-utils" "^29.5.0" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" -express@^4.16.3, express@^4.17.1: +express@^4.16.3: + version "4.21.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915" + integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.10" + proxy-addr "~2.0.7" + qs "6.13.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.2" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +express@^4.17.1: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -6367,10 +6756,10 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.0.1, fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -6387,6 +6776,19 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -6620,7 +7022,22 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.2, function.prototype.name@^1.1.5: +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.2, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== @@ -6660,6 +7077,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -6733,6 +7161,15 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -7112,11 +7549,23 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== +has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" @@ -7141,6 +7590,13 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -7196,6 +7652,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" @@ -7603,6 +8066,15 @@ internal-slot@^1.0.3, internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.4, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + interpret@^1.0.0, interpret@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -7690,7 +8162,7 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" -is-arguments@^1.0.4: +is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== @@ -7707,6 +8179,14 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: get-intrinsic "^1.2.0" is-typed-array "^1.1.10" +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -7808,7 +8288,14 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -7919,11 +8406,21 @@ is-installed-globally@~0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-map@^2.0.2, is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + is-number-object@^1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" @@ -8039,6 +8536,11 @@ is-retry-allowed@^1.1.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-set@^2.0.2, is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -8046,6 +8548,13 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" +is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -8098,6 +8607,13 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -8113,6 +8629,11 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -8120,6 +8641,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + is-whitespace-character@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" @@ -8314,15 +8843,15 @@ jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-diff@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" - integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" jest-docblock@^26.0.0: version "26.0.0" @@ -8372,10 +8901,10 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== jest-haste-map@^26.6.2: version "26.6.2" @@ -8440,15 +8969,15 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-matcher-utils@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" - integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^29.5.0" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" jest-message-util@^26.6.2: version "26.6.2" @@ -8465,18 +8994,18 @@ jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" -jest-message-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" - integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" @@ -8622,12 +9151,12 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -9282,6 +9811,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + make-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -9488,6 +10022,11 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -9527,7 +10066,7 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -9535,6 +10074,14 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -10022,12 +10569,17 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.3, object-inspect@^1.7.0, object-inspect@^1.9.0: +object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2, object-is@^1.1.5: +object-inspect@^1.13.1, object-inspect@^1.7.0: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-is@^1.0.1: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== @@ -10035,6 +10587,14 @@ object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2, object-is@^1.1.5: call-bind "^1.0.2" define-properties "^1.1.3" +object-is@^1.0.2, object-is@^1.1.2, object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -10047,7 +10607,17 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4: +object.assign@^4.1.0, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.assign@^4.1.2, object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -10057,7 +10627,16 @@ object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.6: +object.entries@^1.1.1: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.entries@^1.1.2, object.entries@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== @@ -10066,7 +10645,7 @@ object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" -object.fromentries@^2.0.5, object.fromentries@^2.0.6: +object.fromentries@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== @@ -10075,6 +10654,16 @@ object.fromentries@^2.0.5, object.fromentries@^2.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +object.fromentries@^2.0.7: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + object.getownpropertydescriptors@^2.1.0: version "2.1.6" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz#5e5c384dd209fa4efffead39e3a0512770ccc312" @@ -10101,7 +10690,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.6: +object.values@^1.1.0, object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== @@ -10110,6 +10699,15 @@ object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" +object.values@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -10473,6 +11071,11 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -10624,6 +11227,11 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + postcss-calc@^7.0.1: version "7.0.5" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e" @@ -11098,12 +11706,21 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^29.0.0, pretty-format@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" - integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: - "@jest/schemas" "^29.4.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -11131,13 +11748,16 @@ prompts@^2.0.1: sisteransi "^1.0.5" prop-types-exact@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + version "1.2.5" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.5.tgz#f275e7dc0d629c2f7414782e8189b3e2d2e9e158" + integrity sha512-wHDhA5TSSvU07gdzsdeT/FZg6zay94K4Y7swSK4YsRG3moWB0Qsp9g1Y5BBausP1HF8K4UeVe2Xt7ZFJByKp6A== dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" + call-bind "^1.0.7" + es-errors "^1.3.0" + hasown "^2.0.2" + isarray "^2.0.5" + object.assign "^4.1.5" + reflect.ownkeys "^1.1.4" prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" @@ -11247,6 +11867,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + qs@^6.11.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" @@ -11353,6 +11980,16 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + raw-loader@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" @@ -11387,9 +12024,9 @@ react-intl@^6.4.2: tslib "^2.4.0" "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -11555,10 +12192,16 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" - integrity sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg== +reflect.ownkeys@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-1.1.4.tgz#3cf21da448f2aff8aba63ca601f65c99482e692c" + integrity sha512-iUNmtLgzudssL+qnTUosCmnq3eczlrVd1wXrgx/GhiI/8FvwrTYWtCJ9PNvWIRX+4ftupj2WUfB5mu5s9t6LnA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + globalthis "^1.0.3" regenerate-unicode-properties@^10.1.0: version "10.1.0" @@ -11587,6 +12230,11 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regenerator-transform@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" @@ -11611,6 +12259,16 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: define-properties "^1.2.0" functions-have-names "^1.2.3" +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + regexpu-core@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" @@ -11934,6 +12592,16 @@ safe-array-concat@^1.0.0: has-symbols "^1.0.3" isarray "^2.0.5" +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -11953,6 +12621,15 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -12058,7 +12735,7 @@ semver-regex@^2.0.0: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -12068,6 +12745,11 @@ semver@6.3.0, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^5.7.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -12099,6 +12781,25 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + serialize-javascript@^1.7.0: version "1.9.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" @@ -12134,11 +12835,43 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -12222,6 +12955,16 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -12545,6 +13288,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -12663,7 +13413,17 @@ string.prototype.padend@^3.0.0: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trim@^1.2.1, string.prototype.trim@^1.2.7: +string.prototype.trim@^1.2.1, string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trim@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== @@ -12681,6 +13441,15 @@ string.prototype.trimend@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -12690,6 +13459,15 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -13433,6 +14211,38 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -13442,6 +14252,18 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -13489,6 +14311,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + unherit@^1.0.4: version "1.1.3" resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" @@ -14076,11 +14903,32 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + which-module@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" @@ -14176,7 +15024,14 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^6.0.0, ws@^6.2.1: +ws@^6.0.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.3.tgz#ccc96e4add5fd6fedbc491903075c85c5a11d9ee" + integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== + dependencies: + async-limiter "~1.0.0" + +ws@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==