From ea058371ed2dd00d6d473aa3b88fd30910d7498e Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Wed, 20 Jul 2022 17:29:09 -0400 Subject: [PATCH] Use casual for mock data --- package.json | 3 +- patches/casual+1.6.2.patch | 21 ++ ts/components/ProfileEditor.stories.tsx | 252 +++++++------- .../helpers/getDefaultConversation.ts | 321 +----------------- ts/test-both/helpers/getFakeStory.tsx | 17 +- ts/util/lint/exceptions.json | 12 + yarn.lock | 26 +- 7 files changed, 195 insertions(+), 457 deletions(-) create mode 100644 patches/casual+1.6.2.patch diff --git a/package.json b/package.json index dd8dd4969..91e3718f1 100644 --- a/package.json +++ b/package.json @@ -264,6 +264,7 @@ "babel-core": "7.0.0-bridge.0", "babel-loader": "8.0.6", "babel-plugin-lodash": "3.3.4", + "casual": "1.6.2", "chai": "4.3.4", "chai-as-promised": "7.1.1", "core-js": "2.6.9", @@ -314,7 +315,7 @@ "fabric/jsdom": "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz", "fast-glob/glob-parent": "5.1.2", "read-last-lines/mz/thenify-all/thenify": "3.3.1", - "sharp/color/color-string": "1.7.4" + "sharp/color/color-string": "1.9.0" }, "engines": { "node": "16.13.2" diff --git a/patches/casual+1.6.2.patch b/patches/casual+1.6.2.patch new file mode 100644 index 000000000..5c1def204 --- /dev/null +++ b/patches/casual+1.6.2.patch @@ -0,0 +1,21 @@ +diff --git a/node_modules/casual/src/casual.js b/node_modules/casual/src/casual.js +index 9179af1..ed70b78 100644 +--- a/node_modules/casual/src/casual.js ++++ b/node_modules/casual/src/casual.js +@@ -1,11 +1,11 @@ + var helpers = require('./helpers'); +-var exists = require('fs').existsSync; + + var safe_require = function(filename) { +- if (exists(filename + '.js')) { +- return require(filename); +- } +- return {}; ++ try { ++ return require(filename); ++ } catch (err) { ++ return {}; ++ } + }; + + var build_casual = function() { diff --git a/ts/components/ProfileEditor.stories.tsx b/ts/components/ProfileEditor.stories.tsx index cc47320c1..fa346386c 100644 --- a/ts/components/ProfileEditor.stories.tsx +++ b/ts/components/ProfileEditor.stories.tsx @@ -1,181 +1,175 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import type { Meta, Story } from '@storybook/react'; import React, { useState } from 'react'; - -import { text, boolean, select } from '@storybook/addon-knobs'; -import { action } from '@storybook/addon-actions'; +import casual from 'casual'; import type { PropsType } from './ProfileEditor'; -import { ProfileEditor } from './ProfileEditor'; -import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; -import { - getFirstName, - getLastName, -} from '../test-both/helpers/getDefaultConversation'; -import { getRandomColor } from '../test-both/helpers/getRandomColor'; +import { ProfileEditor } from './ProfileEditor'; +import { UUID } from '../types/UUID'; import { UsernameSaveState } from '../state/ducks/conversationsEnums'; +import { getRandomColor } from '../test-both/helpers/getRandomColor'; +import { setupI18n } from '../util/setupI18n'; const i18n = setupI18n('en', enMessages); export default { + component: ProfileEditor, title: 'Components/ProfileEditor', -}; + argTypes: { + aboutEmoji: { + defaultValue: '', + }, + aboutText: { + defaultValue: casual.sentence, + }, + profileAvatarPath: { + defaultValue: undefined, + }, + clearUsernameSave: { action: true }, + conversationId: { + defaultValue: UUID.generate().toString(), + }, + color: { + defaultValue: getRandomColor(), + }, + deleteAvatarFromDisk: { action: true }, + familyName: { + defaultValue: casual.last_name, + }, + firstName: { + defaultValue: casual.first_name, + }, + i18n: { + defaultValue: i18n, + }, + isUsernameFlagEnabled: { + control: { type: 'checkbox' }, + defaultValue: false, + }, + onEditStateChanged: { action: true }, + onProfileChanged: { action: true }, + onSetSkinTone: { action: true }, + recentEmojis: { + defaultValue: [], + }, + replaceAvatar: { action: true }, + saveAvatarToDisk: { action: true }, + saveUsername: { action: true }, + skinTone: { + defaultValue: 0, + }, + userAvatarData: { + defaultValue: [], + }, + username: { + defaultValue: casual.username, + }, + usernameSaveState: { + control: { type: 'radio' }, + defaultValue: UsernameSaveState.None, + options: { + None: UsernameSaveState.None, + Saving: UsernameSaveState.Saving, + UsernameTakenError: UsernameSaveState.UsernameTakenError, + UsernameMalformedError: UsernameSaveState.UsernameMalformedError, + GeneralError: UsernameSaveState.GeneralError, + DeleteFailed: UsernameSaveState.DeleteFailed, + Success: UsernameSaveState.Success, + }, + }, + }, +} as Meta; -const createProps = (overrideProps: Partial = {}): PropsType => ({ - aboutEmoji: overrideProps.aboutEmoji, - aboutText: text('about', overrideProps.aboutText || ''), - profileAvatarPath: overrideProps.profileAvatarPath, - clearUsernameSave: action('clearUsernameSave'), - conversationId: '123', - color: overrideProps.color || getRandomColor(), - deleteAvatarFromDisk: action('deleteAvatarFromDisk'), - familyName: overrideProps.familyName, - firstName: text('firstName', overrideProps.firstName || getFirstName()), - i18n, - isUsernameFlagEnabled: boolean( - 'isUsernameFlagEnabled', - overrideProps.isUsernameFlagEnabled !== undefined - ? overrideProps.isUsernameFlagEnabled - : false - ), - onEditStateChanged: action('onEditStateChanged'), - onProfileChanged: action('onProfileChanged'), - onSetSkinTone: overrideProps.onSetSkinTone || action('onSetSkinTone'), - recentEmojis: [], - replaceAvatar: action('replaceAvatar'), - saveAvatarToDisk: action('saveAvatarToDisk'), - saveUsername: action('saveUsername'), - skinTone: overrideProps.skinTone || 0, - userAvatarData: [], - username: overrideProps.username, - usernameSaveState: select( - 'usernameSaveState', - Object.values(UsernameSaveState), - overrideProps.usernameSaveState || UsernameSaveState.None - ), -}); - -export const FullSet = (): JSX.Element => { +const Template: Story = args => { const [skinTone, setSkinTone] = useState(0); return ( - + ); }; -export const WithFullName = (): JSX.Element => ( - -); +export const FullSet = Template.bind({}); +FullSet.args = { + aboutEmoji: '🙏', + aboutText: 'Live. Laugh. Love', + familyName: casual.last_name, + firstName: casual.first_name, + profileAvatarPath: '/fixtures/kitten-3-64-64.jpg', +}; +export const WithFullName = Template.bind({}); +WithFullName.args = { + familyName: casual.last_name, +}; WithFullName.story = { name: 'with Full Name', }; -export const WithCustomAbout = (): JSX.Element => ( - -); - +export const WithCustomAbout = Template.bind({}); +WithCustomAbout.args = { + aboutEmoji: '🙏', + aboutText: 'Live. Laugh. Love', +}; WithCustomAbout.story = { name: 'with Custom About', }; -export const WithUsernameFlagEnabled = (): JSX.Element => ( - -); - +export const WithUsernameFlagEnabled = Template.bind({}); +WithUsernameFlagEnabled.args = { + isUsernameFlagEnabled: true, +}; WithUsernameFlagEnabled.story = { name: 'with Username flag enabled', }; -export const WithUsernameFlagEnabledAndUsername = (): JSX.Element => ( - -); - +export const WithUsernameFlagEnabledAndUsername = Template.bind({}); +WithUsernameFlagEnabledAndUsername.args = { + isUsernameFlagEnabled: true, + username: casual.username, +}; WithUsernameFlagEnabledAndUsername.story = { name: 'with Username flag enabled and username', }; -export const UsernameEditingSaving = (): JSX.Element => ( - -); - +export const UsernameEditingSaving = Template.bind({}); +UsernameEditingSaving.args = { + isUsernameFlagEnabled: true, + usernameSaveState: UsernameSaveState.Saving, + username: casual.username, +}; UsernameEditingSaving.story = { name: 'Username editing, saving', }; -export const UsernameEditingUsernameTaken = (): JSX.Element => ( - -); - +export const UsernameEditingUsernameTaken = Template.bind({}); +UsernameEditingUsernameTaken.args = { + isUsernameFlagEnabled: true, + usernameSaveState: UsernameSaveState.UsernameTakenError, + username: casual.username, +}; UsernameEditingUsernameTaken.story = { name: 'Username editing, username taken', }; -export const UsernameEditingUsernameMalformed = (): JSX.Element => ( - -); - +export const UsernameEditingUsernameMalformed = Template.bind({}); +UsernameEditingUsernameMalformed.args = { + isUsernameFlagEnabled: true, + usernameSaveState: UsernameSaveState.UsernameMalformedError, + username: casual.username, +}; UsernameEditingUsernameMalformed.story = { name: 'Username editing, username malformed', }; -export const UsernameEditingGeneralError = (): JSX.Element => ( - -); - +export const UsernameEditingGeneralError = Template.bind({}); +UsernameEditingGeneralError.args = { + isUsernameFlagEnabled: true, + usernameSaveState: UsernameSaveState.GeneralError, + username: casual.username, +}; UsernameEditingGeneralError.story = { name: 'Username editing, general error', }; diff --git a/ts/test-both/helpers/getDefaultConversation.ts b/ts/test-both/helpers/getDefaultConversation.ts index c0ffcfd89..d1e8b0bf1 100644 --- a/ts/test-both/helpers/getDefaultConversation.ts +++ b/ts/test-both/helpers/getDefaultConversation.ts @@ -1,7 +1,7 @@ // Copyright 2020-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { v4 as generateUuid } from 'uuid'; +import casual from 'casual'; import { sample } from 'lodash'; import type { ConversationType } from '../../state/ducks/conversations'; import { UUID } from '../../types/UUID'; @@ -9,315 +9,6 @@ import type { UUIDStringType } from '../../types/UUID'; import { getRandomColor } from './getRandomColor'; import { ConversationColors } from '../../types/Colors'; -const FIRST_NAMES = [ - 'James', - 'John', - 'Robert', - 'Michael', - 'William', - 'David', - 'Richard', - 'Joseph', - 'Thomas', - 'Charles', - 'Christopher', - 'Daniel', - 'Matthew', - 'Anthony', - 'Donald', - 'Mark', - 'Paul', - 'Steven', - 'Andrew', - 'Kenneth', - 'Joshua', - 'Kevin', - 'Brian', - 'George', - 'Edward', - 'Ronald', - 'Timothy', - 'Jason', - 'Jeffrey', - 'Ryan', - 'Jacob', - 'Gary', - 'Nicholas', - 'Eric', - 'Jonathan', - 'Stephen', - 'Larry', - 'Justin', - 'Scott', - 'Brandon', - 'Benjamin', - 'Samuel', - 'Frank', - 'Gregory', - 'Raymond', - 'Alexander', - 'Patrick', - 'Jack', - 'Dennis', - 'Jerry', - 'Tyler', - 'Aaron', - 'Jose', - 'Henry', - 'Adam', - 'Douglas', - 'Nathan', - 'Peter', - 'Zachary', - 'Kyle', - 'Walter', - 'Harold', - 'Jeremy', - 'Ethan', - 'Carl', - 'Keith', - 'Roger', - 'Gerald', - 'Christian', - 'Terry', - 'Sean', - 'Arthur', - 'Austin', - 'Noah', - 'Lawrence', - 'Jesse', - 'Joe', - 'Bryan', - 'Billy', - 'Jordan', - 'Albert', - 'Dylan', - 'Bruce', - 'Willie', - 'Gabriel', - 'Alan', - 'Juan', - 'Logan', - 'Wayne', - 'Ralph', - 'Roy', - 'Eugene', - 'Randy', - 'Vincent', - 'Russell', - 'Louis', - 'Philip', - 'Bobby', - 'Johnny', - 'Bradley', - 'Mary', - 'Patricia', - 'Jennifer', - 'Linda', - 'Elizabeth', - 'Barbara', - 'Susan', - 'Jessica', - 'Sarah', - 'Karen', - 'Nancy', - 'Lisa', - 'Margaret', - 'Betty', - 'Sandra', - 'Ashley', - 'Dorothy', - 'Kimberly', - 'Emily', - 'Donna', - 'Michelle', - 'Carol', - 'Amanda', - 'Melissa', - 'Deborah', - 'Stephanie', - 'Rebecca', - 'Laura', - 'Sharon', - 'Cynthia', - 'Kathleen', - 'Amy', - 'Shirley', - 'Angela', - 'Helen', - 'Anna', - 'Brenda', - 'Pamela', - 'Nicole', - 'Samantha', - 'Katherine', - 'Emma', - 'Ruth', - 'Christine', - 'Catherine', - 'Debra', - 'Rachel', - 'Carolyn', - 'Janet', - 'Virginia', - 'Maria', - 'Heather', - 'Diane', - 'Julie', - 'Joyce', - 'Victoria', - 'Kelly', - 'Christina', - 'Lauren', - 'Joan', - 'Evelyn', - 'Olivia', - 'Judith', - 'Megan', - 'Cheryl', - 'Martha', - 'Andrea', - 'Frances', - 'Hannah', - 'Jacqueline', - 'Ann', - 'Gloria', - 'Jean', - 'Kathryn', - 'Alice', - 'Teresa', - 'Sara', - 'Janice', - 'Doris', - 'Madison', - 'Julia', - 'Grace', - 'Judy', - 'Abigail', - 'Marie', - 'Denise', - 'Beverly', - 'Amber', - 'Theresa', - 'Marilyn', - 'Danielle', - 'Diana', - 'Brittany', - 'Natalie', - 'Sophia', - 'Rose', - 'Isabella', - 'Alexis', - 'Kayla', - 'Charlotte', -]; - -const LAST_NAMES = [ - 'Smith', - 'Johnson', - 'Williams', - 'Brown', - 'Jones', - 'Garcia', - 'Miller', - 'Davis', - 'Rodriguez', - 'Martinez', - 'Hernandez', - 'Lopez', - 'Gonzales', - 'Wilson', - 'Anderson', - 'Thomas', - 'Taylor', - 'Moore', - 'Jackson', - 'Martin', - 'Lee', - 'Perez', - 'Thompson', - 'White', - 'Harris', - 'Sanchez', - 'Clark', - 'Ramirez', - 'Lewis', - 'Robinson', - 'Walker', - 'Young', - 'Allen', - 'King', - 'Wright', - 'Scott', - 'Torres', - 'Nguyen', - 'Hill', - 'Flores', - 'Green', - 'Adams', - 'Nelson', - 'Baker', - 'Hall', - 'Rivera', - 'Campbell', - 'Mitchell', - 'Carter', - 'Roberts', - 'Gomez', - 'Phillips', - 'Evans', - 'Turner', - 'Diaz', - 'Parker', - 'Cruz', - 'Edwards', - 'Collins', - 'Reyes', - 'Stewart', - 'Morris', - 'Morales', - 'Murphy', - 'Cook', - 'Rogers', - 'Gutierrez', - 'Ortiz', - 'Morgan', - 'Cooper', - 'Peterson', - 'Bailey', - 'Reed', - 'Kelly', - 'Howard', - 'Ramos', - 'Kim', - 'Cox', - 'Ward', - 'Richardson', - 'Watson', - 'Brooks', - 'Chavez', - 'Wood', - 'James', - 'Bennet', - 'Gray', - 'Mendoza', - 'Ruiz', - 'Hughes', - 'Price', - 'Alvarez', - 'Castillo', - 'Sanders', - 'Patel', - 'Myers', - 'Long', - 'Ross', - 'Foster', - 'Jimenez', -]; - -export const getFirstName = (): string => sample(FIRST_NAMES) || 'Test'; -export const getLastName = (): string => sample(LAST_NAMES) || 'Test'; - export const getAvatarPath = (): string => sample([ '/fixtures/kitten-1-64-64.jpg', @@ -328,20 +19,20 @@ export const getAvatarPath = (): string => export function getDefaultConversation( overrideProps: Partial = {} ): ConversationType { - const firstName = getFirstName(); - const lastName = getLastName(); + const firstName = casual.first_name; + const lastName = casual.last_name; return { acceptedMessageRequest: true, avatarPath: getAvatarPath(), badges: [], - e164: '+1300555000', + e164: `+${casual.phone.replace(/-/g, '')}`, conversationColor: ConversationColors[0], color: getRandomColor(), firstName, - id: generateUuid(), + id: UUID.generate().toString(), isMe: false, - lastUpdated: Date.now(), + lastUpdated: casual.unix_time, markedUnread: Boolean(overrideProps.markedUnread), sharedGroupNames: [], title: `${firstName} ${lastName}`, diff --git a/ts/test-both/helpers/getFakeStory.tsx b/ts/test-both/helpers/getFakeStory.tsx index 61eb926f0..f43da9e72 100644 --- a/ts/test-both/helpers/getFakeStory.tsx +++ b/ts/test-both/helpers/getFakeStory.tsx @@ -1,7 +1,7 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { v4 as uuid } from 'uuid'; +import casual from 'casual'; import type { AttachmentType } from '../../types/Attachment'; import type { ConversationType } from '../../state/ducks/conversations'; @@ -11,6 +11,7 @@ import type { StoryViewType, } from '../../types/Stories'; import * as durations from '../../util/durations'; +import { UUID } from '../../types/UUID'; import { getDefaultConversation } from './getDefaultConversation'; import { fakeAttachment, fakeThumbnail } from './fakeAttachment'; import { MY_STORIES_ID } from '../../types/Stories'; @@ -23,16 +24,16 @@ function getAttachmentWithThumbnail(url: string): AttachmentType { } export function getFakeMyStory(id?: string, name?: string): MyStoryType { - const storyCount = Math.ceil(Math.random() * 6 + 1); + const storyCount = casual.integer(2, 6); return { - distributionId: id || uuid(), + distributionId: id || UUID.generate().toString(), distributionName: - name || id === MY_STORIES_ID ? 'My Stories' : 'Private Distribution List', + name || id === MY_STORIES_ID ? 'My Stories' : casual.catch_phrase, stories: Array.from(Array(storyCount), () => ({ ...getFakeStoryView(), sendState: [], - views: Math.floor(Math.random() * 20), + views: casual.integer(1, 20), })), }; } @@ -47,9 +48,9 @@ export function getFakeStoryView( attachment: getAttachmentWithThumbnail( attachmentUrl || '/fixtures/tina-rolf-269345-unsplash.jpg' ), - hasReplies: Math.random() > 0.5, - isUnread: Math.random() > 0.5, - messageId: uuid(), + hasReplies: Boolean(casual.coin_flip), + isUnread: Boolean(casual.coin_flip), + messageId: UUID.generate().toString(), sender, timestamp: timestamp || Date.now() - 2 * durations.MINUTE, }; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 376214a97..4b1b27d81 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -1752,6 +1752,18 @@ "reasonCategory": "falseMatch", "updated": "2018-09-19T18:13:29.628Z" }, + { + "rule": "jQuery-$(", + "path": "node_modules/casual/node_modules/moment/min/moment-with-locales.min.js", + "reasonCategory": "falseMatch", + "updated": "2022-07-07T22:51:10.163Z" + }, + { + "rule": "jQuery-$(", + "path": "node_modules/casual/node_modules/moment/min/moment.min.js", + "reasonCategory": "falseMatch", + "updated": "2022-07-07T22:51:10.163Z" + }, { "rule": "eval", "path": "node_modules/config/lib/config.js", diff --git a/yarn.lock b/yarn.lock index aaa2f97f5..236ecb525 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5728,6 +5728,14 @@ case-sensitive-paths-webpack-plugin@^2.3.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +casual@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/casual/-/casual-1.6.2.tgz#f56b87113ae99a6bba45e04bdfa068ca443f9e85" + integrity sha512-NQObL800rg32KZ9bBajHbyDjxLXxxuShChQg7A4tbSeG3n1t7VYGOSkzFSI9gkSgOHp+xilEJ7G0L5l6M30KYA== + dependencies: + mersenne-twister "^1.0.1" + moment "^2.15.2" + ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" @@ -6055,10 +6063,10 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@1.7.4, color-string@^1.9.0: - version "1.7.4" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.7.4.tgz#79a7bf242610a8aa1b5e2681f3bf6bcfa666245f" - integrity sha512-nVdUvPVgZMpRQad5dcsCMOSB5BXLljklTiaxS6ehhKxDsAI5sD7k5VmFuBt1y3Rlym8uulc/ANUN/bMWtBu6Sg== +color-string@1.9.0, color-string@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -11443,6 +11451,11 @@ merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +mersenne-twister@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" + integrity sha512-mUYWsMKNrm4lfygPkL3OfGzOPTR2DBlTkBNHM//F6hGp8cLThY897crAlk3/Jo17LEOOjQUrNAx6DvgO77QJkA== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -11823,6 +11836,11 @@ moment@2.29.4: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moment@^2.15.2: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"