diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml deleted file mode 100644 index f366f1d913..0000000000 --- a/.github/workflows/nix.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Nix CI - -on: {} - -permissions: - id-token: write - contents: read - -concurrency: - group: ${{ github.sha }} - cancel-in-progress: true - -env: - AWS_REGION: eu-central-1 - AWS_ROLE_ARN: arn:aws:iam::332405224602:role/ci - ECR_REGISTRY: 332405224602.dkr.ecr.eu-central-1.amazonaws.com - S3_CACHE: s3://iog-catalyst-nix?region=eu-central-1 - -jobs: - discover: - outputs: - hits: ${{ steps.discovery.outputs.hits }} - nix_conf: ${{ steps.discovery.outputs.nix_conf }} - runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }} - steps: - - name: Standard Discovery - uses: divnix/std-action/discover@v0.0.4 - id: discovery - build-packages: - needs: discover - strategy: - fail-fast: false - matrix: - target: ${{ fromJSON(needs.discover.outputs.hits).packages.build }} - name: ${{ matrix.target.cell }} - ${{ matrix.target.name }} - runs-on: ubuntu-latest - steps: - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1.7.0 - with: - role-to-assume: ${{ env.AWS_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - - uses: divnix/std-action/run@v0.0.4 - with: - extra_nix_config: | - ${{ needs.discover.outputs.nix_conf }} - json: ${{ toJSON(matrix.target) }} - nix_key: ${{ secrets.NIX_SIGNING_KEY }} - cache: ${{ env.S3_CACHE }} - build-devshells: - needs: discover - strategy: - fail-fast: false - matrix: - target: ${{ fromJSON(needs.discover.outputs.hits).devshells.build }} - name: ${{ matrix.target.cell }} - ${{ matrix.target.name }} - runs-on: ubuntu-latest - steps: - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1.7.0 - with: - role-to-assume: ${{ env.AWS_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - - uses: divnix/std-action/run@v0.0.4 - with: - extra_nix_config: | - ${{ needs.discover.outputs.nix_conf }} - json: ${{ toJSON(matrix.target) }} - nix_key: ${{ secrets.NIX_SIGNING_KEY }} - cache: ${{ env.S3_CACHE }} - publish-containers: - if: github.ref == 'refs/heads/main' - needs: - - discover - - build-packages - strategy: - fail-fast: false - matrix: - target: ${{ fromJSON(needs.discover.outputs.hits).containers.publish }} - name: ${{ matrix.target.cell }} - ${{ matrix.target.name }} - runs-on: ubuntu-latest - steps: - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1.7.0 - with: - role-to-assume: ${{ env.AWS_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - - name: Configure Registry - run: | - aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin "${{ env.ECR_REGISTRY }}" - - uses: divnix/std-action/run@v0.0.4 - with: - extra_nix_config: | - ${{ needs.discover.outputs.nix_conf }} - json: ${{ toJSON(matrix.target) }} - nix_key: ${{ secrets.NIX_SIGNING_KEY }} - cache: ${{ env.S3_CACHE }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5531f0eee5..8e5cf8c281 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -229,7 +229,7 @@ jobs: run: cargo install --force cargo-make - name: Install refinery - run: cargo install refinery_cli + run: cargo install refinery_cli --version 0.8.7 --locked - name: Install dependencies run: diff --git a/.gitignore b/.gitignore index 6a888d4651..57771843ef 100644 --- a/.gitignore +++ b/.gitignore @@ -117,5 +117,8 @@ tests/tmp/ lefthook.yml treefmt.toml -# local earthly Environments -local/* \ No newline at end of file +# local earthly environments +local/* +tests/wallet-automation/typhon/usrdatadir/* +tests/wallet-automation/node_modules/* +tests/wallet-automation/typhon/extensions/* \ No newline at end of file diff --git a/Earthfile b/Earthfile index b762609895..6a3fe099a2 100644 --- a/Earthfile +++ b/Earthfile @@ -8,7 +8,7 @@ rust-toolchain: # Installs Cargo chef install-chef: FROM +rust-toolchain - RUN cargo install --debug cargo-chef + RUN cargo install --debug --version 0.1.59 cargo-chef --locked # Prepares the local cache prepare-cache: diff --git a/tests/wallet-automation/Earthfile b/tests/wallet-automation/Earthfile new file mode 100644 index 0000000000..65c1787f05 --- /dev/null +++ b/tests/wallet-automation/Earthfile @@ -0,0 +1,31 @@ +VERSION 0.7 + +# Define a base target for dependencies +deps: + FROM mcr.microsoft.com/playwright:v1.41.0-jammy + WORKDIR /wallet-automation + + # Consolidate RUN commands to reduce layers and ensure cleaner installation + RUN apt-get update && apt-get install -y \ + libnss3 libatk-bridge2.0-0 libdrm-dev libxkbcommon-dev libgbm-dev libasound-dev libatspi2.0-0 libxshmfence-dev postgresql-client xvfb python3.11 python3-pip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + + COPY package.json . + COPY package-lock.json . + + RUN npm install + ENV PATH "/wallet-automation/node_modules/.bin:$PATH" + +# Define a source target that builds upon deps +src: + FROM +deps + + COPY --dir typhon . + COPY playwright.config.ts . + COPY global-setup.ts . + +# Define a test target that builds upon deps +test: + FROM +src + RUN xvfb-run -a npx playwright test diff --git a/tests/wallet-automation/global-setup.ts b/tests/wallet-automation/global-setup.ts new file mode 100644 index 0000000000..d650994b4b --- /dev/null +++ b/tests/wallet-automation/global-setup.ts @@ -0,0 +1,64 @@ +import { test } from '@playwright/test'; +import * as fs from 'fs/promises'; +import * as path from 'path'; + +const typhonId = 'KFDNIEFADAANBJODLDOHAEDPHAFOFFOH'; +const url = `https://clients2.google.com/service/update2/crx?response=redirect&os=win&arch=x64&os_arch=x86_64&nacl_arch=x86-64&prod=chromiumcrx&prodchannel=beta&prodversion=79.0.3945.53&lang=ru&acceptformat=crx3&x=id%3D${typhonId}%26installsource%3Dondemand%26uc`; +const downloadPath = path.resolve(__dirname, 'typhon/extensions'); +const unzip = require("unzip-crx-3"); + +test('downloadFile test', async ({ page }) => { + await fs.mkdir(downloadPath, { recursive: true }); + + const downloadPromise = new Promise(async (resolve) => { + page.once('download', async (download) => { + const originalFilePath = path.join(downloadPath, download.suggestedFilename()); + await download.saveAs(originalFilePath); + console.log(`file has been downloaded to: ${originalFilePath}`); + + // new code: rename the downloaded file + const newFilePath = path.join(downloadPath, typhonId); + await fs.rename(originalFilePath, newFilePath); + console.log(`file has been renamed to: ${newFilePath}`); + + resolve(newFilePath); // resolve the promise with the new file path + }); + }); + + try { + await page.goto(url, { + waitUntil: 'domcontentloaded', + timeout: 10000 + }); + } catch (error) { + console.log('navigation caused an exception, likely due to immediate download:', 'directDownload'); + } + + // wait for the download and rename to complete + const downloadedFilePath = await downloadPromise; + + // verify the file exists + try { + await fs.access(downloadedFilePath as string); // type assertion to string + console.log('file verification succeeded, file exists.'); + } catch { + console.error('file verification failed, file does not exist.'); + throw new Error('downloaded file does not exist.'); + } + + // Assuming the rest of your setup remains the same... + + // Unzip the renamed file + try { + // Create a directory for the unzipped contents if it doesn't exist + const extractPath = path.join(downloadPath, typhonId + "_unzipped"); + await fs.mkdir(extractPath, { recursive: true }); + + // Adjust the unzip call to specify the extraction directory + await unzip(downloadedFilePath, extractPath); // Specify where to unzip + console.log("Successfully unzipped your CRX file to:", extractPath); + } catch (error) { + console.error("Failed to unzip the CRX file:", error.message); + throw new Error('Failed to unzip the CRX file.'); + } +}); \ No newline at end of file diff --git a/tests/wallet-automation/package-lock.json b/tests/wallet-automation/package-lock.json new file mode 100644 index 0000000000..110b0c2fb9 --- /dev/null +++ b/tests/wallet-automation/package-lock.json @@ -0,0 +1,339 @@ +{ + "name": "catalyst-core", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "catalyst-core", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.3.1", + "node-fetch": "^3.3.2", + "playwright": "^1.41.2", + "unzip-crx-3": "^0.2.0" + }, + "devDependencies": { + "@playwright/test": "^1.41.0", + "@types/node": "^20.11.4" + } + }, + "node_modules/@playwright/test": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0.tgz", + "integrity": "sha512-Grvzj841THwtpBOrfiHOeYTJQxDRnKofMSzCiV8XeyLWu3o89qftQ4BCKfkziJhSUQRd0utKhrddtIsiraIwmw==", + "dev": true, + "dependencies": { + "playwright": "1.41.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@playwright/test/node_modules/playwright": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0.tgz", + "integrity": "sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ==", + "dev": true, + "dependencies": { + "playwright-core": "1.41.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@playwright/test/node_modules/playwright-core": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@types/node": { + "version": "20.11.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.4.tgz", + "integrity": "sha512-6I0fMH8Aoy2lOejL3s4LhyIYX34DPwY8bl5xlNjBvUEk8OHrcuzsFt+Ied4LvJihbtXPM+8zUqdydfIti86v9g==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/playwright": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", + "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==", + "dependencies": { + "playwright-core": "1.41.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz", + "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "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" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unzip-crx-3": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz", + "integrity": "sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==", + "dependencies": { + "jszip": "^3.1.0", + "mkdirp": "^0.5.1", + "yaku": "^0.16.6" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/yaku": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/yaku/-/yaku-0.16.7.tgz", + "integrity": "sha512-Syu3IB3rZvKvYk7yTiyl1bo/jiEFaaStrgv1V2TIJTqYPStSMQVO8EQjg/z+DRzLq/4LIIharNT3iH1hylEIRw==" + } + } +} diff --git a/tests/wallet-automation/package.json b/tests/wallet-automation/package.json new file mode 100644 index 0000000000..1411f74378 --- /dev/null +++ b/tests/wallet-automation/package.json @@ -0,0 +1,33 @@ +{ + "name": "catalyst-core", + "version": "1.0.0", + "description": "

Catalyst Core

", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "test": "npx playwright test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/input-output-hk/catalyst-core.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/input-output-hk/catalyst-core/issues" + }, + "homepage": "https://github.com/input-output-hk/catalyst-core#readme", + "devDependencies": { + "@playwright/test": "^1.41.2", + "@types/node": "^20.11.4" + }, + "dependencies": { + "dotenv": "^16.3.1", + "node-fetch": "^3.3.2", + "playwright": "^1.41.2", + "unzip-crx-3": "^0.2.0" + } +} diff --git a/tests/wallet-automation/playwright.config.ts b/tests/wallet-automation/playwright.config.ts new file mode 100644 index 0000000000..274ff509b5 --- /dev/null +++ b/tests/wallet-automation/playwright.config.ts @@ -0,0 +1,34 @@ +import { defineConfig, devices } from '@playwright/test'; +export default defineConfig({ + testDir: '.', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + trace: 'on-first-retry', + + }, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + + + /* Configure projects for major browsers */ + projects: [ + { + name: 'setup', + testMatch: [ + /global\-setup\.ts/, + ], + }, + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + dependencies: ['setup'], + }, +]}); diff --git a/tests/wallet-automation/typhon/credentials.ts b/tests/wallet-automation/typhon/credentials.ts new file mode 100644 index 0000000000..0e21bf309d --- /dev/null +++ b/tests/wallet-automation/typhon/credentials.ts @@ -0,0 +1,49 @@ +import fs from 'fs'; +import path from 'path'; + +const txtContent = fs.readFileSync(path.resolve(__dirname,'typhon-wallet-storage.txt'), 'utf8');; + +// parse the contents and set them to process.env +txtContent.split('\n').forEach(line => { + const [key, value] = line.split('='); + if (key && value) { + process.env[key.trim()] = value.trim(); + } +}); + +interface WalletCredentials { + username: string; + password: string; +} +const getWalletCredentials = (walletID: string): WalletCredentials => { + const username = process.env[`${walletID}_USERNAME`]; + const password = process.env[`${walletID}_PASSWORD`]; + console.log(`username: ${username}, password: ${password}`); + + if (!username || !password) { + throw new Error(`Credentials for ${walletID} not found`); + } + + return { username, password }; +}; + +interface RegistrationPin { + one: string; + two: string; + three: string; + four: string; +} +const getRegistrationPin = (walletID: string): RegistrationPin => { + const one = process.env[`${walletID}_PIN1`]; + const two = process.env[`${walletID}_PIN2`]; + const three = process.env[`${walletID}_PIN3`]; + const four = process.env[`${walletID}_PIN4`]; + +if (!one || !two || !three || !four) { + throw new Error(`PIN for ${walletID} not found`); +} + +return { one, two, three, four }; +}; + +export { getWalletCredentials, getRegistrationPin }; \ No newline at end of file diff --git a/tests/wallet-automation/typhon/seed-phrase.ts b/tests/wallet-automation/typhon/seed-phrase.ts new file mode 100644 index 0000000000..b53bec86ca --- /dev/null +++ b/tests/wallet-automation/typhon/seed-phrase.ts @@ -0,0 +1,33 @@ +import fs from 'fs'; +import path from 'path'; + +// Read the contents of the .txt fileon-wallet-storage.txt'), 'utf8'); +const txtContent = fs.readFileSync(path.resolve(__dirname,'typhon-wallet-storage.txt'), 'utf8'); + +// Parse the contents and set them to process.env +txtContent.split('\n').forEach(line => { + const [key, value] = line.split('='); + if (key && value) { + process.env[key.trim()] = value.trim(); + } +}); + +interface SeedPhrase { + + seedPhrase: string[]; +} + +// function to get the seed phrase from environment variables +const getSeedPhrase = (): string[] => { + const seedPhraseArray: string[] = []; + for (let i = 1; i <= 15; i++) { + const word = process.env[`WALLET1_SEED_WORD_${i}`]; + if (!word) { + throw new Error(`seed word ${i} is missing`); + } + seedPhraseArray.push(word); + } + return seedPhraseArray; +}; + +export { getSeedPhrase }; diff --git a/tests/wallet-automation/typhon/typhon-wallet-registration.spec.ts b/tests/wallet-automation/typhon/typhon-wallet-registration.spec.ts new file mode 100644 index 0000000000..2b2976f28f --- /dev/null +++ b/tests/wallet-automation/typhon/typhon-wallet-registration.spec.ts @@ -0,0 +1,206 @@ +import { test, chromium } from '@playwright/test'; +import { getWalletCredentials, getRegistrationPin } from './credentials'; +import { getSeedPhrase } from './seed-phrase'; +import { waitForDebugger } from 'inspector'; +const path = require('path'); +// extension ID for Typhon: kfdniefadaanbjodldohaedphafoffoh + +test('import wallet', async ({ }) => { + const extensionPath: string = path.resolve(__dirname, 'extensions/KFDNIEFADAANBJODLDOHAEDPHAFOFFOH_unzipped'); + const userDataDir = path.resolve(__dirname, 'usrdatadir'); + + const browser = await chromium.launchPersistentContext(userDataDir, { + headless: false, // extensions only work in headful mode + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + ], + }); + + const page = await browser.newPage(); + await page.waitForTimeout(1000); // adjust the timeout as needed + + const pages = browser.pages(); + + const newTab = pages[pages.length - 1]; + await newTab.bringToFront(); + + // interact with elements on the background page + const firstButtonSelector = '//*[@id="headlessui-menu-button-1"]'; + await newTab.waitForSelector(firstButtonSelector, { state: 'visible' }); + await newTab.click(firstButtonSelector); + + const secondButtonSelector = '#headlessui-menu-item-6'; + await newTab.waitForSelector(secondButtonSelector, { state: 'visible' }); + await newTab.click(secondButtonSelector); + + const thirdButtonSelector = '//*[text()="Import"]'; + await newTab.waitForSelector(thirdButtonSelector, { state: 'visible' }); + await newTab.click(thirdButtonSelector); + + const WalletCredentials = getWalletCredentials('WALLET1'); + const usernameInput = '#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div:nth-child(2) > div > input'; + const passwordInput = '#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div:nth-child(2) > div > div:nth-child(2) > input'; + const cfpwInput = '#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div:nth-child(2) > div > div:nth-child(3) > input'; + await newTab.waitForSelector(usernameInput, { state: 'visible' }); + await newTab.waitForSelector(passwordInput, { state: 'visible' }); + await newTab.waitForSelector(cfpwInput, { state: 'visible' }); + await newTab.fill(usernameInput, WalletCredentials.username); + await newTab.fill(passwordInput, WalletCredentials.password); + await newTab.fill(cfpwInput, WalletCredentials.password); + + const agreeToTC = '#termsAndConditions' + await newTab.waitForSelector(agreeToTC, { state: 'visible' }); + await newTab.click(agreeToTC); + + const continueButton = '#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div:nth-child(2) > div > button'; + await newTab.waitForSelector(continueButton, { state: 'visible' }); + await newTab.click(continueButton); + + async function clickBlankSpace(newTab) { + const blankSpace = '#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div:nth-child(1) > div.flex.justify-between.items-start > div.flex-initial.flex.flex-col.mr-2 > span.text-primary.font-medium.text-xl'; + await newTab.waitForSelector(blankSpace, { state: 'visible' }); + await newTab.click(blankSpace); + } + + const seedPhrase = getSeedPhrase(); + + for (let i = 0; i < seedPhrase.length; i++) { + const ftSeedPhraseSelector = `#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div:nth-child(1) > div:nth-child(2) > div > div > div > div > div:nth-child(${i + 1}) > div > input`; + await newTab.waitForSelector(ftSeedPhraseSelector, { state: 'visible' }); + await newTab.fill(ftSeedPhraseSelector, seedPhrase[i]); + } + + const unlockWallet = '#app > div > div > div.flex-grow.overflow-auto > div > div.my-5.flex.justify-center.py-16 > div > div > div > div.mt-6.text-center.flex.justify-center > button'; + await clickBlankSpace(newTab); + await newTab.waitForSelector(unlockWallet, { state: 'visible' }); + await newTab.click(unlockWallet); + + const divSelector = '//*[@id="lc"]/div[2]/div[1]/div[2]/div/div[1]/div[1]/div/div[2]/div[1]/div/span[1]'; + await newTab.waitForSelector(divSelector, { state: 'visible' }); + + // use the selector to retrieve the element handle + const elementHandle = await newTab.$(divSelector); + if (elementHandle) { + // retrieve the text content of the element + const textContent = await elementHandle.textContent(); + if (textContent !== null) { + // remove any formatting that might interfere with parseFloat + const cleanedText = textContent.replace(/,/g, '').trim(); + const floatValue = parseFloat(cleanedText); + console.log('ADA:', floatValue) + if (!isNaN(floatValue)) { + if (floatValue < 500) { + console.log('not eligible for voting ☹️'); + } else { + console.log('eligible for voting ☺'); + } + } else { + console.log('text content is not a valid float:', textContent); + } + } else { + console.log('no text content found for the specified selector:', divSelector); + } + } else { + console.log('element not found for the specified XPath:', divSelector); + } + + const voting = '//*[@id="app"]/div/div/div[3]/div/div/div[1]/div/div/div[2]/div[5]/a/div/div[2]'; + await newTab.waitForSelector(voting, { state: 'visible' }); + await newTab.click(voting); + + const regForVoting = '//*[@id="lc"]/div[2]/div[2]/div[1]/button/span'; + await newTab.waitForSelector(regForVoting, { state: 'visible' }); + await newTab.click(regForVoting) + + const continueReg = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[2]/div[2]/div/button[2]/span'; + await newTab.waitForSelector(continueReg, { state: 'visible' }); + await newTab.click(continueReg); + + const RegistrationPin = getRegistrationPin('WALLET1'); + const pinReg1 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[1]'; + await newTab.waitForSelector(pinReg1, { state: 'visible' }); + await newTab.fill(pinReg1, RegistrationPin.one); + + const pinReg2 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[2]'; + await newTab.waitForSelector(pinReg2, { state: 'visible' }); + await newTab.fill(pinReg2, RegistrationPin.two); + + const pinReg3 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[3]'; + await newTab.waitForSelector(pinReg3, { state: 'visible' }); + await newTab.fill(pinReg3, RegistrationPin.three); + + const pinReg4 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[4]'; + await newTab.waitForSelector(pinReg4, { state: 'visible' }); + await newTab.fill(pinReg4, RegistrationPin.four); + + const continuePin = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[2]/div[2]/div/button[2]/span'; + await newTab.waitForSelector(continuePin, { state: 'visible' }) + await newTab.click(continuePin); + + const pinConfirm1 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[1]'; + await newTab.waitForSelector(pinConfirm1, { state: 'visible' }); + await newTab.fill(pinConfirm1, RegistrationPin.one); + + const pinConfirm2 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[2]'; + await newTab.waitForSelector(pinConfirm2, { state: 'visible' }); + await newTab.fill(pinConfirm2, RegistrationPin.two); + + const pinConfirm3 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[3]'; + await newTab.waitForSelector(pinConfirm3, { state: 'visible' }); + await newTab.fill(pinConfirm3, RegistrationPin.three); + + const pinConfirm4 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[1]/input[4]'; + await newTab.waitForSelector(pinConfirm4, { state: 'visible' }); + await newTab.fill(pinConfirm4, RegistrationPin.four); + + const continueReg2 = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[2]/div[2]/div/button[2]/div/div/div/span'; + await newTab.waitForSelector(continueReg2, { state: 'visible' }); + await newTab.click(continueReg2); + + const confirmReg = '//*[@id="lc"]/div[2]/div[2]/div[1]/div[2]/div[2]/div[2]/div/button[2]/div/div'; + await newTab.waitForSelector(confirmReg, { state: 'visible' }); + await newTab.click(confirmReg); + + const inputConfirmPassword = 'input[type="password"]'; + await newTab.waitForSelector(inputConfirmPassword, { state: 'visible' }); + await newTab.fill(inputConfirmPassword, WalletCredentials.password); + + const confirmTransactionButton = '//button[text()="confirm"]'; + await newTab.waitForSelector(confirmTransactionButton, { state: 'visible' }); + await newTab.click(confirmTransactionButton); + + try { + await newTab.waitForSelector('//*[@id="lc"]/div[2]/div[2]/div[2]/div[1]/div[2]', { timeout: 5000 }); + const textContent = await newTab.$eval('//*[@id="lc"]/div[2]/div[2]/div[2]/div[1]/div[2]', el => el.textContent); + + if (textContent) { + console.log("registered for voting successfully!"); + } else { + console.log('text content not found'); + } + } catch (error) { + console.error('an error occurred:', error.toString()); + console.log('an error occurred'); + } + + const logOut = '//*[@id="app"]/div/div/div[3]/div/div/div[1]/div/div/div[2]/div[11]/div[2]'; + await newTab.waitForSelector(logOut, { state: 'visible' }); + await newTab.click(logOut); + + const chooseAccount = '//*[@id="app"]/div/div/div[3]/div/div[2]/div/div/div[2]/div'; + await newTab.waitForSelector(chooseAccount, { state: 'visible' }); + await newTab.click(chooseAccount); + + const removeAccount = '//*[@id="app"]/div/div/div[3]/div/div[2]/div/div/div[2]/div[4]/button'; + await newTab.waitForSelector(removeAccount, { state: 'visible' }); + await newTab.click(removeAccount); + + const confirmRemove = 'button.btn.bg-primary'; + await newTab.waitForSelector(confirmRemove, { state: 'visible' }); + await newTab.click(confirmRemove) + + const addNew = '//*[@id="app"]/div/div/div[3]/div/div[2]/div/div/div[4]'; + await newTab.waitForSelector(addNew, { state: 'visible' }); + await newTab.click(addNew); +}); \ No newline at end of file diff --git a/tests/wallet-automation/typhon/typhon-wallet-storage.txt b/tests/wallet-automation/typhon/typhon-wallet-storage.txt new file mode 100644 index 0000000000..8d76d55c5a --- /dev/null +++ b/tests/wallet-automation/typhon/typhon-wallet-storage.txt @@ -0,0 +1,23 @@ +# .txt file +//WALLET1 +WALLET1_USERNAME=chaitea001 +WALLET1_PASSWORD=Chai1234! +WALLET1_SEED_WORD_1=over +WALLET1_SEED_WORD_2=verb +WALLET1_SEED_WORD_3=item +WALLET1_SEED_WORD_4=hope +WALLET1_SEED_WORD_5=heart +WALLET1_SEED_WORD_6=man +WALLET1_SEED_WORD_7=memory +WALLET1_SEED_WORD_8=crouch +WALLET1_SEED_WORD_9=squirrel +WALLET1_SEED_WORD_10=silly +WALLET1_SEED_WORD_11=urge +WALLET1_SEED_WORD_12=explain +WALLET1_SEED_WORD_13=wrestle +WALLET1_SEED_WORD_14=off +WALLET1_SEED_WORD_15=swamp +WALLET1_PIN1 = 1 +WALLET1_PIN2 = 2 +WALLET1_PIN3 = 3 +WALLET1_PIN4 = 4 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000000..ed5e11ab23 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,352 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@playwright/test@^1.41.0": + version "1.41.0" + resolved "https://registry.npmjs.org/@playwright/test/-/test-1.41.0.tgz" + integrity sha512-Grvzj841THwtpBOrfiHOeYTJQxDRnKofMSzCiV8XeyLWu3o89qftQ4BCKfkziJhSUQRd0utKhrddtIsiraIwmw== + dependencies: + playwright "1.41.0" + +"@types/debug@^4.1.0": + version "4.1.12" + resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/node@^20.11.4": + version "20.11.4" + resolved "https://registry.npmjs.org/@types/node/-/node-20.11.4.tgz" + integrity sha512-6I0fMH8Aoy2lOejL3s4LhyIYX34DPwY8bl5xlNjBvUEk8OHrcuzsFt+Ied4LvJihbtXPM+8zUqdydfIti86v9g== + dependencies: + undici-types "~5.26.4" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +clone-deep@^0.2.4: + version "0.2.4" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz" + integrity sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg== + dependencies: + for-own "^0.1.3" + is-plain-object "^2.0.1" + kind-of "^3.0.2" + lazy-cache "^1.0.3" + shallow-clone "^0.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +debug@^4.1.1, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +dotenv@^16.3.1: + version "16.3.1" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz" + integrity sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g== + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +for-own@^0.1.3: + version "0.1.5" + resolved "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz" + integrity sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw== + dependencies: + for-in "^1.0.1" + +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + 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.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-buffer@^1.0.2, is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-plain-object@^2.0.1: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +kind-of@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz" + integrity sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg== + dependencies: + is-buffer "^1.0.2" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +lazy-cache@^0.2.3: + version "0.2.7" + resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz" + integrity sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ== + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" + integrity sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ== + +merge-deep@^3.0.1: + version "3.0.3" + resolved "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz" + integrity sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA== + dependencies: + arr-union "^3.1.0" + clone-deep "^0.2.4" + kind-of "^3.0.2" + +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz" + integrity sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA== + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +playwright-core@*, playwright-core@1.41.1: + version "1.41.1" + resolved "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz" + integrity sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg== + +playwright-core@1.41.0: + version "1.41.0" + resolved "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz" + integrity sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw== + +playwright-extra-plugin-stealth@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/playwright-extra-plugin-stealth/-/playwright-extra-plugin-stealth-0.0.1.tgz" + integrity sha512-eI0Ujf4MXbcupzlVEXaaOnb+Exjt1sFi7t/3KxIA5pVww+WRAXRWdhqTz0glX62jJq2YM8fLu+GyvULpjTpZrw== + +playwright-extra@*, playwright-extra@^4.3.6: + version "4.3.6" + resolved "https://registry.npmjs.org/playwright-extra/-/playwright-extra-4.3.6.tgz" + integrity sha512-q2rVtcE8V8K3vPVF1zny4pvwZveHLH8KBuVU2MoE3Jw4OKVoBWsHI9CH9zPydovHHOCDxjGN2Vg+2m644q3ijA== + dependencies: + debug "^4.3.4" + +playwright@*, playwright@^1.41.1: + version "1.41.1" + resolved "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz" + integrity sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ== + dependencies: + playwright-core "1.41.1" + optionalDependencies: + fsevents "2.3.2" + +playwright@1.41.0: + version "1.41.0" + resolved "https://registry.npmjs.org/playwright/-/playwright-1.41.0.tgz" + integrity sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ== + dependencies: + playwright-core "1.41.0" + optionalDependencies: + fsevents "2.3.2" + +puppeteer-extra-plugin-stealth@^2.11.2: + version "2.11.2" + resolved "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz" + integrity sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ== + dependencies: + debug "^4.1.1" + puppeteer-extra-plugin "^3.2.3" + puppeteer-extra-plugin-user-preferences "^2.4.1" + +puppeteer-extra-plugin-user-data-dir@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz" + integrity sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g== + dependencies: + debug "^4.1.1" + fs-extra "^10.0.0" + puppeteer-extra-plugin "^3.2.3" + rimraf "^3.0.2" + +puppeteer-extra-plugin-user-preferences@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz" + integrity sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A== + dependencies: + debug "^4.1.1" + deepmerge "^4.2.2" + puppeteer-extra-plugin "^3.2.3" + puppeteer-extra-plugin-user-data-dir "^2.4.1" + +puppeteer-extra-plugin@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz" + integrity sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q== + dependencies: + "@types/debug" "^4.1.0" + debug "^4.1.1" + merge-deep "^3.0.1" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +shallow-clone@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz" + integrity sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw== + dependencies: + is-extendable "^0.1.1" + kind-of "^2.0.1" + lazy-cache "^0.2.3" + mixin-object "^2.0.1" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==