@andwhy/e2e-pipeline 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/bin/init.js +8 -4
- package/dist/utils/install.js +16 -4
- package/package.json +1 -1
- package/templates/claude/partials/_robustness-core.md +23 -2
- package/templates/claude/rules/playwright-workflow.md +4 -1
- package/templates/claude/skills/playwright-test-pipeline/SKILL.md +38 -20
- package/templates/github/workflows/checkly-heal.yml +10 -3
- package/templates/github/workflows/pr-playwright-tests.yml +3 -0
package/README.md
CHANGED
|
@@ -48,8 +48,9 @@ Re-running `init --force` in an existing project is the way to pick up a new ver
|
|
|
48
48
|
- `.github/workflows/checkly-heal.yml` — a failing deployed monitor triggers a triage-and-heal agent: production regressions escalate to Slack immediately; stale tests get fixed and flaky tests hardened — including behavior-neutral test hooks (`data-testid`) in app source when needed — and opened as a human-review PR, whose own preview run validates the app-side hooks. Inert until a Checkly webhook alert channel dispatches to it — the skill walks through that setup.
|
|
49
49
|
|
|
50
50
|
The workflows need repository secrets: `CHECKLY_API_KEY`, `CHECKLY_ACCOUNT_ID`,
|
|
51
|
-
`VERCEL_AUTOMATION_BYPASS_SECRET`, and for auto-heal `
|
|
52
|
-
|
|
51
|
+
`VERCEL_AUTOMATION_BYPASS_SECRET`, and for auto-heal `SLACK_WEBHOOK_URL`
|
|
52
|
+
(values in 1Password). `ANTHROPIC_API_KEY` is an organization secret inherited
|
|
53
|
+
by all repos — nothing to add.
|
|
53
54
|
|
|
54
55
|
E2E specs live under **`tests/e2e/`** so a repo can keep unit tests elsewhere under
|
|
55
56
|
`tests/` without Playwright trying to run them. Seeds sit in the same tree and are
|
package/dist/bin/init.js
CHANGED
|
@@ -79,6 +79,11 @@ const run = async () => {
|
|
|
79
79
|
console.log(` ${path.relative(projectRoot, f)}`);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
// Dependencies install before the formatting pass: prettier is resolved
|
|
83
|
+
// from the project's node_modules, which a fresh clone doesn't have yet.
|
|
84
|
+
// Formatting after the install means scaffolded files pick up the project's
|
|
85
|
+
// style even on a first run in a bare checkout.
|
|
86
|
+
installDeps(projectRoot, fileFormat);
|
|
82
87
|
const formatted = await formatFiles(projectRoot, [
|
|
83
88
|
...result.created,
|
|
84
89
|
...result.regenerated,
|
|
@@ -110,13 +115,12 @@ const run = async () => {
|
|
|
110
115
|
" CHECKLY_API_KEY, CHECKLY_ACCOUNT_ID — deploy monitors, auto-heal",
|
|
111
116
|
" VERCEL_AUTOMATION_BYPASS_SECRET — PR tests through Vercel",
|
|
112
117
|
" deployment protection",
|
|
113
|
-
" ANTHROPIC_API_KEY — Checkly auto-heal agent",
|
|
114
118
|
" SLACK_WEBHOOK_URL — auto-heal notifications",
|
|
115
|
-
"
|
|
116
|
-
"
|
|
119
|
+
"(ANTHROPIC_API_KEY is an org-level secret inherited by all repos —",
|
|
120
|
+
"nothing to add.) The auto-heal workflow stays inert until a Checkly",
|
|
121
|
+
"webhook alert channel dispatches to it — the skill walks through that.",
|
|
117
122
|
].join("\n"));
|
|
118
123
|
}
|
|
119
|
-
installDeps(projectRoot, fileFormat);
|
|
120
124
|
console.log("Next steps:");
|
|
121
125
|
let step = 1;
|
|
122
126
|
const ext = fileFormat === "mjs" ? "mjs" : "ts";
|
package/dist/utils/install.js
CHANGED
|
@@ -47,8 +47,20 @@ export const installDeps = (projectRoot, fileFormat = "ts") => {
|
|
|
47
47
|
console.log(` ${cmd}\n`);
|
|
48
48
|
execSync(cmd, { cwd: projectRoot, stdio: "inherit" });
|
|
49
49
|
console.log("\nInstalling Chromium browser…\n");
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
// Non-fatal: restricted networks (CI sandboxes, proxied agents) block the
|
|
51
|
+
// browser CDN, and a failed download must not abort the install — the
|
|
52
|
+
// scaffolded files are already written and CI installs its own browsers.
|
|
53
|
+
try {
|
|
54
|
+
execSync("npx playwright install chromium", {
|
|
55
|
+
cwd: projectRoot,
|
|
56
|
+
stdio: "inherit",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
console.warn([
|
|
61
|
+
"\nChromium download failed (restricted network?). The scaffold is",
|
|
62
|
+
"complete — run `npx playwright install chromium` yourself before",
|
|
63
|
+
"local test runs.",
|
|
64
|
+
].join("\n"));
|
|
65
|
+
}
|
|
54
66
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andwhy/e2e-pipeline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Scaffolds the full e2e testing pipeline: agent-assisted Playwright tests, CI runs against Vercel previews, and Checkly monitors that follow production deploys",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,11 +59,32 @@
|
|
|
59
59
|
await expect(page).not.toHaveURL(initialUrl, { timeout: 10_000 });
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
- **Skip unsupported domains in `beforeEach
|
|
63
|
-
config, skip all tests up front rather than letting them fail:
|
|
62
|
+
- **Skip unsupported domains in `beforeEach` — but never previews.** If a domain has no
|
|
63
|
+
entry in a spec's locale config, skip all tests up front rather than letting them fail:
|
|
64
64
|
|
|
65
65
|
```ts
|
|
66
66
|
test.beforeEach(async ({ page, baseURL }) => {
|
|
67
67
|
test.skip(!getLocaleConfig(baseURL!), "No config for this domain");
|
|
68
68
|
});
|
|
69
69
|
```
|
|
70
|
+
|
|
71
|
+
A preview deployment's hostname (`*.vercel.app`) is never in the locale map, so a plain
|
|
72
|
+
exact-match lookup silently skips the entire suite on every PR run and in
|
|
73
|
+
`checkly pw-test` — green checks that tested nothing. The lookup must resolve preview
|
|
74
|
+
hosts to the config of the production domain the preview serves (ask the user which one
|
|
75
|
+
if it isn't obvious; `PREVIEW_LOCALE_HOST` overrides it at runtime):
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const getLocaleConfig = (baseURL: string) => {
|
|
79
|
+
const hostname = new URL(baseURL).hostname.replace(/^www\./, "");
|
|
80
|
+
const config = LOCALE_CONFIG[hostname];
|
|
81
|
+
if (config) return config;
|
|
82
|
+
// Previews serve <primary domain>'s content; without this every preview run skips.
|
|
83
|
+
if (hostname.endsWith(".vercel.app"))
|
|
84
|
+
return LOCALE_CONFIG[process.env.PREVIEW_LOCALE_HOST ?? "<primary domain>"];
|
|
85
|
+
return undefined;
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Only a genuinely unknown non-preview host skips. Single-domain suites without a locale
|
|
90
|
+
map are unaffected — they have no lookup to fall through.
|
|
@@ -36,7 +36,10 @@ These rules apply to **all** agents (generator, healer, and orchestrator). Inclu
|
|
|
36
36
|
project consent logic; do not reinvent it. The helper file extension must match rule 7.
|
|
37
37
|
|
|
38
38
|
4. Never hardcode a single locale's URL pattern. Use a locale config map keyed
|
|
39
|
-
by bare hostname and resolve at runtime.
|
|
39
|
+
by bare hostname and resolve at runtime. Preview hosts (`*.vercel.app`) are
|
|
40
|
+
never in the map and must fall back to the primary production domain's
|
|
41
|
+
config (overridable via `PREVIEW_LOCALE_HOST`) — an exact-match-only lookup
|
|
42
|
+
skips the whole suite on every PR run.
|
|
40
43
|
|
|
41
44
|
5. After all tests pass locally, verify with
|
|
42
45
|
`BASE_URL=<dev-url> pnpm playwright test` via the terminal tool. If failures
|
|
@@ -88,12 +88,12 @@ The scaffold writes templates, not a working setup. Walk these with the user:
|
|
|
88
88
|
- `VERCEL_AUTOMATION_BYPASS_SECRET` — Vercel → project → Settings →
|
|
89
89
|
Deployment Protection → Protection Bypass for Automation. Per project —
|
|
90
90
|
never copied from another repo.
|
|
91
|
-
- `
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
- `SLACK_WEBHOOK_URL` — the **org-shared** e2e-channel webhook: paste the
|
|
92
|
+
value from 1Password ("e2e Slack webhook"). Same value in every repo;
|
|
93
|
+
do not walk the user through creating a Slack app or webhook — routing
|
|
94
|
+
to the project channel happens via a Slack rule in Phase 5.
|
|
95
|
+
- `ANTHROPIC_API_KEY` needs **no action**: it is an organization-level
|
|
96
|
+
secret inherited by every repo. Do not tell the user to add it.
|
|
97
97
|
5. **Existing tests under `tests/`** (unit, a11y — the scaffold lists them as
|
|
98
98
|
"outside tests/e2e"): leave `testDir` at `./tests/e2e` and give that suite
|
|
99
99
|
its own Playwright project with its own `testDir` if it should run under
|
|
@@ -180,8 +180,9 @@ overwritten):
|
|
|
180
180
|
**Before the first merge, the repo needs secrets** (values in 1Password):
|
|
181
181
|
`CHECKLY_API_KEY` and `CHECKLY_ACCOUNT_ID` for the deploy and auto-heal,
|
|
182
182
|
`VERCEL_AUTOMATION_BYPASS_SECRET` for the PR run through deployment
|
|
183
|
-
protection, `
|
|
184
|
-
|
|
183
|
+
protection, `SLACK_WEBHOOK_URL` for auto-heal notifications
|
|
184
|
+
(`ANTHROPIC_API_KEY` is inherited from the organization — nothing to add).
|
|
185
|
+
If the scaffold just created the workflows, say so explicitly and list the
|
|
185
186
|
secrets — a merge without them fails CI and looks like a pipeline bug.
|
|
186
187
|
|
|
187
188
|
After merge, monitors update once the production deployment goes live — the
|
|
@@ -198,14 +199,20 @@ team's Slack message is written by the pipeline *after* triage (regression
|
|
|
198
199
|
vs. stale test), so it carries a diagnosis and, for stale tests, the fix PR.
|
|
199
200
|
Set up once per project:
|
|
200
201
|
|
|
201
|
-
1. Confirm the `
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
1. Confirm the `SLACK_WEBHOOK_URL` repository secret from Phase 0 exists —
|
|
203
|
+
the workflow escalates nothing without it. (`ANTHROPIC_API_KEY` comes
|
|
204
|
+
from the organization automatically.)
|
|
205
|
+
2. The shared `GH_DISPATCH_TOKEN` secret exists in Checkly and covers this
|
|
206
|
+
repo. This is **one-time organization setup, not yours to perform**: the
|
|
207
|
+
token (`checkly-heal-dispatch`, a fine-grained PAT) is created once and
|
|
208
|
+
maintained by its owner, named in 1Password — see "One-time organization
|
|
209
|
+
setup" in the usage guide
|
|
210
|
+
(https://app.notion.com/p/32df837b627080748719c0f94a3aeaaf). Your only
|
|
211
|
+
per-project concern: if this repo is not yet on the token's repository
|
|
212
|
+
list, the webhook channel's test (step 3) fails with a 404 — then ask
|
|
213
|
+
the token owner to widen it (a one-minute edit on their side; the token
|
|
214
|
+
value doesn't change). Do not walk the user through creating a PAT or a
|
|
215
|
+
Checkly secret.
|
|
209
216
|
3. Checkly → Alert channels → "Add more channels" → **Webhook**:
|
|
210
217
|
- Method `POST`, URL
|
|
211
218
|
`https://api.github.com/repos/<org>/<repo>/dispatches`
|
|
@@ -236,10 +243,21 @@ Set up once per project:
|
|
|
236
243
|
5. **Do not also subscribe a Slack alert channel to these suites.** The
|
|
237
244
|
pipeline is the single alert path; a parallel raw Checkly alert double-pings
|
|
238
245
|
the channel and trains people to ignore one of them.
|
|
239
|
-
6.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
246
|
+
6. **One Slack Workflow Builder rule** routes this project's messages from
|
|
247
|
+
the shared e2e channel to the project channel. All notifications post to
|
|
248
|
+
the e2e channel tagged `[e2e:<repo-name>]`; the rule matches the **full
|
|
249
|
+
bracketed tag** — never a short name like `gbk`, which collides with
|
|
250
|
+
other text — and sends the message to the project channel. The sent
|
|
251
|
+
message's template is just the message-text variable (plus the
|
|
252
|
+
link-to-original variable when available, so threads happen on the
|
|
253
|
+
original) — no static prefix text. Have the user add a **second
|
|
254
|
+
manager** to the rule and record it in the rule registry in the e2e
|
|
255
|
+
channel's canvas.
|
|
256
|
+
7. Dry run: GitHub → Actions → "Checkly auto-heal" → Run workflow, with the
|
|
257
|
+
suite's check name. Confirm the run completes and the message shows up in
|
|
258
|
+
the e2e channel and, via the rule, in the project channel (a passing
|
|
259
|
+
check should end as "not reproducible" — summary only, no message — so a
|
|
260
|
+
real dry run is easiest right after any genuine failure).
|
|
243
261
|
|
|
244
262
|
What the user should expect afterwards: a failing monitor produces either a
|
|
245
263
|
`checkly-heal` PR plus a Slack note (stale or flaky test — **review
|
|
@@ -9,9 +9,12 @@ name: Checkly auto-heal
|
|
|
9
9
|
# validates those; a diff guard aborts changes to protected paths
|
|
10
10
|
# (.github, .claude, configs, lockfiles). The agent never pushes: branch,
|
|
11
11
|
# commit, PR, and Slack are workflow steps.
|
|
12
|
-
# Needs repo secrets
|
|
13
|
-
#
|
|
14
|
-
#
|
|
12
|
+
# Needs repo secrets CHECKLY_API_KEY, CHECKLY_ACCOUNT_ID, SLACK_WEBHOOK_URL
|
|
13
|
+
# (the org-shared e2e-channel webhook; values in 1Password); ANTHROPIC_API_KEY
|
|
14
|
+
# is inherited from the organization. Slack messages carry an [e2e:<repo>]
|
|
15
|
+
# tag so Slack Workflow Builder rules route them from the shared channel to
|
|
16
|
+
# project channels. Setup docs: the playwright-test-pipeline skill walks
|
|
17
|
+
# through the Checkly webhook channel and the Slack rule.
|
|
15
18
|
on:
|
|
16
19
|
repository_dispatch:
|
|
17
20
|
types: [checkly-alert]
|
|
@@ -294,6 +297,10 @@ jobs:
|
|
|
294
297
|
*)
|
|
295
298
|
text=":rotating_light: *Checkly: \`$CHECK_NAME\` failed and the auto-heal run did not finish cleanly.* ${SUMMARY:-Check the run logs.} This needs a developer." ;;
|
|
296
299
|
esac
|
|
300
|
+
# Routing tag: Slack Workflow Builder rules match the full bracketed
|
|
301
|
+
# tag ([e2e:<repo>]) to fan messages out from the shared e2e channel
|
|
302
|
+
# to project channels without substring collisions.
|
|
303
|
+
text="[e2e:${GITHUB_REPOSITORY##*/}] $text"
|
|
297
304
|
[ "$APP_CHANGE" = "true" ] && text="$text\nIncludes app-side test hooks — the PR's preview run validates them."
|
|
298
305
|
[ -n "$RESULT_LINK" ] && text="$text\nCheckly result: $RESULT_LINK"
|
|
299
306
|
text="$text\nRun: $RUN_URL"
|
|
@@ -92,6 +92,9 @@ jobs:
|
|
|
92
92
|
env:
|
|
93
93
|
BASE_URL: ${{ github.event.deployment_status.target_url }}
|
|
94
94
|
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
|
|
95
|
+
# Which production domain's locale config preview hosts resolve to
|
|
96
|
+
# (repo variable, optional — specs default to the primary domain).
|
|
97
|
+
PREVIEW_LOCALE_HOST: ${{ vars.PREVIEW_LOCALE_HOST }}
|
|
95
98
|
PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-results.json
|
|
96
99
|
run: |
|
|
97
100
|
pnpm playwright test \
|