From a535d46f96f3b57236c8e3900bdd2eb07085a5e4 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 06:50:38 -0500 Subject: [PATCH 01/15] Setup playwright tests --- .github/workflows/playwright.yml | 26 + .gitignore | 2 + package.json | 1 + playwright.config.ts | 107 +++ tests/example.spec.ts | 7 + yarn.lock | 1151 +++++++++++++++++++++++++++++- 6 files changed, 1271 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright.config.ts create mode 100644 tests/example.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..f2923824 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,26 @@ +name: Playwright Tests +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: "14.x" + - name: Install dependencies + run: yarn + - name: Install Playwright + run: npx playwright install --with-deps + - name: Run Playwright tests + run: yarn playwright test + - uses: actions/upload-artifact@v2 + if: always() + with: + name: playwright-test-results + path: test-results/ diff --git a/.gitignore b/.gitignore index 5d16fa58..b8faef41 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ tsconfig.tsbuildinfo npm-debug.log* yarn-debug.log* yarn-error.log* +test-results/ +playwright-report/ diff --git a/package.json b/package.json index 10038cff..fed926da 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@kachkaev/markdownlint-config": "^0.3.0", "@netlify/plugin-nextjs": "^4.1.1", "@next/eslint-plugin-next": "^12.0.7", + "@playwright/test": "^1.18.0", "@types/node": "^16.11.19", "@types/react": "^17.0.38", "@types/react-gravatar": "^2.6.10", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..77bbc1dc --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,107 @@ +import { devices, PlaywrightTestConfig } from "@playwright/test"; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: "./tests", + + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + + /* Project-specific settings. */ + use: { + ...devices["Desktop Chrome"], + }, + }, + + { + name: "firefox", + use: { + ...devices["Desktop Firefox"], + }, + }, + + { + name: "webkit", + use: { + ...devices["Desktop Safari"], + }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; +export default config; diff --git a/tests/example.spec.ts b/tests/example.spec.ts new file mode 100644 index 00000000..069c588f --- /dev/null +++ b/tests/example.spec.ts @@ -0,0 +1,7 @@ +import { expect, test } from "@playwright/test"; + +test("basic test", async ({ page }) => { + await page.goto("https://playwright.dev/"); + await page.locator("text=Get started").click(); + await expect(page).toHaveTitle(/Getting started/); +}); diff --git a/yarn.lock b/yarn.lock index 9e3a7aba..6b557ac9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,7 +35,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.7": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.14.5, @babel/code-frame@npm:^7.16.7": version: 7.16.7 resolution: "@babel/code-frame@npm:7.16.7" dependencies: @@ -44,6 +44,36 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.16.4": + version: 7.16.8 + resolution: "@babel/compat-data@npm:7.16.8" + checksum: 10da2dac5ea9589c251412b00920889910e476c1ab24cd7095577635bc3a27c785151c89db4e26285fd39f509510ec29ab9d7e721f4fc16e4aec221cacde784b + languageName: node + linkType: hard + +"@babel/core@npm:^7.14.8": + version: 7.16.10 + resolution: "@babel/core@npm:7.16.10" + dependencies: + "@babel/code-frame": ^7.16.7 + "@babel/generator": ^7.16.8 + "@babel/helper-compilation-targets": ^7.16.7 + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helpers": ^7.16.7 + "@babel/parser": ^7.16.10 + "@babel/template": ^7.16.7 + "@babel/traverse": ^7.16.10 + "@babel/types": ^7.16.8 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.1.2 + semver: ^6.3.0 + source-map: ^0.5.0 + checksum: d34c5edf9258e620d0cbea4ed25a2615f5ab86dc7f7c1b96ac19817fb7d57bfae9af72dc244385a724225dbc701cb878d9559158aee9659e2a3c69d03f93f3e1 + languageName: node + linkType: hard + "@babel/generator@npm:^7.16.7": version: 7.16.7 resolution: "@babel/generator@npm:7.16.7" @@ -55,7 +85,18 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.16.0": +"@babel/generator@npm:^7.16.8": + version: 7.16.8 + resolution: "@babel/generator@npm:7.16.8" + dependencies: + "@babel/types": ^7.16.8 + jsesc: ^2.5.1 + source-map: ^0.5.0 + checksum: 83af38b34735605c9d5f774c87a46c2cffaf666b28e9eeba883b2d7076412257e5c2264c26d9740ce44da6955fdaf857659391db02c012714a2a6dc19e403105 + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.16.0, @babel/helper-annotate-as-pure@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-annotate-as-pure@npm:7.16.7" dependencies: @@ -64,6 +105,37 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-compilation-targets@npm:7.16.7" + dependencies: + "@babel/compat-data": ^7.16.4 + "@babel/helper-validator-option": ^7.16.7 + browserslist: ^4.17.5 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 7238aaee78c011a42fb5ca92e5eff098752f7b314c2111d7bb9cdd58792fcab1b9c819b59f6a0851dc210dc09dc06b30d130a23982753e70eb3111bc65204842 + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.16.10, @babel/helper-create-class-features-plugin@npm:^7.16.7": + version: 7.16.10 + resolution: "@babel/helper-create-class-features-plugin@npm:7.16.10" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.7 + "@babel/helper-environment-visitor": ^7.16.7 + "@babel/helper-function-name": ^7.16.7 + "@babel/helper-member-expression-to-functions": ^7.16.7 + "@babel/helper-optimise-call-expression": ^7.16.7 + "@babel/helper-replace-supers": ^7.16.7 + "@babel/helper-split-export-declaration": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 2ab266aac7f94403311f63a17d32abb718ff040339bcae19880091de3fdb4e8d7196cb4e680f01a92924eb1a00a143364456e452c511c0b7b6e0b1a4b0e696da + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-environment-visitor@npm:7.16.7" @@ -102,7 +174,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.16.0": +"@babel/helper-member-expression-to-functions@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: e275378022278a7e7974a3f65566690f1804ac88c5f4e848725cf936f61cd1e2557e88cfb6cb4fea92ae5a95ad89d78dbccc9a53715d4363f84c9fd109272c18 + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.16.0, @babel/helper-module-imports@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-module-imports@npm:7.16.7" dependencies: @@ -111,13 +192,69 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.14.5": +"@babel/helper-module-transforms@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-module-transforms@npm:7.16.7" + dependencies: + "@babel/helper-environment-visitor": ^7.16.7 + "@babel/helper-module-imports": ^7.16.7 + "@babel/helper-simple-access": ^7.16.7 + "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/helper-validator-identifier": ^7.16.7 + "@babel/template": ^7.16.7 + "@babel/traverse": ^7.16.7 + "@babel/types": ^7.16.7 + checksum: 6e930ce776c979f299cdbeaf80187f4ab086d75287b96ecc1c6896d392fcb561065f0d6219fc06fa79b4ceb4bbdc1a9847da8099aba9b077d0a9e583500fb673 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: 925feb877d5a30a71db56e2be498b3abbd513831311c0188850896c4c1ada865eea795dce5251a1539b0f883ef82493f057f84286dd01abccc4736acfafe15ea + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.16.7 resolution: "@babel/helper-plugin-utils@npm:7.16.7" checksum: d08dd86554a186c2538547cd537552e4029f704994a9201d41d82015c10ed7f58f9036e8d1527c3760f042409163269d308b0b3706589039c5f1884619c6d4ce languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-replace-supers@npm:7.16.7" + dependencies: + "@babel/helper-environment-visitor": ^7.16.7 + "@babel/helper-member-expression-to-functions": ^7.16.7 + "@babel/helper-optimise-call-expression": ^7.16.7 + "@babel/traverse": ^7.16.7 + "@babel/types": ^7.16.7 + checksum: e5c0b6eb3dad8410a6255f93b580dde9b3c1564646c6ef751de59d5b2a65b5caa80cc9e568155f04bbae895ad0f54305c2e833dbd971a4f641f970c90b3d892b + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-simple-access@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: 8d22c46c5ec2ead0686c4d5a3d1d12b5190c59be676bfe0d9d89df62b437b51d1a3df2ccfb8a77dded2e585176ebf12986accb6d45a18cff229eef3b10344f4b + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.16.0" + dependencies: + "@babel/types": ^7.16.0 + checksum: b9ed2896eb253e6a85f472b0d4098ed80403758ad1a4e34b02b11e8276e3083297526758b1a3e6886e292987266f10622d7dbced3508cc22b296a74903b41cfb + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-split-export-declaration@npm:7.16.7" @@ -134,6 +271,24 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-validator-option@npm:7.16.7" + checksum: c5ccc451911883cc9f12125d47be69434f28094475c1b9d2ada7c3452e6ac98a1ee8ddd364ca9e3f9855fcdee96cdeafa32543ebd9d17fee7a1062c202e80570 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helpers@npm:7.16.7" + dependencies: + "@babel/template": ^7.16.7 + "@babel/traverse": ^7.16.7 + "@babel/types": ^7.16.7 + checksum: 75504c76b66a29b91f954fcc0867dfe275a4cfba5b44df6d64405df74ea72f967fccfa63d62c31c423c5502d113290000c581e0e4858a214f0303d7ecf55c29f + languageName: node + linkType: hard + "@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.16.7": version: 7.16.7 resolution: "@babel/highlight@npm:7.16.7" @@ -145,6 +300,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.16.10": + version: 7.16.10 + resolution: "@babel/parser@npm:7.16.10" + bin: + parser: ./bin/babel-parser.js + checksum: d760606039de8ab8c68e993b7d3ae461754ef51dbf1fbd88d1428bf37d7e13bfb7205105332f0ac0a55d3534c231da527ab7b2db26e7cef6e0f9c8940a3c91a3 + languageName: node + linkType: hard + "@babel/parser@npm:^7.16.7": version: 7.16.7 resolution: "@babel/parser@npm:7.16.7" @@ -154,6 +318,161 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-class-properties@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3977e841e17b45b47be749b9a5b67b9e8b25ff0840f9fdad3f00cbcb35db4f5ff15f074939fe19b01207a29688c432cc2c682351959350834d62920b7881f803 + languageName: node + linkType: hard + +"@babel/plugin-proposal-dynamic-import@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-dynamic-import@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5992012484fb8bda1451369350e475091954ed414dd9ef8654a3c4daa2db0205d4f29c94f5d3dedfbc5a434996375c8304586904337d6af938ac0f27a0033e23 + languageName: node + linkType: hard + +"@babel/plugin-proposal-export-namespace-from@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5016079a5305c1c130fea587b42cdce501574739cfefa5b63469dbc1f32d436df0ff42fabf04089fe8b6a00f4ea7563869e944744b457e186c677995983cb166 + languageName: node + linkType: hard + +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c4cf18e10f900d40eaa471c4adce4805e67bd845f997a4b9d5653eced4e653187b9950843b2bf7eab6c0c3e753aba222b1d38888e3e14e013f87295c5b014f19 + languageName: node + linkType: hard + +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bfafc2701697b5c763dbbb65dd97b56979bfb0922e35be27733699a837aeff22316313ddfdd0fb45129efa3f86617219b77110d05338bc4dca4385d8ce83dd19 + languageName: node + linkType: hard + +"@babel/plugin-proposal-numeric-separator@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-numeric-separator@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8e2fb0b32845908c67f80bc637a0968e28a66727d7ffb22b9c801dc355d88e865dc24aec586b00c922c23833ae5d26301b443b53609ea73d8344733cd48a1eca + languageName: node + linkType: hard + +"@babel/plugin-proposal-optional-chaining@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.16.0 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e4a6c1ac7e6817b92a673ea52ab0b7dc1fb39d29fb0820cd414e10ae2cd132bd186b4238dcca881a29fc38fe9d38ed24fc111ba22ca20086481682d343f4f130 + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-methods@npm:^7.14.5": + version: 7.16.11 + resolution: "@babel/plugin-proposal-private-methods@npm:7.16.11" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.10 + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b333e5aa91c265bb394a57b5f4ae1a34fc8ee73a8d75506b12df258d8b5342107cbd9261f95e606bd3264a5b023db77f1f95be30c2e526683916c57f793f7943 + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-property-in-object@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.16.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.7 + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 666d668f51d8c01aaf0dd87b27a83fc0392884d2c8e9d8e17b3b7011c0d348865dee94b44dc2d7070726e58e3b579728dc2588aaa8140d563f7390743ee90f0a + languageName: node + linkType: hard + +"@babel/plugin-syntax-async-generators@npm:^7.8.4": + version: 7.8.4 + resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7ed1c1d9b9e5b64ef028ea5e755c0be2d4e5e4e3d6cf7df757b9a8c4cfa4193d268176d0f1f7fbecdda6fe722885c7fda681f480f3741d8a2d26854736f05367 + languageName: node + linkType: hard + +"@babel/plugin-syntax-dynamic-import@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-dynamic-import@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ce307af83cf433d4ec42932329fad25fa73138ab39c7436882ea28742e1c0066626d224e0ad2988724c82644e41601cef607b36194f695cb78a1fcdc959637bd + languageName: node + linkType: hard + +"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 85740478be5b0de185228e7814451d74ab8ce0a26fcca7613955262a26e99e8e15e9da58f60c754b84515d4c679b590dbd3f2148f0f58025f4ae706f1c5a5d4a + languageName: node + linkType: hard + +"@babel/plugin-syntax-json-strings@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bf5aea1f3188c9a507e16efe030efb996853ca3cadd6512c51db7233cc58f3ac89ff8c6bdfb01d30843b161cfe7d321e1bf28da82f7ab8d7e6bc5464666f354a + languageName: node + linkType: hard + "@babel/plugin-syntax-jsx@npm:7.14.5": version: 7.14.5 resolution: "@babel/plugin-syntax-jsx@npm:7.14.5" @@ -165,6 +484,160 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cd9b0e53c50e8ddb0afaf0f42e0b221a94e4f59aee32a591364266a31195c48cac5fef288d02c1c935686bda982d2e0f1ed61cceb995fc9f6fb09ef5ebecdd2b + languageName: node + linkType: hard + +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: aff33577037e34e515911255cdbb1fd39efee33658aa00b8a5fd3a4b903585112d037cce1cc9e4632f0487dc554486106b79ccd5ea63a2e00df4363f6d4ff886 + languageName: node + linkType: hard + +"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 87aca4918916020d1fedba54c0e232de408df2644a425d153be368313fdde40d96088feed6c4e5ab72aac89be5d07fef2ddf329a15109c5eb65df006bf2580d1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 01ec5547bd0497f76cc903ff4d6b02abc8c05f301c88d2622b6d834e33a5651aa7c7a3d80d8d57656a4588f7276eba357f6b7e006482f5b564b7a6488de493a1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fddcf581a57f77e80eb6b981b10658421bc321ba5f0a5b754118c6a92a5448f12a0c336f77b8abf734841e102e5126d69110a306eadb03ca3e1547cab31f5cbf + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 910d90e72bc90ea1ce698e89c1027fed8845212d5ab588e35ef91f13b93143845f94e2539d831dc8d8ededc14ec02f04f7bd6a8179edd43a326c784e7ed7f0b9 + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: eef94d53a1453361553c1f98b68d17782861a04a392840341bc91780838dd4e695209c783631cf0de14c635758beafb6a3a65399846ffa4386bff90639347f30 + languageName: node + linkType: hard + +"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b317174783e6e96029b743ccff2a67d63d38756876e7e5d0ba53a322e38d9ca452c13354a57de1ad476b4c066dbae699e0ca157441da611117a47af88985ecda + languageName: node + linkType: hard + +"@babel/plugin-syntax-typescript@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/plugin-syntax-typescript@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 661e636060609ede9a402e22603b01784c21fabb0a637e65f561c8159351fe0130bbc11fdefe31902107885e3332fc34d95eb652ac61d3f61f2d61f5da20609e + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.14.5": + version: 7.16.8 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.16.8" + dependencies: + "@babel/helper-module-transforms": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-simple-access": ^7.16.7 + babel-plugin-dynamic-import-node: ^2.3.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c0ac00f5457e12cac7825b14725b6fc787bef78945181469ff79f07ef0fd7df021cb00fe1d3a9f35fc9bc92ae59e6e3fc9075a70b627dfe10e00d0907892aace + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/plugin-transform-react-jsx@npm:7.16.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.7 + "@babel/helper-module-imports": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-jsx": ^7.16.7 + "@babel/types": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0e82346d7c99b4467946d535a8c626a988e5670f65a15dee8520ce9cf4f0147c99decc1cbb4bd352083eaafd259ee3e4299854cac6304a83666d488edf4e58f6 + languageName: node + linkType: hard + +"@babel/plugin-transform-typescript@npm:^7.16.7": + version: 7.16.8 + resolution: "@babel/plugin-transform-typescript@npm:7.16.8" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/plugin-syntax-typescript": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a76d0afcbd550208cf2e7cdedb4f2d3ca3fa287640a4858a5ee0a28270b784d7d20d5a51b5997dc84514e066a5ebef9e0a0f74ed9fffae09e73984786dd08036 + languageName: node + linkType: hard + +"@babel/preset-typescript@npm:^7.14.5": + version: 7.16.7 + resolution: "@babel/preset-typescript@npm:7.16.7" + dependencies: + "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-validator-option": ^7.16.7 + "@babel/plugin-transform-typescript": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 44e2f3fa302befe0dc50a01b79e5aa8c27a9c7047c46df665beae97201173030646ddf7c83d7d3ed3724fc38151745b11693e7b4502c81c4cd67781ff5677da5 + languageName: node + linkType: hard + "@babel/runtime@npm:7.15.4": version: 7.15.4 resolution: "@babel/runtime@npm:7.15.4" @@ -185,6 +658,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7": + version: 7.16.10 + resolution: "@babel/traverse@npm:7.16.10" + dependencies: + "@babel/code-frame": ^7.16.7 + "@babel/generator": ^7.16.8 + "@babel/helper-environment-visitor": ^7.16.7 + "@babel/helper-function-name": ^7.16.7 + "@babel/helper-hoist-variables": ^7.16.7 + "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/parser": ^7.16.10 + "@babel/types": ^7.16.8 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 58f52314f8a02157cd3004712e703e6b22dff57cee4bc1ab1954c511c6f885fd7763ea68d2d5f006891bc7b77b1f2e9c8c7cb0354f580c8343d5559ed971d087 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.4.5": version: 7.16.7 resolution: "@babel/traverse@npm:7.16.7" @@ -213,6 +704,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.8": + version: 7.16.8 + resolution: "@babel/types@npm:7.16.8" + dependencies: + "@babel/helper-validator-identifier": ^7.16.7 + to-fast-properties: ^2.0.0 + checksum: 4f6a187b2924df70e21d6e6c0822f91b1b936fe060bc92bb477b93bd8a712c88fe41a73f85c0ec53b033353374fe33e773b04ffc340ad36afd8f647dd05c4ee1 + languageName: node + linkType: hard + "@babel/types@npm:^7.16.7, @babel/types@npm:^7.8.3": version: 7.16.7 resolution: "@babel/types@npm:7.16.7" @@ -330,6 +831,19 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^27.2.5, @jest/types@npm:^27.4.2": + version: 27.4.2 + resolution: "@jest/types@npm:27.4.2" + dependencies: + "@types/istanbul-lib-coverage": ^2.0.0 + "@types/istanbul-reports": ^3.0.0 + "@types/node": "*" + "@types/yargs": ^16.0.0 + chalk: ^4.0.0 + checksum: 1191022023e32763063cc1c8b1143fa316fb05db2f9698280a7bdbafcabd989e5fd64f8eb875b8a2e54c53f25dba45ed2eea8ced394d9e484da0fda674cd17a5 + languageName: node + linkType: hard + "@kachkaev/eslint-config-base@npm:^0.4.3": version: 0.4.3 resolution: "@kachkaev/eslint-config-base@npm:0.4.3" @@ -630,6 +1144,54 @@ __metadata: languageName: node linkType: hard +"@playwright/test@npm:^1.18.0": + version: 1.18.0 + resolution: "@playwright/test@npm:1.18.0" + dependencies: + "@babel/code-frame": ^7.14.5 + "@babel/core": ^7.14.8 + "@babel/plugin-proposal-class-properties": ^7.14.5 + "@babel/plugin-proposal-dynamic-import": ^7.14.5 + "@babel/plugin-proposal-export-namespace-from": ^7.14.5 + "@babel/plugin-proposal-logical-assignment-operators": ^7.14.5 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.14.5 + "@babel/plugin-proposal-numeric-separator": ^7.14.5 + "@babel/plugin-proposal-optional-chaining": ^7.14.5 + "@babel/plugin-proposal-private-methods": ^7.14.5 + "@babel/plugin-proposal-private-property-in-object": ^7.14.5 + "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/plugin-syntax-json-strings": ^7.8.3 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + "@babel/plugin-transform-modules-commonjs": ^7.14.5 + "@babel/plugin-transform-react-jsx": ^7.14.5 + "@babel/preset-typescript": ^7.14.5 + babel-plugin-module-resolver: ^4.1.0 + colors: 1.4.0 + commander: ^8.2.0 + debug: ^4.1.1 + expect: =27.2.5 + jest-matcher-utils: =27.2.5 + jpeg-js: ^0.4.2 + json5: ^2.2.0 + mime: ^2.4.6 + minimatch: ^3.0.3 + ms: ^2.1.2 + open: ^8.3.0 + pirates: ^4.0.1 + pixelmatch: ^5.2.1 + playwright-core: =1.18.0 + pngjs: ^5.0.0 + rimraf: ^3.0.2 + source-map-support: ^0.4.18 + stack-utils: ^2.0.3 + yazl: ^2.5.1 + bin: + playwright: cli.js + checksum: 576153bdbd4105ef376a4e3423ead165b81c5ed3d9bc881ddd4cffec356eb2e009f56504b7da9674df53547df076641e7c198f2fef5c087199a8c13b986eb302 + languageName: node + linkType: hard + "@prisma/client@npm:^3.8.1": version: 3.8.1 resolution: "@prisma/client@npm:3.8.1" @@ -743,6 +1305,31 @@ __metadata: languageName: node linkType: hard +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0": + version: 2.0.4 + resolution: "@types/istanbul-lib-coverage@npm:2.0.4" + checksum: a25d7589ee65c94d31464c16b72a9dc81dfa0bea9d3e105ae03882d616e2a0712a9c101a599ec482d297c3591e16336962878cb3eb1a0a62d5b76d277a890ce7 + languageName: node + linkType: hard + +"@types/istanbul-lib-report@npm:*": + version: 3.0.0 + resolution: "@types/istanbul-lib-report@npm:3.0.0" + dependencies: + "@types/istanbul-lib-coverage": "*" + checksum: 656398b62dc288e1b5226f8880af98087233cdb90100655c989a09f3052b5775bf98ba58a16c5ae642fb66c61aba402e07a9f2bff1d1569e3b306026c59f3f36 + languageName: node + linkType: hard + +"@types/istanbul-reports@npm:^3.0.0": + version: 3.0.1 + resolution: "@types/istanbul-reports@npm:3.0.1" + dependencies: + "@types/istanbul-lib-report": "*" + checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.9": version: 7.0.9 resolution: "@types/json-schema@npm:7.0.9" @@ -846,14 +1433,46 @@ __metadata: languageName: node linkType: hard -"@types/styled-components@npm:^5.1.19": - version: 5.1.19 - resolution: "@types/styled-components@npm:5.1.19" +"@types/stack-utils@npm:^2.0.0": + version: 2.0.1 + resolution: "@types/stack-utils@npm:2.0.1" + checksum: 205fdbe3326b7046d7eaf5e494d8084f2659086a266f3f9cf00bccc549c8e36e407f88168ad4383c8b07099957ad669f75f2532ed4bc70be2b037330f7bae019 + languageName: node + linkType: hard + +"@types/styled-components@npm:^5.1.19": + version: 5.1.19 + resolution: "@types/styled-components@npm:5.1.19" + dependencies: + "@types/hoist-non-react-statics": "*" + "@types/react": "*" + csstype: ^3.0.2 + checksum: ccd2baa6c980ad176650889f2fb286edbb57f5e2b74a01d80d349f751fa7fbdb4f21ed34a893a4c417cc5731c5f0174dba5d180c3a11954996d29c2818ecab13 + languageName: node + linkType: hard + +"@types/yargs-parser@npm:*": + version: 20.2.1 + resolution: "@types/yargs-parser@npm:20.2.1" + checksum: 1d039e64494a7a61ddd278349a3dc60b19f99ff0517425696e796f794e4252452b9d62178e69755ad03f439f9dc0c8c3d7b3a1201b3a24e134bac1a09fa11eaa + languageName: node + linkType: hard + +"@types/yargs@npm:^16.0.0": + version: 16.0.4 + resolution: "@types/yargs@npm:16.0.4" + dependencies: + "@types/yargs-parser": "*" + checksum: caa21d2c957592fe2184a8368c8cbe5a82a6c2e2f2893722e489f842dc5963293d2f3120bc06fe3933d60a3a0d1e2eb269649fd6b1947fe1820f8841ba611dd9 + languageName: node + linkType: hard + +"@types/yauzl@npm:^2.9.1": + version: 2.9.2 + resolution: "@types/yauzl@npm:2.9.2" dependencies: - "@types/hoist-non-react-statics": "*" - "@types/react": "*" - csstype: ^3.0.2 - checksum: ccd2baa6c980ad176650889f2fb286edbb57f5e2b74a01d80d349f751fa7fbdb4f21ed34a893a4c417cc5731c5f0174dba5d180c3a11954996d29c2818ecab13 + "@types/node": "*" + checksum: dfb49abe82605615712fc694eaa4f7068fe30aa03f38c085e2c2e74408beaad30471d36da9654a811482ece2ea4405575fd99b19c0aa327ed2a9736b554bbf43 languageName: node linkType: hard @@ -1123,6 +1742,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 + languageName: node + linkType: hard + "ansi-styles@npm:^6.0.0": version: 6.1.0 resolution: "ansi-styles@npm:6.1.0" @@ -1275,6 +1901,28 @@ __metadata: languageName: node linkType: hard +"babel-plugin-dynamic-import-node@npm:^2.3.3": + version: 2.3.3 + resolution: "babel-plugin-dynamic-import-node@npm:2.3.3" + dependencies: + object.assign: ^4.1.0 + checksum: c9d24415bcc608d0db7d4c8540d8002ac2f94e2573d2eadced137a29d9eab7e25d2cbb4bc6b9db65cf6ee7430f7dd011d19c911a9a778f0533b4a05ce8292c9b + languageName: node + linkType: hard + +"babel-plugin-module-resolver@npm:^4.1.0": + version: 4.1.0 + resolution: "babel-plugin-module-resolver@npm:4.1.0" + dependencies: + find-babel-config: ^1.2.0 + glob: ^7.1.6 + pkg-up: ^3.1.0 + reselect: ^4.0.0 + resolve: ^1.13.1 + checksum: 3907fba21ca3c66a081e01fbd16bb09c84781749db16aa57805becc376bb5ee8dc373d4b209613e1453d30ea6c836d13073e9e7b6d239ff1806dd1763a9ab18f + languageName: node + linkType: hard + "babel-plugin-styled-components@npm:>= 1.12.0": version: 2.0.2 resolution: "babel-plugin-styled-components@npm:2.0.2" @@ -1470,6 +2118,28 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.17.5": + version: 4.19.1 + resolution: "browserslist@npm:4.19.1" + dependencies: + caniuse-lite: ^1.0.30001286 + electron-to-chromium: ^1.4.17 + escalade: ^3.1.1 + node-releases: ^2.0.1 + picocolors: ^1.0.0 + bin: + browserslist: cli.js + checksum: c0777fd483691638fd6801e16c9d809e1d65f6d2b06db2e806654be51045cbab1452a89841a2c5caea2cbe19d621b4f1d391cffbb24512aa33280039ab345875 + languageName: node + linkType: hard + +"buffer-crc32@npm:~0.2.3": + version: 0.2.13 + resolution: "buffer-crc32@npm:0.2.13" + checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -1604,6 +2274,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001286": + version: 1.0.30001301 + resolution: "caniuse-lite@npm:1.0.30001301" + checksum: 0e359f2c682bed35521902ce7fc91136c485de4c1e1e33272d6fbb7cdf77178df7649960a9b69ce9da12022aadd7a0037b3c9b3594c41146d58216654dc9a236 + languageName: node + linkType: hard + "chalk@npm:2.4.2, chalk@npm:^2.0.0, chalk@npm:^2.4.1": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -1872,6 +2549,13 @@ __metadata: languageName: node linkType: hard +"colors@npm:1.4.0": + version: 1.4.0 + resolution: "colors@npm:1.4.0" + checksum: 98aa2c2418ad87dedf25d781be69dc5fc5908e279d9d30c34d8b702e586a0474605b3a189511482b9d5ed0d20c867515d22749537f7bc546256c6014f3ebdcec + languageName: node + linkType: hard + "commander@npm:^2.20.3": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -1879,7 +2563,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^8.3.0, commander@npm:~8.3.0": +"commander@npm:^8.2.0, commander@npm:^8.3.0, commander@npm:~8.3.0": version: 8.3.0 resolution: "commander@npm:8.3.0" checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 @@ -1930,6 +2614,15 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^1.7.0": + version: 1.8.0 + resolution: "convert-source-map@npm:1.8.0" + dependencies: + safe-buffer: ~5.1.1 + checksum: 985d974a2d33e1a2543ada51c93e1ba2f73eaed608dc39f229afc78f71dcc4c8b7d7c684aa647e3c6a3a204027444d69e53e169ce94e8d1fa8d7dee80c9c8fed + languageName: node + linkType: hard + "cookie@npm:^0.4.1": version: 0.4.1 resolution: "cookie@npm:0.4.1" @@ -2262,6 +2955,13 @@ __metadata: languageName: node linkType: hard +"diff-sequences@npm:^27.4.0": + version: 27.4.0 + resolution: "diff-sequences@npm:27.4.0" + checksum: 66d04033e8632eeacdd029b4ecaf87d233d475e4b0cd1cee035eda99e70e1a7f803507d72f2677990ef526f28a2f6e5709af8d94dcdc0682b8884a3a646190a1 + languageName: node + linkType: hard + "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -2321,6 +3021,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.4.17": + version: 1.4.49 + resolution: "electron-to-chromium@npm:1.4.49" + checksum: 0527cc195c34b609f4ecad25584d238494d758c730d0aafdbd408a0ccce42cc6f05c1c0b76cc055a0a173700e29b1f3352d91b5cbeb91d063321690913afae84 + languageName: node + linkType: hard + "elliptic@npm:^6.5.3": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -2474,6 +3181,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 9f8a2d5743677c16e85c810e3024d54f0c8dea6424fad3c79ef6666e81dd0846f7437f5e729dfcdac8981bc9e5294c39b4580814d114076b8d36318f46ae4395 + languageName: node + linkType: hard + "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -2831,6 +3545,37 @@ __metadata: languageName: node linkType: hard +"expect@npm:=27.2.5": + version: 27.2.5 + resolution: "expect@npm:27.2.5" + dependencies: + "@jest/types": ^27.2.5 + ansi-styles: ^5.0.0 + jest-get-type: ^27.0.6 + jest-matcher-utils: ^27.2.5 + jest-message-util: ^27.2.5 + jest-regex-util: ^27.0.6 + checksum: c9be6ec30d19f69c6b838c379e102c156b3ce231e0e3bfc7928eb7a239e5d2a8ed3a43ded4856ad6b3f2f83944561455ad3cf4dfc5322e7d962f2eddc67941c7 + languageName: node + linkType: hard + +"extract-zip@npm:^2.0.1": + version: 2.0.1 + resolution: "extract-zip@npm:2.0.1" + dependencies: + "@types/yauzl": ^2.9.1 + debug: ^4.1.1 + get-stream: ^5.1.0 + yauzl: ^2.10.0 + dependenciesMeta: + "@types/yauzl": + optional: true + bin: + extract-zip: cli.js + checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -2881,6 +3626,15 @@ __metadata: languageName: node linkType: hard +"fd-slicer@npm:~1.1.0": + version: 1.1.0 + resolution: "fd-slicer@npm:1.1.0" + dependencies: + pend: ~1.2.0 + checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -2899,6 +3653,16 @@ __metadata: languageName: node linkType: hard +"find-babel-config@npm:^1.2.0": + version: 1.2.0 + resolution: "find-babel-config@npm:1.2.0" + dependencies: + json5: ^0.5.1 + path-exists: ^3.0.0 + checksum: 0a1785d3da9f38637885d9d65f183aaa072f51a834f733035e9694e4d0f6983ae8c8e75cd4e08b92af6f595b3b490ee813a1c5a9b14740685aa836fa1e878583 + languageName: node + linkType: hard + "find-cache-dir@npm:3.3.1": version: 3.3.1 resolution: "find-cache-dir@npm:3.3.1" @@ -2919,6 +3683,15 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^3.0.0": + version: 3.0.0 + resolution: "find-up@npm:3.0.0" + dependencies: + locate-path: ^3.0.0 + checksum: 38eba3fe7a66e4bc7f0f5a1366dc25508b7cfc349f852640e3678d26ad9a6d7e2c43eff0a472287de4a9753ef58f066a0ea892a256fa3636ad51b3fe1e17fae9 + languageName: node + linkType: hard + "find-up@npm:^4.0.0, find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" @@ -3067,6 +3840,13 @@ __metadata: languageName: node linkType: hard +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: a7437e58c6be12aa6c90f7730eac7fa9833dc78872b4ad2963d2031b00a3367a93f98aec75f9aaac7220848e4026d67a8655e870b24f20a543d103c0d65952ec + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": version: 1.1.1 resolution: "get-intrinsic@npm:1.1.1" @@ -3182,7 +3962,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:~7.2.0": +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:~7.2.0": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -3275,7 +4055,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.9 resolution: "graceful-fs@npm:4.2.9" checksum: 68ea4e07ff2c041ada184f9278b830375f8e0b75154e3f080af6b70f66172fabb4108d19b3863a96b53fc068a310b9b6493d86d1291acc5f3861eb4b79d26ad6 @@ -4019,6 +4799,73 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^27.2.5, jest-diff@npm:^27.4.6": + version: 27.4.6 + resolution: "jest-diff@npm:27.4.6" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^27.4.0 + jest-get-type: ^27.4.0 + pretty-format: ^27.4.6 + checksum: cf6b7e80e3c64a7c71ab209c0325bbda175991aed985ecee7652df9d6540e4959089038e208c04ab05391c9ddf07adc72f0c8c26cc4cee6fa17f76f500e2bf43 + languageName: node + linkType: hard + +"jest-get-type@npm:^27.0.6, jest-get-type@npm:^27.4.0": + version: 27.4.0 + resolution: "jest-get-type@npm:27.4.0" + checksum: bb9b70e420009fdaed3026d5bccd01569f92c7500f9f544d862796d4f4efa93ced5484864b2f272c7748bfb5bfd3268d48868b169c51ab45fe5b45b9519b6e46 + languageName: node + linkType: hard + +"jest-matcher-utils@npm:=27.2.5": + version: 27.2.5 + resolution: "jest-matcher-utils@npm:27.2.5" + dependencies: + chalk: ^4.0.0 + jest-diff: ^27.2.5 + jest-get-type: ^27.0.6 + pretty-format: ^27.2.5 + checksum: 92f285c8e2a50f2b6761a1d81db98858416b6ccb6559c9ce954ef9cad6b76729ac18b8c1e98e2e81e1a55fca4dc9d8571d5dfbc2161583ed5716119e35b2a089 + languageName: node + linkType: hard + +"jest-matcher-utils@npm:^27.2.5": + version: 27.4.6 + resolution: "jest-matcher-utils@npm:27.4.6" + dependencies: + chalk: ^4.0.0 + jest-diff: ^27.4.6 + jest-get-type: ^27.4.0 + pretty-format: ^27.4.6 + checksum: 445a8cc9eaa7cb08653a10cfc4f109eca76a97d1b1d3a01067bd77efa9cb3a554b74c7402a4c9d5083b21e11218e1515ef538faa47fa47c282072b4825f6b307 + languageName: node + linkType: hard + +"jest-message-util@npm:^27.2.5": + version: 27.4.6 + resolution: "jest-message-util@npm:27.4.6" + dependencies: + "@babel/code-frame": ^7.12.13 + "@jest/types": ^27.4.2 + "@types/stack-utils": ^2.0.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.4 + micromatch: ^4.0.4 + pretty-format: ^27.4.6 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: 1fdd542d091dbf7aa63a484feead97a921e3c4d6db3784fe2e6d83e9110ac06de5691fdc043da991ca1d0ce5d179ea8266c8d93b388f4bba7d80a267fdd946df + languageName: node + linkType: hard + +"jest-regex-util@npm:^27.0.6": + version: 27.4.0 + resolution: "jest-regex-util@npm:27.4.0" + checksum: 222e4aacec601fd2cfdfee74adb8d324fef672f77577a7c2220893ec1a62031a2640388fce8d0bd8be2e4537da1ab40aa74dba60ac531a23b2643b15c65014ac + languageName: node + linkType: hard + "jest-worker@npm:27.0.0-next.5": version: 27.0.0-next.5 resolution: "jest-worker@npm:27.0.0-next.5" @@ -4052,6 +4899,13 @@ __metadata: languageName: node linkType: hard +"jpeg-js@npm:^0.4.2": + version: 0.4.3 + resolution: "jpeg-js@npm:0.4.3" + checksum: 9e5bacc9135efa7da340b62e81fa56fab0c8516ef617228758132af5b7d31b516cc6e1500cdffb82d3161629be341be980099f2b37eb76b81e26db6e3e848c77 + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -4114,6 +4968,15 @@ __metadata: languageName: node linkType: hard +"json5@npm:^0.5.1": + version: 0.5.1 + resolution: "json5@npm:0.5.1" + bin: + json5: lib/cli.js + checksum: 9b85bf06955b23eaa4b7328aa8892e3887e81ca731dd27af04a5f5f1458fbc5e1de57a24442e3272f8a888dd1abe1cb68eb693324035f6b3aeba4fcab7667d62 + languageName: node + linkType: hard + "json5@npm:^1.0.1": version: 1.0.1 resolution: "json5@npm:1.0.1" @@ -4125,6 +4988,17 @@ __metadata: languageName: node linkType: hard +"json5@npm:^2.1.2, json5@npm:^2.2.0": + version: 2.2.0 + resolution: "json5@npm:2.2.0" + dependencies: + minimist: ^1.2.5 + bin: + json5: lib/cli.js + checksum: e88fc5274bb58fc99547baa777886b069d2dd96d9cfc4490b305fd16d711dabd5979e35a4f90873cefbeb552e216b041a304fe56702bedba76e19bc7845f208d + languageName: node + linkType: hard + "jsonc-parser@npm:^3.0.0, jsonc-parser@npm:~3.0.0": version: 3.0.0 resolution: "jsonc-parser@npm:3.0.0" @@ -4298,6 +5172,16 @@ __metadata: languageName: node linkType: hard +"locate-path@npm:^3.0.0": + version: 3.0.0 + resolution: "locate-path@npm:3.0.0" + dependencies: + p-locate: ^3.0.0 + path-exists: ^3.0.0 + checksum: 53db3996672f21f8b0bf2a2c645ae2c13ffdae1eeecfcd399a583bce8516c0b88dcb4222ca6efbbbeb6949df7e46860895be2c02e8d3219abd373ace3bfb4e11 + languageName: node + linkType: hard + "locate-path@npm:^5.0.0": version: 5.0.0 resolution: "locate-path@npm:5.0.0" @@ -4569,6 +5453,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^2.4.6": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 1497ba7b9f6960694268a557eae24b743fd2923da46ec392b042469f4b901721ba0adcf8b0d3c2677839d0e243b209d76e5edcbd09cfdeffa2dfb6bb4df4b862 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -4618,7 +5511,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:~3.0.4": +"minimatch@npm:^3.0.3, minimatch@npm:^3.0.4, minimatch@npm:~3.0.4": version: 3.0.4 resolution: "minimatch@npm:3.0.4" dependencies: @@ -4751,7 +5644,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.2": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -4992,6 +5885,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.1": + version: 2.0.1 + resolution: "node-releases@npm:2.0.1" + checksum: b20dd8d4bced11f75060f0387e05e76b9dc4a0451f7bb3516eade6f50499ea7768ba95d8a60d520c193402df1e58cb3fe301510cc1c1ad68949c3d57b5149866 + languageName: node + linkType: hard + "node-stream-zip@npm:^1.15.0": version: 1.15.0 resolution: "node-stream-zip@npm:1.15.0" @@ -5135,7 +6035,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.2": +"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2": version: 4.1.2 resolution: "object.assign@npm:4.1.2" dependencies: @@ -5233,7 +6133,7 @@ __metadata: languageName: node linkType: hard -"open@npm:^8.0.5": +"open@npm:^8.0.5, open@npm:^8.3.0": version: 8.4.0 resolution: "open@npm:8.4.0" dependencies: @@ -5312,7 +6212,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^2.2.0": +"p-limit@npm:^2.0.0, p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" dependencies: @@ -5330,6 +6230,15 @@ __metadata: languageName: node linkType: hard +"p-locate@npm:^3.0.0": + version: 3.0.0 + resolution: "p-locate@npm:3.0.0" + dependencies: + p-limit: ^2.0.0 + checksum: 83991734a9854a05fe9dbb29f707ea8a0599391f52daac32b86f08e21415e857ffa60f0e120bfe7ce0cc4faf9274a50239c7895fc0d0579d08411e513b83a4ae + languageName: node + linkType: hard + "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -5505,6 +6414,20 @@ __metadata: languageName: node linkType: hard +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d + languageName: node + linkType: hard + +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -5528,6 +6451,24 @@ __metadata: languageName: node linkType: hard +"pirates@npm:^4.0.1": + version: 4.0.4 + resolution: "pirates@npm:4.0.4" + checksum: 6b7187d526fd025a2b91e8fd289c78d88c4adc3ea947b9facbe9cb300a896b0ec00f3e77b36a043001695312a8debbf714453495283bd8a4eaad3bc0c38df425 + languageName: node + linkType: hard + +"pixelmatch@npm:^5.2.1": + version: 5.2.1 + resolution: "pixelmatch@npm:5.2.1" + dependencies: + pngjs: ^4.0.1 + bin: + pixelmatch: bin/pixelmatch + checksum: 0ec7a87168e51b80812d1c39fe1a278e2266dc1e9c426418c2a9d7f0c6465de3c03c51dbf7e6b97c5ba72a043ec3fb576571cdde1f88b12ef0851bf9bfd16da0 + languageName: node + linkType: hard + "pkg-dir@npm:^4.1.0": version: 4.2.0 resolution: "pkg-dir@npm:4.2.0" @@ -5537,6 +6478,15 @@ __metadata: languageName: node linkType: hard +"pkg-up@npm:^3.1.0": + version: 3.1.0 + resolution: "pkg-up@npm:3.1.0" + dependencies: + find-up: ^3.0.0 + checksum: 5bac346b7c7c903613c057ae3ab722f320716199d753f4a7d053d38f2b5955460f3e6ab73b4762c62fd3e947f58e04f1343e92089e7bb6091c90877406fcd8c8 + languageName: node + linkType: hard + "platform@npm:1.3.6": version: 1.3.6 resolution: "platform@npm:1.3.6" @@ -5544,6 +6494,32 @@ __metadata: languageName: node linkType: hard +"playwright-core@npm:=1.18.0": + version: 1.18.0 + resolution: "playwright-core@npm:1.18.0" + dependencies: + commander: ^8.2.0 + debug: ^4.1.1 + extract-zip: ^2.0.1 + https-proxy-agent: ^5.0.0 + jpeg-js: ^0.4.2 + mime: ^2.4.6 + pngjs: ^5.0.0 + progress: ^2.0.3 + proper-lockfile: ^4.1.1 + proxy-from-env: ^1.1.0 + rimraf: ^3.0.2 + socks-proxy-agent: ^6.1.0 + stack-utils: ^2.0.3 + ws: ^7.4.6 + yauzl: ^2.10.0 + yazl: ^2.5.1 + bin: + playwright: cli.js + checksum: 6e76dc5ab993d30a150f46b98ad8921801b2bb6ded517813814db1c074b3bb76975453ba826b0f085600d0a586816e04f9b33ef6e88a624e443bcce2effbef6f + languageName: node + linkType: hard + "pluralize@npm:^8.0.0": version: 8.0.0 resolution: "pluralize@npm:8.0.0" @@ -5551,6 +6527,20 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^4.0.1": + version: 4.0.1 + resolution: "pngjs@npm:4.0.1" + checksum: 9497e08a6c2d850630ba7c8d3738fd36c9db1af7ee8b8c2d4b664e450807a280936dfa1489deb60e6943b968bedd58c9aa93def25a765579d745ea44467fc47f + languageName: node + linkType: hard + +"pngjs@npm:^5.0.0": + version: 5.0.0 + resolution: "pngjs@npm:5.0.0" + checksum: 04e912cc45fb9601564e2284efaf0c5d20d131d9b596244f8a6789fc6cdb6b18d2975a6bbf7a001858d7e159d5c5c5dd7b11592e97629b7137f7f5cef05904c8 + languageName: node + linkType: hard + "postcss-value-parser@npm:^4.0.2": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -5637,6 +6627,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^27.2.5, pretty-format@npm:^27.4.6": + version: 27.4.6 + resolution: "pretty-format@npm:27.4.6" + dependencies: + ansi-regex: ^5.0.1 + ansi-styles: ^5.0.0 + react-is: ^17.0.1 + checksum: 5eda32e4e47ddd1a9e8fe9ebef519b217ba403eb8bcb804ba551dfb37f87e674472013fcf78480ab535844fdddcc706fac94511eba349bfb94a138a02d1a7a59 + languageName: node + linkType: hard + "prisma@npm:^3.8.1": version: 3.8.1 resolution: "prisma@npm:3.8.1" @@ -5663,7 +6664,7 @@ __metadata: languageName: node linkType: hard -"progress@npm:^2.0.0": +"progress@npm:^2.0.0, progress@npm:^2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 @@ -5698,6 +6699,24 @@ __metadata: languageName: node linkType: hard +"proper-lockfile@npm:^4.1.1": + version: 4.1.2 + resolution: "proper-lockfile@npm:4.1.2" + dependencies: + graceful-fs: ^4.2.4 + retry: ^0.12.0 + signal-exit: ^3.0.2 + checksum: 00078ee6a61c216a56a6140c7d2a98c6c733b3678503002dc073ab8beca5d50ca271de4c85fca13b9b8ee2ff546c36674d1850509b84a04a5d0363bcb8638939 + languageName: node + linkType: hard + +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 + languageName: node + linkType: hard + "public-encrypt@npm:^4.0.0": version: 4.0.3 resolution: "public-encrypt@npm:4.0.3" @@ -5840,7 +6859,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:17.0.2": +"react-is@npm:17.0.2, react-is@npm:^17.0.1": version: 17.0.2 resolution: "react-is@npm:17.0.2" checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8 @@ -6022,6 +7041,13 @@ __metadata: languageName: node linkType: hard +"reselect@npm:^4.0.0": + version: 4.1.5 + resolution: "reselect@npm:4.1.5" + checksum: 54c13c1e795b2ea70cba8384138aebe78adda00cbea303cc94b64da0a70d74c896cc9a03115ae38b8bff990e7a60dcd6452ab68cbec01b0b38c1afda70714cf0 + languageName: node + linkType: hard + "resolve-alpn@npm:^1.0.0": version: 1.2.1 resolution: "resolve-alpn@npm:1.2.1" @@ -6049,6 +7075,19 @@ __metadata: languageName: node linkType: hard +"resolve@npm:^1.13.1": + version: 1.21.1 + resolution: "resolve@npm:1.21.1" + dependencies: + is-core-module: ^2.8.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 4703fbd6ddaebf7a7f7834ca29f5cfa2c64ef7988ac34bcd26148f0d017783d3a475936ffca02add1b9696e0d1db2a75210052c4b08e748e144e2886e575617c + languageName: node + linkType: hard + "resolve@npm:^2.0.0-next.3": version: 2.0.0-next.3 resolution: "resolve@npm:2.0.0-next.3" @@ -6072,6 +7111,19 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@^1.13.1#~builtin": + version: 1.21.1 + resolution: "resolve@patch:resolve@npm%3A1.21.1#~builtin::version=1.21.1&hash=07638b" + dependencies: + is-core-module: ^2.8.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 112e944aa5c73dbeeefb1a1d8f9c70886a063b49f7f5bc4a2d3af1dc68967281da9ae388435634c8bf4b5a239026a4b5e25c5e9eca07c1190bc23c3dc0446dc5 + languageName: node + linkType: hard + "resolve@patch:resolve@^2.0.0-next.3#~builtin": version: 2.0.0-next.3 resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin::version=2.0.0-next.3&hash=07638b" @@ -6152,6 +7204,7 @@ __metadata: "@kachkaev/markdownlint-config": ^0.3.0 "@netlify/plugin-nextjs": ^4.1.1 "@next/eslint-plugin-next": ^12.0.7 + "@playwright/test": ^1.18.0 "@prisma/client": ^3.8.1 "@types/node": ^16.11.19 "@types/react": ^17.0.38 @@ -6483,7 +7536,7 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^6.0.0": +"socks-proxy-agent@npm:^6.0.0, socks-proxy-agent@npm:^6.1.0": version: 6.1.1 resolution: "socks-proxy-agent@npm:6.1.1" dependencies: @@ -6527,6 +7580,15 @@ __metadata: languageName: node linkType: hard +"source-map-support@npm:^0.4.18": + version: 0.4.18 + resolution: "source-map-support@npm:0.4.18" + dependencies: + source-map: ^0.5.6 + checksum: 669aa7e992fec586fac0ba9a8dea8ce81b7328f92806335f018ffac5709afb2920e3870b4e56c68164282607229f04b8bbcf5d0e5c845eb1b5119b092e7585c0 + languageName: node + linkType: hard + "source-map-support@npm:^0.5.17": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -6553,7 +7615,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.0": +"source-map@npm:^0.5.0, source-map@npm:^0.5.6": version: 0.5.7 resolution: "source-map@npm:0.5.7" checksum: 5dc2043b93d2f194142c7f38f74a24670cd7a0063acdaf4bf01d2964b402257ae843c2a8fa822ad5b71013b5fcafa55af7421383da919752f22ff488bc553f4d @@ -6610,6 +7672,15 @@ __metadata: languageName: node linkType: hard +"stack-utils@npm:^2.0.3": + version: 2.0.5 + resolution: "stack-utils@npm:2.0.5" + dependencies: + escape-string-regexp: ^2.0.0 + checksum: 76b69da0f5b48a34a0f93c98ee2a96544d2c4ca2557f7eef5ddb961d3bdc33870b46f498a84a7c4f4ffb781df639840e7ebf6639164ed4da5e1aeb659615b9c7 + languageName: node + linkType: hard + "stacktrace-parser@npm:0.1.10": version: 0.1.10 resolution: "stacktrace-parser@npm:0.1.10" @@ -7570,6 +8641,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^7.4.6": + version: 7.5.6 + resolution: "ws@npm:7.5.6" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 0c2ffc9a539dd61dd2b00ff6cc5c98a3371e2521011fe23da4b3578bb7ac26cbdf7ca8a68e8e08023c122ae247013216dde2a20c908de415a6bcc87bdef68c87 + languageName: node + linkType: hard + "ws@npm:^8.2.1": version: 8.4.0 resolution: "ws@npm:8.4.0" @@ -7618,6 +8704,25 @@ __metadata: languageName: node linkType: hard +"yauzl@npm:^2.10.0": + version: 2.10.0 + resolution: "yauzl@npm:2.10.0" + dependencies: + buffer-crc32: ~0.2.3 + fd-slicer: ~1.1.0 + checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b + languageName: node + linkType: hard + +"yazl@npm:^2.5.1": + version: 2.5.1 + resolution: "yazl@npm:2.5.1" + dependencies: + buffer-crc32: ~0.2.3 + checksum: daec5154b5485d8621bfea359e905ddca0b2f068430a4aa0a802bf5d67391157a383e0c2767acccbf5964264851da643bc740155a9458e2d8dce55b94c1cc2ed + languageName: node + linkType: hard + "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1" From 27a014915bff240042b5d49fbc4895960b133fa2 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 06:57:05 -0500 Subject: [PATCH 02/15] Update readme --- README.md | 8 ++++++++ package.json | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc25360e..44a35a4e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,14 @@ yarn dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +## Test + +Run end-2-end tests with [Playwright](https://playwright.dev/). + +```sh +yarn test +``` + ## Environment variables All secrets should never be committed to the git repo, but saved in the environment variables. diff --git a/package.json b/package.json index fed926da..e86718a1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "lint:tsc": "tsc --project .", "lint:yarn-dedupe": "yarn dedupe --check", "start": "next start", - "postinstall": "husky install" + "postinstall": "husky install", + "test": "yarn playwright test --project=chromium" }, "lint-staged": { "**/*": [ From 92e338eea2defc779a311538e2585666aa769b10 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 08:19:05 -0500 Subject: [PATCH 03/15] Add login and comment tests --- tests/.gitignore | 1 + tests/comments.test.ts | 30 ++++++++++++++++++++++++++++ tests/example.spec.ts | 7 ------- tests/login.test.ts | 45 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 tests/.gitignore create mode 100644 tests/comments.test.ts delete mode 100644 tests/example.spec.ts create mode 100644 tests/login.test.ts diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..b7887cb1 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +state.json \ No newline at end of file diff --git a/tests/comments.test.ts b/tests/comments.test.ts new file mode 100644 index 00000000..3dbbeb2a --- /dev/null +++ b/tests/comments.test.ts @@ -0,0 +1,30 @@ +import { chromium, expect, test } from "@playwright/test"; + +test("add comment", async ({}) => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + // Create a new context with the saved storage state. + const context = await browser.newContext({ + storageState: "tests/state.json", + }); + const page = await context.newPage(); + + // Go to http://localhost:3000/ + await page.goto("http://localhost:3000/"); + // Click text=Comments + await Promise.all([ + page.waitForNavigation(/* { url: 'http://localhost:3000/iframes/comments/1' } */), + page.click("text=Comments"), + ]); + // Click textarea + await page.click("textarea"); + // Fill textarea + await page.fill("textarea", "Hi, this is my comment\n"); + // Press Enter + await page.press("textarea", "Enter"); + // Click text=Отправить + await page.click("text=Отправить"); + + await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + await expect(page.locator("text=Hi, this is my comment")).toBeDefined(); +}); diff --git a/tests/example.spec.ts b/tests/example.spec.ts deleted file mode 100644 index 069c588f..00000000 --- a/tests/example.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { expect, test } from "@playwright/test"; - -test("basic test", async ({ page }) => { - await page.goto("https://playwright.dev/"); - await page.locator("text=Get started").click(); - await expect(page).toHaveTitle(/Getting started/); -}); diff --git a/tests/login.test.ts b/tests/login.test.ts new file mode 100644 index 00000000..049b9cae --- /dev/null +++ b/tests/login.test.ts @@ -0,0 +1,45 @@ +import { chromium, expect, test } from "@playwright/test"; + +test("login via auth0 using google account", async ({}) => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + }); + const page = await context.newPage(); + + // Go to http://localhost:3000/ + await page.goto("http://localhost:3000/"); + // Click text=Login + await Promise.all([ + page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SB1OGdCaDdaSUMtQlU0X2NlQWcyTUdrZk5ZU2tXeHdMMKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGduLW8xY2pKTFFoQkpuS2dXaEhRRGh6UjAwMmxLTk9mo2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), + page.click("text=Login"), + ]); + // Click button:has-text("Continue with Google") + await Promise.all([ + page.waitForNavigation(/* { url: 'https://accounts.google.com/o/oauth2/auth/identifier?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow' } */), + page.click('button:has-text("Continue with Google")'), + ]); + // Fill [aria-label="Телефон\ или\ адрес\ эл\.\ почты"] + await page.fill( + '[aria-label="Телефон\\ или\\ адрес\\ эл\\.\\ почты"]', + "dtp.stat.test@gmail.com", + ); + // Click button:has-text("Далее") + await Promise.all([ + page.waitForNavigation(/* { url: 'https://accounts.google.com/signin/v2/challenge/pwd?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow&cid=1&navigationDirection=forward&TL=AM3QAYZq7nx8YKprItrlvoTViq5YZC2HEuc1NeRa2BVV0ZsxYWQqJ4cxbB74WWE8' } */), + page.click('button:has-text("Далее")'), + ]); + // Fill [aria-label="Введите\ пароль"] + await page.fill('[aria-label="Введите\\ пароль"]', "*geB7^QqUYvt2uTuyd"); + // Click button:has-text("Далее") + await Promise.all([ + page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), + page.click('button:has-text("Далее")'), + ]); + + await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + + // Save storage state into the file. + await context.storageState({ path: "tests/state.json" }); +}); From 582f691e4f29263bee16f4a9339ee99c13774b12 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 08:29:36 -0500 Subject: [PATCH 04/15] Setup auth --- playwright.config.ts | 3 +++ tests/global-setup.ts | 47 +++++++++++++++++++++++++++++++++++++++++++ tests/login.test.ts | 40 ++---------------------------------- 3 files changed, 52 insertions(+), 38 deletions(-) create mode 100644 tests/global-setup.ts diff --git a/playwright.config.ts b/playwright.config.ts index 77bbc1dc..15eb92f8 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -6,6 +6,9 @@ import { devices, PlaywrightTestConfig } from "@playwright/test"; const config: PlaywrightTestConfig = { testDir: "./tests", + /* Reuse auth state */ + globalSetup: require.resolve("./tests/global-setup"), + /* Maximum time one test can run for. */ timeout: 30 * 1000, diff --git a/tests/global-setup.ts b/tests/global-setup.ts new file mode 100644 index 00000000..577d1083 --- /dev/null +++ b/tests/global-setup.ts @@ -0,0 +1,47 @@ +import { chromium, expect, FullConfig } from "@playwright/test"; + +async function globalSetup(config: FullConfig) { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + }); + const page = await context.newPage(); + + // Go to http://localhost:3000/ + await page.goto("http://localhost:3000/"); + // Click text=Login + await Promise.all([ + page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SB1OGdCaDdaSUMtQlU0X2NlQWcyTUdrZk5ZU2tXeHdMMKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGduLW8xY2pKTFFoQkpuS2dXaEhRRGh6UjAwMmxLTk9mo2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), + page.click("text=Login"), + ]); + // Click button:has-text("Continue with Google") + await Promise.all([ + page.waitForNavigation(/* { url: 'https://accounts.google.com/o/oauth2/auth/identifier?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow' } */), + page.click('button:has-text("Continue with Google")'), + ]); + // Fill [aria-label="Телефон\ или\ адрес\ эл\.\ почты"] + await page.fill( + '[aria-label="Телефон\\ или\\ адрес\\ эл\\.\\ почты"]', + "dtp.stat.test@gmail.com", + ); + // Click button:has-text("Далее") + await Promise.all([ + page.waitForNavigation(/* { url: 'https://accounts.google.com/signin/v2/challenge/pwd?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow&cid=1&navigationDirection=forward&TL=AM3QAYZq7nx8YKprItrlvoTViq5YZC2HEuc1NeRa2BVV0ZsxYWQqJ4cxbB74WWE8' } */), + page.click('button:has-text("Далее")'), + ]); + // Fill [aria-label="Введите\ пароль"] + await page.fill('[aria-label="Введите\\ пароль"]', "*geB7^QqUYvt2uTuyd"); + // Click button:has-text("Далее") + await Promise.all([ + page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), + page.click('button:has-text("Далее")'), + ]); + + await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + + // Save storage state into the file. + await context.storageState({ path: "tests/state.json" }); +} + +export default globalSetup; diff --git a/tests/login.test.ts b/tests/login.test.ts index 049b9cae..66cc0cdb 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -1,45 +1,9 @@ import { chromium, expect, test } from "@playwright/test"; -test("login via auth0 using google account", async ({}) => { - // Create a Chromium browser instance - const browser = await chromium.launch(); - const context = await browser.newContext({ - locale: "ru-RU", - }); - const page = await context.newPage(); - +test("add comment", async ({ page }) => { // Go to http://localhost:3000/ await page.goto("http://localhost:3000/"); - // Click text=Login - await Promise.all([ - page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SB1OGdCaDdaSUMtQlU0X2NlQWcyTUdrZk5ZU2tXeHdMMKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGduLW8xY2pKTFFoQkpuS2dXaEhRRGh6UjAwMmxLTk9mo2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), - page.click("text=Login"), - ]); - // Click button:has-text("Continue with Google") - await Promise.all([ - page.waitForNavigation(/* { url: 'https://accounts.google.com/o/oauth2/auth/identifier?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow' } */), - page.click('button:has-text("Continue with Google")'), - ]); - // Fill [aria-label="Телефон\ или\ адрес\ эл\.\ почты"] - await page.fill( - '[aria-label="Телефон\\ или\\ адрес\\ эл\\.\\ почты"]', - "dtp.stat.test@gmail.com", - ); - // Click button:has-text("Далее") - await Promise.all([ - page.waitForNavigation(/* { url: 'https://accounts.google.com/signin/v2/challenge/pwd?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow&cid=1&navigationDirection=forward&TL=AM3QAYZq7nx8YKprItrlvoTViq5YZC2HEuc1NeRa2BVV0ZsxYWQqJ4cxbB74WWE8' } */), - page.click('button:has-text("Далее")'), - ]); - // Fill [aria-label="Введите\ пароль"] - await page.fill('[aria-label="Введите\\ пароль"]', "*geB7^QqUYvt2uTuyd"); - // Click button:has-text("Далее") - await Promise.all([ - page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), - page.click('button:has-text("Далее")'), - ]); + // Confirm logged in user name await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); - - // Save storage state into the file. - await context.storageState({ path: "tests/state.json" }); }); From 6e30673eff312ad3bea2c3a79f524382c2d9a037 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 08:56:04 -0500 Subject: [PATCH 05/15] Setup dotenv for test env variables --- package.json | 1 + tests/global-setup.ts | 11 ++++++++--- yarn.lock | 8 ++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e86718a1..4ce056cf 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@auth0/nextjs-auth0": "^1.6.2", "@prisma/client": "^3.8.1", "dayjs": "^1.10.7", + "dotenv": "^14.2.0", "next": "^12.0.7", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/tests/global-setup.ts b/tests/global-setup.ts index 577d1083..d52d56ac 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,4 +1,6 @@ import { chromium, expect, FullConfig } from "@playwright/test"; +// Load test env variables +require("dotenv").config({ path: ".env.local" }); async function globalSetup(config: FullConfig) { // Create a Chromium browser instance @@ -9,7 +11,7 @@ async function globalSetup(config: FullConfig) { const page = await context.newPage(); // Go to http://localhost:3000/ - await page.goto("http://localhost:3000/"); + await page.goto(process.env.AUTH0_BASE_URL as string); // Click text=Login await Promise.all([ page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SB1OGdCaDdaSUMtQlU0X2NlQWcyTUdrZk5ZU2tXeHdMMKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGduLW8xY2pKTFFoQkpuS2dXaEhRRGh6UjAwMmxLTk9mo2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), @@ -23,7 +25,7 @@ async function globalSetup(config: FullConfig) { // Fill [aria-label="Телефон\ или\ адрес\ эл\.\ почты"] await page.fill( '[aria-label="Телефон\\ или\\ адрес\\ эл\\.\\ почты"]', - "dtp.stat.test@gmail.com", + process.env.TEST_LOGIN as string, ); // Click button:has-text("Далее") await Promise.all([ @@ -31,7 +33,10 @@ async function globalSetup(config: FullConfig) { page.click('button:has-text("Далее")'), ]); // Fill [aria-label="Введите\ пароль"] - await page.fill('[aria-label="Введите\\ пароль"]', "*geB7^QqUYvt2uTuyd"); + await page.fill( + '[aria-label="Введите\\ пароль"]', + process.env.TEST_PASSWORD as string, + ); // Click button:has-text("Далее") await Promise.all([ page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), diff --git a/yarn.lock b/yarn.lock index 6b557ac9..9a9c08c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3014,6 +3014,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^14.2.0": + version: 14.2.0 + resolution: "dotenv@npm:14.2.0" + checksum: 85a0e44918ef49e64c278f757dab50a156b9a6ca67f708876fd81d265e575e35b67387fc681d910df99368d6c1edca66cd546edeb0f7db3b499cb876c999233e + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.3.723": version: 1.4.35 resolution: "electron-to-chromium@npm:1.4.35" @@ -7212,6 +7219,7 @@ __metadata: "@types/react-linkify": ^1.0.1 "@types/styled-components": ^5.1.19 dayjs: ^1.10.7 + dotenv: ^14.2.0 eslint: ^8.6.0 husky: ^7.0.4 lint-staged: ^12.1.5 From b16d09bbca2051b44dc853f36718a7ffcf8e1dc6 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 08:57:13 -0500 Subject: [PATCH 06/15] Use env variables in tests --- tests/comments.test.ts | 6 +++--- tests/login.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/comments.test.ts b/tests/comments.test.ts index 3dbbeb2a..6750161a 100644 --- a/tests/comments.test.ts +++ b/tests/comments.test.ts @@ -9,11 +9,11 @@ test("add comment", async ({}) => { }); const page = await context.newPage(); - // Go to http://localhost:3000/ - await page.goto("http://localhost:3000/"); + // Go to / + await page.goto(process.env.AUTH0_BASE_URL as string); // Click text=Comments await Promise.all([ - page.waitForNavigation(/* { url: 'http://localhost:3000/iframes/comments/1' } */), + page.waitForNavigation(/* { url: '/iframes/comments/1' } */), page.click("text=Comments"), ]); // Click textarea diff --git a/tests/login.test.ts b/tests/login.test.ts index 66cc0cdb..ae4ab407 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -1,8 +1,8 @@ import { chromium, expect, test } from "@playwright/test"; test("add comment", async ({ page }) => { - // Go to http://localhost:3000/ - await page.goto("http://localhost:3000/"); + // Go to / + await page.goto(process.env.AUTH0_BASE_URL as string); // Confirm logged in user name await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); From 7879047157755d944799d928017db78aac241e43 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 09:00:29 -0500 Subject: [PATCH 07/15] Add debug mode to README --- README.md | 6 ++++++ package.json | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 44a35a4e..35c7a866 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ Run end-2-end tests with [Playwright](https://playwright.dev/). yarn test ``` +Run tests in interactive debug mode. + +```sh +yarn test:debug +``` + ## Environment variables All secrets should never be committed to the git repo, but saved in the environment variables. diff --git a/package.json b/package.json index 4ce056cf..dad4b2f1 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "lint:yarn-dedupe": "yarn dedupe --check", "start": "next start", "postinstall": "husky install", - "test": "yarn playwright test --project=chromium" + "test": "yarn playwright test --project=chromium", + "test:debug": "PWDEBUG=1 yarn test login" }, "lint-staged": { "**/*": [ From 98dde33f040d338d0787328f16a8260d2dec0541 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 09:16:04 -0500 Subject: [PATCH 08/15] yarn fix:yarn-dedupe --- yarn.lock | 102 ++++-------------------------------------------------- 1 file changed, 7 insertions(+), 95 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9a9c08c1..d3f2bdc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,17 +74,6 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/generator@npm:7.16.7" - dependencies: - "@babel/types": ^7.16.7 - jsesc: ^2.5.1 - source-map: ^0.5.0 - checksum: 20c6a7c5e372a66ec2900c074b2ec3634d3f615cafccbb416770f4b419251c6dc27a0a137b71407e218463fe059a3a6a5afb734f35089d94bdb66e01fe8a9e6f - languageName: node - linkType: hard - "@babel/generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/generator@npm:7.16.8" @@ -300,7 +289,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.16.10": +"@babel/parser@npm:^7.16.10, @babel/parser@npm:^7.16.7": version: 7.16.10 resolution: "@babel/parser@npm:7.16.10" bin: @@ -309,15 +298,6 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.16.7": - version: 7.16.7 - resolution: "@babel/parser@npm:7.16.7" - bin: - parser: ./bin/babel-parser.js - checksum: e664ff1edda164ab3f3c97fc1dd1a8930b0fba9981cbf873d3f25a22d16d50e2efcfaf81daeefa978bff2c4f268d34832f6817c8bc4e03594c3f43beba92fb68 - languageName: node - linkType: hard - "@babel/plugin-proposal-class-properties@npm:^7.14.5": version: 7.16.7 resolution: "@babel/plugin-proposal-class-properties@npm:7.16.7" @@ -658,7 +638,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7": +"@babel/traverse@npm:^7.16.10, @babel/traverse@npm:^7.16.7, @babel/traverse@npm:^7.4.5": version: 7.16.10 resolution: "@babel/traverse@npm:7.16.10" dependencies: @@ -676,24 +656,6 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.4.5": - version: 7.16.7 - resolution: "@babel/traverse@npm:7.16.7" - dependencies: - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.16.7 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.16.7 - "@babel/types": ^7.16.7 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: 65261f7a5bf257c10a9415b6c227fb555ace359ad786645d9cf22f0e3fc8dc8e38895269f3b93cc39eccd8ed992e7bacc358b4cb7d3496fe54f91cda49220834 - languageName: node - linkType: hard - "@babel/types@npm:7.15.0": version: 7.15.0 resolution: "@babel/types@npm:7.15.0" @@ -704,7 +666,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.8": +"@babel/types@npm:^7.16.0, @babel/types@npm:^7.16.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.8.3": version: 7.16.8 resolution: "@babel/types@npm:7.16.8" dependencies: @@ -714,16 +676,6 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.16.7, @babel/types@npm:^7.8.3": - version: 7.16.7 - resolution: "@babel/types@npm:7.16.7" - dependencies: - "@babel/helper-validator-identifier": ^7.16.7 - to-fast-properties: ^2.0.0 - checksum: df9210723259df9faea8c7e5674a59e57ead82664aab9f54daae887db5a50a956f30f57ed77a2d6cbb89b908d520cf8d883267c4e9098e31bc74649f2f714654 - languageName: node - linkType: hard - "@emotion/is-prop-valid@npm:^0.8.8": version: 0.8.8 resolution: "@emotion/is-prop-valid@npm:0.8.8" @@ -2267,14 +2219,7 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001202, caniuse-lite@npm:^1.0.30001219, caniuse-lite@npm:^1.0.30001228": - version: 1.0.30001296 - resolution: "caniuse-lite@npm:1.0.30001296" - checksum: b3fd113ef0d75a282b4271636267680d155ad7faaefb56313f7883881b48828a75663c5c65b27fb7ae30877710e1a662c8c567a9054297d34b2292db33b72db0 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001286": +"caniuse-lite@npm:^1.0.30001202, caniuse-lite@npm:^1.0.30001219, caniuse-lite@npm:^1.0.30001228, caniuse-lite@npm:^1.0.30001286": version: 1.0.30001301 resolution: "caniuse-lite@npm:1.0.30001301" checksum: 0e359f2c682bed35521902ce7fc91136c485de4c1e1e33272d6fbb7cdf77178df7649960a9b69ce9da12022aadd7a0037b3c9b3594c41146d58216654dc9a236 @@ -3021,14 +2966,7 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.3.723": - version: 1.4.35 - resolution: "electron-to-chromium@npm:1.4.35" - checksum: c4d7943abf370ca294129cda8432ea2f83c5b867e708aa33c7953e10f741803cfb23f83ba7341b6a76d76efb649f967b8e7b130ebe1e262971c764537ea5fd9b - languageName: node - linkType: hard - -"electron-to-chromium@npm:^1.4.17": +"electron-to-chromium@npm:^1.3.723, electron-to-chromium@npm:^1.4.17": version: 1.4.49 resolution: "electron-to-chromium@npm:1.4.49" checksum: 0527cc195c34b609f4ecad25584d238494d758c730d0aafdbd408a0ccce42cc6f05c1c0b76cc055a0a173700e29b1f3352d91b5cbeb91d063321690913afae84 @@ -7069,20 +7007,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.20.0": - version: 1.21.0 - resolution: "resolve@npm:1.21.0" - dependencies: - is-core-module: ^2.8.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: d7d9092a5c04a048bea16c7e5a2eb605ac3e8363a0cc5644de1fde17d5028e8d5f4343aab1d99bd327b98e91a66ea83e242718150c64dfedcb96e5e7aad6c4f5 - languageName: node - linkType: hard - -"resolve@npm:^1.13.1": +"resolve@npm:^1.10.0, resolve@npm:^1.13.1, resolve@npm:^1.20.0": version: 1.21.1 resolution: "resolve@npm:1.21.1" dependencies: @@ -7105,20 +7030,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin": - version: 1.21.0 - resolution: "resolve@patch:resolve@npm%3A1.21.0#~builtin::version=1.21.0&hash=07638b" - dependencies: - is-core-module: ^2.8.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: a0a4d1f7409e73190f31f901f8a619960bb3bd4ae38ba3a54c7ea7e1c87758d28a73256bb8d6a35996a903d1bf14f53883f0dcac6c571c063cb8162d813ad26e - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.13.1#~builtin": +"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.13.1#~builtin, resolve@patch:resolve@^1.20.0#~builtin": version: 1.21.1 resolution: "resolve@patch:resolve@npm%3A1.21.1#~builtin::version=1.21.1&hash=07638b" dependencies: From 0fd2aec64dafb08d3a2c5601c255f795fdec8489 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 09:31:22 -0500 Subject: [PATCH 09/15] Fix eslint warnings --- .eslintignore | 1 + playwright.config.ts | 4 +++- tests/comments.test.ts | 6 +++--- tests/global-setup.ts | 14 ++++++++------ tests/login.test.ts | 5 +++-- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.eslintignore b/.eslintignore index f1f07c34..2740323a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -50,3 +50,4 @@ yarn-error.log* .git/ .yarn/ +.history/ \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts index 15eb92f8..bc309719 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -7,7 +7,7 @@ const config: PlaywrightTestConfig = { testDir: "./tests", /* Reuse auth state */ - globalSetup: require.resolve("./tests/global-setup"), + globalSetup: "./tests/global-setup", /* Maximum time one test can run for. */ timeout: 30 * 1000, @@ -107,4 +107,6 @@ const config: PlaywrightTestConfig = { // port: 3000, // }, }; + +// eslint-disable-next-line import/no-default-export export default config; diff --git a/tests/comments.test.ts b/tests/comments.test.ts index 6750161a..0550c765 100644 --- a/tests/comments.test.ts +++ b/tests/comments.test.ts @@ -1,6 +1,6 @@ import { chromium, expect, test } from "@playwright/test"; -test("add comment", async ({}) => { +test("add comment", async () => { // Create a Chromium browser instance const browser = await chromium.launch(); // Create a new context with the saved storage state. @@ -25,6 +25,6 @@ test("add comment", async ({}) => { // Click text=Отправить await page.click("text=Отправить"); - await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); - await expect(page.locator("text=Hi, this is my comment")).toBeDefined(); + expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + expect(page.locator("text=Hi, this is my comment")).toBeDefined(); }); diff --git a/tests/global-setup.ts b/tests/global-setup.ts index d52d56ac..c1b13f59 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,8 +1,9 @@ -import { chromium, expect, FullConfig } from "@playwright/test"; -// Load test env variables -require("dotenv").config({ path: ".env.local" }); +import { chromium, expect } from "@playwright/test"; +import dotenv from "dotenv"; -async function globalSetup(config: FullConfig) { +dotenv.config({ path: ".env.local" }); + +const globalSetup = async () => { // Create a Chromium browser instance const browser = await chromium.launch(); const context = await browser.newContext({ @@ -43,10 +44,11 @@ async function globalSetup(config: FullConfig) { page.click('button:has-text("Далее")'), ]); - await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); // Save storage state into the file. await context.storageState({ path: "tests/state.json" }); -} +}; +// eslint-disable-next-line import/no-default-export export default globalSetup; diff --git a/tests/login.test.ts b/tests/login.test.ts index ae4ab407..ae36ea40 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -1,9 +1,10 @@ -import { chromium, expect, test } from "@playwright/test"; +import { expect, test } from "@playwright/test"; +// eslint-disable-next-line jest/no-done-callback test("add comment", async ({ page }) => { // Go to / await page.goto(process.env.AUTH0_BASE_URL as string); // Confirm logged in user name - await expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); }); From c8938cc08204d7bcf5decb77d822fade5344c6d7 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 09:37:54 -0500 Subject: [PATCH 10/15] DRAFT: Disable dotenv --- tests/global-setup.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/global-setup.ts b/tests/global-setup.ts index c1b13f59..758d6e6a 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,7 +1,8 @@ import { chromium, expect } from "@playwright/test"; -import dotenv from "dotenv"; -dotenv.config({ path: ".env.local" }); +// TODO: Enable oly for local env +// import dotenv from "dotenv"; +// dotenv.config({ path: ".env.local" }); const globalSetup = async () => { // Create a Chromium browser instance From 983a2014d7d21a937dc1423c043b8f9e44246538 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 17:11:37 -0500 Subject: [PATCH 11/15] Add login with email --- tests/global-setup.ts | 59 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/tests/global-setup.ts b/tests/global-setup.ts index 758d6e6a..ab7152c9 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,17 +1,43 @@ -import { chromium, expect } from "@playwright/test"; +import { chromium, Page } from "@playwright/test"; +import dotenv from "dotenv"; -// TODO: Enable oly for local env -// import dotenv from "dotenv"; -// dotenv.config({ path: ".env.local" }); +dotenv.config({ path: ".env.local" }); -const globalSetup = async () => { - // Create a Chromium browser instance - const browser = await chromium.launch(); - const context = await browser.newContext({ - locale: "ru-RU", - }); - const page = await context.newPage(); +interface TestParams { + page: Page; +} +const loginWithEmail = async ({ page }: TestParams) => { + // Go to http://localhost:3000/ + await page.goto(process.env.AUTH0_BASE_URL as string); + + // Click text=Login + await Promise.all([ + page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SBSXy14NTExZ1M3RWJobTFCU1hHS2xzS0FZbkpMejJRVqFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIHk3d2tZVjZwbnI3SzB1UHowUkdCZjItWWpjaExyNnd6o2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), + page.click("text=Login"), + ]); + + // Fill input[name="username"] + await page.fill('input[name="username"]', process.env.TEST_LOGIN as string); + + // Click input[name="password"] + await page.click('input[name="password"]'); + + // Fill input[name="password"] + await page.fill( + 'input[name="password"]', + process.env.TEST_PASSWORD as string, + ); + + // Click button:has-text("Continue") + await Promise.all([ + page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), + page.click('button:has-text("Continue")'), + ]); +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const loginWithGoogle = async ({ page }: TestParams) => { // Go to http://localhost:3000/ await page.goto(process.env.AUTH0_BASE_URL as string); // Click text=Login @@ -44,8 +70,17 @@ const globalSetup = async () => { page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), page.click('button:has-text("Далее")'), ]); +}; + +const globalSetup = async () => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + }); + const page = await context.newPage(); - expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + await loginWithEmail({ page }); // Save storage state into the file. await context.storageState({ path: "tests/state.json" }); From 0c8c392662611f99ba6bb6d38d367bccf3f32b8b Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 18:08:36 -0500 Subject: [PATCH 12/15] Move auth data to auth-helpers --- tests/comments.test.ts | 15 +++++-- tests/global-setup.ts | 84 +---------------------------------- tests/helpers/auth-helpers.ts | 71 +++++++++++++++++++++++++++++ tests/helpers/types.ts | 5 +++ tests/login.test.ts | 45 ++++++++++++++++--- 5 files changed, 127 insertions(+), 93 deletions(-) create mode 100644 tests/helpers/auth-helpers.ts create mode 100644 tests/helpers/types.ts diff --git a/tests/comments.test.ts b/tests/comments.test.ts index 0550c765..a7408750 100644 --- a/tests/comments.test.ts +++ b/tests/comments.test.ts @@ -1,5 +1,7 @@ import { chromium, expect, test } from "@playwright/test"; +import { testAuthData } from "./helpers/auth-helpers"; + test("add comment", async () => { // Create a Chromium browser instance const browser = await chromium.launch(); @@ -8,9 +10,10 @@ test("add comment", async () => { storageState: "tests/state.json", }); const page = await context.newPage(); + const newCommentText = "Hi, this is my comment"; // Go to / - await page.goto(process.env.AUTH0_BASE_URL as string); + await page.goto(testAuthData.URL); // Click text=Comments await Promise.all([ page.waitForNavigation(/* { url: '/iframes/comments/1' } */), @@ -19,12 +22,16 @@ test("add comment", async () => { // Click textarea await page.click("textarea"); // Fill textarea - await page.fill("textarea", "Hi, this is my comment\n"); + await page.fill("textarea", newCommentText); // Press Enter await page.press("textarea", "Enter"); // Click text=Отправить await page.click("text=Отправить"); - expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); - expect(page.locator("text=Hi, this is my comment")).toBeDefined(); + await expect(page.locator(`text=${newCommentText}`)).toHaveText( + newCommentText, + ); + await expect(page.locator(`text=${testAuthData.LOGIN}`)).toHaveText( + testAuthData.LOGIN, + ); }); diff --git a/tests/global-setup.ts b/tests/global-setup.ts index ab7152c9..0803e9bb 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,90 +1,8 @@ -import { chromium, Page } from "@playwright/test"; import dotenv from "dotenv"; dotenv.config({ path: ".env.local" }); -interface TestParams { - page: Page; -} - -const loginWithEmail = async ({ page }: TestParams) => { - // Go to http://localhost:3000/ - await page.goto(process.env.AUTH0_BASE_URL as string); - - // Click text=Login - await Promise.all([ - page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SBSXy14NTExZ1M3RWJobTFCU1hHS2xzS0FZbkpMejJRVqFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIHk3d2tZVjZwbnI3SzB1UHowUkdCZjItWWpjaExyNnd6o2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), - page.click("text=Login"), - ]); - - // Fill input[name="username"] - await page.fill('input[name="username"]', process.env.TEST_LOGIN as string); - - // Click input[name="password"] - await page.click('input[name="password"]'); - - // Fill input[name="password"] - await page.fill( - 'input[name="password"]', - process.env.TEST_PASSWORD as string, - ); - - // Click button:has-text("Continue") - await Promise.all([ - page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), - page.click('button:has-text("Continue")'), - ]); -}; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const loginWithGoogle = async ({ page }: TestParams) => { - // Go to http://localhost:3000/ - await page.goto(process.env.AUTH0_BASE_URL as string); - // Click text=Login - await Promise.all([ - page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SB1OGdCaDdaSUMtQlU0X2NlQWcyTUdrZk5ZU2tXeHdMMKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGduLW8xY2pKTFFoQkpuS2dXaEhRRGh6UjAwMmxLTk9mo2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), - page.click("text=Login"), - ]); - // Click button:has-text("Continue with Google") - await Promise.all([ - page.waitForNavigation(/* { url: 'https://accounts.google.com/o/oauth2/auth/identifier?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow' } */), - page.click('button:has-text("Continue with Google")'), - ]); - // Fill [aria-label="Телефон\ или\ адрес\ эл\.\ почты"] - await page.fill( - '[aria-label="Телефон\\ или\\ адрес\\ эл\\.\\ почты"]', - process.env.TEST_LOGIN as string, - ); - // Click button:has-text("Далее") - await Promise.all([ - page.waitForNavigation(/* { url: 'https://accounts.google.com/signin/v2/challenge/pwd?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow&cid=1&navigationDirection=forward&TL=AM3QAYZq7nx8YKprItrlvoTViq5YZC2HEuc1NeRa2BVV0ZsxYWQqJ4cxbB74WWE8' } */), - page.click('button:has-text("Далее")'), - ]); - // Fill [aria-label="Введите\ пароль"] - await page.fill( - '[aria-label="Введите\\ пароль"]', - process.env.TEST_PASSWORD as string, - ); - // Click button:has-text("Далее") - await Promise.all([ - page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), - page.click('button:has-text("Далее")'), - ]); -}; - -const globalSetup = async () => { - // Create a Chromium browser instance - const browser = await chromium.launch(); - const context = await browser.newContext({ - locale: "ru-RU", - }); - const page = await context.newPage(); - - await loginWithEmail({ page }); - - // Save storage state into the file. - await context.storageState({ path: "tests/state.json" }); -}; +const globalSetup = () => {}; // eslint-disable-next-line import/no-default-export export default globalSetup; diff --git a/tests/helpers/auth-helpers.ts b/tests/helpers/auth-helpers.ts new file mode 100644 index 00000000..c530462d --- /dev/null +++ b/tests/helpers/auth-helpers.ts @@ -0,0 +1,71 @@ +import dotenv from "dotenv"; + +import { TestParams } from "./types"; + +dotenv.config({ path: ".env.local" }); + +export const testAuthData = { + URL: process.env.AUTH0_BASE_URL as string, + LOGIN: process.env.TEST_LOGIN as string, + PASSWORD: process.env.TEST_PASSWORD as string, +}; + +export const loginWithEmail = async ({ page }: TestParams) => { + // Go to / + await page.goto(testAuthData.URL); + + // Click text=Login + await Promise.all([ + page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SBSXy14NTExZ1M3RWJobTFCU1hHS2xzS0FZbkpMejJRVqFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIHk3d2tZVjZwbnI3SzB1UHowUkdCZjItWWpjaExyNnd6o2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), + page.click("text=Login"), + ]); + + // Fill input[name="username"] + await page.fill('input[name="username"]', testAuthData.LOGIN); + + // Click input[name="password"] + await page.click('input[name="password"]'); + + // Fill input[name="password"] + await page.fill('input[name="password"]', testAuthData.PASSWORD); + + // Click button:has-text("Continue") + await Promise.all([ + page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), + page.click('button:has-text("Continue")'), + ]); +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const loginWithGoogle = async ({ page }: TestParams) => { + // Go to / + await page.goto(testAuthData.URL); + + // Click text=Login + await Promise.all([ + page.waitForNavigation(/* { url: 'https://dev-9pt5c6ik.us.auth0.com/u/login?state=hKFo2SB1OGdCaDdaSUMtQlU0X2NlQWcyTUdrZk5ZU2tXeHdMMKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGduLW8xY2pKTFFoQkpuS2dXaEhRRGh6UjAwMmxLTk9mo2NpZNkgVklNTGgwNFNLdjhxUjdXc2VwRUhNT3FrdGJSQlNVeTE' } */), + page.click("text=Login"), + ]); + // Click button:has-text("Continue with Google") + await Promise.all([ + page.waitForNavigation(/* { url: 'https://accounts.google.com/o/oauth2/auth/identifier?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow' } */), + page.click('button:has-text("Continue with Google")'), + ]); + // Fill [aria-label="Телефон\ или\ адрес\ эл\.\ почты"] + await page.fill( + '[aria-label="Телефон\\ или\\ адрес\\ эл\\.\\ почты"]', + testAuthData.LOGIN, + ); + // Click button:has-text("Далее") + await Promise.all([ + page.waitForNavigation(/* { url: 'https://accounts.google.com/signin/v2/challenge/pwd?login_hint&response_type=code&redirect_uri=https%3A%2F%2Flogin.us.auth0.com%2Flogin%2Fcallback&scope=email%20profile&state=n8xYkwcxbRy0TbhioEowcLaDwAoKBhUm&client_id=104565000066-q7q63bouq2ct8gj73drmpu186amn6a3f.apps.googleusercontent.com&flowName=GeneralOAuthFlow&cid=1&navigationDirection=forward&TL=AM3QAYZq7nx8YKprItrlvoTViq5YZC2HEuc1NeRa2BVV0ZsxYWQqJ4cxbB74WWE8' } */), + page.click('button:has-text("Далее")'), + ]); + // Fill [aria-label="Введите\ пароль"] + await page.fill('[aria-label="Введите\\ пароль"]', testAuthData.PASSWORD); + // Click button:has-text("Далее") + await Promise.all([ + page.waitForNavigation(/* { url: 'http://localhost:3000/' } */), + page.click('button:has-text("Далее")'), + ]); +}; diff --git a/tests/helpers/types.ts b/tests/helpers/types.ts new file mode 100644 index 00000000..c9838316 --- /dev/null +++ b/tests/helpers/types.ts @@ -0,0 +1,5 @@ +import { Page } from "@playwright/test"; + +export interface TestParams { + page: Page; +} diff --git a/tests/login.test.ts b/tests/login.test.ts index ae36ea40..f0a4c227 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -1,10 +1,43 @@ -import { expect, test } from "@playwright/test"; +import { chromium, expect, test } from "@playwright/test"; + +import { loginWithEmail, testAuthData } from "./helpers/auth-helpers"; + +test("login with email", async () => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + }); + const page = await context.newPage(); + + await loginWithEmail({ page }); + + // Save storage state into the file. + await context.storageState({ path: "tests/state.json" }); -// eslint-disable-next-line jest/no-done-callback -test("add comment", async ({ page }) => { // Go to / - await page.goto(process.env.AUTH0_BASE_URL as string); + await page.goto(testAuthData.URL); + + const header = page.locator("h4"); + + // Confirm logged in user + await expect(header).toHaveText(process.env.TEST_LOGIN as string); +}); + +test("load auth0 session", async () => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + storageState: "tests/state.json", + }); + const page = await context.newPage(); // Create a new context with the saved storage state. + + // Go to / + await page.goto(testAuthData.URL); + + const header = page.locator("h4"); - // Confirm logged in user name - expect(page.locator("text=DTP-STAT TEST")).toBeDefined(); + // Confirm logged in user + await expect(header).toHaveText(testAuthData.LOGIN); }); From a12a66c3bbf65a1c5961bf50100eb934ce37fc19 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 19:17:01 -0500 Subject: [PATCH 13/15] Mock auth login --- tests/comments.test.ts | 17 ++++------ tests/helpers/auth-helpers.ts | 20 ++++++++++- tests/login.test.ts | 62 +++++++++++++++++++++++++---------- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/tests/comments.test.ts b/tests/comments.test.ts index a7408750..5f4ab008 100644 --- a/tests/comments.test.ts +++ b/tests/comments.test.ts @@ -1,15 +1,12 @@ -import { chromium, expect, test } from "@playwright/test"; +import { expect, test } from "@playwright/test"; -import { testAuthData } from "./helpers/auth-helpers"; +import { mockLoginWithEmail, testAuthData } from "./helpers/auth-helpers"; +import { TestParams } from "./helpers/types"; + +// eslint-disable-next-line jest/no-done-callback +test("add comment", async ({ page }: TestParams) => { + await mockLoginWithEmail({ page }); -test("add comment", async () => { - // Create a Chromium browser instance - const browser = await chromium.launch(); - // Create a new context with the saved storage state. - const context = await browser.newContext({ - storageState: "tests/state.json", - }); - const page = await context.newPage(); const newCommentText = "Hi, this is my comment"; // Go to / diff --git a/tests/helpers/auth-helpers.ts b/tests/helpers/auth-helpers.ts index c530462d..27f273f0 100644 --- a/tests/helpers/auth-helpers.ts +++ b/tests/helpers/auth-helpers.ts @@ -8,6 +8,14 @@ export const testAuthData = { URL: process.env.AUTH0_BASE_URL as string, LOGIN: process.env.TEST_LOGIN as string, PASSWORD: process.env.TEST_PASSWORD as string, + authMeResp: { + nickname: "dtp.stat.test", + name: "dtp.stat.test@gmail.com", + picture: "", + // eslint-disable-next-line @typescript-eslint/naming-convention + updated_at: "2022-01-21T23:42:43.606Z", + sub: "auth0|61eb2c2059e0a90071d8f69f", + }, }; export const loginWithEmail = async ({ page }: TestParams) => { @@ -36,7 +44,6 @@ export const loginWithEmail = async ({ page }: TestParams) => { ]); }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars export const loginWithGoogle = async ({ page }: TestParams) => { // Go to / await page.goto(testAuthData.URL); @@ -69,3 +76,14 @@ export const loginWithGoogle = async ({ page }: TestParams) => { page.click('button:has-text("Далее")'), ]); }; + +export const mockAuth0Login = async ({ page }: TestParams) => { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + await page.route(`${testAuthData.URL}/api/auth/me`, (route) => + route.fulfill({ body: JSON.stringify(testAuthData.authMeResp) }), + ); +}; + +export const mockLoginWithEmail = async ({ page }: TestParams) => { + await mockAuth0Login({ page }); +}; diff --git a/tests/login.test.ts b/tests/login.test.ts index f0a4c227..2d4f9903 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -1,8 +1,12 @@ import { chromium, expect, test } from "@playwright/test"; -import { loginWithEmail, testAuthData } from "./helpers/auth-helpers"; +import { + loginWithEmail, + mockLoginWithEmail, + testAuthData, +} from "./helpers/auth-helpers"; -test("login with email", async () => { +test("mock login with email", async () => { // Create a Chromium browser instance const browser = await chromium.launch(); const context = await browser.newContext({ @@ -10,7 +14,7 @@ test("login with email", async () => { }); const page = await context.newPage(); - await loginWithEmail({ page }); + await mockLoginWithEmail({ page }); // Save storage state into the file. await context.storageState({ path: "tests/state.json" }); @@ -21,23 +25,45 @@ test("login with email", async () => { const header = page.locator("h4"); // Confirm logged in user - await expect(header).toHaveText(process.env.TEST_LOGIN as string); + await expect(header).toHaveText(testAuthData.LOGIN); }); -test("load auth0 session", async () => { - // Create a Chromium browser instance - const browser = await chromium.launch(); - const context = await browser.newContext({ - locale: "ru-RU", - storageState: "tests/state.json", - }); - const page = await context.newPage(); // Create a new context with the saved storage state. +// test("login with email", async () => { +// // Create a Chromium browser instance +// const browser = await chromium.launch(); +// const context = await browser.newContext({ +// locale: "ru-RU", +// }); +// const page = await context.newPage(); - // Go to / - await page.goto(testAuthData.URL); +// await loginWithEmail({ page }); - const header = page.locator("h4"); +// // Save storage state into the file. +// await context.storageState({ path: "tests/state.json" }); - // Confirm logged in user - await expect(header).toHaveText(testAuthData.LOGIN); -}); +// // Go to / +// await page.goto(testAuthData.URL); + +// const header = page.locator("h4"); + +// // Confirm logged in user +// await expect(header).toHaveText(testAuthData.LOGIN); +// }); + +// test("load auth0 session", async () => { +// // Create a Chromium browser instance +// const browser = await chromium.launch(); +// const context = await browser.newContext({ +// locale: "ru-RU", +// storageState: "tests/state.json", +// }); +// const page = await context.newPage(); // Create a new context with the saved storage state. + +// // Go to / +// await page.goto(testAuthData.URL); + +// const header = page.locator("h4"); + +// // Confirm logged in user +// await expect(header).toHaveText(testAuthData.LOGIN); +// }); From 5b0ecced43d668e09bfdd54984dfb674068c3c84 Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 19:21:08 -0500 Subject: [PATCH 14/15] Refactor login mock --- tests/login.test.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tests/login.test.ts b/tests/login.test.ts index 2d4f9903..84183585 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -5,20 +5,12 @@ import { mockLoginWithEmail, testAuthData, } from "./helpers/auth-helpers"; +import { TestParams } from "./helpers/types"; -test("mock login with email", async () => { - // Create a Chromium browser instance - const browser = await chromium.launch(); - const context = await browser.newContext({ - locale: "ru-RU", - }); - const page = await context.newPage(); - +// eslint-disable-next-line jest/no-done-callback +test("mock login with email", async ({ page }: TestParams) => { await mockLoginWithEmail({ page }); - // Save storage state into the file. - await context.storageState({ path: "tests/state.json" }); - // Go to / await page.goto(testAuthData.URL); From d5efb7598fb57cec4b7fd62b1242b8634d993c3a Mon Sep 17 00:00:00 2001 From: Sergei Smirnov Date: Fri, 21 Jan 2022 19:26:31 -0500 Subject: [PATCH 15/15] Separate e2e tests by extension --- tests/login.e2e.ts | 43 ++++++++++++++++++++++++++++++++++++++++ tests/login.test.ts | 48 ++------------------------------------------- 2 files changed, 45 insertions(+), 46 deletions(-) create mode 100644 tests/login.e2e.ts diff --git a/tests/login.e2e.ts b/tests/login.e2e.ts new file mode 100644 index 00000000..acc8e41e --- /dev/null +++ b/tests/login.e2e.ts @@ -0,0 +1,43 @@ +import { chromium, expect, test } from "@playwright/test"; + +import { loginWithEmail, testAuthData } from "./helpers/auth-helpers"; + +test("login with email", async () => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + }); + const page = await context.newPage(); + + await loginWithEmail({ page }); + + // Save storage state into the file. + await context.storageState({ path: "tests/state.json" }); + + // Go to / + await page.goto(testAuthData.URL); + + const header = page.locator("h4"); + + // Confirm logged in user + await expect(header).toHaveText(testAuthData.LOGIN); +}); + +test("load auth0 session", async () => { + // Create a Chromium browser instance + const browser = await chromium.launch(); + const context = await browser.newContext({ + locale: "ru-RU", + storageState: "tests/state.json", + }); + const page = await context.newPage(); // Create a new context with the saved storage state. + + // Go to / + await page.goto(testAuthData.URL); + + const header = page.locator("h4"); + + // Confirm logged in user + await expect(header).toHaveText(testAuthData.LOGIN); +}); diff --git a/tests/login.test.ts b/tests/login.test.ts index 84183585..c00ec34e 100644 --- a/tests/login.test.ts +++ b/tests/login.test.ts @@ -1,10 +1,6 @@ -import { chromium, expect, test } from "@playwright/test"; +import { expect, test } from "@playwright/test"; -import { - loginWithEmail, - mockLoginWithEmail, - testAuthData, -} from "./helpers/auth-helpers"; +import { mockLoginWithEmail, testAuthData } from "./helpers/auth-helpers"; import { TestParams } from "./helpers/types"; // eslint-disable-next-line jest/no-done-callback @@ -19,43 +15,3 @@ test("mock login with email", async ({ page }: TestParams) => { // Confirm logged in user await expect(header).toHaveText(testAuthData.LOGIN); }); - -// test("login with email", async () => { -// // Create a Chromium browser instance -// const browser = await chromium.launch(); -// const context = await browser.newContext({ -// locale: "ru-RU", -// }); -// const page = await context.newPage(); - -// await loginWithEmail({ page }); - -// // Save storage state into the file. -// await context.storageState({ path: "tests/state.json" }); - -// // Go to / -// await page.goto(testAuthData.URL); - -// const header = page.locator("h4"); - -// // Confirm logged in user -// await expect(header).toHaveText(testAuthData.LOGIN); -// }); - -// test("load auth0 session", async () => { -// // Create a Chromium browser instance -// const browser = await chromium.launch(); -// const context = await browser.newContext({ -// locale: "ru-RU", -// storageState: "tests/state.json", -// }); -// const page = await context.newPage(); // Create a new context with the saved storage state. - -// // Go to / -// await page.goto(testAuthData.URL); - -// const header = page.locator("h4"); - -// // Confirm logged in user -// await expect(header).toHaveText(testAuthData.LOGIN); -// });