diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..6debf062 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,68 @@ +name: Test ReefKnot + +on: + workflow_dispatch: + inputs: + STAND_ENV: + description: 'Stand env' + required: true + type: choice + default: testnet + options: + - mainnet + - testnet + WALLETS: + description: 'Select wallet to run tests' + required: true + type: choice + default: All + options: + - All + - metamask + - okx + +jobs: + test: + runs-on: ubuntu-22.04 + environment: test + env: + TEST_BRANCH: ${{ github.event.inputs.branch }} + RPC_URL_KEY: ${{ secrets.RPC_URL_KEY }} + QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} + WALLET_SECRET_PHRASE: ${{ secrets.WALLET_SECRET_PHRASE }} + WALLET_PASSWORD: ${{ secrets.WALLET_PASSWORD }} + STAND_ENV: ${{ github.event.inputs.STAND_ENV || 'testnet' }} + WALLETS: ${{ github.event.inputs.WALLETS || 'All' }} + STAND_TYPE: stand + NODE_OPTIONS: --max-old-space-size=4096 + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha }} + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: yarn install --immutable + working-directory: playwright-tests + + - name: Install Playwright Browsers + run: yarn playwright install chromium --with-deps + working-directory: playwright-tests + + - name: Run tests + working-directory: playwright-tests + run: | + echo "Running All tests" + xvfb-run --auto-servernum -- yarn test:reef-knot + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-tests/playwright-report/ + retention-days: 30 \ No newline at end of file diff --git a/apps/demo-react/components/action-item/action-item.tsx b/apps/demo-react/components/action-item/action-item.tsx index a8b0201b..cdfa3586 100644 --- a/apps/demo-react/components/action-item/action-item.tsx +++ b/apps/demo-react/components/action-item/action-item.tsx @@ -12,7 +12,7 @@ export const ActionItem: FC> = (props) => { return ( - + {children} diff --git a/apps/demo-react/components/layout/header/walletButton/walletButton.tsx b/apps/demo-react/components/layout/header/walletButton/walletButton.tsx index f4bacf9f..4a482dd1 100644 --- a/apps/demo-react/components/layout/header/walletButton/walletButton.tsx +++ b/apps/demo-react/components/layout/header/walletButton/walletButton.tsx @@ -22,6 +22,7 @@ const WalletButton: FC = (props) => { variant="text" color="secondary" onClick={openModal} + data-testid="walletBtn" {...rest} > diff --git a/apps/demo-react/components/stats-item/stats-item.tsx b/apps/demo-react/components/stats-item/stats-item.tsx index 803ffc49..fd17b210 100644 --- a/apps/demo-react/components/stats-item/stats-item.tsx +++ b/apps/demo-react/components/stats-item/stats-item.tsx @@ -23,7 +23,7 @@ export const StatsItem: FC = ({ value, token, loading }) => { return ( - + {loading ? : shortValue} diff --git a/apps/demo-react/components/walletModal/walletModal.tsx b/apps/demo-react/components/walletModal/walletModal.tsx index 3db00972..91085bf5 100644 --- a/apps/demo-react/components/walletModal/walletModal.tsx +++ b/apps/demo-react/components/walletModal/walletModal.tsx @@ -53,7 +53,7 @@ const WalletModal: FC = (props) => { }; return ( - + {connectorName && ( @@ -66,6 +66,7 @@ const WalletModal: FC = (props) => { size="xs" variant="outlined" onClick={handleDisconnect} + data-testid='disconnectBtn' > Disconnect diff --git a/apps/demo-react/features/stats/stats.tsx b/apps/demo-react/features/stats/stats.tsx index 1ccf12c3..51a34dc2 100644 --- a/apps/demo-react/features/stats/stats.tsx +++ b/apps/demo-react/features/stats/stats.tsx @@ -42,7 +42,7 @@ const StatsComponent = () => { : formatEther(wstethBalance.data || BigInt(0)); return ( - + @@ -51,6 +51,7 @@ const StatsComponent = () => { fullwidth onChange={setInputValue} value={inputValue} + data-testid="amountInput" /> @@ -59,11 +60,11 @@ const StatsComponent = () => {
Provider: - {connectorName} + {connectorName}
Network: - {web3Info.chainId} + {web3Info.chainId}
diff --git a/playwright-tests/.env.example b/playwright-tests/.env.example new file mode 100644 index 00000000..df9949d0 --- /dev/null +++ b/playwright-tests/.env.example @@ -0,0 +1,20 @@ +### Playwright test run .env configuration + +# Stand env for the wallet setup. Can be equal: testnet, mainnet +STAND_ENV=testnet + +# Link type for a test run. Can be equal: stand, localhost +STAND_TYPE=localhost + +# Wallet configurations +## Secret phase of the wallet to set up the wallet extension +WALLET_SECRET_PHRASE= +## Wallet password (use minimal 10 symbols with uppercase, lowercase, numbers, and symbols) +WALLET_PASSWORD= +## Drpc private key +RPC_URL_KEY= +## Wallets for test run. Can be equal: All, metamask, okx +WALLETS= + +# Qase reporter +QASE_API_TOKEN= \ No newline at end of file diff --git a/playwright-tests/.eslintignore b/playwright-tests/.eslintignore new file mode 100644 index 00000000..d21957a8 --- /dev/null +++ b/playwright-tests/.eslintignore @@ -0,0 +1,9 @@ +node_modules +dist +playwright-report +test-results + +/.jest + +/.yarn/* +/.pnp.* diff --git a/playwright-tests/.eslintrc.json b/playwright-tests/.eslintrc.json new file mode 100644 index 00000000..909e83f9 --- /dev/null +++ b/playwright-tests/.eslintrc.json @@ -0,0 +1,46 @@ +{ + "env": { + "es2021": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": ["@typescript-eslint", "import"], + "rules": { + "@typescript-eslint/ban-ts-comment": "off", + "prettier/prettier": ["error", {}, { "usePrettierrc": true }], + "no-irregular-whitespace": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unnecessary-type-constraint": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { "ignoreRestSiblings": true } + ] + }, + "overrides": [ + { + "files": ["*.js"], + "rules": { + "@typescript-eslint/explicit-module-boundary-types": "off" + } + } + ], + "settings": { + "import/resolver": { + "typescript": { + "alwaysTryTypes": true, + "project": "./tsconfig.json" + } + } + } +} diff --git a/playwright-tests/.gitignore b/playwright-tests/.gitignore new file mode 100644 index 00000000..210ddcb6 --- /dev/null +++ b/playwright-tests/.gitignore @@ -0,0 +1,8 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# playwright +test-results/ +playwright-report/ +.browser_context_* +logs/ +.yalc \ No newline at end of file diff --git a/playwright-tests/README.md b/playwright-tests/README.md new file mode 100644 index 00000000..8a09337d --- /dev/null +++ b/playwright-tests/README.md @@ -0,0 +1,18 @@ +# Reef Knot logo Reef-Knot. Playwright test + +## ❖ Installation +1. Install dependencies from the `/playwright-tests` directory: +``` +yarn && yarn playwright install chromium +``` +1. Fill the `.env` file in the `/playwright-tests` folder using the `.env.example` file as a template. + +## ❖ How to run tests on a Deployed Stand +1. Set up the `.env` variable: `STAND_TYPE=stand` +2. Navigate to the `/playwright-tests` folder and run: `yarn test:reef-knot` + +## ❖ How to run tests on Localhost +1. Start the `reef-knot` demo app on your local machine: `yarn run dev` +2. Verify the localhost link in `/playwright-tests/config/env.config.ts`, see the `STAND_LINK.localhost` property (_default value is http://localhost:3000_) +3. Set the `.env` variable: `STAND_TYPE=localhost` +4. Navigate to the `/playwright-tests` directory and run: `yarn test:reef-knot` \ No newline at end of file diff --git a/playwright-tests/config/config.ts b/playwright-tests/config/config.ts new file mode 100644 index 00000000..66e4ef0f --- /dev/null +++ b/playwright-tests/config/config.ts @@ -0,0 +1,58 @@ +import { + STAND_CONFIGS, + STAND_LINK, + STAND_ENV, + WALLETS, + TestWalletConfig, +} from './env.config'; +import { pwReefKnotEnvs } from './env.validation'; + +export const REEF_KNOT_CONFIG = { + STAND_TYPE: pwReefKnotEnvs.STAND_TYPE, + STAND_CONFIG: getStandConfig(), + STAND_URL: getStandUrl(), + STAND_ENV: pwReefKnotEnvs.STAND_ENV, + WALLETS: getWalletsForTestRun(), +}; + +function getStandConfig() { + switch (pwReefKnotEnvs.STAND_ENV) { + case 'mainnet': + return STAND_CONFIGS.get(STAND_ENV.mainnet); + case 'testnet': + return STAND_CONFIGS.get(STAND_ENV.testnet); + default: + throw new Error( + `CONFIG_VALIDATION_ERROR: STAND_ENV is not correctly defined (value is "${pwReefKnotEnvs.STAND_ENV}"). Please fix in the .env file`, + ); + } +} + +function getStandUrl() { + switch (pwReefKnotEnvs.STAND_TYPE) { + case 'stand': + return STAND_LINK.stand; + case 'localhost': + return STAND_LINK.localhost; + default: + throw new Error( + `CONFIG_VALIDATION_ERROR: STAND_TYPE is not correctly defined (value is "${pwReefKnotEnvs.STAND_TYPE}"). Please fix in the .env file`, + ); + } +} + +function getWalletsForTestRun() { + const wallets: TestWalletConfig[] = []; + if (pwReefKnotEnvs.WALLETS === 'All') { + WALLETS.forEach((wallet) => wallets.push(wallet)); + } else { + const envWallet = WALLETS.get(pwReefKnotEnvs.WALLETS); + if (envWallet) wallets.push(WALLETS.get(pwReefKnotEnvs.WALLETS)); + } + if (wallets.length === 0) { + throw new Error( + `The chosen wallet is incorrect (the wallet is "${pwReefKnotEnvs.WALLETS}"). Please fix in the .env file`, + ); + } + return wallets; +} diff --git a/playwright-tests/config/env.config.ts b/playwright-tests/config/env.config.ts new file mode 100644 index 00000000..9a2c1d63 --- /dev/null +++ b/playwright-tests/config/env.config.ts @@ -0,0 +1,67 @@ +import { pwReefKnotEnvs } from './env.validation'; + +export interface StandConfig { + chainId: number; + tokenSymbol: string; + chainName: string; + rpcUrl: string; + scan: string; + contracts: { + stake: string; + wrap: string; + withdraw: string; + }; +} + +export const STAND_ENV = { + testnet: 'testnet', + mainnet: 'mainnet', +}; + +export const STAND_LINK = { + stand: 'https://lidofinance.github.io/reef-knot/', + localhost: 'http://localhost:3000/', +}; + +export const STAND_CONFIGS = new Map([ + [ + STAND_ENV.testnet, + { + chainId: 17000, + tokenSymbol: 'ETH', + chainName: 'Holesky', + scan: 'https://holesky.etherscan.io/', + contracts: { + stake: '0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034', + wrap: '0x8d09a4502Cc8Cf1547aD300E066060D043f6982D', + withdraw: '0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50', + }, + rpcUrl: `https://lb.drpc.org/ogrpc?network=holesky&dkey=${pwReefKnotEnvs.RPC_URL_KEY}`, + }, + ], + [ + STAND_ENV.mainnet, + { + chainId: 1, + tokenSymbol: 'ETH', + chainName: 'Ethereum Mainnet', + scan: 'https://etherscan.io/', + contracts: { + stake: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', + wrap: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', + withdraw: '0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1', + }, + rpcUrl: `https://lb.drpc.org/ogrpc?network=ethereum&dkey=${pwReefKnotEnvs.RPC_URL_KEY}`, + }, + ], +]); + +export interface TestWalletConfig { + name: string; + connectWalletEvent: string; +} + +export const WALLETS = new Map([ + ['metamask', { name: 'metamask', connectWalletEvent: 'metaMask connected' }], + ['okx', { name: 'okx', connectWalletEvent: 'okx connected' }], +]); diff --git a/playwright-tests/config/env.validation.ts b/playwright-tests/config/env.validation.ts new file mode 100644 index 00000000..bba6f645 --- /dev/null +++ b/playwright-tests/config/env.validation.ts @@ -0,0 +1,47 @@ +import { z } from 'zod'; +import { config as envConfig } from 'dotenv'; +import path from 'path'; + +const envSchema = z.object({ + STAND_ENV: z.string(), + STAND_TYPE: z.string(), + WALLET_SECRET_PHRASE: z.string(), + WALLET_PASSWORD: z.string(), + RPC_URL_KEY: z.string(), + WALLETS: z.string(), +}); + +envConfig({ path: path.resolve(__dirname, '../.env') }); +class ReefKnotEnvs { + STAND_ENV: string; + STAND_TYPE: string; + WALLET_SECRET_PHRASE: string; + WALLET_PASSWORD: string; + RPC_URL_KEY: string; + WALLETS: string; + + constructor() { + this.validateEnv(); + } + + private validateEnv() { + const result = envSchema.safeParse(process.env); + + if (!result.success) { + throw new Error( + `.env validation error for test run: ${JSON.stringify( + result.error.format(), + )}`, + ); + } + + this.STAND_ENV = result.data.STAND_ENV; + this.STAND_TYPE = result.data.STAND_TYPE; + this.WALLET_SECRET_PHRASE = result.data.WALLET_SECRET_PHRASE; + this.WALLET_PASSWORD = result.data.WALLET_PASSWORD; + this.RPC_URL_KEY = result.data.RPC_URL_KEY; + this.WALLETS = result.data.WALLETS; + } +} + +export const pwReefKnotEnvs = new ReefKnotEnvs(); diff --git a/playwright-tests/config/index.ts b/playwright-tests/config/index.ts new file mode 100644 index 00000000..b7307177 --- /dev/null +++ b/playwright-tests/config/index.ts @@ -0,0 +1,4 @@ +export * from './config'; +export * from './env.config'; +export * from './env.validation'; +export * from './reporter.config'; diff --git a/playwright-tests/config/reporter.config.ts b/playwright-tests/config/reporter.config.ts new file mode 100644 index 00000000..817efc48 --- /dev/null +++ b/playwright-tests/config/reporter.config.ts @@ -0,0 +1,42 @@ +import { REEF_KNOT_CONFIG } from '@config'; +import { ReporterDescription } from '@playwright/test'; + +const htmlReporter: ReporterDescription = ['html', { open: 'never' }]; +const consoleReporter: ReporterDescription = [ + 'list', + { printSteps: !process.env.CI }, +]; +const githubReporter: ReporterDescription = ['github']; +const qaseReporter: ReporterDescription = [ + 'playwright-qase-reporter', + { + debug: false, + environment: REEF_KNOT_CONFIG.STAND_ENV, + mode: 'testops', // value 'testops' enables Qase reporter, 'off' disables + testops: { + api: { + token: process.env.QASE_API_TOKEN, + }, + project: 'REEFKNOT', + uploadAttachments: true, + run: { + complete: true, + title: 'Auto Run [s:@All]', + description: + `Stand url: ${REEF_KNOT_CONFIG.STAND_URL}\n` + + `Env: ${REEF_KNOT_CONFIG.STAND_ENV}`, + }, + batch: { + size: 10, + }, + }, + }, +]; + +export const getReportConfig: () => ReporterDescription[] = function () { + const reporterConfig: ReporterDescription[] = [htmlReporter, consoleReporter]; + if (process.env.CI) { + reporterConfig.push(githubReporter, qaseReporter); + } + return reporterConfig; +}; diff --git a/playwright-tests/logo.svg b/playwright-tests/logo.svg new file mode 100644 index 00000000..cb885c78 --- /dev/null +++ b/playwright-tests/logo.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playwright-tests/package.json b/playwright-tests/package.json new file mode 100644 index 00000000..c37c34ee --- /dev/null +++ b/playwright-tests/package.json @@ -0,0 +1,21 @@ +{ + "name": "reef-knot_playwright_test", + "version": "0.0.1", + "private": true, + "scripts": { + "test:reef-knot": "yarn playwright test" + }, + "dependencies": { + "@lidofinance/lido-ethereum-sdk": "^4.0.1", + "@lidofinance/wallets-testing-extensions": "^1.0.3", + "@lidofinance/wallets-testing-wallets": "1.18.0", + "@lidofinance/wallets-testing-widgets": "1.1.52", + "@playwright/test": "^1.48.2", + "playwright-qase-reporter": "^2.0.16", + "zod": "^3.24.1" + }, + "packageManager": "yarn@1.22.19", + "devDependencies": { + "dotenv": "^16.4.5" + } +} diff --git a/playwright-tests/pages/components/header.ts b/playwright-tests/pages/components/header.ts new file mode 100644 index 00000000..ccaea1a6 --- /dev/null +++ b/playwright-tests/pages/components/header.ts @@ -0,0 +1,27 @@ +import { Locator, Page } from '@playwright/test'; + +export class Header { + page: Page; + header: Locator; + connectWalletButton: Locator; + accountButton: Locator; + + constructor(page: Page) { + this.page = page; + this.header = this.page.locator('header'); + this.connectWalletButton = this.header.getByTestId('connectBtn'); + this.accountButton = this.header.getByTestId('walletBtn'); + } + + async isAccountSectionVisible() { + await this.accountButton + .waitFor({ + state: 'visible', + timeout: 3000, + }) + .catch(() => { + console.log('Account section is not visible'); + }); + return this.accountButton.isVisible(); + } +} diff --git a/playwright-tests/pages/components/index.ts b/playwright-tests/pages/components/index.ts new file mode 100644 index 00000000..21d37d4e --- /dev/null +++ b/playwright-tests/pages/components/index.ts @@ -0,0 +1,6 @@ +export * from './header'; +export * from './stats'; +export * from './wallet-list-modal'; +export * from './wallet-modal'; +export * from './toast'; +export * from './stake-block'; diff --git a/playwright-tests/pages/components/stake-block.ts b/playwright-tests/pages/components/stake-block.ts new file mode 100644 index 00000000..7e7d6986 --- /dev/null +++ b/playwright-tests/pages/components/stake-block.ts @@ -0,0 +1,15 @@ +import { Locator, Page } from '@playwright/test'; + +export class StakeBlock { + page: Page; + mainComponent: Locator; + referralAddressInput: Locator; + stakeBtn: Locator; + + constructor(page: Page) { + this.page = page; + this.mainComponent = this.page.getByTestId('StakeBlock'); + this.referralAddressInput = this.mainComponent.locator('input'); + this.stakeBtn = this.mainComponent.locator('button :has-text("Stake")'); + } +} diff --git a/playwright-tests/pages/components/stats.ts b/playwright-tests/pages/components/stats.ts new file mode 100644 index 00000000..53fcdd5e --- /dev/null +++ b/playwright-tests/pages/components/stats.ts @@ -0,0 +1,25 @@ +import { Locator, Page } from '@playwright/test'; + +export class StatsBlock { + page: Page; + mainComponent: Locator; + input: Locator; + providerValue: Locator; + chainIdValue: Locator; + ethBalance: Locator; + stethBalance: Locator; + wstethBalance: Locator; + amountInput: Locator; + + constructor(page: Page) { + this.page = page; + this.mainComponent = this.page.getByTestId('statsBlock'); + this.input = this.mainComponent.getByTestId('amountInput'); + this.providerValue = this.mainComponent.getByTestId('providerName'); + this.chainIdValue = this.mainComponent.getByTestId('chainId'); + this.ethBalance = this.mainComponent.getByTestId('ETH'); + this.stethBalance = this.mainComponent.getByTestId('stETH'); + this.wstethBalance = this.mainComponent.getByTestId('wstETH'); + this.amountInput = this.mainComponent.getByTestId('amountInput'); + } +} diff --git a/playwright-tests/pages/components/toast.ts b/playwright-tests/pages/components/toast.ts new file mode 100644 index 00000000..6f10372b --- /dev/null +++ b/playwright-tests/pages/components/toast.ts @@ -0,0 +1,17 @@ +import { Locator, Page } from '@playwright/test'; + +export class Toast { + page: Page; + successToast: Locator; + errorToast: Locator; + + constructor(page: Page) { + this.page = page; + this.successToast = this.page.locator( + '.Toastify__toast--success :has-text("Success")', + ); + this.errorToast = this.page.locator( + '.Toastify__toast--error :has-text("Error")', + ); + } +} diff --git a/playwright-tests/pages/components/wallet-list-modal.ts b/playwright-tests/pages/components/wallet-list-modal.ts new file mode 100644 index 00000000..78f728eb --- /dev/null +++ b/playwright-tests/pages/components/wallet-list-modal.ts @@ -0,0 +1,37 @@ +import { Page, Locator, test } from '@playwright/test'; + +export class WalletListModal { + page: Page; + modal: Locator; + termAndPrivacyCheckbox: Locator; + moreWalletsButton: Locator; + lessWalletsButton: Locator; + + constructor(page: Page) { + this.page = page; + this.modal = this.page.locator('div[role="dialog"]', { + hasText: 'Connect wallet', + }); + this.termAndPrivacyCheckbox = this.modal + .locator('input[type=checkbox]') + .locator('..'); + this.moreWalletsButton = this.modal.getByText('More wallets'); + this.lessWalletsButton = this.modal.getByText('Less wallets'); + } + + async confirmConditionWalletModal() { + await test.step('Confirm the Term and privacy checkbox', async () => { + await this.termAndPrivacyCheckbox.check(); + }); + } + + getWalletInModal(walletName: string) { + return this.modal + .getByRole('button') + .getByText(walletName, { exact: true }); + } + + async closePopUp() { + await this.modal.locator('button').nth(0).click(); + } +} diff --git a/playwright-tests/pages/components/wallet-modal.ts b/playwright-tests/pages/components/wallet-modal.ts new file mode 100644 index 00000000..ce110250 --- /dev/null +++ b/playwright-tests/pages/components/wallet-modal.ts @@ -0,0 +1,13 @@ +import { Page, Locator } from '@playwright/test'; + +export class WalletModal { + page: Page; + mainComponent: Locator; + disconnectButton: Locator; + + constructor(page: Page) { + this.page = page; + this.mainComponent = this.page.getByTestId('walletModal'); + this.disconnectButton = this.mainComponent.getByTestId('disconnectBtn'); + } +} diff --git a/playwright-tests/pages/index.ts b/playwright-tests/pages/index.ts new file mode 100644 index 00000000..3b7bdbc6 --- /dev/null +++ b/playwright-tests/pages/index.ts @@ -0,0 +1 @@ +export * from './reefKnot.page'; diff --git a/playwright-tests/pages/reefKnot.page.ts b/playwright-tests/pages/reefKnot.page.ts new file mode 100644 index 00000000..ded3a242 --- /dev/null +++ b/playwright-tests/pages/reefKnot.page.ts @@ -0,0 +1,91 @@ +import { + Header, + WalletListModal, + StatsBlock, + WalletModal, + StakeBlock, + Toast, +} from './components'; +import { Locator, Page, test } from '@playwright/test'; +import { TIMEOUT } from '@test-data'; +import { waitForCallback } from '@services'; + +export class ReefKnotPage { + readonly page: Page; + header: Header; + walletModal: WalletModal; + statsBlock: StatsBlock; + stakeBlock: StakeBlock; + walletListModal: WalletListModal; + toast: Toast; + + constructor(page: Page) { + this.page = page; + this.header = new Header(this.page); + this.walletModal = new WalletModal(this.page); + this.statsBlock = new StatsBlock(this.page); + this.stakeBlock = new StakeBlock(this.page); + this.walletListModal = new WalletListModal(this.page); + this.toast = new Toast(this.page); + } + + async goto(param = '') { + await this.page.goto(param); + } + + async allowUseCookies() { + await test.step('Allow use cookies (if available)', async () => { + if ( + await this.page + .getByRole('button') + .getByText('Allow') + .isVisible({ timeout: 3000 }) + ) { + await this.page.getByRole('button').getByText('Allow').click(); + } + }); + } + + async waitForPage(timeout = TIMEOUT.RPC_WAIT) { + const page = await this.page + .context() + .waitForEvent('page', { timeout: timeout }); + await page.waitForLoadState('load'); + return page; + } + + async disconnectWallet() { + await test.step('Disconnect wallet', async () => { + await this.header.accountButton.click(); + await this.walletModal.mainComponent.waitFor({ state: 'visible' }); + await this.walletModal.disconnectButton.click(); + }); + } + + async getStorageData(name: string) { + return await this.page.evaluate((names) => { + return localStorage.getItem(names); + }, name); + } + + async clickStakeButton() { + const [txPage] = await Promise.all([ + this.waitForPage(TIMEOUT.RPC_WAIT), + this.stakeBlock.stakeBtn.click(), + ]); + return txPage; + } + + async waitForBalance(locator: Locator, timeout = TIMEOUT.RPC_WAIT) { + return await waitForCallback( + async (locator: Locator) => { + return await locator.evaluate((element) => { + const balance = parseFloat(element.textContent); + return balance ? String(balance) : null; + }); + }, + locator, + timeout, + ); + } +} diff --git a/playwright-tests/playwright.config.ts b/playwright-tests/playwright.config.ts new file mode 100644 index 00000000..47fd787c --- /dev/null +++ b/playwright-tests/playwright.config.ts @@ -0,0 +1,34 @@ +import { PlaywrightTestConfig } from 'playwright/test'; +import { REEF_KNOT_CONFIG, getReportConfig } from '@config'; + +const config: PlaywrightTestConfig = { + testDir: './tests', + timeout: 5 * 60 * 1000, + expect: { + timeout: 5000, + }, + fullyParallel: false, + forbidOnly: !!process.env.CI, + retries: 1, + workers: 1, + reporter: getReportConfig(), + use: { + actionTimeout: 15000, + screenshot: { fullPage: true, mode: 'only-on-failure' }, + baseURL: REEF_KNOT_CONFIG.STAND_URL, + trace: 'retain-on-failure', + permissions: ['clipboard-read'], + contextOptions: { + reducedMotion: 'reduce', + }, + browserName: 'chromium', + }, + projects: [ + { + name: 'reef-knot', + testDir: '../playwright-tests/tests', + }, + ], +}; + +export default config; diff --git a/playwright-tests/services/browser/browser-context.service.ts b/playwright-tests/services/browser/browser-context.service.ts new file mode 100644 index 00000000..61d46b4b --- /dev/null +++ b/playwright-tests/services/browser/browser-context.service.ts @@ -0,0 +1,107 @@ +import { Logger } from '@nestjs/common'; +import { BrowserContext, chromium, Page } from '@playwright/test'; +import * as fs from 'fs/promises'; +import * as os from 'os'; +import * as path from 'path'; +import { WalletConfig } from '@lidofinance/wallets-testing-wallets'; + +type OptionsBrowserContext = { + // If contextDataDir is undefined, a temporary directory will be created for storing context data. + // If contextDataDir is defined, a user-specific directory will be created in the current folder for context data. + contextDataDir: string; +}; + +export class BrowserContextService { + page: Page; + browserContext: BrowserContext = null; + browserContextPaths: string[] = []; + walletConfig: WalletConfig; + options: OptionsBrowserContext; + extensionId: string; + private readonly logger = new Logger(BrowserContextService.name); + + async setup(walletConfig: WalletConfig, options?: OptionsBrowserContext) { + this.walletConfig = walletConfig; + this.options = options; + await this.getBrowserContextPage(); + } + async getBrowserContextPage() { + if (!this.browserContext) { + await this.initBrowserContext(); + } + return this.page; + } + + async initBrowserContext() { + this.logger.debug('Starting a new browser context'); + let browserContextPath; + let isCreated; + + if (this.options.contextDataDir) { + browserContextPath = path.join( + process.cwd(), + this.options.contextDataDir, + ); + isCreated = await fs.mkdir(browserContextPath, { + recursive: true, + }); + } else { + browserContextPath = await fs.mkdtemp(os.tmpdir() + path.sep); + isCreated = true; + } + + this.browserContext = await chromium.launchPersistentContext( + browserContextPath, + browserConfig(this.walletConfig.EXTENSION_PATH), + ); + if (isCreated) { + await this.browserContext.waitForEvent('page'); + } + + const pages = this.browserContext.pages(); + this.page = pages.at(-1); + + this.browserContext.on('page', async (page) => { + page.once('crash', () => this.logger.error(`Page ${page.url()} crashed`)); + }); + this.browserContext.once('close', async () => { + this.browserContext = null; + this.browserContextPaths.push(browserContextPath); + this.logger.debug('Browser context closed'); + }); + await this.setExtensionVars(); + } + + async setExtensionVars() { + let [background] = this.browserContext.serviceWorkers(); + if (!background) + background = await this.browserContext.waitForEvent('serviceworker'); + this.extensionId = background.url().split('/')[2]; + } + + async closePages() { + if (!this.browserContext) { + return; + } + this.page = await this.browserContext.newPage(); + await Promise.all( + this.browserContext + .pages() + .slice(0, -1) + .map((page) => page.close()), + ); + } +} + +function browserConfig(extensionPath: string) { + return { + locale: 'en-us', + headless: false, + args: [ + '--lang=en-US', + '--disable-dev-shm-usage', + `--disable-extensions-except=${extensionPath}`, + '--js-flags="--max-old-space-size=2048"', + ], + }; +} diff --git a/playwright-tests/services/browser/browser-setup.service.ts b/playwright-tests/services/browser/browser-setup.service.ts new file mode 100644 index 00000000..08daeaa6 --- /dev/null +++ b/playwright-tests/services/browser/browser-setup.service.ts @@ -0,0 +1,31 @@ +import { ExtensionService } from '@lidofinance/wallets-testing-extensions'; +import { BrowserService } from './browser.service'; +import { ETHEREUM_WIDGET_CONFIG } from '@lidofinance/wallets-testing-widgets'; +import { ReefKnotService } from '@services'; +import { walletConfig } from './browser.constants'; +import { test } from '@playwright/test'; + +export async function initBrowserWithWallet(walletName: string) { + return test.step(`Init browser with the ${walletName} extension`, async () => { + const extensionConfig = walletConfig.get(walletName); + + process.env.EXTENSION_PATH = + await new ExtensionService().getExtensionDirFromId( + extensionConfig.STORE_EXTENSION_ID, + ); + + const browserService = new BrowserService( + extensionConfig, + ETHEREUM_WIDGET_CONFIG, + ); + await browserService.initWalletSetup(); + + const reefKnotService = new ReefKnotService( + await browserService.getBrowserContextPage(), + browserService.getWalletPage(), + browserService.walletConfig.SECRET_PHRASE, + ); + + return { browserService, reefKnotService }; + }); +} diff --git a/playwright-tests/services/browser/browser.constants.ts b/playwright-tests/services/browser/browser.constants.ts new file mode 100644 index 00000000..464c40e7 --- /dev/null +++ b/playwright-tests/services/browser/browser.constants.ts @@ -0,0 +1,19 @@ +import { + CommonWalletConfig, + METAMASK_COMMON_CONFIG, + MetamaskPage, + OKX_COMMON_CONFIG, + OkxPage, +} from '@lidofinance/wallets-testing-wallets'; + +export const WALLET_PAGES = { + metamask: MetamaskPage, + okx: OkxPage, +}; + +export const walletConfig = new Map([ + ['metamask', METAMASK_COMMON_CONFIG], + ['okx', OKX_COMMON_CONFIG], +]); + +export const DEFAULT_BROWSER_CONTEXT_DIR_NAME = '.browser_context'; diff --git a/playwright-tests/services/browser/browser.service.ts b/playwright-tests/services/browser/browser.service.ts new file mode 100644 index 00000000..854fbc87 --- /dev/null +++ b/playwright-tests/services/browser/browser.service.ts @@ -0,0 +1,75 @@ +import { + CommonWalletConfig, + WalletConfig, + WalletPage, +} from '@lidofinance/wallets-testing-wallets'; +import { Extension } from '@lidofinance/wallets-testing-extensions'; +import { WidgetConfig } from '@lidofinance/wallets-testing-widgets'; +import { + DEFAULT_BROWSER_CONTEXT_DIR_NAME, + WALLET_PAGES, +} from './browser.constants'; +import { BrowserContextService } from './browser-context.service'; +import { REEF_KNOT_CONFIG, pwReefKnotEnvs } from '@config'; + +export class BrowserService { + private walletPage: WalletPage; + private browserContextService: BrowserContextService; + private standConfig: WidgetConfig; + readonly commonWalletConfig: CommonWalletConfig; + public walletConfig: WalletConfig; + + constructor( + commonWalletConfig: CommonWalletConfig, + widgetConfig: WidgetConfig, + ) { + this.browserContextService = new BrowserContextService(); + this.commonWalletConfig = commonWalletConfig; + this.standConfig = widgetConfig; + } + + getWalletPage() { + return this.walletPage; + } + + async getBrowserContextPage() { + return await this.browserContextService.getBrowserContextPage(); + } + + async initWalletSetup() { + await this.setup(this.commonWalletConfig, this.standConfig); + await this.walletPage.setupNetwork(REEF_KNOT_CONFIG.STAND_CONFIG); + await this.walletPage.changeNetwork( + REEF_KNOT_CONFIG.STAND_CONFIG.chainName, + ); + await this.browserContextService.closePages(); + } + + async setup( + commonWalletConfig: CommonWalletConfig, + widgetConfig: WidgetConfig, + ) { + this.standConfig = widgetConfig; + this.walletConfig = { + SECRET_PHRASE: pwReefKnotEnvs.WALLET_SECRET_PHRASE, + PASSWORD: pwReefKnotEnvs.WALLET_PASSWORD, + COMMON: commonWalletConfig, + EXTENSION_PATH: process.env.EXTENSION_PATH, + }; + await this.browserContextService.setup(this.walletConfig, { + contextDataDir: `${DEFAULT_BROWSER_CONTEXT_DIR_NAME}_${commonWalletConfig.WALLET_NAME}`, + }); + const extension = new Extension(this.browserContextService.extensionId); + this.walletPage = new WALLET_PAGES[commonWalletConfig.WALLET_NAME]( + this.browserContextService.browserContext, + extension.url, + this.walletConfig, + ); + await this.walletPage.setup(this.standConfig.networkName); + } + + async teardown() { + if (this.browserContextService.browserContext !== null) + await this.browserContextService.browserContext.close(); + } +} diff --git a/playwright-tests/services/browser/index.ts b/playwright-tests/services/browser/index.ts new file mode 100644 index 00000000..67b953d7 --- /dev/null +++ b/playwright-tests/services/browser/index.ts @@ -0,0 +1,4 @@ +export * from './browser.constants'; +export * from './browser.service'; +export * from './browser-context.service'; +export * from './browser-setup.service'; diff --git a/playwright-tests/services/helpers.ts b/playwright-tests/services/helpers.ts new file mode 100644 index 00000000..0fdfb3df --- /dev/null +++ b/playwright-tests/services/helpers.ts @@ -0,0 +1,53 @@ +/** + * Function to trim digits after decimal point + * + * Example: + * ```ts + * 0.12345 => toCut('0.12345', 3) => '0.123' + * ``` + */ +export function toCut(floatAmount: string, decimalPlaces: number) { + const result = floatAmount.split(/\./); + let respLength: number; + if (result.length === 2) { + respLength = result[0].length + 1 + decimalPlaces; + } + return floatAmount.slice(0, respLength); +} + +/** + * Repeatedly calls an asynchronous callback function with the specified arguments until it returns a truthy value + * or the timeout is reached. + * + * @param callback - An asynchronous function that takes arguments of type T and returns a promise. + * @param args - The arguments to pass to the callback function. + * @param timeout - The maximum amount of time (in milliseconds) to wait for the callback to return a truthy value. + * @returns A promise that resolves with the callback's result if it returns a truthy value within the timeout. + * @throws An error if the timeout is reached before the callback returns a truthy value. + * + * @template T - The type of the arguments to be passed to the callback function. + */ +export async function waitForCallback( + callback: (args: T) => Promise, + args: T, + timeout: number, +): Promise { + let shouldTerminate = false; + setTimeout(() => { + shouldTerminate = true; + }, timeout); + + let result; + while (!shouldTerminate) { + result = await callback(args).catch(() => { + console.error('Callback failed'); + }); + if (result) return result; + } + + throw new Error( + `callback still not done after ${ + timeout / 1000 + } sec.\nCallback result: ${result}`, + ); +} diff --git a/playwright-tests/services/index.ts b/playwright-tests/services/index.ts new file mode 100644 index 00000000..5ba086fd --- /dev/null +++ b/playwright-tests/services/index.ts @@ -0,0 +1,3 @@ +export * from './reef-knot.service'; +export * from './sdk.service'; +export * from './helpers'; diff --git a/playwright-tests/services/reef-knot.service.ts b/playwright-tests/services/reef-knot.service.ts new file mode 100644 index 00000000..87bf615f --- /dev/null +++ b/playwright-tests/services/reef-knot.service.ts @@ -0,0 +1,84 @@ +import { expect, test, Page } from '@playwright/test'; +import { ReefKnotPage } from '@pages'; +import { WalletPage } from '@lidofinance/wallets-testing-wallets'; +import { TIMEOUT } from '@test-data'; +import { HDAccount, mnemonicToAccount } from 'viem/accounts'; +import { SdkService } from './sdk.service'; + +export class ReefKnotService { + page: Page; + public walletPage: WalletPage; + reefKnotPage: ReefKnotPage; + seedPhrase: HDAccount; + sdkService: SdkService; + + constructor(page: Page, walletPage: WalletPage, seedPhrase: string) { + this.page = page; + this.walletPage = walletPage; + this.reefKnotPage = new ReefKnotPage(this.page); + this.seedPhrase = mnemonicToAccount(seedPhrase); + this.sdkService = new SdkService(this.seedPhrase); + } + + async isConnectedWallet() { + return test.step('Check wallet connection', async () => { + const recentConnectorId = await this.reefKnotPage.getStorageData( + 'wagmi.recentConnectorId', + ); + return recentConnectorId !== '' + ? this.reefKnotPage.header.isAccountSectionVisible() + : false; + }); + } + + async connectWallet(expectConnectionState = true) { + await test.step('Connect wallet', async () => { + if (await this.isConnectedWallet()) return; + await this.reefKnotPage.header.connectWalletButton.click(); + await this.reefKnotPage.walletListModal.confirmConditionWalletModal(); + const walletIcon = this.reefKnotPage.walletListModal.getWalletInModal( + this.walletPage.config.COMMON.CONNECT_BUTTON_NAME, + ); + if ( + (await walletIcon.isEnabled({ timeout: TIMEOUT.LOW })) && + this.walletPage.config.COMMON.SIMPLE_CONNECT + ) { + await walletIcon.click(); + } else { + try { + const [connectWalletPage] = await Promise.all([ + this.page + .context() + .waitForEvent('page', { timeout: TIMEOUT.RPC_WAIT }), + await walletIcon.click(), + ]); + await this.walletPage.connectWallet(connectWalletPage); + } catch { + console.error('Wallet page didnt open'); + } + } + const assertionMessage = expectConnectionState + ? 'Wallet should be connected' + : 'Wallet should be disconnected'; + expect(await this.isConnectedWallet(), assertionMessage).toBe( + expectConnectionState, + ); + }); + } + + async disconnectWalletForce() { + await test.step('Forcefully disconnect wallet', async () => { + await this.page.evaluate(() => { + const localStorageKeys = Object.keys(localStorage); + + // Remove all keys starting with 'wagmi' + localStorageKeys.forEach((key) => { + if (key.startsWith('wagmi')) { + localStorage.removeItem(key); + } + }); + }); + await this.page.reload(); + }); + } +} diff --git a/playwright-tests/services/sdk.service.ts b/playwright-tests/services/sdk.service.ts new file mode 100644 index 00000000..b4f50352 --- /dev/null +++ b/playwright-tests/services/sdk.service.ts @@ -0,0 +1,44 @@ +import { LidoSDK, VIEM_CHAINS } from '@lidofinance/lido-ethereum-sdk'; +import { HDAccount } from 'viem/accounts'; +import { createWalletClient, formatEther, http } from 'viem'; +import { REEF_KNOT_CONFIG } from '@config'; +import { toCut } from './helpers'; + +global.fetch = fetch; + +export enum sdkToken { + ETH = 'ETH', + stETH = 'stETH', + wstETH = 'wstETH', +} + +export class SdkService extends LidoSDK { + constructor(account: HDAccount) { + super({ + chainId: REEF_KNOT_CONFIG.STAND_CONFIG.chainId, + rpcUrls: [REEF_KNOT_CONFIG.STAND_CONFIG.rpcUrl], + web3Provider: createWalletClient({ + account: account, + chain: VIEM_CHAINS[REEF_KNOT_CONFIG.STAND_CONFIG.chainId], + transport: http(), + }), + logMode: 'none', + }); + } + + async getBalanceByToken(token: sdkToken, decimalPlaces: number) { + let balance: string; + switch (token) { + case sdkToken.ETH: + balance = formatEther(await this.core.balanceETH()); + break; + case sdkToken.stETH: + balance = formatEther(await this.steth.balance()); + break; + case sdkToken.wstETH: + balance = formatEther(await this.wsteth.balance()); + break; + } + return toCut(balance, decimalPlaces); + } +} diff --git a/playwright-tests/test-data/index.ts b/playwright-tests/test-data/index.ts new file mode 100644 index 00000000..42507c95 --- /dev/null +++ b/playwright-tests/test-data/index.ts @@ -0,0 +1,4 @@ +export * from './matomo.data'; +export * from './tags.data'; +export * from './timeout.data'; +export * from './wallet-storage.data'; diff --git a/playwright-tests/test-data/matomo.data.ts b/playwright-tests/test-data/matomo.data.ts new file mode 100644 index 00000000..8b087fbe --- /dev/null +++ b/playwright-tests/test-data/matomo.data.ts @@ -0,0 +1,66 @@ +export const CONFIG_MATOMO_CLICK_TO_WALLET_EVENTS = [ + { + eventMessage: 'metaMask clicked', + walletName: 'MetaMask', + }, + { + eventMessage: 'ledgerHID clicked', + walletName: 'Ledger', + }, + { + eventMessage: 'okx clicked', + walletName: 'OKX Wallet', + }, + { + eventMessage: 'walletConnect clicked', + walletName: 'WalletConnect', + }, + { + eventMessage: 'exodus clicked', + walletName: 'Exodus', + }, + { + eventMessage: 'ambire clicked', + walletName: 'Ambire', + }, + { + eventMessage: 'bitget clicked', + walletName: 'Bitget', + }, + { + eventMessage: 'coin98 clicked', + walletName: 'Coin98', + }, + { + eventMessage: 'brave clicked', + walletName: 'Brave', + }, + { + eventMessage: 'coinbase clicked', + walletName: 'Coinbase', + }, + { + eventMessage: 'coinbaseSmartWallet clicked', + walletName: 'Coinbase Smart Wallet', + }, + { + eventMessage: 'trust clicked', + walletName: 'Trust', + }, + { + eventMessage: 'imToken clicked', + walletName: 'imToken', + }, + { + eventMessage: 'xdefi clicked', + walletName: 'XDEFI', + }, + { + eventMessage: 'binanceWallet clicked', + walletName: 'Binance Web3 Wallet', + }, + { + eventMessage: 'browserExtension clicked', + walletName: 'Browser', + }, +]; diff --git a/playwright-tests/test-data/tags.data.ts b/playwright-tests/test-data/tags.data.ts new file mode 100644 index 00000000..43f72825 --- /dev/null +++ b/playwright-tests/test-data/tags.data.ts @@ -0,0 +1,3 @@ +export const Tags = { + connectedWallet: '@connectedWallet', +}; diff --git a/playwright-tests/test-data/timeout.data.ts b/playwright-tests/test-data/timeout.data.ts new file mode 100644 index 00000000..026f0a7a --- /dev/null +++ b/playwright-tests/test-data/timeout.data.ts @@ -0,0 +1,7 @@ +export const TIMEOUT = { + LOW: 500, + DEFAULT: 1000, + MEDIUM: 5000, + HIGH: 30000, + RPC_WAIT: 60000, +}; diff --git a/playwright-tests/test-data/wallet-storage.data.ts b/playwright-tests/test-data/wallet-storage.data.ts new file mode 100644 index 00000000..d5f0a865 --- /dev/null +++ b/playwright-tests/test-data/wallet-storage.data.ts @@ -0,0 +1,31 @@ +interface WagmiStorageData { + recentConnectorId: string; + 'reef-knot_reconnect-wallet-id': string; + disconnectWalletKey: string; +} +export const connectedWalletStorageData = new Map([ + [ + 'metamask', + { + recentConnectorId: '"io.metamask"', // wagmi.recentConnectorId + 'reef-knot_reconnect-wallet-id': '"metaMask"', // wagmi.reef-knot_reconnect-wallet-id + disconnectWalletKey: 'wagmi.io.metamask.disconnected', + }, + ], + [ + 'browser', + { + recentConnectorId: '"browserExtension"', // wagmi.recentConnectorId + 'reef-knot_reconnect-wallet-id': '"browserExtension"', // wagmi.reef-knot_reconnect-wallet-id + disconnectWalletKey: 'wagmi.browserExtension.disconnected', + }, + ], + [ + 'okx', + { + recentConnectorId: '"com.okex.wallet"', // wagmi.recentConnectorId + 'reef-knot_reconnect-wallet-id': '"okx"', // wagmi.reef-knot_reconnect-wallet-id + disconnectWalletKey: 'wagmi.com.okex.wallet.disconnected', + }, + ], +]); diff --git a/playwright-tests/tests/matomo-event-connected-wallet.spec.ts b/playwright-tests/tests/matomo-event-connected-wallet.spec.ts new file mode 100644 index 00000000..a90710f1 --- /dev/null +++ b/playwright-tests/tests/matomo-event-connected-wallet.spec.ts @@ -0,0 +1,112 @@ +import { expect, test } from '@playwright/test'; +import { Tags } from '@test-data'; +import { ReefKnotService } from '@services'; +import { ReefKnotPage } from '@pages'; +import { BrowserService, initBrowserWithWallet } from '@browser'; +import { qase } from 'playwright-qase-reporter'; +import { REEF_KNOT_CONFIG } from '@config'; + +REEF_KNOT_CONFIG.WALLETS.forEach((wallet) => { + test.describe( + `ReefKnot. Matomo events (${wallet.name})`, + { tag: [Tags.connectedWallet, `@${wallet.name}`] }, + async () => { + let browserService: BrowserService; + let reefKnotService: ReefKnotService; + let reefKnotPage: ReefKnotPage; + + test.beforeAll(async () => { + ({ browserService, reefKnotService } = await initBrowserWithWallet( + wallet.name, + )); + reefKnotPage = reefKnotService.reefKnotPage; + await reefKnotPage.goto(); + await reefKnotPage.allowUseCookies(); + await reefKnotService.disconnectWalletForce(); + }); + + test.afterAll(async () => { + await reefKnotService.disconnectWalletForce(); + await browserService.teardown(); + }); + + test(qase(432, `Connect ${wallet.name} wallet`), async () => { + await qase.groupParameters({ + wallet: wallet.name, + eventName: wallet.connectWalletEvent, + }); + + await test.step('Connect wallet and check console.log', async () => { + const [consoleMessage] = await Promise.all([ + reefKnotPage.page.waitForEvent('console', (msg) => + msg.text().includes(wallet.connectWalletEvent), + ), + reefKnotService.connectWallet(), + ]); + expect( + consoleMessage.text(), + `The request parameter "${consoleMessage.text()}" should match the value "${wallet.connectWalletEvent}"`, + ).toContain(wallet.connectWalletEvent); + }); + }); + }, + ); +}); + +test.describe( + `ReefKnot. Matomo events (Browser)`, + { tag: [Tags.connectedWallet, '@browser+metamask'] }, + async () => { + let browserService: BrowserService; + let reefKnotService: ReefKnotService; + let reefKnotPage: ReefKnotPage; + + test.beforeAll(async () => { + ({ browserService, reefKnotService } = + await initBrowserWithWallet('metamask')); + reefKnotPage = reefKnotService.reefKnotPage; + await reefKnotPage.goto(); + await reefKnotPage.allowUseCookies(); + await reefKnotService.disconnectWalletForce(); + }); + + test.afterAll(async () => { + await reefKnotService.disconnectWalletForce(); + await browserService.teardown(); + }); + + test(qase(432, 'Connect Browser wallet'), async () => { + const expectedNameParam = 'browserExtension connected'; + await qase.groupParameters({ + wallet: 'browser+metamask', + eventName: expectedNameParam, + }); + + await test.step('Connect wallet and check console.log', async () => { + const [connectWalletPage] = + await test.step('Connect wallet with Browser button', async () => { + await reefKnotService.reefKnotPage.header.connectWalletButton.click(); + await reefKnotService.reefKnotPage.walletListModal.confirmConditionWalletModal(); + + return await Promise.all([ + reefKnotService.reefKnotPage.waitForPage(), + reefKnotService.reefKnotPage.walletListModal + .getWalletInModal('Browser') + .click(), + ]); + }); + + const [consoleMessage] = await Promise.all([ + reefKnotPage.page.waitForEvent('console', (msg) => + msg.text().includes(expectedNameParam), + ), + reefKnotService.walletPage.connectWallet(connectWalletPage), + ]); + expect( + consoleMessage.text(), + `The request parameter "${consoleMessage.text()}" should match the value "${expectedNameParam}"`, + ).toContain(expectedNameParam); + }); + }); + }, +); diff --git a/playwright-tests/tests/matomo-event.spec.ts b/playwright-tests/tests/matomo-event.spec.ts new file mode 100644 index 00000000..d1d48523 --- /dev/null +++ b/playwright-tests/tests/matomo-event.spec.ts @@ -0,0 +1,99 @@ +import { ReefKnotPage } from '@pages'; +import { CONFIG_MATOMO_CLICK_TO_WALLET_EVENTS } from '@test-data'; +import { test, expect } from '@playwright/test'; +import { qase } from 'playwright-qase-reporter'; + +test.describe('ReefKnot. Matomo events', async () => { + let reefKnotPage: ReefKnotPage; + + test.beforeAll(async ({ browser }) => { + reefKnotPage = new ReefKnotPage(await browser.newPage()); + await reefKnotPage.goto(); + await reefKnotPage.allowUseCookies(); + }); + + test.beforeEach(async () => { + await reefKnotPage.goto(); + }); + + test.afterEach(async () => { + // close unnecessary pages, after link clicking + if (reefKnotPage.page.context().pages().length > 1) { + await reefKnotPage.page.context().pages()[1].close(); + } + }); + + CONFIG_MATOMO_CLICK_TO_WALLET_EVENTS.forEach((event) => { + test(qase(430, `Click to "${event.walletName}" wallet`), async () => { + await qase.groupParameters({ + wallet: event.walletName, + event: event.eventMessage, + }); + + await test.step('Open the the modal with wallet list', async () => { + await reefKnotPage.header.connectWalletButton.click(); + await reefKnotPage.walletListModal.confirmConditionWalletModal(); + await reefKnotPage.walletListModal.moreWalletsButton.click(); + }); + + await test.step(`Click to wallet ${event.walletName} and check console log`, async () => { + const [consoleMessage] = await Promise.all([ + reefKnotPage.page.waitForEvent('console', (msg) => + msg.text().includes('metrics'), + ), + reefKnotPage.walletListModal + .getWalletInModal(event.walletName) + .click(), + ]); + expect( + consoleMessage.text(), + `The request parameter "${consoleMessage.text()}" should match the value "${event.eventMessage}"`, + ).toContain(event.eventMessage); + }); + }); + }); + + test( + qase(431, 'Click to buttons "More wallets" and "Less wallets"'), + async () => { + const expectedMoreWalletsNameParam = 'more wallets clicked'; + const expectedLessWalletsNameParam = 'less wallets clicked'; + + await qase.groupParameters({ + eventMoreWallet: expectedMoreWalletsNameParam, + eventLessWallet: expectedLessWalletsNameParam, + }); + + await test.step('Open the wallet popup', async () => { + await reefKnotPage.header.connectWalletButton.click(); + await reefKnotPage.walletListModal.confirmConditionWalletModal(); + }); + + await test.step('Click to "More wallets"', async () => { + const [consoleMessage] = await Promise.all([ + reefKnotPage.page.waitForEvent('console', (msg) => + msg.text().includes('metrics'), + ), + reefKnotPage.walletListModal.moreWalletsButton.click(), + ]); + expect( + consoleMessage.text(), + `The request parameter "${consoleMessage.text()}" should match the value "${expectedMoreWalletsNameParam}"`, + ).toContain(expectedMoreWalletsNameParam); + }); + + await test.step('Click to "Less wallets"', async () => { + const [consoleMessage] = await Promise.all([ + reefKnotPage.page.waitForEvent('console', (msg) => + msg.text().includes('metrics'), + ), + reefKnotPage.walletListModal.lessWalletsButton.click(), + ]); + expect( + consoleMessage.text(), + `The request parameter "${consoleMessage.text()}" should match the value "${expectedLessWalletsNameParam}"`, + ).toContain(expectedLessWalletsNameParam); + }); + }, + ); +}); diff --git a/playwright-tests/tests/stake-tx.spec.ts b/playwright-tests/tests/stake-tx.spec.ts new file mode 100644 index 00000000..efa9105f --- /dev/null +++ b/playwright-tests/tests/stake-tx.spec.ts @@ -0,0 +1,82 @@ +import { expect, test } from '@playwright/test'; +import { Tags, TIMEOUT } from '@test-data'; +import { BrowserService, initBrowserWithWallet } from '@browser'; +import { ReefKnotService, toCut } from '@services'; +import { ReefKnotPage } from '@pages'; +import { qase } from 'playwright-qase-reporter'; +import { REEF_KNOT_CONFIG } from '@config'; + +REEF_KNOT_CONFIG.WALLETS.forEach((wallet) => { + test.describe( + `ReefKnot. Stake ETH transaction (${wallet.name})`, + { tag: [Tags.connectedWallet, `@${wallet.name}`] }, + async () => { + const stakeAmount = '0.0003'; + let browserService: BrowserService; + let reefKnotService: ReefKnotService; + let reefKnotPage: ReefKnotPage; + + test.beforeAll(async () => { + ({ browserService, reefKnotService } = await initBrowserWithWallet( + wallet.name, + )); + reefKnotPage = reefKnotService.reefKnotPage; + await reefKnotPage.goto(); + await reefKnotPage.allowUseCookies(); + await reefKnotService.connectWallet(); + }); + + test.afterAll(async () => { + await reefKnotService.disconnectWalletForce(); + await browserService.teardown(); + }); + + test(`Stake ${stakeAmount} ETH`, async () => { + await qase.groupParameters({ + wallet: wallet.name, + txAmount: stakeAmount, + }); + + const newStEthBalance = + await test.step('Calculate the stETH amount result', async () => { + const stEthBalance = parseFloat( + await reefKnotPage.waitForBalance( + reefKnotPage.statsBlock.stethBalance, + ), + ); + return toCut(String(stEthBalance + parseFloat(stakeAmount)), 4); + }); + + const txPage = + await test.step('Fill the amount input and click to Submit button', async () => { + await reefKnotPage.statsBlock.amountInput.fill(stakeAmount); + return await reefKnotPage.clickStakeButton(); + }); + + await reefKnotService.walletPage.confirmTx(txPage, true); + + await test.step('Waiting for transaction success', async () => { + await expect( + reefKnotPage.stakeBlock.stakeBtn, + 'The Stake button should be disabled', + ).toBeDisabled(); + await reefKnotPage.toast.successToast.waitFor({ + state: 'visible', + timeout: TIMEOUT.RPC_WAIT, + }); + await expect( + reefKnotPage.stakeBlock.stakeBtn, + 'The Stake button should be enabled after success tx', + ).toBeEnabled(); + }); + + await test.step('Check the new stETH balance', async () => { + await expect( + reefKnotPage.statsBlock.stethBalance, + 'The displayed stETH balance should be updated after transaction success', + ).toContainText(newStEthBalance, { timeout: TIMEOUT.HIGH }); + }); + }); + }, + ); +}); diff --git a/playwright-tests/tests/wallet-connection.spec.ts b/playwright-tests/tests/wallet-connection.spec.ts new file mode 100644 index 00000000..f5216adc --- /dev/null +++ b/playwright-tests/tests/wallet-connection.spec.ts @@ -0,0 +1,153 @@ +import { ReefKnotService } from '@services'; +import { connectedWalletStorageData, Tags } from '@test-data'; +import { ReefKnotPage } from '@pages'; +import { expect, test } from '@playwright/test'; +import { qase } from 'playwright-qase-reporter'; +import { BrowserService, initBrowserWithWallet } from '@browser'; +import { REEF_KNOT_CONFIG } from '@config'; + +REEF_KNOT_CONFIG.WALLETS.forEach((wallet) => { + test.describe( + `ReefKnot. Wallet connection (${wallet.name})`, + { tag: [Tags.connectedWallet, `@${wallet.name}`] }, + async () => { + test.describe.configure({ mode: 'serial' }); // Serial mode is used because the tests in this test.describe are related to each other. + let browserService: BrowserService; + let reefKnotService: ReefKnotService; + let reefKnotPage: ReefKnotPage; + + test.beforeAll(async () => { + ({ browserService, reefKnotService } = await initBrowserWithWallet( + wallet.name, + )); + reefKnotPage = reefKnotService.reefKnotPage; + await reefKnotPage.goto(); + await reefKnotPage.allowUseCookies(); + }); + + test.afterAll(async () => { + await reefKnotService.disconnectWalletForce(); + await browserService.teardown(); + }); + + test(qase(434, 'Connect wallet'), async () => { + await qase.parameters({ wallet: wallet.name }); + + await test.step('Check the stand appearance before wallet connection', async () => { + await expect( + reefKnotPage.statsBlock.mainComponent, + 'The statistic block should not be visible before the wallet is connected', + ).not.toBeVisible(); + await expect( + reefKnotPage.header.accountButton, + 'The account button should not be visible before the wallet is connected', + ).not.toBeVisible(); + }); + await reefKnotService.connectWallet(); + await test.step('Check the stand appearance after wallet connection', async () => { + await expect( + reefKnotPage.statsBlock.mainComponent, + 'The statistic block should be visible after the wallet is connected', + ).toBeVisible(); + await expect( + reefKnotPage.header.accountButton, + 'The account button should be visible after the wallet is connected', + ).toBeVisible(); + }); + await test.step('Check local storage with connected', async () => { + expect( + await reefKnotPage.getStorageData('wagmi.recentConnectorId'), + 'The value of "wagmi.recentConnectorId" should match the connected wallet', + ).toBe( + connectedWalletStorageData.get( + browserService.commonWalletConfig.WALLET_NAME, + ).recentConnectorId, + ); + expect( + await reefKnotPage.getStorageData( + 'wagmi.reef-knot_reconnect-wallet-id', + ), + 'The value of "wagmi.reef-knot_reconnect-wallet-id" should match the connected wallet', + ).toBe( + connectedWalletStorageData.get( + browserService.commonWalletConfig.WALLET_NAME, + )['reef-knot_reconnect-wallet-id'], + ); + }); + }); + + test( + qase(440, 'Reload page and check that the wallet connection remains'), + async () => { + await qase.parameters({ wallet: wallet.name }); + + await reefKnotPage.page.reload(); + await expect( + reefKnotPage.header.accountButton, + 'The account button should remain visible after the page is reloaded', + ).toBeVisible(); + await expect( + reefKnotPage.statsBlock.mainComponent, + 'The statistic block should remain visible after the page is reloaded', + ).toBeVisible(); + }, + ); + + test(qase(441, 'Disconnect wallet'), async () => { + await qase.parameters({ wallet: wallet.name }); + + await reefKnotPage.disconnectWallet(); + await test.step('Check the stand appearance after wallet disconnection', async () => { + await expect( + reefKnotPage.statsBlock.mainComponent, + 'The statistic block should not be visible after the wallet is disconnected', + ).not.toBeVisible(); + await expect( + reefKnotPage.header.accountButton, + 'The account button should not be visible after the wallet is disconnected', + ).not.toBeVisible(); + }); + await test.step('Check local storage with connected', async () => { + expect( + await reefKnotPage.getStorageData('wagmi.recentConnectorId'), + 'The value of "wagmi.recentConnectorId" should be NaN', + ).toBeNull(); + expect( + await reefKnotPage.getStorageData( + 'wagmi.reef-knot_reconnect-wallet-id', + ), + 'The value of "wagmi.reef-knot_reconnect-wallet-id" should be NaN', + ).toBeNull(); + expect( + await reefKnotPage.getStorageData( + connectedWalletStorageData.get( + browserService.commonWalletConfig.WALLET_NAME, + ).disconnectWalletKey, + ), + 'The recent wallet disconnection status should be "true"', + ).toBe('true'); + }); + }); + + test( + qase( + 442, + 'Reload page and check that the wallet disconnection remains', + ), + async () => { + await qase.parameters({ wallet: wallet.name }); + + await reefKnotPage.page.reload(); + await expect( + reefKnotPage.header.accountButton, + 'The account button should not be visible after the page is reloaded', + ).not.toBeVisible(); + await expect( + reefKnotPage.statsBlock.mainComponent, + 'The statistic block should not be visible after the page is reloaded', + ).not.toBeVisible(); + }, + ); + }, + ); +}); diff --git a/playwright-tests/tests/wallet-stats.spec.ts b/playwright-tests/tests/wallet-stats.spec.ts new file mode 100644 index 00000000..adfb6282 --- /dev/null +++ b/playwright-tests/tests/wallet-stats.spec.ts @@ -0,0 +1,91 @@ +import { expect, test } from '@playwright/test'; +import { qase } from 'playwright-qase-reporter'; +import { REEF_KNOT_CONFIG } from '@config'; +import { ReefKnotService, sdkToken } from '@services'; +import { Tags } from '@test-data'; +import { BrowserService, initBrowserWithWallet } from '@browser'; +import { ReefKnotPage } from '@pages'; + +REEF_KNOT_CONFIG.WALLETS.forEach((wallet) => { + test.describe( + `ReefKnot. Check statistic (${wallet.name})`, + { tag: [Tags.connectedWallet, `@${wallet.name}`] }, + async () => { + let browserService: BrowserService; + let reefKnotService: ReefKnotService; + let reefKnotPage: ReefKnotPage; + + test.beforeAll(async () => { + ({ browserService, reefKnotService } = await initBrowserWithWallet( + wallet.name, + )); + reefKnotPage = reefKnotService.reefKnotPage; + await reefKnotPage.goto(); + await reefKnotPage.allowUseCookies(); + await reefKnotService.connectWallet(); + }); + + test.afterAll(async () => { + await reefKnotService.disconnectWalletForce(); + await browserService.teardown(); + }); + + test(qase(435, 'Check provider name'), async () => { + await qase.parameters({ wallet: wallet.name }); + + await expect( + reefKnotPage.statsBlock.providerValue, + 'The connected wallet name should be displayed correctly', + ).toContainText( + reefKnotService.walletPage.config.COMMON.CONNECTED_WALLET_NAME, + ); + }); + + test(qase(436, 'Check chainId'), async () => { + await qase.parameters({ wallet: wallet.name }); + + await expect( + reefKnotPage.statsBlock.chainIdValue, + 'The connected network ID should be displayed correctly', + ).toContainText(String(REEF_KNOT_CONFIG.STAND_CONFIG.chainId)); + }); + + test(qase(437, 'Check ETH balance'), async () => { + await qase.parameters({ wallet: wallet.name }); + expect( + await reefKnotPage.waitForBalance(reefKnotPage.statsBlock.ethBalance), + 'The address ETH balance should match the value displayed in the ReefKnot stats block', + ).toContain( + await reefKnotService.sdkService.getBalanceByToken(sdkToken.ETH, 5), + ); + }); + + test(qase(438, 'Check stETH balance'), async () => { + await qase.parameters({ wallet: wallet.name }); + expect( + await reefKnotPage.waitForBalance( + reefKnotPage.statsBlock.stethBalance, + ), + 'The address stETH balance should match the value displayed in the ReefKnot stats block', + ).toContain( + await reefKnotService.sdkService.getBalanceByToken(sdkToken.stETH, 5), + ); + }); + + test(qase(439, 'Check wstETH balance'), async () => { + await qase.parameters({ wallet: wallet.name }); + expect( + await reefKnotPage.waitForBalance( + reefKnotPage.statsBlock.wstethBalance, + ), + 'The address wstETH balance should match the value displayed in the ReefKnot stats block', + ).toContain( + await reefKnotService.sdkService.getBalanceByToken( + sdkToken.wstETH, + 5, + ), + ); + }); + }, + ); +}); diff --git a/playwright-tests/tsconfig.json b/playwright-tests/tsconfig.json new file mode 100644 index 00000000..25718e7c --- /dev/null +++ b/playwright-tests/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "skipLibCheck": true, + "alwaysStrict": true, + "strict": true, + "esModuleInterop": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "strictNullChecks": false, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": false, + "declaration": true, + "declarationMap": true, + "incremental": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "module": "commonjs", + "target": "ES2020", + "resolveJsonModule": true, + "composite": true, + "sourceMap": true, + "lib": [ + "ES2020", + "dom" + ], + "baseUrl": ".", + "paths": { + "@test-data": ["test-data"], + "@config": ["config"], + "@pages": ["pages"], + "@services": ["services"], + "@browser": ["services/browser"] + } + }, +} diff --git a/playwright-tests/yarn.lock b/playwright-tests/yarn.lock new file mode 100644 index 00000000..7b7b6f77 --- /dev/null +++ b/playwright-tests/yarn.lock @@ -0,0 +1,1229 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"2-thenable@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/2-thenable/-/2-thenable-1.0.0.tgz#56e9a2e363293b1e507f501aac1aa9927670b2fc" + integrity sha512-HqiDzaLDFCXkcCO/SwoyhRwqYtINFHF7t9BDRq4x90TOKNAJpiqUt9X5lQ08bwxYzc067HUywDjGySpebHcUpw== + dependencies: + d "1" + es5-ext "^0.10.47" + +"@adraffy/ens-normalize@^1.10.1": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" + integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== + +"@babel/code-frame@^7.12.13": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/runtime@^7.15.4": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@graphql-typed-document-node/core@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== + +"@jest/expect-utils@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" + integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== + dependencies: + jest-get-type "^28.0.2" + +"@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" + integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@lidofinance/lido-ethereum-sdk@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@lidofinance/lido-ethereum-sdk/-/lido-ethereum-sdk-4.0.1.tgz#0fd4135d2116a1e3c6c1a35503c43fcb940a0735" + integrity sha512-ft+tA63ZyAxrnvCZijn2DcLMpGnSKkZ6VstNp/AxPL3EiAcQFDxQQWbYMAaLsYwNntVRwMLroajmASPhsvfx2g== + dependencies: + "@ethersproject/bytes" "^5.7.0" + graphql "^16.8.1" + graphql-request "^6.1.0" + +"@lidofinance/wallets-testing-extensions@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@lidofinance/wallets-testing-extensions/-/wallets-testing-extensions-1.0.3.tgz#315228545646cd46944a6fc9c9f6544b54b955e3" + integrity sha512-uvlHygXt2jpfkVA0Ig6Jtg/4aw96ypdCJDcCv9QuGnktwYV2gyk4rd1kEt8XUzp40CS0IvpgY2Lx+5LTq1IDPQ== + dependencies: + "@nestjs/common" "^8.2.5" + "@nestjs/config" "^2.2.0" + "@playwright/test" "^1.44.1" + axios "^0.27.2" + reflect-metadata "^0.1.13" + rxjs "^7.5.6" + unzipper "^0.10.11" + +"@lidofinance/wallets-testing-wallets@1.18.0": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@lidofinance/wallets-testing-wallets/-/wallets-testing-wallets-1.18.0.tgz#c29a37e1ca8884efba02dcc5e0012f8206d93c7a" + integrity sha512-dOvTABDlzF+MVTwdzp7Rpr2LYrLHuUGYzW8hjd13/aQP5gcsfw3t9WomPdm+GG6a0ttXZgsv0pwWkDsuwrccSA== + dependencies: + "@playwright/test" "^1.44.1" + expect "^28.1.3" + reflect-metadata "^0.1.13" + rxjs "^7.5.6" + viem "^2.21.40" + +"@lidofinance/wallets-testing-widgets@1.1.52": + version "1.1.52" + resolved "https://registry.yarnpkg.com/@lidofinance/wallets-testing-widgets/-/wallets-testing-widgets-1.1.52.tgz#f8933d285e034ca3063723b104a2b78d434e60a6" + integrity sha512-RIvKJUMynQd6qdyogmxeS01Gjs03/82InzQAH2DYYSdNtk6yWI1H5noHyeXpG/hT60UtgX1IEdNEXWfQFNGZCg== + dependencies: + "@lidofinance/wallets-testing-wallets" "1.18.0" + "@nestjs/common" "^8.2.5" + "@nestjs/config" "^2.2.0" + "@playwright/test" "^1.44.1" + expect "^28.1.3" + reflect-metadata "^0.1.13" + rxjs "^7.5.6" + +"@nestjs/common@^8.2.5": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-8.4.7.tgz#fc4a575b797e230bb5a0bcab6da8b796aa88d605" + integrity sha512-m/YsbcBal+gA5CFrDpqXqsSfylo+DIQrkFY3qhVIltsYRfu8ct8J9pqsTO6OPf3mvqdOpFGrV5sBjoyAzOBvsw== + dependencies: + axios "0.27.2" + iterare "1.2.1" + tslib "2.4.0" + uuid "8.3.2" + +"@nestjs/config@^2.2.0": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@nestjs/config/-/config-2.3.4.tgz#6378a3c5b163a429e9ba728f28eed7513855bd50" + integrity sha512-IGdSF+0F9MJO6dCRTEahdxPz4iVijjtolcFBxnY+2QYM3bXYQvAgzskGZi+WkAFJN/VzR3TEp60gN5sI74GxPA== + dependencies: + dotenv "16.1.4" + dotenv-expand "10.0.0" + lodash "4.17.21" + uuid "9.0.0" + +"@noble/curves@1.6.0", "@noble/curves@^1.4.0", "@noble/curves@^1.6.0", "@noble/curves@~1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b" + integrity sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ== + dependencies: + "@noble/hashes" "1.5.0" + +"@noble/hashes@1.5.0", "@noble/hashes@^1.4.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" + integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== + +"@playwright/test@^1.44.1", "@playwright/test@^1.48.2": + version "1.49.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e" + integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw== + dependencies: + playwright "1.49.0" + +"@scure/base@~1.1.7", "@scure/base@~1.1.8": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== + +"@scure/bip32@1.5.0", "@scure/bip32@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.5.0.tgz#dd4a2e1b8a9da60e012e776d954c4186db6328e6" + integrity sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw== + dependencies: + "@noble/curves" "~1.6.0" + "@noble/hashes" "~1.5.0" + "@scure/base" "~1.1.7" + +"@scure/bip39@1.4.0", "@scure/bip39@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.4.0.tgz#664d4f851564e2e1d4bffa0339f9546ea55960a6" + integrity sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw== + dependencies: + "@noble/hashes" "~1.5.0" + "@scure/base" "~1.1.8" + +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" + integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/node@*": + version "22.9.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.1.tgz#bdf91c36e0e7ecfb7257b2d75bf1b206b308ca71" + integrity sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg== + dependencies: + undici-types "~6.19.8" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + +abitype@1.0.6, abitype@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.6.tgz#76410903e1d88e34f1362746e2d407513c38565b" + integrity sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A== + +ajv@^8.0.0, ajv@^8.12.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios-retry@^3.5.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.9.1.tgz#c8924a8781c8e0a2c5244abf773deb7566b3830d" + integrity sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w== + dependencies: + "@babel/runtime" "^7.15.4" + is-retry-allowed "^2.2.0" + +axios@0.27.2, axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +axios@^0.28.0: + version "0.28.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.28.1.tgz#2a7bcd34a3837b71ee1a5ca3762214b86b703e70" + integrity sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +big-integer@^1.6.17: + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== + +binary@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== + dependencies: + buffers "~0.1.1" + chainsaw "~0.1.0" + +bluebird@~3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +buffer-indexof-polyfill@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" + integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== + +buffers@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== + +chainsaw@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== + dependencies: + traverse ">=0.3.0 <0.4" + +chalk@^4.0.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +child-process-ext@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/child-process-ext/-/child-process-ext-3.0.2.tgz#701b77a3a27b8eefdf7264d8350b29c3a9cbba32" + integrity sha512-oBePsLbQpTJFxzwyCvs9yWWF0OEM6vGGepHwt1stqmX7QQqOuDc8j2ywdvAs9Tvi44TT7d9ackqhR4Q10l1u8w== + dependencies: + cross-spawn "^7.0.3" + es5-ext "^0.10.62" + log "^6.3.1" + split2 "^3.2.2" + stream-promise "^3.2.0" + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + +cross-spawn@^7.0.3: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +diff-sequences@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" + integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== + +dotenv-expand@10.0.0, dotenv-expand@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" + integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== + +dotenv@16.1.4: + version "16.1.4" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.1.4.tgz#67ac1a10cd9c25f5ba604e4e08bc77c0ebe0ca8c" + integrity sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw== + +dotenv@^16.0.0, dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== + dependencies: + readable-stream "^2.0.2" + +duration@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/duration/-/duration-0.2.2.tgz#ddf149bc3bc6901150fe9017111d016b3357f529" + integrity sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg== + dependencies: + d "1" + es5-ext "~0.10.46" + +env-schema@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/env-schema/-/env-schema-5.2.1.tgz#7af7d5464690f18c862c21f9b1fe662d3ae6485a" + integrity sha512-gWMNrQ3dVHAZcCx7epiFwgXcyfBh4heD/6+OK3bEbke3uL+KqwYA9nUOwzJyRZh1cJOFcwdPuY1n0GKSFlSWAg== + dependencies: + ajv "^8.0.0" + dotenv "^16.0.0" + dotenv-expand "^10.0.0" + +es5-ext@^0.10.35, es5-ext@^0.10.47, es5-ext@^0.10.49, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.46: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +expect@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" + integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== + dependencies: + "@jest/expect-utils" "^28.1.3" + jest-get-type "^28.0.2" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +follow-redirects@^1.14.9, follow-redirects@^1.15.0: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.2.2, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphql-request@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== + dependencies: + "@graphql-typed-document-node/core" "^3.2.0" + cross-fetch "^3.1.5" + +graphql@^16.8.1: + version "16.9.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-retry-allowed@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" + integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isows@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== + +iterare@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/iterare/-/iterare-1.2.1.tgz#139c400ff7363690e33abffa33cbba8920f00042" + integrity sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q== + +jest-diff@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" + integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== + dependencies: + chalk "^4.0.0" + diff-sequences "^28.1.1" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-get-type@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" + integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== + +jest-matcher-utils@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" + integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== + dependencies: + chalk "^4.0.0" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" + integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash@4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log@^6.3.1: + version "6.3.2" + resolved "https://registry.yarnpkg.com/log/-/log-6.3.2.tgz#8e37a672b06161acc994e2b679726c7f3c011016" + integrity sha512-ek8NRg/OPvS9ISOJNWNAz5vZcpYacWNFDWNJjj5OXsc6YuKacfey6wF04cXz/tOJIVrZ2nGSkHpAY5qKtF6ISg== + dependencies: + d "^1.0.2" + duration "^0.2.2" + es5-ext "^0.10.64" + event-emitter "^0.3.5" + sprintf-kit "^2.0.2" + type "^2.7.3" + uni-global "^1.0.0" + +micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.33: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"mkdirp@>=0.5 0": + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +ox@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.1.2.tgz#0f791be2ccabeaf4928e6d423498fe1c8094e560" + integrity sha512-ak/8K0Rtphg9vnRJlbOdaX9R7cmxD2MiSthjWGaQdMk3D7hrAlDoM+6Lxn7hN52Za3vrXfZ7enfke/5WjolDww== + dependencies: + "@adraffy/ens-normalize" "^1.10.1" + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@scure/bip32" "^1.5.0" + "@scure/bip39" "^1.4.0" + abitype "^1.0.6" + eventemitter3 "5.0.1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +picocolors@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +playwright-core@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.49.0.tgz#8e69ffed3f41855b854982f3632f2922c890afcb" + integrity sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA== + +playwright-qase-reporter@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/playwright-qase-reporter/-/playwright-qase-reporter-2.0.16.tgz#65db1669a480ca95beebed5adec3d18ae8303f14" + integrity sha512-MrZWOmIUdeKxv1hNR+3xMyVnb8ZzFk8wMDIdmbfy1AiJ/57q1WovCgOnHnOHW66lG7oDrdPxJLKwClh7JiMibA== + dependencies: + chalk "^4.1.2" + qase-javascript-commons "~2.2.0" + uuid "^9.0.0" + +playwright@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.49.0.tgz#df6b9e05423377a99658202844a294a8afb95d0a" + integrity sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A== + dependencies: + playwright-core "1.49.0" + optionalDependencies: + fsevents "2.3.2" + +pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" + integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== + dependencies: + "@jest/schemas" "^28.1.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +qase-javascript-commons@~2.2.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/qase-javascript-commons/-/qase-javascript-commons-2.2.5.tgz#a6c814df6faf71f635708dcc27815dbaf7954fef" + integrity sha512-35Vuzfx8UgLPz62BSFaITLcK1lrJft3BVdexmKZ6+v+TjKyjbucx/NF/13XsvRbbdYhSdqSW+NRhix12bnygVw== + dependencies: + ajv "^8.12.0" + chalk "^4.1.2" + child-process-ext "^3.0.2" + env-schema "^5.2.0" + form-data "^4.0.0" + lodash.get "^4.4.2" + lodash.merge "^4.6.2" + lodash.mergewith "^4.6.2" + mime-types "^2.1.33" + qaseio "~2.4.0" + strip-ansi "^6.0.1" + uuid "^9.0.0" + +qaseio@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/qaseio/-/qaseio-2.4.0.tgz#64b6d062cbdc53a2768591f788238719bb5a882d" + integrity sha512-M/jK3lkxtrLZ+PI4dPzSIyzgSydsCgB0XKtPsQ+Ip/OnCP8ofoooNqSj1DxflCtUlJ4dHtmDLWVVPhfw2JoHyQ== + dependencies: + axios "^0.28.0" + axios-retry "^3.5.0" + +react-is@^18.0.0: + 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== + +readable-stream@^2.0.2, readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +reflect-metadata@^0.1.13: + version "0.1.14" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" + integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +rimraf@2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rxjs@^7.5.6: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +setimmediate@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +split2@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +sprintf-kit@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/sprintf-kit/-/sprintf-kit-2.0.2.tgz#e79f0c6076d2bc656b5fb55fa43b737ca98d3ecf" + integrity sha512-lnapdj6W4LflHZGKvl9eVkz5YF0xaTrqpRWVA4cNVOTedwqifIP8ooGImldzT/4IAN5KXFQAyXTdLidYVQdyag== + dependencies: + es5-ext "^0.10.64" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +stream-promise@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-promise/-/stream-promise-3.2.0.tgz#bad976f2d0e1f11d56cc95cc11907cfd869a27ff" + integrity sha512-P+7muTGs2C8yRcgJw/PPt61q7O517tDHiwYEzMWo1GSBCcZedUMT/clz7vUNsSxFphIlJ6QUL4GexQKlfJoVtA== + dependencies: + "2-thenable" "^1.0.0" + es5-ext "^0.10.49" + is-stream "^1.1.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +"traverse@>=0.3.0 <0.4": + version "0.3.9" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== + +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tslib@^2.1.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type@^2.5.0, type@^2.7.2, type@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + +undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +uni-global@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/uni-global/-/uni-global-1.0.0.tgz#3583c449e87a2d9dc270ea221410a649bcdad040" + integrity sha512-WWM3HP+siTxzIWPNUg7hZ4XO8clKi6NoCAJJWnuRL+BAqyFXF8gC03WNyTefGoUXYc47uYgXxpKLIEvo65PEHw== + dependencies: + type "^2.5.0" + +unzipper@^0.10.11: + version "0.10.14" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" + integrity sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "^1.0.12" + graceful-fs "^4.2.2" + listenercount "~1.0.1" + readable-stream "~2.3.6" + setimmediate "~1.0.4" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuid@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +viem@^2.21.40: + version "2.21.48" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.48.tgz#f8f1d0bf5381282e22e6a1f8b72ebd6e64426480" + integrity sha512-/hBHyG1gdIIuiQv0z9YmzXl5eWJa0UCZGwkeuQzH2Bmg6FIEwZeEcxgiytXZydip+p2wMBFa1jdr7o5O1+mrIg== + dependencies: + "@noble/curves" "1.6.0" + "@noble/hashes" "1.5.0" + "@scure/bip32" "1.5.0" + "@scure/bip39" "1.4.0" + abitype "1.0.6" + isows "1.0.6" + ox "0.1.2" + webauthn-p256 "0.0.10" + ws "8.18.0" + +webauthn-p256@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.10.tgz#877e75abe8348d3e14485932968edf3325fd2fdd" + integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== + dependencies: + "@noble/curves" "^1.4.0" + "@noble/hashes" "^1.4.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@8.18.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +zod@^3.24.1: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" + integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==