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==