Suites
Three pre-baked vitest suites cover the common shape contracts. Templates wire them in three lines each: when a new case lands in the SDK, every storefront inherits it on `bun update @cimplify/sdk`.
createBrandSuite
Validates lib/brand.ts against BrandSchema. Catches missing fields, placeholder copy, malformed contact info, and seed/businessId mismatch.
import { createBrandSuite } from "@cimplify/sdk/testing/suite";
import { brand } from "../lib/brand";
createBrandSuite({ brand });Cases registered:
- conforms to the Cimplify brand contract
- declares a known mock seed
- has no placeholder copy left in (lorem ipsum, todo, fixme, "your store", …)
- uses the
bus_businessId convention - zod-parses cleanly with full issue paths
createCartFlowSuite
Boots the in-process mock and exercises the full cart lifecycle with shape assertions on every response. Catches SDK ↔ mock contract drift, missing fields, dropped variant payloads.
import { createCartFlowSuite } from "@cimplify/sdk/testing/suite";
import { brand } from "../lib/brand";
createCartFlowSuite({ seed: brand.mock.seed, businessId: brand.mock.businessId });Cases registered:
- starts with an empty cart matching the canonical shape
- adds the first product, persists it, returns a shape-valid cart
- dedupes by
line_keywhen the same product is added twice - removes items and zeroes the subtotal
- (when
businessIdis supplied) returns the brand's businessId on cart responses
createContractSuite
Exercises the schemas against actual mock traffic. Catches outbound payloads dropping required fields and inbound responses missing fields the storefront depends on.
import { createContractSuite } from "@cimplify/sdk/testing/suite";
import { brand } from "../lib/brand";
createContractSuite({ seed: brand.mock.seed });Cases registered:
AddItemPayloadSchemaaccepts a minimal valid bodyAddItemPayloadSchemarejects negative quantityAddItemPayloadSchemarejects emptyitem_id- cart line items returned by the mock match
CartItemSchema CheckoutResponsefrom the mock includesbill_tokenandorder_id
The extend hook
Append your own cases inside the same describe block. createBrandSuite hands you vitest's it; the cart and contract suites also hand you a getHandle() getter for the live TestClientHandle.
import { createBrandSuite } from "@cimplify/sdk/testing/suite";
import { brand } from "../lib/brand";
import { expect } from "vitest";
createBrandSuite({
brand,
extend: (it) => {
it("ships in our merchant's currency", () => {
expect(brand.currency).toBe("GHS");
});
},
});import { createCartFlowSuite } from "@cimplify/sdk/testing/suite";
import { brand } from "../lib/brand";
import { expect } from "vitest";
createCartFlowSuite({
seed: brand.mock.seed,
businessId: brand.mock.businessId,
extend: ({ getHandle, it }) => {
it("every product priced in GHS", async () => {
const list = await getHandle().client.catalogue.getProducts();
if (!list.ok) throw list.error;
for (const p of list.value.items ?? []) {
expect(p.currency).toBe("GHS");
}
});
},
});Suite options
| Suite | Required | Optional |
|---|---|---|
createBrandSuite | brand | label, extend |
createCartFlowSuite | none | seed, businessId, label, extend, all CreateAppOptions (frozenAt, rngSeed, …) |
createContractSuite | none | seed, label, extend, all CreateAppOptions |
Next
-
Test client
createTestClientfor custom flows -
Schemas The shape contract behind every suite
Test client
`createTestClient({ seed })` spins up an in-process Hono mock and a typed SDK client wired to it via `fetch` injection: no port, no MSW, no `globalThis` mutation. Every call gets its own session token, so tests in the same module run in parallel safely.
Schemas
Every shape the SDK, mock, and storefronts must agree on lives as a zod schema in `@cimplify/sdk/testing`. Schemas are registered with metadata, paired with typed `assertX` helpers, and exported as JSON Schema for downstream tooling.