diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..9d0ace3 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "plugins": [], + "presets": ["next/babel", "@babel/preset-typescript"] +} \ No newline at end of file diff --git a/package.json b/package.json index 73d05f0..bbc6bff 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,16 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" }, "dependencies": { + "@babel/preset-typescript": "^7.16.7", "@heroicons/react": "^1.0.5", "axios": "^0.26.0", + "html2canvas": "^1.4.1", "next": "12.1.0", "nextjs-progressbar": "^0.0.13", "nookies": "^2.5.2", diff --git a/src/components/audio-player/audio-player.spec.tsx b/src/components/audio-player/audio-player.spec.tsx new file mode 100644 index 0000000..2b90f99 --- /dev/null +++ b/src/components/audio-player/audio-player.spec.tsx @@ -0,0 +1,81 @@ +import { render, act } from '@testing-library/react' +import userEvent from '@testing-library/user-event' + +import { AudioPlayer } from './audio-player' + +const Component = () => + +describe('', () => { + const iconPath = 'div > div > svg > path' + + const playMock = jest.fn() + const pauseMock = jest.fn() + const volumeMock = jest.fn() + + const useAudioMock = jest.fn().mockReturnValue([ + Component, + { playing: false }, + { + volume: volumeMock, + pause: pauseMock, + play: playMock + } + ]) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should be render a AudioPlayer component', () => { + const { container } = render( + + ) + + expect(useAudioMock).toHaveBeenCalled() + expect(volumeMock).toHaveBeenCalled() + + expect(container.querySelector(iconPath)).toBeInTheDocument() + }) + + it('should be able to click in the play button', () => { + const { container } = render( + + ) + + expect(useAudioMock).toHaveBeenCalled() + + const playButton = container.querySelector(iconPath) + + act(() => { + if (playButton) userEvent.click(playButton) + }) + + expect(playMock).toHaveBeenCalled() + }) + + it('should be able to click in the pause button', () => { + const useAudioMock = jest.fn().mockReturnValue([ + jest.fn(), + { playing: true }, + { + volume: volumeMock, + pause: pauseMock, + play: playMock + } + ]) + + const { container } = render( + + ) + + expect(useAudioMock).toHaveBeenCalled() + + const playButton = container.querySelector(iconPath) + + act(() => { + if (playButton) userEvent.click(playButton) + }) + + expect(pauseMock).toHaveBeenCalled() + }) +}) diff --git a/src/components/audio-player/audio-player.tsx b/src/components/audio-player/audio-player.tsx index 8c7cf17..60f7642 100644 --- a/src/components/audio-player/audio-player.tsx +++ b/src/components/audio-player/audio-player.tsx @@ -1,12 +1,16 @@ import { useEffect } from 'react' -import { useAudio } from 'react-use' +import { useAudio as ReactUseAudio } from 'react-use' import { PlayIcon, PauseIcon } from '@heroicons/react/solid' type AudioPlayerProps = { src: string + useAudio?: typeof ReactUseAudio } -export const AudioPlayer = ({ src }: AudioPlayerProps) => { +export const AudioPlayer = ({ + src, + useAudio = ReactUseAudio +}: AudioPlayerProps) => { const [audio, state, controls] = useAudio({ src: src, autoPlay: false @@ -24,6 +28,7 @@ export const AudioPlayer = ({ src }: AudioPlayerProps) => { {audio} {state.playing ? ( { /> ) : ( ', () => { + it('should be render a Content component', () => { + render( + +

hello content testing

+
+ ) + + expect( + screen.getByRole('heading', { + name: /hello content testing/i + }) + ).toBeInTheDocument() + }) +}) diff --git a/src/components/grid/grid.spec.tsx b/src/components/grid/grid.spec.tsx new file mode 100644 index 0000000..1e9a95a --- /dev/null +++ b/src/components/grid/grid.spec.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@testing-library/react' +import { Grid } from './grid' + +describe('', () => { + it('should be render a Grid component', () => { + render( + +

hello grid testing

+
+ ) + + expect( + screen.getByRole('heading', { + name: /hello grid testing/i + }) + ).toBeInTheDocument() + }) +}) diff --git a/src/components/html-to-image/html-to-image.spec.tsx b/src/components/html-to-image/html-to-image.spec.tsx new file mode 100644 index 0000000..bb282fc --- /dev/null +++ b/src/components/html-to-image/html-to-image.spec.tsx @@ -0,0 +1,182 @@ +import { render, screen, act } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { HTMLToImage } from './html-to-image' + +//TODO: MOVE THIS TO A MOCK DIRECTORY +const artistsMock = [ + { + id: '7zejo99XCAwPzycPCCaoM8', + images: [ + { + height: 640, + url: 'https://i.scdn.co/image/ab67616d0000b27355fd23d477d647c595c8ce5a', + width: 640 + }, + { + height: 300, + url: 'https://i.scdn.co/image/ab67616d00001e0255fd23d477d647c595c8ce5a', + width: 300 + }, + { + height: 64, + url: 'https://i.scdn.co/image/ab67616d0000485155fd23d477d647c595c8ce5a', + width: 64 + } + ], + type: 'artist', + name: 'Michael Nyman Band', + track: { + name: 'Fish Beach', + previewUrl: + 'https://p.scdn.co/mp3-preview/4fb1c7db76d70da33d74db84f6863b1d4dc582ab?cid=c400423b160b43dcbb941e61d7d4cd60' + } + }, + { + id: '0s1ec6aPpRZ4DCj15w1EFg', + images: [ + { + height: 1000, + url: 'https://i.scdn.co/image/549e391d35cccd6f7eb602102dd1378d8afeb06a', + width: 1000 + }, + { + height: 640, + url: 'https://i.scdn.co/image/7390a60d0952bc2bf22ba4cb7c4f20f8bf6cb597', + width: 640 + }, + { + height: 200, + url: 'https://i.scdn.co/image/8b2da52578b6a565da52ae34db2aea368f1fdce0', + width: 200 + }, + { + height: 64, + url: 'https://i.scdn.co/image/3d19a0ded34b4ff9b4cac7ab77a15f26c2b0d7e6', + width: 64 + } + ], + type: 'artist', + name: 'Dario Marianelli', + track: { + name: 'Someone Loves Us', + previewUrl: + 'https://p.scdn.co/mp3-preview/9be2d96c8237bbc39b1778e66f917a27363a9e22?cid=c400423b160b43dcbb941e61d7d4cd60' + } + }, + { + id: '3gGbSXSwHWmrUBIG9IUAau', + images: [ + { + height: 640, + url: 'https://i.scdn.co/image/ab6761610000e5eb2c64d38027090d363ea36044', + width: 640 + }, + { + height: 320, + url: 'https://i.scdn.co/image/ab676161000051742c64d38027090d363ea36044', + width: 320 + }, + { + height: 160, + url: 'https://i.scdn.co/image/ab6761610000f1782c64d38027090d363ea36044', + width: 160 + } + ], + type: 'artist', + name: 'Zbigniew Preisner', + track: { + name: 'Les marionnettes', + previewUrl: + 'https://p.scdn.co/mp3-preview/5ef82ca0e722ef6e51359140ba4d5a319eb35383?cid=c400423b160b43dcbb941e61d7d4cd60' + } + }, + { + id: '3gGbSXSwHWmrUBIG9IUAau2', + images: [ + { + height: 640, + url: 'https://i.scdn.co/image/ab6761610000e5eb2c64d38027090d363ea36044', + width: 640 + }, + { + height: 320, + url: 'https://i.scdn.co/image/ab676161000051742c64d38027090d363ea36044', + width: 320 + }, + { + height: 160, + url: 'https://i.scdn.co/image/ab6761610000f1782c64d38027090d363ea36044', + width: 160 + } + ], + type: 'artist', + name: 'Zbigniew Preisner', + track: { + name: 'Les marionnettes', + previewUrl: + 'https://p.scdn.co/mp3-preview/5ef82ca0e722ef6e51359140ba4d5a319eb35383?cid=c400423b160b43dcbb941e61d7d4cd60' + } + } +] + +describe('', () => { + it('should be render a HTMLToImage component', () => { + render( + + ) + + expect( + screen.getByRole('heading', { + name: /you can download your randomfy!/i + }) + ).toBeInTheDocument() + + expect( + screen.getByRole('button', { + name: /download button/i + }) + ).toBeInTheDocument() + }) + + it('should be return null when artists lenght less than nine', () => { + const { container } = render() + + expect(container.firstChild).toBeNull() + }) + + it('should be able to download the generated image', () => { + const html2canvasMock = jest + .fn() + .mockResolvedValue({ toDataURL: jest.fn() }) + render( + + ) + + const downloadButton = screen.getByRole('button', { + name: /download button/i + }) + + act(() => { + userEvent.click(downloadButton) + }) + + expect(html2canvasMock).toHaveBeenCalled() + }) +}) diff --git a/src/components/html-to-image/html-to-image.tsx b/src/components/html-to-image/html-to-image.tsx new file mode 100644 index 0000000..93e91c9 --- /dev/null +++ b/src/components/html-to-image/html-to-image.tsx @@ -0,0 +1,71 @@ +import Image from 'next/image' +import html2canvasExternal from 'html2canvas' +import { SubTitle, Title } from 'components' +import { Recommendation } from 'types' +import { MAX_RANDOM_FY_ITEMS } from '../../constants' + +type HTMLToImageProps = { + artists: Array + html2canvas?: typeof html2canvasExternal +} + +export const HTMLToImage = ({ + artists, + html2canvas = html2canvasExternal +}: HTMLToImageProps) => { + const printIMG = () => { + const domElement = document.getElementById('grid') + + if (!domElement) return + + html2canvas(domElement, { + onclone: (document) => { + const button = document.getElementById('download') + const subtitle = document.getElementById('subtitle') + if (button && subtitle) { + button.style.visibility = 'hidden' + subtitle.style.visibility = 'hidden' + } + } + }).then((canvas) => { + const image = canvas.toDataURL('png') + const a = document.createElement('a') + a.setAttribute('download', 'random-fy.png') + a.setAttribute('href', image) + a.click() + }) + } + + return artists.length >= MAX_RANDOM_FY_ITEMS ? ( +
+
+ Randomfy +
+
+ You can download your randomfy! +
+
+ {artists.map((artist, index) => ( + {artist.name} + ))} +
+
+ +
+
+ ) : null +} diff --git a/src/components/html-to-image/index.ts b/src/components/html-to-image/index.ts new file mode 100644 index 0000000..3e2e1a0 --- /dev/null +++ b/src/components/html-to-image/index.ts @@ -0,0 +1 @@ +export { HTMLToImage } from './html-to-image' diff --git a/src/components/image-box/image-box.spec.tsx b/src/components/image-box/image-box.spec.tsx new file mode 100644 index 0000000..090c959 --- /dev/null +++ b/src/components/image-box/image-box.spec.tsx @@ -0,0 +1,49 @@ +import { render, screen, act } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { ImageBox } from './image-box' + +describe('', () => { + const heartIconPath = 'div > div:nth-child(2) > svg > path' + const onLikeMock = jest.fn() + + it('should be render a ImageBox component', () => { + const { container } = render( + + ) + + expect( + screen.getByRole('img', { + name: /artist test/i + }) + ).toBeInTheDocument() + expect(screen.getByText(/artist test/i)).toBeInTheDocument() + expect(screen.getByText(/track test/i)).toBeInTheDocument() + + const heartButton = container.querySelector(heartIconPath) + expect(heartButton).toBeInTheDocument() + }) + + it('should be to click on like', () => { + const { container } = render( + + ) + + const heartButton = container.querySelector(heartIconPath) + act(() => { + if (heartButton) userEvent.click(heartButton) + }) + expect(onLikeMock).toHaveBeenCalled() + }) +}) diff --git a/src/components/image-box/image-box.tsx b/src/components/image-box/image-box.tsx index 97625da..bee27b3 100644 --- a/src/components/image-box/image-box.tsx +++ b/src/components/image-box/image-box.tsx @@ -40,6 +40,7 @@ export const ImageBox = ({
', () => { + it('should be render a SubTitle component', () => { + render(hello subtitle testing ) + + expect( + screen.getByRole('heading', { + name: /hello subtitle testing/i + }) + ).toBeInTheDocument() + }) +}) diff --git a/src/components/title/title.spec.tsx b/src/components/title/title.spec.tsx new file mode 100644 index 0000000..9ac1463 --- /dev/null +++ b/src/components/title/title.spec.tsx @@ -0,0 +1,14 @@ +import { render, screen } from '@testing-library/react' +import { Title } from './title' + +describe('', () => { + it('should be render a Title component', () => { + render(<Title>hello title testing ) + + expect( + screen.getByRole('heading', { + name: /hello title testing/i + }) + ).toBeInTheDocument() + }) +}) diff --git a/src/constants/index.ts b/src/constants/index.ts index ab75f18..ca34b4e 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1 +1,2 @@ export { scopes } from './scopes' +export { MAX_RANDOM_FY_ITEMS } from './values' diff --git a/src/constants/values.ts b/src/constants/values.ts new file mode 100644 index 0000000..759ca9a --- /dev/null +++ b/src/constants/values.ts @@ -0,0 +1 @@ +export const MAX_RANDOM_FY_ITEMS = 20 diff --git a/src/templates/home/authenticated.tsx b/src/templates/home/authenticated.tsx index c51344c..eed6c08 100644 --- a/src/templates/home/authenticated.tsx +++ b/src/templates/home/authenticated.tsx @@ -1,39 +1,58 @@ -import { Content, Grid, ImageBox, ImageBoxSkeleton, Title } from 'components' -import { useRecommendation } from 'queries' import { useState } from 'react' +import { + Content, + Grid, + ImageBox, + ImageBoxSkeleton, + Title, + HTMLToImage +} from 'components' +import { useRecommendation } from 'queries' +import { Recommendation } from 'types' +import { MAX_RANDOM_FY_ITEMS } from '../../constants' type AuthenticatedProps = { artistId: string } export const Authenticated = ({ artistId }: AuthenticatedProps) => { + const [likedArtists, setLikedArtists] = useState>([]) const [id, setId] = useState(() => artistId) const { data, isError, isLoading } = useRecommendation(id) + const handleLike = (artist: Recommendation) => { + setId(artist.id) + setLikedArtists((oldLikedArtists) => [...oldLikedArtists, artist]) + } + if (isError) { - return
ocorreu um erro
+ return
Something wrong! :(
} return ( -
- Randomfy -
- - {isLoading && } - {data?.data.map((item) => ( - { - setId(item.id) - }} - /> - ))} - + {likedArtists.length < MAX_RANDOM_FY_ITEMS && ( + <> +
+ Randomfy +
+ + {isLoading && } + {data?.data.map((artist) => ( + handleLike(artist)} + /> + ))} + + + )} + +
) } diff --git a/src/utils/cookies/cookies.spec.ts b/src/utils/cookies/cookies.spec.ts index 7e76a5a..c7e6c26 100644 --- a/src/utils/cookies/cookies.spec.ts +++ b/src/utils/cookies/cookies.spec.ts @@ -25,9 +25,14 @@ describe('Cookies', () => { name: 'token', value: 'fake-token' }) - expect(setCookie).toHaveBeenCalledWith(null, 'token', 'fake-token', { - secure: false - }) + expect(setCookie).toHaveBeenCalledWith( + { res: undefined }, + 'token', + 'fake-token', + { + secure: false + } + ) }) it('should be able to set cookies when context is not undefined', () => { diff --git a/yarn.lock b/yarn.lock index 1f62327..eea1965 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51,6 +51,13 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-compilation-targets@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" @@ -61,6 +68,19 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-create-class-features-plugin@^7.16.7": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" + integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" @@ -91,6 +111,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" @@ -112,11 +139,29 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helper-simple-access@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" @@ -248,13 +293,31 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-typescript@^7.16.7": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" + +"@babel/preset-typescript@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" + "@babel/runtime-corejs3@^7.10.2": version "7.17.2" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz#fdca2cd05fba63388babe85d349b6801b008fd13" @@ -279,7 +342,7 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.16.7", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3", "@babel/traverse@^7.7.2": version "7.17.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== @@ -1968,6 +2031,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + before-after-hook@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" @@ -2477,6 +2545,13 @@ css-in-js-utils@^2.0.0: hyphenate-style-name "^1.0.2" isobject "^3.0.1" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-tree@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -3614,6 +3689,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -6934,6 +7017,13 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -7232,6 +7322,13 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + v8-compile-cache-lib@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8"