├── .github ├── dependabot.yml └── workflows │ └── playwright.yml ├── .gitignore ├── CONTRIBUTING.md ├── LINKEDIN_POST.md ├── README.md ├── package-lock.json ├── package.json ├── v1.49.0-examples ├── README.md ├── new-features.spec.ts └── playwright.config.ts ├── v1.50.0-examples ├── README.md ├── new-features.spec.ts └── playwright.config.ts ├── v1.51.0-examples ├── README.md ├── new-features.spec.ts └── playwright.config.ts └── v1.52.0-examples ├── README.md ├── new-features.spec.ts └── playwright.config.ts /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | open-pull-requests-limit: 10 8 | versioning-strategy: increase 9 | labels: 10 | - "dependencies" 11 | - "automerge" 12 | commit-message: 13 | prefix: "npm" 14 | include: "scope" 15 | 16 | - package-ecosystem: "github-actions" 17 | directory: "/" 18 | schedule: 19 | interval: "weekly" 20 | open-pull-requests-limit: 5 21 | labels: 22 | - "dependencies" 23 | - "github_actions" 24 | - "automerge" -------------------------------------------------------------------------------- /.github/workflows/playwright.yml: -------------------------------------------------------------------------------- 1 | name: Playwright Tests 2 | 3 | on: 4 | push: 5 | branches: [ main, master ] 6 | pull_request: 7 | branches: [ main, master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | name: 'Playwright Tests' 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | version: ['v1.49.0-examples', 'v1.50.0-examples', 'v1.51.0-examples', 'v1.52.0-examples'] 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: actions/setup-node@v4 24 | with: 25 | node-version: 18 26 | 27 | - name: Install dependencies 28 | run: npm ci 29 | 30 | - name: Install Playwright Browsers 31 | run: npx playwright install --with-deps 32 | 33 | - name: Run Playwright tests for ${{ matrix.version }} 34 | run: npx playwright test ${{ matrix.version }} --reporter=html 35 | 36 | - name: Ensure report directory exists 37 | if: always() 38 | run: mkdir -p playwright-report 39 | 40 | - uses: actions/upload-artifact@v4 41 | if: always() 42 | with: 43 | name: playwright-report-${{ matrix.version }} 44 | path: playwright-report/ 45 | retention-days: 30 46 | if-no-files-found: warn 47 | 48 | build-status: 49 | name: 'Test Status' 50 | runs-on: ubuntu-latest 51 | needs: test 52 | if: always() 53 | steps: 54 | - name: Success 55 | if: ${{ needs.test.result == 'success' }} 56 | run: exit 0 57 | 58 | - name: Failure 59 | if: ${{ needs.test.result != 'success' }} 60 | run: exit 1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node dependencies 2 | node_modules/ 3 | 4 | # Playwright specific 5 | /test-results/ 6 | /playwright-report/ 7 | /blob-report/ 8 | /playwright/.cache/ 9 | 10 | # TypeScript/JavaScript 11 | /dist/ 12 | /build/ 13 | /coverage/ 14 | *.tsbuildinfo 15 | 16 | # Environment and secrets 17 | .env 18 | .env.local 19 | .env.*.local 20 | .DS_Store 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | # Editor directories and files 26 | .idea/ 27 | .vscode/* 28 | !.vscode/extensions.json 29 | !.vscode/settings.json 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # OS files 37 | .DS_Store 38 | Thumbs.db 39 | desktop.ini 40 | 41 | # Debug logs 42 | logs 43 | *.log 44 | npm-debug.log* 45 | yarn-debug.log* 46 | yarn-error.log* 47 | pnpm-debug.log* 48 | lerna-debug.log* 49 | 50 | # Playwright outputs 51 | **/screenshots/ 52 | **/videos/ 53 | **/traces/ 54 | **/.auth/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Playwright Release Notes Examples 2 | 3 | Thank you for your interest in contributing to this repository! This project aims to provide clear, working examples of new features in Playwright releases. 4 | 5 | ## How to Contribute 6 | 7 | ### Adding Examples for a New Playwright Version 8 | 9 | 1. Create a new directory named `vX.XX.X-examples` (replace X.XX.X with the actual version) 10 | 2. Add the following files: 11 | - `new-features.spec.ts` - Test examples demonstrating key features 12 | - `README.md` - Documentation of features demonstrated 13 | - `playwright.config.ts` - Configuration showcasing new options 14 | 15 | ### Improving Existing Examples 16 | 17 | If you have write access to the repository: 18 | 1. Create a feature branch (`git checkout -b improve-v1.52-examples`) 19 | 2. Make your changes 20 | 3. Ensure tests pass (`npm test`) 21 | 4. Commit your changes (`git commit -am 'Improve v1.52 examples'`) 22 | 5. Push to the branch (`git push origin improve-v1.52-examples`) 23 | 6. Create a new Pull Request 24 | 25 | If you don't have write access: 26 | 1. Fork the repository 27 | 2. Create a feature branch (`git checkout -b improve-v1.52-examples`) 28 | 3. Make your changes 29 | 4. Ensure tests pass (`npm test`) 30 | 5. Commit your changes (`git commit -am 'Improve v1.52 examples'`) 31 | 6. Push to the branch (`git push origin improve-v1.52-examples`) 32 | 7. Create a new Pull Request from your fork 33 | 34 | ## Code Style Guidelines 35 | 36 | - Use clear, descriptive variable and function names 37 | - Add detailed comments explaining each feature 38 | - Include a comment with a reference to Playwright documentation when possible 39 | - Structure tests to be self-contained and not depend on other tests 40 | 41 | ## Documentation Guidelines 42 | 43 | - Begin each README with a clear version heading 44 | - List features demonstrated with bullet points 45 | - Link to official Playwright documentation for each feature 46 | - Include code snippets for key usage examples 47 | 48 | ## Pull Request Process 49 | 50 | 1. Update the README.md with details of changes if appropriate 51 | 2. The version number will be updated based on the Playwright version being documented 52 | 3. Your PR requires approval from a maintainer before merging 53 | 4. Ensure your PR passes all GitHub Actions tests 54 | 55 | ## Creating Issues 56 | 57 | When creating issues, please: 58 | 59 | 1. Use a clear and descriptive title 60 | 2. Provide steps to reproduce bugs 61 | 3. Include version information (Playwright, Node.js, etc.) 62 | 4. If suggesting a feature, explain why it would be useful 63 | 64 | Thank you for contributing! -------------------------------------------------------------------------------- /LINKEDIN_POST.md: -------------------------------------------------------------------------------- 1 | # 🚀 Excited to Share: Playwright Release Notes Examples 2 | 3 | I'm thrilled to announce my new open source project: **Playwright Release Notes Examples**! 4 | 5 | As web automation and testing continues to evolve, staying current with the latest Playwright features can be challenging. That's why I've created a comprehensive resource with working code examples that demonstrate new features from each Playwright release. 6 | 7 | ## What's included: 8 | - ✅ Practical, runnable examples for features in Playwright v1.50-v1.52 9 | - ✅ GitHub Actions workflow with automated testing 10 | - ✅ Clear documentation for each new feature 11 | - ✅ Organized by version for easy reference 12 | 13 | If you're using Playwright for testing or automation, this repository will help you quickly understand and implement new features as they're released. 14 | 15 | ## Featured examples: 16 | - Enhanced class assertions 17 | - Aria snapshots with improved accessibility testing 18 | - Request API enhancements 19 | - New configuration options 20 | 21 | Check it out at: https://github.com/qa-gary-parker/playwright-release-notes-examples 22 | 23 | Contributions are welcome - feel free to submit PRs for examples from newer releases! 24 | 25 | #Playwright #WebTesting #Automation #OpenSource #QA #Testing #JavaScript #TypeScript 26 | 27 | --- 28 | 29 | *Note: This is a template for your LinkedIn post. Feel free to customize with your personal voice and perspectives before posting.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playwright Release Notes Examples 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | ![Playwright Logo](https://playwright.dev/img/playwright-logo.svg) 22 | 23 | [![Playwright Tests](https://github.com/qa-gary-parker/playwright-release-notes-examples/actions/workflows/playwright.yml/badge.svg)](https://github.com/qa-gary-parker/playwright-release-notes-examples/actions/workflows/playwright.yml) 24 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com) 25 | [![GitHub Stars](https://img.shields.io/github/stars/qa-gary-parker/playwright-release-notes-examples.svg)](https://github.com/qa-gary-parker/playwright-release-notes-examples/stargazers) 26 | [![GitHub Issues](https://img.shields.io/github/issues/qa-gary-parker/playwright-release-notes-examples.svg)](https://github.com/qa-gary-parker/playwright-release-notes-examples/issues) 27 | 28 | **A comprehensive demonstration of new features and improvements in [Playwright](https://playwright.dev) releases** 29 | 30 |
31 | 32 | --- 33 | 34 | This repository provides working code examples for features introduced in each Playwright version, making it easier to: 35 | 36 | 1. ✨ Understand what's new in each release 37 | 2. 🔍 Learn how to use new features with practical examples 38 | 3. 🧪 Compare implementation approaches across versions 39 | 4. 🚀 Test and experiment with new APIs 40 | 41 | ## 📚 Project Structure 42 | 43 | Each version has its own dedicated examples directory: 44 | 45 | - [`v1.49.0-examples/`](./v1.49.0-examples/) - getByAltText locator, TestInfo properties, UI mode improvements 46 | - [`v1.50.0-examples/`](./v1.50.0-examples/) - Test step timeout, conditional step skipping, accessibility improvements 47 | - [`v1.51.0-examples/`](./v1.51.0-examples/) - IndexedDB storage, visibility filtering, contrast emulation 48 | - [`v1.52.0-examples/`](./v1.52.0-examples/) - Class assertions, enhanced aria snapshots, request options 49 | 50 | Each directory contains: 51 | - `new-features.spec.ts` - Practical test examples demonstrating the features 52 | - `README.md` - Detailed explanation of the features with reference links 53 | - `playwright.config.ts` - Configuration using version-specific options 54 | 55 | ## 📋 Feature Index by Version 56 | 57 | | Version | Feature | 58 | |---------|---------| 59 | | v1.52.0 | `toContainClass()` assertion | 60 | | v1.52.0 | Enhanced Aria snapshots with `/children` and `/url` | 61 | | v1.52.0 | `maxRedirects` option in `apiRequest` | 62 | | v1.52.0 | `ref` option in `locator.ariaSnapshot()` | 63 | | v1.52.0 | Test annotations with `testResult.annotations` | 64 | | v1.51.0 | IndexedDB storage | 65 | | v1.51.0 | Visibility filtering | 66 | | v1.51.0 | Contrast emulation | 67 | | v1.50.0 | Test step timeout | 68 | | v1.50.0 | Conditional step skipping | 69 | | v1.50.0 | Accessibility improvements | 70 | | v1.49.0 | Aria snapshots with YAML | 71 | | v1.49.0 | `test.fail.only()` method | 72 | | v1.49.0 | Multiple global setup/teardown | 73 | | v1.49.0 | Canvas preview in snapshots | 74 | | v1.49.0 | `tracing.group()` for trace organization | 75 | 76 | ## 🚀 Getting Started 77 | 78 | ### Prerequisites 79 | 80 | - Node.js 16+ 81 | - npm or yarn 82 | 83 | ### Installation 84 | 85 | 1. Clone this repository: 86 | ```bash 87 | git clone https://github.com/qa-gary-parker/playwright-release-notes-examples.git 88 | cd playwright-release-notes-examples 89 | ``` 90 | 91 | 2. Install dependencies: 92 | ```bash 93 | npm install 94 | ``` 95 | 96 | 3. Install browsers: 97 | ```bash 98 | npx playwright install 99 | ``` 100 | 101 | ### Running Examples 102 | 103 | Use our convenient npm scripts to run examples for specific versions: 104 | 105 | ```bash 106 | # Run v1.52.0 examples 107 | npm run test:v1.52 108 | 109 | # Run v1.51.0 examples 110 | npm run test:v1.51 111 | 112 | # Run v1.50.0 examples 113 | npm run test:v1.50 114 | 115 | # Run v1.49.0 examples 116 | npm run test:v1.49 117 | 118 | # Run all examples 119 | npm run test:all 120 | ``` 121 | 122 | ## 🤝 Contributing 123 | 124 | Contributions are welcome! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to: 125 | 126 | - Report bugs or request features 127 | - Submit pull requests 128 | - Add examples for new Playwright versions 129 | 130 | ## 🌟 Related Resources 131 | 132 | - [Playwright Documentation](https://playwright.dev/docs/intro) 133 | - [Playwright API Reference](https://playwright.dev/docs/api/class-playwright) 134 | - [Release Notes](https://playwright.dev/docs/release-notes) 135 | - [Playwright Community Discord](https://playwright.dev/community/welcome) 136 | 137 | ## 💖 Sponsors and Support 138 | 139 | If you find this project helpful, consider: 140 | 141 | - ⭐ Starring the repository on GitHub 142 | - 🐦 Sharing on social media 143 | - 💻 Contributing examples or improvements -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-release-notes", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "playwright-release-notes", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@playwright/test": "^1.52.0", 13 | "@types/node": "^20.10.0", 14 | "eslint": "^8.54.0", 15 | "typescript": "^5.3.2" 16 | }, 17 | "engines": { 18 | "node": ">=16.0.0" 19 | } 20 | }, 21 | "node_modules/@eslint-community/eslint-utils": { 22 | "version": "4.6.1", 23 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", 24 | "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", 25 | "dev": true, 26 | "dependencies": { 27 | "eslint-visitor-keys": "^3.4.3" 28 | }, 29 | "engines": { 30 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 31 | }, 32 | "funding": { 33 | "url": "https://opencollective.com/eslint" 34 | }, 35 | "peerDependencies": { 36 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 37 | } 38 | }, 39 | "node_modules/@eslint-community/regexpp": { 40 | "version": "4.12.1", 41 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", 42 | "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", 43 | "dev": true, 44 | "engines": { 45 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 46 | } 47 | }, 48 | "node_modules/@eslint/eslintrc": { 49 | "version": "2.1.4", 50 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", 51 | "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", 52 | "dev": true, 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": "8.57.1", 73 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", 74 | "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", 75 | "dev": true, 76 | "engines": { 77 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 78 | } 79 | }, 80 | "node_modules/@humanwhocodes/config-array": { 81 | "version": "0.13.0", 82 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", 83 | "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", 84 | "deprecated": "Use @eslint/config-array instead", 85 | "dev": true, 86 | "dependencies": { 87 | "@humanwhocodes/object-schema": "^2.0.3", 88 | "debug": "^4.3.1", 89 | "minimatch": "^3.0.5" 90 | }, 91 | "engines": { 92 | "node": ">=10.10.0" 93 | } 94 | }, 95 | "node_modules/@humanwhocodes/module-importer": { 96 | "version": "1.0.1", 97 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 98 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 99 | "dev": true, 100 | "engines": { 101 | "node": ">=12.22" 102 | }, 103 | "funding": { 104 | "type": "github", 105 | "url": "https://github.com/sponsors/nzakas" 106 | } 107 | }, 108 | "node_modules/@humanwhocodes/object-schema": { 109 | "version": "2.0.3", 110 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", 111 | "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", 112 | "deprecated": "Use @eslint/object-schema instead", 113 | "dev": true 114 | }, 115 | "node_modules/@nodelib/fs.scandir": { 116 | "version": "2.1.5", 117 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 118 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 119 | "dev": true, 120 | "dependencies": { 121 | "@nodelib/fs.stat": "2.0.5", 122 | "run-parallel": "^1.1.9" 123 | }, 124 | "engines": { 125 | "node": ">= 8" 126 | } 127 | }, 128 | "node_modules/@nodelib/fs.stat": { 129 | "version": "2.0.5", 130 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 131 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 132 | "dev": true, 133 | "engines": { 134 | "node": ">= 8" 135 | } 136 | }, 137 | "node_modules/@nodelib/fs.walk": { 138 | "version": "1.2.8", 139 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 140 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 141 | "dev": true, 142 | "dependencies": { 143 | "@nodelib/fs.scandir": "2.1.5", 144 | "fastq": "^1.6.0" 145 | }, 146 | "engines": { 147 | "node": ">= 8" 148 | } 149 | }, 150 | "node_modules/@playwright/test": { 151 | "version": "1.52.0", 152 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", 153 | "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", 154 | "dev": true, 155 | "dependencies": { 156 | "playwright": "1.52.0" 157 | }, 158 | "bin": { 159 | "playwright": "cli.js" 160 | }, 161 | "engines": { 162 | "node": ">=18" 163 | } 164 | }, 165 | "node_modules/@types/node": { 166 | "version": "20.17.31", 167 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.31.tgz", 168 | "integrity": "sha512-quODOCNXQAbNf1Q7V+fI8WyErOCh0D5Yd31vHnKu4GkSztGQ7rlltAaqXhHhLl33tlVyUXs2386MkANSwgDn6A==", 169 | "dev": true, 170 | "dependencies": { 171 | "undici-types": "~6.19.2" 172 | } 173 | }, 174 | "node_modules/@ungap/structured-clone": { 175 | "version": "1.3.0", 176 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", 177 | "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", 178 | "dev": true 179 | }, 180 | "node_modules/acorn": { 181 | "version": "8.14.1", 182 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", 183 | "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", 184 | "dev": true, 185 | "bin": { 186 | "acorn": "bin/acorn" 187 | }, 188 | "engines": { 189 | "node": ">=0.4.0" 190 | } 191 | }, 192 | "node_modules/acorn-jsx": { 193 | "version": "5.3.2", 194 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 195 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 196 | "dev": true, 197 | "peerDependencies": { 198 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 199 | } 200 | }, 201 | "node_modules/ajv": { 202 | "version": "6.12.6", 203 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 204 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 205 | "dev": true, 206 | "dependencies": { 207 | "fast-deep-equal": "^3.1.1", 208 | "fast-json-stable-stringify": "^2.0.0", 209 | "json-schema-traverse": "^0.4.1", 210 | "uri-js": "^4.2.2" 211 | }, 212 | "funding": { 213 | "type": "github", 214 | "url": "https://github.com/sponsors/epoberezkin" 215 | } 216 | }, 217 | "node_modules/ansi-regex": { 218 | "version": "5.0.1", 219 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 220 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 221 | "dev": true, 222 | "engines": { 223 | "node": ">=8" 224 | } 225 | }, 226 | "node_modules/ansi-styles": { 227 | "version": "4.3.0", 228 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 229 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 230 | "dev": true, 231 | "dependencies": { 232 | "color-convert": "^2.0.1" 233 | }, 234 | "engines": { 235 | "node": ">=8" 236 | }, 237 | "funding": { 238 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 239 | } 240 | }, 241 | "node_modules/argparse": { 242 | "version": "2.0.1", 243 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 244 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 245 | "dev": true 246 | }, 247 | "node_modules/balanced-match": { 248 | "version": "1.0.2", 249 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 250 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 251 | "dev": true 252 | }, 253 | "node_modules/brace-expansion": { 254 | "version": "1.1.11", 255 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 256 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 257 | "dev": true, 258 | "dependencies": { 259 | "balanced-match": "^1.0.0", 260 | "concat-map": "0.0.1" 261 | } 262 | }, 263 | "node_modules/callsites": { 264 | "version": "3.1.0", 265 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 266 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 267 | "dev": true, 268 | "engines": { 269 | "node": ">=6" 270 | } 271 | }, 272 | "node_modules/chalk": { 273 | "version": "4.1.2", 274 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 275 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 276 | "dev": true, 277 | "dependencies": { 278 | "ansi-styles": "^4.1.0", 279 | "supports-color": "^7.1.0" 280 | }, 281 | "engines": { 282 | "node": ">=10" 283 | }, 284 | "funding": { 285 | "url": "https://github.com/chalk/chalk?sponsor=1" 286 | } 287 | }, 288 | "node_modules/color-convert": { 289 | "version": "2.0.1", 290 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 291 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 292 | "dev": true, 293 | "dependencies": { 294 | "color-name": "~1.1.4" 295 | }, 296 | "engines": { 297 | "node": ">=7.0.0" 298 | } 299 | }, 300 | "node_modules/color-name": { 301 | "version": "1.1.4", 302 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 303 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 304 | "dev": true 305 | }, 306 | "node_modules/concat-map": { 307 | "version": "0.0.1", 308 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 309 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 310 | "dev": true 311 | }, 312 | "node_modules/cross-spawn": { 313 | "version": "7.0.6", 314 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 315 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 316 | "dev": true, 317 | "dependencies": { 318 | "path-key": "^3.1.0", 319 | "shebang-command": "^2.0.0", 320 | "which": "^2.0.1" 321 | }, 322 | "engines": { 323 | "node": ">= 8" 324 | } 325 | }, 326 | "node_modules/debug": { 327 | "version": "4.4.0", 328 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 329 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 330 | "dev": true, 331 | "dependencies": { 332 | "ms": "^2.1.3" 333 | }, 334 | "engines": { 335 | "node": ">=6.0" 336 | }, 337 | "peerDependenciesMeta": { 338 | "supports-color": { 339 | "optional": true 340 | } 341 | } 342 | }, 343 | "node_modules/deep-is": { 344 | "version": "0.1.4", 345 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 346 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 347 | "dev": true 348 | }, 349 | "node_modules/doctrine": { 350 | "version": "3.0.0", 351 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 352 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 353 | "dev": true, 354 | "dependencies": { 355 | "esutils": "^2.0.2" 356 | }, 357 | "engines": { 358 | "node": ">=6.0.0" 359 | } 360 | }, 361 | "node_modules/escape-string-regexp": { 362 | "version": "4.0.0", 363 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 364 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 365 | "dev": true, 366 | "engines": { 367 | "node": ">=10" 368 | }, 369 | "funding": { 370 | "url": "https://github.com/sponsors/sindresorhus" 371 | } 372 | }, 373 | "node_modules/eslint": { 374 | "version": "8.57.1", 375 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", 376 | "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", 377 | "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", 378 | "dev": true, 379 | "dependencies": { 380 | "@eslint-community/eslint-utils": "^4.2.0", 381 | "@eslint-community/regexpp": "^4.6.1", 382 | "@eslint/eslintrc": "^2.1.4", 383 | "@eslint/js": "8.57.1", 384 | "@humanwhocodes/config-array": "^0.13.0", 385 | "@humanwhocodes/module-importer": "^1.0.1", 386 | "@nodelib/fs.walk": "^1.2.8", 387 | "@ungap/structured-clone": "^1.2.0", 388 | "ajv": "^6.12.4", 389 | "chalk": "^4.0.0", 390 | "cross-spawn": "^7.0.2", 391 | "debug": "^4.3.2", 392 | "doctrine": "^3.0.0", 393 | "escape-string-regexp": "^4.0.0", 394 | "eslint-scope": "^7.2.2", 395 | "eslint-visitor-keys": "^3.4.3", 396 | "espree": "^9.6.1", 397 | "esquery": "^1.4.2", 398 | "esutils": "^2.0.2", 399 | "fast-deep-equal": "^3.1.3", 400 | "file-entry-cache": "^6.0.1", 401 | "find-up": "^5.0.0", 402 | "glob-parent": "^6.0.2", 403 | "globals": "^13.19.0", 404 | "graphemer": "^1.4.0", 405 | "ignore": "^5.2.0", 406 | "imurmurhash": "^0.1.4", 407 | "is-glob": "^4.0.0", 408 | "is-path-inside": "^3.0.3", 409 | "js-yaml": "^4.1.0", 410 | "json-stable-stringify-without-jsonify": "^1.0.1", 411 | "levn": "^0.4.1", 412 | "lodash.merge": "^4.6.2", 413 | "minimatch": "^3.1.2", 414 | "natural-compare": "^1.4.0", 415 | "optionator": "^0.9.3", 416 | "strip-ansi": "^6.0.1", 417 | "text-table": "^0.2.0" 418 | }, 419 | "bin": { 420 | "eslint": "bin/eslint.js" 421 | }, 422 | "engines": { 423 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 424 | }, 425 | "funding": { 426 | "url": "https://opencollective.com/eslint" 427 | } 428 | }, 429 | "node_modules/eslint-scope": { 430 | "version": "7.2.2", 431 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", 432 | "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", 433 | "dev": true, 434 | "dependencies": { 435 | "esrecurse": "^4.3.0", 436 | "estraverse": "^5.2.0" 437 | }, 438 | "engines": { 439 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 440 | }, 441 | "funding": { 442 | "url": "https://opencollective.com/eslint" 443 | } 444 | }, 445 | "node_modules/eslint-visitor-keys": { 446 | "version": "3.4.3", 447 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 448 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 449 | "dev": true, 450 | "engines": { 451 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 452 | }, 453 | "funding": { 454 | "url": "https://opencollective.com/eslint" 455 | } 456 | }, 457 | "node_modules/espree": { 458 | "version": "9.6.1", 459 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", 460 | "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", 461 | "dev": true, 462 | "dependencies": { 463 | "acorn": "^8.9.0", 464 | "acorn-jsx": "^5.3.2", 465 | "eslint-visitor-keys": "^3.4.1" 466 | }, 467 | "engines": { 468 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 469 | }, 470 | "funding": { 471 | "url": "https://opencollective.com/eslint" 472 | } 473 | }, 474 | "node_modules/esquery": { 475 | "version": "1.6.0", 476 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", 477 | "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 478 | "dev": true, 479 | "dependencies": { 480 | "estraverse": "^5.1.0" 481 | }, 482 | "engines": { 483 | "node": ">=0.10" 484 | } 485 | }, 486 | "node_modules/esrecurse": { 487 | "version": "4.3.0", 488 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 489 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 490 | "dev": true, 491 | "dependencies": { 492 | "estraverse": "^5.2.0" 493 | }, 494 | "engines": { 495 | "node": ">=4.0" 496 | } 497 | }, 498 | "node_modules/estraverse": { 499 | "version": "5.3.0", 500 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 501 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 502 | "dev": true, 503 | "engines": { 504 | "node": ">=4.0" 505 | } 506 | }, 507 | "node_modules/esutils": { 508 | "version": "2.0.3", 509 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 510 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 511 | "dev": true, 512 | "engines": { 513 | "node": ">=0.10.0" 514 | } 515 | }, 516 | "node_modules/fast-deep-equal": { 517 | "version": "3.1.3", 518 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 519 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 520 | "dev": true 521 | }, 522 | "node_modules/fast-json-stable-stringify": { 523 | "version": "2.1.0", 524 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 525 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 526 | "dev": true 527 | }, 528 | "node_modules/fast-levenshtein": { 529 | "version": "2.0.6", 530 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 531 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 532 | "dev": true 533 | }, 534 | "node_modules/fastq": { 535 | "version": "1.19.1", 536 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", 537 | "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", 538 | "dev": true, 539 | "dependencies": { 540 | "reusify": "^1.0.4" 541 | } 542 | }, 543 | "node_modules/file-entry-cache": { 544 | "version": "6.0.1", 545 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 546 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 547 | "dev": true, 548 | "dependencies": { 549 | "flat-cache": "^3.0.4" 550 | }, 551 | "engines": { 552 | "node": "^10.12.0 || >=12.0.0" 553 | } 554 | }, 555 | "node_modules/find-up": { 556 | "version": "5.0.0", 557 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 558 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 559 | "dev": true, 560 | "dependencies": { 561 | "locate-path": "^6.0.0", 562 | "path-exists": "^4.0.0" 563 | }, 564 | "engines": { 565 | "node": ">=10" 566 | }, 567 | "funding": { 568 | "url": "https://github.com/sponsors/sindresorhus" 569 | } 570 | }, 571 | "node_modules/flat-cache": { 572 | "version": "3.2.0", 573 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", 574 | "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", 575 | "dev": true, 576 | "dependencies": { 577 | "flatted": "^3.2.9", 578 | "keyv": "^4.5.3", 579 | "rimraf": "^3.0.2" 580 | }, 581 | "engines": { 582 | "node": "^10.12.0 || >=12.0.0" 583 | } 584 | }, 585 | "node_modules/flatted": { 586 | "version": "3.3.3", 587 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", 588 | "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", 589 | "dev": true 590 | }, 591 | "node_modules/fs.realpath": { 592 | "version": "1.0.0", 593 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 594 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 595 | "dev": true 596 | }, 597 | "node_modules/fsevents": { 598 | "version": "2.3.2", 599 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 600 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 601 | "dev": true, 602 | "hasInstallScript": true, 603 | "optional": true, 604 | "os": [ 605 | "darwin" 606 | ], 607 | "engines": { 608 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 609 | } 610 | }, 611 | "node_modules/glob": { 612 | "version": "7.2.3", 613 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 614 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 615 | "deprecated": "Glob versions prior to v9 are no longer supported", 616 | "dev": true, 617 | "dependencies": { 618 | "fs.realpath": "^1.0.0", 619 | "inflight": "^1.0.4", 620 | "inherits": "2", 621 | "minimatch": "^3.1.1", 622 | "once": "^1.3.0", 623 | "path-is-absolute": "^1.0.0" 624 | }, 625 | "engines": { 626 | "node": "*" 627 | }, 628 | "funding": { 629 | "url": "https://github.com/sponsors/isaacs" 630 | } 631 | }, 632 | "node_modules/glob-parent": { 633 | "version": "6.0.2", 634 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 635 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 636 | "dev": true, 637 | "dependencies": { 638 | "is-glob": "^4.0.3" 639 | }, 640 | "engines": { 641 | "node": ">=10.13.0" 642 | } 643 | }, 644 | "node_modules/globals": { 645 | "version": "13.24.0", 646 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", 647 | "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", 648 | "dev": true, 649 | "dependencies": { 650 | "type-fest": "^0.20.2" 651 | }, 652 | "engines": { 653 | "node": ">=8" 654 | }, 655 | "funding": { 656 | "url": "https://github.com/sponsors/sindresorhus" 657 | } 658 | }, 659 | "node_modules/graphemer": { 660 | "version": "1.4.0", 661 | "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 662 | "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 663 | "dev": true 664 | }, 665 | "node_modules/has-flag": { 666 | "version": "4.0.0", 667 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 668 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 669 | "dev": true, 670 | "engines": { 671 | "node": ">=8" 672 | } 673 | }, 674 | "node_modules/ignore": { 675 | "version": "5.3.2", 676 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 677 | "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 678 | "dev": true, 679 | "engines": { 680 | "node": ">= 4" 681 | } 682 | }, 683 | "node_modules/import-fresh": { 684 | "version": "3.3.1", 685 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", 686 | "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", 687 | "dev": true, 688 | "dependencies": { 689 | "parent-module": "^1.0.0", 690 | "resolve-from": "^4.0.0" 691 | }, 692 | "engines": { 693 | "node": ">=6" 694 | }, 695 | "funding": { 696 | "url": "https://github.com/sponsors/sindresorhus" 697 | } 698 | }, 699 | "node_modules/imurmurhash": { 700 | "version": "0.1.4", 701 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 702 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 703 | "dev": true, 704 | "engines": { 705 | "node": ">=0.8.19" 706 | } 707 | }, 708 | "node_modules/inflight": { 709 | "version": "1.0.6", 710 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 711 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 712 | "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.", 713 | "dev": true, 714 | "dependencies": { 715 | "once": "^1.3.0", 716 | "wrappy": "1" 717 | } 718 | }, 719 | "node_modules/inherits": { 720 | "version": "2.0.4", 721 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 722 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 723 | "dev": true 724 | }, 725 | "node_modules/is-extglob": { 726 | "version": "2.1.1", 727 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 728 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 729 | "dev": true, 730 | "engines": { 731 | "node": ">=0.10.0" 732 | } 733 | }, 734 | "node_modules/is-glob": { 735 | "version": "4.0.3", 736 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 737 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 738 | "dev": true, 739 | "dependencies": { 740 | "is-extglob": "^2.1.1" 741 | }, 742 | "engines": { 743 | "node": ">=0.10.0" 744 | } 745 | }, 746 | "node_modules/is-path-inside": { 747 | "version": "3.0.3", 748 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 749 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 750 | "dev": true, 751 | "engines": { 752 | "node": ">=8" 753 | } 754 | }, 755 | "node_modules/isexe": { 756 | "version": "2.0.0", 757 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 758 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 759 | "dev": true 760 | }, 761 | "node_modules/js-yaml": { 762 | "version": "4.1.0", 763 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 764 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 765 | "dev": true, 766 | "dependencies": { 767 | "argparse": "^2.0.1" 768 | }, 769 | "bin": { 770 | "js-yaml": "bin/js-yaml.js" 771 | } 772 | }, 773 | "node_modules/json-buffer": { 774 | "version": "3.0.1", 775 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 776 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 777 | "dev": true 778 | }, 779 | "node_modules/json-schema-traverse": { 780 | "version": "0.4.1", 781 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 782 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 783 | "dev": true 784 | }, 785 | "node_modules/json-stable-stringify-without-jsonify": { 786 | "version": "1.0.1", 787 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 788 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 789 | "dev": true 790 | }, 791 | "node_modules/keyv": { 792 | "version": "4.5.4", 793 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 794 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 795 | "dev": true, 796 | "dependencies": { 797 | "json-buffer": "3.0.1" 798 | } 799 | }, 800 | "node_modules/levn": { 801 | "version": "0.4.1", 802 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 803 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 804 | "dev": true, 805 | "dependencies": { 806 | "prelude-ls": "^1.2.1", 807 | "type-check": "~0.4.0" 808 | }, 809 | "engines": { 810 | "node": ">= 0.8.0" 811 | } 812 | }, 813 | "node_modules/locate-path": { 814 | "version": "6.0.0", 815 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 816 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 817 | "dev": true, 818 | "dependencies": { 819 | "p-locate": "^5.0.0" 820 | }, 821 | "engines": { 822 | "node": ">=10" 823 | }, 824 | "funding": { 825 | "url": "https://github.com/sponsors/sindresorhus" 826 | } 827 | }, 828 | "node_modules/lodash.merge": { 829 | "version": "4.6.2", 830 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 831 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 832 | "dev": true 833 | }, 834 | "node_modules/minimatch": { 835 | "version": "3.1.2", 836 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 837 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 838 | "dev": true, 839 | "dependencies": { 840 | "brace-expansion": "^1.1.7" 841 | }, 842 | "engines": { 843 | "node": "*" 844 | } 845 | }, 846 | "node_modules/ms": { 847 | "version": "2.1.3", 848 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 849 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 850 | "dev": true 851 | }, 852 | "node_modules/natural-compare": { 853 | "version": "1.4.0", 854 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 855 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 856 | "dev": true 857 | }, 858 | "node_modules/once": { 859 | "version": "1.4.0", 860 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 861 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 862 | "dev": true, 863 | "dependencies": { 864 | "wrappy": "1" 865 | } 866 | }, 867 | "node_modules/optionator": { 868 | "version": "0.9.4", 869 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 870 | "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 871 | "dev": true, 872 | "dependencies": { 873 | "deep-is": "^0.1.3", 874 | "fast-levenshtein": "^2.0.6", 875 | "levn": "^0.4.1", 876 | "prelude-ls": "^1.2.1", 877 | "type-check": "^0.4.0", 878 | "word-wrap": "^1.2.5" 879 | }, 880 | "engines": { 881 | "node": ">= 0.8.0" 882 | } 883 | }, 884 | "node_modules/p-limit": { 885 | "version": "3.1.0", 886 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 887 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 888 | "dev": true, 889 | "dependencies": { 890 | "yocto-queue": "^0.1.0" 891 | }, 892 | "engines": { 893 | "node": ">=10" 894 | }, 895 | "funding": { 896 | "url": "https://github.com/sponsors/sindresorhus" 897 | } 898 | }, 899 | "node_modules/p-locate": { 900 | "version": "5.0.0", 901 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 902 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 903 | "dev": true, 904 | "dependencies": { 905 | "p-limit": "^3.0.2" 906 | }, 907 | "engines": { 908 | "node": ">=10" 909 | }, 910 | "funding": { 911 | "url": "https://github.com/sponsors/sindresorhus" 912 | } 913 | }, 914 | "node_modules/parent-module": { 915 | "version": "1.0.1", 916 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 917 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 918 | "dev": true, 919 | "dependencies": { 920 | "callsites": "^3.0.0" 921 | }, 922 | "engines": { 923 | "node": ">=6" 924 | } 925 | }, 926 | "node_modules/path-exists": { 927 | "version": "4.0.0", 928 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 929 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 930 | "dev": true, 931 | "engines": { 932 | "node": ">=8" 933 | } 934 | }, 935 | "node_modules/path-is-absolute": { 936 | "version": "1.0.1", 937 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 938 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 939 | "dev": true, 940 | "engines": { 941 | "node": ">=0.10.0" 942 | } 943 | }, 944 | "node_modules/path-key": { 945 | "version": "3.1.1", 946 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 947 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 948 | "dev": true, 949 | "engines": { 950 | "node": ">=8" 951 | } 952 | }, 953 | "node_modules/playwright": { 954 | "version": "1.52.0", 955 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", 956 | "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", 957 | "dev": true, 958 | "dependencies": { 959 | "playwright-core": "1.52.0" 960 | }, 961 | "bin": { 962 | "playwright": "cli.js" 963 | }, 964 | "engines": { 965 | "node": ">=18" 966 | }, 967 | "optionalDependencies": { 968 | "fsevents": "2.3.2" 969 | } 970 | }, 971 | "node_modules/playwright-core": { 972 | "version": "1.52.0", 973 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", 974 | "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", 975 | "dev": true, 976 | "bin": { 977 | "playwright-core": "cli.js" 978 | }, 979 | "engines": { 980 | "node": ">=18" 981 | } 982 | }, 983 | "node_modules/prelude-ls": { 984 | "version": "1.2.1", 985 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 986 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 987 | "dev": true, 988 | "engines": { 989 | "node": ">= 0.8.0" 990 | } 991 | }, 992 | "node_modules/punycode": { 993 | "version": "2.3.1", 994 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 995 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 996 | "dev": true, 997 | "engines": { 998 | "node": ">=6" 999 | } 1000 | }, 1001 | "node_modules/queue-microtask": { 1002 | "version": "1.2.3", 1003 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1004 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1005 | "dev": true, 1006 | "funding": [ 1007 | { 1008 | "type": "github", 1009 | "url": "https://github.com/sponsors/feross" 1010 | }, 1011 | { 1012 | "type": "patreon", 1013 | "url": "https://www.patreon.com/feross" 1014 | }, 1015 | { 1016 | "type": "consulting", 1017 | "url": "https://feross.org/support" 1018 | } 1019 | ] 1020 | }, 1021 | "node_modules/resolve-from": { 1022 | "version": "4.0.0", 1023 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1024 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1025 | "dev": true, 1026 | "engines": { 1027 | "node": ">=4" 1028 | } 1029 | }, 1030 | "node_modules/reusify": { 1031 | "version": "1.1.0", 1032 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", 1033 | "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", 1034 | "dev": true, 1035 | "engines": { 1036 | "iojs": ">=1.0.0", 1037 | "node": ">=0.10.0" 1038 | } 1039 | }, 1040 | "node_modules/rimraf": { 1041 | "version": "3.0.2", 1042 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1043 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1044 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 1045 | "dev": true, 1046 | "dependencies": { 1047 | "glob": "^7.1.3" 1048 | }, 1049 | "bin": { 1050 | "rimraf": "bin.js" 1051 | }, 1052 | "funding": { 1053 | "url": "https://github.com/sponsors/isaacs" 1054 | } 1055 | }, 1056 | "node_modules/run-parallel": { 1057 | "version": "1.2.0", 1058 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1059 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1060 | "dev": true, 1061 | "funding": [ 1062 | { 1063 | "type": "github", 1064 | "url": "https://github.com/sponsors/feross" 1065 | }, 1066 | { 1067 | "type": "patreon", 1068 | "url": "https://www.patreon.com/feross" 1069 | }, 1070 | { 1071 | "type": "consulting", 1072 | "url": "https://feross.org/support" 1073 | } 1074 | ], 1075 | "dependencies": { 1076 | "queue-microtask": "^1.2.2" 1077 | } 1078 | }, 1079 | "node_modules/shebang-command": { 1080 | "version": "2.0.0", 1081 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1082 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1083 | "dev": true, 1084 | "dependencies": { 1085 | "shebang-regex": "^3.0.0" 1086 | }, 1087 | "engines": { 1088 | "node": ">=8" 1089 | } 1090 | }, 1091 | "node_modules/shebang-regex": { 1092 | "version": "3.0.0", 1093 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1094 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1095 | "dev": true, 1096 | "engines": { 1097 | "node": ">=8" 1098 | } 1099 | }, 1100 | "node_modules/strip-ansi": { 1101 | "version": "6.0.1", 1102 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1103 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1104 | "dev": true, 1105 | "dependencies": { 1106 | "ansi-regex": "^5.0.1" 1107 | }, 1108 | "engines": { 1109 | "node": ">=8" 1110 | } 1111 | }, 1112 | "node_modules/strip-json-comments": { 1113 | "version": "3.1.1", 1114 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1115 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1116 | "dev": true, 1117 | "engines": { 1118 | "node": ">=8" 1119 | }, 1120 | "funding": { 1121 | "url": "https://github.com/sponsors/sindresorhus" 1122 | } 1123 | }, 1124 | "node_modules/supports-color": { 1125 | "version": "7.2.0", 1126 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1127 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1128 | "dev": true, 1129 | "dependencies": { 1130 | "has-flag": "^4.0.0" 1131 | }, 1132 | "engines": { 1133 | "node": ">=8" 1134 | } 1135 | }, 1136 | "node_modules/text-table": { 1137 | "version": "0.2.0", 1138 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1139 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 1140 | "dev": true 1141 | }, 1142 | "node_modules/type-check": { 1143 | "version": "0.4.0", 1144 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1145 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1146 | "dev": true, 1147 | "dependencies": { 1148 | "prelude-ls": "^1.2.1" 1149 | }, 1150 | "engines": { 1151 | "node": ">= 0.8.0" 1152 | } 1153 | }, 1154 | "node_modules/type-fest": { 1155 | "version": "0.20.2", 1156 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 1157 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 1158 | "dev": true, 1159 | "engines": { 1160 | "node": ">=10" 1161 | }, 1162 | "funding": { 1163 | "url": "https://github.com/sponsors/sindresorhus" 1164 | } 1165 | }, 1166 | "node_modules/typescript": { 1167 | "version": "5.8.3", 1168 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 1169 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 1170 | "dev": true, 1171 | "bin": { 1172 | "tsc": "bin/tsc", 1173 | "tsserver": "bin/tsserver" 1174 | }, 1175 | "engines": { 1176 | "node": ">=14.17" 1177 | } 1178 | }, 1179 | "node_modules/undici-types": { 1180 | "version": "6.19.8", 1181 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 1182 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 1183 | "dev": true 1184 | }, 1185 | "node_modules/uri-js": { 1186 | "version": "4.4.1", 1187 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1188 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1189 | "dev": true, 1190 | "dependencies": { 1191 | "punycode": "^2.1.0" 1192 | } 1193 | }, 1194 | "node_modules/which": { 1195 | "version": "2.0.2", 1196 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1197 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1198 | "dev": true, 1199 | "dependencies": { 1200 | "isexe": "^2.0.0" 1201 | }, 1202 | "bin": { 1203 | "node-which": "bin/node-which" 1204 | }, 1205 | "engines": { 1206 | "node": ">= 8" 1207 | } 1208 | }, 1209 | "node_modules/word-wrap": { 1210 | "version": "1.2.5", 1211 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 1212 | "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 1213 | "dev": true, 1214 | "engines": { 1215 | "node": ">=0.10.0" 1216 | } 1217 | }, 1218 | "node_modules/wrappy": { 1219 | "version": "1.0.2", 1220 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1221 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1222 | "dev": true 1223 | }, 1224 | "node_modules/yocto-queue": { 1225 | "version": "0.1.0", 1226 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1227 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1228 | "dev": true, 1229 | "engines": { 1230 | "node": ">=10" 1231 | }, 1232 | "funding": { 1233 | "url": "https://github.com/sponsors/sindresorhus" 1234 | } 1235 | } 1236 | } 1237 | } 1238 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-release-notes", 3 | "version": "1.0.0", 4 | "description": "Examples demonstrating new features in Playwright releases", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "playwright test", 8 | "test:v1.49": "playwright test v1.49.0-examples/", 9 | "test:v1.50": "playwright test v1.50.0-examples/", 10 | "test:v1.51": "playwright test v1.51.0-examples/", 11 | "test:v1.52": "playwright test v1.52.0-examples/", 12 | "test:all": "playwright test", 13 | "lint": "eslint . --ext .ts", 14 | "update-browsers": "playwright install" 15 | }, 16 | "keywords": [ 17 | "playwright", 18 | "testing", 19 | "automation", 20 | "examples", 21 | "tutorial", 22 | "e2e", 23 | "browser" 24 | ], 25 | "author": "", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "@playwright/test": "^1.52.0", 29 | "@types/node": "^20.10.0", 30 | "eslint": "^8.54.0", 31 | "typescript": "^5.3.2" 32 | }, 33 | "engines": { 34 | "node": ">=16.0.0" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/qa-gary-parker/playwright-release-notes-examples.git" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/qa-gary-parker/playwright-release-notes-examples/issues" 42 | }, 43 | "homepage": "https://github.com/qa-gary-parker/playwright-release-notes-examples#readme" 44 | } -------------------------------------------------------------------------------- /v1.49.0-examples/README.md: -------------------------------------------------------------------------------- 1 | # Playwright v1.49.0 New Features Examples 2 | 3 | This directory contains examples demonstrating the new features introduced in Playwright v1.49.0. 4 | 5 | ## New Features Demonstrated 6 | 7 | 1. **Aria Snapshots** 8 | - New assertion `expect(locator).toMatchAriaSnapshot()` to verify page structure 9 | - Compare with expected accessibility tree in YAML format 10 | - Great for accessibility testing and structural verification 11 | - Example in `new-features.spec.ts` 12 | 13 | 2. **New Test Runner Options** 14 | - New method `test.fail.only()` to focus on a failing test 15 | - Support for multiple global setups and teardowns 16 | - New `'on-first-failure'` value for screenshot options 17 | - Example in `new-features.spec.ts` 18 | 19 | 3. **Canvas Preview in Snapshots** 20 | - `` elements now draw a preview in snapshots 21 | - Better visual representation in reports and traces 22 | - Example in `new-features.spec.ts` 23 | 24 | 4. **Tracing Improvements** 25 | - New `tracing.group()` method to visually group actions 26 | - Better organization of trace entries 27 | - Example in `new-features.spec.ts` 28 | 29 | 5. **Error Causes** 30 | - New properties `testInfoError.cause` and `testError.cause` 31 | - Access full error chain in tests and reporters 32 | - Example in `new-features.spec.ts` 33 | 34 | ## How to Run the Examples 35 | 36 | 1. Install dependencies: 37 | ```bash 38 | npm install 39 | ``` 40 | 41 | 2. Run the tests: 42 | ```bash 43 | npx playwright test 44 | ``` 45 | 46 | 3. View the test report: 47 | ```bash 48 | npx playwright show-report 49 | ``` 50 | 51 | ## Additional Features (Not Implemented in Examples) 52 | 53 | 1. **Enhanced Snapshot Options** 54 | - New `maxDiffPixelRatio` option in the `toMatchSnapshot` configuration 55 | - Example configuration in `playwright.config.ts`: 56 | ```js 57 | // playwright.config.ts 58 | import { defineConfig } from '@playwright/test'; 59 | 60 | export default defineConfig({ 61 | expect: { 62 | toMatchSnapshot: { 63 | maxDiffPixelRatio: 0.05 64 | } 65 | } 66 | }); 67 | ``` 68 | 69 | 2. **Single tsconfig for All Tests** 70 | - New option `testConfig.tsconfig` to specify a single configuration 71 | - Useful for standardizing TypeScript settings across tests 72 | ```js 73 | // playwright.config.ts 74 | import { defineConfig } from '@playwright/test'; 75 | 76 | export default defineConfig({ 77 | tsconfig: './tsconfig.json' 78 | }); 79 | ``` 80 | 81 | 3. **HTML Report Navigation** 82 | - Added "previous" and "next" buttons to the HTML report 83 | - Quickly switch between test cases 84 | 85 | ## Breaking Changes in v1.49.0 86 | 87 | 1. **Headless Mode Changes** 88 | - Channels like `chrome`, `msedge` etc. now use the new headless mode 89 | - May require updating snapshots or adapting test code 90 | 91 | 2. **Platform Support** 92 | - No more updates for WebKit on Ubuntu 20.04 and Debian 11 93 | - Recommendation to update OS to later versions 94 | 95 | 3. **Component Testing Packages** 96 | - `@playwright/experimental-ct-vue2` will no longer be updated 97 | - `@playwright/experimental-ct-solid` will no longer be updated 98 | 99 | ## Notes 100 | 101 | - The examples demonstrate practical uses of v1.49.0 features 102 | - All examples include detailed comments explaining the features 103 | - The tests use the Playwright website (playwright.dev) as a test target where appropriate -------------------------------------------------------------------------------- /v1.49.0-examples/new-features.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('Playwright v1.49.0 New Features', () => { 4 | 5 | test('Aria Snapshots with YAML', async ({ page }) => { 6 | // Create a test page with structured content for aria snapshot testing 7 | await page.setContent(` 8 | 19 |
20 |

Welcome to Our Site

21 |
22 |

Featured Products

23 |
24 |
Product A
25 |
Product B
26 |
27 |
28 |
29 | 32 | `); 33 | 34 | // Using the new expect().toMatchAriaSnapshot() assertion 35 | // This verifies the accessibility tree structure of the page 36 | await expect(page.locator('body')).toMatchAriaSnapshot(` 37 | - navigation: 38 | - list: 39 | - listitem: Home 40 | - listitem: 41 | - link "Products": 42 | - /url: https://example.com/products 43 | - listitem: 44 | - link "About Us": 45 | - /url: https://example.com/about 46 | - main: 47 | - heading "Welcome to Our Site" [level=1] 48 | - heading "Featured Products" [level=2] 49 | - list: 50 | - listitem: Product A 51 | - listitem: Product B 52 | - contentinfo: 53 | - paragraph: Copyright 2023 54 | `); 55 | 56 | // Note: In a real scenario, you would first generate this snapshot using: 57 | // npx playwright test --update-snapshots 58 | }); 59 | 60 | test('Canvas Preview in Snapshots', async ({ page }) => { 61 | // In v1.49.0, canvas elements now draw a preview in snapshots 62 | // We'll create a page with a canvas element that draws something 63 | 64 | await page.setContent(` 65 |

Canvas Element with Drawing

66 | 67 | 80 | `); 81 | 82 | // In v1.49.0, when this page is captured in a snapshot or trace, 83 | // the canvas will show a preview of the actual rendered content 84 | // instead of just an empty placeholder 85 | 86 | // For demonstration, we'll take a screenshot that would include the canvas 87 | await page.screenshot({ path: 'canvas-preview.png' }); 88 | 89 | // Verify the canvas exists 90 | await expect(page.locator('canvas')).toBeVisible(); 91 | }); 92 | 93 | // Uncomment this test when you want to demonstrate test.fail.only() 94 | // This is commented out because it will always fail when run 95 | // test.fail('Demonstrating test.fail.only()', async ({ page }) => { 96 | // // In v1.49.0, you can use test.fail.only() to focus on a failing test 97 | // // Usage would be: test.fail.only('test name', async () => {}) 98 | // // This is useful for debugging failing tests in isolation 99 | // 100 | // await page.goto('https://example.com'); 101 | // // This assertion is meant to fail for demonstration 102 | // await expect(page.locator('h1')).toHaveText('This will fail'); 103 | // 104 | // // To run only this test and expect it to fail: 105 | // // Change the first line to test.fail.only() 106 | // }); 107 | 108 | test('Tracing Group Demonstration', async ({ browser }) => { 109 | // Create a new context specifically for this test 110 | const context = await browser.newContext(); 111 | const page = await context.newPage(); 112 | 113 | // Start tracing 114 | await context.tracing.start({ screenshots: true, snapshots: true }); 115 | 116 | // First group of actions - navigation 117 | await page.goto('https://example.com/'); 118 | await page.waitForLoadState('domcontentloaded'); 119 | 120 | // Second group of actions - interaction 121 | await page.locator('a').first().hover(); 122 | await page.locator('body').click(); 123 | 124 | // Stop tracing before closing the context 125 | await context.tracing.stop({ path: 'trace-with-groups.zip' }); 126 | 127 | // Clean up 128 | await context.close(); 129 | 130 | // Note: To view the trace with groups: 131 | // npx playwright show-trace trace-with-groups.zip 132 | // 133 | // Note: In v1.49.0, organizing traces is now possible using multiple 134 | // trace files rather than chunks, which is a simpler approach 135 | }); 136 | 137 | test('Error Cause Demonstration', async ({ page }) => { 138 | // In v1.49.0, test errors can have causes 139 | // We'll simulate this with a try-catch block 140 | 141 | try { 142 | await page.goto('https://example.com'); 143 | 144 | try { 145 | // This will throw an error 146 | throw new Error('Original error'); 147 | } catch (originalError) { 148 | // Create a new error with the original as its cause 149 | const wrapperError = new Error('Wrapper error'); 150 | wrapperError.cause = originalError; 151 | throw wrapperError; 152 | 153 | // In v1.49.0 test framework, you can access: 154 | // - testInfoError.cause in hooks and fixtures 155 | // - testError.cause in reporters 156 | } 157 | } catch (error) { 158 | // For demonstration purposes only - in actual tests 159 | // you'd let Playwright handle the error 160 | console.log('Error:', error.message); 161 | console.log('Cause:', error.cause?.message); 162 | 163 | // We won't actually throw here to keep the test passing 164 | // but in a real scenario, the error chain would be preserved 165 | } 166 | }); 167 | }); 168 | -------------------------------------------------------------------------------- /v1.49.0-examples/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | import path from 'path'; 3 | 4 | export default defineConfig({ 5 | testDir: './', 6 | fullyParallel: true, 7 | forbidOnly: !!process.env.CI, 8 | retries: process.env.CI ? 2 : 0, 9 | workers: process.env.CI ? 1 : undefined, 10 | reporter: [ 11 | ['html'], 12 | ['list'] // Add list reporter for console output 13 | ], 14 | use: { 15 | baseURL: 'https://playwright.dev', 16 | trace: 'on-first-retry', 17 | // New in v1.49.0: 'on-first-failure' option for screenshot 18 | screenshot: 'on-first-failure', 19 | }, 20 | 21 | // New in v1.49.0: Can use 'chromium' channel to opt into the new headless mode 22 | projects: [ 23 | { 24 | name: 'chromium', 25 | use: { ...devices['Desktop Chrome'] }, 26 | }, 27 | { 28 | name: 'chromium-new-headless', 29 | use: { 30 | ...devices['Desktop Chrome'], 31 | channel: 'chromium', // Opt into new headless mode 32 | }, 33 | }, 34 | { 35 | name: 'firefox', 36 | use: { ...devices['Desktop Firefox'] }, 37 | }, 38 | { 39 | name: 'webkit', 40 | use: { ...devices['Desktop Safari'] }, 41 | }, 42 | // Mobile browsers 43 | { 44 | name: 'Mobile Chrome', 45 | use: { ...devices['Pixel 5'] }, 46 | }, 47 | { 48 | name: 'Mobile Safari', 49 | use: { ...devices['iPhone 12'] }, 50 | }, 51 | ], 52 | 53 | // New in v1.49: Enhanced snapshot options 54 | expect: { 55 | // Configure snapshot options 56 | toMatchSnapshot: { 57 | // New maxDiffPixelRatio option 58 | maxDiffPixelRatio: 0.05 59 | }, 60 | timeout: 5000 61 | }, 62 | 63 | // New in v1.49: Specify a single tsconfig for all tests 64 | tsconfig: path.join(__dirname, '../tsconfig.json'), 65 | 66 | // New in v1.49: Support for multiple global setup and teardown files 67 | globalSetup: [ 68 | './global-setup-1.ts', 69 | './global-setup-2.ts', 70 | ], 71 | globalTeardown: [ 72 | './global-teardown-1.ts', 73 | './global-teardown-2.ts', 74 | ], 75 | 76 | // Useful for debugging 77 | timeout: 30000, 78 | }); -------------------------------------------------------------------------------- /v1.50.0-examples/README.md: -------------------------------------------------------------------------------- 1 | # Playwright v1.50.0 New Features Examples 2 | 3 | This directory contains examples demonstrating the new features introduced in Playwright v1.50.0. 4 | 5 | ## New Features Demonstrated 6 | 7 | 1. **Test Step Timeout Option** 8 | - New `timeout` option for `test.step()` to limit execution time for individual steps 9 | - Steps can timeout independently from the test 10 | - Failed steps don't necessarily fail the whole test 11 | - Example in `new-features.spec.ts` 12 | 13 | 2. **Skipping Test Steps Conditionally** 14 | - New `test.step.skip()` method to conditionally skip steps 15 | - Useful for feature-flag driven tests 16 | - Allows for cleaner test organization 17 | - Example in `new-features.spec.ts` 18 | 19 | 3. **Enhanced Aria Snapshots** 20 | - Support for storing aria snapshots in YAML files 21 | - Better organization for accessibility tests 22 | - Example in `new-features.spec.ts` 23 | 24 | 4. **New Accessibility Assertion** 25 | - New `toHaveAccessibleErrorMessage()` assertion 26 | - Easily test form validation error messages 27 | - Support for exact matches and regex patterns 28 | - Example in `new-features.spec.ts` 29 | 30 | ## How to Run the Examples 31 | 32 | 1. Install dependencies: 33 | ```bash 34 | npm install 35 | ``` 36 | 37 | 2. Run the tests: 38 | ```bash 39 | npx playwright test 40 | ``` 41 | 42 | 3. View the test report: 43 | ```bash 44 | npx playwright show-report 45 | ``` 46 | 47 | ## Additional Features Not Demonstrated in Code 48 | 49 | 1. **Snapshot Update Configuration** 50 | - New `changed` option for `testConfig.updateSnapshots` to only update changed snapshots 51 | - Configure in playwright.config.ts: 52 | ```js 53 | export default defineConfig({ 54 | updateSnapshots: 'changed' // Only update changed snapshots 55 | }); 56 | ``` 57 | 58 | 2. **Project-specific snapshot settings** 59 | - Configure snapshot behavior per project 60 | 61 | ## Notes 62 | 63 | - The examples provide practical demonstrations of key v1.50.0 features 64 | - Focus is on features that improve test reliability and maintainability 65 | - All examples include detailed comments explaining the features -------------------------------------------------------------------------------- /v1.50.0-examples/new-features.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('Playwright v1.50.0 New Features', () => { 4 | test('Test step with timeout option', async ({ page }) => { 5 | await page.setContent(`
Processing...
`); 6 | 7 | let stepResult = 'unknown'; 8 | 9 | // Wait for operation completion, which will time out 10 | await test.step('Wait for operation completion (with timeout)', async () => { 11 | try { 12 | await expect(page.locator('#status')).toHaveText('Completed', { timeout: 1000 }); 13 | stepResult = 'completed'; 14 | } catch (e) { 15 | // This is expected - the element will never have "Completed" text 16 | stepResult = 'timed out'; 17 | console.log('Step timed out as expected'); 18 | } 19 | }, { timeout: 2000 }); 20 | 21 | // Verify the step timed out as expected 22 | expect(stepResult).toBe('timed out'); 23 | 24 | // Test continues after timeout 25 | await page.evaluate(() => { 26 | document.getElementById('status')!.textContent = 'After timeout'; 27 | }); 28 | await expect(page.locator('#status')).toHaveText('After timeout'); 29 | }); 30 | 31 | test('Skip test steps conditionally with test.step.skip()', async ({ page }) => { 32 | // Setting up feature flag simulation 33 | const featureFlags = { 34 | newUIEnabled: false, 35 | darkModeEnabled: true 36 | }; 37 | 38 | // Create a test page 39 | await page.setContent(` 40 |
41 |

Feature Flag Demo

42 |
Light Mode
43 | 46 |
47 | `); 48 | 49 | // Always executed step 50 | await test.step('Verify base page content', async () => { 51 | await expect(page.locator('h1')).toHaveText('Feature Flag Demo'); 52 | }); 53 | 54 | // Using the new test.step.skip() to conditionally skip steps 55 | await test.step('Test new UI components', async step => { 56 | // Skip this step if the new UI feature is not enabled 57 | step.skip(!featureFlags.newUIEnabled, 'New UI is not enabled'); 58 | 59 | // This code won't execute if the step is skipped 60 | await page.evaluate(() => { 61 | const element = document.getElementById('new-ui-container'); 62 | if (element) element.style.display = 'block'; 63 | }); 64 | 65 | await expect(page.locator('#new-ui-container')).toBeVisible(); 66 | }); 67 | 68 | // Another conditional step 69 | await test.step('Test dark mode', async step => { 70 | // Skip if dark mode is not enabled 71 | step.skip(!featureFlags.darkModeEnabled, 'Dark mode is not enabled'); 72 | 73 | // This code will execute since darkModeEnabled is true 74 | await page.evaluate(() => { 75 | const element = document.getElementById('current-theme'); 76 | if (element) element.textContent = 'Dark Mode'; 77 | document.body.style.backgroundColor = '#222'; 78 | document.body.style.color = '#fff'; 79 | }); 80 | 81 | await expect(page.locator('#current-theme')).toHaveText('Dark Mode'); 82 | }); 83 | 84 | // This step always runs regardless of what was skipped 85 | await test.step('Final verification', async () => { 86 | // We can verify that our app still works even with some steps skipped 87 | await expect(page.locator('#app')).toBeVisible(); 88 | }); 89 | }); 90 | 91 | test('Enhanced aria snapshot with YAML file support', async ({ page }) => { 92 | // Set up a page with an accessible component 93 | await page.setContent(` 94 | 108 | `); 109 | 110 | // In v1.50.0, we can match aria snapshots against inline strings 111 | await expect(page.locator('nav')).toMatchAriaSnapshot(` 112 | - navigation "Main navigation": 113 | - menubar: 114 | - menuitem: 115 | - link "Home" 116 | - menuitem: 117 | - link "Products" 118 | - menuitem: 119 | - link "About" 120 | - menu: 121 | - menuitem: 122 | - link "Team" 123 | - menuitem: 124 | - link "History" 125 | - menuitem: 126 | - link "Contact" 127 | `); 128 | 129 | // New in v1.50.0: We can also store snapshots in YAML files 130 | // This would normally reference an external file: 131 | // await expect(page.locator('nav')).toMatchAriaSnapshot({ name: 'navigation-snapshot.yaml' }); 132 | 133 | // For demonstration purposes, we're using inline snapshot 134 | console.log(` 135 | New in v1.50.0: You can store aria snapshots in YAML files: 136 | 137 | await expect(page.locator('nav')).toMatchAriaSnapshot({ 138 | name: 'navigation-snapshot.yaml' 139 | }); 140 | `); 141 | }); 142 | 143 | test('toHaveAccessibleErrorMessage assertion', async ({ page }) => { 144 | // Setup a form with accessible error messages 145 | await page.setContent(` 146 |
147 |
148 | 149 | 150 | 151 |
152 | 153 |
154 | 155 | 156 | 157 |
158 | 159 |
160 | 161 | 162 | 163 |
164 |
165 | `); 166 | 167 | // Use the new toHaveAccessibleErrorMessage assertion from v1.50.0 168 | await expect(page.locator('#username')).toHaveAccessibleErrorMessage('Username must be at least 3 characters'); 169 | await expect(page.locator('#email')).toHaveAccessibleErrorMessage('Please enter a valid email address'); 170 | 171 | // Fix: Check for absence of error message more directly 172 | // Instead of using not.toHaveAccessibleErrorMessage, we'll check a different property 173 | await expect(page.locator('#password')).not.toHaveAttribute('aria-errormessage'); 174 | await expect(page.locator('#password')).not.toHaveAttribute('aria-invalid', 'true'); 175 | 176 | // Can check for partial match using regular expression 177 | await expect(page.locator('#username')).toHaveAccessibleErrorMessage(/at least 3 characters/); 178 | }); 179 | }); -------------------------------------------------------------------------------- /v1.50.0-examples/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './', 5 | fullyParallel: true, 6 | forbidOnly: !!process.env.CI, 7 | retries: process.env.CI ? 2 : 0, 8 | workers: 4, 9 | reporter: [ 10 | ['html'], 11 | ['list'] // Add list reporter for console output 12 | ], 13 | use: { 14 | baseURL: 'https://playwright.dev', 15 | trace: 'on-first-retry', 16 | }, 17 | 18 | projects: [ 19 | { 20 | name: 'chromium', 21 | use: { ...devices['Desktop Chrome'] }, 22 | // New in v1.50.0: Project-specific snapshot settings 23 | snapshotPathTemplate: '{testDir}/{testFileDir}/{testFileName}-{arg}{ext}', 24 | }, 25 | { 26 | name: 'firefox', 27 | use: { ...devices['Desktop Firefox'] }, 28 | }, 29 | { 30 | name: 'webkit', 31 | use: { ...devices['Desktop Safari'] }, 32 | }, 33 | ], 34 | 35 | // New in v1.50.0: Only update snapshots that have changed 36 | updateSnapshots: 'changed', 37 | 38 | // Useful for debugging 39 | timeout: 30000, 40 | expect: { 41 | timeout: 5000 42 | }, 43 | }); -------------------------------------------------------------------------------- /v1.51.0-examples/README.md: -------------------------------------------------------------------------------- 1 | # Playwright v1.51.0 New Features Examples 2 | 3 | This directory contains examples demonstrating the new features introduced in Playwright v1.51.0. 4 | 5 | ## New Features Demonstrated 6 | 7 | 1. **StorageState for indexedDB** 8 | - New `indexedDB` option in `browserContext.storageState()` 9 | - Save and restore IndexedDB contents between test runs 10 | - Particularly useful for applications using IndexedDB for authentication tokens 11 | - Example in `new-features.spec.ts` 12 | 13 | 2. **Filter Visible Elements** 14 | - New `visible` option for `locator.filter()` 15 | - Easily filter for only visible elements on page 16 | - Example in `new-features.spec.ts` 17 | 18 | 3. **Test Step Improvements** 19 | - New `TestStepInfo` object for test steps with improved control 20 | - Add attachments to steps for better documentation 21 | - Skip steps conditionally with `step.skip()` 22 | - Example in `new-features.spec.ts` 23 | 24 | 4. **Contrast Preference Emulation** 25 | - New `contrast` option for `page.emulateMedia()` 26 | - Emulate user preference for contrast level 27 | - Test how your application adapts to high contrast mode 28 | - Example in `new-features.spec.ts` 29 | 30 | 5. **Predicate Support for URL Assertions** 31 | - New support for using predicates in `expect(page).toHaveURL()` 32 | - Complex URL validation with custom functions 33 | - Example in `new-features.spec.ts` 34 | 35 | 6. **API Request Options** 36 | - New `failOnStatusCode` option in API requests 37 | - Makes requests throw on non-2xx/3xx status codes 38 | - Example in `new-features.spec.ts` 39 | 40 | ## How to Run the Examples 41 | 42 | 1. Install dependencies: 43 | ```bash 44 | npm install 45 | ``` 46 | 47 | 2. Run the tests: 48 | ```bash 49 | npx playwright test 50 | ``` 51 | 52 | 3. View the test report: 53 | ```bash 54 | npx playwright show-report 55 | ``` 56 | 57 | ## Additional Features Not Demonstrated in Code 58 | 59 | 1. **Copy as prompt button** - New feature in HTML report, trace viewer and UI mode for copying errors as LLM prompts. 60 | 61 | 2. **Git information in HTML report** - Capture git information with the `captureGitInfo` config option: 62 | ```js 63 | // playwright.config.ts 64 | import { defineConfig } from '@playwright/test'; 65 | 66 | export default defineConfig({ 67 | captureGitInfo: { commit: true, diff: true } 68 | }); 69 | ``` 70 | 71 | ## Notes 72 | 73 | - The examples provide practical demonstrations of each new feature 74 | - Some features require specific configuration in playwright.config.ts 75 | - All examples are self-contained and can be run independently -------------------------------------------------------------------------------- /v1.51.0-examples/new-features.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | import path from 'path'; 3 | 4 | // Define the auth file path as shown in the docs 5 | const authFile = path.join(__dirname, '../playwright/.auth/user.json'); 6 | 7 | test.describe('Playwright v1.51.0 New Features', () => { 8 | test('IndexedDB in storageState - auth example', async ({ page, context }) => { 9 | // This test demonstrates the auth pattern with indexedDB storage option 10 | await page.setContent(` 11 |
Ready
12 | 13 | 17 | 39 | `); 40 | 41 | // Perform login (simulated authentication steps) 42 | await page.click('#login'); 43 | 44 | // Wait for authentication to complete 45 | await expect(page.locator('#status')).toHaveText('Authentication successful', { timeout: 1000 }); 46 | await expect(page.locator('#user-info')).toContainText('sample-auth-token-123'); 47 | 48 | // This is the key part of the v1.51.0 feature - save storage state with indexedDB 49 | // In a real test, this would save IndexedDB contents to the auth file 50 | await context.storageState({ 51 | path: authFile, 52 | indexedDB: true // This is the new option in v1.51.0 53 | }); 54 | 55 | // Note: Since this is just a simulation, we're not actually saving anything meaningful 56 | console.log(`Storage state with indexedDB saved to ${authFile}`); 57 | }); 58 | 59 | test('Filter visible elements with locator.filter()', async ({ page }) => { 60 | // Setup a page with both visible and hidden elements 61 | await page.setContent(` 62 |
63 |
Item 1 (visible)
64 |
Item 2 (visible)
65 | 66 |
Item 4 (visible)
67 | 68 |
69 | `); 70 | 71 | // Get all todo items 72 | const allTodoItems = page.getByTestId('todo-item'); 73 | await expect(allTodoItems).toHaveCount(5); 74 | 75 | // Using the new visible option in filter() to only get visible items 76 | const visibleTodoItems = allTodoItems.filter({ visible: true }); 77 | await expect(visibleTodoItems).toHaveCount(3); 78 | 79 | // We can also check the text of all visible items 80 | const visibleTexts = await visibleTodoItems.allTextContents(); 81 | expect(visibleTexts).toEqual([ 82 | 'Item 1 (visible)', 83 | 'Item 2 (visible)', 84 | 'Item 4 (visible)' 85 | ]); 86 | }); 87 | 88 | test('Using test.step with attachments and skip', async ({ page }) => { 89 | // First step - always executed 90 | await test.step('First step - preparation', async step => { 91 | await page.setContent(` 92 |

Test Page

93 |
Empty container
94 | `); 95 | 96 | // Add an attachment to the step for documentation 97 | await step.attach('page-state', { 98 | body: 'Initial page state', 99 | contentType: 'text/plain' 100 | }); 101 | 102 | await expect(page.locator('#container')).toHaveText('Empty container'); 103 | }); 104 | 105 | // This is an example of a skipped step based on a condition 106 | const isMobile = false; // This would normally be based on your test configuration 107 | await test.step('Mobile-only step', async step => { 108 | // Skip step if not running in mobile mode 109 | step.skip(!isMobile, 'This step only runs on mobile layouts'); 110 | 111 | // This code will not execute if the step is skipped 112 | await page.setContent(` 113 |
Mobile View
114 | `); 115 | }); 116 | 117 | // Third step - always executed 118 | await test.step('Final step', async step => { 119 | await page.locator('#container').evaluate(el => { 120 | el.textContent = 'Updated content'; 121 | }); 122 | 123 | await expect(page.locator('#container')).toHaveText('Updated content'); 124 | 125 | // Add a screenshot attachment 126 | const screenshot = await page.screenshot(); 127 | await step.attach('final-state', { 128 | body: screenshot, 129 | contentType: 'image/png' 130 | }); 131 | }); 132 | }); 133 | 134 | test('emulateMedia with contrast preference', async ({ page }) => { 135 | await page.setContent(` 136 | 153 |

Contrast Test

154 | This is a link 155 |
Current contrast: no-preference
156 | 176 | `); 177 | 178 | // Default contrast 179 | await expect(page.locator('#contrast-state')).toHaveText('Current contrast: no-preference'); 180 | 181 | // Emulate high contrast mode 182 | await page.emulateMedia({ contrast: 'more' }); 183 | await expect(page.locator('#contrast-state')).toHaveText('Current contrast: more'); 184 | 185 | // Check that CSS is applied correctly in high contrast mode 186 | const bgColor = await page.evaluate(() => { 187 | return window.getComputedStyle(document.body).backgroundColor; 188 | }); 189 | expect(bgColor).toBe('rgb(0, 0, 0)'); // black 190 | 191 | // Emulate less contrast mode 192 | await page.emulateMedia({ contrast: 'no-preference' }); 193 | await expect(page.locator('#contrast-state')).toHaveText('Current contrast: no-preference'); 194 | }); 195 | 196 | test('expect(page).toHaveURL() with predicate', async ({ page }) => { 197 | // Navigate to a URL 198 | await page.goto('https://playwright.dev/docs/release-notes'); 199 | 200 | // Check URL with a string 201 | await expect(page).toHaveURL('https://playwright.dev/docs/release-notes'); 202 | 203 | // Check URL with a regular expression 204 | await expect(page).toHaveURL(/playwright\.dev/); 205 | 206 | // New in v1.51: Check URL with a predicate function 207 | await expect(page).toHaveURL(url => { 208 | return url.hostname === 'playwright.dev' && 209 | url.pathname.includes('/docs/') && 210 | url.pathname.endsWith('release-notes'); 211 | }); 212 | 213 | // Another example with a more complex predicate 214 | await expect(page).toHaveURL(url => { 215 | // We can implement any complex logic here 216 | const validHostnames = ['playwright.dev', 'www.playwright.dev']; 217 | const requiredPathSegments = ['docs', 'release-notes']; 218 | 219 | return validHostnames.includes(url.hostname) && 220 | requiredPathSegments.every(segment => url.pathname.includes(segment)); 221 | }); 222 | }); 223 | 224 | test('failOnStatusCode option in APIRequestContext', async ({ request }) => { 225 | // By default, request doesn't throw on non-2xx status codes 226 | const okResponse = await request.get('https://playwright.dev'); 227 | await expect(okResponse).toBeOK(); 228 | 229 | // Using httpbin.org to test various status codes 230 | const notFoundResponse = await request.get('https://httpbin.org/status/404'); 231 | expect(notFoundResponse.status()).toBe(404); 232 | 233 | // With failOnStatusCode: true, non-2xx status codes will throw 234 | try { 235 | await request.get('https://httpbin.org/status/500', { 236 | failOnStatusCode: true 237 | }); 238 | // Should not reach here 239 | throw new Error('Request should have failed'); 240 | } catch (error) { 241 | // Verify the error contains the status code 242 | expect(error.message).toContain('500'); 243 | } 244 | 245 | // 3xx responses should not throw even with failOnStatusCode: true 246 | const redirectResponse = await request.get('https://httpbin.org/status/301', { 247 | failOnStatusCode: true, 248 | maxRedirects: 0 // Don't follow redirects to test the status code handling 249 | }); 250 | expect(redirectResponse.status()).toBe(301); 251 | }); 252 | }); -------------------------------------------------------------------------------- /v1.51.0-examples/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './', 5 | fullyParallel: true, 6 | forbidOnly: !!process.env.CI, 7 | retries: process.env.CI ? 2 : 0, 8 | workers: 4, 9 | reporter: [ 10 | ['html'], 11 | ['list'] // Add list reporter for console output 12 | ], 13 | use: { 14 | baseURL: 'https://playwright.dev', 15 | trace: 'on-first-retry', 16 | }, 17 | 18 | projects: [ 19 | { 20 | name: 'chromium', 21 | use: { ...devices['Desktop Chrome'] }, 22 | }, 23 | { 24 | name: 'firefox', 25 | use: { ...devices['Desktop Firefox'] }, 26 | }, 27 | { 28 | name: 'webkit', 29 | use: { ...devices['Desktop Safari'] }, 30 | }, 31 | ], 32 | 33 | // New in v1.51: Capture Git information 34 | captureGitInfo: { 35 | commit: true, 36 | diff: true 37 | }, 38 | 39 | // Useful for debugging 40 | timeout: 30000, 41 | expect: { 42 | timeout: 5000 43 | }, 44 | }); -------------------------------------------------------------------------------- /v1.52.0-examples/README.md: -------------------------------------------------------------------------------- 1 | # Playwright v1.52.0 New Features Examples 2 | 3 | This directory contains examples demonstrating the new features introduced in Playwright v1.52.0. 4 | 5 | ## New Features Demonstrated 6 | 7 | 1. **Enhanced expect().toContainClass()** 8 | - New method to check for specific class names on elements 9 | - More ergonomic way to assert individual class names 10 | - Example in `new-features.spec.ts` 11 | 12 | 2. **Enhanced Aria Snapshots** 13 | - New `/children` property for strict matching 14 | - New `/url` property for links 15 | - Example in `new-features.spec.ts` 16 | 17 | 3. **New Configuration Options** 18 | - `failOnFlakyTests` option to fail test runs when flaky tests are detected 19 | - Example configuration in `playwright.config.ts` 20 | 21 | 4. **New API Request Options** 22 | - `maxRedirects` option in `apiRequest.newContext()` 23 | - Example in `new-features.spec.ts` 24 | 25 | 5. **Enhanced Aria Snapshot References** 26 | - New `ref` option in `locator.ariaSnapshot()` 27 | - Example in `new-features.spec.ts` 28 | 29 | 6. **Test Result Annotations** 30 | - New `testResult.annotations` property for accessing annotations 31 | - Useful for custom reporters and test analytics 32 | - Example in `new-features.spec.ts` 33 | 34 | 7. **Breaking Changes** 35 | - Glob URL patterns in routing no longer support `?` and `[]` 36 | - `route.continue()` no longer allows overriding `Cookie` header 37 | - Examples of workarounds in `new-features.spec.ts` 38 | 39 | ## Additional Features (Not Implemented in Examples) 40 | 41 | 1. **Project-specific Worker Configuration** 42 | - New `testProject.workers` property to control workers per project 43 | - This is a configuration feature that can be set in playwright.config.ts 44 | 45 | 2. **HTML Reporter Negative Filtering** 46 | - New syntax for excluding tests from HTML report using `!` prefix 47 | - Filter by tag: `!@tag`, file: `!file.spec.ts`, or project: `!p:project` 48 | - This is used when running playwright with CLI options 49 | 50 | ## How to Run the Examples 51 | 52 | 1. Install dependencies: 53 | ```bash 54 | npm install 55 | ``` 56 | 57 | 2. Run the tests: 58 | ```bash 59 | npx playwright test 60 | ``` 61 | 62 | 3. View the test report: 63 | ```bash 64 | npx playwright show-report 65 | ``` 66 | 67 | ## Notes 68 | 69 | - The examples use the Playwright website (playwright.dev) as a test target 70 | - Some features are demonstrated through configuration options 71 | - The tests include both simple and complex examples of each new feature -------------------------------------------------------------------------------- /v1.52.0-examples/new-features.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('Playwright v1.52.0 New Features', () => { 4 | test('New expect().toContainClass() method', async ({ page }) => { 5 | // Setup a page with elements having multiple classes 6 | await page.setContent(` 7 |
Task 1
8 |
Task 2
9 | `); 10 | 11 | // Using toContainClass to check individual classes 12 | const firstItem = page.locator('.todo-item').first(); 13 | await expect(firstItem).toContainClass('done'); // Checks just for 'done' class 14 | await expect(firstItem).toContainClass('important'); // Checks just for 'important' class 15 | 16 | // Compare with toHaveClass which needs all classes to match 17 | await expect(firstItem).toHaveClass('todo-item important done high-priority'); 18 | 19 | // Check second item 20 | const secondItem = page.locator('.todo-item').nth(1); 21 | await expect(secondItem).toContainClass('pending'); 22 | await expect(secondItem).not.toContainClass('done'); 23 | }); 24 | 25 | test('Enhanced Aria Snapshots with /children and /url', async ({ page }) => { 26 | // Setup a page with nested elements and links 27 | await page.setContent(` 28 | 36 | `); 37 | 38 | // Using the new aria snapshot format with /children and /url 39 | await expect(page.locator('nav')).toMatchAriaSnapshot(` 40 | - navigation: 41 | - list: 42 | - /children: equal 43 | - listitem: Feature A 44 | - listitem: 45 | - link "Feature B": 46 | - /url: "https://playwright.dev" 47 | `); 48 | }); 49 | 50 | test('New maxRedirects option in apiRequest', async ({ request }) => { 51 | // Testing maxRedirects option with a redirect chain 52 | const response = await request.get('http://httpbin.org/redirect/2', { 53 | maxRedirects: 2 54 | }); 55 | await expect(response).toBeOK(); 56 | 57 | // Should fail with too many redirects 58 | try { 59 | await request.get('http://httpbin.org/redirect/3', { 60 | maxRedirects: 2 61 | }); 62 | } catch (error) { 63 | await expect(error.message).toContain('Max redirect count exceeded'); 64 | } 65 | }); 66 | 67 | test('New ref option in locator.ariaSnapshot()', async ({ page }) => { 68 | // Setup a simple page with a button 69 | await page.setContent(` 70 |
71 | 72 |
73 | `); 74 | 75 | // Generate aria snapshot with ref enabled 76 | const snapshot = await page.locator('[role="main"]').ariaSnapshot({ 77 | ref: true 78 | }); 79 | 80 | // Log the snapshot with references 81 | console.log(snapshot); 82 | 83 | // Verify the snapshot contains references in the [ref=] format 84 | expect(snapshot).toContain('[ref='); 85 | 86 | // The snapshot should include the main element with its reference 87 | expect(snapshot).toMatch(/main \[ref=.*\]:/); 88 | 89 | // And the button element with its reference 90 | expect(snapshot).toMatch(/button "Save" \[ref=.*\]/); 91 | 92 | // We can verify the button directly using standard locators 93 | const saveButton = page.locator('button[aria-label="Save"]'); 94 | await expect(saveButton).toHaveText('Save'); 95 | 96 | // Extract the reference IDs for documentation or other purposes 97 | const mainMatch = snapshot.match(/main \[ref=([^\]]+)\]/); 98 | const buttonMatch = snapshot.match(/button "Save" \[ref=([^\]]+)\]/); 99 | 100 | if (mainMatch && buttonMatch) { 101 | console.log(`Main element ref ID: ${mainMatch[1]}`); 102 | console.log(`Button element ref ID: ${buttonMatch[1]}`); 103 | 104 | // The ref IDs can be used in documentation or for cross-referencing 105 | // elements in complex snapshots 106 | } 107 | }); 108 | 109 | test('testResult.annotations - accessing test annotations', async ({ page }) => { 110 | // This would typically be used in a custom reporter 111 | // Example of how to access annotations in a reporter: 112 | /* 113 | // In a custom reporter implementation: 114 | onTestEnd(test, result) { 115 | // New in v1.52: Access annotations for each test retry 116 | for (const annotation of result.annotations) { 117 | console.log(`Annotation: ${annotation.type} - ${annotation.description}`); 118 | } 119 | } 120 | */ 121 | 122 | // In an actual test, you can add annotations like this: 123 | test.info().annotations.push({ type: 'issue', description: 'https://github.com/org/repo/issues/123' }); 124 | test.info().annotations.push({ type: 'feature', description: 'login' }); 125 | 126 | // Run a sample test 127 | await page.goto('https://playwright.dev'); 128 | await expect(page).toHaveTitle(/Playwright/); 129 | }); 130 | 131 | test('Breaking Changes: Glob URL patterns', async ({ page }) => { 132 | // NOTE: In v1.52.0, glob URL patterns in route() don't support ? and [] anymore 133 | // Old way (no longer works): 134 | // await page.route('**/api/items?id=[0-9]+', route => route.fulfill({ body: '[]' })); 135 | 136 | // First navigate to a real URL to establish a base 137 | await page.goto('https://playwright.dev'); 138 | 139 | // Set up the route before content is loaded - use a simpler pattern 140 | await page.route(/.*playwright\.dev\/api\/items\?id=\d+/, route => { 141 | console.log('Route handler triggered for:', route.request().url()); 142 | return route.fulfill({ 143 | status: 200, 144 | body: JSON.stringify([{ id: 1, name: 'Item 1' }]), 145 | contentType: 'application/json' 146 | }); 147 | }); 148 | 149 | // Test the route with a mock API request 150 | await page.setContent(` 151 | 152 |
Loading...
153 | 169 | `); 170 | 171 | // Check that our routing worked, with a longer timeout 172 | await expect(page.locator('#result')).toContainText('Item 1', { timeout: 10000 }); 173 | }); 174 | 175 | test('Breaking Change: route.continue() and Cookie header', async ({ page, context }) => { 176 | // In v1.52.0, route.continue() no longer allows overriding Cookie header 177 | // The cookies must be set via browserContext.addCookies() instead 178 | 179 | // Set cookies properly via context 180 | await context.addCookies([ 181 | { name: 'sessionId', value: '123456', url: 'https://playwright.dev' } 182 | ]); 183 | 184 | // Route to demonstrate the changes 185 | await page.route('**/api/user', async route => { 186 | // This would no longer work in v1.52.0: 187 | // await route.continue({ headers: { 'Cookie': 'sessionId=abcdef' } }); 188 | 189 | // Instead, just continue the route (cookies from browser context will be used) 190 | await route.continue(); 191 | }); 192 | 193 | // The test itself 194 | await page.goto('https://playwright.dev'); 195 | }); 196 | }); -------------------------------------------------------------------------------- /v1.52.0-examples/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './', 5 | fullyParallel: true, 6 | forbidOnly: !!process.env.CI, 7 | retries: process.env.CI ? 2 : 0, 8 | workers: 4, // Global worker limit 9 | reporter: [ 10 | ['html'], 11 | ['list'] // Add list reporter for console output 12 | ], 13 | use: { 14 | baseURL: 'https://playwright.dev', 15 | trace: 'on-first-retry', 16 | }, 17 | 18 | // New in v1.52: Project-specific worker counts 19 | projects: [ 20 | { 21 | name: 'chromium-parallel', 22 | use: { ...devices['Desktop Chrome'] }, 23 | // This project gets 2 workers (half of global limit) 24 | testMatch: /.*\.parallel\.spec\.ts/, 25 | workers: '50%' 26 | }, 27 | { 28 | name: 'chromium-serial', 29 | use: { ...devices['Desktop Chrome'] }, 30 | // This project gets 1 worker for tests that need to run serially 31 | testMatch: /.*\.serial\.spec\.ts/, 32 | workers: 1 33 | }, 34 | { 35 | name: 'firefox', 36 | use: { ...devices['Desktop Firefox'] }, 37 | }, 38 | { 39 | name: 'webkit', 40 | use: { ...devices['Desktop Safari'] }, 41 | }, 42 | ], 43 | 44 | // New in v1.52: Fail if any tests are flaky 45 | failOnFlakyTests: true, 46 | 47 | // Useful for debugging 48 | timeout: 30000, 49 | expect: { 50 | timeout: 5000 51 | }, 52 | }); --------------------------------------------------------------------------------