Developer DocsAcceptance & Testing

Acceptance & Testing

The pricing engine is a pure module pinned by a Vitest suite. The math that decides every quote lives in src/pricing/engine.ts as a single side-effect-free function, computePricing, and src/pricing/engine.test.ts locks its outputs to exact numbers. Because the engine takes no clock, no randomness, and no IO, the same inputs always produce the same price — so the fixtures can assert dollar amounts to the cent.

How testing works

Run the suite from the app root:

npm test

That runs vitest run (see package.json). The file src/pricing/engine.test.ts contains 30 test cases across 8 describe blocks. Tests build inputs from the real seeds — SEED_CAFE_CONFIG, SEED_LAW_OFFICE_CONFIG, and DEFAULT_SETTINGS from src/data/defaults.ts — and compare computePricing output against the acceptance numbers from the spec.

The acceptance criteria split into two kinds of verification:

  • Pinned as unit fixtures: AC-1, AC-2, AC-4, AC-5, AC-6, AC-7, AC-8. These are deterministic engine outputs, so they are asserted directly in engine.test.ts.
  • Verified in-browser: AC-3 (the 2-minute path), AC-9 (local run, reset/seed), and AC-10 (the role boundary). These are interaction- and environment-level guarantees that a unit test on the engine cannot reach, so they were checked by hand in the running app. AC-4’s “constants are live” behavior is pinned as a fixture and was browser-verified end-to-end (edit a constant in Settings, reopen the quote, watch the total move).

Two of the 8 describe blocks — additional engine rules and AC-8: engine purity — go beyond the numbered criteria. The purity block reads engine.ts as text and asserts the source contains no new Date, Date.now, Math.random, localStorage, fetch(, or node: import, then re-runs the CAFE quote twice and asserts the two results are deeply equal.

The fixture numbers are computed exactly from the seeded constants and the §4.2 formulas. When a value below is quoted to the cent, it matches an assertion in engine.test.ts — see Pricing Engine for where each number comes from and Settings & Constants for the seeded rates.

AC-1 — Seeded CAFE quote (channel letters)

Opening the seeded quote 1001 (front-lit channel letters, 4 letters × 18”, 5” returns, white acrylic, flush stud on brick, bucket access, permit on) must produce:

Line / valueExpected
Fabrication (per-inch)$1,368.00 (72” × $19)
Install$500.00 (5.0 hrs × $100, bucket ×1.25, brick ×1.0, above the $175 min)
Permit$300.00
Design$150.00
Engineering / minimum linesnone
TOTAL$2,318
Margin badgegreen at 74.7% (marginPct ≈ 0.7472)
Cost subtotal$345.81 = materials $173.31 (incl. 7% waste) + labor $172.50
Cost-plus reference price$691.62 (fabCostPlus)
Derived chips5 modules/letter, 20 total, 20 W, 1 power supply

The test asserts each lineItems entry by key (fabrication, install, permit, design), confirms engineering and minimum are undefined, and checks the derived object matches { modsPerLetter: 5, jobModules: 20, totalWatts: 20, psCount: 1 }.

AC-2 — Seeded LAW OFFICE quote (dimensional)

Opening the seeded quote 1002 (dimensional cast-aluminum letters, 8 letters × 12”, standard finish, flush stud on drywall, ground/ladder access, markup 1.5) must produce:

Line / valueExpected
Supplied letters$856.56 (8 × $71.38 × 1.5)
Install$300.00 (3.0 hrs × $100; under the 2×wholesale cap of $1,142.08; above the $175 min)
installCapAppliedfalse
Permit / engineering / design / minimum linesnone
Margin badge / marginPctnone (undefined)
TOTAL$1,157 (total ≈ 1156.56)

This quote reproduces the research taxonomy’s Example 2 exactly — every number above matches the taxonomy’s worked dimensional example. Dimensional quotes carry no margin badge because the supplied-letter price is a wholesale-plus-markup pass-through, not a cost-built fabrication.

AC-3 — Two-minute path

From the Quotes list, a tester can create a new front-lit quote (any text, 6 letters, 24”, otherwise defaults), enter a customer name, and download the branded PDF in ≤ 8 meaningful interactions and under 2 minutes, with the PDF containing every block specified in §4.4.

This is a UX-timing guarantee, so it was verified in-browser, not by a unit fixture. See the User Guide for the click-by-click path.

AC-4 — Constants are live

Changing price_per_inch_frontlit from 19 → 25 in Settings and reopening quote 1001 yields fabrication $1,800 and TOTAL $2,750. The fixture builds DEFAULT_SETTINGS with that one constant overridden and asserts both numbers; the same edit was also exercised through the Settings screen in the browser to confirm the UI re-prices live.

AC-5 — Margin warning fires

Setting price_per_inch_frontlit to 5 turns quote 1001’s badge red. Fabrication drops to $360 (72” × $5), the cost floor stays at $345.81, so the margin is (360 − 345.81) / 360 = 3.9% (marginPct ≈ 0.0394) and badge === 'red'. In the app this badge is accompanied by the floor warning text.

AC-6 — Halo & raceway rates

Three illumination/mount variants of quote 1001 are pinned:

VariantFabrication (per-inch)
Halo / reverse-lit$1,584 (72” × $22)
Front-lit + raceway$1,512 (72” × $21)
Halo + raceway together$1,584 — halo takes precedence over raceway

The raceway also adds a material line inside the cost-plus reference, not the displayed price: the fixture computes raceway material at ≈ $122.65 and confirms the raceway cost subtotal exceeds the flush-mount subtotal by ≈ $131.23, while the displayed per-inch fabrication moves only by the $21 vs $19 rate difference. The “halo takes precedence over raceway” assertion is the engine’s [A10] tie-break rule.

AC-7 — Dimensional grid interpolation

The dimensional price grid is anchored at fixed letter heights and linearly interpolated between them:

InputExpected gridBasePerLetterNotes
15” cast aluminum$112.17lerp(71.38, 152.96); heightClamped === false
18” cast aluminum$152.96exact grid anchor
30” cast aluminum$249.36clamped to the 6–24” range; heightClamped === true

Heights outside 6–24” are clamped, and heightClamped is set so the UI can show a visible note. A separate assertion confirms finish and mount multipliers compound onto the base (71.38 × 1.5 polished × 1.35 rail).

AC-8 — Engine integrity (purity)

The Vitest suite pins the AC-1/2/4/5/6/7 numbers as fixtures and passes, and the engine is proven pure. The purity block:

  • reads engine.ts as source text and asserts it contains no new Date, Date.now, Math.random, localStorage, fetch(, or node: import;
  • runs computePricing on the CAFE quote twice and asserts the results are deeply equal (a toEqual b).

This is what lets every other fixture assert exact cents — there is no nondeterminism to flake on.

AC-9 — Never empty, runs locally

A fresh clone followed by npm install && npm run dev renders the seeded Quotes list with both quote 1001 and 1002, and the “Reset demo data” action restores them. This is an environment/persistence guarantee, so it was verified in-browser rather than by a unit fixture. See Getting Started.

AC-10 — Role boundary

No cost, rate, labor-hour, or margin number appears anywhere in the quote editor or the PDF — the only exceptions are the margin badge and the two prices in the editor’s “Pricing detail” row. The full cost breakdown renders only on Settings. This is a UI-surface guarantee about what is rendered where, so it was verified in-browser. The underlying rule is the two-table model described on the Overview and Architecture pages.

Coverage matrix

ACWhat it coversCovered byStatus
AC-1Seeded CAFE quote totals & chipsunit fixturegreen
AC-2Seeded LAW OFFICE quote (taxonomy Ex.2)unit fixturegreen
AC-3Two-minute / ≤ 8-interaction pathbrowsergreen
AC-4Constants are liveunit fixture + browsergreen
AC-5Margin warning fires (red badge)unit fixturegreen
AC-6Halo & raceway rates + precedenceunit fixturegreen
AC-7Dimensional grid interpolation & clampunit fixturegreen
AC-8Engine integrity / purityunit fixturegreen
AC-9Never empty, runs locally; reset/seedbrowsergreen
AC-10Role boundarybrowsergreen

Taxonomy cross-check

The seeded quotes are validated against the research taxonomy’s worked examples:

  • AC-2 reproduces the taxonomy’s Example 2 exactly — every dimensional number matches.
  • AC-1’s per-inch fabrication matches exactly ($1,368). The install differs from the taxonomy narrative: Example 1 hand-waved a ~$600 install for a total of $2,418, whereas the deterministic engine yields a $500 install (5.0 hrs × $100, bucket ×1.25) and a total of $2,318within 4.2% of the narrative. The engine prefers a reproducible, formula-driven number over the narrative’s round figure.
💡

All 10 acceptance criteria pass. The seven deterministic criteria are green under npm test (30 cases), and the three interaction/environment criteria were verified in the running app. The git tag demo-v1 marks the repository state where this full AC-1…AC-10 pass holds.