Skip to content

Commit a52fc8b

Browse files
authored
Merge pull request #245 from checkly/add-new-env-var-guide
Add environment-aware Playwright tests guide
2 parents 4496de5 + e682240 commit a52fc8b

3 files changed

Lines changed: 286 additions & 1 deletion

File tree

docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,8 @@
978978
"guides/moving-from-puppeteer-to-playwright",
979979
"guides/developer-fixtures",
980980
"guides/auto-waiting-methods",
981-
"guides/reading-traces"
981+
"guides/reading-traces",
982+
"guides/playwright-environments"
982983
]
983984
},
984985
{

guides/playwright-environments.mdx

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
---
2+
title: How to use environment variables in Checkly Playwright Check Suites
3+
description: Learn how to run the same Playwright tests against different environments and URLs using Checkly.
4+
sidebarTitle: Environment-aware Playwright Tests
5+
---
6+
7+
import { YoutubeCallout } from "/snippets/youtube-callout.jsx"
8+
9+
When you set up [a Playwright Check Suite](/detect/synthetic-monitoring/playwright-checks/overview) to run Playwright in the Checkly infrastructure, your tests run in at least two very different contexts. Locally, they hit `localhost`. On Checkly, they hit a deployed URL.
10+
11+
While you could maintain separate Playwright config files for each environment, you can also rely on environment variables to handle the entire Checkly workflow from a single `playwright.config.ts`. Run `npx playwright test` locally, [`npx checkly test`](/cli/checkly-test) against a staging URL, and [`npx checkly deploy`](/cli/checkly-deploy) for scheduled production monitoring.
12+
13+
## What Checkly sets for you
14+
15+
When your Playwright tests run as a Playwright Check Suite, Checkly sets [environment variables](/detect/synthetic-monitoring/playwright-checks/environment-variables) into your test process:
16+
17+
| Variable | Value | Use case |
18+
|---|---|---|
19+
| `CHECKLY` | `"1"` | Detect that you're running on Checkly at all |
20+
| `CHECKLY_RUN_SOURCE` | `"SCHEDULER"`, `"TEST_NO_RECORD"`, `"TEST_RECORD"`, `"TRIGGER_API"`, `"CLI_DEPLOY"`, `"DEPLOYMENT"`, etc. | Distinguish _how_ the check was triggered |
21+
| `CHECKLY_REGION` | `"eu-west-1"`, `"us-east-1"`, etc. | Know which data center is running the check |
22+
| `CHECK_NAME` | String | The name of the check |
23+
| `CHECKLY_CHECK_ID` | UUID | The UUID of the check |
24+
25+
`CHECKLY` is the simplest variable. It's a boolean flag.
26+
27+
`CHECKLY_RUN_SOURCE` gives you more granularity: was this a scheduled production run, a test from the CLI, or a deployment trigger?
28+
29+
<Tip>Please see [the Playwright Check Suites environment variable docs](/detect/synthetic-monitoring/playwright-checks/environment-variables) for all built-in environment variables. You can also define your own variables and secrets at the check, group, or account level. See [Environment Variables](/platform/variables) and [Secrets](/platform/secrets).</Tip>
30+
31+
## The basic pattern: a simple toggle
32+
33+
If you only need two modes (local development and Checkly monitoring), a single boolean defined in your `playwright.config` does the job.
34+
35+
```typescript playwright.config.ts highlight={3}
36+
import { defineConfig, devices } from "@playwright/test";
37+
38+
const isCheckly = !!process.env.CHECKLY;
39+
40+
export default defineConfig({
41+
retries: isCheckly ? 2 : 0,
42+
use: {
43+
trace: isCheckly ? "on" : "retain-on-failure",
44+
},
45+
projects: [
46+
{
47+
name: "checkly",
48+
use: {
49+
...devices["Desktop Chrome"],
50+
baseURL: isCheckly
51+
? "https://staging.example.com"
52+
: "http://localhost:3000",
53+
},
54+
},
55+
],
56+
webServer: isCheckly
57+
? undefined
58+
: {
59+
command: "npm run dev",
60+
url: "http://localhost:3000",
61+
reuseExistingServer: true,
62+
},
63+
});
64+
```
65+
66+
In this scenario, `isCheckly` toggles four things at once:
67+
68+
- **`baseURL`**: `localhost` for local dev, your deployed URL on Checkly
69+
- **`retries`**: no retries locally (fast feedback), 2 retries on Checkly (resilience against transient network issues and test flakiness)
70+
- **`trace`**: always captured on Checkly for debugging, only on failure locally to save disk space (see [Reading traces](/guides/reading-traces))
71+
- **`webServer`**: starts your dev server locally, skipped on Checkly (the deployed site is already running)
72+
73+
<Note>`baseURL` only takes effect when your tests use relative URLs in `page.goto()` calls, e.g. `page.goto('/')`, not `page.goto('https://example.com')`.</Note>
74+
75+
This pattern covers most setups. If you have one deployed URL and one local URL, you're done.
76+
77+
## The advanced pattern: environment-aware configuration
78+
79+
Some projects need more than two modes. Maybe your CI runs tests against a preview deployment, while Checkly monitors production. Or you want different behavior depending on whether a Checkly run was triggered by a schedule, a deployment, or a manual test. This pattern also works well with [CI/CD integrations](/integrations/ci-cd/overview) where `npx checkly test` runs against preview deployments before promoting to production.
80+
81+
Instead of a boolean, you can use `CHECKLY_RUN_SOURCE` to determine which environment your tests are running in and map that to a specific URL. Sometimes it's not just about whether you're running on Checkly or not, but also _where_ your tests run. `CHECKLY_REGION` lets you adapt test behavior based on the data center location, for example to validate geo-specific content.
82+
83+
### Step 1: Create an environment helper
84+
85+
```typescript checkly-env.ts
86+
export type Environment = "DEV" | "CI" | "PROD";
87+
88+
export function getEnvironment(): Environment {
89+
// `npx checkly test` sets CHECKLY_RUN_SOURCE to "TEST_NO_RECORD" or "TEST_RECORD"
90+
if (process.env.CHECKLY_RUN_SOURCE?.startsWith("TEST")) return "CI";
91+
92+
// Scheduled runs, triggers, and deployments are production
93+
if (
94+
process.env.CHECKLY_RUN_SOURCE?.startsWith("TRIGGER") ||
95+
process.env.CHECKLY_RUN_SOURCE === "SCHEDULER" ||
96+
process.env.CHECKLY_RUN_SOURCE === "SCHEDULE_NOW" ||
97+
process.env.CHECKLY_RUN_SOURCE === "GROUP_RUN_ALL" ||
98+
process.env.CHECKLY_RUN_SOURCE === "CLI_DEPLOY" ||
99+
process.env.CHECKLY_RUN_SOURCE === "DEPLOYMENT"
100+
)
101+
return "PROD";
102+
103+
// No Checkly env vars? We're running locally
104+
return "DEV";
105+
}
106+
107+
export function getBaseUrl(env: Environment) {
108+
switch (env) {
109+
case "DEV":
110+
return "http://localhost:3000";
111+
case "CI":
112+
return "https://preview.example.com";
113+
case "PROD":
114+
return "https://www.example.com";
115+
}
116+
}
117+
118+
export function getLocationCountry() {
119+
if (process.env.CHECKLY !== "1") return null;
120+
121+
const region = process.env.CHECKLY_REGION;
122+
if (region === "eu-central-1") return "DE";
123+
if (region === "eu-west-1") return "IE";
124+
if (region?.startsWith("us-")) return "US";
125+
if (region === "ap-southeast-2") return "AU";
126+
// more regions ...
127+
128+
return null;
129+
}
130+
```
131+
132+
The logic: `npx checkly test` (what you run in CI pipelines) triggers a `TEST_*` source that maps to your preview/staging URL. Scheduled runs and deployments are production monitoring and point to the live site. Everything else is local dev.
133+
134+
Once you know the environment, you can set different `baseURL` values, adjust retry and trace settings, or add other environment-specific logic.
135+
136+
<Tip>
137+
<p>
138+
Platforms like Vercel and Netlify expose preview deployment URLs via environment variables (e.g. `VERCEL_URL`). You can pass these to your Checkly test runs using the `-e` flag: `npx checkly test -e ENVIRONMENT_URL=https://preview-abc.vercel.app`.
139+
</p>
140+
<p>
141+
Your `getBaseUrl` function can then read `process.env.ENVIRONMENT_URL` instead of hardcoding a preview URL. See the [CI/CD integration docs](/integrations/ci-cd/overview) and the [`npx checkly test` reference](/cli/checkly-test) for more details.
142+
</p>
143+
</Tip>
144+
145+
### Step 2: Use the helper in your Playwright config
146+
147+
```typescript playwright.config.ts highlight={4-5, 10}
148+
import { defineConfig, devices } from "@playwright/test";
149+
import { getBaseUrl, getEnvironment } from "./tests/checkly-env";
150+
151+
const environment = getEnvironment(); // "DEV" | "CI" | "PROD"
152+
const baseURL = getBaseUrl(environment); // "http://localhost:3000" | "https://preview.example.com" | "https://www.example.com"
153+
154+
export default defineConfig({
155+
retries: environment === "DEV" ? 0 : 2,
156+
use: {
157+
baseURL,
158+
trace: environment === "DEV" ? "retain-on-failure" : "on",
159+
},
160+
projects: [
161+
{
162+
name: "Critical",
163+
use: { ...devices["Desktop Chrome"] },
164+
grep: /@critical/,
165+
},
166+
],
167+
});
168+
```
169+
170+
With this approach, you have one config file that targets three environments with different base URLs and no duplication.
171+
172+
### Going further: per-environment behavior in tests
173+
174+
If you need per-test access to environment values, you can take the setup from Step 2 further by wiring your helpers into [Playwright test fixtures](https://playwright.dev/docs/test-fixtures). This keeps environment logic out of individual test files.
175+
176+
<YoutubeCallout>
177+
Watch [Reuse Playwright Code across Files and Tests with Fixtures](https://www.youtube.com/watch?v=2O7dyz6XO2s) for a walkthrough of the fixture patterns covered in this section.
178+
</YoutubeCallout>
179+
180+
#### Create a custom test fixture
181+
182+
Create a `base.ts` file that extends Playwright's `test` object with custom fixture values. All your test files import `test` and `expect` from here instead of `@playwright/test`:
183+
184+
```typescript tests/base.ts highlight={11,13}
185+
import { test as base, expect } from "@playwright/test";
186+
import { type Environment } from "./checkly-env";
187+
188+
export type TestOptions = {
189+
environment: Environment;
190+
locationCountry: null | string;
191+
};
192+
193+
export const test = base.extend<TestOptions>({
194+
// make `environment` available in your tests
195+
environment: ["DEV", { option: true }],
196+
// make `locationCountry` available in your tests
197+
locationCountry: [null, { option: true }],
198+
});
199+
200+
export { expect };
201+
```
202+
203+
In this example, you specify that `environment` and `locationCountry` should be accessible in your Playwright tests. Their default values can be overwritten via the `use` option in your Playwright config.
204+
205+
#### Wire it all up in the Playwright config
206+
207+
Pass the helper values into your custom fixtures via the `use` option:
208+
209+
```typescript playwright.config.ts highlight={17-19}
210+
import { defineConfig, devices } from "@playwright/test";
211+
import { type TestOptions } from "./tests/base";
212+
import {
213+
getBaseUrl,
214+
getEnvironment,
215+
getLocationCountry,
216+
} from "./tests/checkly-env";
217+
218+
const environment = getEnvironment();
219+
const baseURL = getBaseUrl(environment);
220+
const locationCountry = getLocationCountry();
221+
222+
export default defineConfig<TestOptions>({
223+
testDir: "./tests",
224+
retries: environment === "DEV" ? 0 : 2,
225+
use: {
226+
environment,
227+
baseURL,
228+
locationCountry,
229+
trace: environment === "DEV" ? "retain-on-failure" : "on",
230+
},
231+
projects: [
232+
{
233+
name: "Chromium",
234+
use: { ...devices["Desktop Chrome"] },
235+
},
236+
],
237+
});
238+
```
239+
240+
#### Use fixtures in your tests
241+
242+
Your tests stay clean because the environment logic is handled by the fixtures:
243+
244+
```typescript tests/geo-location.spec.ts highlight={4}
245+
import { expect, test } from "./base";
246+
247+
// `environment` and `locationCountry` are now available in your tests
248+
test("geo location works @critical", async ({ page, locationCountry }) => {
249+
await page.goto("/");
250+
251+
const locationContainer = page.getByTestId("geo-location");
252+
if (locationCountry) {
253+
await expect(locationContainer).toContainText(locationCountry);
254+
} else {
255+
await expect(locationContainer).toContainText("Geo location unavailable");
256+
}
257+
});
258+
```
259+
260+
The `locationCountry` fixture is automatically populated based on which Checkly region runs the check. There's no need for environment logic in the test itself.
261+
262+
## Wrapping up
263+
264+
Start with the simple `isCheckly` toggle. It handles most cases. When you need more than two environments or per-environment behavior beyond `baseURL`, extract the logic into a dedicated helper file.
265+
266+
The pattern scales: add a new environment by adding a case to `getEnvironment()` and a URL to `getBaseUrl()`. Your tests and Playwright config stay the same.
267+
268+
For the full reference on available environment variables, check the [Playwright Check Suite environment variables docs](/detect/synthetic-monitoring/playwright-checks/environment-variables).

snippets/youtube-callout.jsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export const YoutubeCallout = ({ children }) => (
2+
<div className="callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10">
3+
<div className="mt-1 w-4">
4+
<svg viewBox="0 0 28 20" className="w-4 h-auto" aria-label="YouTube video">
5+
<path
6+
fill="#FF0000"
7+
d="M27.4 3.1a3.5 3.5 0 0 0-2.5-2.5C22.7 0 14 0 14 0S5.3 0 3.1.6A3.5 3.5 0 0 0 .6 3.1C0 5.3 0 10 0 10s0 4.7.6 6.9a3.5 3.5 0 0 0 2.5 2.5C5.3 20 14 20 14 20s8.7 0 10.9-.6a3.5 3.5 0 0 0 2.5-2.5C28 14.7 28 10 28 10s0-4.7-.6-6.9Z"
8+
/>
9+
<path fill="#fff" d="m11.2 14.3 7.2-4.3-7.2-4.3v8.6Z" />
10+
</svg>
11+
</div>
12+
<div className="text-sm prose min-w-0 w-full text-red-900 dark:text-red-200">
13+
{children}
14+
</div>
15+
</div>
16+
);

0 commit comments

Comments
 (0)