Skip to content

VCST-4776: Add coupon scenarios (E2E, GraphQL, dataset) - #187

Open
Lenajava1 wants to merge 14 commits into
devfrom
feat/VCST-4776
Open

VCST-4776: Add coupon scenarios (E2E, GraphQL, dataset)#187
Lenajava1 wants to merge 14 commits into
devfrom
feat/VCST-4776

Conversation

@Lenajava1

Copy link
Copy Markdown
Collaborator

Add coupon test coverage for the cart discount/coupons sidebar and the /account/coupons page, plus the supporting GraphQL and UI layers and seeded coupon/promotion/product dataset.

  • GraphQL: validateCoupon operation, promotionCoupons operation + type, cart discountTotal/taxTotal fields
  • E2E: cart coupon presets (apply/remove/anonymous), percentage-on-sale, account coupon list, copy-to-clipboard, VCST-5233 case-fidelity guard
  • Dataset: preset/percentage/fixed/expired/lowercase coupons + SALE-001

Lenajava1 and others added 14 commits July 22, 2026 16:13
Add coupon test coverage for the cart discount/coupons sidebar and the
/account/coupons page, plus the supporting GraphQL and UI layers and
seeded coupon/promotion/product dataset.

- GraphQL: validateCoupon operation, promotionCoupons operation + type,
  cart discountTotal/taxTotal fields
- E2E: cart coupon presets (apply/remove/anonymous), percentage-on-sale,
  account coupon list, copy-to-clipboard, VCST-5233 case-fidelity guard
- Dataset: preset/percentage/fixed/expired/lowercase coupons + SALE-001

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI (latest frontend build) confirmed 5 tests fail when the coupon UI/data
is absent. Make them skip gracefully or assert robustly instead:

- promotionCoupons: poll_until populated; skip on persistent-empty
- account coupons E2E (x3): probe-and-skip when /account/coupons markup absent
- percentage_ui_on_sale: skip when product not on sale; assert discount>0 and
  grand-total drop instead of brittle subtotal*pct

Real assertions preserved on builds that do surface the coupon UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…style

Add isPublic, localizedLabel, and endDate to coupon promotion seeds so
promotionCoupons and preset E2E tests exercise real data instead of
skipping. Use `is True` over `== True` and strip trailing whitespace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Assert cart money fields are present before dereferencing in
_assert_totals_consistent so a missing field fails cleanly instead of
raising AttributeError. Drop the brittle grand-total-text delta check in
the preset-switch E2E test; applied-card identity already proves the switch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The copy handler writes via the async Clipboard API, which has not always
resolved by the time click() returns, so an immediate readText() intermittently
saw an empty clipboard (empty at t=0ms, populated at t=200ms in local runs).
Poll the read briefly until the write lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace CartCouponSection.card_by_name (brittle promotion-name substring
match) with applied_code_input, the applied card's readonly code field.
Assert the preset apply/switch via expect(...).to_have_value(code): the
code survives both default and applied states (the applied card's button
drops it, and value is not a reflected DOM attribute), and to_have_value
polls out the non-atomic switch between two applied coupons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Non-public promotion (unreachable $100k min-subtotal) plus its
QAMINSUB100K coupon, for the unmet-condition coupon test. Adds the
MIN_SUBTOTAL_COUPON_CODE constant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- promotionCoupons: first limit + name asc/desc sort (verified live);
  anonymous access is denied server-side (Unauthorized).
- Cart coupon rejected by an unmet min-subtotal condition
  (recorded but is_applied_successfully False, no discount).
- E2E: preset-coupon removal restores the cart; the "View all" link
  opens /account/coupons in a new tab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The returns/localized-label/end-date tests each re-ran the identical
get_promotion_coupons(first=100) query, then asserted a different field.
Merge them into one test with three Allure steps — same assertions, one
round-trip instead of three. Pagination/sort and anonymous-denied stay
standalone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ized case

Both an unknown code and a found-but-expired code are rejected identically
at the UI ("This code is not valid"), so collapse the two near-duplicate
tests into one parametrized test over both codes. The semantic difference
is already covered at the API layer by test_validate_coupon_valid_and_invalid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…skips

The cart is a single-page checkout that auto-initializes shipment and
payment concurrently with every coupon mutation. Each background write
returns the whole cart and can briefly carry a not-yet-re-evaluated
coupon snapshot (isAppliedSuccessfully:false, pre-discount total). When
such a stale write lands in the SPA after addCoupon/removeCoupon, it
clobbers the applied state non-deterministically, and the clobbering
write is sometimes the last one in the cascade, so no wait converges.
This surfaced as a different coupon test failing on each full-file run
while each passed in isolation.

Rewrite the apply/switch/remove flows to await the coupon mutation's
own GraphQL response and then reload the cart, so assertions read the
authoritative, settled cart (promotion re-evaluation done, no concurrent
stale write). This is deterministic where a longer timeout was not
(verified 8/8 consecutive full-file runs green, zero skipped, zero
failed). Assertions are unchanged in strength: a coupon that never
applies still fails loudly.

Also remove the capability-probe skips that could mask real failures:
_require_coupon_ui and the preset-count/view-all guards in
test_cart_coupon.py, _require_account_coupons in test_account_coupons.py,
and the <2-coupons guard in test_promotion_coupons.py. On a properly
seeded store these tests now run and pass outright; missing UI/data
fails loudly instead of silently skipping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The prior fix covered only the coupon-mutation race and still flaked in
full-file runs — including test_cart_coupon_presets_render, which applies
no coupon. Root cause: the cart is a single-page checkout that, on a fresh
cart, auto-initializes shipment then payment (AddOrUpdateCartShipment ->
AddOrUpdateCartPayment, landing ~5s after the load event). That cascade
keeps re-rendering the coupon section for several seconds, so on faster
machines the read-only preset assertions read mid-re-render (transient
empty/detached nodes) and the coupon-state assertions read a stale,
concurrently-clobbered snapshot.

Route every cart open through _open_cart, which waits for network idle so
the whole shipment/payment cascade quiesces before any assertion runs
(measured: cascade done ~5s, idle ~9s — reads now happen strictly after
it, independent of machine speed). Route every coupon mutation through
_apply_and_settle, which awaits the mutation's own GraphQL response (so
the reopen cannot cancel the in-flight request) then reopens the settled
cart; on reopen the coupon is already persisted/re-evaluated and shipment/
payment do not re-fire, so nothing clobbers the applied state. Preset
label/name reads are now web-first (expect .to_have_text) so any residual
micro-re-render is retried rather than captured. No sleeps, no skips;
assertions unchanged in strength.

Verified 10/10 consecutive full-file runs, 8 passed / 0 skipped / 0 failed
each.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dead code)

- Narrow Optional discount_total/tax_total before .amount access (fixes 8
  pyright reportOptionalMemberAccess errors) via a _discount() helper.
- Rework promotionCoupons sort assertion to compare the name sort-key
  sequence instead of coupon-code order, so it stays correct under shared
  names; guard the asc!=desc check for single-coupon stores.
- Drop the Lucide icon-class applied-state assertion (applied state is
  already proven by applied_cards count + applied_code_input value).
- Remove dead code: unused SALE_PRODUCT_LIST/SALE_PRICE constants,
  AccountCouponsPage.find_card, unused cart subtotal/discount labels.
- Import Callable from collections.abc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant