diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 0000000..668e227 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,27 @@ +name: Chromatic +on: + push: + branches: + - develop + release: + types: [created] +jobs: + chromatic: + name: Publish Storybook on chromatic + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Install dependencies + run: yarn && yarn add @mui/material @emotion/react @emotion/styled inversify inversify-react mobx mobx-react-lite + + - name: Lint + run: yarn lint + + - name: Publish to Chromatic + uses: chromaui/action@v1 + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8280e72..246b819 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,13 @@ name: CI -on: [push] +on: + push: + branches: + - develop + pull_request: + branches: + - main + - develop + jobs: build: name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} @@ -19,14 +27,14 @@ jobs: with: node-version: ${{ matrix.node }} - - name: Install deps and build (with cache) - uses: bahmutov/npm-install@v1 + - name: Install + run: yarn install && yarn add reflect-metadata - name: Lint run: yarn lint - # - name: Test - # run: yarn test --ci --coverage --maxWorkers=2 + - name: Test + run: yarn test --ci --coverage --maxWorkers=2 - name: Build run: yarn build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 372c932..7f4d6bf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,6 +22,9 @@ jobs: - name: Lint run: npm run lint + - name: Test + run: yarn test --ci --coverage --maxWorkers=2 + - name: Generate build run: npm run build diff --git a/.storybook/main.js b/.storybook/main.js index 6ce63a2..ed6445c 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -4,5 +4,12 @@ module.exports = { '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-postcss', + '@storybook/addon-interactions', ], + refs: { + 'chromatic-published-Storybook': { + title: 'Componentz', + url: 'https://chromatic.com/library?appId=61a3dfbf9c186c003a9e79f1&branch=develop', + }, + }, }; diff --git a/LICENSE b/LICENSE index 8c22fc2..b72a758 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/jest.config.js b/jest.config.js index e4672de..fdf4e7c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,10 +1,10 @@ module.exports = { moduleDirectories: ['node_modules', 'src'], moduleFileExtensions: ['js', 'ts', 'tsx', 'json', 'node'], - roots: ['tests'], + testPathIgnorePatterns: ['/node_modules/', '/dist/'], + setupFilesAfterEnv: ['/src/tests/setupTests.ts'], testEnvironment: 'jest-environment-jsdom', // Use browser-like testing environment testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], - testPathIgnorePatterns: ['\\\\node_modules\\\\'], transform: { '^.+\\.(ts|tsx)$': 'ts-jest', // That one tells Jest to use ts-jest when dealing with TypeScript files }, diff --git a/package.json b/package.json index 86a70c6..db1eaef 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "lint": "tsc --project ./tsconfig.json && eslint src -c .eslintrc.json --ext tsx,ts", "test": "jest", "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook" + "build-storybook": "build-storybook", + "chromatic": "npx chromatic --exit-zero-on-changes" }, "devDependencies": { "@babel/core": "^7.16.0", @@ -44,9 +45,14 @@ "@rollup/plugin-typescript": "^8.3.0", "@storybook/addon-actions": "^6.4.0", "@storybook/addon-essentials": "^6.4.0", + "@storybook/addon-interactions": "^6.4.0", "@storybook/addon-links": "^6.4.0", "@storybook/addon-postcss": "^2.0.0", + "@storybook/jest": "^0.0.5", "@storybook/react": "^6.4.0", + "@storybook/testing-library": "^0.0.7", + "@testing-library/dom": "^8.11.1", + "@testing-library/jest-dom": "^5.15.1", "@testing-library/react": "^12.1.2", "@types/faker": "^5.5.9", "@types/jest": "^27.0.3", @@ -56,12 +62,14 @@ "@typescript-eslint/parser": "^5.4.0", "babel-jest": "^27.3.1", "babel-loader": "^8.2.3", + "chromatic": "^6.0.6", "eslint": "^8.3.0", "eslint-plugin-react": "^7.27.1", "husky": "^7.0.4", "inversify": "^6.0.1", "inversify-react": "^1.0.0", "jest": "^27.3.1", + "jest-dom": "^4.0.0", "mobx": "^6.3.7", "mobx-react-lite": "^3.2.2", "postcss": "^8.4.0", diff --git a/src/components/Breadcrumbs/Breadcrumbs.test.tsx b/src/components/Breadcrumbs/Breadcrumbs.test.tsx new file mode 100644 index 0000000..50f3f46 --- /dev/null +++ b/src/components/Breadcrumbs/Breadcrumbs.test.tsx @@ -0,0 +1,75 @@ +import 'reflect-metadata'; + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { globalContainer } from 'index'; +import { Provider } from 'inversify-react'; +import { BreadcrumbsContainer } from '../../tests/fixtures/BreadcrumbFixtures'; + +describe('Breadcrumbs', () => { + let onClickSpy: jest.Mock; + + beforeEach(() => { + onClickSpy = jest.fn(); + }); + + it('should renders correctly', async () => { + const paths = [ + { + label: 'Home', + }, + { + label: 'About', + }, + ]; + + render( + + + + ); + + expect(await screen.findByText(/Home/i)).not.toBeNull(); + }); + + it('should call onClick function when the user clicks', async () => { + const paths = [ + { + label: 'Home', + onClick: onClickSpy, + }, + { + label: 'About', + }, + ]; + + render( + + + + ); + (await screen.findByText(/Home/i)).click(); + expect(onClickSpy).toHaveBeenCalled(); + }); + + it('should disable last path', async () => { + const paths = [ + { + label: 'Home', + }, + { + label: 'About', + onClick: onClickSpy, + }, + ]; + + render( + + + + ); + + (await screen.findByText(/About/i)).click(); + expect(onClickSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/src/components/Breadcrumbs/index.tsx b/src/components/Breadcrumbs/index.tsx index be48311..88e5108 100644 --- a/src/components/Breadcrumbs/index.tsx +++ b/src/components/Breadcrumbs/index.tsx @@ -24,7 +24,7 @@ function Breadcrumbs(props: BreadcrumbsProps) { {uiStore.breadcrumb.linkedPaths.map((path) => ( { + let onRejectSpy: jest.Mock; + let onAcceptSpy: jest.Mock; + + beforeEach(() => { + onAcceptSpy = jest.fn(); + onRejectSpy = jest.fn(); + }); + + it('should renders correctly', async () => { + const content = { + title: 'Test title', + onReject: onRejectSpy, + onAccept: onAcceptSpy, + content: 'Test content', + }; + + render( + + + + ); + + expect(await screen.findByText(/Test content/i)).not.toBeNull(); + }); + + it('should call onAccept function when the user clicks', async () => { + const content = { + title: 'Test title', + onReject: onRejectSpy, + onAccept: onAcceptSpy, + content: 'Test content', + }; + + render( + + + + ); + (await screen.findByText(/Confirm/i)).click(); + expect(onAcceptSpy).toHaveBeenCalled(); + }); + + it('should call onReject function when the user clicks', async () => { + const content = { + title: 'Test title', + onReject: onRejectSpy, + onAccept: onAcceptSpy, + content: 'Test content', + }; + + render( + + + + ); + (await screen.findByText(/Cancel/i)).click(); + expect(onRejectSpy).toHaveBeenCalled(); + }); + + it('should render a JSX as content', () => { + const content = { + title: 'Test title', + onReject: onRejectSpy, + onAccept: onAcceptSpy, + content:
Test content
, + }; + + render( + + + + ); + + expect(screen.getByText(/Test content/i)).not.toBeNull(); + }); +}); diff --git a/src/components/Snackbar/Snackbar.test.tsx b/src/components/Snackbar/Snackbar.test.tsx new file mode 100644 index 0000000..17d566a --- /dev/null +++ b/src/components/Snackbar/Snackbar.test.tsx @@ -0,0 +1,168 @@ +import 'reflect-metadata'; +import React from 'react'; +import { + render, + RenderResult, + screen, + getByTestId, + waitFor, +} from '@testing-library/react'; +import { globalContainer, Snackbar } from 'index'; +import { Provider } from 'inversify-react'; +import { Container } from 'inversify'; +import TYPES from '../../containers/global.types'; +import { UIStoreType } from '../../stores/types'; + +jest.setTimeout(30000); + +describe('Snackbar', () => { + let onActionSpy: jest.Mock; + let mockedSnackbar: RenderResult; + let unitContainer: Container; + + beforeEach(() => { + unitContainer = globalContainer(); + + onActionSpy = jest.fn(); + mockedSnackbar = render( + + + + + ); + }); + + it('should renders correctly', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + uiStore.snackbar.show({ + message: 'message', + severity: 'error', + actionLabel: 'action label test', + onActionClick: onActionSpy, + }); + + expect(await screen.findByText(/message/i)).not.toBeNull(); + await waitFor(() => { + expect(getByTestId(mockedSnackbar.container, 'snackbar')).not.toBeNull(); + }); + }); + + it('should call onAction function when the user clicks', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + uiStore.snackbar.show({ + message: 'message', + severity: 'error', + actionLabel: 'action label test', + onActionClick: onActionSpy, + }); + + (await screen.findByText(/action label test/i)).click(); + expect(onActionSpy).toHaveBeenCalled(); + }); + + it('should close alert snackbar after 3 seconds', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + + uiStore.snackbar.show({ + message: 'Hello World', + severity: 'success', + }); + expect(await screen.findByText(/Hello World/i)).not.toBeNull(); + await waitFor( + () => { + expect(screen.queryByTestId('snackbar')).toBeNull(); + }, + { timeout: 3300 } + ); + }); + + it('should close alert snackbar after click away', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + + uiStore.snackbar.show({ + message: 'Hello World', + severity: 'success', + }); + + getByTestId(mockedSnackbar.container, 'outButton').click(); + await waitFor( + () => { + expect(screen.queryByTestId('snackbar')).toBeNull(); + }, + { timeout: 3300 } + ); + }); + + it('should have a test button in alert snackbar', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + + function snackbarAction() { + uiStore.snackbar.close(); + } + + uiStore.snackbar.show({ + message: 'Hello World', + severity: 'success', + actionLabel: 'Test', + onActionClick: snackbarAction, + }); + + expect(getByTestId(mockedSnackbar.container, 'snackbar')).not.toBeNull(); + }); + + it('should close alert snackbar after click on close button', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + + function closeSnackbar() { + uiStore.snackbar.close(); + } + + uiStore.snackbar.show({ + message: 'Hello World', + severity: 'success', + actionLabel: 'action label test', + onActionClick: closeSnackbar, + }); + + expect(getByTestId(mockedSnackbar.container, 'snackbar')).not.toBeNull(); + + (await screen.findByText(/action label test/i)).click(); + + await waitFor(() => { + expect(screen.queryByTestId('snackbar')).toBeNull(); + }); + }); + + it('should enqueue new snackbar', async () => { + const uiStore = unitContainer.get(TYPES.UIStore); + + uiStore.snackbar.show({ + message: 'Hello World', + severity: 'success', + }); + uiStore.snackbar.show({ + message: "I'm a second snackbar", + severity: 'error', + }); + uiStore.snackbar.show({ + message: "I'm a third snackbar", + severity: 'warning', + }); + + expect(await screen.findByText(/Hello World/i)).not.toBeNull(); + + await waitFor( + () => { + expect(screen.findByText(/I'm a second snackbar/i)).not.toBeNull(); + }, + { timeout: 3500 } + ); + + await waitFor( + () => { + expect(screen.findByText(/I'm a second snackbar/i)).not.toBeNull(); + }, + { timeout: 3500 } + ); + }); +}); diff --git a/src/components/Snackbar/index.tsx b/src/components/Snackbar/index.tsx index f00a191..9975aa6 100644 --- a/src/components/Snackbar/index.tsx +++ b/src/components/Snackbar/index.tsx @@ -32,6 +32,7 @@ function Snackbar({ ...rest }: SnackbarProps) { onClose={uiStore.snackbar.close} > void = undefined; show({ message, severity, actionLabel, onActionClick }: SnackbarOptions) { + if (this.isOpen) { + return this.enqueueSnackbar({ + message, + severity, + actionLabel, + onActionClick, + }); + } + this.isOpen = true; this.message = message; this.severity = severity; @@ -23,11 +34,17 @@ class SnackbarStore implements SnackbarStoreType { } close() { - this.isOpen = false; + if (this.queue.length > 0) { + this.isOpen = false; + const nextSnackbar = this.queue.shift(); - setTimeout(() => { + if (nextSnackbar) { + this.show(nextSnackbar); + } + } else { this.reset(); - }, 500); + this.isOpen = false; + } } reset() { @@ -36,6 +53,10 @@ class SnackbarStore implements SnackbarStoreType { this.severity = undefined; this.onActionClick = undefined; } + + enqueueSnackbar(options: SnackbarOptions) { + this.queue.push(options); + } } export default SnackbarStore; diff --git a/src/stores/types.ts b/src/stores/types.ts index 5e11b92..88f6a50 100644 --- a/src/stores/types.ts +++ b/src/stores/types.ts @@ -17,6 +17,7 @@ export interface SnackbarStoreType { show(options: SnackbarOptions): void; close(): void; reset(): void; + enqueueSnackbar(options: SnackbarOptions): void; } export interface DialogStoreType { diff --git a/src/tests/fixtures/BreadcrumbFixtures.tsx b/src/tests/fixtures/BreadcrumbFixtures.tsx new file mode 100644 index 0000000..9aeec5a --- /dev/null +++ b/src/tests/fixtures/BreadcrumbFixtures.tsx @@ -0,0 +1,18 @@ +import React, { useEffect } from 'react'; +import Breadcrumbs from '../../components/Breadcrumbs'; +import { BreadcrumbPath } from '../../components/Breadcrumbs/types'; +import { useUIStore } from '../../index'; + +export const BreadcrumbsContainer = ({ + newPaths, +}: { + newPaths: BreadcrumbPath[]; +}) => { + const uiStore = useUIStore(); + + useEffect(() => { + uiStore.breadcrumb.setPaths(newPaths); + }); + + return ; +}; diff --git a/src/tests/fixtures/DialogFixtures.tsx b/src/tests/fixtures/DialogFixtures.tsx new file mode 100644 index 0000000..bc3c2c6 --- /dev/null +++ b/src/tests/fixtures/DialogFixtures.tsx @@ -0,0 +1,21 @@ +import { DialogProps } from '@components/Dialog/types'; +import { DialogOptions } from '@stores/types'; +import React, { useEffect } from 'react'; +import Dialog from '../../components/Dialog'; +import { useUIStore } from '../../index'; + +export const DialogContainer = ({ + newContent, + props, +}: { + newContent: DialogOptions; + props?: DialogProps; +}) => { + const uiStore = useUIStore(); + + useEffect(() => { + uiStore.dialog.set(newContent); + }); + + return ; +}; diff --git a/src/tests/setupTests.ts b/src/tests/setupTests.ts new file mode 100644 index 0000000..666127a --- /dev/null +++ b/src/tests/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/extend-expect'; diff --git a/src/utils/delay.ts b/src/utils/delay.ts new file mode 100644 index 0000000..5ff2715 --- /dev/null +++ b/src/utils/delay.ts @@ -0,0 +1,3 @@ +export default function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/yarn.lock b/yarn.lock index d75bea5..a6f7a26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1058,7 +1058,15 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime-corejs3@^7.10.2": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.3.tgz#1e25de4fa994c57c18e5fdda6cc810dac70f5590" + integrity sha512-IAdDC7T0+wEB4y2gbIL0uOXEYpiZEeuFUTVbdGq+UwCcF35T/tS8KrmMomEwEc5wBbyfH3PJVpTSUqrhPDXFcQ== + dependencies: + core-js-pure "^3.19.0" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== @@ -1950,6 +1958,7 @@ source-map "^0.7.3" "@popperjs/core@^2.4.4", "@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": + version "2.11.0" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.0.tgz#6734f8ebc106a0860dff7f92bf90df193f0935d7" integrity sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ== @@ -2151,6 +2160,24 @@ regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" +"@storybook/addon-interactions@^6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-6.4.0.tgz#06f594a397c46da88daac39a5fe68557497fc662" + integrity sha512-EhxknwdXlhqC8QWa41rZ7+oM4us+psETnCORB6Rjkh0kF2RtbdxeJVVPz4L/Qah0veCCSCYn5eNVd+5B2Og3SA== + dependencies: + "@storybook/addons" "6.4.0" + "@storybook/api" "6.4.0" + "@storybook/components" "6.4.0" + "@storybook/core-common" "6.4.0" + "@storybook/core-events" "6.4.0" + "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/instrumenter" "6.4.0" + "@storybook/theming" "6.4.0" + global "^4.4.0" + jest-mock "^27.0.6" + polished "^4.0.5" + ts-dedent "^2.2.0" + "@storybook/addon-links@^6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.4.0.tgz#e15f27d011fe036c4c58b447ae474a9636922da0" @@ -2256,6 +2283,23 @@ global "^4.4.0" regenerator-runtime "^0.13.7" +"@storybook/addons@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.4.0-rc.5.tgz#621015adba5feb2e619d005f565cdc37d9f75d43" + integrity sha512-2HOhx0BURJXI0l4EYmNNtVSfI7VdNGWmjXHE5/m2BPBacrYj19f3y1eku5OtECJeUSwgSfvkCkBGTPxwP5oHxw== + dependencies: + "@storybook/api" "6.4.0-rc.5" + "@storybook/channels" "6.4.0-rc.5" + "@storybook/client-logger" "6.4.0-rc.5" + "@storybook/core-events" "6.4.0-rc.5" + "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/router" "6.4.0-rc.5" + "@storybook/theming" "6.4.0-rc.5" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + "@storybook/api@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.4.0.tgz#2b38da7605ff6a2c8f9f44d17c45084740ebf6ea" @@ -2279,6 +2323,29 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/api@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.4.0-rc.5.tgz#48e07bb077d26fb8fafa8f9f125dea42bfb445da" + integrity sha512-aToSN6jDyvE93umJlvRNsTjCeurQph5ixqo/wI+2/Bp2hq0eRxqWBDHbiK97P3ayKdm8+9e34PGr8RNCC5Tkvg== + dependencies: + "@storybook/channels" "6.4.0-rc.5" + "@storybook/client-logger" "6.4.0-rc.5" + "@storybook/core-events" "6.4.0-rc.5" + "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/router" "6.4.0-rc.5" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.4.0-rc.5" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^5.3.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/builder-webpack4@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.4.0.tgz#f96669652e4cac28d6c3b94c708dba3d3f471570" @@ -2388,6 +2455,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/channels@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.4.0-rc.5.tgz#3f9e31961cee197776a1a41c2b7fa6c38b251da9" + integrity sha512-WeUPqko6JJYtFUEiqag/rVil73Q3BtYX00Z+I9ivsBfCKI3MbROAKKLjBpRWpDEwIzlvCCjB2Pg40C67L39/BQ== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-api@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.4.0.tgz#e8ac12df26e346339346f2b3adc7b30caf4425ad" @@ -2422,6 +2498,14 @@ core-js "^3.8.2" global "^4.4.0" +"@storybook/client-logger@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.4.0-rc.5.tgz#9302c1ecaaac5772aa836d7fed11e82f5de16d73" + integrity sha512-pKEffUTZmVMnNqkX8MAcDrzH/lCwTTJMMvTWNvpDEnlBsHDBq4Rb4O7yXcZbNQBa5lXZ8D7sNLhUfq44ynkPpQ== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + "@storybook/components@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.4.0.tgz#36f443bf43e3a5d23fa8a4968f3d0583c4f63688" @@ -2540,6 +2624,13 @@ dependencies: core-js "^3.8.2" +"@storybook/core-events@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.4.0-rc.5.tgz#f0edf13856b2d704810814541deec897d55add3d" + integrity sha512-q2UdLf5MPVWQvJfMrqxlVpprN69Iprw8a1DAp61wMMT/wyYkurjrkNL523vYHAdYqss+xgdjCE2kWB2d3nQnTg== + dependencies: + core-js "^3.8.2" + "@storybook/core-server@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.4.0.tgz#b1cf8689d9c388d97cf2420780c49add664e46f0" @@ -2626,6 +2717,40 @@ dependencies: lodash "^4.17.15" +"@storybook/expect@storybook-jest": + version "27.3.2-2" + resolved "https://registry.yarnpkg.com/@storybook/expect/-/expect-27.3.2-2.tgz#f9b432511ca8e99dd486b99f42dff64c6eabb7bd" + integrity sha512-lSwPo7jqzhKCSrRQzRs8Fsf35Uv6byAkiQGv1uC5WbMoGCYsn2/FiHxmHcT7DmDy+psfK7gbjZjUL2G4OUX5Xw== + +"@storybook/instrumenter@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-6.4.0.tgz#bc42a167fe1c2497eedbc97423f2f5181a1e1905" + integrity sha512-QaDJH6AJUeslxIgUzyZg9gTrwfFVCbRLpj/tnsot7ph/dt7/7JPOhbb7xyeJ9iFxoYKYJ8OR0hzIpuzhQxrVrQ== + dependencies: + "@storybook/addons" "6.4.0" + "@storybook/client-logger" "6.4.0" + "@storybook/core-events" "6.4.0" + global "^4.4.0" + +"@storybook/instrumenter@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-6.4.0-rc.5.tgz#eef42e84b441f0df21bdf3fd67b26059b1c0a61f" + integrity sha512-Uge2Dek5NDKaNMvYZUAZ0hoIlIkqji5DlgUpt2mB/qI1AGHEM2vXcV8ATlJA05Dc9t9RvE8WvzxmGiZqel9V0w== + dependencies: + "@storybook/addons" "6.4.0-rc.5" + "@storybook/client-logger" "6.4.0-rc.5" + "@storybook/core-events" "6.4.0-rc.5" + global "^4.4.0" + +"@storybook/jest@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@storybook/jest/-/jest-0.0.5.tgz#b7513dc23d65a76c88b336f440b4d32e4fc9635e" + integrity sha512-aWpzNlmA80s4WqJBYi6zngba+IinUiePAhs5AomHPOyfu5tQXD/x+UMpjuKSrIsxsyftn1snBn7D7ZfZwrUbjw== + dependencies: + "@storybook/expect" storybook-jest + "@storybook/instrumenter" "6.4.0-rc.5" + jest-mock "^27.3.0" + "@storybook/manager-webpack4@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.4.0.tgz#19bafe124de9950fb81ebcb0a78cf25fff949f12" @@ -2769,6 +2894,23 @@ react-router-dom "^6.0.0" ts-dedent "^2.0.0" +"@storybook/router@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.4.0-rc.5.tgz#a044113a2a267c828246bb816e540cb470c2e86e" + integrity sha512-A5nxAws7DUKdWMcJ222UOBUiz471uUJ0g/VqK1P7XTRCse8qi6zkO/+sgwbQt2X0gNGx/J1+d4k8AGERFlEJTQ== + dependencies: + "@storybook/client-logger" "6.4.0-rc.5" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + history "5.0.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + qs "^6.10.0" + react-router "^6.0.0-beta.8" + react-router-dom "^6.0.0-beta.8" + ts-dedent "^2.0.0" + "@storybook/semver@^7.3.2": version "7.3.2" resolved "https://registry.yarnpkg.com/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" @@ -2814,6 +2956,17 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/testing-library@^0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@storybook/testing-library/-/testing-library-0.0.7.tgz#9c7c9b5c8bee85cea1466da51f4c0f8cfc8ea459" + integrity sha512-1VCHTOvygZiwShwQJpifLHEPQZCo9W/tZzZfPRiwk2MrV1boJswVTSxPHbdtZDftYYguoPiLIFljDu9HwXnSkw== + dependencies: + "@storybook/client-logger" "6.4.0-rc.5" + "@storybook/instrumenter" "6.4.0-rc.5" + "@testing-library/dom" "^8.3.0" + "@testing-library/user-event" "^13.2.1" + ts-dedent "^2.2.0" + "@storybook/theming@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.4.0.tgz#c884de8a8931d2f08b477cedf105b6c4fa129f12" @@ -2832,6 +2985,24 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" +"@storybook/theming@6.4.0-rc.5": + version "6.4.0-rc.5" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.4.0-rc.5.tgz#007ee06fefaaf97dee5a32cb223a7ca927517cf7" + integrity sha512-o6l0H2qrT032iWqGGnbHjMUKvDwRu06xTwUup/UsTaRoXFoVSrRZMX3sUVZ6fajSYXbkUg7nVEnD0Xfz+GSQgg== + dependencies: + "@emotion/core" "^10.1.1" + "@emotion/is-prop-valid" "^0.8.6" + "@emotion/styled" "^10.0.27" + "@storybook/client-logger" "6.4.0-rc.5" + core-js "^3.8.2" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.27" + global "^4.4.0" + memoizerific "^1.11.3" + polished "^4.0.5" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + "@storybook/ui@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.4.0.tgz#463942f6d3e804a7c7a41033a3b14a806916118e" @@ -2866,7 +3037,7 @@ resolve-from "^5.0.0" store2 "^2.12.0" -"@testing-library/dom@^8.0.0": +"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.11.1", "@testing-library/dom@^8.3.0": version "8.11.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.1.tgz#03fa2684aa09ade589b460db46b4c7be9fc69753" integrity sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg== @@ -2880,6 +3051,21 @@ lz-string "^1.4.4" pretty-format "^27.0.2" +"@testing-library/jest-dom@^5.15.1": + version "5.15.1" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.15.1.tgz#4c49ba4d244f235aec53f0a83498daeb4ee06c33" + integrity sha512-kmj8opVDRE1E4GXyLlESsQthCXK7An28dFWxhiMwD7ZUI7ZxA6sjdJRxLerD9Jd8cHX4BDc1jzXaaZKqzlUkvg== + 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" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + "@testing-library/react@^12.1.2": version "12.1.2" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.2.tgz#f1bc9a45943461fa2a598bb4597df1ae044cfc76" @@ -2888,6 +3074,13 @@ "@babel/runtime" "^7.12.5" "@testing-library/dom" "^8.0.0" +"@testing-library/user-event@^13.2.1": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" + integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== + dependencies: + "@babel/runtime" "^7.12.5" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -3014,7 +3207,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^27.0.3": +"@types/jest@*", "@types/jest@^27.0.3": version "27.0.3" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.3.tgz#0cf9dfe9009e467f70a342f0f94ead19842a783a" integrity sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg== @@ -3171,6 +3364,13 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.1" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.1.tgz#014162a5cee6571819d48e999980694e2f657c3c" + integrity sha512-Gk9vaXfbzc5zCXI9eYE9BI5BNHEp4D3FWjgqBE/ePGYElLAP+KvxBcsdkwfIVvezs605oiyd/VrpiHe3Oeg+Aw== + dependencies: + "@types/jest" "*" + "@types/uglify-js@*": version "3.13.1" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea" @@ -3701,6 +3901,14 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" + integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + aria-query@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" @@ -4506,6 +4714,14 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.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.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -4578,6 +4794,11 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chromatic@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/chromatic/-/chromatic-6.0.6.tgz#f0e6343c322ca6b838f61b1559f25ff0d4df7a8c" + integrity sha512-px0i8VDjCCMtAq8Oxo++tQb8/ADuE54Cy3VCAMpdYyT/4Vly8nmJbdxDx/YzJwhSD+w1uUurh18DA9AZQ/xW0A== + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -4935,7 +5156,7 @@ core-js-compat@^3.18.0, core-js-compat@^3.19.1, core-js-compat@^3.8.1: browserslist "^4.17.6" semver "7.0.0" -core-js-pure@^3.8.1, core-js-pure@^3.8.2: +core-js-pure@^3.19.0, core-js-pure@^3.8.1, core-js-pure@^3.8.2: version "3.19.1" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.1.tgz#edffc1fc7634000a55ba05e95b3f0fe9587a5aa4" integrity sha512-Q0Knr8Es84vtv62ei6/6jXH/7izKmOrtrxH9WJTHLCMAVeU+8TF8z8Nr08CsH4Ot0oJKzBzJJL9SJBYIv7WlfQ== @@ -5120,6 +5341,20 @@ css-what@^5.0.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== +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" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -5424,7 +5659,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.9: +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: version "0.5.10" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz#caa6d08f60388d0bb4539dd75fe458a9a1d0014c" integrity sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g== @@ -7819,6 +8054,11 @@ jest-docblock@^27.0.6: dependencies: detect-newline "^3.0.0" +jest-dom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jest-dom/-/jest-dom-4.0.0.tgz#94eba3cbc6576e7bd6821867c92d176de28920eb" + integrity sha512-gBxYZlZB1Jgvf2gP2pRfjjUWF8woGBHj/g5rAQgFPB/0K2atGuhVcPO+BItyjWeKg9zM+dokgcMOH01vrWVMFA== + jest-each@^27.3.1: version "27.3.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.3.1.tgz#14c56bb4f18dd18dc6bdd853919b5f16a17761ff" @@ -7958,7 +8198,7 @@ jest-message-util@^27.3.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.3.0: +jest-mock@^27.0.6, jest-mock@^27.3.0: version "27.3.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.3.0.tgz#ddf0ec3cc3e68c8ccd489bef4d1f525571a1b867" integrity sha512-ziZiLk0elZOQjD08bLkegBzv5hCABu/c8Ytx45nJKkysQwGaonvmTxwjLqEA4qGdasq9o2I8/HtdGMNnVsMTGw== @@ -10425,7 +10665,7 @@ react-refresh@^0.10.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ== -react-router-dom@^6.0.0: +react-router-dom@^6.0.0, react-router-dom@^6.0.0-beta.8: version "6.0.2" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.0.2.tgz#860cefa697b9d4965eced3f91e82cdbc5995f3ad" integrity sha512-cOpJ4B6raFutr0EG8O/M2fEoyQmwvZWomf1c6W2YXBZuFBx8oTk/zqjXghwScyhfrtnt0lANXV2182NQblRxFA== @@ -10433,7 +10673,7 @@ react-router-dom@^6.0.0: history "^5.1.0" react-router "6.0.2" -react-router@6.0.2, react-router@^6.0.0: +react-router@6.0.2, react-router@^6.0.0, react-router@^6.0.0-beta.8: version "6.0.2" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.0.2.tgz#bd2b0fa84fd1d152671e9f654d9c0b1f5a7c86da" integrity sha512-8/Wm3Ed8t7TuedXjAvV39+c8j0vwrI5qVsYqjFr5WkJjsJpEvNSoLRUbtqSEYzqaTUj1IV+sbPJxvO+accvU0Q== @@ -11236,6 +11476,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.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -11893,7 +12141,7 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -ts-dedent@^2.0.0: +ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==