diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_api_tests.yml similarity index 76% rename from .github/workflows/e2e_tests.yml rename to .github/workflows/e2e_api_tests.yml index 7c94cfa1db..e02e4e4533 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_api_tests.yml @@ -1,12 +1,17 @@ -name: End-to-End Tests Lite +name: E2E_API Tests on: schedule: - - cron: '0 8 * * *' + # workflow will run daily at 8:00 AM BDT (2:00 AM UTC) + - cron: '0 2 * * *' + # workflow will trigger on pull request pull_request: + branches: [develop] + # workflow will trigger on push to develop branch push: - branches: - - develop + branches: [develop] + + # workflow can be manually triggered workflow_dispatch: # Cancels all previous workflow runs for pull requests that have not completed. @@ -15,12 +20,14 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# workflow permissions permissions: checks: write - contents: read + contents: write issues: write pull-requests: write +# workflow environment variables env: ADMIN: admin ADMIN_PASSWORD: password @@ -39,16 +46,14 @@ env: DB_PREFIX: wp PR_NUMBER: ${{ github.event.number }} SHA: ${{ github.event.pull_request.head.sha }} - SYSTEM_INFO: ./tests/pw/systemInfo.json + SYSTEM_INFO: ./tests/pw/playwright/systemInfo.json API_TEST_RESULT: ./tests/pw/playwright-report/api/summary-report/results.json E2E_TEST_RESULT: ./tests/pw/playwright-report/e2e/summary-report/results.json jobs: tests: - name: e2e tests + name: e2e_api tests runs-on: ubuntu-latest - # strategy: - # fail-fast: false steps: - name: Checkout testing repo @@ -102,7 +107,6 @@ jobs: run: | cd tests/pw echo "The value of test DB_PORT is ${{ env.DB_PORT }}" - echo "The value of test DB_PORT is ${{ env.GMAP }}" # Set permalink structure - name: Set Permalink structure @@ -146,10 +150,17 @@ jobs: cd tests/pw npm run pw:browser-with-deps + # Install only the OS dependencies if cache hit not needed + - name: Install Playwright OS dependencies + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: | + cd tests/pw + npm run pw:deps-only + # Run e2e tests - name: 🧪 Running the e2e tests id: e2e-test - if: steps.pw-install.outcome == 'success' + if: success() timeout-minutes: 40 run: | cd tests/pw @@ -158,7 +169,7 @@ jobs: # Run API tests - name: 🧪 Running the api tests id: api-test - if: always() + if: always() && steps.db-port.outcome == 'success' timeout-minutes: 5 run: | cd tests/pw @@ -175,25 +186,26 @@ jobs: const script = require("./tests/pw/utils/gitTestSummary.ts") return await script({github, context, core}) - # Find PR comment - - name: Find PR comment by github-actions[bot] - uses: peter-evans/find-comment@v2 - id: find-comment - if: github.event_name == 'pull_request' - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: Tests Summary - - # Post test summary as PR comment - - name: Create or update PR comment - uses: peter-evans/create-or-update-comment@v2 - if: github.event_name == 'pull_request' - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: ${{ steps.prepare-test-summary.outputs.result }} - edit-mode: replace + # # Find PR comment + # - name: Find PR comment by github-actions[bot] + # uses: peter-evans/find-comment@v2 + # id: find-comment + # if: github.event_name == 'pull_request' + # with: + # issue-number: ${{ github.event.pull_request.number }} + # comment-author: 'github-actions[bot]' + # body-includes: Tests Summary + + # # Post test summary as PR comment + # - name: Create or update PR comment + # uses: peter-evans/create-or-update-comment@v2 + # if: github.event_name == 'pull_request' + # with: + # comment-id: ${{ steps.find-comment.outputs.comment-id }} + # issue-number: ${{ github.event.pull_request.number }} + # body: ${{ steps.prepare-test-summary.outputs.result }} + # reactions: hooray + # edit-mode: replace # Upload artifacts - name: Archive test artifacts (screenshots, HTML snapshots, Reports) diff --git a/tests/pw/.env.example b/tests/pw/.env.example index e2365e301f..bfcc1405e1 100644 --- a/tests/pw/.env.example +++ b/tests/pw/.env.example @@ -13,7 +13,7 @@ DOKAN_PRO=true [Dokan pro active status] BASE_URL=https://example.com [Base URL of the test site] CI=true [CI/CD environment indicator] SLOWMO=10 [Slow down test execution by provided seconds] [optional] -SETUP=true [Whether not to run setup tests before actual tests] [optional] +NO_SETUP=true [Whether not to run setup tests before actual tests] [optional] # Database Configuration DB_HOST_NAME=localhost [Database server hostname or IP address] diff --git a/tests/pw/.gitignore b/tests/pw/.gitignore index a95068d139..9ce7da8f57 100644 --- a/tests/pw/.gitignore +++ b/tests/pw/.gitignore @@ -174,6 +174,7 @@ playwright/ test-results/ playwright-report/ playwright/.cache/ +visual.spec.ts-snapshots/ storageState.json adminStorageState.json customerStorageState.json diff --git a/tests/pw/README.MD b/tests/pw/README.MD index b6b19021bd..acea26dfa7 100644 --- a/tests/pw/README.MD +++ b/tests/pw/README.MD @@ -124,7 +124,7 @@ DOKAN_PRO=true [Dokan pro active status] BASE_URL=https://example.com [Base URL of the test site] CI=true [CI/CD environment indicator] SLOWMO=10 [Slow down test execution by provided seconds] [optional] -SETUP=true [Whether not to run setup tests before actual tests] [optional] +NO_SETUP=true [Whether not to run setup tests before actual tests] [optional] # Database Configuration DB_HOST_NAME=localhost [Database server hostname or IP address] diff --git a/tests/pw/api.config.ts b/tests/pw/api.config.ts index 8ce06c1df9..600a415aaa 100644 --- a/tests/pw/api.config.ts +++ b/tests/pw/api.config.ts @@ -1,11 +1,12 @@ -import { defineConfig } from '@playwright/test'; +import { defineConfig, expect } from '@playwright/test'; +import { customExpect } from '@utils/pwMatchers'; import 'dotenv/config'; export default defineConfig({ testDir: './tests/api' /* test directory */, outputDir: 'playwright/api/test-artifacts/' /* Folder for test artifacts such as screenshots, videos, traces, etc. */, globalSetup: './global-setup' /* Path to the global setup file. This file will be required and run before all the tests. */, - globalTeardown: './global-teardown' /* Path to the global teardown file. This file will be required and run after all the tests. */, + // globalTeardown: './global-teardown' /* Path to the global teardown file. This file will be required and run after all the tests. */, globalTimeout: process.env.CI ? 20 * (60 * 1000) : 20 * (60 * 1000) /* Maximum time in milliseconds the whole test suite can run */, maxFailures: process.env.CI ? 30 : 30 /* The maximum number of test failures for the whole test suite run. After reaching this number, testing will stop and exit with an error. */, timeout: process.env.CI ? 5 * 1000 : 10 * 1000 /* Maximum time one test can run for. */, @@ -17,21 +18,21 @@ export default defineConfig({ // forbidOnly : !!process.env.CI, /* Fail the build on CI if you accidentally left testonly in the source code. */ repeatEach: 1 /* The number of times to repeat each test, useful for debugging flaky tests. */, retries: process.env.CI ? 1 : 0 /* The maximum number of retry attempts given to failed tests. */, - workers: process.env.CI ? 1 : 1 /* Opt out of parallel tests on CI. */, + workers: process.env.CI ? 4 : 4 /* Opt out of parallel tests on CI. */, reportSlowTests: { max: 3, threshold: 10 } /* Whether to report slow test files. Pass null to disable this feature. */, reporter: process.env.CI ? [ + ['github'], ['html', { open: 'never', outputFolder: 'playwright-report/api/html/html-report-api' }], - ['junit', { outputFile: 'playwright-report/api/junit-report/api-results.xml' }], + // ['junit', { outputFile: 'playwright-report/api/junit-report/api-results.xml' }], ['list', { printSteps: true }], ['./utils/summaryReporter.ts', { outputFile: 'playwright-report/api/summary-report/results.json' }], ] : [ ['html', { open: 'never', outputFolder: 'playwright-report/api/html/html-report-api' }], - ['junit', { outputFile: 'playwright-report/api/junit-report/api-results.xml' }], + // ['junit', { outputFile: 'playwright-report/api/junit-report/api-results.xml' }], ['list', { printSteps: true }], ['./utils/summaryReporter.ts', { outputFile: 'playwright-report/api/summary-report/results.json' }], - // ['allure-playwright', { detail: true, outputFolder: 'playwright-report/api/allure/allure-report', suiteTitle: false }], ], use: { @@ -47,17 +48,33 @@ export default defineConfig({ projects: [ // Api project + // global_setup + { + name: 'global_setup', + testMatch: /global\.setup\.ts/, + }, + // api_setup { name: 'api_setup', - testMatch: /.*\.setup\.ts/, + // testMatch: /.*\.setup\.ts/, + testMatch: /.*\.setup\.spec\.ts/, }, // api_tests { name: 'api_tests', testMatch: /.*\.spec\.ts/, - // dependencies: process.env.SETUP ? [] : ['api_setup'] /* whether not to run setup tests before running actual tests */, + dependencies: process.env.NO_SETUP ? [] : ['api_setup'] /* whether not to run setup tests before running actual tests */, + // teardown: 'global_teardown', + }, + + // global_teardown + { + name: 'global_teardown', + testMatch: /global\.teardown\.ts/, }, ], }); + +expect.extend(customExpect); diff --git a/tests/pw/e2e.config.ts b/tests/pw/e2e.config.ts index 48d8449108..8dd96be0fb 100644 --- a/tests/pw/e2e.config.ts +++ b/tests/pw/e2e.config.ts @@ -1,16 +1,22 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices, expect } from '@playwright/test'; +import { customExpect } from '@utils/pwMatchers'; import 'dotenv/config'; export default defineConfig({ testDir: './tests/e2e' /* test directory */, outputDir: 'playwright/e2e/test-artifacts/' /* Folder for test artifacts such as screenshots, videos, traces, etc. */, globalSetup: './global-setup' /* Path to the global setup file. This file will be required and run before all the tests. */, - globalTeardown: './global-teardown' /* Path to the global teardown file. This file will be required and run after all the tests. */, + // globalTeardown: './global-teardown' /* Path to the global teardown file. This file will be required and run after all the tests. */, globalTimeout: process.env.CI ? 40 * (60 * 1000) : 20 * (60 * 1000) /* Maximum time in milliseconds the whole test suite can run */, maxFailures: process.env.CI ? 30 : 30 /* The maximum number of test failures for the whole test suite run. After reaching this number, testing will stop and exit with an error. */, timeout: process.env.CI ? 40 * 1000 : 35 * 1000 /* Maximum time one test can run for. */, expect: { timeout: 10 * 1000 /* Maximum time expect() should wait for the condition to be met. For example in `await expect(locator).toHaveText();`*/, + toHaveScreenshot: { + maxDiffPixelRatio: 0.2, + maxDiffPixels: 500, + threshold: 0.5, + }, } /* Configuration for the expect assertion library */, preserveOutput: 'always' /* Whether to preserve test output in the testConfig.outputDir. Defaults to 'always'. */, // fullyParallel : true, /* Run tests in files in parallel */ @@ -21,17 +27,17 @@ export default defineConfig({ reportSlowTests: { max: 3, threshold: 25 } /* Whether to report slow test files. Pass null to disable this feature. */, reporter: process.env.CI ? [ + ['github'], ['html', { open: 'never', outputFolder: 'playwright-report/e2e/html/html-report-e2e' }], - ['junit', { outputFile: 'playwright-report/e2e/junit-report/e2e-results.xml' }], + // ['junit', { outputFile: 'playwright-report/e2e/junit-report/e2e-results.xml' }], ['list', { printSteps: true }], ['./utils/summaryReporter.ts', { outputFile: 'playwright-report/e2e/summary-report/results.json' }], ] : [ ['html', { open: 'never', outputFolder: 'playwright-report/e2e/html/html-report-e2e' }], - ['junit', { outputFile: 'playwright-report/e2e/junit-report/e2e-results.xml' }], + // ['junit', { outputFile: 'playwright-report/e2e/junit-report/e2e-results.xml' }], ['list', { printSteps: true }], ['./utils/summaryReporter.ts', { outputFile: 'playwright-report/e2e/summary-report/results.json' }], - // ['allure-playwright', { detail: true, outputFolder: 'playwright-report/e2e/allure/allure-report', suiteTitle: false }] ], use: { @@ -40,16 +46,15 @@ export default defineConfig({ actionTimeout: 15 * 1000 /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */, navigationTimeout: 20 * 1000 /* Maximum time each navigation such as 'goto()' can take. */, baseURL: process.env.BASE_URL ? process.env.BASE_URL : 'http://localhost:9999' /* Base URL */, - // browserName : 'chromium', /* Name of the browser that runs tests. */ + // browserName: 'chromium' /* Name of the browser that runs tests. */, bypassCSP: true /* Toggles bypassing page's Content-Security-Policy. */, - // channel : 'chrome', /* Browser distribution channel. */ - // colorScheme : 'dark', /* Emulates 'prefers-colors-scheme' media feature, supported values are 'light', 'dark', 'no-preference' */ + // channel: 'chrome' /* Browser distribution channel. */, + // colorScheme: 'dark' /* Emulates 'prefers-colors-scheme' media feature, supported values are 'light', 'dark', 'no-preference' */, headless: process.env.CI ? !!process.env.CI : false /* Whether to run tests on headless or non-headless mode */, ignoreHTTPSErrors: true /* Whether to ignore HTTPS errors during navigation. */, - // trace : 'retain-on-failure', /* Record trace only when retrying a test for the first time. */ trace: 'on-first-retry' /* Record trace only when retrying a test for the first time. */, screenshot: 'only-on-failure' /* Capture screenshot after each test failure. */, - // video : 'retain-on-failure', /* Record video only when retrying a test for the first time. */ + // video: 'retain-on-failure' /* Record video only when retrying a test for the first time. */, video: 'on-first-retry' /* Record video only when retrying a test for the first time. */, // viewport : { width: 1280, height: 720 }, /* Size of viewport */ launchOptions: { slowMo: process.env.SLOWMO ? Number(process.env.SLOWMO) * 1000 : 0 /* whether to slow down test execution by provided seconds */ }, @@ -61,14 +66,15 @@ export default defineConfig({ // e2e_setup { name: 'e2e_setup', - testMatch: /.*\.setup\.ts/, + // testMatch: /.*\.setup\.ts/, + testMatch: /.*\.setup\.spec\.ts/, }, // e2e_tests { name: 'e2e_tests', testMatch: /.*\.spec\.ts/, - // dependencies: process.env.SETUP ? [] : ['e2e_setup'] /* whether not to run setup tests before running actual tests */, + // dependencies: process.env.NO_SETUP ? [] : ['e2e_setup'] /* whether not to run setup tests before running actual tests */, }, // local site setup project @@ -79,3 +85,5 @@ export default defineConfig({ }, ], }); + +expect.extend(customExpect); \ No newline at end of file diff --git a/tests/pw/global-setup.ts b/tests/pw/global-setup.ts index b5d68c0a8d..65da7bbf43 100644 --- a/tests/pw/global-setup.ts +++ b/tests/pw/global-setup.ts @@ -16,7 +16,7 @@ async function globalSetup(config: FullConfig) { } console.log('retrying...'); } - console.log('ServerUrl:', process.env.SERVER_URL); + console.log('ServerUrl:', serverUrl); console.log('Global Setup Finished!'); } diff --git a/tests/pw/global-teardown.ts b/tests/pw/global-teardown.ts index a813d76d42..beb5440544 100644 --- a/tests/pw/global-teardown.ts +++ b/tests/pw/global-teardown.ts @@ -6,7 +6,7 @@ import { payloads } from '@utils/payloads'; async function globalSetup() { console.log('Global Teardown running....'); - const systemInfo = 'systemInfo.json'; + const systemInfo = 'playwright/systemInfo.json'; // get test environment info if (!helpers.fileExists(systemInfo)) { diff --git a/tests/pw/package-lock.json b/tests/pw/package-lock.json index 41173c4dec..a99d8c7f61 100644 --- a/tests/pw/package-lock.json +++ b/tests/pw/package-lock.json @@ -9,22 +9,21 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@faker-js/faker": "^8.0.2", - "@playwright/test": "^1.38.1", + "@faker-js/faker": "^8.2.0", + "@playwright/test": "^1.39.0", "@wordpress/env": "^8.9.0", - "allure-playwright": "^2.7.0", "dotenv": "^16.3.1", "mysqlconnector": "^2.0.1", "php-serialize": "^4.1.1", - "xml-js": "^1.6.11" + "zod": "^3.22.4" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^6.6.0", - "@typescript-eslint/parser": "^6.6.0", - "eslint": "^8.49.0", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", + "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", - "eslint-plugin-playwright": "^0.16.0", - "npm-check-updates": "^16.13.3", + "eslint-plugin-playwright": "^0.17.0", + "npm-check-updates": "^16.14.6", "prettier": "^3.0.3", "typescript": "^5.2.2" } @@ -114,18 +113,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@faker-js/faker": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.0.2.tgz", - "integrity": "sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.2.0.tgz", + "integrity": "sha512-VacmzZqVxdWdf9y64lDOMZNDMM/FQdtM9IsaOPKOm2suYwEatb8VkdHqOzXcDnZbk7YDE2BmsJmy/2Hmkn563g==", "funding": [ { "type": "opencollective", @@ -425,11 +424,11 @@ } }, "node_modules/@playwright/test": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", - "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", + "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", "dependencies": { - "playwright": "1.38.1" + "playwright": "1.39.0" }, "bin": { "playwright": "cli.js" @@ -595,9 +594,9 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/keyv": { @@ -622,22 +621,22 @@ } }, "node_modules/@types/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.6.0.tgz", - "integrity": "sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.6.0", - "@typescript-eslint/type-utils": "6.6.0", - "@typescript-eslint/utils": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -663,15 +662,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.6.0.tgz", - "integrity": "sha512-setq5aJgUwtzGrhW177/i+DMLqBaJbdwGj2CPIVFFLE0NCliy5ujIdLHd2D1ysmlmsjdL2GWW+hR85neEfc12w==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.6.0", - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/typescript-estree": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4" }, "engines": { @@ -691,13 +690,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.6.0.tgz", - "integrity": "sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -708,13 +707,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.6.0.tgz", - "integrity": "sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.6.0", - "@typescript-eslint/utils": "6.6.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -735,9 +734,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.6.0.tgz", - "integrity": "sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -748,13 +747,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.6.0.tgz", - "integrity": "sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -775,17 +774,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.6.0.tgz", - "integrity": "sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.6.0", - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/typescript-estree": "6.6.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", "semver": "^7.5.4" }, "engines": { @@ -800,12 +799,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.6.0.tgz", - "integrity": "sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.6.0", + "@typescript-eslint/types": "6.8.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -920,23 +919,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/allure-js-commons": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-2.7.0.tgz", - "integrity": "sha512-pLXrTyBC4XQ+eH7sJEIOieAd4Rv32kElyQB8G5Ubvg2+fKsZIr0Hfy8qVZxwaHAQRlcOcvJ7OaY/UvICiky5MA==", - "dependencies": { - "properties": "^1.2.1", - "uuid": "^8.3.0" - } - }, - "node_modules/allure-playwright": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-2.7.0.tgz", - "integrity": "sha512-tMfSXcLPziv9pZu4QG3PqdmXSgMwinTJdcSNmzHwzAMpiG1tYoFj1Qj2Suq7nwe8SfkE6Ec0UWqr82r0VQzJ2A==", - "dependencies": { - "allure-js-commons": "2.7.0" - } - }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -1805,15 +1787,15 @@ } }, "node_modules/eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -1871,10 +1853,13 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.16.0.tgz", - "integrity": "sha512-DcHpF0SLbNeh9MT4pMzUGuUSnJ7q5MWbP8sSEFIMS6j7Ggnduq8ghNlfhURgty4c1YFny7Ge9xYTO1FSAoV2Vw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.17.0.tgz", + "integrity": "sha512-5IABk90JcD96SkD9sr5V9RZHPdsZqBZYwlnRcTIcYmbxfxD2vtd9MQ2rPsTPLaMGKAPdfoyFaWARQElJCgYDuQ==", "dev": true, + "dependencies": { + "globals": "^13.23.0" + }, "peerDependencies": { "eslint": ">=7", "eslint-plugin-jest": ">=25" @@ -2224,9 +2209,9 @@ } }, "node_modules/fp-and-or": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/fp-and-or/-/fp-and-or-0.1.3.tgz", - "integrity": "sha512-wJaE62fLaB3jCYvY2ZHjZvmKK2iiLiiehX38rz5QZxtdN8fVPJDeZUiVvJrHStdTc+23LHlyZuSEKgFc0pxi2g==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/fp-and-or/-/fp-and-or-0.1.4.tgz", + "integrity": "sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==", "dev": true, "engines": { "node": ">=10" @@ -2328,19 +2313,19 @@ } }, "node_modules/glob": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", - "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2419,9 +2404,9 @@ } }, "node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2892,9 +2877,9 @@ "dev": true }, "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -3587,9 +3572,9 @@ } }, "node_modules/npm-check-updates": { - "version": "16.13.3", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.13.3.tgz", - "integrity": "sha512-l3FQtm+ZtDwqtK2r27vCuNdtnoDsXzk8D2WczvrAJy2bGPZJvRmuUa/Q9Gv+AbZV0IHSNJD2oHtQqUeqQRhEsw==", + "version": "16.14.6", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.6.tgz", + "integrity": "sha512-sJ6w4AmSDP7YzBXah94Ul2JhiIbjBDfx9XYgib15um2wtiQkOyjE7Lov3MNUSQ84Ry7T81mE4ynMbl/mGbK4HQ==", "dev": true, "dependencies": { "chalk": "^5.3.0", @@ -3597,7 +3582,7 @@ "commander": "^10.0.1", "fast-memoize": "^2.5.2", "find-up": "5.0.0", - "fp-and-or": "^0.1.3", + "fp-and-or": "^0.1.4", "get-stdin": "^8.0.0", "globby": "^11.0.4", "hosted-git-info": "^5.1.0", @@ -3615,11 +3600,12 @@ "prompts-ncu": "^3.0.0", "rc-config-loader": "^4.1.3", "remote-git-tags": "^3.0.0", - "rimraf": "^5.0.1", + "rimraf": "^5.0.5", "semver": "^7.5.4", "semver-utils": "^1.1.4", "source-map-support": "^0.5.21", - "spawn-please": "^2.0.1", + "spawn-please": "^2.0.2", + "strip-ansi": "^7.1.0", "strip-json-comments": "^5.0.1", "untildify": "^4.0.0", "update-notifier": "^6.0.2" @@ -3632,6 +3618,18 @@ "node": ">=14.14" } }, + "node_modules/npm-check-updates/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/npm-check-updates/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3687,15 +3685,15 @@ } }, "node_modules/npm-check-updates/node_modules/rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^10.2.5" + "glob": "^10.3.7" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": ">=14" @@ -3704,6 +3702,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/npm-check-updates/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/npm-check-updates/node_modules/strip-json-comments": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", @@ -4219,13 +4232,13 @@ } }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -4235,23 +4248,14 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "dev": true, "engines": { "node": "14 || >=16.14" } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4287,11 +4291,11 @@ } }, "node_modules/playwright": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", - "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", "dependencies": { - "playwright-core": "1.38.1" + "playwright-core": "1.39.0" }, "bin": { "playwright": "cli.js" @@ -4304,9 +4308,9 @@ } }, "node_modules/playwright-core": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", - "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", "bin": { "playwright-core": "cli.js" }, @@ -4393,14 +4397,6 @@ "node": ">= 14" } }, - "node_modules/properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/properties/-/properties-1.2.1.tgz", - "integrity": "sha512-qYNxyMj1JeW54i/EWEFsM1cVwxJbtgPp8+0Wg9XjNaK6VE/c4oRi6PNu5p7w1mNXEIQIjV5Wwn8v8Gz82/QzdQ==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -4777,11 +4773,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -4973,9 +4964,9 @@ } }, "node_modules/spawn-please": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.1.tgz", - "integrity": "sha512-W+cFbZR2q2mMTfjz5ZGvhBAiX+e/zczFCNlbS9mxiSdYswBXwUuBUT+a0urH+xZZa8f/bs0mXHyZsZHR9hKogA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.2.tgz", + "integrity": "sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3" @@ -5417,14 +5408,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -5676,17 +5659,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -5746,6 +5718,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -5812,15 +5792,15 @@ } }, "@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true }, "@faker-js/faker": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.0.2.tgz", - "integrity": "sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A==" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.2.0.tgz", + "integrity": "sha512-VacmzZqVxdWdf9y64lDOMZNDMM/FQdtM9IsaOPKOm2suYwEatb8VkdHqOzXcDnZbk7YDE2BmsJmy/2Hmkn563g==" }, "@humanwhocodes/config-array": { "version": "0.11.11", @@ -6036,11 +6016,11 @@ "optional": true }, "@playwright/test": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", - "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", + "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", "requires": { - "playwright": "1.38.1" + "playwright": "1.39.0" } }, "@pnpm/config.env-replace": { @@ -6165,9 +6145,9 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "@types/keyv": { @@ -6192,22 +6172,22 @@ } }, "@types/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.6.0.tgz", - "integrity": "sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.6.0", - "@typescript-eslint/type-utils": "6.6.0", - "@typescript-eslint/utils": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -6217,54 +6197,54 @@ } }, "@typescript-eslint/parser": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.6.0.tgz", - "integrity": "sha512-setq5aJgUwtzGrhW177/i+DMLqBaJbdwGj2CPIVFFLE0NCliy5ujIdLHd2D1ysmlmsjdL2GWW+hR85neEfc12w==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.6.0", - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/typescript-estree": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.6.0.tgz", - "integrity": "sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "requires": { - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" } }, "@typescript-eslint/type-utils": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.6.0.tgz", - "integrity": "sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.6.0", - "@typescript-eslint/utils": "6.6.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.6.0.tgz", - "integrity": "sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.6.0.tgz", - "integrity": "sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "requires": { - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/visitor-keys": "6.6.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -6273,27 +6253,27 @@ } }, "@typescript-eslint/utils": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.6.0.tgz", - "integrity": "sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.6.0", - "@typescript-eslint/types": "6.6.0", - "@typescript-eslint/typescript-estree": "6.6.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.6.0.tgz", - "integrity": "sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "requires": { - "@typescript-eslint/types": "6.6.0", + "@typescript-eslint/types": "6.8.0", "eslint-visitor-keys": "^3.4.1" } }, @@ -6377,23 +6357,6 @@ "uri-js": "^4.2.2" } }, - "allure-js-commons": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-2.7.0.tgz", - "integrity": "sha512-pLXrTyBC4XQ+eH7sJEIOieAd4Rv32kElyQB8G5Ubvg2+fKsZIr0Hfy8qVZxwaHAQRlcOcvJ7OaY/UvICiky5MA==", - "requires": { - "properties": "^1.2.1", - "uuid": "^8.3.0" - } - }, - "allure-playwright": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-2.7.0.tgz", - "integrity": "sha512-tMfSXcLPziv9pZu4QG3PqdmXSgMwinTJdcSNmzHwzAMpiG1tYoFj1Qj2Suq7nwe8SfkE6Ec0UWqr82r0VQzJ2A==", - "requires": { - "allure-js-commons": "2.7.0" - } - }, "ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -7019,15 +6982,15 @@ "dev": true }, "eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7088,11 +7051,13 @@ "requires": {} }, "eslint-plugin-playwright": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.16.0.tgz", - "integrity": "sha512-DcHpF0SLbNeh9MT4pMzUGuUSnJ7q5MWbP8sSEFIMS6j7Ggnduq8ghNlfhURgty4c1YFny7Ge9xYTO1FSAoV2Vw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-0.17.0.tgz", + "integrity": "sha512-5IABk90JcD96SkD9sr5V9RZHPdsZqBZYwlnRcTIcYmbxfxD2vtd9MQ2rPsTPLaMGKAPdfoyFaWARQElJCgYDuQ==", "dev": true, - "requires": {} + "requires": { + "globals": "^13.23.0" + } }, "eslint-scope": { "version": "7.2.2", @@ -7339,9 +7304,9 @@ "dev": true }, "fp-and-or": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/fp-and-or/-/fp-and-or-0.1.3.tgz", - "integrity": "sha512-wJaE62fLaB3jCYvY2ZHjZvmKK2iiLiiehX38rz5QZxtdN8fVPJDeZUiVvJrHStdTc+23LHlyZuSEKgFc0pxi2g==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/fp-and-or/-/fp-and-or-0.1.4.tgz", + "integrity": "sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==", "dev": true }, "fs-minipass": { @@ -7414,16 +7379,16 @@ } }, "glob": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", - "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "dependencies": { "brace-expansion": { @@ -7479,9 +7444,9 @@ } }, "globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -7834,9 +7799,9 @@ "dev": true }, "jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", @@ -8380,9 +8345,9 @@ } }, "npm-check-updates": { - "version": "16.13.3", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.13.3.tgz", - "integrity": "sha512-l3FQtm+ZtDwqtK2r27vCuNdtnoDsXzk8D2WczvrAJy2bGPZJvRmuUa/Q9Gv+AbZV0IHSNJD2oHtQqUeqQRhEsw==", + "version": "16.14.6", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.6.tgz", + "integrity": "sha512-sJ6w4AmSDP7YzBXah94Ul2JhiIbjBDfx9XYgib15um2wtiQkOyjE7Lov3MNUSQ84Ry7T81mE4ynMbl/mGbK4HQ==", "dev": true, "requires": { "chalk": "^5.3.0", @@ -8390,7 +8355,7 @@ "commander": "^10.0.1", "fast-memoize": "^2.5.2", "find-up": "5.0.0", - "fp-and-or": "^0.1.3", + "fp-and-or": "^0.1.4", "get-stdin": "^8.0.0", "globby": "^11.0.4", "hosted-git-info": "^5.1.0", @@ -8408,16 +8373,23 @@ "prompts-ncu": "^3.0.0", "rc-config-loader": "^4.1.3", "remote-git-tags": "^3.0.0", - "rimraf": "^5.0.1", + "rimraf": "^5.0.5", "semver": "^7.5.4", "semver-utils": "^1.1.4", "source-map-support": "^0.5.21", - "spawn-please": "^2.0.1", + "spawn-please": "^2.0.2", + "strip-ansi": "^7.1.0", "strip-json-comments": "^5.0.1", "untildify": "^4.0.0", "update-notifier": "^6.0.2" }, "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -8458,12 +8430,21 @@ } }, "rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "requires": { + "glob": "^10.3.7" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "requires": { - "glob": "^10.2.5" + "ansi-regex": "^6.0.1" } }, "strip-json-comments": { @@ -8830,25 +8811,19 @@ "dev": true }, "path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "requires": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { "lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", - "dev": true - }, - "minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "dev": true } } @@ -8876,18 +8851,18 @@ "dev": true }, "playwright": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", - "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", "requires": { "fsevents": "2.3.2", - "playwright-core": "1.38.1" + "playwright-core": "1.39.0" } }, "playwright-core": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", - "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==" + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==" }, "prelude-ls": { "version": "1.2.1", @@ -8944,11 +8919,6 @@ "sisteransi": "^1.0.5" } }, - "properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/properties/-/properties-1.2.1.tgz", - "integrity": "sha512-qYNxyMj1JeW54i/EWEFsM1cVwxJbtgPp8+0Wg9XjNaK6VE/c4oRi6PNu5p7w1mNXEIQIjV5Wwn8v8Gz82/QzdQ==" - }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -9225,11 +9195,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -9369,9 +9334,9 @@ } }, "spawn-please": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.1.tgz", - "integrity": "sha512-W+cFbZR2q2mMTfjz5ZGvhBAiX+e/zczFCNlbS9mxiSdYswBXwUuBUT+a0urH+xZZa8f/bs0mXHyZsZHR9hKogA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.2.tgz", + "integrity": "sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==", "dev": true, "requires": { "cross-spawn": "^7.0.3" @@ -9706,11 +9671,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -9892,14 +9852,6 @@ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", "dev": true }, - "xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "requires": { - "sax": "^1.2.4" - } - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -9944,6 +9896,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==" } } } diff --git a/tests/pw/package.json b/tests/pw/package.json index e036379ae9..191b9a28e5 100644 --- a/tests/pw/package.json +++ b/tests/pw/package.json @@ -14,12 +14,14 @@ "test:api": "npx playwright test --project=api_tests --config=api.config.ts ", "test:e2e": "npx playwright test --project=e2e_tests --config=e2e.config.ts ", "test:e2e-i": "npx playwright test --project=e2e_tests --headed", - "test:e2e:lite": "npx playwright test --project=e2e_tests --grep '@lite|@liteOnly' --grep-invert @pro --config=e2e.config.ts ", + "test:e2e:lite": "npx playwright test --project=e2e_tests --grep '@lite|@liteOnly' --grep-invert '@pro' --config=e2e.config.ts ", "test:api:lite": "npx playwright test --project=api_tests --grep '@lite|@liteOnly' --grep-invert @pro --config=api.config.ts ", "test:e2e:lite:explo": "npx playwright test --project=e2e_tests --grep '(?=.*@lite)(?=.*@explo)' --config=e2e.config.ts ", "test:api:pro": "npx playwright test --project=api_tests --grep '@lite|@pro' --grep-invert @liteOnly --config=api.config.ts ", "test:e2e:pro": "npx playwright test --project=e2e_tests --grep '@lite|@pro' --grep-invert @liteOnly --config=e2e.config.ts ", + "test:e2e:visual": "npx playwright test --project=e2e_tests --grep @visual --config=e2e.config.ts ", "test:e2e:explo": "npx playwright test --project=e2e_tests --grep @explo --config=e2e.config.ts ", + "test:e2e:core": "npx playwright test --project=e2e_tests --grep @core --config=e2e.config.ts ", "test:grep": "npx playwright test --grep", "test:debug": "npx playwright test --debug", "test:codegen": "playwright codegen", @@ -49,23 +51,22 @@ "author": "", "license": "ISC", "devDependencies": { - "@typescript-eslint/eslint-plugin": "^6.6.0", - "@typescript-eslint/parser": "^6.6.0", - "eslint": "^8.49.0", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", + "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", - "eslint-plugin-playwright": "^0.16.0", - "npm-check-updates": "^16.13.3", + "eslint-plugin-playwright": "^0.17.0", + "npm-check-updates": "^16.14.6", "prettier": "^3.0.3", "typescript": "^5.2.2" }, "dependencies": { - "@faker-js/faker": "^8.0.2", - "@playwright/test": "^1.38.1", + "@faker-js/faker": "^8.2.0", + "@playwright/test": "^1.39.0", "@wordpress/env": "^8.9.0", - "allure-playwright": "^2.7.0", "dotenv": "^16.3.1", "mysqlconnector": "^2.0.1", "php-serialize": "^4.1.1", - "xml-js": "^1.6.11" + "zod": "^3.22.4" } } diff --git a/tests/pw/pages/basePage.ts b/tests/pw/pages/basePage.ts index a291e5b6c3..f9699ebf40 100644 --- a/tests/pw/pages/basePage.ts +++ b/tests/pw/pages/basePage.ts @@ -263,7 +263,7 @@ export class BasePage { // type & wait for response async typeViaPageAndWaitForResponse(subUrl: string, selector: string, text: string, code = 200): Promise { - const [response] = await Promise.all([this.page.waitForResponse(resp => resp.url().includes(subUrl) && resp.status() === code), this.page.type(selector, text, { delay: 100 })]); + const [response] = await Promise.all([this.page.waitForResponse(resp => resp.url().includes(subUrl) && resp.status() === code), this.page.locator(selector).pressSequentially(text, { delay: 100 })]); return response; } @@ -271,7 +271,7 @@ export class BasePage { async typeAndWaitForResponse(subUrl: string, selector: string, text: string, code = 200): Promise { const [response] = await Promise.all([ this.page.waitForResponse(resp => resp.url().includes(subUrl) && resp.status() === code), - // await this.page.type(selector, text), + // await this.page.locator(selector).pressSequentially(text), this.clearAndFill(selector, text), ]); return response; @@ -281,7 +281,7 @@ export class BasePage { async typeAndWaitForResponseAndLoadState(subUrl: string, selector: string, text: string, code = 200): Promise { const [response] = await Promise.all([ this.page.waitForResponse(resp => resp.url().includes(subUrl) && resp.status() === code), - // await this.page.type(selector, text), + // await this.page.locator(selector).pressSequentially(text), this.page.waitForLoadState('networkidle'), this.clearAndFill(selector, text), ]); @@ -371,7 +371,6 @@ export class BasePage { // wait for selector async waitForSelector(selector: string): Promise { await this.page.locator(selector).waitFor(); - // await this.page.waitForSelector(selector); } // get locator @@ -632,7 +631,7 @@ export class BasePage { // type in input field async type(selector: string, text: string): Promise { - await this.page.type(selector, text); + await this.page.locator(selector).pressSequentially(text); } // fill in input field @@ -652,7 +651,7 @@ export class BasePage { async append(selector: string, text: string): Promise { await this.focus(selector); await this.press('End'); - await this.page.type(selector, text); + await this.page.locator(selector).pressSequentially(text); } // check if visible @@ -846,7 +845,7 @@ export class BasePage { async typeFrameSelector(frame: string, frameSelector: string, text: string): Promise { const locator = this.page.frameLocator(frame).locator(frameSelector); await locator.fill(text); - // await locator.type(text); + await locator.pressSequentially(text); } /** @@ -1123,7 +1122,7 @@ export class BasePage { // type on input locator async typeOnLocator(selector: string, text: string): Promise { const locator = this.page.locator(selector); - await locator.type(text); + await locator.pressSequentially(text); } // uncheck locator @@ -1297,6 +1296,10 @@ export class BasePage { } } + async toHaveScreenshot(page: Page, locators?: Locator[]) { + await expect(page).toHaveScreenshot({ fullPage: true, mask: locators, maskColor: 'black', animations: 'disabled' }); + } + /** * Assertion methods */ diff --git a/tests/pw/pages/myOrdersPage.ts b/tests/pw/pages/myOrdersPage.ts index 20152639a9..10a3b19e59 100644 --- a/tests/pw/pages/myOrdersPage.ts +++ b/tests/pw/pages/myOrdersPage.ts @@ -35,7 +35,8 @@ export class MyOrdersPage extends CustomerPage { // view order details async viewOrderDetails(orderId: string) { - await this.goIfNotThere(data.subUrls.frontend.orderDetails(orderId)); + // await this.goIfNotThere(data.subUrls.frontend.orderDetails(orderId)); + await this.goto(data.subUrls.frontend.orderDetails(orderId)); // order details are visible await this.multipleElementVisible(selector.customer.cOrderDetails.orderDetails); diff --git a/tests/pw/pages/selectors.ts b/tests/pw/pages/selectors.ts index 751eefd7c1..b20370967f 100644 --- a/tests/pw/pages/selectors.ts +++ b/tests/pw/pages/selectors.ts @@ -1724,7 +1724,7 @@ export const selector = { fields: '.dokan-settings-fields', - saveChanges: '.submit', + saveChanges: '//input[@id="submit" and @value="Save Changes"]', search: { searchBox: '.search-box', @@ -1978,7 +1978,6 @@ export const selector = { displayOnOrderDetails: '.enabled_for_customer_order .switch', displayOnSingleProductPage: '#dokan_store_support_setting\\[store_support_product_page\\]', supportButtonLabel: '#dokan_store_support_setting\\[support_button_label\\]', - supportTicketEmailNotification: '.dokan_admin_email_notification .switch', storeSupportSaveChanges: '#submit', }, diff --git a/tests/pw/pages/settingsPage.ts b/tests/pw/pages/settingsPage.ts index 9043a580d2..979d2bbcef 100644 --- a/tests/pw/pages/settingsPage.ts +++ b/tests/pw/pages/settingsPage.ts @@ -106,7 +106,7 @@ export class SettingsPage extends AdminPage { await this.enableSwitcher(selector.admin.dokan.settings.selling.productDiscount); await this.enableSwitcher(selector.admin.dokan.settings.selling.vendorProductReviewStatusChange); await this.enableSwitcher(selector.admin.dokan.settings.selling.guestProductEnquiry); - await this.enableSwitcher(selector.admin.dokan.settings.selling.newVendorEnableAuction); + await this.enableSwitcher(selector.admin.dokan.settings.selling.newVendorEnableAuction); // todo: add condition for simple auction plugin enabled await this.enableSwitcher(selector.admin.dokan.settings.selling.enableMinMaxQuantities); await this.enableSwitcher(selector.admin.dokan.settings.selling.enableMinMaxAmount); } @@ -281,7 +281,6 @@ export class SettingsPage extends AdminPage { await this.enableSwitcher(selector.admin.dokan.settings.storeSupport.displayOnOrderDetails); await this.selectByValue(selector.admin.dokan.settings.storeSupport.displayOnSingleProductPage, storeSupport.displayOnSingleProductPage); await this.clearAndType(selector.admin.dokan.settings.storeSupport.supportButtonLabel, storeSupport.supportButtonLabel); - await this.enableSwitcher(selector.admin.dokan.settings.storeSupport.supportTicketEmailNotification); // save settings await this.clickAndWaitForResponseAndLoadState(data.subUrls.ajax, selector.admin.dokan.settings.storeSupport.storeSupportSaveChanges); diff --git a/tests/pw/pages/vendorSettingsPage.ts b/tests/pw/pages/vendorSettingsPage.ts index e92f9e2f40..acc77410af 100644 --- a/tests/pw/pages/vendorSettingsPage.ts +++ b/tests/pw/pages/vendorSettingsPage.ts @@ -41,7 +41,7 @@ export class VendorSettingsPage extends VendorPage { await this.toBeVisible(selector.vendor.vStoreSettings.email); // map is visible - // await this.toBeVisible(selector.vendor.vStoreSettings.map); //todo: gmap not reading from env + // await this.toBeVisible(selector.vendor.vStoreSettings.map); //TODO: g_map value not found // todo: catalog, discount, vacation, open close, store category diff --git a/tests/pw/pages/visualPage.ts b/tests/pw/pages/visualPage.ts new file mode 100644 index 0000000000..b34865fb63 --- /dev/null +++ b/tests/pw/pages/visualPage.ts @@ -0,0 +1,98 @@ +import { Page } from '@playwright/test'; +import { AdminPage } from '@pages/adminPage'; +import { selector } from '@pages/selectors'; +import { data } from '@utils/testData'; + +export class VisualPage extends AdminPage { + constructor(page: Page) { + super(page); + } + + // reverse withdrawal modal + async addReverseWithdrawal() { + await this.goIfNotThere(data.subUrls.backend.dokan.reverseWithdraws); + await this.click(selector.admin.dokan.reverseWithdraw.addNewReverseWithdrawal); + await this.toHaveScreenshot(this.page); //todo: fullscreen modal + } + + // admin add new vendors + async addVendor() { + await this.goIfNotThere(data.subUrls.backend.dokan.vendors); + await this.click(selector.admin.dokan.vendors.addNewVendor); + await this.toHaveScreenshot(this.page); + + await this.click(selector.admin.dokan.vendors.newVendor.addNewVendorCloseModal); + } + + // store categories render properly + async adminStoreCategoryRenderProperly() { + await this.goIfNotThere(data.subUrls.backend.dokan.vendors); + await this.click(selector.admin.dokan.vendors.storeCategories); + const notice = this.getLocator(selector.admin.dokan.notice.noticeDiv); + await this.toHaveScreenshot(this.page, [notice]); + } + + // create seller badge + async createSellerBadge() { + await this.goIfNotThere(data.subUrls.backend.dokan.sellerBadge); + await this.clickAndWaitForResponse(data.subUrls.api.dokan.sellerBadgeEvent, selector.admin.dokan.sellerBadge.createBadge); + await this.toHaveScreenshot(this.page); + } + + // add quote + async addQuote() { + await this.goIfNotThere(data.subUrls.backend.dokan.requestForQuote); + await this.click(selector.admin.dokan.requestForQuotation.quotesList.newQuote); + await this.toHaveScreenshot(this.page); + } + + // add quote + async addQuoteRule() { + await this.goIfNotThere(data.subUrls.backend.dokan.requestForQuoteRules); + + await Promise.all([ + this.page.waitForResponse(resp => resp.url().includes(data.subUrls.api.dokan.quotes) && resp.status() === 200), + this.page.waitForResponse(resp => resp.url().includes(data.subUrls.api.dokan.products) && resp.status() === 200), + this.page.locator(selector.admin.dokan.requestForQuotation.quoteRules.newQuoteRule).click(), + ]); + await this.toHaveScreenshot(this.page); + } + + async addAnnouncement() { + await this.goIfNotThere(data.subUrls.backend.dokan.announcements); + await this.click(selector.admin.dokan.announcements.addNewAnnouncement); + const notice = this.getLocator(selector.admin.dokan.notice.noticeDiv); + await this.toHaveScreenshot(this.page, [notice]); + } + + // modules plan render properly + async adminModulesPlanRenderProperly() { + await this.goIfNotThere(data.subUrls.backend.dokan.modules); + await this.click(selector.admin.dokan.modules.pro.modulePlan.upgradePlan); + await this.toHaveScreenshot(this.page); + } + + // add new product advertisement + async addNewProductAdvertisement() { + await this.goIfNotThere(data.subUrls.backend.dokan.productAdvertising); + await this.click(selector.admin.dokan.productAdvertising.addNewProductAdvertising); + await this.toHaveScreenshot(this.page); + } + + // dokan menu + async dokanMenu(locator: string) { + await this.goIfNotThere(locator); + const notice = this.getLocator(selector.admin.dokan.notice.noticeDiv); + await this.toHaveScreenshot(this.page, [notice]); + } + + // dokan setting menus + async dokanSettingsMenu(locator: string) { + await this.goToDokanSettings(); + await this.wait(1); + const notice = this.getLocator(selector.admin.dokan.notice.noticeDiv); + await this.click(locator); + await this.wait(1.5); + await this.toHaveScreenshot(this.page, [notice]); + } +} diff --git a/tests/pw/pages/withdrawsPage.ts b/tests/pw/pages/withdrawsPage.ts index 5c3b2404a6..e0e826fd80 100644 --- a/tests/pw/pages/withdrawsPage.ts +++ b/tests/pw/pages/withdrawsPage.ts @@ -38,6 +38,7 @@ export class WithdrawsPage extends AdminPage { // filter withdraws async filterWithdraws(input: string, action: string): Promise { await this.goIfNotThere(data.subUrls.backend.dokan.withdraw); + await this.click(selector.admin.dokan.withdraw.filters.clearFilter); switch (action) { case 'by-vendor': diff --git a/tests/pw/tests/api/_env.setup.ts b/tests/pw/tests/api/_env.setup.spec.ts similarity index 54% rename from tests/pw/tests/api/_env.setup.ts rename to tests/pw/tests/api/_env.setup.spec.ts index 447600b84d..62b17a70a6 100644 --- a/tests/pw/tests/api/_env.setup.ts +++ b/tests/pw/tests/api/_env.setup.spec.ts @@ -2,11 +2,11 @@ import { test as setup, expect } from '@playwright/test'; import { ApiUtils } from '@utils/apiUtils'; import { endPoints } from '@utils/apiEndPoints'; import { payloads } from '@utils/payloads'; +import { helpers } from '@utils/helpers'; import { dbUtils } from '@utils/dbUtils'; import { dbData } from '@utils/dbData'; -import { helpers } from '@utils/helpers'; -setup.describe(' setup environment', () => { +setup.describe('setup test environment', () => { let apiUtils: ApiUtils; setup.beforeAll(async ({ request }) => { @@ -20,32 +20,34 @@ setup.describe(' setup environment', () => { setup('create customer @lite', async () => { const [, customerId] = await apiUtils.createCustomer(payloads.createCustomer1, payloads.adminAuth); - process.env.CUSTOMER_ID = customerId; + console.log('CUSTOMER_ID:', customerId); + (global as any).CUSTOMER_ID = customerId; }); setup('create vendor @lite', async () => { const [, sellerId] = await apiUtils.createStore(payloads.createStore1, payloads.adminAuth); - process.env.VENDOR_ID = sellerId; + console.log('VENDOR_ID:', sellerId); + (global as any).VENDOR_ID = sellerId; }); - // setup('set dokan general settings @lite', async () => { - // await dbUtils.setDokanSettings(dbData.dokan.optionName.general, { ...dbData.dokan.generalSettings, store_category_type: 'single' }); - // }); + setup('set dokan general settings @lite', async () => { + await dbUtils.setDokanSettings(dbData.dokan.optionName.general, { ...dbData.dokan.generalSettings, store_category_type: 'single' }); + }); - // setup('admin set dokan selling settings @lite', async () => { - // await dbUtils.setDokanSettings(dbData.dokan.optionName.selling, dbData.dokan.sellingSettings); - // }); + setup('admin set dokan selling settings @lite', async () => { + await dbUtils.setDokanSettings(dbData.dokan.optionName.selling, dbData.dokan.sellingSettings); + }); - // setup('admin set dokan withdraw settings @lite', async () => { - // await dbUtils.setDokanSettings(dbData.dokan.optionName.withdraw, dbData.dokan.withdrawSettings); - // }); + setup('admin set dokan withdraw settings @lite', async () => { + await dbUtils.setDokanSettings(dbData.dokan.optionName.withdraw, dbData.dokan.withdrawSettings); + }); - // setup('admin set dokan reverse withdraw settings @lite', async () => { - // await dbUtils.setDokanSettings(dbData.dokan.optionName.reverseWithdraw, dbData.dokan.reverseWithdrawSettings); - // }); + setup('admin set dokan reverse withdraw settings @lite', async () => { + await dbUtils.setDokanSettings(dbData.dokan.optionName.reverseWithdraw, dbData.dokan.reverseWithdrawSettings); + }); - // setup('get test environment info @lite', async () => { - // const [, summaryInfo] = await apiUtils.getSystemStatus(); - // helpers.writeFile('systemInfo.json', JSON.stringify(summaryInfo)); - // }); + setup('get test environment info @lite', async () => { + const [, summaryInfo] = await apiUtils.getSystemStatus(); + helpers.writeFile('playwright/systemInfo.json', JSON.stringify(summaryInfo)); + }); }); diff --git a/tests/pw/tests/api/_resetSite.teardown.ts b/tests/pw/tests/api/_resetSite.teardown.ts new file mode 100644 index 0000000000..186dc832f2 --- /dev/null +++ b/tests/pw/tests/api/_resetSite.teardown.ts @@ -0,0 +1,27 @@ +import { test as test, expect } from '@playwright/test'; +import { ApiUtils } from '@utils/apiUtils'; +import { payloads } from '@utils/payloads'; +// import { endPoints } from '@utils/apiEndPoints'; +// import { dbUtils } from '@utils/dbUtils'; +// import { dbData } from '@utils/dbData'; +// import { helpers } from '@utils/helpers'; + +test.describe(' test environment', () => { + let apiUtils: ApiUtils; + + test.beforeAll(async ({ request }) => { + apiUtils = new ApiUtils(request); + }); + + test('delete all products @lite', async () => { + await apiUtils.deleteAllProducts('', payloads.adminAuth); + }); + + test('delete all stores @lite', async () => { + await apiUtils.deleteAllStores(payloads.adminAuth); //todo: don't work + }); + + test('delete all customers @lite', async () => { + await apiUtils.deleteAllCustomers(payloads.adminAuth); + }); +}); diff --git a/tests/pw/tests/api/abuseReports.spec.ts b/tests/pw/tests/api/abuseReports.spec.ts index 192f4bdfca..f715e99755 100644 --- a/tests/pw/tests/api/abuseReports.spec.ts +++ b/tests/pw/tests/api/abuseReports.spec.ts @@ -10,7 +10,7 @@ import { payloads } from '@utils/payloads'; import { dbUtils } from '@utils/dbUtils'; import { dbData } from '@utils/dbData'; -const { VENDOR_ID, CUSTOMER_ID } = process.env; +const { VENDOR_ID, CUSTOMER_ID } = global as any; test.describe('abuse report api test', () => { let apiUtils: ApiUtils; diff --git a/tests/pw/tests/api/admins.spec.ts b/tests/pw/tests/api/admins.spec.ts index 2a44cc5b6d..918d924d02 100644 --- a/tests/pw/tests/api/admins.spec.ts +++ b/tests/pw/tests/api/admins.spec.ts @@ -12,6 +12,7 @@ import { test, expect } from '@playwright/test'; import { ApiUtils } from '@utils/apiUtils'; import { endPoints } from '@utils/apiEndPoints'; +import { schemas } from '@utils/schemas'; test.describe('admin api test', () => { let apiUtils: ApiUtils; @@ -24,59 +25,69 @@ test.describe('admin api test', () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminReportOverview); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.reportOverviewSchema); }); test('get admin report summary @lite', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminReportSummary); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.reportSummarySchema); }); test('get admin dashboard feed @lite', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminDashboardFeed); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.adminDashboardFeedSchema); }); test('get admin help @lite', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminHelp); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.adminHelpSchema); }); test('get changelog lite @lite', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminChangelogLite); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.changelogLiteSchema); }); test('get changelog pro @pro', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminChangelogPro); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.changelogProSchema); }); test('get admin notices @lite', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminNotices); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.adminNoticesSchema); }); test('get admin promo notices @lite', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminPromoNotices); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.adminPromoNoticeSchema); }); test('get admin logs @pro', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminLogs); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.adminLogsSchema); }); test('get admin export logs @pro', async () => { const [response, responseBody] = await apiUtils.get(endPoints.getAdminExportLogs); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.admin.adminExportLogsSchema); }); }); diff --git a/tests/pw/tests/api/announcements.spec.ts b/tests/pw/tests/api/announcements.spec.ts index 149e8db445..dfee9ee83b 100644 --- a/tests/pw/tests/api/announcements.spec.ts +++ b/tests/pw/tests/api/announcements.spec.ts @@ -65,4 +65,27 @@ test.describe('announcements api test', () => { // restore all announcements await apiUtils.updateBatchAnnouncements('restore', allAnnouncementIds); }); + + // announcement notice + + test.skip('get single announcement notice @pro', async () => { + const [response, responseBody] = await apiUtils.get(endPoints.getSingleAnnouncementNotice(noticeId), { headers: payloads.vendorAuth }); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + console.log(responseBody); + }); + + test.skip('update a announcement notice @pro', async () => { + const [response, responseBody] = await apiUtils.post(endPoints.updateAnnouncementNotice(noticeId), { data: payloads.updateAnnouncementNotice, headers: payloads.vendorAuth }); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + console.log(responseBody); + }); + + test.skip('delete a announcement notice @pro', async () => { + const [response, responseBody] = await apiUtils.delete(endPoints.deleteAnnouncementNotice(noticeId), { headers: payloads.vendorAuth }); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + console.log(responseBody); + }); }); diff --git a/tests/pw/tests/api/calculation.spec.ts b/tests/pw/tests/api/calculation.spec.ts index edc0a373b3..a19a97bf63 100644 --- a/tests/pw/tests/api/calculation.spec.ts +++ b/tests/pw/tests/api/calculation.spec.ts @@ -3,21 +3,25 @@ import { ApiUtils } from '@utils/apiUtils'; import { helpers } from '@utils/helpers'; import { payloads } from '@utils/payloads'; import { dbUtils } from '@utils/dbUtils'; +import { commission, feeRecipient } from '@utils/interfaces'; + +// test.use({ extraHTTPHeaders: { Authorization: payloads.adminAuth.Authorization } }); test.describe('calculation test', () => { let apiUtils: ApiUtils; let taxRate: number; + let commission: commission; + let feeRecipient: feeRecipient; test.beforeAll(async ({ request }) => { apiUtils = new ApiUtils(request); taxRate = await apiUtils.setUpTaxRate(payloads.enableTaxRate, payloads.createTaxRate); // todo: get tax rate instead of setup if possible + [commission, feeRecipient] = await dbUtils.getSellingInfo(); }); test('calculation test @pro', async () => { // todo: modify for lite as well - const [commission, feeRecipient] = await dbUtils.getSellingInfo(); - const [, res, oid] = await apiUtils.createOrder(payloads.createProduct(), payloads.createOrder); // console.log( res ); console.log('Cal: order id:', oid); @@ -39,7 +43,7 @@ test.describe('calculation test', () => { const adminCommission = orderReport.commission; const vendorEarning = orderReport.vendor_earning; // todo: compare with all order total - // todo: add discount scenario + // todo: add discount scenario const calculatedSubTotal = helpers.subtotal([productPrice], [productQuantity]); // todo: update it for multiple products const calculatedProductTax = helpers.productTax(taxRate, calculatedSubTotal); @@ -51,20 +55,101 @@ test.describe('calculation test', () => { console.log( `\ncalculatedSubTotal: ${calculatedSubTotal}\n`, - `calculatedOrderTotal: ${calculatedOrderTotal}\n`, - `calculatedVendorEarning: ${calculatedVendorEarning}\n`, - `calculatedAdminCommission: ${calculatedAdminCommission}\n`, + `calculatedOrderTotal: ${calculatedOrderTotal} received: ${Number(orderTotal)}\n`, + `calculatedVendorEarning: ${calculatedVendorEarning} received: ${Number(vendorEarning)}\n`, + `calculatedAdminCommission: ${calculatedAdminCommission} received: ${Number(adminCommission)}\n`, `providedShippingFee: ${shippingFee}\n`, - `calculatedShippingTax: ${calculatedShippingTax}\n`, - `calculatedProductTax: ${calculatedProductTax}\n`, - `calculatedTotalTax: ${calculatedTotalTax}\n`, + `calculatedShippingTax: ${calculatedShippingTax} received: ${Number(shippingTax)}\n`, + `calculatedProductTax: ${calculatedProductTax} received: ${Number(cartTax)} \n`, + `calculatedTotalTax: ${calculatedTotalTax} received: ${Number(totalTax)}\n`, ); - expect(calculatedProductTax).toEqual(Number(cartTax)); - expect(calculatedShippingTax).toEqual(Number(shippingTax)); - expect(calculatedTotalTax).toEqual(Number(totalTax)); - expect(calculatedOrderTotal).toEqual(Number(orderTotal)); - expect(calculatedAdminCommission).toEqual(Number(adminCommission)); - expect(calculatedVendorEarning).toEqual(Number(vendorEarning)); + expect(Number(cartTax)).toEqual(calculatedProductTax); + expect(Number(shippingTax)).toEqual(calculatedShippingTax); + expect(Number(totalTax)).toEqual(calculatedTotalTax); + expect(Number(orderTotal)).toEqual(calculatedOrderTotal); + expect(Number(adminCommission)).toEqual(calculatedAdminCommission); + expect(Number(vendorEarning)).toEqual(calculatedVendorEarning); + }); +}); + +test.describe(' Marketplace Coupon calculation test', () => { + let apiUtils: ApiUtils; + let taxRate: number = 5; + let commission: commission; + let feeRecipient: feeRecipient; + let sequentialCoupon: { value: string } | boolean; + + test.beforeAll(async ({ request }) => { + apiUtils = new ApiUtils(request); + taxRate = await apiUtils.setUpTaxRate(payloads.enableTaxRate, payloads.createTaxRate); + // taxRate = await apiUtils.updateSingleWcSettingOptions('general', 'woocommerce_calc_discounts_sequentially', { value: 'no' }); + sequentialCoupon = await apiUtils.getSingleWcSettingOptions('general', 'woocommerce_calc_discounts_sequentially'); + sequentialCoupon = sequentialCoupon.value === 'yes' ? true : false; + // console.log('applySequentially:', sequentialCoupon); + [commission, feeRecipient] = await dbUtils.getSellingInfo(); + }); + + test('coupon calculation test @pro', async () => { + //todo: apply single coupon, multiple coupon, sequential coupon with options and make separate test + const [, , code1, amount1] = await apiUtils.createMarketPlaceCoupon({ ...payloads.createMarketPlaceCoupon(), discount_type: 'percent' }, payloads.adminAuth); + const [, , code2, amount2] = await apiUtils.createMarketPlaceCoupon({ ...payloads.createMarketPlaceCoupon(), discount_type: 'percent' }, payloads.adminAuth); + const discount = { + type: 'coupon', + coupon: { + type: 'percentage', + amount: [amount1, amount2], + // amount: [5, 8], + applySequentially: sequentialCoupon, + }, + }; + const [, res, oid] = await apiUtils.createOrder(payloads.createProduct(), { ...payloads.createOrder, coupon_lines: [{ code: code1 }, { code: code2 }] }); + // const [, res, oid] = await apiUtils.createOrder('997', { ...payloads.createOrder, coupon_lines: [{ code: 'ac_66iu9e4awq' }, { code: 'ac_05taq2zkqo' }] }); + // console.log(res); + console.log('Order id:', oid); + const discountTotal = res.discount_total; + const discountTax = res.discount_tax; + const shippingTotal = res.shipping_total; + const shippingTax = res.shipping_tax; + const cartTax = res.cart_tax; + const totalTax = res.total_tax; + const orderTotal = res.total; + const gatewayFee = 0; + // const paymentMethod = res.payment_method_ti; + const productPrice = res.line_items[0].subtotal; + const productQuantity = res.line_items[0].quantity; + const orderReport = await apiUtils.getSingleOrderLog(String(oid)); + // console.log(orderReport); + const adminCommission = orderReport.commission; + const vendorEarning = orderReport.vendor_earning; + + const calculatedSubTotal = helpers.subtotal([productPrice], [productQuantity]); // todo: update it for multiple products + const calculatedDiscount = helpers.discount(calculatedSubTotal, discount); + const calculatedDiscountedSubTotal = helpers.roundToTwo(calculatedSubTotal - calculatedDiscount); + const calculatedProductTax = helpers.productTax(taxRate, calculatedDiscountedSubTotal); + const calculatedShippingTax = helpers.shippingTax(taxRate, shippingTotal); + const calculatedTotalTax = helpers.roundToTwo(calculatedProductTax + calculatedShippingTax); + const calculatedOrderTotal = helpers.orderTotal(calculatedDiscountedSubTotal, calculatedProductTax, calculatedShippingTax, shippingTotal); + const calculatedAdminCommission = helpers.adminCommission(calculatedDiscountedSubTotal, commission, calculatedProductTax, calculatedShippingTax, shippingTotal, gatewayFee, feeRecipient); + const calculatedVendorEarning = helpers.vendorEarning(calculatedDiscountedSubTotal, calculatedAdminCommission, calculatedProductTax, calculatedShippingTax, shippingTotal, gatewayFee, feeRecipient); + + console.log( + `\n calculatedSubTotal: ${calculatedSubTotal}\n`, + `calculatedDiscount: ${calculatedDiscount} received: ${Number(discountTotal)}\n`, + `calculatedOrderTotal: ${calculatedOrderTotal} received: ${Number(orderTotal)}\n`, + `calculatedVendorEarning: ${calculatedVendorEarning} received: ${Number(vendorEarning)}\n`, + `calculatedAdminCommission: ${calculatedAdminCommission} received: ${Number(adminCommission)}\n`, + `calculatedShippingTax: ${calculatedShippingTax} received: ${Number(shippingTax)}\n`, + `calculatedProductTax: ${calculatedProductTax} received: ${Number(cartTax)} \n`, + `calculatedTotalTax: ${calculatedTotalTax} received: ${Number(totalTax)}\n`, + `shippingTotal: ${shippingTotal}\n`, + ); + expect(Number(discountTotal)).toEqual(calculatedDiscount); + expect(Number(cartTax)).toEqual(calculatedProductTax); + expect(Number(shippingTax)).toEqual(calculatedShippingTax); + expect(Number(totalTax)).toEqual(calculatedTotalTax); + expect(Number(orderTotal)).toEqual(calculatedOrderTotal); + expect(Number(adminCommission)).toEqual(calculatedAdminCommission); + expect(Number(vendorEarning)).toEqual(calculatedVendorEarning); }); }); diff --git a/tests/pw/tests/api/orders.spec.ts b/tests/pw/tests/api/orders.spec.ts index 2ff62f1930..12a02feda5 100644 --- a/tests/pw/tests/api/orders.spec.ts +++ b/tests/pw/tests/api/orders.spec.ts @@ -44,7 +44,7 @@ for (const version of versions) { }); test('get single order @lite', async () => { - const [response, responseBody] = await apiUtils.get(endPoints.getSingleOrder(orderId).replace('v1', version)); + const [response, responseBody] = await apiUtils.get(endPoints.getSingleOrder(orderId).replace('v1', version), { headers: payloads.adminAuth }); expect(response.ok()).toBeTruthy(); expect(responseBody).toBeTruthy(); }); diff --git a/tests/pw/tests/api/supportTickets.spec.ts b/tests/pw/tests/api/supportTickets.spec.ts index 2f7347ecaa..c620f04051 100644 --- a/tests/pw/tests/api/supportTickets.spec.ts +++ b/tests/pw/tests/api/supportTickets.spec.ts @@ -12,7 +12,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { endPoints } from '@utils/apiEndPoints'; import { payloads } from '@utils/payloads'; -const { VENDOR_ID, CUSTOMER_ID } = process.env; +const { VENDOR_ID, CUSTOMER_ID } = global as any; test.describe('support ticket api test', () => { let apiUtils: ApiUtils; diff --git a/tests/pw/tests/e2e/_auth.setup.spec.ts b/tests/pw/tests/e2e/_auth.setup.spec.ts index 9242f8045f..aafa624f7b 100644 --- a/tests/pw/tests/e2e/_auth.setup.spec.ts +++ b/tests/pw/tests/e2e/_auth.setup.spec.ts @@ -4,6 +4,9 @@ import { WpPage } from '@pages/wpPage'; import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; +import { helpers } from '@utils/helpers'; + +const { DOKAN_PRO } = process.env; setup.describe('authenticate users & set permalink', () => { setup('authenticate admin @lite', async ({ page }) => { @@ -16,27 +19,30 @@ setup.describe('authenticate users & set permalink', () => { const wpPage = new WpPage(page); await loginPage.adminLogin(data.admin); await wpPage.setPermalinkSettings(data.wpSettings.permalink); - process.env.SERVER_URL = process.env.BASE_URL + '/wp-json'; }); setup('add customer1 @lite', async ({ request }) => { const apiUtils = new ApiUtils(request); const [, customerId] = await apiUtils.createCustomer(payloads.createCustomer1, payloads.adminAuth); - process.env.CUSTOMER_ID = customerId; + console.log('CUSTOMER_ID:', customerId); + (global as any).CUSTOMER_ID = customerId; }); setup('add vendor1 @lite', async ({ request }) => { const apiUtils = new ApiUtils(request); const [, sellerId] = await apiUtils.createStore(payloads.createStore1, payloads.adminAuth); await apiUtils.updateCustomer(sellerId, payloads.updateAddress, payloads.adminAuth); - process.env.VENDOR_ID = sellerId; + console.log('VENDOR_ID:', sellerId); + (global as any).VENDOR_ID = sellerId; }); setup('add vendor2 @lite', async ({ request }) => { const apiUtils = new ApiUtils(request); const [, sellerId] = await apiUtils.createStore(payloads.createStore2, payloads.adminAuth); await apiUtils.updateCustomer(sellerId, payloads.updateAddress, payloads.adminAuth); - process.env.VENDOR2_ID = sellerId; + console.log('VENDOR2_ID:', sellerId); + (global as any).VENDOR2_ID = sellerId; + // helpers.writeEnvJson('VENDOR2_ID', sellerId); }); setup('authenticate customer @lite', async ({ page }) => { @@ -49,10 +55,18 @@ setup.describe('authenticate users & set permalink', () => { await loginPage.login(data.vendor, data.auth.vendorAuthFile); }); - setup('dokan pro enabled or not @pro', async ({ request }) => { + setup('dokan pro enabled or not @lite', async ({ request }) => { + const apiUtils = new ApiUtils(request); + let res = await apiUtils.checkPluginsExistence(data.plugin.dokanPro, payloads.adminAuth); + if (res) { + res = await apiUtils.pluginsActiveOrNot(data.plugin.dokanPro, payloads.adminAuth); + } + DOKAN_PRO ? expect(res).toBeTruthy() : expect(res).toBeFalsy(); + }); + + setup('get test environment info @lite', async ({ request }) => { const apiUtils = new ApiUtils(request); - const res = await apiUtils.pluginsActiveOrNot(data.plugin.dokanPro, payloads.adminAuth); - process.env.DOKAN_PRO = String(res); - expect(res).toBeTruthy(); + const [, summaryInfo] = await apiUtils.getSystemStatus(payloads.adminAuth); + helpers.writeFile('playwright/systemInfo.json', JSON.stringify(summaryInfo)); }); }); diff --git a/tests/pw/tests/e2e/_environment.setup.spec.ts b/tests/pw/tests/e2e/_environment.setup.spec.ts index 40a3aa3799..85b63867d6 100644 --- a/tests/pw/tests/e2e/_environment.setup.spec.ts +++ b/tests/pw/tests/e2e/_environment.setup.spec.ts @@ -7,8 +7,10 @@ import { payloads } from '@utils/payloads'; import { dbUtils } from '@utils/dbUtils'; import { dbData } from '@utils/dbData'; import { data } from '@utils/testData'; +// import { helpers } from '@utils/helpers'; -const { CUSTOMER_ID, DOKAN_PRO, HPOS } = process.env; +const { DOKAN_PRO } = process.env; +const { CUSTOMER_ID, HPOS } = global as any; setup.describe('setup site & woocommerce & user settings', () => { setup.use({ extraHTTPHeaders: { Authorization: payloads.adminAuth.Authorization } }); @@ -107,7 +109,6 @@ setup.describe('setup site & woocommerce & user settings', () => { setup('disable simple-auction ajax bid check @pro', async () => { const [, , status] = await apiUtils.getSinglePlugin('wa/woocommerce-simple-auctions', payloads.adminAuth); - console.log(status); status === 'active' && (await dbUtils.updateWpOptionTable('simple_auctions_live_check', 'no')); }); }); @@ -129,7 +130,8 @@ setup.describe('setup user settings', () => { // create store product const product = { ...payloads.createProduct(), name: data.predefined.simpleProduct.product1.name }; const [, productId] = await apiUtils.createProduct(product, payloads.vendorAuth); - process.env.PRODUCT_ID = productId; + (global as any).PRODUCT_ID = productId; + // helpers.writeEnvJson('PRODUCT_ID', productId); }); setup('add vendor2 product @lite', async () => { @@ -139,7 +141,8 @@ setup.describe('setup user settings', () => { // create store product const product = { ...payloads.createProduct(), name: data.predefined.vendor2.simpleProduct.product1.name }; const [, productId] = await apiUtils.createProduct(product, payloads.vendor2Auth); - process.env.V2_PRODUCT_ID = productId; + (global as any).V2_PRODUCT_ID = productId; + // helpers.writeEnvJson('V2_PRODUCT_ID', productId); }); setup('add vendor coupon @pro', async () => { @@ -278,21 +281,21 @@ setup.describe('setup dokan settings e2e', () => { await vPage.close(); }); - setup('recreate product advertisement payment product via settings save @pro', async () => { - await productAdvertisingPage.recreateProductAdvertisementPaymentViaSettingsSave(); + setup('recreate reverse withdrawal payment product via settings save @lite', async () => { + await reverseWithdrawsPage.reCreateReverseWithdrawalPaymentViaSettingsSave(); }); - setup('product advertisement payment product exists @pro', async () => { - const product = await apiUtils.checkProductExistence('Product Advertisement Payment', payloads.adminAuth); + setup('reverse Withdraw payment product exists @lite', async () => { + const product = await apiUtils.checkProductExistence('Reverse Withdrawal Payment', payloads.adminAuth); expect(product).toBeTruthy(); }); - setup('recreate reverse withdrawal payment product via settings save @lite', async () => { - await reverseWithdrawsPage.reCreateReverseWithdrawalPaymentViaSettingsSave(); + setup('recreate product advertisement payment product via settings save @pro', async () => { + await productAdvertisingPage.recreateProductAdvertisementPaymentViaSettingsSave(); }); - setup('reverse Withdraw payment product exists @lite', async () => { - const product = await apiUtils.checkProductExistence('Reverse Withdrawal Payment', payloads.adminAuth); + setup('product advertisement payment product exists @pro', async () => { + const product = await apiUtils.checkProductExistence('Product Advertisement Payment', payloads.adminAuth); expect(product).toBeTruthy(); }); diff --git a/tests/pw/tests/e2e/abuseReports.spec.ts b/tests/pw/tests/e2e/abuseReports.spec.ts index c2361509c6..a7e7d5890f 100644 --- a/tests/pw/tests/e2e/abuseReports.spec.ts +++ b/tests/pw/tests/e2e/abuseReports.spec.ts @@ -6,7 +6,7 @@ import { data } from '@utils/testData'; import { dbData } from '@utils/dbData'; import { payloads } from '@utils/payloads'; -const { VENDOR_ID, CUSTOMER_ID } = process.env; +const { VENDOR_ID, CUSTOMER_ID } = global as any; test.describe('Abuse report test', () => { let admin: AbuseReportsPage; diff --git a/tests/pw/tests/e2e/coupons.spec.ts b/tests/pw/tests/e2e/coupons.spec.ts index fd1023f9c2..86784d7df6 100644 --- a/tests/pw/tests/e2e/coupons.spec.ts +++ b/tests/pw/tests/e2e/coupons.spec.ts @@ -4,7 +4,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { PRODUCT_ID } = process.env; +const { PRODUCT_ID } = global as any; test.describe('Coupons test', () => { let admin: CouponsPage; diff --git a/tests/pw/tests/e2e/myOrders.spec.ts b/tests/pw/tests/e2e/myOrders.spec.ts index d1d0f3a6bd..1199296588 100644 --- a/tests/pw/tests/e2e/myOrders.spec.ts +++ b/tests/pw/tests/e2e/myOrders.spec.ts @@ -4,7 +4,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { CUSTOMER_ID, PRODUCT_ID } = process.env; +const { CUSTOMER_ID, PRODUCT_ID } = global as any; test.describe('My orders functionality test', () => { let customer: MyOrdersPage; diff --git a/tests/pw/tests/e2e/orders.spec.ts b/tests/pw/tests/e2e/orders.spec.ts index ba6d0d4552..1a01430db7 100644 --- a/tests/pw/tests/e2e/orders.spec.ts +++ b/tests/pw/tests/e2e/orders.spec.ts @@ -6,7 +6,8 @@ import { payloads } from '@utils/payloads'; import { dbUtils } from '@utils/dbUtils'; import { dbData } from '@utils/dbData'; -const { DOKAN_PRO, CUSTOMER_ID, PRODUCT_ID } = process.env; +const { DOKAN_PRO } = process.env; +const { CUSTOMER_ID, PRODUCT_ID } = global as any; test.describe('Order functionality test', () => { let vendor: OrdersPage; diff --git a/tests/pw/tests/e2e/productEnquiry.spec.ts b/tests/pw/tests/e2e/productEnquiry.spec.ts index f494585c12..2040dce80a 100644 --- a/tests/pw/tests/e2e/productEnquiry.spec.ts +++ b/tests/pw/tests/e2e/productEnquiry.spec.ts @@ -6,7 +6,7 @@ import { data } from '@utils/testData'; import { dbData } from '@utils/dbData'; import { payloads } from '@utils/payloads'; -const { VENDOR_ID, CUSTOMER_ID } = process.env; +const { VENDOR_ID, CUSTOMER_ID } = global as any; test.describe('Product Enquiry test', () => { // let admin: ProductEnquiryPage; diff --git a/tests/pw/tests/e2e/productReviews.spec.ts b/tests/pw/tests/e2e/productReviews.spec.ts index 8969c77b99..b28ec736d2 100644 --- a/tests/pw/tests/e2e/productReviews.spec.ts +++ b/tests/pw/tests/e2e/productReviews.spec.ts @@ -4,7 +4,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { PRODUCT_ID } = process.env; +const { PRODUCT_ID } = global as any; test.describe('Product Reviews test', () => { let vendor: ProductReviewsPage; diff --git a/tests/pw/tests/e2e/refunds.spec.ts b/tests/pw/tests/e2e/refunds.spec.ts index bc9592e771..a97eae8983 100644 --- a/tests/pw/tests/e2e/refunds.spec.ts +++ b/tests/pw/tests/e2e/refunds.spec.ts @@ -5,7 +5,7 @@ import { dbUtils } from '@utils/dbUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { CUSTOMER_ID, PRODUCT_ID } = process.env; +const { CUSTOMER_ID, PRODUCT_ID } = global as any; test.describe('Refunds test', () => { let admin: RefundsPage; diff --git a/tests/pw/tests/e2e/reports.spec.ts b/tests/pw/tests/e2e/reports.spec.ts index 0da8f57bda..75b758cfa0 100644 --- a/tests/pw/tests/e2e/reports.spec.ts +++ b/tests/pw/tests/e2e/reports.spec.ts @@ -4,7 +4,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { PRODUCT_ID } = process.env; +const { PRODUCT_ID } = global as any; test.describe('Reports test', () => { let admin: ReportsPage; diff --git a/tests/pw/tests/e2e/requestForQuotes.spec.ts b/tests/pw/tests/e2e/requestForQuotes.spec.ts index d8d4ee73bf..38d4570f00 100644 --- a/tests/pw/tests/e2e/requestForQuotes.spec.ts +++ b/tests/pw/tests/e2e/requestForQuotes.spec.ts @@ -4,7 +4,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { CUSTOMER_ID } = process.env; +const { CUSTOMER_ID } = global as any; test.describe('Request for quotation test admin', () => { let admin: RequestForQuotationsPage; diff --git a/tests/pw/tests/e2e/reverseWithdraws.spec.ts b/tests/pw/tests/e2e/reverseWithdraws.spec.ts index 353e133fcb..06cfa71e90 100644 --- a/tests/pw/tests/e2e/reverseWithdraws.spec.ts +++ b/tests/pw/tests/e2e/reverseWithdraws.spec.ts @@ -7,7 +7,7 @@ import { payloads } from '@utils/payloads'; import { dbUtils } from '@utils/dbUtils'; import { dbData } from '@utils/dbData'; -const { PRODUCT_ID } = process.env; +const { PRODUCT_ID } = global as any; test.describe('Reverse withdraw test', () => { let admin: ReverseWithdrawsPage; diff --git a/tests/pw/tests/e2e/storeReviews.spec.ts b/tests/pw/tests/e2e/storeReviews.spec.ts index 6e940eb49a..6ddea83282 100644 --- a/tests/pw/tests/e2e/storeReviews.spec.ts +++ b/tests/pw/tests/e2e/storeReviews.spec.ts @@ -4,9 +4,9 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -test.describe('Store Reviews test', () => { - const { VENDOR_ID } = process.env; +const { VENDOR_ID } = global as any; +test.describe('Store Reviews test', () => { let admin: StoreReviewsPage; let vendor: StoreReviewsPage; let customer: StoreReviewsPage; diff --git a/tests/pw/tests/e2e/storeSupports.spec.ts b/tests/pw/tests/e2e/storeSupports.spec.ts index b496e1c832..037248cc9b 100644 --- a/tests/pw/tests/e2e/storeSupports.spec.ts +++ b/tests/pw/tests/e2e/storeSupports.spec.ts @@ -5,7 +5,7 @@ import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; import { responseBody } from '@utils/interfaces'; -const { PRODUCT_ID, VENDOR_ID, CUSTOMER_ID } = process.env; +const { VENDOR_ID, CUSTOMER_ID, PRODUCT_ID } = global as any; test.describe('Store Support test (admin)', () => { let admin: StoreSupportsPage; diff --git a/tests/pw/tests/e2e/stores.spec.ts b/tests/pw/tests/e2e/stores.spec.ts index 836e0b6d42..44616ffa4a 100644 --- a/tests/pw/tests/e2e/stores.spec.ts +++ b/tests/pw/tests/e2e/stores.spec.ts @@ -4,7 +4,7 @@ import { StoresPage } from '@pages/storesPage'; import { data } from '@utils/testData'; // import { payloads } from '@utils/payloads'; -const { VENDOR_ID } = process.env; +const { VENDOR_ID } = global as any; test.describe('Stores test', () => { let admin: StoresPage; diff --git a/tests/pw/tests/e2e/vendorReturnRequest.spec.ts b/tests/pw/tests/e2e/vendorReturnRequest.spec.ts index 42fce54efa..5d971dc25d 100644 --- a/tests/pw/tests/e2e/vendorReturnRequest.spec.ts +++ b/tests/pw/tests/e2e/vendorReturnRequest.spec.ts @@ -6,7 +6,7 @@ import { OrdersPage } from '@pages/ordersPage'; import { data } from '@utils/testData'; // import { payloads } from '@utils/payloads'; -// const { CUSTOMER_ID, PRODUCT_ID } = process.env; +// const { CUSTOMER_ID, PRODUCT_ID } = global as any; test.describe('Vendor RMA test', () => { let vendor: VendorReturnRequestPage; diff --git a/tests/pw/tests/e2e/vendorStaff.spec.ts b/tests/pw/tests/e2e/vendorStaff.spec.ts index 81565adae8..0c1b926b17 100644 --- a/tests/pw/tests/e2e/vendorStaff.spec.ts +++ b/tests/pw/tests/e2e/vendorStaff.spec.ts @@ -16,7 +16,7 @@ test.describe('Vendor staff test', () => { vendor = new VendorStaffPage(vPage); apiUtils = new ApiUtils(request); - await apiUtils.createVendorStaff({...payloads.createStaff(), first_name: staff.firstName, last_name: staff.lastName}, payloads.vendorAuth); + await apiUtils.createVendorStaff({ ...payloads.createStaff(), first_name: staff.firstName, last_name: staff.lastName }, payloads.vendorAuth); }); test.afterAll(async () => { diff --git a/tests/pw/tests/e2e/visual.spec.ts b/tests/pw/tests/e2e/visual.spec.ts new file mode 100644 index 0000000000..23ee6e9bae --- /dev/null +++ b/tests/pw/tests/e2e/visual.spec.ts @@ -0,0 +1,250 @@ +import { test, Page } from '@playwright/test'; +import { VisualPage } from '@pages/visualPage'; +import { data } from '@utils/testData'; +import { selector } from '@pages/selectors'; + +test.describe('dokan visual test', () => { + test.skip(!!process.env.CI, 'skip visual test on CI'); + let admin: VisualPage; + let aPage: Page; + + test.beforeAll(async ({ browser }) => { + const adminContext = await browser.newContext(data.auth.adminAuth); + aPage = await adminContext.newPage(); + admin = new VisualPage(aPage); + }); + + test.afterAll(async () => { + await aPage.close(); + }); + + // TODO: add dokan lite pages like modules, promotions + + test('dokan admin dashboard @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.dokan); + }); + + test('admin withdraw menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.withdraw); + }); + + test('admin reverse withdraw menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.reverseWithdraws); + }); + + test('admin add reverse withdrawal @visual', async () => { + await admin.addReverseWithdrawal(); + }); + + test('admin vendors menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.vendors); + }); + + test('admin can add vendor @visual', async () => { + await admin.addVendor(); + }); + + test('admin store category @visual', async () => { + await admin.adminStoreCategoryRenderProperly(); + }); + + test('dokan store reviews menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.storeReviews); + }); + + test('dokan store support menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.storeSupport); + }); + + test('dokan seller badge menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.sellerBadge); + }); + + test('admin can create seller badge @visual', async () => { + await admin.createSellerBadge(); + }); + + test('admin quotes menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.requestForQuote); + }); + + test('admin can add quote @visual', async () => { + await admin.addQuote(); + }); + + test('admin quote rules menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.requestForQuoteRules); + }); + + test('admin can add quote rule @visual', async () => { + await admin.addQuoteRule(); + }); + + test('dokan abuse report menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.abuseReports); + }); + + test('dokan announcements menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.announcements); + }); + + test('admin can add announcement @visual', async () => { + await admin.addAnnouncement(); + }); + + test('admin refunds menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.refunds); + }); + + test('admin reports menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.reports); + }); + + test('admin All Logs menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.allLogs); + }); + + test('dokan modules menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.modules); + }); + + test('dokan modules plan @visual', async () => { + await admin.adminModulesPlanRenderProperly(); + }); + + test('dokan tools menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.tools); + }); + + test('admin verifications menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.verifications); + }); + + test('dokan product advertising menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.productAdvertising); + }); + + test('admin can add product advertisement @visual', async () => { + await admin.addNewProductAdvertisement(); + }); + + test('dokan wholesale customers menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.wholeSaleCustomer); + }); + + test('dokan help menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.help); + }); + + test('dokan settings general menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.general); + }); + + test('dokan settings selling menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.sellingOptions); + }); + + test('dokan settings withdraw menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.withdrawOptions); + }); + + test('dokan settings reverseWithdraw menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.reverseWithdrawal); + }); + + test('dokan settings page menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.pageSettings); + }); + + test('dokan settings appearance menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.appearance); + }); + + test('dokan settings privacyPolicy menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.privacyPolicy); + }); + + test('dokan settings colors menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.colors); + }); + + test('dokan settings liveSearch menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.liveSearch); + }); + + test('dokan settings storeSupport menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.storeSupport); + }); + + test('dokan settings sellerVerification menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.sellerVerification); + }); + + test('dokan settings verificationSmsGateways menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.verificationSmsGateways); + }); + + test('dokan settings emailVerification menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.emailVerification); + }); + + test('dokan settings socialApi menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.socialApi); + }); + + test('dokan settings shippingStatus menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.shippingStatus); + }); + + test('dokan settings quote menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.quote); + }); + + test('dokan settings liveChat menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.liveChat); + }); + + test('dokan settings rma menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.rma); + }); + + test('dokan settings wholesale menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.wholesale); + }); + + test('dokan settings euComplianceFields menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.euComplianceFields); + }); + + test('dokan settings deliveryTime menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.deliveryTime); + }); + + test('dokan settings productAdvertising menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.productAdvertising); + }); + + test('dokan settings geolocation menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.geolocation); + }); + + test('dokan settings productReportAbuse menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.productReportAbuse); + }); + + test('dokan settings singleProductMultiVendor menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.singleProductMultiVendor); + }); + + test('dokan settings vendorSubscription menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.vendorSubscription); + }); + + test('dokan settings vendorAnalytics menu @visual', async () => { + await admin.dokanSettingsMenu(selector.admin.dokan.settings.menus.vendorAnalytics); + }); + + test('dokan license menu @visual', async () => { + await admin.dokanMenu(data.subUrls.backend.dokan.license); + }); +}); diff --git a/tests/pw/tests/e2e/wholesaleCustomers.spec.ts b/tests/pw/tests/e2e/wholesaleCustomers.spec.ts index 67bcfe8cc4..9d7602021a 100644 --- a/tests/pw/tests/e2e/wholesaleCustomers.spec.ts +++ b/tests/pw/tests/e2e/wholesaleCustomers.spec.ts @@ -96,7 +96,7 @@ test.describe.skip('Wholesale customers test customer', () => { customer = new WholesaleCustomersPage(cPage); apiUtils = new ApiUtils(request); - await apiUtils.createWholesaleCustomer(payloads.createCustomer1, payloads.adminAuth); // todo: need to update customer auth if crated wholesale or move to env setup + await apiUtils.createWholesaleCustomer(payloads.createCustomer1, payloads.adminAuth); // todo: need to update customer auth if created wholesale or move to env setup const [responseBody, ,] = await apiUtils.createProduct(payloads.createWholesaleProduct(), payloads.vendorAuth); productName = responseBody.name; diff --git a/tests/pw/tests/e2e/withdraws.spec.ts b/tests/pw/tests/e2e/withdraws.spec.ts index d56cd0efca..5a6c842230 100644 --- a/tests/pw/tests/e2e/withdraws.spec.ts +++ b/tests/pw/tests/e2e/withdraws.spec.ts @@ -4,7 +4,7 @@ import { ApiUtils } from '@utils/apiUtils'; import { data } from '@utils/testData'; import { payloads } from '@utils/payloads'; -const { PRODUCT_ID } = process.env; +const { PRODUCT_ID } = global as any; test.describe('Withdraw test', () => { let admin: WithdrawsPage; diff --git a/tests/pw/tsconfig.json b/tests/pw/tsconfig.json index 15934ba53a..ad9cdfbfaf 100644 --- a/tests/pw/tsconfig.json +++ b/tests/pw/tsconfig.json @@ -25,11 +25,13 @@ "@helpers": ["utils/helpers"], "@interfaces": ["utils/interfaces"], "@payloads": ["utils/payloads"], - "@testData": ["utils/testData"] + "@schemas": ["utils/schemas"], + "@testData": ["utils/testData"], + "@pwMatchers": ["utils/pwMatchers"] }, "esModuleInterop": true /* fixes some issues TS originally had with the ES6 spec where TypeScript treats CommonJS/AMD/UMD modules similar to ES6 module*/, "moduleResolution": "node" /* Pretty much always node for modern JS. Other option is "classic"*/, - "typeRoots": ["./node_modules/@types", "./types"] /* List of folders to include type definitions from. */, + "typeRoots": ["node_modules/@types", "types/**/*"] /* List of folders to include type definitions from. */, "resolveJsonModule": true, "importHelpers": true /* Import emit helpers from 'tslib'. */, "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, diff --git a/tests/pw/types/environment.d.ts b/tests/pw/types/environment.d.ts index d33e026753..8659df1942 100644 --- a/tests/pw/types/environment.d.ts +++ b/tests/pw/types/environment.d.ts @@ -1,4 +1,4 @@ -export { }; +export {}; declare global { namespace NodeJS { @@ -17,14 +17,15 @@ declare global { PRODUCT_ID: string; V2_PRODUCT_ID: string; GMAP: string; - DOKAN_PRO: string - BASE_URL:string; - QUERY:string; + DOKAN_PRO: string; + BASE_URL: string; + QUERY: string; HEADLESS: string; SLOWMO: string; DEVTOOLS: string; RETRY_TIMES: string; TIME_OUT: string; + NO_SETUP: string; SERVER_URL: string; ADMIN_AUTH: string; VENDOR_AUTH: string; @@ -35,10 +36,8 @@ declare global { DATABASE: string; DB_PORT: number; DB_PREFIX: string; - API_TEST_RESULT: string - E2E_TEST_RESULT: string - - + API_TEST_RESULT: string; + E2E_TEST_RESULT: string; } } } diff --git a/tests/pw/types/global.d.ts b/tests/pw/types/global.d.ts new file mode 100644 index 0000000000..fa99c1ee70 --- /dev/null +++ b/tests/pw/types/global.d.ts @@ -0,0 +1,10 @@ +import { ZodTypeAny } from 'zod'; + +declare global { + namespace PlaywrightTest { + interface Matchers { + toMatchSchema(schema: ZodTypeAny): Promise; + toBeWithinRange(a: number, b: number): R; + } + } +} diff --git a/tests/pw/utils/apiEndPoints.ts b/tests/pw/utils/apiEndPoints.ts index 33b5b34eda..c370373424 100644 --- a/tests/pw/utils/apiEndPoints.ts +++ b/tests/pw/utils/apiEndPoints.ts @@ -1,6 +1,6 @@ -// const { SERVER_URL } = process.env; +import 'dotenv/config'; -const SERVER_URL = process.env.SERVER_URL ? process.env.SERVER_URL : process.env.BASE_URL + '/wp-json'; +const { SERVER_URL } = process.env; export const endPoints = { serverUrl: `${SERVER_URL}`, @@ -141,6 +141,11 @@ export const endPoints = { restoreDeletedAnnouncement: (announcementId: string) => `${SERVER_URL}/dokan/v1/announcement/${announcementId}/restore`, // put updateBatchAnnouncements: `${SERVER_URL}/dokan/v1/announcement/batch`, // method: trash, delete, restore + // announcement notice + getSingleAnnouncementNotice: (noticeId: string) => `${SERVER_URL}/dokan/v1/announcement/notice/${noticeId}`, + updateAnnouncementNotice: (noticeId: string) => `${SERVER_URL}/dokan/v1/announcement/notice/${noticeId}`, + deleteAnnouncementNotice: (noticeId: string) => `${SERVER_URL}/dokan/v1/announcement/notice/${noticeId}`, + // refunds getAllRefunds: `${SERVER_URL}/dokan/v1/refunds`, approveRefund: (refundId: string) => `${SERVER_URL}/dokan/v1/refunds/${refundId}/approve`, // put @@ -221,7 +226,7 @@ export const endPoints = { createSupportTicketComment: (supportTicketId: string) => `${SERVER_URL}/dokan/v1/admin/support-ticket/${supportTicketId}`, updateSupportTicketStatus: (supportTicketId: string) => `${SERVER_URL}/dokan/v1/admin/support-ticket/${supportTicketId}/status`, // post updateSupportTicketEmailNotification: (supportTicketId: string) => `${SERVER_URL}/dokan/v1/admin/support-ticket/${supportTicketId}/email-notification`, // post - deleteSupportTicketComment: (supportTicketId: string) => `${SERVER_URL}/dokan/v1/admin/support-ticket/${supportTicketId}/comment`, + deleteSupportTicketComment: (supportTicketCommentId: string) => `${SERVER_URL}/dokan/v1/admin/support-ticket/${supportTicketCommentId}/comment`, updateBatchSupportTickets: `${SERVER_URL}/dokan/v1/admin/support-ticket/batch`, // method: close // admin @@ -471,7 +476,7 @@ export const endPoints = { getAllSettingsGroups: `${SERVER_URL}/wc/v3/settings`, getAllSettingOptions: (groupId: string) => `${SERVER_URL}/wc/v3/settings/${groupId}`, getSingleSettingOption: (groupId: string, optionId: string) => `${SERVER_URL}/wc/v3/settings/${groupId}/${optionId}`, - updateSettingOption: (groupId: string, optionId: string) => `${SERVER_URL}/wc/v3/settings/${groupId}/${optionId}`, + updateSingleSettingOption: (groupId: string, optionId: string) => `${SERVER_URL}/wc/v3/settings/${groupId}/${optionId}`, updateBatchSettingOptions: (groupId: string) => `${SERVER_URL}/wc/v3/settings/${groupId}/batch`, // system status @@ -524,7 +529,6 @@ export const endPoints = { }, wp: { - // todo : add all wp endpoints // users getAllUsers: `${SERVER_URL}/wp/v2/users`, getCurrentUser: `${SERVER_URL}/wp/v2/users/me`, diff --git a/tests/pw/utils/apiUtils.ts b/tests/pw/utils/apiUtils.ts index 7b58e996f2..68ce40644a 100644 --- a/tests/pw/utils/apiUtils.ts +++ b/tests/pw/utils/apiUtils.ts @@ -4,8 +4,7 @@ import { payloads } from '@utils/payloads'; import { helpers } from '@utils/helpers'; import fs from 'fs'; import { auth, user_api, taxRate, coupon_api, marketPlaceCoupon, reqOptions, headers, storageState, responseBody } from '@utils/interfaces'; - -const { VENDOR_ID, CUSTOMER_ID } = process.env; +const { VENDOR_ID, CUSTOMER_ID } = global as any; export class ApiUtils { readonly request: APIRequestContext; @@ -106,6 +105,8 @@ export class ApiUtils { console.log('Status Code: ', response.status()); console.log('Response text: ', await response.text()); console.log('Error: ', err.message); // todo: showing playwright error message instead of api error message + // console.log('header:', response.headers()); + // console.log('header:', response.headersArray()); } } @@ -154,7 +155,6 @@ export class ApiUtils { // get sellerId async getSellerId(storeName?: string, auth?: auth): Promise { - // todo: apply multiple optional parameter if (arguments.length === 1 && typeof storeName === 'object') { auth = storeName as auth; storeName = undefined; @@ -193,6 +193,19 @@ export class ApiUtils { return responseBody; } + // delete all stores + async deleteAllStores(auth?: auth): Promise { + // todo: apply multiple optional parameter (implement from deleteAllProducts) + const allStores = await this.getAllStores(auth); + if (!allStores?.length) { + console.log('No store exists'); + return; + } + const allStoreIds = allStores.map((o: { id: unknown }) => o.id); + const [, responseBody] = await this.put(endPoints.updateBatchStores, { data: { delete: allStoreIds }, headers: payloads.adminAuth }); + return responseBody; + } + // create store review async createStoreReview(sellerId: string, payload: object, auth?: auth): Promise<[responseBody, string]> { const [, responseBody] = await this.post(endPoints.createStoreReview(sellerId), { data: payload, headers: auth }); @@ -204,7 +217,7 @@ export class ApiUtils { * follow store methods */ - // follow unfollow store + // follow un-follow store async followUnfollowStore(sellerId: string, auth?: auth): Promise { const [, responseBody] = await this.post(endPoints.followUnfollowStore, { data: { vendor_id: Number(sellerId) }, headers: auth }); return responseBody; @@ -220,11 +233,21 @@ export class ApiUtils { return responseBody; } + // get single product + async getSingleProduct(productId: string, auth?: auth): Promise { + const [, responseBody] = await this.get(endPoints.getSingleProduct(productId), { headers: auth }); + return responseBody; + } + // get productId - async getProductId(productName: string, auth?: auth): Promise { - // todo: apply multiple optional parameter + async getProductId(productName?: string, auth?: auth): Promise { + if (arguments.length === 1 && typeof productName === 'object') { + auth = productName as auth; + productName = undefined; + } + const allProducts = await this.getAllProducts(auth); - const productId = productName ? allProducts.find((o: { name: string }) => o.name.toLowerCase() === productName.toLowerCase())?.id : allProducts[0]?.id; + const productId = productName ? allProducts.find((o: { name: string }) => o.name.toLowerCase() === productName!.toLowerCase())?.id : allProducts[0]?.id; return productId; } @@ -243,7 +266,7 @@ export class ApiUtils { } // delete all products - async deleteAllProducts(productName: string, auth?: auth): Promise { + async deleteAllProducts(productName?: string, auth?: auth): Promise { // todo: apply multiple optional parameter const allProducts = await this.getAllProducts(auth); if (!allProducts?.length) { @@ -373,10 +396,14 @@ export class ApiUtils { } // get couponId - async getCouponId(couponCode: string, auth?: auth): Promise { - // todo: apply multiple optional parameter + async getCouponId(couponCode?: string, auth?: auth): Promise { + if (arguments.length === 1 && typeof couponCode === 'object') { + auth = couponCode as auth; + couponCode = undefined; + } + const allCoupons = await this.getAllCoupons(auth); - const couponId = couponCode ? allCoupons.find((o: { code: string }) => o.code.toLowerCase() === couponCode.toLowerCase())?.id : allCoupons[0]?.id; + const couponId = couponCode ? allCoupons.find((o: { code: string }) => o.code.toLowerCase() === couponCode!.toLowerCase())?.id : allCoupons[0]?.id; return couponId; } @@ -414,15 +441,19 @@ export class ApiUtils { } // get marketplace couponId - async getMarketPlaceCouponId(couponCode: string, auth?: auth): Promise { - // todo: apply multiple optional parameter + async getMarketPlaceCouponId(couponCode?: string, auth?: auth): Promise { + if (arguments.length === 1 && typeof couponCode === 'object') { + auth = couponCode as auth; + couponCode = undefined; + } + const [, allCoupons] = await this.get(endPoints.wc.getAllCoupons, { params: { per_page: 100 }, headers: auth }); - const couponId = couponCode ? allCoupons.find((o: { code: string }) => o.code.toLowerCase() === couponCode.toLowerCase())?.id : allCoupons[0]?.id; + const couponId = couponCode ? allCoupons.find((o: { code: string }) => o.code.toLowerCase() === couponCode!.toLowerCase())?.id : allCoupons[0]?.id; return couponId; } // create marketplace coupon - async createMarketPlaceCoupon(coupon: marketPlaceCoupon, auth?: auth): Promise<[responseBody, string, string]> { + async createMarketPlaceCoupon(coupon: marketPlaceCoupon, auth?: auth): Promise<[responseBody, string, string, string]> { const [response, responseBody] = await this.post(endPoints.wc.createCoupon, { data: coupon, headers: auth }, false); let couponId: string; let couponCode: string; @@ -439,7 +470,7 @@ export class ApiUtils { couponId = String(responseBody?.id); couponCode = String(responseBody?.code); } - return [responseBody, couponId, couponCode]; + return [responseBody, couponId, couponCode, coupon.amount]; } // update marketplace coupon @@ -724,10 +755,14 @@ export class ApiUtils { } // get customerId - async getCustomerId(username: string, auth?: auth): Promise { - // todo: apply multiple optional parameter + async getCustomerId(username?: string, auth?: auth): Promise { + if (arguments.length === 1 && typeof username === 'object') { + auth = username as auth; + username = undefined; + } + const allCustomers = await this.getAllCustomers(auth); - const customerId = username ? allCustomers.find((o: { username: string }) => o.username.toLowerCase() === username.toLowerCase())?.id : allCustomers[0]?.id; + const customerId = username ? allCustomers.find((o: { username: string }) => o.username.toLowerCase() === username!.toLowerCase())?.id : allCustomers[0]?.id; return customerId; } @@ -762,6 +797,19 @@ export class ApiUtils { return responseBody; } + // delete all customers + async deleteAllCustomers(auth?: auth): Promise { + // todo: apply multiple optional parameter + const allCustomers = await this.getAllCustomers(auth); + if (!allCustomers?.length) { + console.log('No customer exists'); + return; + } + const allCustomersIds = allCustomers.map((o: { id: unknown }) => o.id); + const [, responseBody] = await this.put(endPoints.updateBatchCustomers, { data: { delete: allCustomersIds }, headers: payloads.adminAuth }); + return responseBody; + } + /** * wholesale customers api methods */ @@ -826,6 +874,20 @@ export class ApiUtils { return responseBody; } + // get single announcement + async getSingleAnnouncement(announcementId: string, auth?: auth): Promise<[responseBody, string]> { + const [, responseBody] = await this.get(endPoints.getSingleAnnouncement(announcementId), { headers: auth }); + const noticeId = responseBody?.notice_id; + return [responseBody, noticeId]; + } + + // get announcement notice Id + async getAnnouncementNoticeId(auth?: auth): Promise { + const allAnnouncements = await this.getAllAnnouncements(auth); + const noticeId = allAnnouncements[0]?.notice_id; + return noticeId; + } + // create announcement async createAnnouncement(payload: object, auth?: auth): Promise<[responseBody, string, string]> { const [, responseBody] = await this.post(endPoints.createAnnouncement, { data: payload, headers: auth }); @@ -909,10 +971,14 @@ export class ApiUtils { } // get store category Id - async getStoreCategoryId(StoreCategoryName: string, auth?: auth): Promise { - // todo: apply multiple optional parameter + async getStoreCategoryId(StoreCategoryName?: string, auth?: auth): Promise { + if (arguments.length === 1 && typeof StoreCategoryName === 'object') { + auth = StoreCategoryName as auth; + StoreCategoryName = undefined; + } + const allStoreCategories = await this.getAllStoreCategories(auth); - const storeCategoryId = StoreCategoryName ? allStoreCategories.find((o: { name: string }) => o.name.toLowerCase() === StoreCategoryName.toLowerCase())?.id : allStoreCategories[0]?.id; + const storeCategoryId = StoreCategoryName ? allStoreCategories.find((o: { name: string }) => o.name.toLowerCase() === StoreCategoryName!.toLowerCase())?.id : allStoreCategories[0]?.id; return storeCategoryId; } @@ -1128,14 +1194,11 @@ export class ApiUtils { // add spmv product to store async addSpmvProductToStore(productId: string, auth?: auth): Promise<[APIResponse, responseBody]> { const [response, responseBody] = await this.post(endPoints.addToStore, { data: { product_id: productId }, headers: auth }, false); - - // todo: need to handle already cloned product, dokan issue: fatal error for requested with cloned product id - // if(responseBody.code){ - // expect(response.status()).toBe(500); - // } else { - // expect(response.ok()).toBeTruthy(); - // } - + if (responseBody.code) { + expect(response.status()).toBe(500); + } else { + expect(response.ok()).toBeTruthy(); + } return [response, responseBody]; } @@ -1184,6 +1247,13 @@ export class ApiUtils { return responseBody; } + // get user id + async getUserId(fullName: string, auth?: auth): Promise { + const allUsers = await this.getAllUsers(auth); + const userId = allUsers.find((o: { name: string }) => o.name.toLowerCase() === fullName!.toLowerCase())?.id; + return userId; + } + // create user async createUser(payload: object, auth?: auth): Promise { // administrator, customer, seller @@ -1366,7 +1436,7 @@ export class ApiUtils { // update single woocommerce setting options async updateSingleWcSettingOptions(groupId: string, optionId: string, payload: object, auth?: auth): Promise { - const [, responseBody] = await this.post(endPoints.wc.updateSettingOption(groupId, optionId), { data: payload, headers: auth }); + const [, responseBody] = await this.post(endPoints.wc.updateSingleSettingOption(groupId, optionId), { data: payload, headers: auth }); return responseBody; } @@ -1382,6 +1452,7 @@ export class ApiUtils { async createProductReview(payload: string | object, review: object, auth?: auth): Promise<[responseBody, string, string]> { let productId: string; typeof payload === 'object' ? ([, productId] = await this.createProduct(payload, auth)) : (productId = payload); + //todo: check if product exists with that id follow: createOrder const [, responseBody] = await this.post(endPoints.wc.createReview, { data: { ...review, product_id: productId }, headers: auth }); const reviewId = String(responseBody?.id); const reviewMessage = String(responseBody?.review); @@ -1442,13 +1513,31 @@ export class ApiUtils { // create order async createOrder(product: string | object, orderPayload: any, auth?: auth): Promise<[APIResponse, responseBody, string, string]> { let productId: string; + // if (!product) { + // [, productId] = await this.createProduct(payloads.createProduct(), auth); + // } else { + // typeof product === 'object' ? ([, productId] = await this.createProduct(product, auth)) : (productId = product); + // } //todo: have to resolve invalid id form env issue + if (!product) { [, productId] = await this.createProduct(payloads.createProduct(), auth); } else { - typeof product === 'object' ? ([, productId] = await this.createProduct(product, auth)) : (productId = product); + if (typeof product === 'object') { + [, productId] = await this.createProduct(product, auth); + } else { + const responseBody = await this.getSingleProduct(product, payloads.adminAuth); + if (responseBody.code === 'dokan_rest_invalid_product_id') { + [, productId] = await this.createProduct(payloads.createProduct(), auth); + } else { + productId = product; + } + } } + // Set the product ID in the order payload const payload = orderPayload; payload.line_items[0].product_id = productId; + + // Post the order and return the results // Todo: add comment for all methods const [response, responseBody] = await this.post(endPoints.wc.createOrder, { data: payload, headers: payloads.adminAuth }, false); const orderId = String(responseBody?.id); return [response, responseBody, orderId, productId]; diff --git a/tests/pw/utils/data.json b/tests/pw/utils/data.json new file mode 100644 index 0000000000..ee99d07aa0 --- /dev/null +++ b/tests/pw/utils/data.json @@ -0,0 +1,9 @@ +{ + "SERVER_URL": "", + "CUSTOMER_ID": "", + "VENDOR_ID": "", + "VENDOR2_ID": "", + "PRODUCT_ID": "", + "V2_PRODUCT_ID": "", + "HPOS": "" +} diff --git a/tests/pw/utils/dbData.ts b/tests/pw/utils/dbData.ts index d433ce9d0a..8cfac1a723 100644 --- a/tests/pw/utils/dbData.ts +++ b/tests/pw/utils/dbData.ts @@ -52,6 +52,11 @@ export const dbData = { enable_tc_on_reg: 'on', enable_single_seller_mode: 'off', store_category_type: 'multiple', // none, multiple + + // product page settings + product_page_options: '', + show_vendor_info: 'on', + enabled_more_products_tab: 'on', }, sellingSettings: { @@ -181,12 +186,12 @@ export const dbData = { store_banner_height: '300', store_open_close: 'on', enable_theme_store_sidebar: 'off', - show_vendor_info: 'on', hide_vendor_info: { email: '', phone: '', address: '', }, + disable_dokan_fontawesome: 'off', }, privacyPolicySettings: { @@ -226,7 +231,6 @@ export const dbData = { enabled_for_customer_order: 'on', store_support_product_page: 'above_tab', support_button_label: 'Get Support', - dokan_admin_email_notification: 'on', }, sellerVerificationSettings: { diff --git a/tests/pw/utils/dbUtils.ts b/tests/pw/utils/dbUtils.ts index 8334b670d0..7f851fd852 100644 --- a/tests/pw/utils/dbUtils.ts +++ b/tests/pw/utils/dbUtils.ts @@ -3,6 +3,7 @@ import { MySqlConnection, DbContext } from 'mysqlconnector'; import { serialize, unserialize } from 'php-serialize'; import { dbData } from '@utils/dbData'; import { helpers } from '@utils/helpers'; +import { commission, feeRecipient } from '@utils/interfaces'; const { DB_HOST_NAME, DB_USER_NAME, DB_USER_PASSWORD, DATABASE, DB_PORT, DB_PREFIX } = process.env; const mySql = new MySqlConnection({ @@ -73,7 +74,7 @@ export const dbUtils = { }, // get selling info - async getSellingInfo(): Promise { + async getSellingInfo(): Promise<[commission, feeRecipient]> { const res = await this.getDokanSettings(dbData.dokan.optionName.selling); const commission = { type: res.commission_type, diff --git a/tests/pw/utils/gitTestSummary.ts b/tests/pw/utils/gitTestSummary.ts index cc144de5c3..0b3bfe5b7c 100644 --- a/tests/pw/utils/gitTestSummary.ts +++ b/tests/pw/utils/gitTestSummary.ts @@ -4,13 +4,13 @@ const { SHA, PR_NUMBER, SYSTEM_INFO, API_TEST_RESULT, E2E_TEST_RESULT } = proces const replace = obj => Object.keys(obj).forEach(key => (typeof obj[key] == 'object' ? replace(obj[key]) : (obj[key] = String(obj[key])))); const readFile = filePath => (fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, 'utf8')) : false); const getTestResult = (suiteName, filePath) => { - const testResult = readFile(filePath); - if (!testResult) { - return []; - } - replace(testResult); - const testSummary = [suiteName, testResult.total_tests, testResult.passed, testResult.failed, testResult.flaky, testResult.skipped, testResult.suite_duration_formatted]; - return testSummary; + const testResult = readFile(filePath); + if (!testResult) { + return []; + } + replace(testResult); + const testSummary = [suiteName, testResult.total_tests, testResult.passed, testResult.failed, testResult.flaky, testResult.skipped, testResult.suite_duration_formatted]; + return testSummary; }; const addSummaryHeadingAndTable = core => { diff --git a/tests/pw/utils/helpers.ts b/tests/pw/utils/helpers.ts index c7f9a2c13b..89ff4227df 100644 --- a/tests/pw/utils/helpers.ts +++ b/tests/pw/utils/helpers.ts @@ -127,7 +127,13 @@ export const helpers = { // calculate percentage percentage(number: number, percentage: number) { + // return this.roundToTwo(number * (percentage / 100)); + return number * (percentage / 100); + }, + + percentageWithRound(number: number, percentage: number) { return this.roundToTwo(number * (percentage / 100)); + // return number * (percentage / 100); }, // calculate percentage @@ -141,15 +147,57 @@ export const helpers = { return subtotal.reduce((a, b) => a + b, 0); }, + // discount + discount(subTotal: number, discount: any) { + let discount_total = 0; + switch (discount.type) { + case 'coupon': + { + switch (discount.coupon.type) { + case 'percentage': + for (const rate of discount.coupon.amount) { + if (discount.coupon.applySequentially) { + const discount = this.percentageWithRound(Number(subTotal), Number(rate)); + subTotal -= discount; + discount_total += discount; + } else { + discount_total += this.percentageWithRound(Number(subTotal), Number(rate)); + } + } + break; + + case 'fixed': + discount_total = Number(subTotal - discount.amount); + break; + + default: + break; + } + } + + break; + + case 'amount_discount': + break; + + case 'quantity_discount': + break; + + default: + break; + } + return this.roundToTwo(discount_total); + }, + // product tax productTax(taxRate: number, subtotal: number) { - const productTax = this.percentage(subtotal, taxRate); + const productTax = this.percentage(Number(subtotal), Number(taxRate)); return this.roundToTwo(productTax); }, // product tax shippingTax(taxRate: number, shippingFee = 0) { - const shippingTax = this.percentage(shippingFee, taxRate); + const shippingTax = this.percentage(Number(shippingFee), Number(taxRate)); return this.roundToTwo(shippingTax); }, @@ -235,8 +283,24 @@ export const helpers = { return fs.readFileSync(filePath, 'utf8'); }, + // read json readJson(filePath: string) { - return JSON.parse(this.readFile(filePath)); + if (fs.existsSync(filePath)) { + return JSON.parse(this.readFile(filePath)); + } + }, + + // read a single json data + readJsonData(filePath: string, propertyName: string) { + const data = this.readJson(filePath); + return data[propertyName]; + }, + + // write a single json data + writeJsonData(filePath: string, property: string, value: string) { + const jsonData = this.readJson(filePath); + jsonData[property] = value; + this.writeFile(filePath, JSON.stringify(jsonData, null, 2)); }, // write file @@ -244,6 +308,13 @@ export const helpers = { fs.writeFileSync(filePath, content, { encoding: 'utf8' }); }, + // delete file + deleteFile(filePath: string) { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + }, + // append file appendFile(filePath: string, content: string) { fs.appendFileSync(filePath, content, { encoding: 'utf8' }); @@ -260,6 +331,17 @@ export const helpers = { this.appendFile('.env', content); }, + // write env json + writeEnvJson(property: string, value: string) { + const filePath = 'utils/data.json'; + let envData: { [key: string]: string } = {}; + if (fs.existsSync(filePath)) { + envData = this.readJson(filePath); + } + envData[property] = value; + this.writeFile(filePath, JSON.stringify(envData, null, 2)); + }, + async createPage(browser: Browser, options?: BrowserContextOptions | undefined) { const browserContext = await browser.newContext(options); return browserContext.newPage(); diff --git a/tests/pw/utils/interfaces.ts b/tests/pw/utils/interfaces.ts index aff0bf8dc8..263523ff21 100644 --- a/tests/pw/utils/interfaces.ts +++ b/tests/pw/utils/interfaces.ts @@ -1754,3 +1754,15 @@ export interface storageState { // eslint-disable-next-line @typescript-eslint/no-explicit-any export type responseBody = any; + +export interface commission { + type: string; + amount: string; + additionalAmount: string; +} + +export interface feeRecipient { + shippingFeeRecipient: string; + taxFeeRecipient: string; + shippingTaxFeeRecipient: string; +} diff --git a/tests/pw/utils/payloads.ts b/tests/pw/utils/payloads.ts index d277b89564..1a1284e44c 100644 --- a/tests/pw/utils/payloads.ts +++ b/tests/pw/utils/payloads.ts @@ -614,7 +614,7 @@ export const payloads = { }, { key: 'coupon_commissions_type', - value: 'default', + value: 'default', // 'default', 'from_vendor', 'from_admin', 'shared_coupon' }, { key: 'admin_coupons_show_on_stores', @@ -678,6 +678,8 @@ export const payloads = { total: '10.00', }, ], + + coupon_lines: [], }, createOrderCod: { @@ -2213,6 +2215,10 @@ export const payloads = { sender_type: 'all_seller', }, + updateAnnouncementNotice: { + read_status: 'read', // read, unread + }, + // product review updateProductReview: { @@ -2592,7 +2598,7 @@ export const payloads = { email: process.env.VENDOR2 + '@yopmail.com', store_name: process.env.VENDOR2 + 'store', first_name: process.env.VENDOR2, - last_name: 'v1', + last_name: 'v', social: { fb: 'http://dokan.test', youtube: 'http://dokan.test', diff --git a/tests/pw/utils/pwMatchers.ts b/tests/pw/utils/pwMatchers.ts new file mode 100644 index 0000000000..3ff23bb7a0 --- /dev/null +++ b/tests/pw/utils/pwMatchers.ts @@ -0,0 +1,40 @@ +import { ZodTypeAny } from 'zod'; + +export const customMatchers = { + toMatchSchema(responseBody: object, schema: ZodTypeAny) { + const result = schema.safeParse(responseBody); + if (result.success) { + return { + message: () => 'schema matched', + pass: true, + }; + } else { + return { + message: () => '\x1b[91m ' + ' Result does not match schema: ' + result.error.issues.map(issue => issue.message).join('\n') + '\n' + 'Details: ' + JSON.stringify(result.error, null, 2) + '\x1b[0m', + pass: false, + }; + } + }, + + toBeWithinRange(received: number, floor: number, ceiling: number) { + const pass = received >= floor && received <= ceiling; + if (pass) { + return { + message: () => 'passed', + pass: true, + }; + } else { + return { + message: () => 'failed', + pass: false, + }; + } + }, +}; + +export const customExpect = { + toMatchSchema: customMatchers.toMatchSchema, + toBeWithinRange: customMatchers.toBeWithinRange, +}; + +//todo: add more custom matchers diff --git a/tests/pw/utils/schemas.ts b/tests/pw/utils/schemas.ts new file mode 100644 index 0000000000..dd7b31f60f --- /dev/null +++ b/tests/pw/utils/schemas.ts @@ -0,0 +1,900 @@ +import { z } from 'zod'; + +export const schemas = { + abuseReportsSchema: { + abuseReportReasonsSchema: z.array( + z.object({ + id: z.string(), + value: z.string(), + }), + ), + + abuseReportSchema: z.array( + z.object({ + id: z.number(), + reason: z.string(), + product: z.object({ + id: z.number(), + title: z.string(), + admin_url: z.string().url(), + }), + vendor: z.object({ + id: z.number(), + name: z.string(), + admin_url: z.string().url(), + }), + reported_by: z.object({ + id: z.number(), + name: z.string(), + email: z.string().email(), + admin_url: z.string().url(), + }), + description: z.string(), + reported_at: z.string(), //todo add date format + }), + ), + + // abuseReportsSchema: z.array(schemas.abuseReportsSchema.abuseReportsSchema), + }, + + admin: { + //reportOverviewSchema + reportOverviewSchema: z.object({ + labels: z.array(z.string()), + datasets: z.array( + z.object({ + label: z.string(), + borderColor: z.string(), + fill: z.boolean(), + data: z.array(z.union([z.string(), z.number()])), + tooltipLabel: z.string().optional(), + tooltipPrefix: z.string().optional(), + }), + ), + }), + + // reportSummarySchema + reportSummarySchema: z.object({ + products: z.object({ + this_month: z.number(), + last_month: z.number(), + this_period: z.null(), + class: z.string(), + parcent: z.number(), + }), + withdraw: z.object({ + pending: z.union([z.string(), z.number()]), + completed: z.union([z.string(), z.number()]), + cancelled: z.union([z.string(), z.number()]), + }), + vendors: z.object({ + inactive: z.number(), + active: z.number(), + this_month: z.number(), + last_month: z.number(), + this_period: z.null(), + class: z.string(), + parcent: z.number(), + }), + sales: z.object({ + this_month: z.number(), + last_month: z.number(), + this_period: z.null(), + class: z.string(), + parcent: z.number(), + }), + orders: z.object({ + this_month: z.number(), + last_month: z.number(), + this_period: z.null(), + class: z.string(), + parcent: z.number(), + }), + earning: z.object({ + this_month: z.number(), + last_month: z.number(), + this_period: z.null(), + class: z.string(), + parcent: z.number(), + }), + }), + + //adminDashboardFeedSchema + adminDashboardFeedSchema: z.array( + z.object({ + link: z.string(), + title: z.string(), + desc: z.string(), + summary: z.string(), + date: z.string(), + author: z.string(), + }), + ), + + //adminHelpSchema + adminHelpSchema: z.array( + z.object({ + title: z.string(), + icon: z.string(), + questions: z.array( + z.object({ + title: z.string(), + link: z.string(), + }), + ), + }), + ), + + //adminNoticesSchema + adminNoticesSchema: z.array( + z.object({ + type: z.string(), + title: z.string(), + description: z.string().optional(), + priority: z.number(), + show_close_button: z.boolean().optional(), + ajax_data: z + .object({ + action: z.string(), + nonce: z.string().optional(), + key: z.string().optional(), + dokan_promotion_dismissed: z.boolean().optional(), + _wpnonce: z.string().optional(), + }) + .optional(), + actions: z + .array( + z.object({ + type: z.string(), + text: z.string(), + action: z.string().optional(), + target: z.string().optional(), + loading_text: z.string().optional(), + completed_text: z.string().optional(), + reload: z.boolean().optional(), + ajax_data: z + .object({ + action: z.string(), + nonce: z.string().optional(), + }) + .optional(), + }), + ) + .optional(), + }), + ), + + //changelogLiteSchema + changelogLiteSchema: z.string(), //todo: update schema + + //changelogProSchema + changelogProSchema: z.string(), //todo: update schema + + //adminPromoNoticeSchema + adminPromoNoticeSchema: z.array(z.unknown()), //todo: update schema + + //adminLogsSchema + adminLogsSchema: z.array( + z.object({ + order_id: z.string(), + vendor_id: z.string(), + vendor_name: z.string(), + previous_order_total: z.string(), + order_total: z.string(), + vendor_earning: z.string(), + commission: z.string(), + dokan_gateway_fee: z.union([z.string(), z.number()]), + gateway_fee_paid_by: z.string(), + shipping_total: z.string(), + shipping_total_refunded: z.string(), + shipping_total_remains: z.string(), + has_shipping_refund: z.boolean(), + shipping_total_tax: z.string(), + shipping_total_tax_refunded: z.string(), + shipping_total_tax_remains: z.string(), + has_shipping_tax_refund: z.boolean(), + tax_total: z.string(), + tax_total_refunded: z.string(), + tax_total_remains: z.string(), + has_tax_refund: z.boolean(), + status: z.string(), + date: z.string(), + has_refund: z.boolean(), + shipping_recipient: z.string(), + shipping_tax_recipient: z.string(), + tax_recipient: z.string(), + }), + ), + + //adminExportLogsSchema + adminExportLogsSchema: z.object({ + step: z.number(), + percentage: z.number(), + columns: z.object({ + order_id: z.string(), + vendor_id: z.string(), + vendor_name: z.string(), + previous_order_total: z.string(), + order_total: z.string(), + vendor_earning: z.string(), + commission: z.string(), + dokan_gateway_fee: z.string(), + gateway_fee_paid_by: z.string(), + shipping_total: z.string(), + tax_total: z.string(), + status: z.string(), + date: z.string(), + }), + }), + }, + + announcementsSchema: { + announcementSchema: z.object({ + id: z.number(), + title: z.string(), + content: z.string(), + status: z.string(), + created_at: z.string(), + sender_type: z.string(), + sender_ids: z.array( + z.object({ + id: z.number(), + name: z.string(), + shop_name: z.string(), + }), + ), + _links: z.object({ + self: z.array( + z.object({ + href: z.string(), + }), + ), + collection: z.array( + z.object({ + href: z.string(), + }), + ), + }), + }), + }, + + attributesSchema: {}, //TODO: + attributeTeermsSchema: {}, //TODO: + couponsSchema: { + //todo: this schema might be sufficient for all + couponSchema: z.object({ + id: z.number(), + code: z.string(), + amount: z.string(), + date_created: z.string(), //todo add date format + date_created_gmt: z.string(), + date_modified: z.string(), + date_modified_gmt: z.string(), + discount_type: z.string(), + description: z.string(), + date_expires: z.nullable(z.string()), + date_expires_gmt: z.nullable(z.string()), + usage_count: z.number(), + individual_use: z.boolean(), + product_ids: z.array(z.string()), + excluded_product_ids: z.array(z.string()), + usage_limit: z.nullable(z.number()), + usage_limit_per_user: z.nullable(z.number()), + limit_usage_to_x_items: z.nullable(z.number()), + free_shipping: z.boolean(), + product_categories: z.array(z.string()), + excluded_product_categories: z.array(z.string()), + exclude_sale_items: z.boolean(), + minimum_amount: z.string(), + maximum_amount: z.string(), + email_restrictions: z.array(z.string()), + used_by: z.array(z.string()), + meta_data: z.array( + z.object({ + id: z.number(), + key: z.string(), + value: z.string(), + }), + ), + _links: z.object({ + self: z.array( + z.object({ + href: z.string().url(), + }), + ), + collection: z.array( + z.object({ + href: z.string().url(), + }), + ), + }), + }), + }, + customersSchema: {}, //TODO: + + dokanEndpointsSchema: {}, //TODO: + + dummyDataSchema: {}, //TODO: + + followStoresSchema: {}, //TODO: + + modulesSchema: { + //todo: this schema might be sufficient for all + modulesSchema: z.object({ + id: z.string(), + name: z.string(), + description: z.string(), + thumbnail: z.string().url(), + plan: z.enum(['starter', 'liquidweb', 'professional', 'business', 'enterprise']), + active: z.boolean(), + available: z.boolean(), + doc_id: z.string().nullable(), + doc_link: z.string().nullable(), + mod_link: z.string().nullable(), + pre_requisites: z.string().nullable(), + categories: z.array(z.string()).nullable(), + video_id: z.string().nullable(), + }), + }, + orderDownloadsSchema: {}, //TODO: + + orderNotesSchema: {}, //TODO: + + ordersSchema: {}, //TODO: + + productAdvertisementsSchema: {}, //TODO: + + productBlocksSchema: {}, //TODO: + + productDuplicateSchema: {}, //TODO: + + productFilterSchema: {}, //TODO: + + productReviewsSchema: {}, //TODO: + + productsSchema: {}, //TODO: + + productVariationsSchema: {}, //TODO: + + quoteRequestsSchema: {}, //TODO: + + quoteRulesSchema: {}, //TODO: + + rankMathSchema: {}, //TODO: + + refundsSchema: {}, //TODO: + + reportsSchema: {}, //TODO: + + reverseWithdrawalSchema: {}, //TODO: + + rolesSchema: {}, //TODO: + + sellerBadgeSchema: {}, //TODO: + + settingsSchema: {}, //TODO: + + settingsGroupSchema: {}, //TODO: + + spmvSchema: { + spmvSettingsSchema: z.object({ + success: z.boolean(), + }), + + spmvProductsSchema: z.array( + z.object({ + average_rating: z.string(), + title: z.string(), + image: z.string(), + permalink: z.string(), + review_count: z.number(), + type: z.string(), + id: z.number(), + price: z.string(), + price_html: z.string(), + category_list: z.string(), + vendor_name: z.string(), + action: z.string(), + }), + ), + + addToStoreSchema: z.object({ + status: z.boolean(), + success_message: z.string(), + }), + }, + + storeCategoriesSchema: {}, //TODO: + + storeReviewsSchema: {}, //TODO: + + storesSchema: {}, //TODO: + + supportTicketsSchema: {}, //TODO: + + vendorDashboardSchema: {}, //TODO: + + vendorStaffSchema: { + staff: z.object({ + ID: z.string(), + user_login: z.string(), + user_nicename: z.string(), + user_email: z.string(), + user_url: z.string(), + user_registered: z.string(), + user_activation_key: z.string(), + user_status: z.string(), + display_name: z.string(), + phone: z.string(), + first_name: z.string(), + last_name: z.string(), + registered_at: z.string(), + avatar: z.string(), + capabilities: z.object({ + read: z.boolean(), + vendor_staff: z.boolean(), + dokandar: z.boolean(), + delete_pages: z.boolean(), + publish_posts: z.boolean(), + edit_posts: z.boolean(), + delete_published_posts: z.boolean(), + edit_published_posts: z.boolean(), + delete_posts: z.boolean(), + manage_categories: z.boolean(), + moderate_comments: z.boolean(), + upload_files: z.boolean(), + edit_shop_orders: z.boolean(), + edit_product: z.boolean(), + dokan_view_sales_overview: z.boolean(), + dokan_view_sales_report_chart: z.boolean(), + dokan_view_announcement: z.boolean(), + dokan_view_order_report: z.boolean(), + dokan_view_review_reports: z.boolean(), + dokan_view_product_status_report: z.boolean(), + dokan_add_product: z.boolean(), + dokan_edit_product: z.boolean(), + dokan_delete_product: z.boolean(), + dokan_view_product: z.boolean(), + dokan_duplicate_product: z.boolean(), + dokan_import_product: z.boolean(), + dokan_export_product: z.boolean(), + dokan_view_order: z.boolean(), + dokan_manage_order: z.boolean(), + dokan_manage_order_note: z.boolean(), + dokan_manage_reviews: z.boolean(), + dokan_view_overview_menu: z.boolean(), + dokan_view_product_menu: z.boolean(), + dokan_view_order_menu: z.boolean(), + dokan_view_review_menu: z.boolean(), + dokan_view_store_settings_menu: z.boolean(), + dokan_view_store_shipping_menu: z.boolean(), + dokan_view_store_social_menu: z.boolean(), + dokan_view_store_seo_menu: z.boolean(), + dokan_export_order: z.boolean(), + }), + }), + + allStaffCapabilities: z.object({ + all: z.object({ + overview: z.object({ + dokan_view_sales_overview: z.string(), + dokan_view_sales_report_chart: z.string(), + dokan_view_announcement: z.string(), + dokan_view_order_report: z.string(), + dokan_view_review_reports: z.string(), + dokan_view_product_status_report: z.string(), + }), + report: z.object({ + dokan_view_overview_report: z.string(), + dokan_view_daily_sale_report: z.string(), + dokan_view_top_selling_report: z.string(), + dokan_view_top_earning_report: z.string(), + dokan_view_statement_report: z.string(), + }), + order: z.object({ + dokan_view_order: z.string(), + dokan_manage_order: z.string(), + dokan_manage_order_note: z.string(), + dokan_manage_refund: z.string(), + dokan_export_order: z.string(), + }), + coupon: z.object({ + dokan_add_coupon: z.string(), + dokan_edit_coupon: z.string(), + dokan_delete_coupon: z.string(), + }), + review: z.object({ + dokan_view_reviews: z.string(), + dokan_manage_reviews: z.string(), + }), + withdraw: z.object({ + dokan_manage_withdraw: z.string(), + }), + product: z.object({ + dokan_add_product: z.string(), + dokan_edit_product: z.string(), + dokan_delete_product: z.string(), + dokan_view_product: z.string(), + dokan_duplicate_product: z.string(), + dokan_import_product: z.string(), + dokan_export_product: z.string(), + }), + menu: z.object({ + dokan_view_overview_menu: z.string(), + dokan_view_product_menu: z.string(), + dokan_view_order_menu: z.string(), + dokan_view_coupon_menu: z.string(), + dokan_view_report_menu: z.string(), + dokan_view_review_menu: z.string(), + dokan_view_withdraw_menu: z.string(), + dokan_view_store_settings_menu: z.string(), + dokan_view_store_payment_menu: z.string(), + dokan_view_store_shipping_menu: z.string(), + dokan_view_store_social_menu: z.string(), + dokan_view_store_seo_menu: z.string(), + dokan_view_booking_menu: z.string(), + dokan_view_tools_menu: z.string(), + dokan_view_store_verification_menu: z.string(), + dokan_view_auction_menu: z.string(), + }), + booking: z.object({ + dokan_manage_booking_products: z.string(), + dokan_manage_booking_calendar: z.string(), + dokan_manage_bookings: z.string(), + dokan_manage_booking_resource: z.string(), + dokan_add_booking_product: z.string(), + dokan_edit_booking_product: z.string(), + dokan_delete_booking_product: z.string(), + }), + store_support: z.object({ + dokan_manage_support_tickets: z.string(), + }), + auction: z.object({ + dokan_add_auction_product: z.string(), + dokan_edit_auction_product: z.string(), + dokan_delete_auction_product: z.string(), + }), + }), + default: z.array(z.string()), + }), + + staffCapabilities: z.object({ + ID: z.string(), + user_login: z.string(), + user_nicename: z.string(), + user_email: z.string(), + user_url: z.string(), + user_registered: z.string(), + user_activation_key: z.string(), + user_status: z.string(), + display_name: z.string(), + phone: z.string(), + first_name: z.string(), + last_name: z.string(), + registered_at: z.string(), + avatar: z.string(), + capabilities: z.object({ + read: z.boolean(), + vendor_staff: z.boolean(), + dokandar: z.boolean(), + delete_pages: z.boolean(), + publish_posts: z.boolean(), + edit_posts: z.boolean(), + delete_published_posts: z.boolean(), + edit_published_posts: z.boolean(), + delete_posts: z.boolean(), + manage_categories: z.boolean(), + moderate_comments: z.boolean(), + upload_files: z.boolean(), + edit_shop_orders: z.boolean(), + edit_product: z.boolean(), + dokan_view_sales_overview: z.boolean(), + dokan_view_sales_report_chart: z.boolean(), + dokan_view_announcement: z.boolean(), + dokan_view_order_report: z.boolean(), + dokan_view_review_reports: z.boolean(), + dokan_view_product_status_report: z.boolean(), + dokan_add_product: z.boolean(), + dokan_edit_product: z.boolean(), + dokan_delete_product: z.boolean(), + dokan_view_product: z.boolean(), + dokan_duplicate_product: z.boolean(), + dokan_import_product: z.boolean(), + dokan_export_product: z.boolean(), + dokan_view_order: z.boolean(), + dokan_manage_order: z.boolean(), + dokan_manage_order_note: z.boolean(), + dokan_manage_reviews: z.boolean(), + dokan_view_overview_menu: z.boolean(), + dokan_view_product_menu: z.boolean(), + dokan_view_order_menu: z.boolean(), + dokan_view_review_menu: z.boolean(), + dokan_view_store_settings_menu: z.boolean(), + dokan_view_store_shipping_menu: z.boolean(), + dokan_view_store_social_menu: z.boolean(), + dokan_view_store_seo_menu: z.boolean(), + dokan_export_order: z.boolean(), + }), + }), + + updateCapabilities: z.object({ + dokan_view_sales_overview: z.boolean(), + dokan_view_sales_report_chart: z.boolean(), + dokan_view_announcement: z.boolean(), + dokan_view_order_report: z.boolean(), + dokan_view_review_reports: z.boolean(), + dokan_view_product_status_report: z.boolean(), + dokan_add_product: z.boolean(), + dokan_edit_product: z.boolean(), + dokan_delete_product: z.boolean(), + dokan_view_product: z.boolean(), + dokan_duplicate_product: z.boolean(), + dokan_import_product: z.boolean(), + dokan_export_product: z.boolean(), + dokan_view_order: z.boolean(), + dokan_manage_order: z.boolean(), + dokan_manage_order_note: z.boolean(), + dokan_manage_reviews: z.boolean(), + dokan_view_overview_menu: z.boolean(), + dokan_view_product_menu: z.boolean(), + dokan_view_order_menu: z.boolean(), + dokan_view_review_menu: z.boolean(), + dokan_view_store_settings_menu: z.boolean(), + dokan_view_store_shipping_menu: z.boolean(), + dokan_view_store_social_menu: z.boolean(), + dokan_view_store_seo_menu: z.boolean(), + dokan_export_order: z.boolean(), + read: z.boolean(), + vendor_staff: z.boolean(), + dokandar: z.boolean(), + delete_pages: z.boolean(), + publish_posts: z.boolean(), + edit_posts: z.boolean(), + delete_published_posts: z.boolean(), + edit_published_posts: z.boolean(), + delete_posts: z.boolean(), + manage_categories: z.boolean(), + moderate_comments: z.boolean(), + upload_files: z.boolean(), + edit_shop_orders: z.boolean(), + edit_product: z.boolean(), + }), + }, + + wholesaleCustomersSchema: { + wholesaleCustomersSchema: z.object({ + id: z.number(), + first_name: z.string(), + last_name: z.string(), + username: z.string(), + name: z.string(), + email: z.string(), + avatar: z.string(), + url: z.string(), + role: z.string(), + registered_date: z.string(), + wholesale_status: z.string(), + _links: z.object({ + self: z.array( + z.object({ + href: z.string(), + }), + ), + collection: z.array( + z.object({ + href: z.string(), + }), + ), + }), + }), + }, //TODO: + + withdrawsSchema: { + // getBalanceDetailsSchema + getBalanceDetailsSchema: z.object({ + current_balance: z.number(), + withdraw_limit: z.string(), + withdraw_threshold: z.number(), + withdraw_methods: z.enum(['paypal', 'bank']), //TODO: add more + last_withdraw: z + .array( + z.object({ + id: z.string(), + user_id: z.string(), + amount: z.string(), + date: z.string(), + status: z.string(), + method: z.string(), + note: z.string(), + details: z.string(), + ip: z.string(), + }), + ) + .optional(), + }), + + allWithdrawsSchema: z.object({ + id: z.string(), + user: z.object({ + id: z.string(), + store_name: z.string(), + first_name: z.string(), + last_name: z.string(), + email: z.string(), + social: z.record(z.string()), // assuming social is a record of strings + phone: z.string(), + show_email: z.boolean(), + address: z.object({ + street_1: z.string(), + street_2: z.string(), + city: z.string(), + zip: z.string(), + country: z.string(), + state: z.string(), + }), + location: z.string(), + banner: z.string(), + banner_id: z.string(), + gravatar: z.string(), + gravatar_id: z.string(), + shop_url: z.string(), + toc_enabled: z.boolean(), + store_toc: z.string(), + featured: z.boolean(), + rating: z.object({ + rating: z.string(), + count: z.number(), + }), + enabled: z.boolean(), + registered: z.string(), + payment: z.object({ + bank: z + .object({ + ac_name: z.string(), + ac_type: z.string(), + ac_number: z.string(), + bank_name: z.string(), + bank_addr: z.string(), + routing_number: z.string(), + iban: z.string(), + swift: z.string(), + declaration: z.string(), + }) + .nullable(), // Assuming bank is optional + paypal: z + .object({ + email: z.string(), + }) + .nullable(), // Assuming paypal is optional + stripe_express: z.boolean(), + }), + trusted: z.boolean(), + store_open_close: z.object({ + enabled: z.boolean(), + time: z.record( + z.object({ + status: z.string(), + opening_time: z.array(z.string()), + closing_time: z.array(z.string()), + }), + ), + open_notice: z.string(), + close_notice: z.string(), + }), + sale_only_here: z.boolean(), + company_name: z.string(), + vat_number: z.string(), + company_id_number: z.string(), + bank_name: z.string(), + bank_iban: z.string(), + categories: z.array( + z.object({ + term_id: z.number(), + name: z.string(), + slug: z.string(), + term_group: z.number(), + term_taxonomy_id: z.number(), + taxonomy: z.string(), + description: z.string(), + parent: z.number(), + count: z.number(), + filter: z.string(), + }), + ), + }), + amount: z.number(), + created: z.string(), + status: z.string(), + method: z.string(), + method_title: z.string(), + note: z.string(), + details: z.union([ + z.object({ + paypal: z.object({ + email: z.string(), + }), + }), + z.object({ + bank: z.object({ + ac_name: z.string(), + ac_type: z.string(), + ac_number: z.string(), + bank_name: z.string(), + bank_addr: z.string(), + routing_number: z.string(), + iban: z.string(), + swift: z.string(), + }), + }), + z.object({ + skrill: z.object({ + email: z.string(), + }), + }), + ]), + ip: z.string(), + _links: z.object({ + self: z.array(z.object({ href: z.string().url() })), + collection: z.array(z.object({ href: z.string().url() })), + }), + + WithdrawPaymentMethods: z.array( + z.object({ + id: z.string(), + title: z.string(), + }), + ), + + batchUpdateWithdraw: z.object({ + success: z.array(z.unknown()), + failed: z.object({ + approved: z.array(z.number()), + }), + }), + }), + }, //TODO: + + withdrawSettingsSchema: { + withdrawSettingsSchema: z.object({ + withdraw_method: z.enum(['paypal', 'bank', 'skrill', 'dokan_custom']), + payment_methods: z.array( + z.object({ + label: z.string(), + value: z.string(), + }), + ), + }), + + withdrawSummarySchema: z.object({ + total: z.number(), + pending: z.number(), + approved: z.number(), + cancelled: z.number(), + }), + + withdrawDisbursementSettingsSchema: z.object({ + enabled: z.boolean(), + selected_schedule: z.enum(['quarterly', 'monthly', 'biweekly', 'weekly']), + minimum_amount_list: z.array(z.number()), + minimum_amount_selected: z.number(), + reserve_balance_list: z.array(z.number()), + reserve_balance_selected: z.number(), + default_method: z.enum(['paypal', 'bank', 'skrill', 'dokan_custom']), + schedules: z.record( + z.object({ + next: z.string(), + title: z.string(), + description: z.string(), + }), + ), + active_methods: z.array(z.enum(['PayPal', 'Bank Transfer', 'Skrill', 'bkash'])), + method_additional_info: z.array(z.enum(['paypal', 'bank', 'skrill', 'dokan_custom'])), + minimum_amount_needed: z.number(), + is_schedule_selected: z.boolean(), + }), + + updateWithdrawDisbursementSettingsSchema: z.object({ + success: z.boolean(), + }), + }, //TODO: +}; diff --git a/tests/pw/utils/testData.ts b/tests/pw/utils/testData.ts index 6ff9cbe438..35cdf6491c 100644 --- a/tests/pw/utils/testData.ts +++ b/tests/pw/utils/testData.ts @@ -1,5 +1,7 @@ +// import { request } from '@playwright/test'; import { faker } from '@faker-js/faker'; import { helpers } from '@utils/helpers'; +import 'dotenv/config'; interface user { username: string; @@ -14,7 +16,19 @@ interface admin { export { admin, user }; export const data = { - // Generated test data + envData: 'utils/data.json', + env: { + DOKAN_PRO: process.env.DOKAN_PRO ? true : false, + + // db data + DB_HOST_NAME: process.env.DB_HOST_NAME, + DB_USER_NAME: process.env.DB_USER_NAME, + DB_USER_PASSWORD: process.env.DB_USER_PASSWORD, + DATABASE: process.env.DATABASE, + DB_PORT: process.env.DB_PORT, + DB_PREFIX: process.env.DB_PREFIX, + }, + auth: { adminAuthFile: 'playwright/.auth/adminStorageState.json', vendorAuthFile: 'playwright/.auth/vendorStorageState.json', @@ -931,7 +945,7 @@ export const data = { companyId: faker.string.alphanumeric(5), vatNumber: faker.string.alphanumeric(10), bankIban: faker.finance.iban(), - phoneNumber: faker.phone.number('(###) ###-####'), + phoneNumber: faker.phone.number(), phone: '0123456789', street1: 'abc street', street2: 'xyz street', @@ -1279,7 +1293,7 @@ export const data = { firstName: faker.person.firstName('male'), lastName: faker.person.lastName('male'), email: faker.internet.email(), - phone: faker.phone.number('(###) ###-####'), + phone: faker.phone.number(), password: String(process.env.USER_PASSWORD), }), @@ -1306,7 +1320,7 @@ export const data = { bankName: 'bankName', bankAddress: 'bankAddress', bankIban: faker.finance.iban(), - phone: faker.phone.number('(###) ###-####'), + phone: faker.phone.number(), street1: 'abc street', street2: 'xyz street', country: 'United States (US)', @@ -1522,7 +1536,7 @@ export const data = { fullName: faker.person.fullName({ sex: 'male' }), email: faker.person.firstName('male') + '@email.com', companyName: faker.company.name(), - phoneNumber: faker.phone.number('(###) ###-####'), + phoneNumber: faker.phone.number(), }), }, @@ -1697,7 +1711,7 @@ export const data = { general: { vendorStoreUrl: 'store', setupWizardMessage: - 'Thank you for choosing The Marketplace to power your online store! This quick setup wizard will help you configure the basic settings. It\'s completely optional and shouldn\'t take longer than two minutes.', + "Thank you for choosing The Marketplace to power your online store! This quick setup wizard will help you configure the basic settings. It's completely optional and shouldn't take longer than two minutes.", sellingProductTypes: 'sell_both', // 'sell_both', 'sell_physical', 'sell_digital' storeProductPerPage: '12', storCategory: 'multiple', // 'none', 'single', 'multiple'