Developers
Testing
The storefront's two test seams — the Playwright smoke suite in Demo Mode and the Port contract suite — the CI jobs that run them, and the Companion plugin's PHPUnit and PHPCS checks.
The storefront tests at two seams only: the browser, and the Port. Anything a unit test of an adapter’s internals or of a component would catch surfaces at one of those two, so the repository doesn’t write such tests. New Port behaviour extends the contract suite; every new screen or SRS scenario gets a Playwright test. The Companion plugin is tested separately with PHPUnit.
Seam 1: the Playwright smoke suite
Section titled “Seam 1: the Playwright smoke suite”apps/storefront/e2e/smoke/ drives the real app in a browser. It runs in Demo Mode with a hermetic environment, so it needs no
backend, no network and no payment account, and it must stay green after every change.
pnpm test:e2e # from the repo root; stop pnpm dev firstThe web server Playwright starts (playwright.config.ts) pins what makes runs repeatable: COMMERCE_SOURCE=mock (the Fake
Adapter), an RSC_API_URL that points at an unreachable port so nothing leaks to a real backend, a fixed demo clock (DEMO_NOW),
a dummy AUTH_SECRET, strict i18n, the default Template Family, English only, and a few Fake Adapter switches that exercise edge
cases (custom scripts on, the cookies policy page omitted, an empty category).
| Spec | Covers |
|---|---|
account-security.spec.ts |
Disconnecting a social sign-in provider, and refusing to remove the last way to sign in |
address-book.spec.ts |
The Address Book in the account and at checkout (the legacy pair is tagged @legacy-addresses) |
auth.spec.ts |
The sign-in form, account-area redirects, the demo account |
cart.spec.ts |
Empty cart, add to cart, variants, the mini cart |
catalog.spec.ts |
Home, shop, category and other listings (@look) |
checkout-payment.spec.ts |
The payment-page script policy, the demo card, declines and challenges (offline-only cases tagged @offline-only) |
chrome.spec.ts |
Header, icon menu, footer, toasts (@look) |
custom-scripts.spec.ts |
Header and footer script placement, hydration, SPA page views |
express-checkout.spec.ts |
The demo wallet on product, cart and checkout (absence tagged @offline-only) |
locale.spec.ts |
Locale prefixes and direction, the switcher, Translatable Markers (@locale) |
order-confirmation.spec.ts |
Thank-you page variants, such as on hold |
order-status.spec.ts |
The order status steps |
payment-security.spec.ts |
The product page’s Payment and Security card in each mode (@look) |
product-preview.spec.ts |
Editor preview links and the read-only preview page |
reviews.spec.ts |
Summary, media, seller replies, filters and sort, guest replies and helpful votes |
seo.spec.ts |
Robots rules, canonical addresses, sharing tags and structured data for public and private routes |
static-pages.spec.ts |
Static pages, Policy Pages (including the real 404 for a missing one), About |
stock-alerts.spec.ts |
Back-in-stock sign-up and its email links |
trust-badges.spec.ts |
Trust badges under the buy buttons |
turnstile.spec.ts |
The human check on reviews, replies and stock alerts (with turnstile-stub.ts) |
Rules for this seam:
- Every Gherkin scenario in an SRS feature file gets a Playwright test, and the test renders the exact UI copy the spec quotes.
- A new route goes into
seo.spec.ts, in its public or private list. - Woo-mode browser runs against the QA store (
westore.welabs.dev) are read-only: never create orders or accounts there.
Seam 2: the contract suite
Section titled “Seam 2: the contract suite”@welabs/retail-commerce-core/contract-suite is the behavioural specification of the Port. It exports one function,
describeAdapterContract(options), which each adapter package calls from its own contract.test.ts. It is backend-agnostic and
asserts shapes and invariants, never specific catalog contents.
pnpm test # every package's Vitest suite; hermetic- Capability-gated. A section whose capability the adapter does not declare is skipped, never failed. The first test checks that every capability flag is an explicit boolean.
- Sections cover discovery reads, featured products, videos, reviews, trust badges, the Payment and Security card, scheduled sales, brands and brand facets, identity, theme, Policy Pages, SEO fields, cart, embedded payment, express checkout, wishlist, stock notifications, order history, geography, customer stats, the Address Book, invoices, Product Preview and multilingual content.
- Optional hooks unlock deeper cases only the adapter’s own test can drive:
issuePaymentToken,completeShopperAction,approvePreparedPayment,issueExpressPaymentToken,issueProductPreviewLink,makeAddressBookAdapter,makeForeignOrder,makeLocalizedAdapter,freeShippingDestinations,exerciseReviewWrites. - Skipped, not faked. When a Processor step needs a person in a browser (Stripe’s 3-D Secure page, a PayPal sandbox buyer), the
hook throws
ShopperActionNotAutomatableorApprovalNotAutomatable. Everything before that step is still asserted.
The Fake Adapter runs the full suite on every pnpm test; it is the reference implementation.
The WooCommerce run, against a disposable store only
Section titled “The WooCommerce run, against a disposable store only”The WooCommerce adapter’s contract test does nothing unless WOO_CONTRACT=1 and WOO_STORE_URL are set, so pnpm test stays
hermetic. Its inputs:
| Variable | Purpose |
|---|---|
WOO_CONTRACT=1 |
Opt in |
WOO_STORE_URL, WOO_CONSUMER_KEY, WOO_CONSUMER_SECRET |
The adapter’s own connection settings |
WOO_CONTRACT_STRIPE_SECRET_KEY |
Optional. A Stripe test secret key (sk_test_ or rk_test_) of the account the store’s Stripe gateway uses; enables the placement cases. Any other key is refused. |
WOO_CONTRACT_BILLING |
Optional JSON billing address the store delivers to (default: Berlin) |
WOO_CONTRACT_EXPRESS_PAGES |
Optional, such as product,cart,checkout: the pages the store has an express wallet switched on for |
Storefront, .github/workflows/ci.yml, on pushes to develop and main and on every pull request:
| Job | Runs |
|---|---|
checks |
pnpm typecheck, pnpm lint, the RTL grep guard (no physical-direction utilities in shopper-facing source), pnpm test |
smoke |
The full Playwright suite |
family-smoke |
--grep @look, once per Template Family in the matrix (today only default) |
locale-smoke |
--grep @locale with STOREFRONT_LOCALES=en,ar, both with and without a backend header document |
address-legacy-smoke |
--grep @legacy-addresses with FAKE_ADDRESS_BOOK_OFF=1 |
offline-only-checkout-smoke |
--grep @offline-only with FAKE_EMBEDDED_PAYMENT_OFF=1 (no embedded methods, no express button) |
.github/workflows/woo-contract.yml runs the embedded-payment contract cases against a disposable WooCommerce store weekly
(Mondays) and on demand. It skips itself with a notice unless the repository has the WOO_CONTRACT_STORE_URL secret.
The rule: no unit tests of internals
Section titled “The rule: no unit tests of internals”There are no tests of adapter internals or of components. A mapper bug shows up as a contract-suite failure; a component bug shows
up in the smoke suite. This keeps the tests about behaviour the storefront promises, so refactoring the inside of an adapter or a
component never means rewriting tests. A handful of pure-function tests sit beside shared logic where the behaviour is the
function, such as SEO pattern filling in commerce-core (its cases are a JSON fixture the Companion also tests against) and a few
WooCommerce mapper cases.
The Companion plugin’s tests
Section titled “The Companion plugin’s tests”The Companion is test-driven by its own rules (its CLAUDE.md): every class in includes/ has a mirror test class at the same
sub-path under tests/php/src/, written first.
| Command | Runs |
|---|---|
composer test |
The main suite (phpunit.xml), then the integration suite (phpunit-integration.xml) |
composer test-f <name> |
--filter on the main suite |
composer test-g <group> |
--group on the main suite |
composer test-integration |
The integration suite only |
composer test-contract |
The payment plugin-contract check only |
composer phpcs |
PHPCS with phpcs.xml |
- Main suite: WordPress integration tests through
wp-phpunit, with Brain Monkey and Mockery. It runs without WooCommerce and fakes it. Tests extendBaseTestCase, which boots a fresh REST server and an admin user. - Integration suite: the same throwaway database plus WooCommerce itself, so tests drive the real Store API checkout. It
includes
PaymentPluginContractTest, which asserts every Stripe, PayPal and Store API internal that embedded payment depends on. - Test database:
tests/php/phpunit-wp-config.phpis gitignored and must point at a throwaway database (locallyrsc_tests). The suite drops and recreates every table in it on each run, so it must never point at a real site’s database. - CI:
phpunit.ymlruns both suites on every pull request, with WooCommerce 11.1.2, Stripe Gateway 11.0.0 and PayPal Payments 4.1.3, the versions embedded payment is pinned to.phpcs.ymlruns PHPCS on PHP 8.2 against the changed PHP files. PHPCS runs in strict mode, so warnings fail too.