diff --git a/.eslintignore b/.eslintignore index c2c9d0994..5b1e11ac7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ node_modules dist *.js -scripts \ No newline at end of file +scripts +vitest.config.ts \ No newline at end of file diff --git a/package.json b/package.json index 905268c6c..a62ffa09e 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,11 @@ "@typescript-eslint/parser": "^5.59.7", "@vercel/routing-utils": "^3.1.0", "@vitest/coverage-v8": "^2.0.5", + "@testing-library/react": "^16.0.0", + "@testing-library/dom": "^10.1.0", + "@testing-library/react-hooks": "^8.0.1", + "happy-dom": "^14.12.0", + "react-test-renderer": "^18.3.1", "buffer": "^5.5.0", "chalk": "^5.2.0", "command-line-args": "^5.2.1", diff --git a/vitest.config.ts b/vitest.config.ts index 0da549d7b..19ebacfe1 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,8 +1,8 @@ -/// -import { defineConfig } from 'vite'; +import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { + environment: 'happy-dom', watch: false, }, }); diff --git a/wallets/react/package.json b/wallets/react/package.json index fd6d30a3f..e4451eda8 100644 --- a/wallets/react/package.json +++ b/wallets/react/package.json @@ -6,9 +6,11 @@ "source": "./src/index.ts", "main": "./dist/index.js", "exports": { - ".": "./dist/index.js" + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } }, - "typings": "dist/index.d.ts", "files": [ "dist", "src" @@ -18,7 +20,9 @@ "ts-check": "tsc --declaration --emitDeclarationOnly -p ./tsconfig.json", "clean": "rimraf dist", "format": "prettier --write '{.,src}/**/*.{ts,tsx}'", - "lint": "eslint \"**/*.{ts,tsx}\" --ignore-path ../../.eslintignore" + "lint": "eslint \"**/*.{ts,tsx}\" --ignore-path ../../.eslintignore", + "test": "vitest", + "coverage": "vitest run --coverage" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -30,7 +34,11 @@ "@rango-dev/wallets-shared": "^0.37.1-next.1", "rango-types": "^0.1.69" }, + "devDependencies": { + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.25" + }, "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/wallets/react/src/test-utils/env.ts b/wallets/react/src/test-utils/env.ts new file mode 100644 index 000000000..53c44f8b6 --- /dev/null +++ b/wallets/react/src/test-utils/env.ts @@ -0,0 +1,10 @@ +const TEST_ENVIRONMENT_AGENT = 'HappyDOM/0.0.0'; + +/** + * Detecting wether our code is running in a test environment or not. + * + * Note: This is only useful when HappyDOM or jsdom has been added to test runner. + */ +export function isRunningInTestEnvironmentWithDom(): boolean { + return navigator.userAgent.includes(TEST_ENVIRONMENT_AGENT); +} diff --git a/wallets/react/src/test-utils/fixtures.ts b/wallets/react/src/test-utils/fixtures.ts new file mode 100644 index 000000000..f1dc90b6d --- /dev/null +++ b/wallets/react/src/test-utils/fixtures.ts @@ -0,0 +1,225 @@ +import type { LegacyProviderInterface } from '@rango-dev/wallets-core/legacy'; +import type { BlockchainMeta, GenericSigner } from 'rango-types'; +import type { EvmTransaction } from 'rango-types/mainApi'; + +import { DefaultSignerFactory, TransactionType } from 'rango-types'; + +export const legacyAddress = '0x000000000000000000000000000000000000dead'; + +export class MockEvmSigner implements GenericSigner { + async signMessage( + _msg: string, + _address: string, + _chainId: string | null + ): Promise { + return '0x'; + } + async signAndSendTx( + _tx: EvmTransaction, + _address: string, + _chainId: string | null + ): Promise<{ hash: string; response?: any }> { + return { + hash: '0x', + }; + } +} + +export const legacyProvider: LegacyProviderInterface = { + config: { + type: 'legacy-garbage', + }, + async connect({ network }) { + // we added a timeout to simulate requesting to extension for connection. + return await new Promise((resolve, reject) => { + setTimeout(() => { + // This is useful for simulating error scenarios. + if (network === 'When Airdrop?') { + reject( + 'please stay tuned we shall announce the detailed information soon.' + ); + } else { + resolve([ + { + accounts: [legacyAddress], + chainId: network || '', + }, + ]); + } + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + }, 10); + }); + }, + getInstance() { + return {}; + }, + async getSigners() { + const signers = new DefaultSignerFactory(); + signers.registerSigner(TransactionType.EVM, new MockEvmSigner()); + return signers; + }, + getWalletInfo() { + return { + name: 'legacy garbage wallet', + color: '#000', + img: 'https://...', + installLink: 'https://...', + supportedChains: [], + }; + }, +}; + +export const blockchainsMeta: BlockchainMeta[] = [ + { + name: 'BTC', + defaultDecimals: 8, + addressPatterns: [ + '^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$|^(bc1)[0-9A-Za-z]{39,59}$', + ], + feeAssets: [ + { + blockchain: 'BTC', + symbol: 'BTC', + address: null, + }, + ], + logo: 'https://raw.githubusercontent.com/rango-exchange/assets/main/blockchains/BTC/icon.svg', + displayName: 'Bitcoin', + shortName: 'BTC', + sort: 12, + color: '#F7931A', + enabled: true, + type: TransactionType.TRANSFER, + chainId: null, + info: null, + }, + { + name: 'ETH', + defaultDecimals: 18, + addressPatterns: ['^(0x)[0-9A-Fa-f]{40}$'], + feeAssets: [ + { + blockchain: 'ETH', + symbol: 'ETH', + address: null, + }, + ], + logo: 'https://raw.githubusercontent.com/rango-exchange/assets/main/blockchains/ETH/icon.svg', + displayName: 'Ethereum', + shortName: 'ETH', + sort: 0, + color: '#ecf0f1', + enabled: true, + type: TransactionType.EVM, + chainId: '0x1', + info: { + infoType: 'EvmMetaInfo', + chainName: 'Ethereum Mainnet', + nativeCurrency: { + name: 'ETH', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: ['https://rpc.ankr.com/eth'], + blockExplorerUrls: ['https://etherscan.io'], + addressUrl: 'https://etherscan.io/address/{wallet}', + transactionUrl: 'https://etherscan.io/tx/{txHash}', + // enableGasV2: true, + }, + }, + { + name: 'SOLANA', + defaultDecimals: 9, + addressPatterns: ['^[1-9A-HJ-NP-Za-km-z]{32,44}$'], + feeAssets: [ + { + blockchain: 'SOLANA', + symbol: 'SOL', + address: null, + }, + ], + logo: 'https://raw.githubusercontent.com/rango-exchange/assets/main/blockchains/SOLANA/icon.svg', + displayName: 'Solana', + shortName: 'Solana', + sort: 20, + color: '#708DD2', + enabled: true, + type: TransactionType.SOLANA, + chainId: 'mainnet-beta', + info: null, + }, + { + name: 'COSMOS', + defaultDecimals: 6, + addressPatterns: ['^(cosmos1)[0-9a-z]{38}$'], + feeAssets: [ + { + blockchain: 'COSMOS', + symbol: 'ATOM', + address: null, + }, + ], + logo: 'https://raw.githubusercontent.com/rango-exchange/assets/main/blockchains/COSMOS/icon.svg', + displayName: 'Cosmos', + shortName: 'Cosmos', + sort: 15, + color: '#2E3148', + enabled: true, + type: TransactionType.COSMOS, + chainId: 'cosmoshub-4', + info: { + infoType: 'CosmosMetaInfo', + experimental: false, + rpc: 'https://cosmos-rpc.polkachu.com', + rest: 'https://lcd-cosmoshub.blockapsis.com', + cosmostationLcdUrl: 'https://lcd-cosmoshub.blockapsis.com', + cosmostationApiUrl: 'https://cosmos-rpc.polkachu.com', + cosmostationDenomTracePath: '/ibc/apps/transfer/v1/denom_traces/', + mintScanName: 'cosmos', + chainName: 'Cosmos', + stakeCurrency: { + coinDenom: 'ATOM', + coinMinimalDenom: 'uatom', + coinDecimals: 6, + coinGeckoId: 'cosmos', + coinImageUrl: '/tokens/blockchain/cosmos.svg', + }, + bip44: { + coinType: 118, + }, + bech32Config: { + bech32PrefixAccAddr: 'cosmos', + bech32PrefixAccPub: 'cosmospub', + bech32PrefixValAddr: 'cosmosvaloper', + bech32PrefixValPub: 'cosmosvaloperpub', + bech32PrefixConsAddr: 'cosmosvalcons', + bech32PrefixConsPub: 'cosmosvalconspub', + }, + currencies: [ + { + coinDenom: 'ATOM', + coinMinimalDenom: 'uatom', + coinDecimals: 6, + coinGeckoId: 'cosmos', + coinImageUrl: '/tokens/blockchain/cosmos.svg', + }, + ], + feeCurrencies: [ + { + coinDenom: 'ATOM', + coinMinimalDenom: 'uatom', + coinDecimals: 6, + coinGeckoId: 'cosmos', + coinImageUrl: '/tokens/blockchain/cosmos.svg', + }, + ], + features: ['stargate', 'ibc-transfer'], + explorerUrlToTx: 'https://www.mintscan.io/cosmos/txs/{txHash}', + gasPriceStep: { + low: 0.01, + average: 0.025, + high: 0.04, + }, + }, + }, +]; diff --git a/wallets/react/tests/legacy.test.tsx b/wallets/react/tests/legacy.test.tsx new file mode 100644 index 000000000..8bf57185c --- /dev/null +++ b/wallets/react/tests/legacy.test.tsx @@ -0,0 +1,261 @@ +import { legacyProviderImportsToVersionsInterface } from '@rango-dev/wallets-core/utils'; +import { Namespace as LegacyNamespace } from '@rango-dev/wallets-shared'; +import { renderHook } from '@testing-library/react-hooks'; +import { TransactionType } from 'rango-types'; +import React from 'react'; +import { describe, expect, test, vi } from 'vitest'; + +import { Provider, useWallets } from '../src/index.js'; +import { + blockchainsMeta, + legacyAddress, + legacyProvider, +} from '../src/test-utils/fixtures.js'; + +describe('check legacy is working correctly', () => { + test("initialize legacy provider and it's accessible", async () => { + const wrapper = ({ children }: any) => { + const list = [legacyProviderImportsToVersionsInterface(legacyProvider)]; + + return {children}; + }; + + const { result } = renderHook(() => useWallets(), { + wrapper, + }); + + // providers are evaluated lazily, so before calling `connect` its value is `null` + expect(result.current.providers()['legacy-garbage']).toBe(null); + + // while try to connect, `provider` will be initialized. + await result.current.connect('legacy-garbage'); + expect(result.current.providers()['legacy-garbage']).toStrictEqual( + legacyProvider.getInstance() + ); + }); + + test.todo('check allBlockchains works correctly', () => { + // + }); + + test.todo('check onUpdateState works correctly', () => { + // + }); + + test.todo('change props value after initializing', () => { + // + }); +}); + +describe('check legacy connect method is working', () => { + test('throw an error if trying to connect to an undefined wallet', async () => { + const wrapper = ({ children }: any) => { + const list = [legacyProviderImportsToVersionsInterface(legacyProvider)]; + + return {children}; + }; + + const { result } = renderHook(() => useWallets(), { + wrapper, + }); + + await expect(result.current.connect('whatever')).rejects.toThrow( + 'You should add' + ); + }); + + test('will be ignored and use first namespace when passing multiple namespaces', async () => { + const wrapper = ({ children }: any) => { + const list = [legacyProviderImportsToVersionsInterface(legacyProvider)]; + + return {children}; + }; + + const { result } = renderHook(() => useWallets(), { + wrapper, + }); + + const connectResult = await result.current.connect('legacy-garbage', [ + { + namespace: LegacyNamespace.Evm, + network: 'eth', + }, + { + namespace: LegacyNamespace.Evm, + network: 'arb', + }, + ]); + + expect(connectResult).toHaveLength(1); + expect(connectResult[0].network).toBe('eth'); + }); + test('update internal states correctly', async () => { + const wrapper = ({ children }: any) => { + const list = [legacyProviderImportsToVersionsInterface(legacyProvider)]; + + return ( + + {children} + + ); + }; + + const { result } = renderHook(() => useWallets(), { + wrapper, + }); + + const initialState = { + installed: true, + connecting: false, + connected: false, + accounts: null, + network: null, + }; + expect(result.current.state('legacy-garbage')).toMatchObject(initialState); + + // connecting successfully. + await result.current.connect('legacy-garbage', [ + { namespace: LegacyNamespace.Evm, network: 'ETH' }, + ]); + + expect(result.current.state('legacy-garbage')).toMatchObject({ + installed: true, + connecting: false, + connected: true, + accounts: [`ETH:${legacyAddress}`], + network: 'ETH', + }); + + // unsuccessful connection + await expect( + result.current.connect('legacy-garbage', [ + { namespace: LegacyNamespace.Evm, network: 'When Airdrop?' }, + ]) + ).rejects.toThrowError(); + // state should be reset + expect(result.current.state('legacy-garbage')).toMatchObject(initialState); + }); + + test.todo("throw error when it's already on connecting", async () => { + // + }); + + test.todo('ensure extra context added to `connect` function', async () => { + // make sure correct values passed to `provider connect fn`: { instance, network, meta, namespaces} + }); + + test.todo( + 'return multiple result from connect should handle network and accounts correctly ', + async () => { + // if connect fn returns array, it has own logic. + } + ); + + test.todo('return empty result from connect', async () => { + // if connect fn return empty array, state shouldn't be updated. + }); + + test.todo('suggestAndConnect', async () => { + // + }); +}); + +describe('check legacy switching network', () => { + test('switch network & canSwitchNetworkTo', async () => { + const switchNetwork = vi.fn(); + + const wrapper = ({ children }: any) => { + const extendLegacyProvider = { ...legacyProvider }; + extendLegacyProvider.canSwitchNetworkTo = ({ meta, network }) => { + return !!meta.find((blockchain) => blockchain.name === network); + }; + extendLegacyProvider.getWalletInfo = (allBlockChains) => { + const base = legacyProvider.getWalletInfo(allBlockChains); + return { + ...base, + supportedChains: blockchainsMeta, + }; + }; + extendLegacyProvider.switchNetwork = async () => { + switchNetwork(); + }; + + const list = [ + legacyProviderImportsToVersionsInterface(extendLegacyProvider), + ]; + + return ( + + {children} + + ); + }; + + const { result } = renderHook(() => useWallets(), { + wrapper, + }); + + // First we should connect to any network + await result.current.connect('legacy-garbage', [ + { + namespace: LegacyNamespace.Evm, + network: 'ETH', + }, + ]); + + expect(result.current.state('legacy-garbage')).toMatchObject({ + network: 'ETH', + }); + + expect(result.current.canSwitchNetworkTo('legacy-garbage', 'COSMOS')).toBe( + true + ); + expect( + result.current.canSwitchNetworkTo('legacy-garbage', 'whatever') + ).toBe(false); + + // Then passing a different network name, will trigger switch network. + await result.current.connect('legacy-garbage', [ + { + namespace: LegacyNamespace.Evm, + network: 'COSMOS', + }, + ]); + + expect(switchNetwork).toBeCalledTimes(1); + + expect(result.current.state('legacy-garbage')).toMatchObject({ + network: 'COSMOS', + }); + }); +}); + +describe('check functionality related to connect', () => { + test.todo('disconnect & disconnectAll', async () => { + // + }); + + test.todo('autoConnect', async () => { + // connect method has an `if` for persisting wallet, check that as well. + }); +}); + +describe('check signers', () => { + test('should signers be accessible', async () => { + const wrapper = ({ children }: any) => { + const list = [legacyProviderImportsToVersionsInterface(legacyProvider)]; + + return {children}; + }; + + const { result } = renderHook(() => useWallets(), { + wrapper, + }); + + const signers = await result.current.getSigners('legacy-garbage'); + expect( + await signers.getSigner(TransactionType.EVM).signMessage('', '', null) + ).toBeTypeOf('string'); + expect(() => signers.getSigner(TransactionType.SOLANA)).toThrowError(); + }); +}); diff --git a/yarn.lock b/yarn.lock index 5dcc05a55..86538b301 100644 --- a/yarn.lock +++ b/yarn.lock @@ -64,6 +64,14 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": version "7.24.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" @@ -72,14 +80,6 @@ "@babel/highlight" "^7.24.2" picocolors "^1.0.0" -"@babel/code-frame@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" - integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== - dependencies: - "@babel/highlight" "^7.24.7" - picocolors "^1.0.0" - "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9", "@babel/compat-data@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11" @@ -7298,6 +7298,35 @@ dependencies: rxjs "^7.0.0" +"@testing-library/dom@^10.1.0": + version "10.4.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/react-hooks@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12" + integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g== + dependencies: + "@babel/runtime" "^7.12.5" + react-error-boundary "^3.1.0" + +"@testing-library/react@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.0.0.tgz#0a1e0c7a3de25841c3591b8cb7fb0cf0c0a27321" + integrity sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ== + dependencies: + "@babel/runtime" "^7.12.5" + "@tonconnect/isomorphic-eventsource@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@tonconnect/isomorphic-eventsource/-/isomorphic-eventsource-0.0.1.tgz#199e5a86c31dad706b79826f65879e0d77d3dd51" @@ -7508,6 +7537,11 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.18.0": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -7884,7 +7918,7 @@ "@types/react" "*" "@types/reactcss" "*" -"@types/react-dom@*": +"@types/react-dom@*", "@types/react-dom@^18.0.25": version "18.3.0" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== @@ -9094,6 +9128,13 @@ aria-hidden@^1.1.1: dependencies: tslib "^2.0.0" +aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + array-back@^3.0.1, array-back@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" @@ -11138,7 +11179,7 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -dequal@^2.0.2: +dequal@^2.0.2, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== @@ -11241,6 +11282,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -11497,7 +11543,7 @@ entities@^3.0.1: resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== -entities@^4.2.0, entities@^4.4.0: +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -12947,6 +12993,15 @@ handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" +happy-dom@^14.12.0: + version "14.12.3" + resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-14.12.3.tgz#1b5892c670461fd1db041bee690981c22d3d521f" + integrity sha512-vsYlEs3E9gLwA1Hp+w3qzu+RUDFf4VTT8cyKqVICoZ2k7WM++Qyd2LwzyTi5bqMJFiIC/vNpTDYuxdreENRK/g== + dependencies: + entities "^4.5.0" + webidl-conversions "^7.0.0" + whatwg-mimetype "^3.0.0" + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -14673,6 +14728,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + magic-string@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" @@ -16274,6 +16334,15 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -16758,6 +16827,13 @@ react-element-to-jsx-string@^15.0.0: is-plain-object "5.0.0" react-is "18.1.0" +react-error-boundary@^3.1.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" + integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== + dependencies: + "@babel/runtime" "^7.12.5" + react-error-overlay@6.0.9: version "6.0.9" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" @@ -16799,7 +16875,7 @@ react-i18next@^12.2.0: "@babel/runtime" "^7.20.6" html-parse-stringify "^3.0.1" -react-is@17.0.2: +react-is@17.0.2, react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -16809,6 +16885,11 @@ react-is@18.1.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.3: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -16884,6 +16965,14 @@ react-router@6.20.0: dependencies: "@remix-run/router" "1.13.0" +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== + dependencies: + object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" + react-style-singleton@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" @@ -16915,6 +17004,15 @@ react-syntax-highlighter@^15.5.0: prismjs "^1.27.0" refractor "^3.6.0" +react-test-renderer@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.3.1.tgz#e693608a1f96283400d4a3afead6893f958b80b4" + integrity sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA== + dependencies: + react-is "^18.3.1" + react-shallow-renderer "^16.15.0" + scheduler "^0.23.2" + react-textarea-autosize@^7.1.0: version "7.1.2" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz#70fdb333ef86bcca72717e25e623e90c336e2cda" @@ -17530,6 +17628,13 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== + dependencies: + loose-envify "^1.1.0" + schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" @@ -19258,6 +19363,11 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-dev-middleware@^6.1.2: version "6.1.3" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz#79f4103f8c898564c9e96c3a9c2422de50f249bc" @@ -19328,6 +19438,11 @@ whatwg-fetch@>=0.10.0: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz#caefd92ae630b91c07345537e67f8354db470973" integrity sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"