From 71222a9c533ffc2950f83d6b889b9bb0545ef4e7 Mon Sep 17 00:00:00 2001 From: stefan judis Date: Thu, 2 Oct 2025 08:50:06 +0200 Subject: [PATCH 01/34] Improve Playwright Check Suites docs --- .../playwright-checks/add-to-group.mdx | 17 +- .../playwright-checks/configuration.mdx | 122 ++--- .../playwright-checks/custom-dependencies.mdx | 14 +- .../playwright-checks/overview.mdx | 49 +- .../playwright-checks/quickstart.mdx | 156 ------- .../playwright-checks/test-organization.mdx | 426 ++++++++++++++++++ docs.json | 1 + images/playwright-tests-sub-set.jpg | Bin 0 -> 568425 bytes images/pwn-environment-grouping.png | Bin 0 -> 134636 bytes images/pwn-test-result.png | Bin 0 -> 341806 bytes images/pwn-urgency-grouping.png | Bin 0 -> 123012 bytes images/pwn-use-case-grouping.png | Bin 0 -> 193125 bytes 12 files changed, 540 insertions(+), 245 deletions(-) delete mode 100644 detect/synthetic-monitoring/playwright-checks/quickstart.mdx create mode 100644 detect/synthetic-monitoring/playwright-checks/test-organization.mdx create mode 100644 images/playwright-tests-sub-set.jpg create mode 100644 images/pwn-environment-grouping.png create mode 100644 images/pwn-test-result.png create mode 100644 images/pwn-urgency-grouping.png create mode 100644 images/pwn-use-case-grouping.png diff --git a/detect/synthetic-monitoring/playwright-checks/add-to-group.mdx b/detect/synthetic-monitoring/playwright-checks/add-to-group.mdx index 9fdc954..7bbb725 100644 --- a/detect/synthetic-monitoring/playwright-checks/add-to-group.mdx +++ b/detect/synthetic-monitoring/playwright-checks/add-to-group.mdx @@ -14,9 +14,9 @@ You can define a new group, or associate a Playwright Check Suite to an existing ### 1. Create a group for your checks -To define a new group, create a group file, for example `website-group.check.ts`. +Create a new file to create and configure your new group. -```typescript +```typescript website-group.check.ts import { CheckGroup } from 'checkly/constructs' export const myGroup = new CheckGroup('production-group', { @@ -39,17 +39,15 @@ export const myGroup = new CheckGroup('production-group', { ``` -Learn more about [using groups](/cli/constructs-reference/#checkgroup) to unify checks in Checkly. +Learn more about [using groups](/constructs/check-group-v2) to unify checks in Checkly. ### 2. Associate the group to the check When specifying your Playwright Check Suite, you can reference the new or existing group, using its name: -```typescript -import { defineConfig } from 'checkly' - -const config = defineConfig({ +```typescript checkly.config.ts highlight={13} +export default defineConfig({ logicalId: 'checkly-website', projectName: 'checkly-website', checks: { @@ -65,9 +63,6 @@ const config = defineConfig({ }, ], }, - cli: { - runLocation: 'us-east-1', - }, }) export default config @@ -80,4 +75,4 @@ npx checkly deploy --preview # confirm what will be deployed npx checkly deploy # deploy ``` -You can now see your Playwright Check Suite in your group. +You can now see your Playwright Check Suite assigned to your group. diff --git a/detect/synthetic-monitoring/playwright-checks/configuration.mdx b/detect/synthetic-monitoring/playwright-checks/configuration.mdx index d0bbf0f..3a9f2a0 100644 --- a/detect/synthetic-monitoring/playwright-checks/configuration.mdx +++ b/detect/synthetic-monitoring/playwright-checks/configuration.mdx @@ -8,17 +8,17 @@ import PlaywrightCheckSuiteBetaNote from '/snippets/playwright-check-suite-beta- -Use the `checkly.config.ts/js` file to define your Playwright Check Suite. - -Each Playwright Check Suite can be connected to references in your `playwright.config.ts/js` file. +Use the `checkly.config.ts/js` file to define your Playwright Check Suite. Each Playwright Check Suite can be connected to references in your `playwright.config.ts/js` file. + During the Beta, **a Playwright Check Suite can run up to 20 minutes**. This limit can be adjusted based on feedback. + ## Playwright Check Suite definition To add Playwright Check Suites to your Checkly monitoring setup, specify the path to your `playwright.config.ts/js` and add a new `playwrightChecks` property to the existing `checks` configuration in your `checkly.config.ts/js`. -```typescript +```typescript checkly.config.ts highlight={8-14} import { defineConfig } from 'checkly' import { Frequency } from 'checkly/constructs' @@ -29,11 +29,8 @@ export default defineConfig({ playwrightConfigPath: './playwright.config.ts', playwrightChecks: [ { - name: 'critical-tagged', - pwTags: 'critical', - pwProjects: 'chromium', - frequency: Frequency.EVERY_1M, - locations: ['us-east-1', 'eu-west-1','eu-central-1', 'ap-south-1'], + name: 'all-pwt-tests', + locicalId: 'all-tests', } ], }, @@ -49,41 +46,48 @@ Other available properties like `frequency`, `alertChannels` or `locations` are ## Playwright references -The Checkly infrastructure will run and deploy all your existing Playwright tests (similar to what `npx playwright test` runs) as monitors if you don't reference Playwright projects or tags. +Without limiting and selecting specific tests or Playwright projects, the Checkly infrastructure will run and deploy **all your existing Playwright tests** (similar to what `npx playwright test` runs) as monitors. Specify which tests should become part of global end-to-end monitoring by defining these properties: -* `pwProjects` --- select existing project names from your Playwright configuration to create a Playwright Check Suite. +* `pwProjects`: select existing project by name from your Playwright configuration to create a Playwright Check Suite. -* `pwTags` --- select test tags that will be grouped into a Playwright Check Suite. +* `pwTags`: select tagged test that will be grouped into a Playwright Check Suite. You can combine `pwTags` and `pwProjects` to generate your check suite, too. For example: -```typescript +```typescript checkly.config.ts highlight={11, 20} export default defineConfig({ // ... checks: { playwrightConfigPath: './playwright.config.ts', playwrightChecks: [ { - /** - * Run critical tagged tests in Chromium - * every minute from 4 locations. - */ - name: 'critical-tagged', - pwTags: '@critical', - pwProjects: 'chromium', - frequency: Frequency.EVERY_1M, - locations: ['us-east-1', 'eu-west-1','eu-central-1', 'ap-south-1'], + name: "Marketing Environment", + logicalId: "environment-marketing-suite", + // Run the `environment-marketing` Playwright project + // in two locations every hour + pwProjects: ["environment-marketing"], + frequency: Frequency.EVERY_1H, + locations: ["us-west-1", "eu-west-2"], + }, + { + name: "Critical production tests", + logicalId: "critical-tests", + // Run `@critical` tagged Playwright tests + // in three locations every ten minutes + pwTags: ["@checkly"], + frequency: Frequency.EVERY_10M, + locations: ["us-west-1", "eu-west-2", "af-south-1"], }, ], }, }); ``` -> We recommend using `pwTags` and `pwProjects` to group your Playwright tests into different monitoring levels with different monitoring settings. For example, critical application functionality may be monitored best with short monitoring frequencies to lower the mean time to detect (MTTD). +Learn more about [best practices to organize and structure your Playwright tests](/detect/synthetic-monitoring/playwright-checks/test-organization/) for synthetic monitoring. ## Monitoring customizations @@ -100,7 +104,7 @@ A Playwright Check Suite inherits multiple properties from [the abstract `Check` - `retryStrategy` - `alertEscalationPolicy` -Check [the reference documentation](/cli/constructs-reference/#check) for more information on these settings. +Check [the reference documentation](/constructs/project#param-checks-playwright-checks) for more information on these settings. Additionally, Playwright Check Suites provide specific configuration for your Playwright monitoring. @@ -110,36 +114,42 @@ Additionally, Playwright Check Suites provide specific configuration for your Pl * `groupName:` The group this check belongs to. -```typescript -checks: { - playwrightConfigPath: './playwright.config.ts', // specify your config file - playwrightChecks: [ - { - /** - * Run `@e2e` tagged tests across browsers - * every 5 minute from 4 locations. - */ - // Human readable name - name: 'E2E test suite', - // Reference id - logicalId: 'e2e-test-suite', - // Playwright project references defined in `playwright.config` - pwProjects: ['chromium', 'firefox', 'webkit'], - // Playwright tags defined in `spec` files - pwTags: '@e2e', - // Override default dependencies install command - installCommand: 'npm install --dev', - // Override the default test command - testCommand: 'npx playwright test --grep@checkly --config=playwright.foo.config.ts', - // Activate the check so that it runs on a schedule, true by default - activated: true, - // Mute the check so that it doesn't send alerts - muted: false, - // Add a check to a group - groupName: 'production-group', - frequency: Frequency.EVERY_5M, - locations: ['us-east-1', 'eu-west-1','eu-central-1', 'ap-south-1'], - } - ] - }, +```typescript checkly.config.ts +export default defineConfig({ + // ... + checks: { + // specify your config file + playwrightConfigPath: './playwright.config.ts', + playwrightChecks: [ + { + /** + * Run `@e2e` tagged tests across browsers + * every 5 minute from 4 locations. + */ + // Human readable name + name: 'E2E test suite', + // Reference id + logicalId: 'e2e-test-suite', + // Playwright project references defined in `playwright.config` + pwProjects: ['chromium', 'firefox', 'webkit'], + // Playwright tags defined in `spec` files + pwTags: '@e2e', + // Override default dependencies install command + installCommand: 'npm install --dev', + // Override the default test command + testCommand: 'npx playwright test --grep@checkly --config=playwright.foo.config.ts', + // Activate the check so that it runs on a schedule, true by default + activated: true, + // Mute the check so that it doesn't send alerts + muted: false, + // Add a check to a group + groupName: 'production-group', + // Specify a monitoring frequency + frequency: Frequency.EVERY_5M, + // Define locations to monitor from + locations: ['us-east-1', 'eu-west-1','eu-central-1', 'ap-south-1'], + } + ] + }, +} ``` diff --git a/detect/synthetic-monitoring/playwright-checks/custom-dependencies.mdx b/detect/synthetic-monitoring/playwright-checks/custom-dependencies.mdx index ed80f78..eb0b9c7 100644 --- a/detect/synthetic-monitoring/playwright-checks/custom-dependencies.mdx +++ b/detect/synthetic-monitoring/playwright-checks/custom-dependencies.mdx @@ -22,7 +22,9 @@ Whether you use [npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/), or } ``` -**By default, the Checkly infrastructure installs dependencies via npm.** + +By default, the Checkly infrastructure installs dependencies via npm. + However, if an alternative lock file (`pnpm-lock.json` or `yarn.lock`) is discovered, Checkly uses the matching package manager to install the project dependencies. @@ -32,7 +34,9 @@ However, if an alternative lock file (`pnpm-lock.json` or `yarn.lock`) is discov | `pnpm-lock.json` | pnpm | | `yarn.lock` | yarn | -> **For `pnpm` users:** if you define the same dependency in `dependencies` and `devDependencies`, please use a custom `installCommand` in your `checkly.config.ts|js` to register your package as a development dependency: `pnpm install --dev additional-package` + +**For `pnpm` users:** if you define the same dependency in `dependencies` and `devDependencies`, please use a custom `installCommand` in your `checkly.config.ts|js` to register your package as a development dependency: `pnpm install --dev additional-package` + ## Using private dependencies @@ -87,11 +91,11 @@ registry=https://yourcompany.jfrog.io/artifactory/api/npm/yourcompany.jfrog.io/a ### Include custom configuration files -To make the Checkly CLI and infrastructure aware of a custom package manager configuration for private packages, specify the additional files in the `checks.include` property in your `checkly.config.ts|js`. +To make the Checkly CLI and infrastructure aware of a custom package manager configuration for private packages, specify the additional files in the `checks.include` property in your `checkly.config.ts`. -Checkly CLI commands like `test` or `deploy` will then bundle and upload these files so they are available in the Checkly infrastructure when running essential commands like `install`. +Checkly CLI commands like `test` or `deploy` will bundle and upload these files so they are available in the Checkly infrastructure when running essential commands like `install`. -```typescript +```typescript checkly.config.ts export default defineConfig({ checks: { playwrightConfigPath: './playwright.config.ts', diff --git a/detect/synthetic-monitoring/playwright-checks/overview.mdx b/detect/synthetic-monitoring/playwright-checks/overview.mdx index 31aa49c..2630d3a 100644 --- a/detect/synthetic-monitoring/playwright-checks/overview.mdx +++ b/detect/synthetic-monitoring/playwright-checks/overview.mdx @@ -14,13 +14,17 @@ import PlaywrightCheckSuiteBetaNote from '/snippets/playwright-check-suite-beta- ## What are Playwright Checks? -Playwright Checks enable you to run entire Playwright Suites or Projects as both pre-production tests and global production monitors, without rewriting your tests and configuration. +Playwright Check Suites enable you to run entire Playwright end-to-end test suites, Playwright projects or tagged tests as pre-production tests and global production monitors, without rewriting your tests and configuration. -Unlike [Browser Checks](detect/synthetic-monitoring/browser-checks/overview), Playwright Checks are not limited to a single test spec running in an async browser session and our preinstalled runtime dependencies. Playwright Checks enable you to run Playwright projects complete with all of your required dependencies, private packages, storage, data, and more. It's a complete native way to run Playwright in production, enabling you to detect issues with your applications or services even in the most complex scenarios. +Unlike [Browser Checks](detect/synthetic-monitoring/browser-checks/overview), Playwright Check Suites are not limited to a single test spec running in an async browser session and our preinstalled runtime dependencies. Playwright Check Suites enable you to run Playwright projects complete with all of your required dependencies, private packages, storage, data, and more and enable you to detect issues with your applications or services even in the most complex scenarios. -Playwright Checks support for the full Playwright API and ecosystem, meaning you can use your existing tests and `playwright.config.ts` files as is. + + ![Playwright Check Suites test result](/images/pwn-test-result.png) + -**Playwright Checks are perfect for:** +**Playwright Check Suites are the native way to run Playwright in production** and support the full Playwright API and ecosystem, meaning you can use your existing tests and `playwright.config.ts` files as is. + +**Playwright Check Suites are perfect for:** - Converting existing E2E tests into monitoring - Testing complex user workflows across multiple browsers - Monitoring critical business processes @@ -31,7 +35,7 @@ Playwright Checks support for the full Playwright API and ecosystem, meaning you -Playwright Checks analyze your Playwright configuration and test files to understand your existing test structure. +Playwright Check Suites analyze your Playwright configuration and test files to understand your existing test structure. @@ -59,19 +63,30 @@ Receive notifications when tests fail or performance degrades, keeping your team Control which tests become monitoring checks using: -- **Tags**: Select tests marked with specific tags (e.g., `@critical`, `@smoke`) - **Projects**: Choose browser configurations from your Playwright config +- **Tags**: Select tests marked with specific tags (e.g., `@critical`, `@smoke`) - **Combinations**: Mix tags and projects for precise test selection -```typescript -// Example: Monitor critical tests in Chromium -{ - name: 'Critical User Flows', - pwTags: '@critical', - pwProjects: 'chromium', - frequency: Frequency.EVERY_1M, - locations: ['us-east-1', 'eu-west-1'] -} +```typescript checkly.config.ts +export default defineConfig({ + checks: { + playwrightChecks: [ + { + name: "Marketing Environment", + logicalId: "environment-marketing-suite", + // Select the tests defined in + // the `environment-marketing` Playwright project + pwProjects: ["environment-marketing"], + }, + { + name: "@critical tests", + logicalId: "critical tests", + // Select tests tagged as `@critical` + pwTags: ["@critical"], + }, + ], + }, +}) ``` ## Monitoring Configuration @@ -89,11 +104,11 @@ Customize monitoring behavior with: Ready to turn your Playwright tests into monitoring? Start with our quickstart guide to get running in 5 minutes. - + Get from zero to deployed monitoring in 5 minutes - + Complete configuration options and examples diff --git a/detect/synthetic-monitoring/playwright-checks/quickstart.mdx b/detect/synthetic-monitoring/playwright-checks/quickstart.mdx deleted file mode 100644 index 8014701..0000000 --- a/detect/synthetic-monitoring/playwright-checks/quickstart.mdx +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: 'Playwright Checks Quickstart' -description: 'Get started with Playwright Check Suites in 5 minutes. Turn your existing Playwright tests into globally distributed monitoring.' -sidebarTitle: 'Quickstart' ---- - -import PlaywrightCheckSuiteBetaNote from '/snippets/playwright-check-suite-beta-note.mdx' - - - -Use your existing Playwright tests as live, scheduled monitoring checks. No rewrites required. Get from zero to deployed checks in 5 minutes. - - -Before you begin, you need: - -- A Checkly account -- A repository using Playwright for E2E tests and a Playwright configuration file - - -## Steps - -### 1. Install the Checkly CLI - -```bash -npm install --save-dev checkly -``` - -### 2. [Optional] If you're using TypeScript - -If you're using TypeScript, install the dev dependencies [`jiti`](https://www.npmjs.com/package/jiti) and [`typescript`](https://www.npmjs.com/package/typescript). These help the CLI bundle your typescript config and test files correctly. - -```bash -npm install --save-dev jiti typescript -``` - -### 3. Test with `pw-test` - -Run your Playwright test suite using `pw-test`. - -`pw-test` accepts both Checkly and Playwright command line arguments using the following syntax: - -`npx checkly pw-test --checkly-flag -- --playwright-flag`. Use `--` to separate Checkly and Playwright flags. - -The CLI command will then return a link to results, traces and more details: - -```bash -> npx checkly pw-test -- --project=chromium - -Parsing your Playwright project... ✅ - -Validating project resources... ✅ - -Bundling project resources... ✅ - -Running 1 checks in eu-west-1. - -playwright.config.ts - ✔ Playwright Test: --project=chromium (16s) - -Detailed session summary at: https://chkly.link/l/linkhere -``` - -### 4. Convert tests to checks - -Using `pw-test` with the `--create-check` flag will create a `checkly.config.ts` file if it doesn't exist, and will add the new check. Afterwards, you can tweak the monitoring configuration for your check. - -For example: - -```bash -> npx checkly pw-test --create-check -- --project=chromium -⚠ No Checkly config file found - -ℹ Creating a default checkly config file. - -Creating Checkly check from Playwright test... ✅ -``` - -Review the resulting check configuration in your `checkly.config.ts` file and make sure to tweak locations and schedule as needed. - -```typescript -import { defineConfig } from 'checkly' - -const config = defineConfig({ - logicalId: 'my-repo-name', - projectName: 'my-repo-name', - checks: { - playwrightConfigPath: './playwright.config.ts', - playwrightChecks: [ - { - logicalId: 'playwright-check-project-chromium', // tweak ID - name: 'Playwright Test: --project=chromium', // tweak name - testCommand: 'npx playwright test --project=chromium', // what we'll run as part of your check suite - locations: [ - 'eu-central-1', // add or change locations - ], - frequency: 10, // a custom per-check frequency - }, - ], - frequency: 10, // a global default frequency - locations: [ - 'us-east-1', // a global default location - ], - }, - cli: { - runLocation: 'us-east-1', // where test and pw-test will run - }, -}) - -export default config -``` - -### 5. Deploy your Playwright Check Suites - -Once you are ready to start monitoring your applications with these checks, deploy your Playwright Check Suite into global monitoring with `npx checkly deploy`. - -```bash -npx checkly deploy - -> You are about to deploy your project "playwright-check-suites" to account "Checkly E2E Prod". Do you want to continue? … yes - -Successfully deployed project "playwright-check-suite" to account "Checkly E2E Prod". -``` - -Once deployed, your checks will run on a schedule and results will appear in your [home dashboard](https://app.checklyhq.com/). - -### 6. Set up Alerts - -Configure alert channels and alert groups to get notified when checks fail. You can receive alerts via email, Slack, webhooks, and more. - -To set up alerts: - -1. Go to the Checkly dashboard. -2. Navigate to [**Alert Channels**](https://app.checklyhq.com/accounts/alerts/settings) to create a new channel. -3. Assign channels to your project or individual checks. - -Learn more in the [alerting guide](https://www.checklyhq.com/docs/alerts/). - -## Next Steps - - - -Complete CLI options and configuration reference - - - -Organize checks with groups for better management - - - -Use private packages and custom registries - - - -Run checks from your own infrastructure - - diff --git a/detect/synthetic-monitoring/playwright-checks/test-organization.mdx b/detect/synthetic-monitoring/playwright-checks/test-organization.mdx new file mode 100644 index 0000000..4585827 --- /dev/null +++ b/detect/synthetic-monitoring/playwright-checks/test-organization.mdx @@ -0,0 +1,426 @@ +--- +title: Organize your tests and monitors +sidebarTitle: Test organization +tags: ['synthetic-monitoring', 'playwright-checks'] +--- + +import PlaywrightCheckSuiteBetaNote from '/snippets/playwright-check-suite-beta-note.mdx' + + + +Playwright Check Suites enable you to reuse your existing Playwright end-to-end tests for global synthetic monitoring. + + + Playwright Check Suites should be a subset of the entire Playwright end-to-end test suite. + + +Run your entire Playwright end-to-end test suite, pick specific tests or group multiple tests together to run them as global synthetic monitoring and get alerted when your Playwright-based monitors fail. + +## How to include Playwright tests in your Playwright Check Suite + +The quickest way to reuse your existing Playwright end-to-end tests as Playwright Check Suites is to specify your Playwright config path and a single Playwright Check Suite in your `checkly.config.ts`. + +```typescript highlight={3-10} +export default defineConfig({ + checks: { + playwrightConfigPath: "./playwright.config.ts", + playwrightChecks: [ + // Playwright Check Suite without + // additional configuration or test selection + { + name: "Multiple Browser Suite", + logicalId: "browser-compat-e2e-suite", + }, + ], + }, +}) +``` + +Each Playwright Check Suite requires a `name` and `logicalId`. Without providing additional configuration, the Playwright Check Suite will run all tests defined in the `playwright.config.ts`. It will include the tests that would also be run by `npx playwright test`. + + +While possible it's possible to run **all** your Playwright tests, we recommend to group your tests into multiple Playwright Check Suites with different monitoring configuration. + +This approach leads to faster Mean Time to Detect (MTTD), overall transparency and more granular monitoring information. + + +## How to Organize your tests in Playwright Check Suites + +Use the `pwProjects` and `pwTags` properties to group and organize your Playwright Check Suites. + +### Group your Tests using Playwright Projects (recommended) + +Select existing Playwright tests and run them in a Playwright Check Suite by relying on [Playwright projects](https://playwright.dev/docs/test-projects). + +Use the `pwProjects` option to choose Playwright projects defined in the `playwright.config.ts` by name. + + +```typescript checkly.config.ts highlight={10} +export default defineConfig({ + checks: { + playwrightConfigPath: "./playwright.config.ts", + playwrightChecks: [ + { + name: "Multiple Browser Suite", + logicalId: "browser-compat-e2e-suite", + // Specify which projects should be + // included in the Playwright Check Suite + pwProjects: ["chromium", "firefox"], + frequency: Frequency.EVERY_10M, + }, + ], + }, +}) +``` + +```typescript playwright.config.ts +export default defineConfig({ + projects: [ + { + name: "chromium", + testMatch: /.*\/example-1\/.*\.spec\.ts/, + use: { + ...devices["Desktop Chrome"], + }, + }, + { + name: "firefox", + testMatch: /.*\/example-1\/.*\.spec\.ts/, + use: { + ...devices["Desktop Firefox"], + }, + }, + ], +}) +``` + + + +Playwright projects are most commonly used to configure different browser settings, but they can be used to configure different environments, authentication states, or any other Playwright configuration. + +[Find more Playwright project examples below](#recommendations-and-examples). + + +### Group your Tests using Playwright tags + +Select existing Playwright tests and run them in a Playwright Check Suite by relying on [Playwright test annoations and tags](https://playwright.dev/docs/test-annotations#tag-tests). + + +```typescript checkly.config.ts highlight={9} +export default defineConfig({ + checks: { + playwrightChecks: [ + { + name: "Tagged Checkly Tests", + logicalId: "tagged-tests", + // Specify which tagged tests should be + // included in the Playwright Check Suite + pwTags: ["@checkly"], + frequency: Frequency.EVERY_1H, + }, + ], + }, +}) +``` + +```typescript example.spec.ts highlight={6} +import { expect, test } from "@playwright/test" + +test( + "Visit Checkly home page", + // Annotate a test to reuse is for synthetic monitoring + { tag: "@checkly" }, + async ({ page }) => { + await page.goto("/") + + // More test code ... + } +) +``` + + + +Playwright Check Suites let you filter existing tests using `pwTags`. However, we recommend to always start with a separated Playwright project and reuse it via `pwProjects` in your `checkly.config.ts`. + +This approach improves the maintainability and separates the Playwright test configuration (`playwright.config.ts`) from the Checkly monitoring configuration (`checkly.config.ts`). + + +## Recommendations and Examples + +Synthetic monitoring with Playwright Check Suites is most effective when you group and organize your Playwright tests to reflect your monitoring needs. The ideal configuration varies and depends on your application. + + +[The following examples are also available on GitHub](https://github.com/checkly/playwright-check-suite-examples/) if you want to see them in action. + + +### Group different environments + +If you want to use your Playwright tests to monitor different environments (e.g., `staging, production, ...` or `/de, /fr, ...`, ), create Playwright projects for each environment target. + + + ![Playwright Check Suites grouped by environment](/images/pwn-environment-grouping.png) + + +Specify a different `baseURL` options for each project. + +```typescript playwright.config.ts highlight={7,14} +export default defineConfig({ + projects: [ + { + name: "environment-staging", + use: { + ...devices["Desktop Chrome"], + baseURL: "https://staging.checklyhq.com" + }, + }, + { + name: "environment-production", + use: { + ...devices["Desktop Chrome"], + baseURL: "https://checklyhq.com", + }, + }, + ], +}) +``` + + +`baseURL` is one example of using advanced Playwright configuration. + +The Playwright test runner can always be configured on a global, project and test level. If neccessary, configure your project: + +- to increase timeouts for a slow environment (e.g. `timeout` or `expect.timeout`) +- to run more retries for a instable environment (e.g. `retries`) +- to use different authentication for each environment (e.g. `storageState`) + + +And configure different monitoring configuration for each Playwright project in your Playwright Check Suite. + +```typescript checkly.config.ts highlight={8-12, 17-21} +export default defineConfig({ + checks: { + playwrightConfigPath: "./playwright.config.ts", + playwrightChecks: [ + { + name: "Staging Environment", + logicalId: "environment-staging", + // Pick the Playwright project for the staging environment + pwProjects: ["environment-staging"], + // Run this Playwright Check Suite in one location every hour + frequency: Frequency.EVERY_1H, + locations: ["us-west-1", "eu-west-2", "af-south-1"], + }, + { + name: "Production Environment", + logicalId: "environment-production", + // Pick the Playwright project for the production environment + pwProjects: ["environment-production"], + // Run this Playwright check suite in three locations every ten minutes + frequency: Frequency.EVERY_10M, + locations: ["us-west-1", "eu-west-2", "af-south-1"], + }, + ], + }, +}) +``` + +### Group specific application areas + +If you want to use your Playwright tests to monitor different application areas (e.g. customer functionality, search, etc.), create [Playwright projects](https://playwright.dev/docs/test-projects) and select files via [Playwright annotations and tags](https://playwright.dev/docs/test-annotations#tag-tests). + + + ![Playwright Check Suites monitoring different application areas.](/images/pwn-use-case-grouping.png) + + +Select and group tests via Playwright projects. + +```typescript playwright.config.ts highlight={6,11} +export default defineConfig({ + projects: [ + { + name: "user-sign-up", + use: { ...devices["Desktop Chrome"] }, + grep: /@signup/, + }, + { + name: "search", + use: { ...devices["Desktop Chrome"] }, + grep: /@search/, + }, + ], +}) +``` + +And configure different monitoring configuration for each Playwright project in your Playwright Check Suite. + +```typescript checkly.config.ts highlight={8-12, 17-21} +export default defineConfig({ + checks: { + playwrightConfigPath: "./playwright.config.ts", + playwrightChecks: [ + { + name: "Staging Environment", + logicalId: "user-sign-up", + // Pick the Playwright project for the staging environment + pwProjects: ["user-sign-up"], + // Run this Playwright Check Suite in one location every hour + frequency: Frequency.EVERY_1H, + locations: ["us-west-1"], + }, + { + name: "Production Environment", + logicalId: "search", + // Pick the Playwright project for the production environment + pwProjects: ["search"], + // Run this Playwright check suite in three locations every ten minutes + frequency: Frequency.EVERY_10M, + locations: ["us-west-1", "eu-west-2", "af-south-1"], + }, + ], + }, +}) +``` + +### Group tests dependending on urgency or execution time + +If you want to use your Playwright tests to monitor highly critical user flows (e.g. login) on a high interval with critical alert settings and still monitor uncritical flows (e.g. profile updates) on a lower interval, create [Playwright projects](https://playwright.dev/docs/test-projects) and select files via [Playwright annotations and tags](https://playwright.dev/docs/test-annotations#tag-tests). + + + ![Two Playwright Check Suites grouped by urgency](/images/pwn-urgency-grouping.png) + + +Select and group tests via Playwright projects. + +```typescript playwright.config.ts highlight={6,11} +export default defineConfig({ + projects: [ + { + name: "critical", + use: { ...devices["Desktop Chrome"] }, + grep: /@critical/, + }, + { + name: "important", + use: { ...devices["Desktop Chrome"] }, + grep: /@important/, + }, + ], +}) +``` + +And configure different monitoring configuration for each Playwright project in your Playwright Check Suite. + +```typescript checkly.config.ts highlight={8-14, 19-25} +export default defineConfig({ + checks: { + playwrightConfigPath: "./playwright.config.ts", + playwrightChecks: [ + { + name: "Critical Application Flows", + logicalId: "critical", + // Pick the `critical` project + pwProjects: ["critical"], + // Run this Playwright Check Suite in three location every 5 minutes + frequency: Frequency.EVERY_5M, + locations: ["us-west-1", "eu-west-2", "af-south-1"], + // Assign critical alert channels + alertChannels: [criticalPagerDuty, criticalSlack], + }, + { + name: "Important Application Flows", + logicalId: "important", + // Pick the `important` project + pwProjects: ["important"], + // Run this Playwright check suite in two locations every thirty minutes + frequency: Frequency.EVERY_1H, + locations: ["us-west-1", "eu-west-2"], + // Assign important alert channels + alertChannels: [importantPagerDuty, importantSlack], + }, + ], + }, +}) +``` + + +Critical Playwright Check Suites running on high intervals should only include fast test cases to guarantee a short Mean Time to Detect (MTTD). + +**If a Check Suite run takes longer than the specified interval frequency, runs will be skipped.** + + +### Group tests to reuse authentication states + +If your existing Playwright tests require authentication and a login step, use [Playwright project dependencies and storage state](https://playwright.dev/docs/test-global-setup-teardown#option-1-project-dependencies) to log in once and reuse the browser session information. + +Create a Playwright test that performs your login actions and calls [`context().storageState()`](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state). + +```typescript login.setup.ts +const AUTH_FILE = ".auth/user.json" + +setup("Log into Checkly", async ({ page }) => { + await page.goto("/") + + // Perform your login actions + // ... + + // Use storage state to write the browser state to disk + await page.context().storageState({ path: AUTH_FILE }) +}) +``` + +Configure two new Playwright projects. The first one performs the login actions to persist the browser state, while the other one imports the browser state to avoid the login steps. + +```typescript playwright.config.ts highlight={15, 18} +export default defineConfig({ + projects: [ + { + name: "login-setup", + use: { + ...devices["Desktop Chrome"], + baseURL: "https://checklyhq.com" + }, + }, + { + name: "logged-in-tests", + use: { + ...devices["Desktop Chrome"], + // 2. Reuse the written browser state to avoid login steps + storageState: path.resolve(__dirname, AUTH_FILE), + }, + // 1. Set the project doing the login as a dependency + dependencies: ["login-setup"], + }, + ], +}) +``` + +This Playwright setup will always run the `login-setup` project before running `logged-in-tests` so that the authentication will be available and the browser state can be reused. + +Reuse the `logged-in-tests` project in your Checkly configuration. + +```typescript checkly.config.ts highlight={9} +export default defineConfig({ + checks: { + playwrightChecks: [ + { + name: "Logged-in tests", + logicalId: "logged-in-tests", + // Run the `logged-in-tests` project which will automatically run + // `login-setup` to write the authentication file to disk + pwProjects: ["logged-in-tests"], + frequency: Frequency.EVERY_1H, + }, + ], + }, +}) +``` + +Project dependencies and storage state work the same way as your standard Playwright project. + + +Check this video to learn more about [Playwright `storageState` and how to configure Playwright project dependencies](https://www.youtube.com/watch?v=nSHPCLUwwVk). + diff --git a/docs.json b/docs.json index f34ccbe..23e727d 100644 --- a/docs.json +++ b/docs.json @@ -224,6 +224,7 @@ "pages": [ "detect/synthetic-monitoring/playwright-checks/overview", "detect/synthetic-monitoring/playwright-checks/configuration", + "detect/synthetic-monitoring/playwright-checks/test-organization", "detect/synthetic-monitoring/playwright-checks/add-to-group", "detect/synthetic-monitoring/playwright-checks/custom-dependencies" ] diff --git a/images/playwright-tests-sub-set.jpg b/images/playwright-tests-sub-set.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d549b81c897203cb5fe15515ad435df5e97d0a96 GIT binary patch literal 568425 zcmeFYc{H2r8!nvAMO#%htL#?IQw=dSm0jJaC@SVLMU_xB69l`hq2^s z@f|tr27y7KBPagrvqAsub>!%=<0np@I?ct+!?}TY9(44`v13P%A3Jg4_;JqNQJl{~ z$N5fNxU72fB)^60sVe~&)gC`DIW4JQ+bMt;pEdZDY8lm9>qnoxOwmeUAs8UMTON;E>QTba=$m*tqzFXNgG}nOWI6xi4Pk zy(%rkmg6e$m38$E#Kxw#%`IJ|ZgNlWhra$%%GmhCbp&0&EpUWe@Tvw?it$fq|B38>F0d#6OOgHe!2XZ8ra@_@NX{oHy!?45&o@)|5n3)tKq-Z@ZW0q|3fv@ zR!KYrU2GR;3DAWS+htjfjHdYZOBg6Uv|z6$k>yU?bf6e~3XYTtj+0p1+rGW1KI=4a zBB$6>=w&3{TS%}ay4Wu$D#XuE#)jua^2Y!xf4eh~hb}67dT?#b4IL{2!p66vNA8`W zTz_C_%5PX5qHtp~!>(2H6tAs7N{z|c_)>`oBEeL@K(ete6TV3lCc7XqEs_FJu%#O2 zvqR8RBJ2V92#|CL(myDGd75+n)gY1Ye%(3kLF@HQKHs08$mVa=xXR8Z&9*PXW zL4A%-EOxty(#x?bFBNS?qY@Kc{ASdOkE&N$3y%-(U?ePc6b@YCxE_SySK6lQQZdq( z%zm1Go~Su-e{3mIrN3T9sIKaUvE@ME7c+O^caag}IY+a8{=}0sqq+wGUTXzNlTx5OS39w*mL*_J-*jU3#JP))1c zhHrf9M3bKu8i+@mJR?~~=)QGwhB{@>eNS}|HoY)6e*ed)iu>MQ2QNCjJN^VW!n&$u z7GWEU3>keOcR>kZQKk2%DjR#s-|(1v-}&QJRk2{JCFXxUJlR$7+gvR8=B z4IS}14f`|K-=%l&RHZ|x8zFbzX?2cw(*ADF^7Q0xnDK^GSU=6y{c)c*=8ecI9TYJj zYWFMbI@M?n>{MMk7T$!0U-uXuSn?N5 z6P&l`xst%Tsb}0Ka?Nsb_bkqzv0WFxOf^$auJiKLP*2h1=I^#8?Q_3%AU-JMl5Gy^ zsp`?E&}XvilG<|5N8O}P_{nsxb?t?u(`Xw$WxrJZWww>?#F(0MPytx_Bxspgk|eKmu+ zt~va6AmI7Oz>Slx!nzi{_ujnyK*>a8Ytt(9TVl&H5dM}+zE|^YjU~t~;nNpEcaQvj z0>rtT%41mr!}Q1n^LR-{Q_mrYvp3F0p9R#M5tLiWBYut6Ym|;; zgyPFk=9MlppXY{Axaz5QZ{vs7>|BGJvNP+C9gdnjiN2DbJphK$XgcyX^*Tg%>ZiJ6ws zJ&M~D&_f4pBXf(O6W?ryPrO`ePO8{uk&dNXg$>i%6Im+8(>*_d1`PRwhHXF(RWE|}8VczUN# zCHz>gU?E?Ts&D#8%y~L#D&puGLwe}*YV=T)nopFz3eU>VmNDC=i3!_Kp@`X!p)O*? z@nE0A2TE?eYk)!mD!}%F$1f#}N!Q)(mR*JM1eK;>|1o(tc}o%=cgm#<^_JYPo~qq! zlO=DMk5eh6Rei_t-ba6#$UgCmaBE6GTW2Ry3eo0e5xf4DC?HR#ZJlco`j1-1B` z_nZ(TD(>eqsFAx=^-q>mOH^+>@fi|cG#90|Z#I_i@OHf6SMVX|6U&@FBSTfJIg#%n zTxF|t=ixQs6lG1K{LmvT_c(r%)UQ#|2iNQJCTeZ+Tc#}FD}xZg>m!R*!_Rik*f_s* zrL#VD4G0$dUm>{9bDW5w)AxV?Hg284zw7@WZ1GE-3~TD$ zP+Eu)Sa&(bsM*BsOQT`EdSVmGm>V>elV}_>1-L;y1PQF@9%Oemw0_#HIkz^a+A#j3 z&24nmVS4#ad;Jq8&!4V2$Nv(j2~FS$(3FmY+K43teK8azLTu8m#9O?uF0skV)$pcN zoAb&C;bhlvBHF~tP-s%?&A&cXLvLWVWJ_JLW_7dVzuC!$;K!01@IwXba$TsEMOnN| zsjXgp&FS6Yubylk^;qfiLW!SpQlV9h+dYJS~jGGKZhr8!|Pa0uFR5l1^`1nBAS zRLu*e26o$ayRNv>txw;mi))A9n@=ZL z(0$OyEPfJb+ho3xZ#V_)~?*Oan?cuo9=p!3X(sf zB?eu>GM0W-RUEk3^(zOJhDmXCm93gTZ^*~VSq7EQwGUaeH|cfN6yNyk|AIA-F&5d} zWX^13=(em2#q5h!jJqV~HKFaKO-Oym3Uo*1tvzuYOCI2F%bBmtRUbm80bUO%z=k!|EG zI33Io>g`1CsSPK=fKaH+A?PDE`tc#?JP+q9UL_wp1jRxQLBGNo9Sq?&upd`B@lc(? zY^$*7qZ}Cd_7HUc0BKLG2h#UdSdnzY^nd_yejgW+3D&8_<=P~pM7^nyau;vC(}kN0 zvsar;JRq~vor1o;elj6VuOHg}Ikl-9n8T~v&wyO;wX8N5i2LTv-tZK5L--j;cnHt# zk0#k_k$}!DGjHtz!nKl<8_RGs#z;Q&XMt12btT6o-zl%MAF#5RsUT+kL2=B(`8Ws9 z9>1v$nqevf{C+yNynk)kTu9KNHzA~AA)dBQDpI5~OV7S0QftoJ8wH%nT+Jh9H;g(C z`bBv?YFHbTQik9l{!>y}uKn`>f;O#c@@5lUfZFu^jz5{r-{^aPj<8oMZIF=|)>wruW~R4^cg zBOQX`^z;~ET`)eDQzL+1yyVl#j0j7BnwO&=L|uszGcZMMJ@Op;F=%$Z0+-5)O}d&m z(zE?F0D0>@i~rf;LznA33B4olO^zkto@K3D6na=#;WvIEVi#zXiSNtF2 zgNK)#+{hD!0;1OL#+_Z_tid5QQwTOYEtk#3*$XG+=$uiVT8K%D)&xwbe$Vvy?Eo;X z5lgRI{GB26tYPcI?ELKN93MR_+0i!s=N-zA&#p~5U?I|dC6`m#Qgs=Y3X1)S>0uzR+H2F_45g&~|IG1wE7?+d-2s}|aj(U`a1RbXnA^hSU&=AV7- zc}a2(ABBK`I2Cw}R1aZ=$ydic#2kJmhm9Sm)}-nc{0|Vte*_KQ#ZorUsYB2yuIO=C zt>@-2&2K81i2(xWb@N>vCm*u7U@1goQnV;11_(G&v?B#7Nu39q2)@{}O~PnssGOem znv;Apayg@4bK=piiLaxDslAd!&jjy^3NK6c1CSo?wQS$0F1r3rjjRa02B{d0KUW0i zv*j)rTd`phmk=qH5SfidF3YF5k60OCSOlG%fBz*1uq%^X1r#s(e{W?3S6byrhg49y zArkP0wn72!+9v@{-@OBumzGTS;t$eeyfh979UdKjb~HRe+x&dL$tb3#$F)YnVr!PQ z0#~&1=8DB9r=y7bH@&9Ib1y9vHaI(1ZRFdI%=KFOC}_UZ9En!w7O`athUML7&uJHL zZ1`g2YXtf$dkUsgzW6B{znz)3{FYmfA3TQ&+<}d4KkVH{$`98~GR$EsU-7q6WL} z2#v8k8()L|P3anUaZ`^3(qy-Gv;0Nxi2$=9c$Spb#oc;LV_O4j*Ho_1mK zw#oVOEuj#>?EL0m!@NLt9PP|JSC(ZzLl{M4*A6(KlR_kUUE`_~!(vh7b`OJ2dxDNk zUG0I?b z_|r*pi~QBFM5KeHktx^1w2J8xQ8ZEO{wLY2puw;x*QI&k@ZbD+X;k`HO=Snu6 z!yMPxgBDhYpzzJ`W}O@CN6zM#15WC%E2<4`Cp-&@dh&O@qEKT<^FeW~Ra+B`xcu`* zu?h_K&Sxdc(}iz64s`Lzb^=SQc!QJ1xoTJne!#=6satG86_ym3{`3&k#YypJ1?W%N z{7AqwhS>2YhRVyCYg;xy@8&!Jhc(5}Ob$V(4?&+{#13Ky&6#Q582znneixb8UcEcK z8+Kz4_hxT+qN7fomk{MRwU4Zdj3Ml+xNdZCM%-3h|MORK2sC~7>1F02$c8=BuUEer zl`zf`9Nt9F&+P2O+&9{q8kp^rFHB4;i@n+0pjX3zDqa64>F!t}kjzk_4tE}cPOgLb zqLt8xpiUT1I~X0d4`W~8y~HwL*u*UqpIkvsPgkeKVPhVmi@Tfl9we5>pw=Ub@=I3_ zLB8!zH(OqpxZ`HRbHn^^$(Zq~?WFdh3j~;_VTvaL zlSS3dTuNSwmD2Je6XRtB;yXu*WJI2o#F8jW%oi!94#BdNTfg7XjZyze~DQ+D(k^ZU)?%?EHoOK+}Tw{ zYsPX1b|Y{ieMYvzjiciP@z2m1`Hvpj9=~#sX@%N&t9V|K1Twt9u`X*-zwC?KTw>qV zxn#>ZpMe|ypLa*tB8Q-b&G~81-dnWvjm7=nP%Y)9<*e&ZsrZAxTdm8dXPrnmpr$+T z5R}Y9E~s$Fc+dQ85;+X~;S1%XDWu+z%7~HnyJ!l_Jf3*G?hs@G@MgM3WAhousa&)< z^?mu+rHjk@sfv1F0p#;G_uWJ{FFetwQqH-2lqE@KQTTRSo1{~g#MQbEL0;oYzJ^4H zY=Y~`PBnJJnmbKyZioMPCMBUE`7a0 zW4QSiFCc5g5=-9a#UOxVjAQl(nd}P#bQNYSJv}aDm=6E}l1RpbYEJ$0agcrqVHYo` zTtJ(2&;2(3;{}bO(b#KLJE?K{hp+pcaR`sE;da!6o>5bY=A-)}q! zH;wpFL2Ivv$11p@#`01aRz4$Gv(jKoW(L-J45xjwOTGZRDb&GH2fy02cJM(~iCTC1TXCm8bkm z%ZCio;=6ZRaZ5R)k>-sIADxBoSExkWBLz!uTjv$YcL`79xJQblt3`J&Cy=SM>M-++ zvH;7p9JF2EbvZdqO@auL|Le;|TuMI!c-RC^Aealtj}>@X5)3a?CsfNs%I)Y|UD>O; zW$Iks(IU$BgrQ;L95TTRsK8kKDog|;4NqV1fnD+d9xoo8+;-&ULpv@s#fO+Do~5p@ zhYwcI6%&ABbHVoGP2*L{2->{mr%ZC-dW$u=iaA~M5*4sypt7Q)636l^a-}AdmXdkm z2mBcm7=GUtaTX$ryU)4NQw7lGWpjn^8v}Ku_jxkCk?C=4bCJALgn zOOm1QyQ>6~{Bvb9aMeP)e$#2WcQedHduQvB?2$1w-M{+#N%H-q%h`{QpTGUvSKDQy z8Su9@4$_0g^jkyiIb6ehVV?bRSCmbnJF;&_fSGC^VL!g|enVb71bwX{9D?p?s~lYM z{F!?QIzDy?$_iw^hcP)?LtPEKqUOlt_90045Y*>PMlvmyi}&iu;12&@R;JU9Ly#L3 zcL=(BZ8>Zo5k1wxNjV#~3;8k6zzv6>9>3EpDf%v}erBb19Wn2%ca;Gduk4--ZNEVG zOxW0s5k|kJ4Js>r?MNoINR$Nwo=K5g^}nxQ{??}*rb~mGbbW0O!9;moUDNfxR%)o$ zp-7;~_ZLY#$<9lzwh`Zih!NdOAw%0$8Be+&yq(t84fMTf+HdX6NoX2!Js||`*&zAg zrT)CYx-r({dJgF-?pVkUbC^bP$nWBV!h4fQPd%wtK&whGQQWaBs(FF3D|QIt%Xh{N zPJt8Ve%_PZh<+dmi&8o9mZtG*0<2U}9pf(El~*y_E;hth8l5-3)G^(N>**!y@dtek zsU1n(-1K--EY*=Wl2z$gL;uy9KD9MPVPX%8I(%Y|7%;9g{Qx4BH@S!#H9ER%O(*_H z4Jt$S9S<|X0_pl=ztu8M#X}zZDF*4K)07lU#uihb8hgF!xIVajXXY{)Dj4u*_dQk3 zz`Hd9-hY|)7Ny$hAgaw)v51uAlg#JLsYgD!p28tX1FBg8St_2oh=~i{tZY=DAy#f` zKBMe1WL#q3#eM}`)X}KL5-?)&AH=F$`2_PCPTS*6XblVS@r(4O_mVUnYSP-T=3oDi z7b!M-tILJIeq4p6gY)K7XfLb;2>U!HV@I{?+X}P)+#3Qfq{-0GEzO3uHm?PYd+er1 zMTt;GS%ef=|IB(-dTHru zrq53;>Q*-r#ImP9dd%Rb#-u`37`*SJ1?PsBS;1{AfdY zL@MpJ^tR`NCp0ly+=-F-!-+p2HL+I(3OGO@2<>15s4X<9g-ml|}@dR;Gx$(?yE0S3*g+d3|rl-fgIY1!0h!)c(aCF3ar#mV7^knP3rsrtsNm`l1uU6j+Ts=h-TeNDi z4~~V!-u!UaS!Q_X0WujXR2<-YC&^xkTOjACNLGN}48I9)KSS+(i&Oi^>&SiX+$NGo z)W}Y|cz~HX$$xM$Z@k)gQR;EFM?04iRQcvD@D$(L{vR6(Ruyj-Pg45J)w8QX1-kc5PL5z zz9UB+`c5G0fpBZRm@rA`aSc@O%J~9seF*BEv0*^#{dbU3Sjjcv4Ly&iBzJmU)|<~Q z7S)D`lsgyi%F!Hl!)roV4T>2MTfG*r=W?o(t=%({8h-EEX8{PB((5jVsG_XPTeUT;c6=YV z`qn}Qb0ss>W8V;+Wfv7_U!1W}X+bR9m}xB^DLWVwkzW8dR$obbd7l>9N2H_zX3mv|LglAraXX6kJDJX%jXp zp!ck&wQEAx1MVXEQn>I#XB}$gSY}(P>6lA@)ofu2ylVMi)?%*%c_s=4%$CJ;qB}Ti zdwEk5%kCGHfB}Ebix6W{naKLVoK%&r4#D2CrtZyKz0Wb4O_ZyXgC!JqN_tYciS5Lh z9C5-%&Y;NaK|zt6 zCbj<}TMYk_B%3D5w>!01nrq0*Wq)fO(XFVKjDO^U^xhnfc8r`6D<&?pguwrFF$FQd zkGEgoz}rqdkWa%Uc%<=OMDtS3V~H^m-q9DE|9brpYE(5{o%^(*`T@v(3v(TonDFe! z(%q1=(vzElPZkxQlFXE8P$_8iDU~?!v-fAu*j(o`=iy$BYSCC4d{(H%^pV%=iE%5d zRzMX5%%A=GB5{F%p0XxWLJ0|77DZRA^7kPRn!UPwHZ4P<%A&^v#`k$Ycd&Ub?}ONL z5;Eoq^T*h#kX*7Ht@-U6z>y(ya}0W7(b;=$i0k|J`}B{Z6;b@;x*5CaS#O~n3BgH7 zs--!l8rLzV=&N)kJORdsjwCy&8m)&HAqStB%DMZ8xF(S-1HDzP3t@A2IW1w~p*CHA zj<6#ocU|m0hi^_DAPEbqt#2Fi)A;$#>>_rIHD{$@f%O(x!RueJq##U8MZWwfN4!CKX&gEGoLn)p_ML#EbgIlkwbPZ1150A$yq;Rtl#) zeaz_PV2nsw@TU)%x3s!Co^S*~vpOTiKs1keaE6w?^~hMf5yCp_3Do_hJ_Einr5{pHHzr7Bj66LE{1o zx@?C<@Gph81a?1kha;1qOZRx7xC;_UqIDFB3fl~~e&_P`elgY0iLEiaFuRG+3C7TLzEj2uTsQ4`);F{g^ z@3nZv@gD_RT`z|P-2#N2@;GgmHw}VU>Z)CeF3CSCLzQDKurh;EuO_C8BqF=*Jk3tJ~{r1mC3ohwcMM%TdGsv~oRa$zGy3 zqV^22;l8Uh=ylphy|7ye9rf9mVZNvF_wZE0D8Vr2T*$iXIL1)s3IK znI|};vn9F@Mn`~!SVna7jc@b(@kBc54+_SprWlM?p6_CYMQ9Q#8naR1W#)db*Utvz zljWq^zS;=ge*4#Z%e*wX1;zc?YXH>DZ&$@L6|bw&PIwlwB>GnZwA4b^YV&&=ZCQxe zPi0^019BRr;~jTjV-zJ3xCvQ#3;%*}QLy!7uRL=&|C)+zsNs0H8bHi~NUj z)SSXdecGB19`Ow!Bo7*qC8TpE`}dvnF3^qsVH_gp*N!R;j>J76?B-ZU{bG?^O=^R3 z9a!ip^~=^X2JhL7rOzo*cJ^c05aGM8u+g?iej6+LkBe2mqKI}u4P%eS(=GqQIFf4e z7AqE3`&mhk5Cm(2vqg!SdbNGedj1q8SBaAS{1=ZqhkohBbjrWpC67LWA{hjeH$Dk} z0!8}^b42O*=W@TfK-yo%Q~-a?e9*gZMcT5d^>egcwO?J~RBFydA`z0P`xg z%d>}&g3wEhp!c~M$+i=%GI*ZEhC-tc@v9BU3oA_))9Y-0#&OLi!Jk?Z5)8fEGM}7@ zpPD;PD24%4@eow_U`o^Hx!cm?LArLzNqNrGS`{nxzw3>r-fl+J824#ZjugTXD6A$$ zZ(ey&jWvH(+{H1uG=z!x!Adg|3 z_y1spj)(a2QxhJ4hiduIE}8%FX^UlycO0M%^P?pAMq)+vUS!NWD^AZf<2u!EiA5EO zNYL&W@V{wIzuL_5oqsZ8JPr{Yty*eqo~WiKZQHgY(vP1gONF6Hevl1@ z%CP`mu1_R4^*rGSjoJ`T!{pK=?6W}p>7YJ{Y175h@f9ZYV{&q!izLA%&DMeF@WMNQ zU~NqrdzB@le7oUWM&`Gb^iS#M*IUBkew+>JZfQ+Fmr0!kTTX@;ze-%rP`YkhSHx!C zUU;UFsq5(2-*?g5#qh{^!QX8A5m?<_ltp*CGmvISZG{_Ih6TQ*sKuT-@HiCGsFekC`#~pxGla3D3?xk*jIVZ^n zwUHV`q|meRWzmFeV1F?+FFYgd`zyJ zc;)%z69_L~UT$h?;(+1h>`~cQh8c#|2%}|23Qo31H83kL#r@)v9Nes56~5Uxo8i#( zO0OTmsq@~doGM>7YGLf$IF~3z@cSdZTP2nA>j#*#F4QdKChqc{2B$>~ldFAhw?HzYX7Ted3m7^1^q-`TOUj&%4aKR&M1^Kr4Hc&v~- zQEi&u1J6Rd%X-6DB5NB+FKge;x_og-$H3bs2OZZng+){G?sI}|hNWi0YY38IS$