Running the test harness
The Edge template QA harness is provided by Kaptio during onboarding. It runs locally
with Node.js against a checkout of your configuration repository, reads your tenant's
qa-manifest.yaml, and verifies every declared expectation. This page is a
step-by-step guide a QA engineer can follow without prior context.
What the harness does
For each run, the harness:
- Validates every dataset against its schema using ajv (JSON Schema validation). Bad sample data fails before anything renders.
- Renders each dataset — and each action's
display_overridesset — through the same render engine the platform uses in production. - Asserts the manifest's selectors (
expect_selectors/absent_selectors) at both declared viewports: desktop 1200×900 and mobile 390×844. - Scans rendered output for forbidden strings (
undefined,[object Object],NaN,{{,Invalid date). - Produces screenshots and PDFs for every render, so failures can be inspected visually and print output can be reviewed.
Step 1 — Prerequisites
You need:
- Node.js 20 or later (
node --versionto check). - The harness package, provided by Kaptio during onboarding.
- A checkout of your configuration repository, on the branch you want to test.
Step 2 — Install
From the harness directory, install dependencies once:
npm install
Step 3 — Run the full suite
node run-tests.mjs --configs <your-config-repo> --tenant <your-tenant> --actions
--configspoints at the root of your configuration repository checkout.--tenantnames the tenant folder to test.--actionsincludes the action matrix — every action variant declared in the manifest, not just the default renders. Always include it before a merge request.
Step 4 — Narrow the loop while iterating
Run a single document type while you work on it:
node run-tests.mjs --configs <your-config-repo> --tenant <your-tenant> --doc option
Render one dataset straight to an HTML file you can open in a browser:
node render.mjs --configs <your-config-repo> --tenant <your-tenant> --doc option \
--data <your-config-repo>/tenants/<your-tenant>/sample-data/option.json \
--out /tmp/option.html
Run the helper parity gate — required whenever a template uses a new Handlebars helper, because helpers must exist in every production render registry:
node run-tests.mjs --configs <your-config-repo> --tenant <your-tenant> \
--parity --platform <path-to-the-platform-package-supplied-with-the-harness>
The parity gate needs the platform package (supplied alongside the harness) to compare
helper registries, which is why --platform is required here and not for the render
suite.
Step 5 — Read the report
The suite prints a per-dataset, per-action result and writes screenshots and PDFs for each render. On failure, start with the artifact for the failing render — most failures are obvious the moment you see the page.
Step 6 — Meet the quality gate
Before a change merges: all datasets and all document types green, including the action matrix. A red action entry is a real defect even when the default render passes — it means one of your configured document actions produces broken output. The full release checklist is in The quality bar.
Common failures and what they mean
Selector missing (expect_selectors not found)
The section did not render. There are two usual causes:
- The backing data is empty. Every section collapses entirely when it has no data (the empty-collapse guard), regardless of display flags. Check the dataset for the fields the section reads.
- A
_displayflag hid it. Check the action'sdisplay_overridesin the manifest against the blueprint'shide_sections/show_sectionsfor that action — they must agree.
Open the screenshot for the failing render first; it usually shows immediately whether the section is absent or merely empty.
Selector present (absent_selectors found)
A section rendered that should not have. Usual causes:
- A missing or wrong
_displayguard in the template. Sections that are hidden by default must use the positive pattern{{#if _display.showX}}; content sections use the unless-false pattern. Using the wrong pattern inverts the default. - Data leaked into a dataset that should not carry it — for example price-line data in a dataset meant to exercise a document without a breakdown.
Forbidden string in output
An unrendered placeholder or bad data reached the output. Each string fingerprints a different defect:
{{— a template expression did not resolve: a helper missing from a render registry, or a typo in the expression.undefined/NaN— missing or non-numeric data reached a template without a guard.[object Object]— an object was interpolated without formatting.Invalid date— a date helper received a nil or malformed value; date expressions must be guarded against missing data.
Open the rendered HTML, search for the string, and trace the field back through the dataset and schema.
Schema validation error (ajv)
The dataset and the schema disagree — a data/contract mismatch. The ajv error names the
exact JSON path. Either the data is wrong, or the schema needs adjusting — remember
that optional fields must be nullable (type: ["string", "null"]) because the data
layer emits nulls; only required fields stay strict. See
Schemas.
Parity gate failure
A template uses a Handlebars helper that is not present in every production render
registry; it will render in one context and fail in another. Replace the helper with
one that exists everywhere, or use an inline partial. and/or/not helpers do not
exist — nest blocks instead.
Deeper regression checks
Beyond the standard suite, the harness ships deeper regression checks that are run after structural changes (new sections, new document types, layout reworks):
- Minimal-data and empty/null renders — documents render correctly when optional data is absent.
- Action-by-dataset and flag matrices — every flag combination against every dataset.
- Helper parity — the registry check described above.
- Image audit — every image URL reachable and subject-appropriate.
- DOM, contrast, responsive, and print probes — accessibility contrast, horizontal-overflow detection at mobile width, and print/PDF flattening.
Each of these protects against a specific class of failure. They are not required on every copy change, but run the relevant ones whenever you change document structure — and expect Kaptio to run them on review.