Skip to content
weLabsweLabsStorefront Playbook
Live demoQuickstart

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.

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.

Terminal window
pnpm test:e2e # from the repo root; stop pnpm dev first

The 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.

@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.

Terminal window
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 ShopperActionNotAutomatable or ApprovalNotAutomatable. 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.

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 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 extend BaseTestCase, 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.php is gitignored and must point at a throwaway database (locally rsc_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.yml runs 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.yml runs PHPCS on PHP 8.2 against the changed PHP files. PHPCS runs in strict mode, so warnings fail too.
Storefront Playbook · Built by weLabsFeaturesFAQTalk to us