Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d3a4724
resync order shipping of shipping lines and lineItems fulfillment NONE
pbennett1-godaddy May 28, 2026
efcd796
fix feedback from PR and add tests
pbennett1-godaddy May 28, 2026
ae77874
show error if sync fails
pbennett1-godaddy May 28, 2026
8981bc0
add confirmCheckout blocker
pbennett1-godaddy May 28, 2026
3a00d7d
throw actual error
pbennett1-godaddy May 28, 2026
3322ea5
reuse sync key logic
pbennett1-godaddy May 28, 2026
4511e19
refactor all update draft order syncs to avoid extra order mutations
pbennett1-godaddy May 28, 2026
6c6ec23
add recovery paths and errors for failed syncs
pbennett1-godaddy May 28, 2026
4964414
address feedback
pbennett1-godaddy May 28, 2026
127daa8
add catch and drain noops
pbennett1-godaddy May 28, 2026
bda5a2c
minor fixes for payment method selection and address field name
pbennett1-godaddy May 29, 2026
f486550
sync and type fixes
pbennett1-godaddy May 29, 2026
101bf26
Merge branch 'main' of https://github.com/godaddy/javascript into ref…
pbennett1-godaddy May 29, 2026
2c37b1f
add refetchOnWindowFocus for draft order
pbennett1-godaddy Jun 1, 2026
2e68c6c
fix shipping method tax recalc and resync order on order refresh
pbennett1-godaddy Jun 1, 2026
e9c84a0
test(react): cover free payment transitions
pbennett1-godaddy Jun 2, 2026
d04f864
test(react): cover checkout pure utilities
pbennett1-godaddy Jun 2, 2026
7839e22
test(react): cover address and pickup helpers
pbennett1-godaddy Jun 2, 2026
8223e7a
test(react): cover totals line-items and tips edges
pbennett1-godaddy Jun 2, 2026
5708ec3
test(react): cover draft order sync hydration
pbennett1-godaddy Jun 2, 2026
61546f3
test(react): cover session discount redirect polish
pbennett1-godaddy Jun 2, 2026
a31bbb5
test(react): add checkout tracking contract coverage
pbennett1-godaddy Jun 2, 2026
bb19099
test(react): finalize checkout tracking contract coverage
pbennett1-godaddy Jun 2, 2026
89148dc
add checkout test suite
pbennett1-godaddy Jun 2, 2026
81bb278
update test readme
pbennett1-godaddy Jun 2, 2026
edea6ac
remove coverage plan
pbennett1-godaddy Jun 2, 2026
45875c8
block mutations while confirming checkout and do not override non-ord…
pbennett1-godaddy Jun 2, 2026
4eacf7e
adjust changeset
pbennett1-godaddy Jun 3, 2026
71ec219
adjust pickup selection test
pbennett1-godaddy Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-checkouts-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@godaddy/react": patch
---

Refactor checkout draft order synchronization to flush pending order updates before confirmation, avoid duplicate mutations, and improve sync failure handling.
5 changes: 5 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"lint": "biome check ./src",
"lint:fix": "biome check --write --unsafe ./src",
"prepublishOnly": "pnpm run build"
Expand Down Expand Up @@ -102,6 +103,9 @@
},
"devDependencies": {
"@biomejs/biome": "^2.3.2",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^22.13.1",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
Expand All @@ -115,6 +119,7 @@
"tsdown": "^0.15.6",
"typescript": "~5.7.3",
"vite": "^6.4.1",
"@vitest/coverage-v8": "4.1.2",
"vitest": "^4.1.2"
},
"publishConfig": {
Expand Down
138 changes: 138 additions & 0 deletions packages/react/src/components/checkout/__tests__/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Checkout test infrastructure

Integration tests for `<Checkout />` live here. They mount the real component
through `renderCheckout()` and assert behavior against an in-memory mock of
`@/lib/godaddy/godaddy`. Pure-unit tests for transformers / hooks live next
to their source as `*.test.ts(x)` and don't need any of the helpers below.

`checkout-test-env.tsx` is the file you import from. It re-exports everything
in `checkout-test-utils.tsx` and additionally installs `vi.mock(...)` calls
for the wallet / express / Stripe payment buttons so jsdom doesn't try to
boot real SDKs. Importing **anything** from `./checkout-test-env` in a test
file is what activates those mocks — keep that import even if you don't
reference the named export it provides.

## Helpers

### Setup
- `mockGodaddyApi(options)` — install the in-memory API mock. `renderCheckout`
calls this for you with sensible defaults; call directly when you need a
bare-mocked API without rendering.
- `renderCheckout(options?)` — mount `<Checkout />` inside `<GoDaddyProvider>`
with a fresh QueryClient. Returns the standard RTL result plus
`{ user, queryClient, session, draftOrder }`.
- `renderCheckoutWithProps(checkoutProps, options?)` — same, but spread
`checkoutProps` onto the rendered `<Checkout />`.
- `createTestQueryClient()` — a QueryClient with retries off and infinite
staleTime, suitable for predictable integration tests.

### Builders / fixtures
- `buildCheckoutSession`, `buildDraftOrder`, `buildLineItem`,
`buildShippingAddress`, `buildBillingAddress`, `buildPickupLocation`,
`buildShippingRates` — deep-mergeable factory functions.
- `buildDraftOrderUpdate(input, session?)` — wrap an `UpdateDraftOrderInput`
payload with the `context` block the API expects.
- `noBillingAddress`, `getLastUpdateInput`, `getLastConfirmInput` (in
`checkout-test-fixtures.ts`).

### Operation log
- `getOperations(op?)` — recorded API calls, optionally filtered by name.
- `getOperationNames()` — names only.
- `getOperationOrder(names)` — array of indices for the first occurrence of
each named op in the log. Use this to assert relative ordering of recorded
operations without `.indexOf` chains.
- `clearOperations()` — reset the log (does not reset draft-order state).

### Error injection
- `setApiError(key, error)` — make every subsequent call to the matching API
reject. `key` is the API key form, e.g. `'updateDraftOrder'`,
`'applyShippingMethod'`, `'applyDiscount'`.
- `clearApiError(key)` — reset.
- `setApiErrorOnce(key, error?)` — fire `error` for the **next** matching
call only, then auto-clear. Useful for "fail then recover" scenarios
(auto-apply rollback, draft-order sync retry, free → paid coupon round-trip).

### Mutating the in-memory draft order
- `getCurrentDraftOrder()` / `setCurrentDraftOrder(draftOrder)`.
- `setFeeTotal(value, currencyCode?)` — adjust `draftOrder.feeTotal` (and the
recomputed total) for the next refetch. Used to exercise the fees row in
the totals summary.
- `setPriceAdjustments(adjustments)` — set the response returned by
`getDraftOrderPriceAdjustments`. Defaults to `[]`. Only consumed by the
express-checkout buttons; standard checkout flows ignore it.

### Timing helpers
- `advanceCheckoutDebounce(ms = 1200)` — advance fake timers and flush
promises in one shot to drive the draft-order sync debounce.
- `flushPromises()` — `act(async () => Promise.resolve())`.
- `waitForCheckoutReady()` — wait until the form has rendered Contact +
Payment.
- `waitForCheckoutIdle()` — wait until pending mutations have settled.
- `waitForOperation(op, count?, timeout?)` — wait until at least `count`
occurrences of `op` are recorded.
- `refetchDraftOrder(queryClient, sessionId)` — invalidate the draft-order
query.

### Tokenize / wallet
- `MockTokenizeJs` — replaces `window.TokenizeJs`. `setupCheckoutTestGlobals`
wires it up automatically.
- `getLastTokenizeInstance` / `getTokenizeInstances`.
- `WalletSupport` (option on `mockGodaddyApi`) controls what
`MockTokenizeJs.supportWalletPayments()` resolves with.

### URL / storage / JWT
- `mockWindowLocation`, `setCheckoutUrl`, `seedCheckoutSessionStorage`,
`restoreWindowLocation`, `getMockedLocation`.
- `createMockJwt(payload?)` — produces a JWT with a far-future `exp` by
default.

### Form interaction
- `typeIntoNamedField(user, name, value)` / `typeIntoPlaceholder` /
`getTextbox(name)` / `getNamedInput(name)`.
- `fillShippingAddress(user, overrides?)` — fill all required shipping
fields with sensible defaults.

### Tracking (T-005)
- `mockTrack()` — returns `{ getTrackedEvents, clearTrackedEvents,
expectTracked }`. Requires the test file to install
`vi.mock('@/tracking/track', ...)` at the top:
```ts
vi.mock('@/tracking/track', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/tracking/track')>();
return { ...actual, track: vi.fn() };
});
```
`expectTracked(eventId, propsMatcher)` accepts either an `expect.objectContaining`-style
partial object or a `(props) => boolean` predicate. `eventId` matches
either an exact id or a suffix (the production `track` fn prepends
`godaddy.checkout.` — both forms work).

## Test design guidelines

- Keep integration tests focused on the smallest behavior needed for the
feature under test. Prefer separate tests for rendering state, validation,
transitions, confirmation payloads, and tracking instead of one broad test
that clicks through the entire checkout flow.
- Avoid extra submit/confirm clicks when the assertion only needs to verify
that UI rendered or switched state. Payment button clicks trigger React Hook
Form validation, draft-order sync, query updates, and checkout confirming
state; exercising those side effects unnecessarily can produce unrelated
`act(...)` warnings and make tests harder to reason about.
- Seed fixtures into the intended initial state when possible. For example, if
a test only needs a valid names-only billing form, put those names in the
draft-order fixture instead of typing them and triggering debounced sync.
- Assert the tracking source that actually owns the behavior. Payment lifecycle
events come from the confirm-checkout path; parent form submit handling is
intentionally minimal and should not be used as a proxy for payment button
behavior.

## Gotchas

- **Fake timers are on by default** (`vi.useFakeTimers({ shouldAdvanceTime: true })`).
When you need to drive the debounce manually, prefer `advanceCheckoutDebounce`.
- The wallet / express / Stripe button mocks are inert — they render
recognizable test ids but never tokenize. Don't try to assert on
authorize/confirm flows that go through them. End-to-end coverage for those
buttons belongs in real-browser tests.
- `clearOperations()` only clears the log; it does not reset the draft order.
Use `setCurrentDraftOrder` or re-call `mockGodaddyApi` for a clean slate.
Loading