Browse Source

Set up tests and components

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
TheoryOfNekomata 3 years ago
parent
commit
8641177528
16 changed files with 329 additions and 61 deletions
  1. +9
    -13
      REQUIREMENTS.md
  2. +2
    -1
      packages/app-web/jest.config.js
  3. +1
    -0
      packages/app-web/jest.setup.ts
  4. +1
    -0
      packages/app-web/package.json
  5. +8
    -11
      packages/app-web/src/components/molecules/forms/ActionButton/index.test.tsx
  6. +5
    -11
      packages/app-web/src/components/molecules/forms/TextArea/index.test.tsx
  7. +5
    -11
      packages/app-web/src/components/molecules/forms/TextInput/index.test.tsx
  8. +15
    -0
      packages/app-web/src/components/molecules/navigation/Link/index.test.tsx
  9. +33
    -0
      packages/app-web/src/components/organisms/forms/CreateRingtone/index.test.tsx
  10. +18
    -5
      packages/app-web/src/components/organisms/forms/CreateRingtone/index.tsx
  11. +27
    -0
      packages/app-web/src/components/organisms/forms/Omnisearch/index.test.tsx
  12. +38
    -0
      packages/app-web/src/components/organisms/forms/Omnisearch/index.tsx
  13. +20
    -0
      packages/app-web/src/components/templates/CreateRingtone/index.tsx
  14. +62
    -0
      packages/app-web/src/utils/api/fetch.ts
  15. +10
    -5
      packages/app-web/tsconfig.test.json
  16. +75
    -4
      packages/app-web/yarn.lock

+ 9
- 13
REQUIREMENTS.md View File

@@ -1,30 +1,26 @@
# Requirements

- 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.
- As a client, I want to log out from the service.
- [ ] 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.
- 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.
- [ ] 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.
- 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.
- [ ] 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 back-end, the server stores the 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 back-end, the server stores the 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.

+ 2
- 1
packages/app-web/jest.config.js View File

@@ -10,5 +10,6 @@ module.exports = {
'src/components/**/*.{jsx,tsx}',
'src/modules/**/*.{js,ts}',
'src/utils/**/*.{js,ts}',
]
],
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts']
};

+ 1
- 0
packages/app-web/jest.setup.ts View File

@@ -0,0 +1 @@
import '@testing-library/jest-dom'

+ 1
- 0
packages/app-web/package.json View File

@@ -18,6 +18,7 @@
"styled-components": "^5.3.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.6",
"@types/jest": "^26.0.23",
"@types/node": "^15.0.2",


+ 8
- 11
packages/app-web/src/components/molecules/forms/ActionButton/index.test.tsx View File

@@ -1,22 +1,19 @@
import {cleanup, render} from '@testing-library/react'
import {render, screen} from '@testing-library/react'
import ActionButton from '.'

describe('button component for triggering actions', () => {
afterEach(() => {
cleanup()
})

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) => {
beforeEach(() => {
render(<ActionButton type={type} />)
})

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)
})
})
})

+ 5
- 11
packages/app-web/src/components/molecules/forms/TextArea/index.test.tsx View File

@@ -1,21 +1,15 @@
import {render, cleanup} from '@testing-library/react'
import {render, screen} from '@testing-library/react'
import TextInput from '.'

describe('single-line text input component', () => {
afterEach(() => {
cleanup()
})

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', () => {
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()
})
})

+ 5
- 11
packages/app-web/src/components/molecules/forms/TextInput/index.test.tsx View File

@@ -1,21 +1,15 @@
import {render, cleanup} from '@testing-library/react'
import {render, screen} from '@testing-library/react'
import TextInput from '.'

describe('single-line text input component', () => {
afterEach(() => {
cleanup()
})

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', () => {
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()
})
})

+ 15
- 0
packages/app-web/src/components/molecules/navigation/Link/index.test.tsx View File

@@ -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()
})
})

+ 33
- 0
packages/app-web/src/components/organisms/forms/CreateRingtone/index.test.tsx View File

@@ -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()
})
})

+ 18
- 5
packages/app-web/src/components/organisms/forms/CreateRingtone/index.tsx View File

@@ -10,25 +10,38 @@ const Form = styled('form')({
})

type Props = {
onSubmit?: FormEventHandler
onSubmit?: FormEventHandler,
action?: string,
labels: Record<string, string>,
}

const CreateRingtoneForm: FC<Props> = ({
onSubmit,
action,
labels,
}) => {
return (
<Form
onSubmit={onSubmit}
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
type="submit"
block
>
Post
{labels['cta'] || 'Post'}
</ActionButton>
</Form>
)


+ 27
- 0
packages/app-web/src/components/organisms/forms/Omnisearch/index.test.tsx View File

@@ -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()
})
})

+ 38
- 0
packages/app-web/src/components/organisms/forms/Omnisearch/index.tsx View File

@@ -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

+ 20
- 0
packages/app-web/src/components/templates/CreateRingtone/index.tsx View File

@@ -2,16 +2,29 @@ import {FC, FormEventHandler} from 'react'
import { LeftSidebarWithMenu } from '@tesseract-design/viewfinder'
import CreateRingtoneForm from '../../organisms/forms/CreateRingtone'
import Link from '../../molecules/navigation/Link'
import OmnisearchForm from '../../organisms/forms/Omnisearch'

type Props = {
onSearch?: FormEventHandler,
onSubmit?: FormEventHandler
}

const CreateRingtoneTemplate: FC<Props> = ({
onSearch,
onSubmit,
}) => {
return (
<LeftSidebarWithMenu.Layout
topBarCenter={
<OmnisearchForm
labels={{
form: 'Search',
query: 'Query',
}}
onSubmit={onSearch}
action="/api/a/search"
/>
}
linkComponent={({ url, icon, label, }) => (
<Link
href={url}
@@ -68,6 +81,13 @@ const CreateRingtoneTemplate: FC<Props> = ({
<LeftSidebarWithMenu.ContentContainer>
<CreateRingtoneForm
onSubmit={onSubmit}
action="/api/a/create/ringtone"
labels={{
form: 'Create Ringtone',
name: 'Name',
data: 'Data',
cta: 'Post',
}}
/>
</LeftSidebarWithMenu.ContentContainer>
</LeftSidebarWithMenu.Layout>


+ 62
- 0
packages/app-web/src/utils/api/fetch.ts View File

@@ -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)

+ 10
- 5
packages/app-web/tsconfig.test.json View File

@@ -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"
]
}
}

+ 75
- 4
packages/app-web/yarn.lock View File

@@ -280,7 +280,7 @@
dependencies:
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"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
@@ -697,6 +697,20 @@
lz-string "^1.4.4"
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":
version "11.2.6"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b"
@@ -782,7 +796,7 @@
dependencies:
"@types/istanbul-lib-report" "*"

"@types/jest@^26.0.23":
"@types/jest@*", "@types/jest@^26.0.23":
version "26.0.23"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7"
integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==
@@ -853,6 +867,13 @@
"@types/react" "*"
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@*":
version "20.2.0"
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"
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:
version "4.1.1"
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"
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"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=

css@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
dependencies:
inherits "^2.0.4"
source-map "^0.6.1"
source-map-resolve "^0.6.0"

cssnano-preset-simple@^2.0.0:
version "2.0.0"
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"
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:
version "1.0.6"
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"
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"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
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"
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:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@@ -4777,6 +4825,14 @@ readdirp@~3.5.0:
dependencies:
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:
version "0.13.7"
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"
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:
version "0.5.19"
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"
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:
version "5.3.0"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.0.tgz#e47c3d3e9ddfff539f118a3dd0fd4f8f4fb25727"


Loading…
Cancel
Save