The tests for each component has been set up. The way on writing tests have been patterned from https://kentcdodds.com/blog/common-mistakes-with-react-testing-library. In addition, the requirements document has been modified to use a consistent formatting which can be translated into code.master
@@ -1,30 +1,26 @@ | |||||
# Requirements | # Requirements | ||||
- As a client, I want to log in to the service. | - As a client, I want to log in to the service. | ||||
- [ ] In the front-end, the client requests login by providing a username and a password to the back-end. | |||||
- [ ] In the front-end, the client provides a username, and a password to request login to the back-end. | |||||
- [ ] In the back-end, the server processes the login request from the front-end. | - [ ] In the back-end, the server processes the login request from the front-end. | ||||
- As a client, I want to log out from the service. | - As a client, I want to log out from the service. | ||||
- [ ] In the front-end, the client requests logout to the backend. | - [ ] In the front-end, the client requests logout to the backend. | ||||
- [ ] In the back-end, the server processes the logout request from the front-end. | - [ ] In the back-end, the server processes the logout request from the front-end. | ||||
- As a client, I want to search for available ringtones. | |||||
- [ ] In the front-end, the client requests ringtones by providing a search keyword to the back-end. | |||||
- [ ] In the back-end, the server retrieves the ringtones whose name matches the search keyword provided by the | |||||
front-end. | |||||
- As a client, I want to download a ringtone. | - As a client, I want to download a ringtone. | ||||
- [ ] In the front-end, the client requests the data of a ringtone from the back-end. | |||||
- [ ] In the front-end, the client provides a ringtone ID to request a ringtone from the back-end. | |||||
- [ ] In the back-end, the server sends the ringtone data to the front-end. | - [ ] In the back-end, the server sends the ringtone data to the front-end. | ||||
- As a client, I want to search for composers. | |||||
- [ ] In the front-end, the client requests composers by providing a search keyword to the back-end. | |||||
- [ ] In the back-end, the server retrieves the composers whose name matches the search keyword provided by the | |||||
front-end. | |||||
- As a client, I want to search for composers and ringtones. | |||||
- [ ] In the front-end, the client requests provides a search keyword to request for composers and ringtones from the back-end. | |||||
- [ ] In the back-end, the server retrieves the composers and ringtones whose name matches the search keyword provided | |||||
by the front-end. | |||||
- As a composer, I want to create a ringtone. | - As a composer, I want to create a ringtone. | ||||
- [ ] In the front-end, the client inputs ringtone data to the view. | - [ ] In the front-end, the client inputs ringtone data to the view. | ||||
- [ ] In the front-end, the client sends the ringtone data to the back-end. | - [ ] In the front-end, the client sends the ringtone data to the back-end. | ||||
- [ ] In the back-end, the server stores the ringtone. | - [ ] In the back-end, the server stores the ringtone. | ||||
- As a composer, I want to update a ringtone. | - As a composer, I want to update a ringtone. | ||||
- [ ] In the front-end, the client modifies the ringtone data loaded on the view. | |||||
- [ ] In the front-end, the client modifies the ringtone data retrieved from the back-end and loaded on the view. | |||||
- [ ] In the front-end, the client sends the ringtone data to the back-end. | - [ ] In the front-end, the client sends the ringtone data to the back-end. | ||||
- [ ] In the back-end, the server stores the ringtone. | - [ ] In the back-end, the server stores the ringtone. | ||||
- As a composer, I want to delete a ringtone. | - As a composer, I want to delete a ringtone. | ||||
- [ ] In the front-end, the client requests deletion of a ringtone to the back-end. | |||||
- [ ] In the back-end, the server deletes the ringtone. | |||||
- [ ] In the front-end, the client provides a ringtone ID to request a ringtone's deletion to the back-end. | |||||
- [ ] In the back-end, the server deletes the ringtone if its ID matches the one provided by the front-end. |
@@ -10,5 +10,6 @@ module.exports = { | |||||
'src/components/**/*.{jsx,tsx}', | 'src/components/**/*.{jsx,tsx}', | ||||
'src/modules/**/*.{js,ts}', | 'src/modules/**/*.{js,ts}', | ||||
'src/utils/**/*.{js,ts}', | 'src/utils/**/*.{js,ts}', | ||||
] | |||||
], | |||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'] | |||||
}; | }; |
@@ -0,0 +1 @@ | |||||
import '@testing-library/jest-dom' |
@@ -18,6 +18,7 @@ | |||||
"styled-components": "^5.3.0" | "styled-components": "^5.3.0" | ||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"@testing-library/jest-dom": "^5.12.0", | |||||
"@testing-library/react": "^11.2.6", | "@testing-library/react": "^11.2.6", | ||||
"@types/jest": "^26.0.23", | "@types/jest": "^26.0.23", | ||||
"@types/node": "^15.0.2", | "@types/node": "^15.0.2", | ||||
@@ -1,22 +1,19 @@ | |||||
import {cleanup, render} from '@testing-library/react' | |||||
import {render, screen} from '@testing-library/react' | |||||
import ActionButton from '.' | import ActionButton from '.' | ||||
describe('button component for triggering actions', () => { | describe('button component for triggering actions', () => { | ||||
afterEach(() => { | |||||
cleanup() | |||||
}) | |||||
it('should render a button element with a no-op action', () => { | it('should render a button element with a no-op action', () => { | ||||
const result = render(<ActionButton />) | |||||
const input = result.queryByRole('button') | |||||
expect(input).not.toBeNull() | |||||
render(<ActionButton />) | |||||
expect(screen.getByRole('button')).toBeInTheDocument() | |||||
}) | }) | ||||
describe.each(['button', 'reset', 'submit'] as const)('on %p action', (type) => { | describe.each(['button', 'reset', 'submit'] as const)('on %p action', (type) => { | ||||
beforeEach(() => { | |||||
render(<ActionButton type={type} />) | |||||
}) | |||||
it('should render a button element with a submit action', () => { | it('should render a button element with a submit action', () => { | ||||
const result = render(<ActionButton type={type} />) | |||||
const input = result.queryByRole('button') as HTMLButtonElement | |||||
expect(input.type).toBe(type) | |||||
expect(screen.getByRole('button')).toHaveAttribute('type', type) | |||||
}) | }) | ||||
}) | }) | ||||
}) | }) |
@@ -1,21 +1,15 @@ | |||||
import {render, cleanup} from '@testing-library/react' | |||||
import {render, screen} from '@testing-library/react' | |||||
import TextInput from '.' | import TextInput from '.' | ||||
describe('single-line text input component', () => { | describe('single-line text input component', () => { | ||||
afterEach(() => { | |||||
cleanup() | |||||
}) | |||||
it('should contain a text input element', () => { | it('should contain a text input element', () => { | ||||
const result = render(<TextInput label="" name="" />) | |||||
const input = result.queryByRole('textbox') | |||||
expect(input).not.toBeNull() | |||||
render(<TextInput label="" name="" />) | |||||
expect(screen.getByRole('textbox')).toBeInTheDocument() | |||||
}) | }) | ||||
it('should acquire a descriptive label', () => { | it('should acquire a descriptive label', () => { | ||||
const label = 'foo' | const label = 'foo' | ||||
const result = render(<TextInput label={label} name="" />) | |||||
const input = result.queryByLabelText(label) | |||||
expect(input).not.toBeNull() | |||||
render(<TextInput label={label} name="" />) | |||||
expect(screen.getByRole('textbox', { name: label, })).toBeInTheDocument() | |||||
}) | }) | ||||
}) | }) |
@@ -1,21 +1,15 @@ | |||||
import {render, cleanup} from '@testing-library/react' | |||||
import {render, screen} from '@testing-library/react' | |||||
import TextInput from '.' | import TextInput from '.' | ||||
describe('single-line text input component', () => { | describe('single-line text input component', () => { | ||||
afterEach(() => { | |||||
cleanup() | |||||
}) | |||||
it('should contain a text input element', () => { | it('should contain a text input element', () => { | ||||
const result = render(<TextInput label="" name="" />) | |||||
const input = result.queryByRole('textbox') | |||||
expect(input).not.toBeNull() | |||||
render(<TextInput label="" name="" />) | |||||
expect(screen.getByRole('textbox')).toBeInTheDocument() | |||||
}) | }) | ||||
it('should acquire a descriptive label', () => { | it('should acquire a descriptive label', () => { | ||||
const label = 'foo' | const label = 'foo' | ||||
const result = render(<TextInput label={label} name="" />) | |||||
const input = result.queryByLabelText(label) | |||||
expect(input).not.toBeNull() | |||||
render(<TextInput label={label} name="" />) | |||||
expect(screen.getByLabelText(label)).toBeInTheDocument() | |||||
}) | }) | ||||
}) | }) |
@@ -0,0 +1,15 @@ | |||||
import { render, screen } from '@testing-library/react' | |||||
import Link from '.' | |||||
describe('link component', () => { | |||||
it('should render an anchor element', () => { | |||||
render( | |||||
<Link | |||||
href={{ | |||||
pathname: 'foo', | |||||
}} | |||||
/> | |||||
) | |||||
expect(screen.getByRole('link')).toBeInTheDocument() | |||||
}) | |||||
}) |
@@ -0,0 +1,33 @@ | |||||
import {render, screen} from '@testing-library/react' | |||||
import CreateRingtoneForm from '.' | |||||
describe('form for creating ringtones', () => { | |||||
beforeEach(() => { | |||||
render( | |||||
<CreateRingtoneForm | |||||
labels={{ | |||||
form: 'Create Ringtone', | |||||
name: 'Name', | |||||
data: 'Data', | |||||
cta: 'Post', | |||||
}} | |||||
/> | |||||
) | |||||
}) | |||||
it('should render a form', () => { | |||||
expect(screen.getByRole('form', { name: 'Create Ringtone' })).toBeInTheDocument() | |||||
}) | |||||
it('should render a textbox for the ringtone name', () => { | |||||
expect(screen.getByRole('textbox', { name: 'Name' })).toBeInTheDocument() | |||||
}) | |||||
it('should render a textbox for the ringtone data', () => { | |||||
expect(screen.getByRole('textbox', { name: 'Data' })).toBeInTheDocument() | |||||
}) | |||||
it('should render a button for submitting the ringtone data', () => { | |||||
expect(screen.getByRole('button', { name: 'Post' })).toBeInTheDocument() | |||||
}) | |||||
}) |
@@ -10,25 +10,38 @@ const Form = styled('form')({ | |||||
}) | }) | ||||
type Props = { | type Props = { | ||||
onSubmit?: FormEventHandler | |||||
onSubmit?: FormEventHandler, | |||||
action?: string, | |||||
labels: Record<string, string>, | |||||
} | } | ||||
const CreateRingtoneForm: FC<Props> = ({ | const CreateRingtoneForm: FC<Props> = ({ | ||||
onSubmit, | onSubmit, | ||||
action, | |||||
labels, | |||||
}) => { | }) => { | ||||
return ( | return ( | ||||
<Form | <Form | ||||
onSubmit={onSubmit} | onSubmit={onSubmit} | ||||
method="post" | method="post" | ||||
action="/api/a/create/ringtone" | |||||
action={action} | |||||
aria-label={labels['form']} | |||||
> | > | ||||
<TextInput label="Name" name="name" block /> | |||||
<TextArea label="Data" name="data" block /> | |||||
<TextInput | |||||
label={labels['name'] || 'Name'} | |||||
name="name" | |||||
block | |||||
/> | |||||
<TextArea | |||||
label={labels['data'] || 'Data'} | |||||
name="data" | |||||
block | |||||
/> | |||||
<ActionButton | <ActionButton | ||||
type="submit" | type="submit" | ||||
block | block | ||||
> | > | ||||
Post | |||||
{labels['cta'] || 'Post'} | |||||
</ActionButton> | </ActionButton> | ||||
</Form> | </Form> | ||||
) | ) | ||||
@@ -0,0 +1,27 @@ | |||||
import {render, screen} from '@testing-library/react' | |||||
import OmnisearchForm from '.' | |||||
describe('form for searching everything', () => { | |||||
beforeEach(() => { | |||||
render( | |||||
<OmnisearchForm | |||||
labels={{ | |||||
form: 'Search', | |||||
query: 'Query', | |||||
}} | |||||
/> | |||||
) | |||||
}) | |||||
it('should render a form', () => { | |||||
expect(screen.getByRole('form', { name: 'Search' })).toBeInTheDocument() | |||||
}) | |||||
it('should render a textbox for the search query', () => { | |||||
expect(screen.getByRole('textbox', { name: 'Query' })).toBeInTheDocument() | |||||
}) | |||||
it('should render a button for submitting the search query', () => { | |||||
expect(screen.getByRole('button', { name: 'Search' })).toBeInTheDocument() | |||||
}) | |||||
}) |
@@ -0,0 +1,38 @@ | |||||
import {FC, FormEventHandler} from 'react' | |||||
import TextInput from '../../../molecules/forms/TextInput' | |||||
import ActionButton from '../../../molecules/forms/ActionButton'; | |||||
type Props = { | |||||
onSubmit?: FormEventHandler, | |||||
action?: string, | |||||
labels: Record<string, string>, | |||||
} | |||||
const OmnisearchForm: FC<Props> = ({ | |||||
onSubmit, | |||||
labels, | |||||
action, | |||||
}) => { | |||||
return ( | |||||
<form | |||||
method="get" | |||||
onSubmit={onSubmit} | |||||
action={action} | |||||
aria-label={labels['form']} | |||||
> | |||||
<TextInput | |||||
label={labels['query'] || 'Query'} | |||||
name="q" | |||||
block | |||||
/> | |||||
<ActionButton | |||||
type="submit" | |||||
block | |||||
> | |||||
Search | |||||
</ActionButton> | |||||
</form> | |||||
) | |||||
} | |||||
export default OmnisearchForm |
@@ -2,16 +2,29 @@ import {FC, FormEventHandler} from 'react' | |||||
import { LeftSidebarWithMenu } from '@tesseract-design/viewfinder' | import { LeftSidebarWithMenu } from '@tesseract-design/viewfinder' | ||||
import CreateRingtoneForm from '../../organisms/forms/CreateRingtone' | import CreateRingtoneForm from '../../organisms/forms/CreateRingtone' | ||||
import Link from '../../molecules/navigation/Link' | import Link from '../../molecules/navigation/Link' | ||||
import OmnisearchForm from '../../organisms/forms/Omnisearch' | |||||
type Props = { | type Props = { | ||||
onSearch?: FormEventHandler, | |||||
onSubmit?: FormEventHandler | onSubmit?: FormEventHandler | ||||
} | } | ||||
const CreateRingtoneTemplate: FC<Props> = ({ | const CreateRingtoneTemplate: FC<Props> = ({ | ||||
onSearch, | |||||
onSubmit, | onSubmit, | ||||
}) => { | }) => { | ||||
return ( | return ( | ||||
<LeftSidebarWithMenu.Layout | <LeftSidebarWithMenu.Layout | ||||
topBarCenter={ | |||||
<OmnisearchForm | |||||
labels={{ | |||||
form: 'Search', | |||||
query: 'Query', | |||||
}} | |||||
onSubmit={onSearch} | |||||
action="/api/a/search" | |||||
/> | |||||
} | |||||
linkComponent={({ url, icon, label, }) => ( | linkComponent={({ url, icon, label, }) => ( | ||||
<Link | <Link | ||||
href={url} | href={url} | ||||
@@ -68,6 +81,13 @@ const CreateRingtoneTemplate: FC<Props> = ({ | |||||
<LeftSidebarWithMenu.ContentContainer> | <LeftSidebarWithMenu.ContentContainer> | ||||
<CreateRingtoneForm | <CreateRingtoneForm | ||||
onSubmit={onSubmit} | onSubmit={onSubmit} | ||||
action="/api/a/create/ringtone" | |||||
labels={{ | |||||
form: 'Create Ringtone', | |||||
name: 'Name', | |||||
data: 'Data', | |||||
cta: 'Post', | |||||
}} | |||||
/> | /> | ||||
</LeftSidebarWithMenu.ContentContainer> | </LeftSidebarWithMenu.ContentContainer> | ||||
</LeftSidebarWithMenu.Layout> | </LeftSidebarWithMenu.Layout> | ||||
@@ -0,0 +1,62 @@ | |||||
import {URLSearchParams} from 'url'; | |||||
enum Method { | |||||
HEAD = 'HEAD', | |||||
GET = 'GET', | |||||
POST = 'POST', | |||||
PUT = 'PUT', | |||||
PATCH = 'PATCH', | |||||
DELETE = 'DELETE', | |||||
} | |||||
type Fetch = (input: RequestInfo, init?: RequestInit) => Promise<Response> | |||||
type CreateFetchClientOptions = { | |||||
baseUrl: string, | |||||
headers?: HeadersInit, | |||||
fetch: Fetch, | |||||
} | |||||
export type FetchClientParams = { | |||||
url: string, | |||||
method: Method, | |||||
body?: BodyInit, | |||||
query?: URLSearchParams | string | NodeJS.Dict<string | ReadonlyArray<string>> | Iterable<[string, string]> | ReadonlyArray<[string, string]>, | |||||
headers?: HeadersInit, | |||||
fetch?: Fetch, | |||||
} | |||||
type FetchClient = (params: FetchClientParams) => Promise<Response> | |||||
type CreateFetchClient = (options: CreateFetchClientOptions) => FetchClient | |||||
export const createFetchClient: CreateFetchClient = ({ | |||||
baseUrl, | |||||
headers: defaultHeaders = {}, | |||||
fetch: f = fetch, | |||||
}) => { | |||||
return ({ | |||||
url, | |||||
method, | |||||
body, | |||||
query, | |||||
headers = {}, | |||||
fetch: ff = f, | |||||
}) => { | |||||
const theUrl = new URL(url, baseUrl) | |||||
if (Boolean(query as unknown)) { | |||||
theUrl.search = new URLSearchParams(query).toString() | |||||
} | |||||
return ff(theUrl.toString(), { | |||||
method, | |||||
body, | |||||
// TODO handle any kind of headers init | |||||
headers: { | |||||
...defaultHeaders, | |||||
...headers, | |||||
}, | |||||
}) | |||||
} | |||||
} | |||||
export const createMockFetch = (body: BodyInit, response: ResponseInit): Fetch => async () => new Response(body, response) |
@@ -1,7 +1,12 @@ | |||||
{ | { | ||||
"extends": "./tsconfig.json", | |||||
"compilerOptions": { | |||||
"jsx": "react-jsx", | |||||
"types": ["jest"] | |||||
}, | |||||
"extends": "./tsconfig.json", | |||||
"include": [ | |||||
"./jest.setup.ts" | |||||
], | |||||
"compilerOptions": { | |||||
"jsx": "react-jsx", | |||||
"types": [ | |||||
"jest" | |||||
] | |||||
} | |||||
} | } |
@@ -280,7 +280,7 @@ | |||||
dependencies: | dependencies: | ||||
regenerator-runtime "^0.13.4" | regenerator-runtime "^0.13.4" | ||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5": | |||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.9.2": | |||||
version "7.14.0" | version "7.14.0" | ||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" | ||||
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== | integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== | ||||
@@ -697,6 +697,20 @@ | |||||
lz-string "^1.4.4" | lz-string "^1.4.4" | ||||
pretty-format "^26.6.2" | pretty-format "^26.6.2" | ||||
"@testing-library/jest-dom@^5.12.0": | |||||
version "5.12.0" | |||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.12.0.tgz#6a5d340b092c44b7bce17a4791b47d9bc2c61443" | |||||
integrity sha512-N9Y82b2Z3j6wzIoAqajlKVF1Zt7sOH0pPee0sUHXHc5cv2Fdn23r+vpWm0MBBoGJtPOly5+Bdx1lnc3CD+A+ow== | |||||
dependencies: | |||||
"@babel/runtime" "^7.9.2" | |||||
"@types/testing-library__jest-dom" "^5.9.1" | |||||
aria-query "^4.2.2" | |||||
chalk "^3.0.0" | |||||
css "^3.0.0" | |||||
css.escape "^1.5.1" | |||||
lodash "^4.17.15" | |||||
redent "^3.0.0" | |||||
"@testing-library/react@^11.2.6": | "@testing-library/react@^11.2.6": | ||||
version "11.2.6" | version "11.2.6" | ||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b" | resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b" | ||||
@@ -782,7 +796,7 @@ | |||||
dependencies: | dependencies: | ||||
"@types/istanbul-lib-report" "*" | "@types/istanbul-lib-report" "*" | ||||
"@types/jest@^26.0.23": | |||||
"@types/jest@*", "@types/jest@^26.0.23": | |||||
version "26.0.23" | version "26.0.23" | ||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" | resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" | ||||
integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== | integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== | ||||
@@ -853,6 +867,13 @@ | |||||
"@types/react" "*" | "@types/react" "*" | ||||
csstype "^3.0.2" | csstype "^3.0.2" | ||||
"@types/testing-library__jest-dom@^5.9.1": | |||||
version "5.9.5" | |||||
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0" | |||||
integrity sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ== | |||||
dependencies: | |||||
"@types/jest" "*" | |||||
"@types/yargs-parser@*": | "@types/yargs-parser@*": | ||||
version "20.2.0" | version "20.2.0" | ||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" | resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" | ||||
@@ -1516,6 +1537,14 @@ chalk@^1.0.0, chalk@^1.1.3: | |||||
strip-ansi "^3.0.0" | strip-ansi "^3.0.0" | ||||
supports-color "^2.0.0" | supports-color "^2.0.0" | ||||
chalk@^3.0.0: | |||||
version "3.0.0" | |||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" | |||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== | |||||
dependencies: | |||||
ansi-styles "^4.1.0" | |||||
supports-color "^7.1.0" | |||||
chalk@^4.0.0, chalk@^4.1.0: | chalk@^4.0.0, chalk@^4.1.0: | ||||
version "4.1.1" | version "4.1.1" | ||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" | ||||
@@ -1841,11 +1870,20 @@ css-to-react-native@^3.0.0: | |||||
css-color-keywords "^1.0.0" | css-color-keywords "^1.0.0" | ||||
postcss-value-parser "^4.0.2" | postcss-value-parser "^4.0.2" | ||||
css.escape@1.5.1: | |||||
css.escape@1.5.1, css.escape@^1.5.1: | |||||
version "1.5.1" | version "1.5.1" | ||||
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" | resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" | ||||
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= | integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= | ||||
css@^3.0.0: | |||||
version "3.0.0" | |||||
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" | |||||
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== | |||||
dependencies: | |||||
inherits "^2.0.4" | |||||
source-map "^0.6.1" | |||||
source-map-resolve "^0.6.0" | |||||
cssnano-preset-simple@^2.0.0: | cssnano-preset-simple@^2.0.0: | ||||
version "2.0.0" | version "2.0.0" | ||||
resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-2.0.0.tgz#b55e72cb970713f425560a0e141b0335249e2f96" | resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-2.0.0.tgz#b55e72cb970713f425560a0e141b0335249e2f96" | ||||
@@ -2840,6 +2878,11 @@ indent-string@^3.0.0: | |||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" | ||||
integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= | integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= | ||||
indent-string@^4.0.0: | |||||
version "4.0.0" | |||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" | |||||
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== | |||||
inflight@^1.0.4: | inflight@^1.0.4: | ||||
version "1.0.6" | version "1.0.6" | ||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" | ||||
@@ -3847,7 +3890,7 @@ lodash.sortby@^4.7.0: | |||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" | resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" | ||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= | integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= | ||||
lodash@4.x, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: | |||||
lodash@4.x, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: | |||||
version "4.17.21" | version "4.17.21" | ||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" | ||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== | ||||
@@ -3997,6 +4040,11 @@ mimic-fn@^2.1.0: | |||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" | ||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== | ||||
min-indent@^1.0.0: | |||||
version "1.0.1" | |||||
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" | |||||
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== | |||||
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: | minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: | ||||
version "1.0.1" | version "1.0.1" | ||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" | resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" | ||||
@@ -4777,6 +4825,14 @@ readdirp@~3.5.0: | |||||
dependencies: | dependencies: | ||||
picomatch "^2.2.1" | picomatch "^2.2.1" | ||||
redent@^3.0.0: | |||||
version "3.0.0" | |||||
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" | |||||
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== | |||||
dependencies: | |||||
indent-string "^4.0.0" | |||||
strip-indent "^3.0.0" | |||||
regenerator-runtime@^0.13.4: | regenerator-runtime@^0.13.4: | ||||
version "0.13.7" | version "0.13.7" | ||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" | ||||
@@ -5139,6 +5195,14 @@ source-map-resolve@^0.5.0: | |||||
source-map-url "^0.4.0" | source-map-url "^0.4.0" | ||||
urix "^0.1.0" | urix "^0.1.0" | ||||
source-map-resolve@^0.6.0: | |||||
version "0.6.0" | |||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" | |||||
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== | |||||
dependencies: | |||||
atob "^2.1.2" | |||||
decode-uri-component "^0.2.0" | |||||
source-map-support@^0.5.6: | source-map-support@^0.5.6: | ||||
version "0.5.19" | version "0.5.19" | ||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" | ||||
@@ -5408,6 +5472,13 @@ strip-final-newline@^2.0.0: | |||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" | resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" | ||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== | integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== | ||||
strip-indent@^3.0.0: | |||||
version "3.0.0" | |||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" | |||||
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== | |||||
dependencies: | |||||
min-indent "^1.0.0" | |||||
styled-components@^5.3.0: | styled-components@^5.3.0: | ||||
version "5.3.0" | version "5.3.0" | ||||
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.0.tgz#e47c3d3e9ddfff539f118a3dd0fd4f8f4fb25727" | resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.0.tgz#e47c3d3e9ddfff539f118a3dd0fd4f8f4fb25727" | ||||