├── .gitignore ├── .prettierrc ├── 404-detection ├── .gitignore ├── checkly.config.ts ├── package-lock.json ├── package.json ├── playwright.config.ts └── tests │ └── no-404s.spec.ts ├── README.md ├── api-schema-validation ├── .gitignore ├── .prettierrc ├── package-lock.json ├── package.json ├── playwright.config.ts ├── server.js └── tests │ └── api.spec.ts ├── controlling-time ├── .gitignore ├── demo │ ├── index.html │ └── timezone.html ├── package-lock.json ├── package.json ├── playwright.config.ts └── tests │ ├── control-time.spec.ts │ └── control-timezones.spec.ts ├── global-before-after-each ├── .gitignore ├── package-lock.json ├── package.json ├── playwright.config.ts └── tests │ ├── base.ts │ ├── example-1.spec.ts │ └── example-2.spec.ts ├── parameterized-fixtures ├── .gitignore ├── package-lock.json ├── package.json ├── playwright.config.ts └── tests │ ├── base.ts │ ├── parameterized-fixtures.spec.ts │ └── poms │ └── dashboard.ts ├── project-setup-and-storage-state ├── .gitignore ├── .prettierrc ├── package-lock.json ├── package.json ├── playwright.config.ts └── tests │ ├── product.spec.ts │ └── session.setup.ts ├── test-step-decorators ├── .gitignore ├── package-lock.json ├── package.json ├── playwright.config.ts └── tests │ ├── base.ts │ ├── docs.spec.ts │ └── poms │ └── playwright-page.ts └── type-check-and-lint ├── .gitignore ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── playwright.config.ts ├── tests └── example.spec.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { "semi": false } 2 | -------------------------------------------------------------------------------- /404-detection/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | -------------------------------------------------------------------------------- /404-detection/checkly.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "checkly" 2 | 3 | /** 4 | * See https://www.checklyhq.com/docs/cli/project-structure/ 5 | */ 6 | const config = defineConfig({ 7 | /* A human friendly name for your project */ 8 | projectName: "404-detection", 9 | /** A logical ID that needs to be unique across your Checkly account, 10 | * See https://www.checklyhq.com/docs/cli/constructs/ to learn more about logical IDs. 11 | */ 12 | logicalId: "404-detection", 13 | 14 | /* Sets default values for Checks */ 15 | checks: { 16 | /* A default for how often your Check should run in minutes */ 17 | frequency: 10, 18 | /* Checkly data centers to run your Checks as monitors */ 19 | locations: ["us-east-1", "eu-west-1"], 20 | /* An optional array of tags to organize your Checks */ 21 | tags: ["mac"], 22 | /** The Checkly Runtime identifier, determining npm packages and the Node.js version available at runtime. 23 | * See https://www.checklyhq.com/docs/cli/npm-packages/ 24 | */ 25 | runtimeId: "2024.02", 26 | /* A glob pattern that matches the Checks inside your repo, see https://www.checklyhq.com/docs/cli/using-check-test-match/ */ 27 | checkMatch: "**/__checks__/**/*.check.ts", 28 | /* Global configuration option for Playwright-powered checks. See https://docs/browser-checks/playwright-test/#global-configuration */ 29 | browserChecks: { 30 | /* A glob pattern matches any Playwright .spec.ts files and automagically creates a Browser Check. This way, you 31 | * can just write native Playwright code. See https://www.checklyhq.com/docs/cli/using-check-test-match/ 32 | * */ 33 | testMatch: "**/tests/**/*.spec.ts", 34 | }, 35 | }, 36 | cli: { 37 | /* The default datacenter location to use when running npx checkly test */ 38 | runLocation: "eu-west-1", 39 | /* An array of default reporters to use when a reporter is not specified with the "--reporter" flag */ 40 | reporters: ["list"], 41 | /* How many times to retry a failing test run when running `npx checkly test` or `npx checkly trigger` (max. 3) */ 42 | retries: 0, 43 | }, 44 | }) 45 | 46 | export default config 47 | -------------------------------------------------------------------------------- /404-detection/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "404-detection", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": {}, 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "description": "", 10 | "devDependencies": { 11 | "@playwright/test": "^1.46.1", 12 | "@types/node": "^22.5.3", 13 | "checkly": "latest", 14 | "ts-node": "latest", 15 | "typescript": "latest" 16 | } 17 | } -------------------------------------------------------------------------------- /404-detection/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test" 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 9 | 10 | /** 11 | * See https://playwright.dev/docs/test-configuration. 12 | */ 13 | export default defineConfig({ 14 | testDir: "./tests", 15 | /* Run tests in files in parallel */ 16 | fullyParallel: true, 17 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 18 | forbidOnly: !!process.env.CI, 19 | /* Retry on CI only */ 20 | retries: process.env.CI ? 2 : 0, 21 | /* Opt out of parallel tests on CI. */ 22 | workers: process.env.CI ? 1 : undefined, 23 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 24 | reporter: "html", 25 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 26 | use: { 27 | /* Base URL to use in actions like `await page.goto('/')`. */ 28 | // baseURL: 'http://127.0.0.1:3000', 29 | 30 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 31 | trace: "on-first-retry", 32 | baseURL: "https://www.checklyhq.com/", 33 | }, 34 | 35 | timeout: 60 * 1000, 36 | 37 | /* Configure projects for major browsers */ 38 | projects: [ 39 | { 40 | name: "chromium", 41 | use: { ...devices["Desktop Chrome"] }, 42 | }, 43 | 44 | // { 45 | // name: "firefox", 46 | // use: { ...devices["Desktop Firefox"] }, 47 | // }, 48 | 49 | // { 50 | // name: "webkit", 51 | // use: { ...devices["Desktop Safari"] }, 52 | // }, 53 | 54 | /* Test against mobile viewports. */ 55 | // { 56 | // name: 'Mobile Chrome', 57 | // use: { ...devices['Pixel 5'] }, 58 | // }, 59 | // { 60 | // name: 'Mobile Safari', 61 | // use: { ...devices['iPhone 12'] }, 62 | // }, 63 | 64 | /* Test against branded browsers. */ 65 | // { 66 | // name: 'Microsoft Edge', 67 | // use: { ...devices['Desktop Edge'], channel: 'msedge' }, 68 | // }, 69 | // { 70 | // name: 'Google Chrome', 71 | // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, 72 | // }, 73 | ], 74 | 75 | /* Run your local dev server before starting the tests */ 76 | // webServer: { 77 | // command: 'npm run start', 78 | // url: 'http://127.0.0.1:3000', 79 | // reuseExistingServer: !process.env.CI, 80 | // }, 81 | }) 82 | -------------------------------------------------------------------------------- /404-detection/tests/no-404s.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, Page, test } from "@playwright/test" 2 | 3 | async function getAllLinksFromPage(page: Page) { 4 | // getByRole('link') only matches visible links 5 | // 6 | // if you want to check all links, you can use a CSS selector 7 | // like 'locator("a")' 8 | const links = page.getByRole("link") 9 | 10 | const allLinks = await links.all() 11 | const allLinkHrefs = await Promise.all( 12 | allLinks.map((link) => link.getAttribute("href")) 13 | ) 14 | const validHrefs = allLinkHrefs.reduce((links, link) => { 15 | expect.soft(link, "link has no a proper href").not.toBeFalsy() 16 | 17 | if (link && !link?.startsWith("mailto:") && !link?.startsWith("#")) 18 | links.add(new URL(link, page.url()).href) 19 | return links 20 | }, new Set()) 21 | 22 | return validHrefs 23 | } 24 | 25 | test.describe("No 404s on Checkly pages", () => { 26 | test(`The docs have no 404s`, async ({ page }, testInfo) => { 27 | await page.goto("https://www.checklyhq.com/docs/") 28 | const linkUrls = await getAllLinksFromPage(page) 29 | 30 | for (const url of linkUrls) { 31 | await test.step(`Checking link: ${url}`, async () => { 32 | try { 33 | // Note that some hosters / firewalls will block plain requests (Cloudflare, etc.) 34 | // if that's the case for you, consider using `page.goto` 35 | // or excluding particular URLs from the test 36 | const response = await page.request.get(url) 37 | 38 | expect 39 | .soft(response.ok(), `${url} has no green status code`) 40 | .toBeTruthy() 41 | } catch { 42 | expect.soft(null, `${url} has no green status code`).toBeTruthy() 43 | } 44 | }) 45 | } 46 | 47 | testInfo.attach("checked-links.txt", { 48 | body: Array.from(linkUrls).join("\n"), 49 | }) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Checkly Playwright Examples 2 | 3 | This repository holds the demo code for Checkly tutorials and videos. 4 | 5 | ## How to add Type Checking and Linting to your Playwright Project 6 | 7 | ![types-checking-social](https://github.com/user-attachments/assets/b86dbc82-65a9-4a2e-b7ce-3a20177136ab) 8 | 9 | ### 🧑‍💻 [Code](/type-check-and-lint) | ✍️ [Article](https://www.checklyhq.com/blog/playwright-type-checking-and-linting/) | 🎥 [Video](https://www.youtube.com/watch?v=3gT7LuzqOAk) 10 | 11 | ## How to Speed up your Playwright Tests with shared "storageState" 12 | 13 | ![storage-state](https://github.com/user-attachments/assets/c49583a3-902b-4bc5-8fff-05b90ecca904) 14 | 15 | ### 🧑‍💻 [Code](/project-setup-and-storage-state/) | ✍️ [Article](https://www.checklyhq.com/blog/speed-up-playwright-tests-with-storage-state/) | 🎥 [Video](https://www.youtube.com/watch?v=nSHPCLUwwVk) 16 | 17 | ## Apply Playwright test steps with TypeScript decorators 18 | 19 | ![decorators](https://github.com/user-attachments/assets/98e1a831-e2b9-481e-88d7-149fbc9d9067) 20 | 21 | ### 🧑‍💻 [Code](/test-step-decorators/) | ✍️ [Article](https://www.checklyhq.com/blog/playwright-test-steps-with-typescript-decorators/) | 🎥 [Video](https://www.youtube.com/watch?v=of1v9cycTdQ) 22 | 23 | ## Detect broken links with Playwright 24 | 25 | ![broken links](https://github.com/user-attachments/assets/ac4ca28b-12e5-44b1-afb4-9a8291cf24fd) 26 | 27 | ### 🧑‍💻 [Code](/404-detection/) | ✍️ [Article](https://www.checklyhq.com/blog/how-to-detect-broken-links-with-playwright/) | 🎥 [Video](https://www.youtube.com/watch?v=EJJ_PYK8YiM) 28 | 29 | ## Parameterize your custom Playwright fixtures 30 | 31 | ![Parameterize your custom Playwright fixtures](https://github.com/user-attachments/assets/9801fa83-849b-4fdd-aca8-cfe3df7010f1) 32 | 33 | ### 🧑‍💻 [Code](/parameterized-fixtures/) | ✍️ [Article](https://www.checklyhq.com/blog/how-to-parameterize-playwright-projects/) | 🎥 [Video](https://www.youtube.com/watch?v=rRmfYu8hlbw) 34 | 35 | ## Five Playwright CLI features you should know 36 | 37 | ![Five Playwright CLI features you should know](https://github.com/user-attachments/assets/97efe257-31f3-48c8-8568-7e407a651bd4) 38 | 39 | ### ✍️ [Article](https://www.checklyhq.com/blog/five-playwright-cli-features-you-should-know/) | 🎥 [Video](https://www.youtube.com/watch?v=kw_8LXRSd10) 40 | 41 | ## How to control time zones and timeouts in Playwright 42 | 43 | ![How to control time zones and timeouts in Playwright ](https://github.com/user-attachments/assets/fbf07a0a-3202-44fc-9931-ba71ecbd162e) 44 | 45 | ### 🧑‍💻 [Code](/controlling-time/) | 🎥 [Video](https://www.youtube.com/watch?v=28DfWgT_zQk) 46 | 47 | ## How to add global `beforeEach` and `afterEach` hooks 48 | 49 | ![export 3xc(1)](https://github.com/user-attachments/assets/bc4d9711-802d-4e90-b887-07b63decf295) 50 | 51 | ### 🧑‍💻 [Code](/global-before-after-each/) | 🎥 [Video](https://www.youtube.com/watch?v=hegZS46J0rA) 52 | 53 | ## Use Playwright to Validate an API Response Schema 54 | 55 | ![export 3xc(2)](https://github.com/user-attachments/assets/d1bc9835-5227-4a89-8bcd-ff15e443cdae) 56 | 57 | ### 🧑‍💻 [Code](/api-schema-validation/) | 🎥 [Video](https://www.youtube.com/watch?v=axBr9gnITOo) 58 | -------------------------------------------------------------------------------- /api-schema-validation/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Playwright 3 | node_modules/ 4 | /test-results/ 5 | /playwright-report/ 6 | /blob-report/ 7 | /playwright/.cache/ 8 | -------------------------------------------------------------------------------- /api-schema-validation/.prettierrc: -------------------------------------------------------------------------------- 1 | { "semi": true, "printWidth": 60 } 2 | -------------------------------------------------------------------------------- /api-schema-validation/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-schema-validation", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "api-schema-validation", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "ajv": "^8.17.1" 13 | }, 14 | "devDependencies": { 15 | "@faker-js/faker": "^9.4.0", 16 | "@playwright/test": "^1.50.1", 17 | "@types/node": "^22.13.0", 18 | "zod": "^3.24.1" 19 | } 20 | }, 21 | "node_modules/@faker-js/faker": { 22 | "version": "9.5.0", 23 | "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.0.tgz", 24 | "integrity": "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==", 25 | "dev": true, 26 | "funding": [ 27 | { 28 | "type": "opencollective", 29 | "url": "https://opencollective.com/fakerjs" 30 | } 31 | ], 32 | "license": "MIT", 33 | "engines": { 34 | "node": ">=18.0.0", 35 | "npm": ">=9.0.0" 36 | } 37 | }, 38 | "node_modules/@playwright/test": { 39 | "version": "1.50.1", 40 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", 41 | "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", 42 | "dev": true, 43 | "license": "Apache-2.0", 44 | "dependencies": { 45 | "playwright": "1.50.1" 46 | }, 47 | "bin": { 48 | "playwright": "cli.js" 49 | }, 50 | "engines": { 51 | "node": ">=18" 52 | } 53 | }, 54 | "node_modules/@types/node": { 55 | "version": "22.13.5", 56 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", 57 | "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==", 58 | "dev": true, 59 | "license": "MIT", 60 | "dependencies": { 61 | "undici-types": "~6.20.0" 62 | } 63 | }, 64 | "node_modules/ajv": { 65 | "version": "8.17.1", 66 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", 67 | "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", 68 | "license": "MIT", 69 | "dependencies": { 70 | "fast-deep-equal": "^3.1.3", 71 | "fast-uri": "^3.0.1", 72 | "json-schema-traverse": "^1.0.0", 73 | "require-from-string": "^2.0.2" 74 | }, 75 | "funding": { 76 | "type": "github", 77 | "url": "https://github.com/sponsors/epoberezkin" 78 | } 79 | }, 80 | "node_modules/fast-deep-equal": { 81 | "version": "3.1.3", 82 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 83 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 84 | "license": "MIT" 85 | }, 86 | "node_modules/fast-uri": { 87 | "version": "3.0.6", 88 | "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", 89 | "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", 90 | "funding": [ 91 | { 92 | "type": "github", 93 | "url": "https://github.com/sponsors/fastify" 94 | }, 95 | { 96 | "type": "opencollective", 97 | "url": "https://opencollective.com/fastify" 98 | } 99 | ], 100 | "license": "BSD-3-Clause" 101 | }, 102 | "node_modules/fsevents": { 103 | "version": "2.3.2", 104 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 105 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 106 | "dev": true, 107 | "hasInstallScript": true, 108 | "license": "MIT", 109 | "optional": true, 110 | "os": [ 111 | "darwin" 112 | ], 113 | "engines": { 114 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 115 | } 116 | }, 117 | "node_modules/json-schema-traverse": { 118 | "version": "1.0.0", 119 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 120 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 121 | "license": "MIT" 122 | }, 123 | "node_modules/playwright": { 124 | "version": "1.50.1", 125 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", 126 | "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", 127 | "dev": true, 128 | "license": "Apache-2.0", 129 | "dependencies": { 130 | "playwright-core": "1.50.1" 131 | }, 132 | "bin": { 133 | "playwright": "cli.js" 134 | }, 135 | "engines": { 136 | "node": ">=18" 137 | }, 138 | "optionalDependencies": { 139 | "fsevents": "2.3.2" 140 | } 141 | }, 142 | "node_modules/playwright-core": { 143 | "version": "1.50.1", 144 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", 145 | "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", 146 | "dev": true, 147 | "license": "Apache-2.0", 148 | "bin": { 149 | "playwright-core": "cli.js" 150 | }, 151 | "engines": { 152 | "node": ">=18" 153 | } 154 | }, 155 | "node_modules/require-from-string": { 156 | "version": "2.0.2", 157 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 158 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 159 | "license": "MIT", 160 | "engines": { 161 | "node": ">=0.10.0" 162 | } 163 | }, 164 | "node_modules/undici-types": { 165 | "version": "6.20.0", 166 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 167 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", 168 | "dev": true, 169 | "license": "MIT" 170 | }, 171 | "node_modules/zod": { 172 | "version": "3.24.2", 173 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", 174 | "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", 175 | "dev": true, 176 | "license": "MIT", 177 | "funding": { 178 | "url": "https://github.com/sponsors/colinhacks" 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /api-schema-validation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-schema-validation", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "node server.js" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "devDependencies": { 13 | "@faker-js/faker": "^9.4.0", 14 | "@playwright/test": "^1.50.1", 15 | "@types/node": "^22.13.0", 16 | "zod": "^3.24.1" 17 | }, 18 | "dependencies": { 19 | "ajv": "^8.17.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api-schema-validation/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // import path from 'path'; 9 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 10 | 11 | /** 12 | * See https://playwright.dev/docs/test-configuration. 13 | */ 14 | export default defineConfig({ 15 | testDir: "./tests", 16 | /* Run tests in files in parallel */ 17 | fullyParallel: true, 18 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 19 | forbidOnly: !!process.env.CI, 20 | /* Retry on CI only */ 21 | retries: process.env.CI ? 2 : 0, 22 | /* Opt out of parallel tests on CI. */ 23 | workers: process.env.CI ? 1 : undefined, 24 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 25 | reporter: "html", 26 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 27 | use: { 28 | /* Base URL to use in actions like `await page.goto('/')`. */ 29 | baseURL: "http://127.0.0.1:8000", 30 | 31 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 32 | trace: "on-first-retry", 33 | }, 34 | 35 | /* Configure projects for major browsers */ 36 | projects: [ 37 | { 38 | name: "API tests", 39 | }, 40 | ], 41 | 42 | /* Run your local dev server before starting the tests */ 43 | webServer: { 44 | command: "npm run start", 45 | url: "http://127.0.0.1:8000", 46 | reuseExistingServer: !process.env.CI, 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /api-schema-validation/server.js: -------------------------------------------------------------------------------- 1 | const { faker } = require("@faker-js/faker"); 2 | const http = require("node:http"); 3 | 4 | const server = http.createServer({}, (req, res) => { 5 | const url = new URL( 6 | req.url || "/", 7 | "http://localhost:8080" 8 | ); 9 | const id = url.searchParams.get("id"); 10 | 11 | if (url.pathname === "/complex") { 12 | res.writeHead(200, { 13 | "Content-Type": "application/json", 14 | }); 15 | return res.end( 16 | JSON.stringify({ 17 | id: faker.string.uuid(), 18 | name: faker.person.fullName(), 19 | relatedPeople: 20 | Math.random() > 0.5 21 | ? Array.from({ length: 5 }, () => ({ 22 | id: faker.string.uuid(), 23 | name: faker.person.fullName(), 24 | })) 25 | : null, 26 | }) 27 | ); 28 | } 29 | 30 | if (req.method === "GET") { 31 | res.writeHead(200, { 32 | "Content-Type": "application/json", 33 | }); 34 | return res.end( 35 | JSON.stringify({ 36 | id, 37 | name: "Check Lee", 38 | }) 39 | ); 40 | } 41 | 42 | if (req.method === "POST") { 43 | let body = ""; 44 | 45 | req.on("data", (chunk) => { 46 | body += chunk.toString(); 47 | }); 48 | 49 | req.on("end", () => { 50 | let parsedBody; 51 | try { 52 | parsedBody = JSON.parse(body); 53 | console.log(parsedBody); 54 | } catch (e) { 55 | res.writeHead(400, { 56 | "Content-Type": "application/json", 57 | }); 58 | return res.end( 59 | JSON.stringify({ error: "Invalid JSON" }) 60 | ); 61 | } 62 | 63 | res.writeHead(201, { 64 | "Content-Type": "application/json", 65 | }); 66 | return res.end( 67 | JSON.stringify({ 68 | id: faker.string.uuid(), 69 | name: parsedBody.name, 70 | }) 71 | ); 72 | }); 73 | } 74 | }); 75 | 76 | server.listen(8000); 77 | -------------------------------------------------------------------------------- /api-schema-validation/tests/api.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "@playwright/test"; 2 | import { z } from "zod"; 3 | 4 | const id = "9f6beeba-6791-4ee6-a2d8-d0c1730e1cef"; 5 | 6 | test("[Basic] GET API call has a valid response", async ({ 7 | request, 8 | }) => { 9 | const response = await request.get(`/?id=${id}`); 10 | const body = await response.json(); 11 | 12 | await expect(response).toBeOK(); 13 | expect(body).toMatchObject({ 14 | id, 15 | name: "Check Lee", 16 | }); 17 | }); 18 | 19 | test("[Basic] POST API call has a valid response schema", async ({ 20 | request, 21 | }) => { 22 | const name = "Check Lee"; 23 | const response = await request.post(`/`, { 24 | data: { 25 | name, 26 | }, 27 | }); 28 | const body = await response.json(); 29 | 30 | await expect(response).toBeOK(); 31 | 32 | expect(body).toMatchObject({ 33 | name, 34 | id: expect.any(String), 35 | }); 36 | }); 37 | 38 | test("[Complex] API call has a valid response schema", async ({ 39 | request, 40 | }) => { 41 | const response = await request.get(`/complex`); 42 | const body = await response.json(); 43 | await expect(response).toBeOK(); 44 | 45 | const schema = z.object({ 46 | id: z.string(), 47 | name: z.string(), 48 | relatedPeople: z 49 | .array(z.object({ name: z.string(), id: z.string() })) 50 | .or(z.null()), 51 | }); 52 | 53 | expect(() => { 54 | schema.parse(body); 55 | }).not.toThrow(); 56 | 57 | // { 58 | // "id": String, 59 | // "name": String, 60 | // "relatedPeople": [ 61 | // { 62 | // "id": String, 63 | // "name": String 64 | // }, 65 | // ... 66 | // ] or null 67 | // } 68 | }); 69 | -------------------------------------------------------------------------------- /controlling-time/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | -------------------------------------------------------------------------------- /controlling-time/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 75 | 76 | 77 | 78 |
79 | 80 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /controlling-time/demo/timezone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 33 | 34 | 35 |
36 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /controlling-time/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "controlling-time", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "controlling-time", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "http-server": "^14.1.1" 13 | }, 14 | "devDependencies": { 15 | "@playwright/test": "^1.48.1", 16 | "@types/node": "^22.7.5" 17 | } 18 | }, 19 | "node_modules/@playwright/test": { 20 | "version": "1.48.1", 21 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.1.tgz", 22 | "integrity": "sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==", 23 | "dev": true, 24 | "license": "Apache-2.0", 25 | "dependencies": { 26 | "playwright": "1.48.1" 27 | }, 28 | "bin": { 29 | "playwright": "cli.js" 30 | }, 31 | "engines": { 32 | "node": ">=18" 33 | } 34 | }, 35 | "node_modules/@types/node": { 36 | "version": "22.7.5", 37 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", 38 | "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", 39 | "dev": true, 40 | "license": "MIT", 41 | "dependencies": { 42 | "undici-types": "~6.19.2" 43 | } 44 | }, 45 | "node_modules/ansi-styles": { 46 | "version": "4.3.0", 47 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 48 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 49 | "license": "MIT", 50 | "dependencies": { 51 | "color-convert": "^2.0.1" 52 | }, 53 | "engines": { 54 | "node": ">=8" 55 | }, 56 | "funding": { 57 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 58 | } 59 | }, 60 | "node_modules/async": { 61 | "version": "2.6.4", 62 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 63 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 64 | "license": "MIT", 65 | "dependencies": { 66 | "lodash": "^4.17.14" 67 | } 68 | }, 69 | "node_modules/basic-auth": { 70 | "version": "2.0.1", 71 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 72 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 73 | "license": "MIT", 74 | "dependencies": { 75 | "safe-buffer": "5.1.2" 76 | }, 77 | "engines": { 78 | "node": ">= 0.8" 79 | } 80 | }, 81 | "node_modules/call-bind": { 82 | "version": "1.0.7", 83 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 84 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 85 | "license": "MIT", 86 | "dependencies": { 87 | "es-define-property": "^1.0.0", 88 | "es-errors": "^1.3.0", 89 | "function-bind": "^1.1.2", 90 | "get-intrinsic": "^1.2.4", 91 | "set-function-length": "^1.2.1" 92 | }, 93 | "engines": { 94 | "node": ">= 0.4" 95 | }, 96 | "funding": { 97 | "url": "https://github.com/sponsors/ljharb" 98 | } 99 | }, 100 | "node_modules/chalk": { 101 | "version": "4.1.2", 102 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 103 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 104 | "license": "MIT", 105 | "dependencies": { 106 | "ansi-styles": "^4.1.0", 107 | "supports-color": "^7.1.0" 108 | }, 109 | "engines": { 110 | "node": ">=10" 111 | }, 112 | "funding": { 113 | "url": "https://github.com/chalk/chalk?sponsor=1" 114 | } 115 | }, 116 | "node_modules/color-convert": { 117 | "version": "2.0.1", 118 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 119 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 120 | "license": "MIT", 121 | "dependencies": { 122 | "color-name": "~1.1.4" 123 | }, 124 | "engines": { 125 | "node": ">=7.0.0" 126 | } 127 | }, 128 | "node_modules/color-name": { 129 | "version": "1.1.4", 130 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 131 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 132 | "license": "MIT" 133 | }, 134 | "node_modules/corser": { 135 | "version": "2.0.1", 136 | "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", 137 | "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", 138 | "license": "MIT", 139 | "engines": { 140 | "node": ">= 0.4.0" 141 | } 142 | }, 143 | "node_modules/debug": { 144 | "version": "3.2.7", 145 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 146 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 147 | "license": "MIT", 148 | "dependencies": { 149 | "ms": "^2.1.1" 150 | } 151 | }, 152 | "node_modules/define-data-property": { 153 | "version": "1.1.4", 154 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 155 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 156 | "license": "MIT", 157 | "dependencies": { 158 | "es-define-property": "^1.0.0", 159 | "es-errors": "^1.3.0", 160 | "gopd": "^1.0.1" 161 | }, 162 | "engines": { 163 | "node": ">= 0.4" 164 | }, 165 | "funding": { 166 | "url": "https://github.com/sponsors/ljharb" 167 | } 168 | }, 169 | "node_modules/es-define-property": { 170 | "version": "1.0.0", 171 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 172 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 173 | "license": "MIT", 174 | "dependencies": { 175 | "get-intrinsic": "^1.2.4" 176 | }, 177 | "engines": { 178 | "node": ">= 0.4" 179 | } 180 | }, 181 | "node_modules/es-errors": { 182 | "version": "1.3.0", 183 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 184 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 185 | "license": "MIT", 186 | "engines": { 187 | "node": ">= 0.4" 188 | } 189 | }, 190 | "node_modules/eventemitter3": { 191 | "version": "4.0.7", 192 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 193 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", 194 | "license": "MIT" 195 | }, 196 | "node_modules/follow-redirects": { 197 | "version": "1.15.9", 198 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 199 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 200 | "funding": [ 201 | { 202 | "type": "individual", 203 | "url": "https://github.com/sponsors/RubenVerborgh" 204 | } 205 | ], 206 | "license": "MIT", 207 | "engines": { 208 | "node": ">=4.0" 209 | }, 210 | "peerDependenciesMeta": { 211 | "debug": { 212 | "optional": true 213 | } 214 | } 215 | }, 216 | "node_modules/fsevents": { 217 | "version": "2.3.2", 218 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 219 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 220 | "dev": true, 221 | "hasInstallScript": true, 222 | "license": "MIT", 223 | "optional": true, 224 | "os": [ 225 | "darwin" 226 | ], 227 | "engines": { 228 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 229 | } 230 | }, 231 | "node_modules/function-bind": { 232 | "version": "1.1.2", 233 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 234 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 235 | "license": "MIT", 236 | "funding": { 237 | "url": "https://github.com/sponsors/ljharb" 238 | } 239 | }, 240 | "node_modules/get-intrinsic": { 241 | "version": "1.2.4", 242 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 243 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 244 | "license": "MIT", 245 | "dependencies": { 246 | "es-errors": "^1.3.0", 247 | "function-bind": "^1.1.2", 248 | "has-proto": "^1.0.1", 249 | "has-symbols": "^1.0.3", 250 | "hasown": "^2.0.0" 251 | }, 252 | "engines": { 253 | "node": ">= 0.4" 254 | }, 255 | "funding": { 256 | "url": "https://github.com/sponsors/ljharb" 257 | } 258 | }, 259 | "node_modules/gopd": { 260 | "version": "1.0.1", 261 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 262 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 263 | "license": "MIT", 264 | "dependencies": { 265 | "get-intrinsic": "^1.1.3" 266 | }, 267 | "funding": { 268 | "url": "https://github.com/sponsors/ljharb" 269 | } 270 | }, 271 | "node_modules/has-flag": { 272 | "version": "4.0.0", 273 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 274 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 275 | "license": "MIT", 276 | "engines": { 277 | "node": ">=8" 278 | } 279 | }, 280 | "node_modules/has-property-descriptors": { 281 | "version": "1.0.2", 282 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 283 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 284 | "license": "MIT", 285 | "dependencies": { 286 | "es-define-property": "^1.0.0" 287 | }, 288 | "funding": { 289 | "url": "https://github.com/sponsors/ljharb" 290 | } 291 | }, 292 | "node_modules/has-proto": { 293 | "version": "1.0.3", 294 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 295 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 296 | "license": "MIT", 297 | "engines": { 298 | "node": ">= 0.4" 299 | }, 300 | "funding": { 301 | "url": "https://github.com/sponsors/ljharb" 302 | } 303 | }, 304 | "node_modules/has-symbols": { 305 | "version": "1.0.3", 306 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 307 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 308 | "license": "MIT", 309 | "engines": { 310 | "node": ">= 0.4" 311 | }, 312 | "funding": { 313 | "url": "https://github.com/sponsors/ljharb" 314 | } 315 | }, 316 | "node_modules/hasown": { 317 | "version": "2.0.2", 318 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 319 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 320 | "license": "MIT", 321 | "dependencies": { 322 | "function-bind": "^1.1.2" 323 | }, 324 | "engines": { 325 | "node": ">= 0.4" 326 | } 327 | }, 328 | "node_modules/he": { 329 | "version": "1.2.0", 330 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 331 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 332 | "license": "MIT", 333 | "bin": { 334 | "he": "bin/he" 335 | } 336 | }, 337 | "node_modules/html-encoding-sniffer": { 338 | "version": "3.0.0", 339 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", 340 | "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", 341 | "license": "MIT", 342 | "dependencies": { 343 | "whatwg-encoding": "^2.0.0" 344 | }, 345 | "engines": { 346 | "node": ">=12" 347 | } 348 | }, 349 | "node_modules/http-proxy": { 350 | "version": "1.18.1", 351 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", 352 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", 353 | "license": "MIT", 354 | "dependencies": { 355 | "eventemitter3": "^4.0.0", 356 | "follow-redirects": "^1.0.0", 357 | "requires-port": "^1.0.0" 358 | }, 359 | "engines": { 360 | "node": ">=8.0.0" 361 | } 362 | }, 363 | "node_modules/http-server": { 364 | "version": "14.1.1", 365 | "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", 366 | "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", 367 | "license": "MIT", 368 | "dependencies": { 369 | "basic-auth": "^2.0.1", 370 | "chalk": "^4.1.2", 371 | "corser": "^2.0.1", 372 | "he": "^1.2.0", 373 | "html-encoding-sniffer": "^3.0.0", 374 | "http-proxy": "^1.18.1", 375 | "mime": "^1.6.0", 376 | "minimist": "^1.2.6", 377 | "opener": "^1.5.1", 378 | "portfinder": "^1.0.28", 379 | "secure-compare": "3.0.1", 380 | "union": "~0.5.0", 381 | "url-join": "^4.0.1" 382 | }, 383 | "bin": { 384 | "http-server": "bin/http-server" 385 | }, 386 | "engines": { 387 | "node": ">=12" 388 | } 389 | }, 390 | "node_modules/iconv-lite": { 391 | "version": "0.6.3", 392 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 393 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 394 | "license": "MIT", 395 | "dependencies": { 396 | "safer-buffer": ">= 2.1.2 < 3.0.0" 397 | }, 398 | "engines": { 399 | "node": ">=0.10.0" 400 | } 401 | }, 402 | "node_modules/lodash": { 403 | "version": "4.17.21", 404 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 405 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 406 | "license": "MIT" 407 | }, 408 | "node_modules/mime": { 409 | "version": "1.6.0", 410 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 411 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 412 | "license": "MIT", 413 | "bin": { 414 | "mime": "cli.js" 415 | }, 416 | "engines": { 417 | "node": ">=4" 418 | } 419 | }, 420 | "node_modules/minimist": { 421 | "version": "1.2.8", 422 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 423 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 424 | "license": "MIT", 425 | "funding": { 426 | "url": "https://github.com/sponsors/ljharb" 427 | } 428 | }, 429 | "node_modules/mkdirp": { 430 | "version": "0.5.6", 431 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 432 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 433 | "license": "MIT", 434 | "dependencies": { 435 | "minimist": "^1.2.6" 436 | }, 437 | "bin": { 438 | "mkdirp": "bin/cmd.js" 439 | } 440 | }, 441 | "node_modules/ms": { 442 | "version": "2.1.3", 443 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 444 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 445 | "license": "MIT" 446 | }, 447 | "node_modules/object-inspect": { 448 | "version": "1.13.2", 449 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 450 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 451 | "license": "MIT", 452 | "engines": { 453 | "node": ">= 0.4" 454 | }, 455 | "funding": { 456 | "url": "https://github.com/sponsors/ljharb" 457 | } 458 | }, 459 | "node_modules/opener": { 460 | "version": "1.5.2", 461 | "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", 462 | "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", 463 | "license": "(WTFPL OR MIT)", 464 | "bin": { 465 | "opener": "bin/opener-bin.js" 466 | } 467 | }, 468 | "node_modules/playwright": { 469 | "version": "1.48.1", 470 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.1.tgz", 471 | "integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==", 472 | "dev": true, 473 | "license": "Apache-2.0", 474 | "dependencies": { 475 | "playwright-core": "1.48.1" 476 | }, 477 | "bin": { 478 | "playwright": "cli.js" 479 | }, 480 | "engines": { 481 | "node": ">=18" 482 | }, 483 | "optionalDependencies": { 484 | "fsevents": "2.3.2" 485 | } 486 | }, 487 | "node_modules/playwright-core": { 488 | "version": "1.48.1", 489 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.1.tgz", 490 | "integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==", 491 | "dev": true, 492 | "license": "Apache-2.0", 493 | "bin": { 494 | "playwright-core": "cli.js" 495 | }, 496 | "engines": { 497 | "node": ">=18" 498 | } 499 | }, 500 | "node_modules/portfinder": { 501 | "version": "1.0.32", 502 | "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", 503 | "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", 504 | "license": "MIT", 505 | "dependencies": { 506 | "async": "^2.6.4", 507 | "debug": "^3.2.7", 508 | "mkdirp": "^0.5.6" 509 | }, 510 | "engines": { 511 | "node": ">= 0.12.0" 512 | } 513 | }, 514 | "node_modules/qs": { 515 | "version": "6.13.0", 516 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 517 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 518 | "license": "BSD-3-Clause", 519 | "dependencies": { 520 | "side-channel": "^1.0.6" 521 | }, 522 | "engines": { 523 | "node": ">=0.6" 524 | }, 525 | "funding": { 526 | "url": "https://github.com/sponsors/ljharb" 527 | } 528 | }, 529 | "node_modules/requires-port": { 530 | "version": "1.0.0", 531 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 532 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", 533 | "license": "MIT" 534 | }, 535 | "node_modules/safe-buffer": { 536 | "version": "5.1.2", 537 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 538 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 539 | "license": "MIT" 540 | }, 541 | "node_modules/safer-buffer": { 542 | "version": "2.1.2", 543 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 544 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 545 | "license": "MIT" 546 | }, 547 | "node_modules/secure-compare": { 548 | "version": "3.0.1", 549 | "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", 550 | "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", 551 | "license": "MIT" 552 | }, 553 | "node_modules/set-function-length": { 554 | "version": "1.2.2", 555 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 556 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 557 | "license": "MIT", 558 | "dependencies": { 559 | "define-data-property": "^1.1.4", 560 | "es-errors": "^1.3.0", 561 | "function-bind": "^1.1.2", 562 | "get-intrinsic": "^1.2.4", 563 | "gopd": "^1.0.1", 564 | "has-property-descriptors": "^1.0.2" 565 | }, 566 | "engines": { 567 | "node": ">= 0.4" 568 | } 569 | }, 570 | "node_modules/side-channel": { 571 | "version": "1.0.6", 572 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 573 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 574 | "license": "MIT", 575 | "dependencies": { 576 | "call-bind": "^1.0.7", 577 | "es-errors": "^1.3.0", 578 | "get-intrinsic": "^1.2.4", 579 | "object-inspect": "^1.13.1" 580 | }, 581 | "engines": { 582 | "node": ">= 0.4" 583 | }, 584 | "funding": { 585 | "url": "https://github.com/sponsors/ljharb" 586 | } 587 | }, 588 | "node_modules/supports-color": { 589 | "version": "7.2.0", 590 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 591 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 592 | "license": "MIT", 593 | "dependencies": { 594 | "has-flag": "^4.0.0" 595 | }, 596 | "engines": { 597 | "node": ">=8" 598 | } 599 | }, 600 | "node_modules/undici-types": { 601 | "version": "6.19.8", 602 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 603 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 604 | "dev": true, 605 | "license": "MIT" 606 | }, 607 | "node_modules/union": { 608 | "version": "0.5.0", 609 | "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", 610 | "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", 611 | "dependencies": { 612 | "qs": "^6.4.0" 613 | }, 614 | "engines": { 615 | "node": ">= 0.8.0" 616 | } 617 | }, 618 | "node_modules/url-join": { 619 | "version": "4.0.1", 620 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", 621 | "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", 622 | "license": "MIT" 623 | }, 624 | "node_modules/whatwg-encoding": { 625 | "version": "2.0.0", 626 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", 627 | "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", 628 | "license": "MIT", 629 | "dependencies": { 630 | "iconv-lite": "0.6.3" 631 | }, 632 | "engines": { 633 | "node": ">=12" 634 | } 635 | } 636 | } 637 | } 638 | -------------------------------------------------------------------------------- /controlling-time/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "controlling-time", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "npx http-server demo" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "devDependencies": { 13 | "@playwright/test": "^1.48.1", 14 | "@types/node": "^22.7.5" 15 | }, 16 | "dependencies": { 17 | "http-server": "^14.1.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /controlling-time/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test" 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // import path from 'path'; 9 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 10 | 11 | /** 12 | * See https://playwright.dev/docs/test-configuration. 13 | */ 14 | export default defineConfig({ 15 | testDir: "./tests", 16 | /* Run tests in files in parallel */ 17 | fullyParallel: true, 18 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 19 | forbidOnly: !!process.env.CI, 20 | /* Retry on CI only */ 21 | retries: process.env.CI ? 2 : 0, 22 | /* Opt out of parallel tests on CI. */ 23 | workers: process.env.CI ? 1 : undefined, 24 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 25 | reporter: "html", 26 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 27 | use: { 28 | /* Base URL to use in actions like `await page.goto('/')`. */ 29 | baseURL: "http://127.0.0.1:8080", 30 | 31 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 32 | trace: "on-first-retry", 33 | }, 34 | 35 | /* Configure projects for major browsers */ 36 | projects: [ 37 | { 38 | name: "chromium", 39 | use: { ...devices["Desktop Chrome"] }, 40 | }, 41 | ], 42 | 43 | /* Run your local dev server before starting the tests */ 44 | webServer: { 45 | command: "npm run start", 46 | url: "http://127.0.0.1:8080", 47 | reuseExistingServer: !process.env.CI, 48 | }, 49 | }) 50 | -------------------------------------------------------------------------------- /controlling-time/tests/control-time.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "@playwright/test" 2 | 3 | test("control time", async ({ page }) => { 4 | await page.clock.install() 5 | 6 | await page.goto("/") 7 | const button = page.getByRole("button", { name: "Start session" }) 8 | await button.click() 9 | await expect(button).toBeDisabled() 10 | 11 | const timeDisplay = page.getByTestId("elapsed-time") 12 | 13 | await page.clock.fastForward(60_000) 14 | await expect(timeDisplay).toContainText(/3m 5\ds/) 15 | 16 | await page.clock.fastForward(240_000) 17 | await expect(timeDisplay).toBeHidden() 18 | await expect(button).not.toBeDisabled() 19 | }) 20 | -------------------------------------------------------------------------------- /controlling-time/tests/control-timezones.spec.ts: -------------------------------------------------------------------------------- 1 | import test, { expect } from "@playwright/test" 2 | 3 | const TIMEZONES = [ 4 | { 5 | name: "Berlin, Germany", 6 | timezoneId: "Europe/Berlin", 7 | expectedTime: "Friday, October 25, 2024 at 4:00 PM", 8 | }, 9 | { 10 | name: "NYC, USA", 11 | timezoneId: "America/New_York", 12 | expectedTime: "Friday, October 25, 2024 at 10:00 AM", 13 | }, 14 | { 15 | name: "Sydney, Australia", 16 | timezoneId: "Australia/Sydney", 17 | expectedTime: "Saturday, October 26, 2024 at 1:00 AM", 18 | }, 19 | ] 20 | 21 | TIMEZONES.forEach(({ name, timezoneId, expectedTime }) => { 22 | test.describe(`Test from ${name}`, () => { 23 | test.use({ 24 | timezoneId: timezoneId, 25 | }) 26 | 27 | test(`Event time is correct`, async ({ page }) => { 28 | await page.goto("/timezone.html") 29 | await expect(page.getByRole("heading")).toHaveText(expectedTime) 30 | }) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /global-before-after-each/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | -------------------------------------------------------------------------------- /global-before-after-each/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "global-before-after-each", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "global-before-after-each", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@playwright/test": "^1.48.2", 13 | "@types/node": "^22.9.0" 14 | } 15 | }, 16 | "node_modules/@playwright/test": { 17 | "version": "1.48.2", 18 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", 19 | "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", 20 | "dev": true, 21 | "license": "Apache-2.0", 22 | "dependencies": { 23 | "playwright": "1.48.2" 24 | }, 25 | "bin": { 26 | "playwright": "cli.js" 27 | }, 28 | "engines": { 29 | "node": ">=18" 30 | } 31 | }, 32 | "node_modules/@types/node": { 33 | "version": "22.9.0", 34 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", 35 | "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", 36 | "dev": true, 37 | "license": "MIT", 38 | "dependencies": { 39 | "undici-types": "~6.19.8" 40 | } 41 | }, 42 | "node_modules/fsevents": { 43 | "version": "2.3.2", 44 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 45 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 46 | "dev": true, 47 | "hasInstallScript": true, 48 | "license": "MIT", 49 | "optional": true, 50 | "os": [ 51 | "darwin" 52 | ], 53 | "engines": { 54 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 55 | } 56 | }, 57 | "node_modules/playwright": { 58 | "version": "1.48.2", 59 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", 60 | "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", 61 | "dev": true, 62 | "license": "Apache-2.0", 63 | "dependencies": { 64 | "playwright-core": "1.48.2" 65 | }, 66 | "bin": { 67 | "playwright": "cli.js" 68 | }, 69 | "engines": { 70 | "node": ">=18" 71 | }, 72 | "optionalDependencies": { 73 | "fsevents": "2.3.2" 74 | } 75 | }, 76 | "node_modules/playwright-core": { 77 | "version": "1.48.2", 78 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", 79 | "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", 80 | "dev": true, 81 | "license": "Apache-2.0", 82 | "bin": { 83 | "playwright-core": "cli.js" 84 | }, 85 | "engines": { 86 | "node": ">=18" 87 | } 88 | }, 89 | "node_modules/undici-types": { 90 | "version": "6.19.8", 91 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 92 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 93 | "dev": true, 94 | "license": "MIT" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /global-before-after-each/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "global-before-after-each", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": {}, 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "description": "", 10 | "devDependencies": { 11 | "@playwright/test": "^1.48.2", 12 | "@types/node": "^22.9.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /global-before-after-each/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test" 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // import path from 'path'; 9 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 10 | 11 | /** 12 | * See https://playwright.dev/docs/test-configuration. 13 | */ 14 | export default defineConfig({ 15 | testDir: "./tests", 16 | /* Run tests in files in parallel */ 17 | fullyParallel: true, 18 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 19 | forbidOnly: !!process.env.CI, 20 | /* Retry on CI only */ 21 | retries: process.env.CI ? 2 : 0, 22 | /* Opt out of parallel tests on CI. */ 23 | workers: process.env.CI ? 1 : undefined, 24 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 25 | reporter: "html", 26 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 27 | use: { 28 | /* Base URL to use in actions like `await page.goto('/')`. */ 29 | // baseURL: 'http://127.0.0.1:3000', 30 | 31 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 32 | trace: "retain-on-failure", 33 | }, 34 | 35 | /* Configure projects for major browsers */ 36 | projects: [ 37 | { 38 | name: "chromium", 39 | use: { ...devices["Desktop Chrome"] }, 40 | }, 41 | ], 42 | 43 | /* Run your local dev server before starting the tests */ 44 | // webServer: { 45 | // command: 'npm run start', 46 | // url: 'http://127.0.0.1:3000', 47 | // reuseExistingServer: !process.env.CI, 48 | // }, 49 | }) 50 | -------------------------------------------------------------------------------- /global-before-after-each/tests/base.ts: -------------------------------------------------------------------------------- 1 | import { test as base } from "@playwright/test"; 2 | 3 | export const test = base.extend<{ 4 | exceptionLogger: void; 5 | timeLogger: void; 6 | }>({ 7 | page: async ({ page }, use) => { 8 | await use(page); 9 | }, 10 | timeLogger: [ 11 | async ({}, use) => { 12 | // before test 13 | test.info().annotations.push({ 14 | type: "Start", 15 | description: new Date().toISOString(), 16 | }); 17 | 18 | await use(); 19 | 20 | // after test 21 | test.info().annotations.push({ 22 | type: "End", 23 | description: new Date().toISOString(), 24 | }); 25 | }, 26 | { auto: true }, 27 | ], 28 | exceptionLogger: [ 29 | async ({ page }, use) => { 30 | // before test 31 | const errors: Error[] = []; 32 | page.on("pageerror", (error) => errors.push(error)); 33 | 34 | await use(); 35 | 36 | // after test 37 | if (errors.length > 0) { 38 | await test.info().attach("frontend-exceptions", { 39 | body: errors 40 | .map((error) => `${error.message}\n${error.stack}`) 41 | .join("\n-----\n"), 42 | }); 43 | 44 | throw new Error("Something went wrong in JS land"); 45 | } 46 | }, 47 | { auto: true }, 48 | ], 49 | }); 50 | 51 | export { expect } from "@playwright/test"; 52 | -------------------------------------------------------------------------------- /global-before-after-each/tests/example-1.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "./base" 2 | 3 | test("example page loads", async ({ page }) => { 4 | await page.goto(`data:text/html,

Hello world!

`) 5 | await expect(page.getByRole("heading", { name: "Hello World" })).toBeVisible() 6 | }) 7 | -------------------------------------------------------------------------------- /global-before-after-each/tests/example-2.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "./base" 2 | 3 | test("example page loads", async ({ page }) => { 4 | await page.goto( 5 | `data:text/html, 6 |

Hello World

7 | 8 | ` 9 | ) 10 | await expect(page.getByRole("heading", { name: "Hello World" })).toBeVisible() 11 | }) 12 | -------------------------------------------------------------------------------- /parameterized-fixtures/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | -------------------------------------------------------------------------------- /parameterized-fixtures/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parameterized-fixtures", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "parameterized-fixtures", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@playwright/test": "^1.47.1", 13 | "@types/node": "^22.5.5" 14 | } 15 | }, 16 | "node_modules/@playwright/test": { 17 | "version": "1.47.1", 18 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz", 19 | "integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==", 20 | "dev": true, 21 | "license": "Apache-2.0", 22 | "dependencies": { 23 | "playwright": "1.47.1" 24 | }, 25 | "bin": { 26 | "playwright": "cli.js" 27 | }, 28 | "engines": { 29 | "node": ">=18" 30 | } 31 | }, 32 | "node_modules/@types/node": { 33 | "version": "22.5.5", 34 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", 35 | "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", 36 | "dev": true, 37 | "license": "MIT", 38 | "dependencies": { 39 | "undici-types": "~6.19.2" 40 | } 41 | }, 42 | "node_modules/fsevents": { 43 | "version": "2.3.2", 44 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 45 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 46 | "dev": true, 47 | "hasInstallScript": true, 48 | "license": "MIT", 49 | "optional": true, 50 | "os": [ 51 | "darwin" 52 | ], 53 | "engines": { 54 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 55 | } 56 | }, 57 | "node_modules/playwright": { 58 | "version": "1.47.1", 59 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz", 60 | "integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==", 61 | "dev": true, 62 | "license": "Apache-2.0", 63 | "dependencies": { 64 | "playwright-core": "1.47.1" 65 | }, 66 | "bin": { 67 | "playwright": "cli.js" 68 | }, 69 | "engines": { 70 | "node": ">=18" 71 | }, 72 | "optionalDependencies": { 73 | "fsevents": "2.3.2" 74 | } 75 | }, 76 | "node_modules/playwright-core": { 77 | "version": "1.47.1", 78 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz", 79 | "integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==", 80 | "dev": true, 81 | "license": "Apache-2.0", 82 | "bin": { 83 | "playwright-core": "cli.js" 84 | }, 85 | "engines": { 86 | "node": ">=18" 87 | } 88 | }, 89 | "node_modules/undici-types": { 90 | "version": "6.19.8", 91 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 92 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 93 | "dev": true, 94 | "license": "MIT" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /parameterized-fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parameterized-fixtures", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": {}, 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "description": "", 10 | "devDependencies": { 11 | "@playwright/test": "^1.47.1", 12 | "@types/node": "^22.5.5" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /parameterized-fixtures/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test" 2 | import { TestOptions } from "./tests/base" 3 | 4 | /** 5 | * Read environment variables from file. 6 | * https://github.com/motdotla/dotenv 7 | */ 8 | // import dotenv from 'dotenv'; 9 | // import path from 'path'; 10 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 11 | 12 | /** 13 | * See https://playwright.dev/docs/test-configuration. 14 | */ 15 | export default defineConfig({ 16 | testDir: "./tests", 17 | fullyParallel: true, 18 | forbidOnly: !!process.env.CI, 19 | retries: process.env.CI ? 2 : 0, 20 | workers: process.env.CI ? 1 : undefined, 21 | reporter: "html", 22 | use: { 23 | trace: "on-first-retry", 24 | // override the default user 25 | user: { email: "stefan@checklyhq.com", password: "..." } 26 | }, 27 | 28 | projects: [ 29 | { 30 | name: "user-a", 31 | use: { 32 | ...devices["Desktop Chrome"], 33 | // override the default user 34 | user: { email: "stefan@checklyhq.com", password: "..." }, 35 | }, 36 | }, 37 | { 38 | name: "user-b", 39 | use: { 40 | ...devices["Desktop Chrome"], 41 | // override the default user 42 | user: { email: "raccoon@checklyhq.com", password: "..." }, 43 | }, 44 | }, 45 | ], 46 | }) 47 | -------------------------------------------------------------------------------- /parameterized-fixtures/tests/base.ts: -------------------------------------------------------------------------------- 1 | import { test as base } from "@playwright/test" 2 | import { DashboardPage, User } from "./poms/dashboard" 3 | 4 | export type TestOptions = { 5 | user: User 6 | dashboardPage: DashboardPage 7 | } 8 | 9 | export const test = base.extend({ 10 | user: [ 11 | // this is the default user if not specified otherwise 12 | // in playwright.config or the test itself 13 | { email: "stefan@checklyhq.com", password: "..." }, 14 | { option: true }, 15 | ], 16 | 17 | dashboardPage: async ({ page, user }, use) => { 18 | const dashboardPage = new DashboardPage(page, user) 19 | await dashboardPage.login() 20 | await use(dashboardPage) 21 | }, 22 | }) 23 | 24 | export { expect } from "@playwright/test" 25 | -------------------------------------------------------------------------------- /parameterized-fixtures/tests/parameterized-fixtures.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from "./base" 2 | 3 | // override the default user 4 | test.use({ user: { email: "hello@checklyhq.com", password: "" } }) 5 | 6 | test("create check", async ({ dashboardPage }) => { 7 | await dashboardPage.createCheck() 8 | }) 9 | -------------------------------------------------------------------------------- /parameterized-fixtures/tests/poms/dashboard.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page, expect } from "@playwright/test" 2 | 3 | export type User = { 4 | email: string 5 | password: string 6 | } 7 | 8 | export class DashboardPage { 9 | readonly page: Page 10 | readonly user: User 11 | readonly $email: Locator 12 | readonly $password: Locator 13 | readonly $login: Locator 14 | readonly $homeDashboard: Locator 15 | 16 | constructor(page: Page, user: User) { 17 | this.user = user 18 | this.page = page 19 | this.$email = page.getByPlaceholder("yours@example.com") 20 | this.$password = page.getByPlaceholder("your password") 21 | this.$login = page.getByLabel("Log In") 22 | this.$homeDashboard = this.page.getByTestId("home-dashboard-table") 23 | } 24 | 25 | public async login() { 26 | // set your login URL 27 | await this.page.goto("...") 28 | await this.$email.fill(this.user.email) 29 | await this.$password.fill(this.user.password) 30 | await this.$login.click() 31 | await expect(this.$homeDashboard).toBeVisible() 32 | } 33 | 34 | public async createCheck() { 35 | await this.page.getByRole("button", { name: "Create new entity" }).click() 36 | await expect( 37 | this.page.getByRole("heading", { name: "Create from scratch" }) 38 | ).toBeVisible() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | .env 7 | .auth 8 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/.prettierrc: -------------------------------------------------------------------------------- 1 | { "semi": false } 2 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-setup-and-storage-state", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "project-setup-and-storage-state", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@playwright/test": "^1.45.3", 13 | "@types/node": "^22.1.0" 14 | } 15 | }, 16 | "node_modules/@playwright/test": { 17 | "version": "1.45.3", 18 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", 19 | "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", 20 | "dev": true, 21 | "license": "Apache-2.0", 22 | "dependencies": { 23 | "playwright": "1.45.3" 24 | }, 25 | "bin": { 26 | "playwright": "cli.js" 27 | }, 28 | "engines": { 29 | "node": ">=18" 30 | } 31 | }, 32 | "node_modules/@types/node": { 33 | "version": "22.1.0", 34 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", 35 | "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", 36 | "dev": true, 37 | "license": "MIT", 38 | "dependencies": { 39 | "undici-types": "~6.13.0" 40 | } 41 | }, 42 | "node_modules/fsevents": { 43 | "version": "2.3.2", 44 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 45 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 46 | "dev": true, 47 | "hasInstallScript": true, 48 | "license": "MIT", 49 | "optional": true, 50 | "os": [ 51 | "darwin" 52 | ], 53 | "engines": { 54 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 55 | } 56 | }, 57 | "node_modules/playwright": { 58 | "version": "1.45.3", 59 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", 60 | "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", 61 | "dev": true, 62 | "license": "Apache-2.0", 63 | "dependencies": { 64 | "playwright-core": "1.45.3" 65 | }, 66 | "bin": { 67 | "playwright": "cli.js" 68 | }, 69 | "engines": { 70 | "node": ">=18" 71 | }, 72 | "optionalDependencies": { 73 | "fsevents": "2.3.2" 74 | } 75 | }, 76 | "node_modules/playwright-core": { 77 | "version": "1.45.3", 78 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", 79 | "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", 80 | "dev": true, 81 | "license": "Apache-2.0", 82 | "bin": { 83 | "playwright-core": "cli.js" 84 | }, 85 | "engines": { 86 | "node": ">=18" 87 | } 88 | }, 89 | "node_modules/undici-types": { 90 | "version": "6.13.0", 91 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", 92 | "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", 93 | "dev": true, 94 | "license": "MIT" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-setup-and-storage-state", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": {}, 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "description": "", 10 | "devDependencies": { 11 | "@playwright/test": "^1.45.3", 12 | "@types/node": "^22.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test" 2 | /** 3 | * Read environment variables from file. 4 | * https://github.com/motdotla/dotenv 5 | */ 6 | // import dotenv from 'dotenv'; 7 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 8 | 9 | /** 10 | * See https://playwright.dev/docs/test-configuration. 11 | */ 12 | export default defineConfig({ 13 | testDir: "./tests", 14 | /* Run tests in files in parallel */ 15 | fullyParallel: true, 16 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 17 | forbidOnly: !!process.env.CI, 18 | /* Retry on CI only */ 19 | retries: process.env.CI ? 2 : 0, 20 | /* Opt out of parallel tests on CI. */ 21 | workers: process.env.CI ? 1 : undefined, 22 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 23 | reporter: "html", 24 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 25 | use: { 26 | /* Base URL to use in actions like `await page.goto('/')`. */ 27 | // baseURL: 'http://127.0.0.1:3000', 28 | 29 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 30 | trace: "on-first-retry", 31 | }, 32 | 33 | /* Configure projects for major browsers */ 34 | projects: [ 35 | { 36 | name: "setup", 37 | use: { ...devices["Desktop Chrome"] }, 38 | testMatch: /.*\.setup\.ts/, 39 | }, 40 | { 41 | name: "behind-login", 42 | use: { 43 | ...devices["Desktop Chrome"], 44 | storageState: ".auth/user.json", 45 | }, 46 | dependencies: ["setup"], 47 | }, 48 | ], 49 | 50 | /* Run your local dev server before starting the tests */ 51 | // webServer: { 52 | // command: 'npm run start', 53 | // url: 'http://127.0.0.1:3000', 54 | // reuseExistingServer: !process.env.CI, 55 | // }, 56 | }) 57 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/tests/product.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "@playwright/test" 2 | 3 | test("home dashboard loads", async ({ page }) => { 4 | await page.goto("https://app.checklyhq.com") 5 | await expect(page.getByTestId("home-dashboard-table")).toBeVisible() 6 | // more test instructions 7 | // ... 8 | }) 9 | -------------------------------------------------------------------------------- /project-setup-and-storage-state/tests/session.setup.ts: -------------------------------------------------------------------------------- 1 | import { expect, test as setup } from "@playwright/test" 2 | 3 | const AUTH_FILE = ".auth/user.json" 4 | 5 | setup("authenticate", async ({ page }) => { 6 | await page.goto("https://app.checklyhq.com") 7 | await page 8 | .getByPlaceholder("yours@example.com") 9 | .fill(process.env.USER as string) 10 | await page.getByPlaceholder("your password").fill(process.env.PW as string) 11 | await page.getByLabel("Log In").click() 12 | await expect(page.getByLabel("Home")).toBeVisible() 13 | await page.context().storageState({ path: AUTH_FILE }) 14 | }) 15 | -------------------------------------------------------------------------------- /test-step-decorators/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | *-snapshots/ 7 | -------------------------------------------------------------------------------- /test-step-decorators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-step-decorators", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": {}, 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "description": "", 10 | "devDependencies": { 11 | "@playwright/test": "^1.46.1", 12 | "@types/node": "^22.4.1", 13 | "checkly": "latest", 14 | "ts-node": "latest", 15 | "typescript": "latest" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test-step-decorators/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test" 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 9 | 10 | /** 11 | * See https://playwright.dev/docs/test-configuration. 12 | */ 13 | export default defineConfig({ 14 | testDir: "./tests", 15 | /* Run tests in files in parallel */ 16 | fullyParallel: true, 17 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 18 | forbidOnly: !!process.env.CI, 19 | /* Retry on CI only */ 20 | retries: process.env.CI ? 2 : 0, 21 | /* Opt out of parallel tests on CI. */ 22 | workers: process.env.CI ? 1 : undefined, 23 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 24 | reporter: "html", 25 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 26 | use: { 27 | /* Base URL to use in actions like `await page.goto('/')`. */ 28 | // baseURL: 'http://127.0.0.1:3000', 29 | 30 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 31 | trace: "on-first-retry", 32 | }, 33 | 34 | /* Configure projects for major browsers */ 35 | projects: [ 36 | { 37 | name: "chromium", 38 | use: { ...devices["Desktop Chrome"] }, 39 | }, 40 | 41 | // { 42 | // name: 'firefox', 43 | // use: { ...devices['Desktop Firefox'] }, 44 | // }, 45 | 46 | // { 47 | // name: 'webkit', 48 | // use: { ...devices['Desktop Safari'] }, 49 | // }, 50 | 51 | /* Test against mobile viewports. */ 52 | // { 53 | // name: 'Mobile Chrome', 54 | // use: { ...devices['Pixel 5'] }, 55 | // }, 56 | // { 57 | // name: 'Mobile Safari', 58 | // use: { ...devices['iPhone 12'] }, 59 | // }, 60 | 61 | /* Test against branded browsers. */ 62 | // { 63 | // name: 'Microsoft Edge', 64 | // use: { ...devices['Desktop Edge'], channel: 'msedge' }, 65 | // }, 66 | // { 67 | // name: 'Google Chrome', 68 | // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, 69 | // }, 70 | ], 71 | 72 | /* Run your local dev server before starting the tests */ 73 | // webServer: { 74 | // command: 'npm run start', 75 | // url: 'http://127.0.0.1:3000', 76 | // reuseExistingServer: !process.env.CI, 77 | // }, 78 | }) 79 | -------------------------------------------------------------------------------- /test-step-decorators/tests/base.ts: -------------------------------------------------------------------------------- 1 | import { test as base } from "@playwright/test" 2 | import { PlaywrightPage } from "./poms/playwright-page" 3 | 4 | export const test = base.extend<{ playwrightPage: PlaywrightPage }>({ 5 | playwrightPage: async ({ page }, use) => { 6 | const playwrightPage = new PlaywrightPage(page) 7 | await use(playwrightPage) 8 | }, 9 | }) 10 | 11 | export { expect } from "@playwright/test" 12 | 13 | /** 14 | * Decorator function for wrapping POM methods in a test.step. 15 | * 16 | * Use it without a step name `@step()`. 17 | * 18 | * Or with a step name `@step("Search something")`. 19 | * 20 | * @param stepName - The name of the test step. 21 | * @returns A decorator function that can be used to decorate test methods. 22 | */ 23 | export function step(stepName?: string) { 24 | return function decorator( 25 | target: Function, 26 | context: ClassMethodDecoratorContext 27 | ) { 28 | return function replacementMethod(...args: any) { 29 | const name = `${stepName || (context.name as string)} (${this.name})` 30 | return test.step(name, async () => { 31 | return await target.call(this, ...args) 32 | }) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test-step-decorators/tests/docs.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from "./base" 2 | 3 | test("base features work", async ({ playwrightPage }) => { 4 | await playwrightPage.goto() 5 | await playwrightPage.search("getting started") 6 | await playwrightPage.openGitHub() 7 | await playwrightPage.toggleDarkMode() 8 | }) 9 | -------------------------------------------------------------------------------- /test-step-decorators/tests/poms/playwright-page.ts: -------------------------------------------------------------------------------- 1 | import { expect, type Locator, type Page } from "@playwright/test" 2 | import { step } from "../base" 3 | 4 | export class PlaywrightPage { 5 | public name = "Playwright Page POM" 6 | readonly page: Page 7 | readonly searchBtn: Locator 8 | readonly searchInput: Locator 9 | readonly gitHubLink: Locator 10 | readonly themeSwitch: Locator 11 | 12 | constructor(page: Page, name = "Playwright Page POM") { 13 | this.name = name 14 | this.page = page 15 | this.searchBtn = page.getByLabel("Search") 16 | this.searchInput = page.getByPlaceholder("Search docs") 17 | this.gitHubLink = page.getByLabel("GitHub repository") 18 | this.themeSwitch = page.getByLabel("Switch between dark and light") 19 | } 20 | 21 | async goto() { 22 | await this.page.goto("https://playwright.dev") 23 | } 24 | 25 | @step('Search for "Writing tests"') 26 | async search(query) { 27 | await this.searchBtn.click() 28 | await this.searchInput.fill(query) 29 | await this.page.getByRole("link", { name: "Writing tests" }).click() 30 | await this.page.getByRole("heading", { name: "Writing tests" }).click() 31 | } 32 | 33 | @step("Check GitHub link") 34 | async openGitHub() { 35 | const gitHubPromise = this.page.waitForEvent("popup") 36 | await this.gitHubLink.click() 37 | const gitHubPage = await gitHubPromise 38 | await expect(gitHubPage).toHaveURL(/github.com/) 39 | } 40 | 41 | @step("Toggle dark mode") 42 | async toggleDarkMode() { 43 | await this.themeSwitch.click() 44 | await expect(this.page).toHaveScreenshot("dark-mode.png") 45 | await this.themeSwitch.click() 46 | await expect(this.page).toHaveScreenshot("light-mode.png") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /type-check-and-lint/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | -------------------------------------------------------------------------------- /type-check-and-lint/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import eslint from "@eslint/js" 4 | import tseslint from "typescript-eslint" 5 | 6 | export default tseslint.config( 7 | eslint.configs.recommended, 8 | ...tseslint.configs.recommended 9 | // { 10 | // languageOptions: { 11 | // parserOptions: { 12 | // project: true, 13 | // tsconfigRootDir: ".", 14 | // }, 15 | // }, 16 | // rules: { 17 | // "@typescript-eslint/no-floating-promises": "error", 18 | // "@typescript-eslint/await-thenable": "error", 19 | // }, 20 | // } 21 | ) 22 | -------------------------------------------------------------------------------- /type-check-and-lint/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwt-playwright-type-check-and-lint", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "pwt-playwright-type-check-and-lint", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@eslint/js": "^9.7.0", 13 | "@playwright/test": "^1.45.3", 14 | "@types/eslint__js": "^8.42.3", 15 | "@types/node": "^20.14.11", 16 | "eslint": "^8.57.0", 17 | "typescript": "^5.5.4", 18 | "typescript-eslint": "^7.17.0" 19 | } 20 | }, 21 | "node_modules/@eslint-community/eslint-utils": { 22 | "version": "4.4.0", 23 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 24 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 25 | "dev": true, 26 | "license": "MIT", 27 | "dependencies": { 28 | "eslint-visitor-keys": "^3.3.0" 29 | }, 30 | "engines": { 31 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 32 | }, 33 | "peerDependencies": { 34 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 35 | } 36 | }, 37 | "node_modules/@eslint-community/regexpp": { 38 | "version": "4.11.0", 39 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", 40 | "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", 41 | "dev": true, 42 | "license": "MIT", 43 | "engines": { 44 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 45 | } 46 | }, 47 | "node_modules/@eslint/eslintrc": { 48 | "version": "2.1.4", 49 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", 50 | "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", 51 | "dev": true, 52 | "license": "MIT", 53 | "dependencies": { 54 | "ajv": "^6.12.4", 55 | "debug": "^4.3.2", 56 | "espree": "^9.6.0", 57 | "globals": "^13.19.0", 58 | "ignore": "^5.2.0", 59 | "import-fresh": "^3.2.1", 60 | "js-yaml": "^4.1.0", 61 | "minimatch": "^3.1.2", 62 | "strip-json-comments": "^3.1.1" 63 | }, 64 | "engines": { 65 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 66 | }, 67 | "funding": { 68 | "url": "https://opencollective.com/eslint" 69 | } 70 | }, 71 | "node_modules/@eslint/js": { 72 | "version": "9.7.0", 73 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", 74 | "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", 75 | "dev": true, 76 | "license": "MIT", 77 | "engines": { 78 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 79 | } 80 | }, 81 | "node_modules/@humanwhocodes/config-array": { 82 | "version": "0.11.14", 83 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", 84 | "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", 85 | "deprecated": "Use @eslint/config-array instead", 86 | "dev": true, 87 | "license": "Apache-2.0", 88 | "dependencies": { 89 | "@humanwhocodes/object-schema": "^2.0.2", 90 | "debug": "^4.3.1", 91 | "minimatch": "^3.0.5" 92 | }, 93 | "engines": { 94 | "node": ">=10.10.0" 95 | } 96 | }, 97 | "node_modules/@humanwhocodes/module-importer": { 98 | "version": "1.0.1", 99 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 100 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 101 | "dev": true, 102 | "license": "Apache-2.0", 103 | "engines": { 104 | "node": ">=12.22" 105 | }, 106 | "funding": { 107 | "type": "github", 108 | "url": "https://github.com/sponsors/nzakas" 109 | } 110 | }, 111 | "node_modules/@humanwhocodes/object-schema": { 112 | "version": "2.0.3", 113 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", 114 | "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", 115 | "deprecated": "Use @eslint/object-schema instead", 116 | "dev": true, 117 | "license": "BSD-3-Clause" 118 | }, 119 | "node_modules/@nodelib/fs.scandir": { 120 | "version": "2.1.5", 121 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 122 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 123 | "dev": true, 124 | "license": "MIT", 125 | "dependencies": { 126 | "@nodelib/fs.stat": "2.0.5", 127 | "run-parallel": "^1.1.9" 128 | }, 129 | "engines": { 130 | "node": ">= 8" 131 | } 132 | }, 133 | "node_modules/@nodelib/fs.stat": { 134 | "version": "2.0.5", 135 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 136 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 137 | "dev": true, 138 | "license": "MIT", 139 | "engines": { 140 | "node": ">= 8" 141 | } 142 | }, 143 | "node_modules/@nodelib/fs.walk": { 144 | "version": "1.2.8", 145 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 146 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 147 | "dev": true, 148 | "license": "MIT", 149 | "dependencies": { 150 | "@nodelib/fs.scandir": "2.1.5", 151 | "fastq": "^1.6.0" 152 | }, 153 | "engines": { 154 | "node": ">= 8" 155 | } 156 | }, 157 | "node_modules/@playwright/test": { 158 | "version": "1.45.3", 159 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", 160 | "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", 161 | "dev": true, 162 | "license": "Apache-2.0", 163 | "dependencies": { 164 | "playwright": "1.45.3" 165 | }, 166 | "bin": { 167 | "playwright": "cli.js" 168 | }, 169 | "engines": { 170 | "node": ">=18" 171 | } 172 | }, 173 | "node_modules/@types/eslint": { 174 | "version": "9.6.0", 175 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", 176 | "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", 177 | "dev": true, 178 | "license": "MIT", 179 | "dependencies": { 180 | "@types/estree": "*", 181 | "@types/json-schema": "*" 182 | } 183 | }, 184 | "node_modules/@types/eslint__js": { 185 | "version": "8.42.3", 186 | "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", 187 | "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", 188 | "dev": true, 189 | "license": "MIT", 190 | "dependencies": { 191 | "@types/eslint": "*" 192 | } 193 | }, 194 | "node_modules/@types/estree": { 195 | "version": "1.0.5", 196 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 197 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 198 | "dev": true, 199 | "license": "MIT" 200 | }, 201 | "node_modules/@types/json-schema": { 202 | "version": "7.0.15", 203 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 204 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 205 | "dev": true, 206 | "license": "MIT" 207 | }, 208 | "node_modules/@types/node": { 209 | "version": "20.14.11", 210 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", 211 | "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", 212 | "dev": true, 213 | "license": "MIT", 214 | "dependencies": { 215 | "undici-types": "~5.26.4" 216 | } 217 | }, 218 | "node_modules/@typescript-eslint/eslint-plugin": { 219 | "version": "7.17.0", 220 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", 221 | "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", 222 | "dev": true, 223 | "license": "MIT", 224 | "dependencies": { 225 | "@eslint-community/regexpp": "^4.10.0", 226 | "@typescript-eslint/scope-manager": "7.17.0", 227 | "@typescript-eslint/type-utils": "7.17.0", 228 | "@typescript-eslint/utils": "7.17.0", 229 | "@typescript-eslint/visitor-keys": "7.17.0", 230 | "graphemer": "^1.4.0", 231 | "ignore": "^5.3.1", 232 | "natural-compare": "^1.4.0", 233 | "ts-api-utils": "^1.3.0" 234 | }, 235 | "engines": { 236 | "node": "^18.18.0 || >=20.0.0" 237 | }, 238 | "funding": { 239 | "type": "opencollective", 240 | "url": "https://opencollective.com/typescript-eslint" 241 | }, 242 | "peerDependencies": { 243 | "@typescript-eslint/parser": "^7.0.0", 244 | "eslint": "^8.56.0" 245 | }, 246 | "peerDependenciesMeta": { 247 | "typescript": { 248 | "optional": true 249 | } 250 | } 251 | }, 252 | "node_modules/@typescript-eslint/parser": { 253 | "version": "7.17.0", 254 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", 255 | "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", 256 | "dev": true, 257 | "license": "BSD-2-Clause", 258 | "dependencies": { 259 | "@typescript-eslint/scope-manager": "7.17.0", 260 | "@typescript-eslint/types": "7.17.0", 261 | "@typescript-eslint/typescript-estree": "7.17.0", 262 | "@typescript-eslint/visitor-keys": "7.17.0", 263 | "debug": "^4.3.4" 264 | }, 265 | "engines": { 266 | "node": "^18.18.0 || >=20.0.0" 267 | }, 268 | "funding": { 269 | "type": "opencollective", 270 | "url": "https://opencollective.com/typescript-eslint" 271 | }, 272 | "peerDependencies": { 273 | "eslint": "^8.56.0" 274 | }, 275 | "peerDependenciesMeta": { 276 | "typescript": { 277 | "optional": true 278 | } 279 | } 280 | }, 281 | "node_modules/@typescript-eslint/scope-manager": { 282 | "version": "7.17.0", 283 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", 284 | "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", 285 | "dev": true, 286 | "license": "MIT", 287 | "dependencies": { 288 | "@typescript-eslint/types": "7.17.0", 289 | "@typescript-eslint/visitor-keys": "7.17.0" 290 | }, 291 | "engines": { 292 | "node": "^18.18.0 || >=20.0.0" 293 | }, 294 | "funding": { 295 | "type": "opencollective", 296 | "url": "https://opencollective.com/typescript-eslint" 297 | } 298 | }, 299 | "node_modules/@typescript-eslint/type-utils": { 300 | "version": "7.17.0", 301 | "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", 302 | "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", 303 | "dev": true, 304 | "license": "MIT", 305 | "dependencies": { 306 | "@typescript-eslint/typescript-estree": "7.17.0", 307 | "@typescript-eslint/utils": "7.17.0", 308 | "debug": "^4.3.4", 309 | "ts-api-utils": "^1.3.0" 310 | }, 311 | "engines": { 312 | "node": "^18.18.0 || >=20.0.0" 313 | }, 314 | "funding": { 315 | "type": "opencollective", 316 | "url": "https://opencollective.com/typescript-eslint" 317 | }, 318 | "peerDependencies": { 319 | "eslint": "^8.56.0" 320 | }, 321 | "peerDependenciesMeta": { 322 | "typescript": { 323 | "optional": true 324 | } 325 | } 326 | }, 327 | "node_modules/@typescript-eslint/types": { 328 | "version": "7.17.0", 329 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", 330 | "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", 331 | "dev": true, 332 | "license": "MIT", 333 | "engines": { 334 | "node": "^18.18.0 || >=20.0.0" 335 | }, 336 | "funding": { 337 | "type": "opencollective", 338 | "url": "https://opencollective.com/typescript-eslint" 339 | } 340 | }, 341 | "node_modules/@typescript-eslint/typescript-estree": { 342 | "version": "7.17.0", 343 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", 344 | "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", 345 | "dev": true, 346 | "license": "BSD-2-Clause", 347 | "dependencies": { 348 | "@typescript-eslint/types": "7.17.0", 349 | "@typescript-eslint/visitor-keys": "7.17.0", 350 | "debug": "^4.3.4", 351 | "globby": "^11.1.0", 352 | "is-glob": "^4.0.3", 353 | "minimatch": "^9.0.4", 354 | "semver": "^7.6.0", 355 | "ts-api-utils": "^1.3.0" 356 | }, 357 | "engines": { 358 | "node": "^18.18.0 || >=20.0.0" 359 | }, 360 | "funding": { 361 | "type": "opencollective", 362 | "url": "https://opencollective.com/typescript-eslint" 363 | }, 364 | "peerDependenciesMeta": { 365 | "typescript": { 366 | "optional": true 367 | } 368 | } 369 | }, 370 | "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { 371 | "version": "2.0.1", 372 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 373 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 374 | "dev": true, 375 | "license": "MIT", 376 | "dependencies": { 377 | "balanced-match": "^1.0.0" 378 | } 379 | }, 380 | "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { 381 | "version": "9.0.5", 382 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", 383 | "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 384 | "dev": true, 385 | "license": "ISC", 386 | "dependencies": { 387 | "brace-expansion": "^2.0.1" 388 | }, 389 | "engines": { 390 | "node": ">=16 || 14 >=14.17" 391 | }, 392 | "funding": { 393 | "url": "https://github.com/sponsors/isaacs" 394 | } 395 | }, 396 | "node_modules/@typescript-eslint/utils": { 397 | "version": "7.17.0", 398 | "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", 399 | "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", 400 | "dev": true, 401 | "license": "MIT", 402 | "dependencies": { 403 | "@eslint-community/eslint-utils": "^4.4.0", 404 | "@typescript-eslint/scope-manager": "7.17.0", 405 | "@typescript-eslint/types": "7.17.0", 406 | "@typescript-eslint/typescript-estree": "7.17.0" 407 | }, 408 | "engines": { 409 | "node": "^18.18.0 || >=20.0.0" 410 | }, 411 | "funding": { 412 | "type": "opencollective", 413 | "url": "https://opencollective.com/typescript-eslint" 414 | }, 415 | "peerDependencies": { 416 | "eslint": "^8.56.0" 417 | } 418 | }, 419 | "node_modules/@typescript-eslint/visitor-keys": { 420 | "version": "7.17.0", 421 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", 422 | "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", 423 | "dev": true, 424 | "license": "MIT", 425 | "dependencies": { 426 | "@typescript-eslint/types": "7.17.0", 427 | "eslint-visitor-keys": "^3.4.3" 428 | }, 429 | "engines": { 430 | "node": "^18.18.0 || >=20.0.0" 431 | }, 432 | "funding": { 433 | "type": "opencollective", 434 | "url": "https://opencollective.com/typescript-eslint" 435 | } 436 | }, 437 | "node_modules/@ungap/structured-clone": { 438 | "version": "1.2.0", 439 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", 440 | "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", 441 | "dev": true, 442 | "license": "ISC" 443 | }, 444 | "node_modules/acorn": { 445 | "version": "8.12.1", 446 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", 447 | "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", 448 | "dev": true, 449 | "license": "MIT", 450 | "bin": { 451 | "acorn": "bin/acorn" 452 | }, 453 | "engines": { 454 | "node": ">=0.4.0" 455 | } 456 | }, 457 | "node_modules/acorn-jsx": { 458 | "version": "5.3.2", 459 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 460 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 461 | "dev": true, 462 | "license": "MIT", 463 | "peerDependencies": { 464 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 465 | } 466 | }, 467 | "node_modules/ajv": { 468 | "version": "6.12.6", 469 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 470 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 471 | "dev": true, 472 | "license": "MIT", 473 | "dependencies": { 474 | "fast-deep-equal": "^3.1.1", 475 | "fast-json-stable-stringify": "^2.0.0", 476 | "json-schema-traverse": "^0.4.1", 477 | "uri-js": "^4.2.2" 478 | }, 479 | "funding": { 480 | "type": "github", 481 | "url": "https://github.com/sponsors/epoberezkin" 482 | } 483 | }, 484 | "node_modules/ansi-regex": { 485 | "version": "5.0.1", 486 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 487 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 488 | "dev": true, 489 | "license": "MIT", 490 | "engines": { 491 | "node": ">=8" 492 | } 493 | }, 494 | "node_modules/ansi-styles": { 495 | "version": "4.3.0", 496 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 497 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 498 | "dev": true, 499 | "license": "MIT", 500 | "dependencies": { 501 | "color-convert": "^2.0.1" 502 | }, 503 | "engines": { 504 | "node": ">=8" 505 | }, 506 | "funding": { 507 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 508 | } 509 | }, 510 | "node_modules/argparse": { 511 | "version": "2.0.1", 512 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 513 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 514 | "dev": true, 515 | "license": "Python-2.0" 516 | }, 517 | "node_modules/array-union": { 518 | "version": "2.1.0", 519 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 520 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 521 | "dev": true, 522 | "license": "MIT", 523 | "engines": { 524 | "node": ">=8" 525 | } 526 | }, 527 | "node_modules/balanced-match": { 528 | "version": "1.0.2", 529 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 530 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 531 | "dev": true, 532 | "license": "MIT" 533 | }, 534 | "node_modules/brace-expansion": { 535 | "version": "1.1.11", 536 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 537 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 538 | "dev": true, 539 | "license": "MIT", 540 | "dependencies": { 541 | "balanced-match": "^1.0.0", 542 | "concat-map": "0.0.1" 543 | } 544 | }, 545 | "node_modules/braces": { 546 | "version": "3.0.3", 547 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 548 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 549 | "dev": true, 550 | "license": "MIT", 551 | "dependencies": { 552 | "fill-range": "^7.1.1" 553 | }, 554 | "engines": { 555 | "node": ">=8" 556 | } 557 | }, 558 | "node_modules/callsites": { 559 | "version": "3.1.0", 560 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 561 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 562 | "dev": true, 563 | "license": "MIT", 564 | "engines": { 565 | "node": ">=6" 566 | } 567 | }, 568 | "node_modules/chalk": { 569 | "version": "4.1.2", 570 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 571 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 572 | "dev": true, 573 | "license": "MIT", 574 | "dependencies": { 575 | "ansi-styles": "^4.1.0", 576 | "supports-color": "^7.1.0" 577 | }, 578 | "engines": { 579 | "node": ">=10" 580 | }, 581 | "funding": { 582 | "url": "https://github.com/chalk/chalk?sponsor=1" 583 | } 584 | }, 585 | "node_modules/color-convert": { 586 | "version": "2.0.1", 587 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 588 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 589 | "dev": true, 590 | "license": "MIT", 591 | "dependencies": { 592 | "color-name": "~1.1.4" 593 | }, 594 | "engines": { 595 | "node": ">=7.0.0" 596 | } 597 | }, 598 | "node_modules/color-name": { 599 | "version": "1.1.4", 600 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 601 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 602 | "dev": true, 603 | "license": "MIT" 604 | }, 605 | "node_modules/concat-map": { 606 | "version": "0.0.1", 607 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 608 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 609 | "dev": true, 610 | "license": "MIT" 611 | }, 612 | "node_modules/cross-spawn": { 613 | "version": "7.0.3", 614 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 615 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 616 | "dev": true, 617 | "license": "MIT", 618 | "dependencies": { 619 | "path-key": "^3.1.0", 620 | "shebang-command": "^2.0.0", 621 | "which": "^2.0.1" 622 | }, 623 | "engines": { 624 | "node": ">= 8" 625 | } 626 | }, 627 | "node_modules/debug": { 628 | "version": "4.3.5", 629 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 630 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 631 | "dev": true, 632 | "license": "MIT", 633 | "dependencies": { 634 | "ms": "2.1.2" 635 | }, 636 | "engines": { 637 | "node": ">=6.0" 638 | }, 639 | "peerDependenciesMeta": { 640 | "supports-color": { 641 | "optional": true 642 | } 643 | } 644 | }, 645 | "node_modules/deep-is": { 646 | "version": "0.1.4", 647 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 648 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 649 | "dev": true, 650 | "license": "MIT" 651 | }, 652 | "node_modules/dir-glob": { 653 | "version": "3.0.1", 654 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 655 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 656 | "dev": true, 657 | "license": "MIT", 658 | "dependencies": { 659 | "path-type": "^4.0.0" 660 | }, 661 | "engines": { 662 | "node": ">=8" 663 | } 664 | }, 665 | "node_modules/doctrine": { 666 | "version": "3.0.0", 667 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 668 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 669 | "dev": true, 670 | "license": "Apache-2.0", 671 | "dependencies": { 672 | "esutils": "^2.0.2" 673 | }, 674 | "engines": { 675 | "node": ">=6.0.0" 676 | } 677 | }, 678 | "node_modules/escape-string-regexp": { 679 | "version": "4.0.0", 680 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 681 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 682 | "dev": true, 683 | "license": "MIT", 684 | "engines": { 685 | "node": ">=10" 686 | }, 687 | "funding": { 688 | "url": "https://github.com/sponsors/sindresorhus" 689 | } 690 | }, 691 | "node_modules/eslint": { 692 | "version": "8.57.0", 693 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", 694 | "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", 695 | "dev": true, 696 | "license": "MIT", 697 | "dependencies": { 698 | "@eslint-community/eslint-utils": "^4.2.0", 699 | "@eslint-community/regexpp": "^4.6.1", 700 | "@eslint/eslintrc": "^2.1.4", 701 | "@eslint/js": "8.57.0", 702 | "@humanwhocodes/config-array": "^0.11.14", 703 | "@humanwhocodes/module-importer": "^1.0.1", 704 | "@nodelib/fs.walk": "^1.2.8", 705 | "@ungap/structured-clone": "^1.2.0", 706 | "ajv": "^6.12.4", 707 | "chalk": "^4.0.0", 708 | "cross-spawn": "^7.0.2", 709 | "debug": "^4.3.2", 710 | "doctrine": "^3.0.0", 711 | "escape-string-regexp": "^4.0.0", 712 | "eslint-scope": "^7.2.2", 713 | "eslint-visitor-keys": "^3.4.3", 714 | "espree": "^9.6.1", 715 | "esquery": "^1.4.2", 716 | "esutils": "^2.0.2", 717 | "fast-deep-equal": "^3.1.3", 718 | "file-entry-cache": "^6.0.1", 719 | "find-up": "^5.0.0", 720 | "glob-parent": "^6.0.2", 721 | "globals": "^13.19.0", 722 | "graphemer": "^1.4.0", 723 | "ignore": "^5.2.0", 724 | "imurmurhash": "^0.1.4", 725 | "is-glob": "^4.0.0", 726 | "is-path-inside": "^3.0.3", 727 | "js-yaml": "^4.1.0", 728 | "json-stable-stringify-without-jsonify": "^1.0.1", 729 | "levn": "^0.4.1", 730 | "lodash.merge": "^4.6.2", 731 | "minimatch": "^3.1.2", 732 | "natural-compare": "^1.4.0", 733 | "optionator": "^0.9.3", 734 | "strip-ansi": "^6.0.1", 735 | "text-table": "^0.2.0" 736 | }, 737 | "bin": { 738 | "eslint": "bin/eslint.js" 739 | }, 740 | "engines": { 741 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 742 | }, 743 | "funding": { 744 | "url": "https://opencollective.com/eslint" 745 | } 746 | }, 747 | "node_modules/eslint-scope": { 748 | "version": "7.2.2", 749 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", 750 | "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", 751 | "dev": true, 752 | "license": "BSD-2-Clause", 753 | "dependencies": { 754 | "esrecurse": "^4.3.0", 755 | "estraverse": "^5.2.0" 756 | }, 757 | "engines": { 758 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 759 | }, 760 | "funding": { 761 | "url": "https://opencollective.com/eslint" 762 | } 763 | }, 764 | "node_modules/eslint-visitor-keys": { 765 | "version": "3.4.3", 766 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 767 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 768 | "dev": true, 769 | "license": "Apache-2.0", 770 | "engines": { 771 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 772 | }, 773 | "funding": { 774 | "url": "https://opencollective.com/eslint" 775 | } 776 | }, 777 | "node_modules/eslint/node_modules/@eslint/js": { 778 | "version": "8.57.0", 779 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", 780 | "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", 781 | "dev": true, 782 | "license": "MIT", 783 | "engines": { 784 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 785 | } 786 | }, 787 | "node_modules/espree": { 788 | "version": "9.6.1", 789 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", 790 | "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", 791 | "dev": true, 792 | "license": "BSD-2-Clause", 793 | "dependencies": { 794 | "acorn": "^8.9.0", 795 | "acorn-jsx": "^5.3.2", 796 | "eslint-visitor-keys": "^3.4.1" 797 | }, 798 | "engines": { 799 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 800 | }, 801 | "funding": { 802 | "url": "https://opencollective.com/eslint" 803 | } 804 | }, 805 | "node_modules/esquery": { 806 | "version": "1.6.0", 807 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", 808 | "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 809 | "dev": true, 810 | "license": "BSD-3-Clause", 811 | "dependencies": { 812 | "estraverse": "^5.1.0" 813 | }, 814 | "engines": { 815 | "node": ">=0.10" 816 | } 817 | }, 818 | "node_modules/esrecurse": { 819 | "version": "4.3.0", 820 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 821 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 822 | "dev": true, 823 | "license": "BSD-2-Clause", 824 | "dependencies": { 825 | "estraverse": "^5.2.0" 826 | }, 827 | "engines": { 828 | "node": ">=4.0" 829 | } 830 | }, 831 | "node_modules/estraverse": { 832 | "version": "5.3.0", 833 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 834 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 835 | "dev": true, 836 | "license": "BSD-2-Clause", 837 | "engines": { 838 | "node": ">=4.0" 839 | } 840 | }, 841 | "node_modules/esutils": { 842 | "version": "2.0.3", 843 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 844 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 845 | "dev": true, 846 | "license": "BSD-2-Clause", 847 | "engines": { 848 | "node": ">=0.10.0" 849 | } 850 | }, 851 | "node_modules/fast-deep-equal": { 852 | "version": "3.1.3", 853 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 854 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 855 | "dev": true, 856 | "license": "MIT" 857 | }, 858 | "node_modules/fast-glob": { 859 | "version": "3.3.2", 860 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 861 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 862 | "dev": true, 863 | "license": "MIT", 864 | "dependencies": { 865 | "@nodelib/fs.stat": "^2.0.2", 866 | "@nodelib/fs.walk": "^1.2.3", 867 | "glob-parent": "^5.1.2", 868 | "merge2": "^1.3.0", 869 | "micromatch": "^4.0.4" 870 | }, 871 | "engines": { 872 | "node": ">=8.6.0" 873 | } 874 | }, 875 | "node_modules/fast-glob/node_modules/glob-parent": { 876 | "version": "5.1.2", 877 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 878 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 879 | "dev": true, 880 | "license": "ISC", 881 | "dependencies": { 882 | "is-glob": "^4.0.1" 883 | }, 884 | "engines": { 885 | "node": ">= 6" 886 | } 887 | }, 888 | "node_modules/fast-json-stable-stringify": { 889 | "version": "2.1.0", 890 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 891 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 892 | "dev": true, 893 | "license": "MIT" 894 | }, 895 | "node_modules/fast-levenshtein": { 896 | "version": "2.0.6", 897 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 898 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 899 | "dev": true, 900 | "license": "MIT" 901 | }, 902 | "node_modules/fastq": { 903 | "version": "1.17.1", 904 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 905 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 906 | "dev": true, 907 | "license": "ISC", 908 | "dependencies": { 909 | "reusify": "^1.0.4" 910 | } 911 | }, 912 | "node_modules/file-entry-cache": { 913 | "version": "6.0.1", 914 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 915 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 916 | "dev": true, 917 | "license": "MIT", 918 | "dependencies": { 919 | "flat-cache": "^3.0.4" 920 | }, 921 | "engines": { 922 | "node": "^10.12.0 || >=12.0.0" 923 | } 924 | }, 925 | "node_modules/fill-range": { 926 | "version": "7.1.1", 927 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 928 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 929 | "dev": true, 930 | "license": "MIT", 931 | "dependencies": { 932 | "to-regex-range": "^5.0.1" 933 | }, 934 | "engines": { 935 | "node": ">=8" 936 | } 937 | }, 938 | "node_modules/find-up": { 939 | "version": "5.0.0", 940 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 941 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 942 | "dev": true, 943 | "license": "MIT", 944 | "dependencies": { 945 | "locate-path": "^6.0.0", 946 | "path-exists": "^4.0.0" 947 | }, 948 | "engines": { 949 | "node": ">=10" 950 | }, 951 | "funding": { 952 | "url": "https://github.com/sponsors/sindresorhus" 953 | } 954 | }, 955 | "node_modules/flat-cache": { 956 | "version": "3.2.0", 957 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", 958 | "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", 959 | "dev": true, 960 | "license": "MIT", 961 | "dependencies": { 962 | "flatted": "^3.2.9", 963 | "keyv": "^4.5.3", 964 | "rimraf": "^3.0.2" 965 | }, 966 | "engines": { 967 | "node": "^10.12.0 || >=12.0.0" 968 | } 969 | }, 970 | "node_modules/flatted": { 971 | "version": "3.3.1", 972 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", 973 | "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", 974 | "dev": true, 975 | "license": "ISC" 976 | }, 977 | "node_modules/fs.realpath": { 978 | "version": "1.0.0", 979 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 980 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 981 | "dev": true, 982 | "license": "ISC" 983 | }, 984 | "node_modules/fsevents": { 985 | "version": "2.3.2", 986 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 987 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 988 | "dev": true, 989 | "hasInstallScript": true, 990 | "license": "MIT", 991 | "optional": true, 992 | "os": [ 993 | "darwin" 994 | ], 995 | "engines": { 996 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 997 | } 998 | }, 999 | "node_modules/glob": { 1000 | "version": "7.2.3", 1001 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1002 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1003 | "deprecated": "Glob versions prior to v9 are no longer supported", 1004 | "dev": true, 1005 | "license": "ISC", 1006 | "dependencies": { 1007 | "fs.realpath": "^1.0.0", 1008 | "inflight": "^1.0.4", 1009 | "inherits": "2", 1010 | "minimatch": "^3.1.1", 1011 | "once": "^1.3.0", 1012 | "path-is-absolute": "^1.0.0" 1013 | }, 1014 | "engines": { 1015 | "node": "*" 1016 | }, 1017 | "funding": { 1018 | "url": "https://github.com/sponsors/isaacs" 1019 | } 1020 | }, 1021 | "node_modules/glob-parent": { 1022 | "version": "6.0.2", 1023 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1024 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1025 | "dev": true, 1026 | "license": "ISC", 1027 | "dependencies": { 1028 | "is-glob": "^4.0.3" 1029 | }, 1030 | "engines": { 1031 | "node": ">=10.13.0" 1032 | } 1033 | }, 1034 | "node_modules/globals": { 1035 | "version": "13.24.0", 1036 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", 1037 | "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", 1038 | "dev": true, 1039 | "license": "MIT", 1040 | "dependencies": { 1041 | "type-fest": "^0.20.2" 1042 | }, 1043 | "engines": { 1044 | "node": ">=8" 1045 | }, 1046 | "funding": { 1047 | "url": "https://github.com/sponsors/sindresorhus" 1048 | } 1049 | }, 1050 | "node_modules/globby": { 1051 | "version": "11.1.0", 1052 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 1053 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 1054 | "dev": true, 1055 | "license": "MIT", 1056 | "dependencies": { 1057 | "array-union": "^2.1.0", 1058 | "dir-glob": "^3.0.1", 1059 | "fast-glob": "^3.2.9", 1060 | "ignore": "^5.2.0", 1061 | "merge2": "^1.4.1", 1062 | "slash": "^3.0.0" 1063 | }, 1064 | "engines": { 1065 | "node": ">=10" 1066 | }, 1067 | "funding": { 1068 | "url": "https://github.com/sponsors/sindresorhus" 1069 | } 1070 | }, 1071 | "node_modules/graphemer": { 1072 | "version": "1.4.0", 1073 | "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 1074 | "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 1075 | "dev": true, 1076 | "license": "MIT" 1077 | }, 1078 | "node_modules/has-flag": { 1079 | "version": "4.0.0", 1080 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1081 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1082 | "dev": true, 1083 | "license": "MIT", 1084 | "engines": { 1085 | "node": ">=8" 1086 | } 1087 | }, 1088 | "node_modules/ignore": { 1089 | "version": "5.3.1", 1090 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 1091 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 1092 | "dev": true, 1093 | "license": "MIT", 1094 | "engines": { 1095 | "node": ">= 4" 1096 | } 1097 | }, 1098 | "node_modules/import-fresh": { 1099 | "version": "3.3.0", 1100 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1101 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1102 | "dev": true, 1103 | "license": "MIT", 1104 | "dependencies": { 1105 | "parent-module": "^1.0.0", 1106 | "resolve-from": "^4.0.0" 1107 | }, 1108 | "engines": { 1109 | "node": ">=6" 1110 | }, 1111 | "funding": { 1112 | "url": "https://github.com/sponsors/sindresorhus" 1113 | } 1114 | }, 1115 | "node_modules/imurmurhash": { 1116 | "version": "0.1.4", 1117 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1118 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 1119 | "dev": true, 1120 | "license": "MIT", 1121 | "engines": { 1122 | "node": ">=0.8.19" 1123 | } 1124 | }, 1125 | "node_modules/inflight": { 1126 | "version": "1.0.6", 1127 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1128 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1129 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 1130 | "dev": true, 1131 | "license": "ISC", 1132 | "dependencies": { 1133 | "once": "^1.3.0", 1134 | "wrappy": "1" 1135 | } 1136 | }, 1137 | "node_modules/inherits": { 1138 | "version": "2.0.4", 1139 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1140 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1141 | "dev": true, 1142 | "license": "ISC" 1143 | }, 1144 | "node_modules/is-extglob": { 1145 | "version": "2.1.1", 1146 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1147 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1148 | "dev": true, 1149 | "license": "MIT", 1150 | "engines": { 1151 | "node": ">=0.10.0" 1152 | } 1153 | }, 1154 | "node_modules/is-glob": { 1155 | "version": "4.0.3", 1156 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1157 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1158 | "dev": true, 1159 | "license": "MIT", 1160 | "dependencies": { 1161 | "is-extglob": "^2.1.1" 1162 | }, 1163 | "engines": { 1164 | "node": ">=0.10.0" 1165 | } 1166 | }, 1167 | "node_modules/is-number": { 1168 | "version": "7.0.0", 1169 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1170 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1171 | "dev": true, 1172 | "license": "MIT", 1173 | "engines": { 1174 | "node": ">=0.12.0" 1175 | } 1176 | }, 1177 | "node_modules/is-path-inside": { 1178 | "version": "3.0.3", 1179 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 1180 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 1181 | "dev": true, 1182 | "license": "MIT", 1183 | "engines": { 1184 | "node": ">=8" 1185 | } 1186 | }, 1187 | "node_modules/isexe": { 1188 | "version": "2.0.0", 1189 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1190 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1191 | "dev": true, 1192 | "license": "ISC" 1193 | }, 1194 | "node_modules/js-yaml": { 1195 | "version": "4.1.0", 1196 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1197 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1198 | "dev": true, 1199 | "license": "MIT", 1200 | "dependencies": { 1201 | "argparse": "^2.0.1" 1202 | }, 1203 | "bin": { 1204 | "js-yaml": "bin/js-yaml.js" 1205 | } 1206 | }, 1207 | "node_modules/json-buffer": { 1208 | "version": "3.0.1", 1209 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 1210 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 1211 | "dev": true, 1212 | "license": "MIT" 1213 | }, 1214 | "node_modules/json-schema-traverse": { 1215 | "version": "0.4.1", 1216 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1217 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1218 | "dev": true, 1219 | "license": "MIT" 1220 | }, 1221 | "node_modules/json-stable-stringify-without-jsonify": { 1222 | "version": "1.0.1", 1223 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1224 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 1225 | "dev": true, 1226 | "license": "MIT" 1227 | }, 1228 | "node_modules/keyv": { 1229 | "version": "4.5.4", 1230 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 1231 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 1232 | "dev": true, 1233 | "license": "MIT", 1234 | "dependencies": { 1235 | "json-buffer": "3.0.1" 1236 | } 1237 | }, 1238 | "node_modules/levn": { 1239 | "version": "0.4.1", 1240 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1241 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1242 | "dev": true, 1243 | "license": "MIT", 1244 | "dependencies": { 1245 | "prelude-ls": "^1.2.1", 1246 | "type-check": "~0.4.0" 1247 | }, 1248 | "engines": { 1249 | "node": ">= 0.8.0" 1250 | } 1251 | }, 1252 | "node_modules/locate-path": { 1253 | "version": "6.0.0", 1254 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1255 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1256 | "dev": true, 1257 | "license": "MIT", 1258 | "dependencies": { 1259 | "p-locate": "^5.0.0" 1260 | }, 1261 | "engines": { 1262 | "node": ">=10" 1263 | }, 1264 | "funding": { 1265 | "url": "https://github.com/sponsors/sindresorhus" 1266 | } 1267 | }, 1268 | "node_modules/lodash.merge": { 1269 | "version": "4.6.2", 1270 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1271 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1272 | "dev": true, 1273 | "license": "MIT" 1274 | }, 1275 | "node_modules/merge2": { 1276 | "version": "1.4.1", 1277 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1278 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1279 | "dev": true, 1280 | "license": "MIT", 1281 | "engines": { 1282 | "node": ">= 8" 1283 | } 1284 | }, 1285 | "node_modules/micromatch": { 1286 | "version": "4.0.7", 1287 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", 1288 | "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", 1289 | "dev": true, 1290 | "license": "MIT", 1291 | "dependencies": { 1292 | "braces": "^3.0.3", 1293 | "picomatch": "^2.3.1" 1294 | }, 1295 | "engines": { 1296 | "node": ">=8.6" 1297 | } 1298 | }, 1299 | "node_modules/minimatch": { 1300 | "version": "3.1.2", 1301 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1302 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1303 | "dev": true, 1304 | "license": "ISC", 1305 | "dependencies": { 1306 | "brace-expansion": "^1.1.7" 1307 | }, 1308 | "engines": { 1309 | "node": "*" 1310 | } 1311 | }, 1312 | "node_modules/ms": { 1313 | "version": "2.1.2", 1314 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1315 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1316 | "dev": true, 1317 | "license": "MIT" 1318 | }, 1319 | "node_modules/natural-compare": { 1320 | "version": "1.4.0", 1321 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1322 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 1323 | "dev": true, 1324 | "license": "MIT" 1325 | }, 1326 | "node_modules/once": { 1327 | "version": "1.4.0", 1328 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1329 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1330 | "dev": true, 1331 | "license": "ISC", 1332 | "dependencies": { 1333 | "wrappy": "1" 1334 | } 1335 | }, 1336 | "node_modules/optionator": { 1337 | "version": "0.9.4", 1338 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 1339 | "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 1340 | "dev": true, 1341 | "license": "MIT", 1342 | "dependencies": { 1343 | "deep-is": "^0.1.3", 1344 | "fast-levenshtein": "^2.0.6", 1345 | "levn": "^0.4.1", 1346 | "prelude-ls": "^1.2.1", 1347 | "type-check": "^0.4.0", 1348 | "word-wrap": "^1.2.5" 1349 | }, 1350 | "engines": { 1351 | "node": ">= 0.8.0" 1352 | } 1353 | }, 1354 | "node_modules/p-limit": { 1355 | "version": "3.1.0", 1356 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1357 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1358 | "dev": true, 1359 | "license": "MIT", 1360 | "dependencies": { 1361 | "yocto-queue": "^0.1.0" 1362 | }, 1363 | "engines": { 1364 | "node": ">=10" 1365 | }, 1366 | "funding": { 1367 | "url": "https://github.com/sponsors/sindresorhus" 1368 | } 1369 | }, 1370 | "node_modules/p-locate": { 1371 | "version": "5.0.0", 1372 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1373 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1374 | "dev": true, 1375 | "license": "MIT", 1376 | "dependencies": { 1377 | "p-limit": "^3.0.2" 1378 | }, 1379 | "engines": { 1380 | "node": ">=10" 1381 | }, 1382 | "funding": { 1383 | "url": "https://github.com/sponsors/sindresorhus" 1384 | } 1385 | }, 1386 | "node_modules/parent-module": { 1387 | "version": "1.0.1", 1388 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1389 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1390 | "dev": true, 1391 | "license": "MIT", 1392 | "dependencies": { 1393 | "callsites": "^3.0.0" 1394 | }, 1395 | "engines": { 1396 | "node": ">=6" 1397 | } 1398 | }, 1399 | "node_modules/path-exists": { 1400 | "version": "4.0.0", 1401 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1402 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1403 | "dev": true, 1404 | "license": "MIT", 1405 | "engines": { 1406 | "node": ">=8" 1407 | } 1408 | }, 1409 | "node_modules/path-is-absolute": { 1410 | "version": "1.0.1", 1411 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1412 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1413 | "dev": true, 1414 | "license": "MIT", 1415 | "engines": { 1416 | "node": ">=0.10.0" 1417 | } 1418 | }, 1419 | "node_modules/path-key": { 1420 | "version": "3.1.1", 1421 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1422 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1423 | "dev": true, 1424 | "license": "MIT", 1425 | "engines": { 1426 | "node": ">=8" 1427 | } 1428 | }, 1429 | "node_modules/path-type": { 1430 | "version": "4.0.0", 1431 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1432 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1433 | "dev": true, 1434 | "license": "MIT", 1435 | "engines": { 1436 | "node": ">=8" 1437 | } 1438 | }, 1439 | "node_modules/picomatch": { 1440 | "version": "2.3.1", 1441 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1442 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1443 | "dev": true, 1444 | "license": "MIT", 1445 | "engines": { 1446 | "node": ">=8.6" 1447 | }, 1448 | "funding": { 1449 | "url": "https://github.com/sponsors/jonschlinkert" 1450 | } 1451 | }, 1452 | "node_modules/playwright": { 1453 | "version": "1.45.3", 1454 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", 1455 | "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", 1456 | "dev": true, 1457 | "license": "Apache-2.0", 1458 | "dependencies": { 1459 | "playwright-core": "1.45.3" 1460 | }, 1461 | "bin": { 1462 | "playwright": "cli.js" 1463 | }, 1464 | "engines": { 1465 | "node": ">=18" 1466 | }, 1467 | "optionalDependencies": { 1468 | "fsevents": "2.3.2" 1469 | } 1470 | }, 1471 | "node_modules/playwright-core": { 1472 | "version": "1.45.3", 1473 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", 1474 | "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", 1475 | "dev": true, 1476 | "license": "Apache-2.0", 1477 | "bin": { 1478 | "playwright-core": "cli.js" 1479 | }, 1480 | "engines": { 1481 | "node": ">=18" 1482 | } 1483 | }, 1484 | "node_modules/prelude-ls": { 1485 | "version": "1.2.1", 1486 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1487 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1488 | "dev": true, 1489 | "license": "MIT", 1490 | "engines": { 1491 | "node": ">= 0.8.0" 1492 | } 1493 | }, 1494 | "node_modules/punycode": { 1495 | "version": "2.3.1", 1496 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1497 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1498 | "dev": true, 1499 | "license": "MIT", 1500 | "engines": { 1501 | "node": ">=6" 1502 | } 1503 | }, 1504 | "node_modules/queue-microtask": { 1505 | "version": "1.2.3", 1506 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1507 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1508 | "dev": true, 1509 | "funding": [ 1510 | { 1511 | "type": "github", 1512 | "url": "https://github.com/sponsors/feross" 1513 | }, 1514 | { 1515 | "type": "patreon", 1516 | "url": "https://www.patreon.com/feross" 1517 | }, 1518 | { 1519 | "type": "consulting", 1520 | "url": "https://feross.org/support" 1521 | } 1522 | ], 1523 | "license": "MIT" 1524 | }, 1525 | "node_modules/resolve-from": { 1526 | "version": "4.0.0", 1527 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1528 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1529 | "dev": true, 1530 | "license": "MIT", 1531 | "engines": { 1532 | "node": ">=4" 1533 | } 1534 | }, 1535 | "node_modules/reusify": { 1536 | "version": "1.0.4", 1537 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1538 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1539 | "dev": true, 1540 | "license": "MIT", 1541 | "engines": { 1542 | "iojs": ">=1.0.0", 1543 | "node": ">=0.10.0" 1544 | } 1545 | }, 1546 | "node_modules/rimraf": { 1547 | "version": "3.0.2", 1548 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1549 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1550 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 1551 | "dev": true, 1552 | "license": "ISC", 1553 | "dependencies": { 1554 | "glob": "^7.1.3" 1555 | }, 1556 | "bin": { 1557 | "rimraf": "bin.js" 1558 | }, 1559 | "funding": { 1560 | "url": "https://github.com/sponsors/isaacs" 1561 | } 1562 | }, 1563 | "node_modules/run-parallel": { 1564 | "version": "1.2.0", 1565 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1566 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1567 | "dev": true, 1568 | "funding": [ 1569 | { 1570 | "type": "github", 1571 | "url": "https://github.com/sponsors/feross" 1572 | }, 1573 | { 1574 | "type": "patreon", 1575 | "url": "https://www.patreon.com/feross" 1576 | }, 1577 | { 1578 | "type": "consulting", 1579 | "url": "https://feross.org/support" 1580 | } 1581 | ], 1582 | "license": "MIT", 1583 | "dependencies": { 1584 | "queue-microtask": "^1.2.2" 1585 | } 1586 | }, 1587 | "node_modules/semver": { 1588 | "version": "7.6.3", 1589 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 1590 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 1591 | "dev": true, 1592 | "license": "ISC", 1593 | "bin": { 1594 | "semver": "bin/semver.js" 1595 | }, 1596 | "engines": { 1597 | "node": ">=10" 1598 | } 1599 | }, 1600 | "node_modules/shebang-command": { 1601 | "version": "2.0.0", 1602 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1603 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1604 | "dev": true, 1605 | "license": "MIT", 1606 | "dependencies": { 1607 | "shebang-regex": "^3.0.0" 1608 | }, 1609 | "engines": { 1610 | "node": ">=8" 1611 | } 1612 | }, 1613 | "node_modules/shebang-regex": { 1614 | "version": "3.0.0", 1615 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1616 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1617 | "dev": true, 1618 | "license": "MIT", 1619 | "engines": { 1620 | "node": ">=8" 1621 | } 1622 | }, 1623 | "node_modules/slash": { 1624 | "version": "3.0.0", 1625 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1626 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 1627 | "dev": true, 1628 | "license": "MIT", 1629 | "engines": { 1630 | "node": ">=8" 1631 | } 1632 | }, 1633 | "node_modules/strip-ansi": { 1634 | "version": "6.0.1", 1635 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1636 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1637 | "dev": true, 1638 | "license": "MIT", 1639 | "dependencies": { 1640 | "ansi-regex": "^5.0.1" 1641 | }, 1642 | "engines": { 1643 | "node": ">=8" 1644 | } 1645 | }, 1646 | "node_modules/strip-json-comments": { 1647 | "version": "3.1.1", 1648 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1649 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1650 | "dev": true, 1651 | "license": "MIT", 1652 | "engines": { 1653 | "node": ">=8" 1654 | }, 1655 | "funding": { 1656 | "url": "https://github.com/sponsors/sindresorhus" 1657 | } 1658 | }, 1659 | "node_modules/supports-color": { 1660 | "version": "7.2.0", 1661 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1662 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1663 | "dev": true, 1664 | "license": "MIT", 1665 | "dependencies": { 1666 | "has-flag": "^4.0.0" 1667 | }, 1668 | "engines": { 1669 | "node": ">=8" 1670 | } 1671 | }, 1672 | "node_modules/text-table": { 1673 | "version": "0.2.0", 1674 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1675 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 1676 | "dev": true, 1677 | "license": "MIT" 1678 | }, 1679 | "node_modules/to-regex-range": { 1680 | "version": "5.0.1", 1681 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1682 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1683 | "dev": true, 1684 | "license": "MIT", 1685 | "dependencies": { 1686 | "is-number": "^7.0.0" 1687 | }, 1688 | "engines": { 1689 | "node": ">=8.0" 1690 | } 1691 | }, 1692 | "node_modules/ts-api-utils": { 1693 | "version": "1.3.0", 1694 | "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", 1695 | "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", 1696 | "dev": true, 1697 | "license": "MIT", 1698 | "engines": { 1699 | "node": ">=16" 1700 | }, 1701 | "peerDependencies": { 1702 | "typescript": ">=4.2.0" 1703 | } 1704 | }, 1705 | "node_modules/type-check": { 1706 | "version": "0.4.0", 1707 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1708 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1709 | "dev": true, 1710 | "license": "MIT", 1711 | "dependencies": { 1712 | "prelude-ls": "^1.2.1" 1713 | }, 1714 | "engines": { 1715 | "node": ">= 0.8.0" 1716 | } 1717 | }, 1718 | "node_modules/type-fest": { 1719 | "version": "0.20.2", 1720 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 1721 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 1722 | "dev": true, 1723 | "license": "(MIT OR CC0-1.0)", 1724 | "engines": { 1725 | "node": ">=10" 1726 | }, 1727 | "funding": { 1728 | "url": "https://github.com/sponsors/sindresorhus" 1729 | } 1730 | }, 1731 | "node_modules/typescript": { 1732 | "version": "5.5.4", 1733 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", 1734 | "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", 1735 | "dev": true, 1736 | "license": "Apache-2.0", 1737 | "bin": { 1738 | "tsc": "bin/tsc", 1739 | "tsserver": "bin/tsserver" 1740 | }, 1741 | "engines": { 1742 | "node": ">=14.17" 1743 | } 1744 | }, 1745 | "node_modules/typescript-eslint": { 1746 | "version": "7.17.0", 1747 | "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.17.0.tgz", 1748 | "integrity": "sha512-spQxsQvPguduCUfyUvLItvKqK3l8KJ/kqs5Pb/URtzQ5AC53Z6us32St37rpmlt2uESG23lOFpV4UErrmy4dZQ==", 1749 | "dev": true, 1750 | "license": "MIT", 1751 | "dependencies": { 1752 | "@typescript-eslint/eslint-plugin": "7.17.0", 1753 | "@typescript-eslint/parser": "7.17.0", 1754 | "@typescript-eslint/utils": "7.17.0" 1755 | }, 1756 | "engines": { 1757 | "node": "^18.18.0 || >=20.0.0" 1758 | }, 1759 | "funding": { 1760 | "type": "opencollective", 1761 | "url": "https://opencollective.com/typescript-eslint" 1762 | }, 1763 | "peerDependencies": { 1764 | "eslint": "^8.56.0" 1765 | }, 1766 | "peerDependenciesMeta": { 1767 | "typescript": { 1768 | "optional": true 1769 | } 1770 | } 1771 | }, 1772 | "node_modules/undici-types": { 1773 | "version": "5.26.5", 1774 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1775 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 1776 | "dev": true, 1777 | "license": "MIT" 1778 | }, 1779 | "node_modules/uri-js": { 1780 | "version": "4.4.1", 1781 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1782 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1783 | "dev": true, 1784 | "license": "BSD-2-Clause", 1785 | "dependencies": { 1786 | "punycode": "^2.1.0" 1787 | } 1788 | }, 1789 | "node_modules/which": { 1790 | "version": "2.0.2", 1791 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1792 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1793 | "dev": true, 1794 | "license": "ISC", 1795 | "dependencies": { 1796 | "isexe": "^2.0.0" 1797 | }, 1798 | "bin": { 1799 | "node-which": "bin/node-which" 1800 | }, 1801 | "engines": { 1802 | "node": ">= 8" 1803 | } 1804 | }, 1805 | "node_modules/word-wrap": { 1806 | "version": "1.2.5", 1807 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 1808 | "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 1809 | "dev": true, 1810 | "license": "MIT", 1811 | "engines": { 1812 | "node": ">=0.10.0" 1813 | } 1814 | }, 1815 | "node_modules/wrappy": { 1816 | "version": "1.0.2", 1817 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1818 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1819 | "dev": true, 1820 | "license": "ISC" 1821 | }, 1822 | "node_modules/yocto-queue": { 1823 | "version": "0.1.0", 1824 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1825 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1826 | "dev": true, 1827 | "license": "MIT", 1828 | "engines": { 1829 | "node": ">=10" 1830 | }, 1831 | "funding": { 1832 | "url": "https://github.com/sponsors/sindresorhus" 1833 | } 1834 | } 1835 | } 1836 | } 1837 | -------------------------------------------------------------------------------- /type-check-and-lint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwt-playwright-type-check-and-lint", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "pretest": "tsc --noEmit && eslint tests/**", 6 | "test": "playwright test" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "description": "", 12 | "devDependencies": { 13 | "@eslint/js": "^9.7.0", 14 | "@playwright/test": "^1.45.3", 15 | "@types/eslint__js": "^8.42.3", 16 | "@types/node": "^20.14.11", 17 | "eslint": "^8.57.0", 18 | "typescript": "^5.5.4", 19 | "typescript-eslint": "^7.17.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /type-check-and-lint/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 9 | 10 | /** 11 | * See https://playwright.dev/docs/test-configuration. 12 | */ 13 | export default defineConfig({ 14 | testDir: './tests', 15 | /* Run tests in files in parallel */ 16 | fullyParallel: true, 17 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 18 | forbidOnly: !!process.env.CI, 19 | /* Retry on CI only */ 20 | retries: process.env.CI ? 2 : 0, 21 | /* Opt out of parallel tests on CI. */ 22 | workers: process.env.CI ? 1 : undefined, 23 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 24 | reporter: 'html', 25 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 26 | use: { 27 | /* Base URL to use in actions like `await page.goto('/')`. */ 28 | // baseURL: 'http://127.0.0.1:3000', 29 | 30 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 31 | trace: 'on-first-retry', 32 | }, 33 | 34 | /* Configure projects for major browsers */ 35 | projects: [ 36 | { 37 | name: 'chromium', 38 | use: { ...devices['Desktop Chrome'] }, 39 | }, 40 | 41 | // { 42 | // name: 'firefox', 43 | // use: { ...devices['Desktop Firefox'] }, 44 | // }, 45 | 46 | // { 47 | // name: 'webkit', 48 | // use: { ...devices['Desktop Safari'] }, 49 | // }, 50 | 51 | /* Test against mobile viewports. */ 52 | // { 53 | // name: 'Mobile Chrome', 54 | // use: { ...devices['Pixel 5'] }, 55 | // }, 56 | // { 57 | // name: 'Mobile Safari', 58 | // use: { ...devices['iPhone 12'] }, 59 | // }, 60 | 61 | /* Test against branded browsers. */ 62 | // { 63 | // name: 'Microsoft Edge', 64 | // use: { ...devices['Desktop Edge'], channel: 'msedge' }, 65 | // }, 66 | // { 67 | // name: 'Google Chrome', 68 | // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, 69 | // }, 70 | ], 71 | 72 | /* Run your local dev server before starting the tests */ 73 | // webServer: { 74 | // command: 'npm run start', 75 | // url: 'http://127.0.0.1:3000', 76 | // reuseExistingServer: !process.env.CI, 77 | // }, 78 | }); 79 | -------------------------------------------------------------------------------- /type-check-and-lint/tests/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "@playwright/test" 2 | 3 | test("test with a type error", async ({ page }) => { 4 | await page.goto("https://playwright.dev/") 5 | 6 | await expect( 7 | page.getByRole("heading", { name: "Installation" }) 8 | ).toBeVisible() 9 | }) 10 | 11 | test.skip("test with incorrect promise handling", async ({ page }) => { 12 | await page.goto("https://playwright.dev/") 13 | 14 | const button = await page.getByRole("link", { name: "Get started" }) 15 | button.click() 16 | }) 17 | -------------------------------------------------------------------------------- /type-check-and-lint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs" /* Specify what module code is generated. */, 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 63 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 64 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 65 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 66 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 67 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 68 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 69 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 70 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 71 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 72 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 73 | 74 | /* Interop Constraints */ 75 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 76 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 77 | // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ 78 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 79 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 80 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 81 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 82 | 83 | /* Type Checking */ 84 | "strict": true /* Enable all strict type-checking options. */, 85 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 86 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 87 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 88 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 89 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 90 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 91 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 92 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 93 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 94 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 95 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 96 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 97 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 98 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 99 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 100 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 101 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 102 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 103 | 104 | /* Completeness */ 105 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 106 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 107 | } 108 | } 109 | --------------------------------------------------------------------------------