Skip to main content

Testing primer — the Playwright suite, in plain terms

Audience: anyone new to tests/ · Scope: the concepts and vocabulary behind the functional test suite — what kind of tests these are, how they're built, and the words to use · Last reviewed: 2026-07-11

Part of Testing. The how-to is guide.md; the reusable state/fixture commands are support-api.md; a catalog of the specs is catalog.md; install & run is tests/README.md.

What kind of tests are these?

End-to-end (E2E) functional tests, driven through a real browser with Playwright. "End-to-end" because each test exercises the whole stack the way a person would — click in the browser → ConfigBox controller → model → database → the outbound Pipedrive call. We don't test functions in isolation (that would be unit testing); we verify observable behaviour across the system.

In practice they double as integration tests for the Pipedrive sync: the headline assertion is usually "did the right data get sent to Pipedrive?", checked against the intercepted request.

Every test follows Arrange → Act → Assert (AAA):

PhaseHere it meansExample
ArrangeGet the system into a known state (a.k.a. fixtures / seeding)log in as the agent; create a realistic quote
ActPerform one real user action in the browsermark a quote "Lost"; change a line-item quantity
AssertCheck the outcome in two placesthe quote row in the DB and the captured Pipedrive payload

The central trick: intercept the boundary, don't call Pipedrive

There's no Pipedrive sandbox — only production. So we never let a test hit Pipedrive. Every outbound write funnels through one place (the BcPipedriveApi write wrappers), which in capture mode record the outgoing request to a file and return a fake success instead of sending it.

In testing vocabulary that interception point is a test double at the integration boundary — specifically a stub (returns a canned response so the flow continues) that is also a spy/recorder (captures the call arguments for later inspection). Asserting on those captured payloads is contract / payload testing: we check the shape and values of what would have been sent.

A second, independent safety layer — the org-6034 guard — is a production guardrail (not a test concept): outside live, the gate refuses any deal write that isn't for the Pipedrive DEV organisation. Capture mode means nothing is sent at all; the guard means even a non-capture mistake can't touch a real deal.

Test data: created through the app, never hand-written SQL

The "Arrange" step uses fixtures (a.k.a. seed data). We build them by driving the real application — e.g. configuring a product and adding it to a quote — rather than inserting rows directly. This "create through the domain, not the database" approach (an application factory) means pricing, BOM, totals and the branch→org link are all correct by construction. Each test (or the seed project) cleans up what it created, so runs are isolated and data doesn't accumulate.

Playwright vocabulary you'll meet

TermWhat it is
SpecA test file (specs/**/*.spec.ts). Holds one or more test(...) blocks.
ProjectA named run profile: a browser + which specs it runs + what it depends on. We split anon (no login) and agent (logged-in) projects.
Setup / teardown projectA project other projects depend on, used to prepare/clean shared state — here: grant cookie consent, log in, seed a quote, delete it afterwards. Wired via dependencies and teardown.
storageStateA saved snapshot of cookies + localStorage (e.g. the consent cookie, the logged-in session). Replayed by later projects so we don't repeat login/consent in every test.
FixturePlaywright's term for something injected into a test, like the page (a browser tab). (Not to be confused with test-data fixtures above — same word, different sense.)
LocatorA lazy handle to an element (page.locator('…')).
Web-first assertionexpect(locator).toBeVisible() etc. — auto-waits and retries until true or it times out, which is why we rarely sleep.

The support layer (the glue between Node and the app)

Playwright runs in Node; the app is PHP/Joomla. They meet through a secret-gated HTTP endpoint (ConfigboxControllerBctestsupportBcTestSupport) that runs the app's own code — so a spec can inspect and prepare state, and run the deal update in capture mode, against any environment without a local PHP checkout. Using the app's own code (rather than re-implementing DB access in JS) keeps the tests honest and avoids duplicating schema knowledge. The full command set is support-api.md.

HelperRole
support/cli.tsThe support.* client — calls the test-support endpoint over HTTP: read a quote's pipedrive_* columns, run the deal update in capture mode, seed stages, mint follow-up tokens, delete a quote, and more.
support/captures.tsReads the (inline-returned) Pipedrive capture payloads, filtered by quote serial.
support/seedQuote.tsDrives the configurator to build a realistic, priced quote (the application-factory seed).
support/followupForm.tsRace-proof gestures for the follow-up form (chooseOpenAndStage, chooseLost, submitAndConfirm).
support/{paths,env,loadEnv,seedFile,nav}.tsPaths/config, .env loading, the seed-serial file, abort-tolerant navigation.

A test, start to finish (concrete)

specs/agent/change-line-item-quantity.spec.ts:

  1. ArrangeseedDecorativeQuote(page) configures the first Decorative product and adds it to a new quote owned by the agent (a priced line item).
  2. Act/Assert (baseline) — run the deal update in capture mode; read the captured value.
  3. Act — change the line-item quantity to 3 in My Quotes.
  4. Assert (DB) — the quote is flagged for a Pipedrive update.
  5. Assert (payload) — run the deal update again; the captured value is now higher.
  6. Cleanup — delete the seeded quote.

Glossary (the terms, crisply)

  • E2E / functional test — verifies behaviour through the real UI across the whole stack.
  • Integration test — verifies two systems work together; here, ConfigBox ↔ Pipedrive (at the payload level).
  • AAA — Arrange, Act, Assert: the shape of every test.
  • Test double — a stand-in for a real dependency. Flavours: stub (canned response), spy / recorder (captures calls), mock (pre-set expectations). Our capture-mode gate is a stub+spy.
  • Capture mode — our gate records outbound Pipedrive payloads and sends nothing.
  • Guardrail / guard — the always-on org-6034 safety rule in the app (not a test double).
  • Fixture / seed data — the known starting state a test arranges.
  • Application factory — building seed data by driving the app's own flows, not raw SQL.
  • Flaky test — passes/fails non-deterministically (usually a timing race); we fix, not retry.
  • Test isolation — each test sets up and tears down its own data so order doesn't matter.

Where next

  • guide.md — the how-to: local stack, projects, accounts, settings, patterns, adding a test.
  • support-api.md — the reusable test-support commands.
  • catalog.md — every existing spec and what it asserts.
  • ../pipedrive/testing.md — the capture / org-6034 guard gate and the deal-payload matrix.
  • tests/README.md — install and run.