Unit test MessageActionBar (#8732)
* test most basic paths in messageactionbar Signed-off-by: Kerry Archibald <kerrya@element.io> * tidy Signed-off-by: Kerry Archibald <kerrya@element.io> * add rtl * add code style note about using rtl Signed-off-by: Kerry Archibald <kerrya@element.io> * downgrade to rtl 12 * use rtl for MessageActionBar test Signed-off-by: Kerry Archibald <kerrya@element.io> * try mocking settingsstore for ci only failure Signed-off-by: Kerry Archibald <kerrya@element.io> * mock setValue too Signed-off-by: Kerry Archibald <kerrya@element.io> * uupdate lockfile Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
parent
7c57680b93
commit
158e42f764
8 changed files with 436 additions and 2 deletions
|
@ -208,3 +208,7 @@ React
|
|||
information in component state that could be derived from the model?
|
||||
|
||||
- Avoid things marked as Legacy or Deprecated in React 16 (e.g string refs and legacy contexts)
|
||||
|
||||
Unit tests
|
||||
-----
|
||||
- New tests should use [react testing library](https://testing-library.com/docs/react-testing-library/intro/)
|
|
@ -58,6 +58,7 @@
|
|||
"@babel/runtime": "^7.12.5",
|
||||
"@sentry/browser": "^6.11.0",
|
||||
"@sentry/tracing": "^6.11.0",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@types/geojson": "^7946.0.8",
|
||||
"await-lock": "^2.1.0",
|
||||
"blurhash": "^1.1.3",
|
||||
|
|
|
@ -670,6 +670,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||
{...this.props}
|
||||
className="mx_MessageContextMenu"
|
||||
compact={true}
|
||||
data-testid="mx_MessageContextMenu"
|
||||
>
|
||||
{ nativeItemsList }
|
||||
{ quickItemsList }
|
||||
|
|
|
@ -124,6 +124,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
|
|||
onChoose={this.onChoose}
|
||||
selectedEmojis={this.state.selectedEmojis}
|
||||
showQuickReactions={true}
|
||||
data-testid='mx_ReactionPicker'
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
|
363
test/components/views/messages/MessageActionBar-test.tsx
Normal file
363
test/components/views/messages/MessageActionBar-test.tsx
Normal file
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { act } from 'react-test-renderer';
|
||||
import {
|
||||
EventType,
|
||||
EventStatus,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
MsgType,
|
||||
Room,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
|
||||
import MessageActionBar from '../../../../src/components/views/messages/MessageActionBar';
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsUser,
|
||||
mockClientMethodsEvents,
|
||||
} from '../../../test-utils';
|
||||
import { RoomPermalinkCreator } from '../../../../src/utils/permalinks/Permalinks';
|
||||
import RoomContext, { TimelineRenderingType } from '../../../../src/contexts/RoomContext';
|
||||
import { IRoomState } from '../../../../src/components/structures/RoomView';
|
||||
import dispatcher from '../../../../src/dispatcher/dispatcher';
|
||||
import SettingsStore from '../../../../src/settings/SettingsStore';
|
||||
|
||||
jest.mock('../../../../src/dispatcher/dispatcher');
|
||||
|
||||
describe('<MessageActionBar />', () => {
|
||||
const userId = '@alice:server.org';
|
||||
const roomId = '!room:server.org';
|
||||
const alicesMessageEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
},
|
||||
});
|
||||
|
||||
const bobsMessageEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: '@bob:server.org',
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'I am bob',
|
||||
},
|
||||
});
|
||||
|
||||
const redactedEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
});
|
||||
redactedEvent.makeRedacted(redactedEvent);
|
||||
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
...mockClientMethodsEvents(),
|
||||
getRoom: jest.fn(),
|
||||
});
|
||||
const room = new Room(roomId, client, userId);
|
||||
jest.spyOn(room, 'getPendingEvents').mockReturnValue([]);
|
||||
|
||||
client.getRoom.mockReturnValue(room);
|
||||
|
||||
const defaultProps = {
|
||||
getTile: jest.fn(),
|
||||
getReplyChain: jest.fn(),
|
||||
toggleThreadExpanded: jest.fn(),
|
||||
mxEvent: alicesMessageEvent,
|
||||
permalinkCreator: new RoomPermalinkCreator(room),
|
||||
};
|
||||
const defaultRoomContext = {
|
||||
...RoomContext,
|
||||
timelineRenderingType: TimelineRenderingType.Room,
|
||||
canSendMessages: true,
|
||||
canReact: true,
|
||||
} as unknown as IRoomState;
|
||||
const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) =>
|
||||
render(
|
||||
<RoomContext.Provider value={{ ...defaultRoomContext, ...roomContext }}>
|
||||
<MessageActionBar {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
alicesMessageEvent.setStatus(EventStatus.SENT);
|
||||
jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, 'setValue').mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(SettingsStore, 'getValue').mockRestore();
|
||||
jest.spyOn(SettingsStore, 'setValue').mockRestore();
|
||||
});
|
||||
|
||||
it('kills event listeners on unmount', () => {
|
||||
const offSpy = jest.spyOn(alicesMessageEvent, 'off').mockClear();
|
||||
const wrapper = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
expect(offSpy.mock.calls[0][0]).toEqual(MatrixEventEvent.Status);
|
||||
expect(offSpy.mock.calls[1][0]).toEqual(MatrixEventEvent.Decrypted);
|
||||
expect(offSpy.mock.calls[2][0]).toEqual(MatrixEventEvent.BeforeRedaction);
|
||||
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('decryption', () => {
|
||||
it('decrypts event if needed', () => {
|
||||
getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('updates component on decrypted event', () => {
|
||||
const decryptingEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {},
|
||||
});
|
||||
jest.spyOn(decryptingEvent, 'isBeingDecrypted').mockReturnValue(true);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: decryptingEvent });
|
||||
|
||||
// still encrypted event is not actionable => no reply button
|
||||
expect(queryByLabelText('Reply')).toBeFalsy();
|
||||
|
||||
act(() => {
|
||||
// ''decrypt'' the event
|
||||
decryptingEvent.event.type = alicesMessageEvent.getType();
|
||||
decryptingEvent.event.content = alicesMessageEvent.getContent();
|
||||
decryptingEvent.emit(MatrixEventEvent.Decrypted, decryptingEvent);
|
||||
});
|
||||
|
||||
// new available actions after decryption
|
||||
expect(queryByLabelText('Reply')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('status', () => {
|
||||
it('updates component when event status changes', () => {
|
||||
alicesMessageEvent.setStatus(EventStatus.QUEUED);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
// pending event status, cancel action available
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
alicesMessageEvent.setStatus(EventStatus.SENT);
|
||||
});
|
||||
|
||||
// event is sent, no longer cancelable
|
||||
expect(queryByLabelText('Delete')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('redaction', () => {
|
||||
// this doesn't do what it's supposed to
|
||||
// because beforeRedaction event is fired... before redaction
|
||||
// event is unchanged at point when this component updates
|
||||
// TODO file bug
|
||||
xit('updates component on before redaction event', () => {
|
||||
const event = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
},
|
||||
});
|
||||
const { queryByLabelText } = getComponent({ mxEvent: event });
|
||||
|
||||
// no pending redaction => no delete button
|
||||
expect(queryByLabelText('Delete')).toBeFalsy();
|
||||
|
||||
act(() => {
|
||||
const redactionEvent = new MatrixEvent({
|
||||
type: EventType.RoomRedaction,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
});
|
||||
redactionEvent.setStatus(EventStatus.QUEUED);
|
||||
event.markLocallyRedacted(redactionEvent);
|
||||
});
|
||||
|
||||
// updated with local redaction event, delete now available
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('options button', () => {
|
||||
it('renders options menu', () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Options')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('opens message context menu on click', () => {
|
||||
const { findByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText('Options'));
|
||||
});
|
||||
expect(findByTestId('mx_MessageContextMenu')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reply button', () => {
|
||||
it('renders reply button on own actionable event', () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Reply')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders reply button on others actionable event', () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent }, { canSendMessages: true });
|
||||
expect(queryByLabelText('Reply')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render reply button on non-actionable event', () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent });
|
||||
expect(queryByLabelText('Reply')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render reply button when user cannot send messaged', () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }, { canSendMessages: false });
|
||||
expect(queryByLabelText('Reply')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('dispatches reply event on click', () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText('Reply'));
|
||||
});
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: 'reply_to_event',
|
||||
event: alicesMessageEvent,
|
||||
context: TimelineRenderingType.Room,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('react button', () => {
|
||||
it('renders react button on own actionable event', () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('React')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders react button on others actionable event', () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent });
|
||||
expect(queryByLabelText('React')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render react button on non-actionable event', () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent });
|
||||
expect(queryByLabelText('React')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render react button when user cannot react', () => {
|
||||
// redacted event is not actionable
|
||||
const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }, { canReact: false });
|
||||
expect(queryByLabelText('React')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('opens reaction picker on click', () => {
|
||||
const { queryByLabelText, findByTestId } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText('React'));
|
||||
});
|
||||
expect(findByTestId('mx_ReactionPicker')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancel button', () => {
|
||||
it('renders cancel button for an event with a cancelable status', () => {
|
||||
alicesMessageEvent.setStatus(EventStatus.QUEUED);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders cancel button for an event with a pending edit', () => {
|
||||
const event = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
},
|
||||
});
|
||||
event.setStatus(EventStatus.SENT);
|
||||
const replacingEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'replacing event body',
|
||||
},
|
||||
});
|
||||
replacingEvent.setStatus(EventStatus.QUEUED);
|
||||
event.makeReplaced(replacingEvent);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: event });
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders cancel button for an event with a pending redaction', () => {
|
||||
const event = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
body: 'Hello',
|
||||
},
|
||||
});
|
||||
event.setStatus(EventStatus.SENT);
|
||||
|
||||
const redactionEvent = new MatrixEvent({
|
||||
type: EventType.RoomRedaction,
|
||||
sender: userId,
|
||||
room_id: roomId,
|
||||
});
|
||||
redactionEvent.setStatus(EventStatus.QUEUED);
|
||||
|
||||
event.markLocallyRedacted(redactionEvent);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: event });
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders cancel and retry button for an event with NOT_SENT status', () => {
|
||||
alicesMessageEvent.setStatus(EventStatus.NOT_SENT);
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText('Retry')).toBeTruthy();
|
||||
expect(queryByLabelText('Delete')).toBeTruthy();
|
||||
});
|
||||
|
||||
it.todo('unsends event on cancel click');
|
||||
it.todo('retrys event on retry click');
|
||||
});
|
||||
});
|
|
@ -67,4 +67,17 @@ export const mockClientMethodsUser = (userId = '@alice:domain') => ({
|
|||
getUserId: jest.fn().mockReturnValue(userId),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue('mock-mxcUrlToHttp'),
|
||||
credentials: { userId },
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns basic mocked client methods related to rendering events
|
||||
* ```
|
||||
* const mockClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser('@mytestuser:domain'),
|
||||
});
|
||||
* ```
|
||||
*/
|
||||
export const mockClientMethodsEvents = () => ({
|
||||
decryptEventIfNeeded: jest.fn(),
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ export function untilDispatch(waitForAction: DispatcherAction): Promise<ActionPa
|
|||
const findByAttr = (attr: string) => (component: ReactWrapper, value: string) => component.find(`[${attr}="${value}"]`);
|
||||
export const findByTestId = findByAttr('data-test-id');
|
||||
export const findById = findByAttr('id');
|
||||
export const findByAriaLabel = findByAttr('aria-label');
|
||||
|
||||
const findByTagAndAttr = (attr: string) =>
|
||||
(component: ReactWrapper, value: string, tag: string) =>
|
||||
|
|
54
yarn.lock
54
yarn.lock
|
@ -50,7 +50,7 @@
|
|||
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
|
||||
chokidar "^3.4.0"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7":
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
|
||||
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
|
||||
|
@ -1852,11 +1852,39 @@
|
|||
remark "^13.0.0"
|
||||
unist-util-find-all-after "^3.0.2"
|
||||
|
||||
"@testing-library/dom@^8.0.0":
|
||||
version "8.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.13.0.tgz#bc00bdd64c7d8b40841e27a70211399ad3af46f5"
|
||||
integrity sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@types/aria-query" "^4.2.0"
|
||||
aria-query "^5.0.0"
|
||||
chalk "^4.1.0"
|
||||
dom-accessibility-api "^0.5.9"
|
||||
lz-string "^1.4.4"
|
||||
pretty-format "^27.0.2"
|
||||
|
||||
"@testing-library/react@^12.1.5":
|
||||
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"
|
||||
|
||||
"@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@^4.2.0":
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
|
||||
integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==
|
||||
|
||||
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7":
|
||||
version "7.1.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460"
|
||||
|
@ -2107,6 +2135,13 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-dom@<18.0.0":
|
||||
version "17.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1"
|
||||
integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==
|
||||
dependencies:
|
||||
"@types/react" "^17"
|
||||
|
||||
"@types/react-redux@^7.1.20":
|
||||
version "7.1.24"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
|
||||
|
@ -2484,6 +2519,11 @@ aria-query@^4.2.2:
|
|||
"@babel/runtime" "^7.10.2"
|
||||
"@babel/runtime-corejs3" "^7.10.2"
|
||||
|
||||
aria-query@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c"
|
||||
integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==
|
||||
|
||||
arr-diff@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
||||
|
@ -3744,6 +3784,11 @@ doctrine@^3.0.0:
|
|||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dom-accessibility-api@^0.5.9:
|
||||
version "0.5.14"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
|
||||
integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==
|
||||
|
||||
dom-helpers@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||
|
@ -6723,6 +6768,11 @@ lru-queue@^0.1.0:
|
|||
dependencies:
|
||||
es5-ext "~0.10.2"
|
||||
|
||||
lz-string@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==
|
||||
|
||||
make-dir@^2.0.0, make-dir@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||
|
@ -7725,7 +7775,7 @@ pretty-format@^26.0.0, pretty-format@^26.6.2:
|
|||
ansi-styles "^4.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
pretty-format@^27.5.1:
|
||||
pretty-format@^27.0.2, pretty-format@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
|
||||
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
||||
|
|
Loading…
Reference in a new issue