|
| 1 | +# docs-tests |
| 2 | + |
| 3 | +Docs-as-tests for the Apify Console. Every UI claim in the platform docs — a |
| 4 | +route resolves, a tab is named X, a button exists on page Y — is a testable |
| 5 | +assertion. This package extracts those assertions from the docs with an LLM, |
| 6 | +stores them as a reviewed baseline, and verifies them against Console staging |
| 7 | +with Playwright, so documentation drift is caught automatically. |
| 8 | + |
| 9 | +``` |
| 10 | +pages.json ──extract──▶ assertions/*.json ──Playwright──▶ output/issues.json |
| 11 | +(page list) (claude -p) (committed baseline) (vs staging) (drift report) |
| 12 | +``` |
| 13 | + |
| 14 | +## Model |
| 15 | + |
| 16 | +1. **`pages.json`** is an adjustable list of documentation pages (real source |
| 17 | + files under `sources/platform/…`) to cover. |
| 18 | +2. **`scripts/extract.sh`** feeds one page to `claude -p` with a strict JSON |
| 19 | + schema and writes the result to `assertions/<slug>.json`. |
| 20 | + `scripts/extract-all.sh` does the whole manifest. |
| 21 | +3. **`assertions/`** is the *stored, reviewed baseline* — committed to the repo. |
| 22 | + Regenerate it with the LLM whenever docs change, review the diff, commit. |
| 23 | + The assertion set is owned by humans even though a model drafts it. |
| 24 | +4. **`tests/from-doc.spec.ts`** reads every stored assertion and emits one |
| 25 | + Playwright `test()` per assertion, run against `$CONSOLE_STAGING_URL`. |
| 26 | +5. Failures point back to `source_file:line` so the offending prose is one click |
| 27 | + away, and land in `output/issues.json` for downstream triage. |
| 28 | + |
| 29 | +The Notion plan *"AI-based testing for docs"* (its Part 1 routes + Part 2 |
| 30 | +elements) is the inspiration for which pages and claims to cover — not a fixed |
| 31 | +transcription. The authoritative set is whatever the manifest + extractor |
| 32 | +produce and a human commits. |
| 33 | + |
| 34 | +## Assertion kinds |
| 35 | + |
| 36 | +| Kind | Checks | |
| 37 | +| ---------------- | ------------------------------------------------------------------ | |
| 38 | +| `route` | Documented path is reachable (HTTP < 400) | |
| 39 | +| `element_tab` | Documented tab label exists on the page named in `at` | |
| 40 | +| `element_button` | Documented button label exists on the page named in `at` | |
| 41 | +| `element_text` | Documented heading/label/field is visible on the page named in `at` | |
| 42 | + |
| 43 | +## One-time setup |
| 44 | + |
| 45 | +```bash |
| 46 | +pnpm install |
| 47 | +pnpm exec playwright install chromium |
| 48 | +cp .env.example .env # fill in CONSOLE_STAGING_URL + seeded-user email/password |
| 49 | +``` |
| 50 | + |
| 51 | +## Generate / refresh the assertion baseline |
| 52 | + |
| 53 | +```bash |
| 54 | +# Every page in pages.json: |
| 55 | +pnpm extract:all |
| 56 | + |
| 57 | +# Or a single page: |
| 58 | +pnpm extract sources/platform/console/settings.md |
| 59 | +``` |
| 60 | + |
| 61 | +Review the diff in `assertions/`, then commit. This is the step a human owns. |
| 62 | + |
| 63 | +## Run the tests |
| 64 | + |
| 65 | +```bash |
| 66 | +pnpm test # evaluate all stored assertions against staging |
| 67 | +pnpm issues # machine-readable, action-oriented failures |
| 68 | +pnpm report # HTML report (failures include screenshots, video, trace) |
| 69 | +``` |
| 70 | + |
| 71 | +Authentication is automatic: a worker-scoped fixture (`tests/auth.fixture.ts`) |
| 72 | +logs in once per run with `CONSOLE_STAGING_USER_EMAIL` / `_PASSWORD` and keeps |
| 73 | +the session in memory. **No `auth.json` is written or read** — nothing has to |
| 74 | +pre-exist, so it behaves identically locally and in CI (where the credentials |
| 75 | +come from GitHub Secrets). The seeded staging user has no 2FA. |
| 76 | + |
| 77 | +`pnpm test` always writes `output/issues.json` — a summary plus one entry per |
| 78 | +failing assertion, sorted by `source_line`, each carrying `source_file:line`, |
| 79 | +the offending `source_quote`, and a one-line error. For `element_*` failures it |
| 80 | +also captures the live page's same-kind labels (`observed_candidates`) and, when |
| 81 | +unambiguous, a `suggested_target`, so a downstream LLM can propose a doc fix |
| 82 | +without re-running the browser. |
| 83 | + |
| 84 | +## Adjusting coverage |
| 85 | + |
| 86 | +Edit `pages.json` and re-run `pnpm extract:all`. Add a page → it gets an |
| 87 | +assertion set; remove one → delete its `assertions/<slug>.json`. |
| 88 | + |
| 89 | +## CI |
| 90 | + |
| 91 | +`.github/workflows/docs-ui-tests.yaml` runs the evaluation on a weekly schedule |
| 92 | +and on manual dispatch: it installs Playwright, logs in with the |
| 93 | +`CONSOLE_STAGING_*` repo secrets, evaluates the committed baseline against |
| 94 | +staging, uploads the report, and files a `docs-ui-drift` issue when an assertion |
| 95 | +fails. Extraction never runs in CI — the reviewed baseline is the only input. |
| 96 | + |
| 97 | +## Known gaps (deferred) |
| 98 | + |
| 99 | +- **Coverage is a starting slice.** `pages.json` covers the Console section |
| 100 | + (index, settings, billing, store) — routes and landing-page elements. Widening |
| 101 | + to more pages is a follow-up: add to `pages.json`, re-extract, review, commit. |
| 102 | +- **Detail-page fixtures.** Assertions about Actor-detail, Schedule-detail, etc. |
| 103 | + need a known fixture to navigate to. The runner currently *skips* element |
| 104 | + assertions with no `at` route — surfacing the gap without false negatives. |
| 105 | + Requires the seeded-user fixtures (1 Actor, 1 task, 1 schedule, named storages, |
| 106 | + 1 webhook, 1 completed run) from the Notion plan. |
| 107 | +- **Left-nav group check.** The documented global nav items (Dashboard/Store/ |
| 108 | + Actors/…) are a Console-wide check, not a per-page claim — not modeled yet. |
| 109 | +- **Multi-step flows.** The schema only supports atomic claims (one |
| 110 | + navigate-then-check). "Click X, then Y, then Z" sequences are not modeled. |
| 111 | +- **Surface mismatch.** Some docs describe the public marketing site (e.g. |
| 112 | + `apify.com/store`) while the harness tests the Console (`/store`); the two can |
| 113 | + use different labels, so those element claims may not map. Check the surface a |
| 114 | + page actually documents before adding element assertions for it. |
| 115 | + |
| 116 | +## Files |
| 117 | + |
| 118 | +``` |
| 119 | +docs-tests/ |
| 120 | +├── pages.json # adjustable list of docs pages to cover |
| 121 | +├── assertions/ # committed baseline, one JSON per page (generated) |
| 122 | +├── prompts/ |
| 123 | +│ ├── extract-system.md # system prompt + known-routes table |
| 124 | +│ └── assertion-schema.json # JSON Schema for the extractor output |
| 125 | +├── scripts/ |
| 126 | +│ ├── extract.sh # one page → assertions/<slug>.json |
| 127 | +│ └── extract-all.sh # whole manifest |
| 128 | +├── reporters/issues-reporter.ts # custom Playwright reporter → output/issues.json |
| 129 | +├── tests/ |
| 130 | +│ ├── auth.fixture.ts # worker-scoped login from env creds (in-memory session) |
| 131 | +│ ├── from-doc.spec.ts # reads assertions/*.json, emits tests |
| 132 | +│ └── similarity.ts # suggest-replacement helper for failures |
| 133 | +├── playwright.config.ts |
| 134 | +└── .env # CONSOLE_STAGING_URL (gitignored) |
| 135 | +``` |
0 commit comments