Skip to main content

How QA works

Every tenant ships a machine-readable test contract: qa-manifest.yaml, at the root of the tenant folder. It declares, per document type and per action, exactly what a correct render looks like — which sections must appear, which must not, and which strings must never show up in output.

Changes are test-driven: you extend the manifest first (the suite goes red), then implement until it goes green. The manifest is not documentation that drifts from reality — the test harness reads it directly and renders every declared dataset on every change. See Running the test harness.

What the manifest declares

Per document type

Each entry under doc_types declares the schema, the template, and one or more datasets. Each dataset carries expect_selectors (section ids that must be present in the rendered document) and absent_selectors (section ids that must not render). The proposal document also declares an itinerary_mode per dataset, because it renders either day-based or service-based itineraries.

A real excerpt (one dataset shown):

tenant_id: meridian
doc_types:
option:
schema: schemas/option-1.0.json
template: templates/option/v1.0/template.html
datasets:
- id: touring
data: sample-data/option.json
description: Fixed-departure escorted tour
itinerary_mode: days
expect_selectors: ["#hero", "#personal-message", "#highlights",
"#itinerary", "#accommodations", "#inclusions", "#pricing",
"#terms", "#specialist", "#trust-pillars", "#footer"]
absent_selectors: ["#services", "#stay-units", "#flights"]

The selector ids come from a canonical section registry (documented in the manifest header) — templates must use these exact ids, because the harness asserts them.

Per action

Document actions render the same dataset with different _display flags — a no-price proposal hides pricing, an option hold shows the hold banner. Each actions entry declares display_overrides (merged into _display exactly as the blueprint's hide_sections / show_sections configuration would inject them) plus the selector expectations that variant must produce:

actions:
- id: create_no_price_quote
dataset: touring
display_overrides: { showPricing: false, showPaymentButton: false,
showTravelProtection: false, showTerms: false }
absent_selectors: ["#pricing", "#payment-cta", "#travel-protection", "#terms"]

This mirrors the blueprint one-to-one: the manifest encodes what each create_document_actions entry must produce, so a template edit that breaks an action variant fails the suite even if the default render still looks fine. See Tailoring documents for how to add action entries.

Defaults

The defaults block pins the conditions every render runs under:

defaults:
template_version: v1.0
schema_version: '1.0'
viewports:
- { name: desktop, width: 1200, height: 900 }
- { name: mobile, width: 390, height: 844 }
forbidden_strings: ["undefined", "[object Object]", "NaN", "{{", "Invalid date"]
  • Versions — the template and schema versions under test are pinned, so a version bump is an explicit, reviewed change.
  • Two viewports — every dataset and every action variant is rendered at desktop (1200×900) and mobile (390×844), and the selector assertions run at both.
  • Forbidden stringsundefined, [object Object], NaN, {{, and Invalid date must never appear anywhere in rendered output. Each of these is the fingerprint of a specific defect: missing data reaching a template, an object rendered without formatting, broken arithmetic, an unrendered placeholder, or a bad date value.
Proposal rendered at the desktop viewport the harness asserts against
Desktop (1200×900) — selector assertions and screenshot
Proposal rendered at the mobile viewport the harness asserts against
Mobile (390×844) — the same assertions, zero horizontal overflow

Why this matters to you

Every template or configuration change is verified by rendering every dataset of every document type, at both viewports, including every action variant, before it can merge. That means:

  • A change to one section cannot silently break another document type — the whole matrix re-renders.
  • Brand or token changes are verified visually and structurally, not by eyeballing one page.
  • The no-price guarantee, the trade firewall, and section visibility rules are asserted by machine on every merge request, not by convention.

The manifest travels with your tenant when you fork the baseline. Keep it authoritative: when you trim document types, remove their manifest entries; when you add anything, add its expectations first.