@mohasinac/appkit 4.42.0 → 4.42.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +2 -30
- package/dist/_internal/server/features/offers/adapters.d.ts +29 -7
- package/dist/_internal/server/features/offers/adapters.js +14 -3
- package/dist/_internal/server/features/orders/adapters.js +54 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +1 -0
- package/dist/features/account/components/UserOrdersView.js +30 -2
- package/dist/features/account/hooks/useAddresses.d.ts +21 -6
- package/dist/features/account/hooks/useAddresses.js +1 -1
- package/dist/features/addresses/hooks/usePostalLookup.js +84 -12
- package/dist/features/admin/components/AdminCategoriesView.js +37 -2
- package/dist/features/admin/components/AdminOffersView.js +2 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +25 -0
- package/dist/features/admin/constants/filter-tabs.js +30 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +35 -0
- package/dist/features/admin/hooks/useAdminListingData.js +45 -9
- package/dist/features/cart/schemas/index.d.ts +15 -0
- package/dist/features/cart/schemas/index.js +2 -0
- package/dist/features/cart/types/index.d.ts +22 -0
- package/dist/features/categories/components/CategoriesIndexListing.js +19 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +14 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +8 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +1 -0
- package/dist/features/events/actions/event-actions.d.ts +13 -0
- package/dist/features/events/actions/event-actions.js +75 -1
- package/dist/features/events/repository/event-entry.repository.d.ts +17 -0
- package/dist/features/events/repository/event-entry.repository.js +36 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/layout/AppLayoutShell.js +2 -2
- package/dist/features/layout/BottomNavLayout.d.ts +9 -3
- package/dist/features/layout/BottomNavLayout.js +9 -3
- package/dist/features/media/upload/MediaUploadField.js +21 -3
- package/dist/features/orders/components/OrdersList.js +2 -1
- package/dist/features/orders/index.d.ts +1 -0
- package/dist/features/orders/index.js +1 -0
- package/dist/features/orders/types/index.d.ts +2 -0
- package/dist/features/orders/utils/order-ref.d.ts +45 -0
- package/dist/features/orders/utils/order-ref.js +58 -0
- package/dist/features/products/components/MakeOfferButton.d.ts +18 -1
- package/dist/features/products/components/MakeOfferButton.js +23 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +5 -0
- package/dist/features/products/components/ProductDetailPageView.js +3 -2
- package/dist/features/products/repository/products.repository.js +29 -2
- package/dist/features/seller/actions/offer-actions.js +32 -0
- package/dist/features/seller/components/QuickProductForm.js +15 -1
- package/dist/features/seller/components/SellerBidsView.js +3 -1
- package/dist/features/seller/components/SellerOffersView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +49 -6
- package/dist/features/seller/components/SellerProductsCards.js +23 -1
- package/dist/features/seller/components/SellerProductsView.js +27 -2
- package/dist/features/seller/hooks/useSellerListingData.d.ts +0 -1
- package/dist/features/seller/hooks/useSellerListingData.js +17 -19
- package/dist/features/shell/SectionForm.js +33 -2
- package/dist/features/tester/seed-data/_happy-path.d.ts +23 -0
- package/dist/features/tester/seed-data/_happy-path.js +710 -0
- package/dist/features/tester/seed-data/_money-flows.js +34 -23
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +22 -3
- package/dist/features/tester/seed-data/authored/addresses__address-filters.js +17 -12
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +7 -6
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +10 -4
- package/dist/features/tester/seed-data/authored/addresses__state-picker.js +13 -8
- package/dist/features/tester/seed-data/authored/addresses__unban-request.js +49 -11
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +10 -6
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +71 -32
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +80 -31
- package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.js +62 -25
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +61 -31
- package/dist/features/tester/seed-data/authored/admin__bundles.js +84 -37
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +90 -57
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +38 -26
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +47 -28
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +118 -31
- package/dist/features/tester/seed-data/authored/admin__content-deletes.js +30 -24
- package/dist/features/tester/seed-data/authored/admin__content-marketing.js +83 -46
- package/dist/features/tester/seed-data/authored/admin__coupons.js +8 -8
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +9 -9
- package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +19 -4
- package/dist/features/tester/seed-data/authored/admin__media-watermark.js +77 -33
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +5 -5
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +29 -5
- package/dist/features/tester/seed-data/authored/admin__site-system.js +45 -23
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +53 -28
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +20 -20
- package/dist/features/tester/seed-data/authored/buying__bidding.js +19 -2
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +20 -5
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +28 -10
- package/dist/features/tester/seed-data/authored/buying__cart.js +10 -3
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +0 -16
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +27 -5
- package/dist/features/tester/seed-data/authored/buying__offers.js +22 -7
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +3 -3
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +35 -4
- package/dist/features/tester/seed-data/authored/buying__return-request.js +19 -6
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +36 -18
- package/dist/features/tester/seed-data/authored/buying__reviews.js +16 -1
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +8 -6
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +16 -1
- package/dist/features/tester/seed-data/authored/community-support__public-profile.js +3 -2
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__blog.js +19 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +5 -5
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +41 -6
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +63 -8
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +29 -29
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +2 -2
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +23 -1
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +6 -6
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +1 -1
- package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +1 -1
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +31 -6
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +34 -10
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +35 -8
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +31 -9
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +29 -3
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +10 -10
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +3 -3
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +8 -8
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +2 -2
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +39 -16
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +43 -8
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +40 -14
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +31 -14
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +7 -6
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +14 -5
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +1 -1
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +4 -2
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +3 -3
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +45 -11
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -17
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +36 -5
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +29 -4
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +23 -0
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +14 -13
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +14 -9
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +34 -7
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +17 -7
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +9 -9
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +9 -4
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +25 -1
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +27 -6
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +19 -1
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +63 -22
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +9 -5
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +7 -6
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +3 -2
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +18 -18
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +13 -12
- package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +19 -3
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +22 -4
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +20 -8
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +10 -9
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +2 -2
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +5 -5
- package/dist/features/tester/seed-data/authored/seo__og-images.js +1 -1
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +7 -7
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +3 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +126 -24
- package/dist/features/tester/utils/phases.d.ts +17 -0
- package/dist/features/tester/utils/phases.js +29 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/seo/json-ld.d.ts +23 -0
- package/dist/seo/json-ld.js +3 -2
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: The core commerce path, end to end, for all four identities — the flows
|
|
3
|
+
* that must work for the site to be a marketplace at all. Browse a
|
|
4
|
+
* category, reach a brand, reach a store, open a standard product, buy it,
|
|
5
|
+
* pay for it, watch the seller fulfil it and the admin verify it.
|
|
6
|
+
*
|
|
7
|
+
* The catalogue had ~1,295 cases and no page that walked the whole path in
|
|
8
|
+
* order. Coverage was by SURFACE — a cart page, a checkout page, an orders
|
|
9
|
+
* page — so a break in the seam BETWEEN two surfaces had no owner, and the
|
|
10
|
+
* seams are where this codebase fails: an adapter that drops a field
|
|
11
|
+
* (#57), a settlement that writes an order nobody can pay for (#60), a
|
|
12
|
+
* price that is right in the cart and wrong at capture (#75).
|
|
13
|
+
*
|
|
14
|
+
* WHAT: Exports happyPathPages — consumed by group() in
|
|
15
|
+
* tester-checklist-seed-data.ts. Authored INLINE, six parts per case,
|
|
16
|
+
* against _money-flows.ts as the reference.
|
|
17
|
+
*
|
|
18
|
+
* 🛑 SCOPE IS DELIBERATELY NARROW: categories, brands, stores and STANDARD
|
|
19
|
+
* products only. No auctions, pre-orders, prize draws, bundles, classifieds,
|
|
20
|
+
* digital codes, live items or art/stickers — each has its own group, and
|
|
21
|
+
* widening this one would turn "does buying work" into "does everything
|
|
22
|
+
* work", which is what the other 1,295 cases are for.
|
|
23
|
+
*
|
|
24
|
+
* 🛑 MANUAL PAYMENT IS THE PAYMENT PATH TESTED HERE, and `cash` is what reaches
|
|
25
|
+
* it: CheckoutRouteClient redirects a cash order to ROUTES.USER.ORDER_PAYMENT,
|
|
26
|
+
* the proof-upload page. PhonePe replaced Razorpay in the online lane and
|
|
27
|
+
* needs a real gateway session, so it is not a happy-path case.
|
|
28
|
+
*
|
|
29
|
+
* @tag domain:tester
|
|
30
|
+
* @tag layer:seed
|
|
31
|
+
* @tag access:server-only
|
|
32
|
+
* @tag consumers:tester-checklist-seed-data.ts
|
|
33
|
+
* @tag sideEffects:none
|
|
34
|
+
*/
|
|
35
|
+
/* Reused verbatim so the wording cannot drift between cases. */
|
|
36
|
+
const SIGNIN_BUYER = "Sign in as rehan.sheikh@gmail.com / TempPass123!.";
|
|
37
|
+
const SIGNIN_SELLER = "Sign in as tyson@beybladearena.in / TempPass123!, who owns store-beyblade-arena.";
|
|
38
|
+
const SIGNIN_ADMIN = "Sign in as admin@letitrip.in / TempPass123!.";
|
|
39
|
+
export const happyPathPages = [
|
|
40
|
+
/* ── 1. Guest browse ───────────────────────────────────────────────────── */
|
|
41
|
+
{
|
|
42
|
+
pageKey: "guest-browse",
|
|
43
|
+
pageLabel: "Guest: browse the catalogue",
|
|
44
|
+
href: "/",
|
|
45
|
+
cases: [
|
|
46
|
+
{
|
|
47
|
+
key: "home-renders-with-products",
|
|
48
|
+
label: "The homepage renders real listings, not an empty shell",
|
|
49
|
+
roles: ["guest"],
|
|
50
|
+
startPage: "/",
|
|
51
|
+
steps: [
|
|
52
|
+
"Open / signed out.",
|
|
53
|
+
"Wait for the page to finish hydrating.",
|
|
54
|
+
"Read the section headings and count the product cards under the first listing section.",
|
|
55
|
+
],
|
|
56
|
+
expectedBehaviour: "The homepage renders its configured sections server-side and each listing section is populated from the live catalogue.",
|
|
57
|
+
expectedUiState: "Exactly one h1 is present. At least one section shows product cards with a title and an image. No section shows 'Nothing listed here yet' where the catalogue has stock.",
|
|
58
|
+
endResult: "Nothing is written; reloading renders the same sections.",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: "categories-index-lists-roots",
|
|
62
|
+
label: "The categories index shows the real root categories",
|
|
63
|
+
roles: ["guest"],
|
|
64
|
+
startPage: "/categories",
|
|
65
|
+
steps: [
|
|
66
|
+
"Open /categories signed out.",
|
|
67
|
+
"Wait for the grid to render.",
|
|
68
|
+
"Read every category name shown at the top level.",
|
|
69
|
+
],
|
|
70
|
+
expectedBehaviour: "The index lists the tier-0 roots of the category forest, which are Spinning Tops and Living Collectibles.",
|
|
71
|
+
expectedUiState: "Both 'Spinning Tops' and 'Living Collectibles' are visible as cards or links. The page is not empty and does not render only leaf categories.",
|
|
72
|
+
expectedData: { rootCategoriesVisible: 2 },
|
|
73
|
+
endResult: "Nothing is written; reloading shows the same roots.",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: "category-detail-lists-products",
|
|
77
|
+
label: "A category page lists the products filed under it",
|
|
78
|
+
roles: ["guest"],
|
|
79
|
+
startPage: "/categories/category-beyblade-x",
|
|
80
|
+
steps: [
|
|
81
|
+
"Open /categories/category-beyblade-x signed out.",
|
|
82
|
+
"Wait for the product grid to render.",
|
|
83
|
+
"Count the product cards and read the first card's title.",
|
|
84
|
+
],
|
|
85
|
+
expectedBehaviour: "Products are matched by array-contains-any against their own categorySlugs chain, so a product filed at this node appears without descendant expansion.",
|
|
86
|
+
expectedUiState: "At least one product card is rendered. 'Beyblade X BX-01 Wizard Arrow' is among the titles. The grid does not read 'Nothing listed here yet'.",
|
|
87
|
+
endResult: "Nothing is written; reloading shows the same products.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
key: "brand-detail-lists-products",
|
|
91
|
+
label: "A brand page lists that brand's products",
|
|
92
|
+
roles: ["guest"],
|
|
93
|
+
startPage: "/brands/brand-beyblade",
|
|
94
|
+
steps: [
|
|
95
|
+
"Open /brands/brand-beyblade signed out.",
|
|
96
|
+
"Wait for the grid to render.",
|
|
97
|
+
"Count the product cards.",
|
|
98
|
+
],
|
|
99
|
+
expectedBehaviour: "Products are matched on the brand DISPLAY NAME, not the slug, so the brand row's name and every product's brand string must agree exactly.",
|
|
100
|
+
expectedUiState: "At least one product card is rendered, and an 'About this brand' panel is present. The grid does not read 'Nothing listed here yet'.",
|
|
101
|
+
endResult: "Nothing is written; reloading shows the same products.",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "store-detail-lists-products",
|
|
105
|
+
label: "A store page lists that store's listings",
|
|
106
|
+
roles: ["guest"],
|
|
107
|
+
startPage: "/stores/store-beyblade-arena",
|
|
108
|
+
steps: [
|
|
109
|
+
"Open /stores/store-beyblade-arena signed out.",
|
|
110
|
+
"Wait for the page to render.",
|
|
111
|
+
"Read the store name in the header and count the listing cards.",
|
|
112
|
+
],
|
|
113
|
+
expectedBehaviour: "The store detail page loads the store by slug and lists its published, available listings.",
|
|
114
|
+
expectedUiState: "The header reads 'Beyblade Arena'. At least one listing card is rendered.",
|
|
115
|
+
endResult: "Nothing is written; reloading shows the same listings.",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
key: "product-detail-renders",
|
|
119
|
+
label: "A standard product page renders its title, images and add-to-cart",
|
|
120
|
+
roles: ["guest"],
|
|
121
|
+
startPage: "/products/product-beyblade-x-wizard-arrow",
|
|
122
|
+
steps: [
|
|
123
|
+
"Open /products/product-beyblade-x-wizard-arrow signed out.",
|
|
124
|
+
"Wait for the gallery to render.",
|
|
125
|
+
"Read the title, the seller name, and the label on the primary call to action.",
|
|
126
|
+
],
|
|
127
|
+
expectedBehaviour: "The product detail page renders the standard-listing layout with a cart affordance, since standard products have canAddToCart.",
|
|
128
|
+
expectedUiState: "The title reads 'Beyblade X BX-01 Wizard Arrow'. At least one gallery image is shown. A cart call to action is present and is not disabled.",
|
|
129
|
+
endResult: "Nothing is written; reloading renders the same page.",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
key: "guest-sees-no-price",
|
|
133
|
+
label: "🛑 A signed-out visitor is shown a sign-in prompt where the price would be, and never a flash of the real amount",
|
|
134
|
+
roles: ["guest"],
|
|
135
|
+
startPage: "/products/product-beyblade-x-wizard-arrow",
|
|
136
|
+
steps: [
|
|
137
|
+
"Open /products/product-beyblade-x-wizard-arrow signed out.",
|
|
138
|
+
"Read the price row immediately, before hydration completes.",
|
|
139
|
+
"Wait three seconds and read the price row again.",
|
|
140
|
+
"Reload the page twice more, reading the price row each time.",
|
|
141
|
+
],
|
|
142
|
+
expectedBehaviour: "Prices are gated from signed-out visitors. The gate has three states, and the unresolved state renders a neutral placeholder rather than either the amount or the prompt.",
|
|
143
|
+
expectedUiState: "The price row reads 'Sign in to see price' and links to /auth/login. The rupee amount 899 never appears in the price row at any point, on any of the three loads.",
|
|
144
|
+
expectedData: { rupeeAmountVisibleToGuest: false },
|
|
145
|
+
endResult: "Nothing is written; every reload behaves the same way.",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
key: "search-filters-with-nonsense-control",
|
|
149
|
+
label: "Catalogue search narrows results, proven against a nonsense term",
|
|
150
|
+
roles: ["guest"],
|
|
151
|
+
startPage: "/products",
|
|
152
|
+
steps: [
|
|
153
|
+
"Open /products signed out.",
|
|
154
|
+
"Type dranzer into the catalogue search box and submit.",
|
|
155
|
+
"Count the product cards rendered.",
|
|
156
|
+
"Clear the box, type zzzznope and submit.",
|
|
157
|
+
"Count the product cards rendered.",
|
|
158
|
+
],
|
|
159
|
+
inputs: { realTerm: "dranzer", nonsenseTerm: "zzzznope" },
|
|
160
|
+
expectedBehaviour: "The search term reaches the query rather than being dropped, so the two terms return different result sets.",
|
|
161
|
+
expectedUiState: "'dranzer' renders at least one card and every title shown relates to Dranzer. 'zzzznope' renders zero cards and an empty state. The two counts differ — identical counts mean the term is being ignored, which is a FAIL however plausible the rows look.",
|
|
162
|
+
expectedData: { nonsenseResultCount: 0 },
|
|
163
|
+
endResult: "Nothing is written; the URL carries the term and reloading repeats the result.",
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
/* ── 2. Buyer purchase, manual payment ─────────────────────────────────── */
|
|
168
|
+
{
|
|
169
|
+
pageKey: "buyer-purchase",
|
|
170
|
+
pageLabel: "Buyer: cart to paid order (manual payment)",
|
|
171
|
+
href: "/products/product-beyblade-x-wizard-arrow",
|
|
172
|
+
cases: [
|
|
173
|
+
{
|
|
174
|
+
key: "add-to-cart",
|
|
175
|
+
label: "Adding a standard product to the cart puts it in the cart",
|
|
176
|
+
roles: ["buyer"],
|
|
177
|
+
startPage: "/products/product-beyblade-x-wizard-arrow",
|
|
178
|
+
steps: [
|
|
179
|
+
SIGNIN_BUYER,
|
|
180
|
+
"Open /products/product-beyblade-x-wizard-arrow.",
|
|
181
|
+
"Click the add-to-cart call to action.",
|
|
182
|
+
"Open /cart.",
|
|
183
|
+
],
|
|
184
|
+
expectedBehaviour: "A cart line is created for this product at quantity 1, priced from the product document rather than from anything the client sent.",
|
|
185
|
+
expectedUiState: "The cart lists one line reading 'Beyblade X BX-01 Wizard Arrow' at ₹899, quantity 1, under the seller heading 'Beyblade Arena'. The cart does not read 'Your cart is empty'.",
|
|
186
|
+
expectedData: { cartLineCount: 1, lineUnitPrice: 899 },
|
|
187
|
+
endResult: "After reloading /cart the line is still present at ₹899, quantity 1.",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
key: "cart-line-shows-no-raw-field-names",
|
|
191
|
+
label: "🛑 The cart line shows no raw field name to the buyer",
|
|
192
|
+
roles: ["buyer"],
|
|
193
|
+
startPage: "/cart",
|
|
194
|
+
steps: [
|
|
195
|
+
SIGNIN_BUYER,
|
|
196
|
+
"Open /cart holding the single Wizard Arrow line.",
|
|
197
|
+
"Read every line of text inside the cart line, between the product title and the price.",
|
|
198
|
+
],
|
|
199
|
+
expectedBehaviour: "The line renders the seller once, as the group heading. CartItemRow prints each meta.attributes entry verbatim as `key: value`, so any plumbing field placed in that bag is published to the buyer with its own field name.",
|
|
200
|
+
expectedUiState: "No text of the form `<fieldName>: <value>` appears anywhere in the line. In particular the string 'storeName:' does NOT appear. The seller is shown once, as 'SOLD BY BEYBLADE ARENA' above the line.",
|
|
201
|
+
expectedData: { rawFieldLabelsVisible: 0 },
|
|
202
|
+
endResult: "Nothing is written; the same line reads the same way after a reload.",
|
|
203
|
+
href: "/cart",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
key: "cart-quantity-updates-total",
|
|
207
|
+
label: "Changing the quantity updates the line and the cart total",
|
|
208
|
+
roles: ["buyer"],
|
|
209
|
+
startPage: "/cart",
|
|
210
|
+
steps: [
|
|
211
|
+
SIGNIN_BUYER,
|
|
212
|
+
"Open /cart holding the single Wizard Arrow line at quantity 1.",
|
|
213
|
+
"Click the quantity stepper's increment control once.",
|
|
214
|
+
"Wait for the total to settle.",
|
|
215
|
+
"Reload /cart.",
|
|
216
|
+
],
|
|
217
|
+
expectedBehaviour: "The quantity is written to the cart document and the subtotal is recomputed server-side from the stored quantity, not from a client-held figure.",
|
|
218
|
+
expectedUiState: "The line quantity reads 2 and the line total reads ₹1,798. The cart subtotal reflects the same figure.",
|
|
219
|
+
expectedData: { lineQuantity: 2, lineTotal: 1798 },
|
|
220
|
+
endResult: "After the reload the quantity is still 2 — not reset to 1.",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
key: "checkout-address-step",
|
|
224
|
+
label: "Checkout opens on the address step with the buyer's saved address selectable",
|
|
225
|
+
roles: ["buyer"],
|
|
226
|
+
startPage: "/checkout",
|
|
227
|
+
steps: [
|
|
228
|
+
SIGNIN_BUYER,
|
|
229
|
+
"Open /cart and click the checkout call to action.",
|
|
230
|
+
"Read the step indicator and the address options offered.",
|
|
231
|
+
],
|
|
232
|
+
expectedBehaviour: "Checkout is a three-step flow — address, then extras and fees, then payment — and opens on the address step with the signed-in buyer's saved addresses loaded.",
|
|
233
|
+
expectedUiState: "The step indicator shows the address step as active. The saved address 'Home' at 123 Stadium Lane, Indore is listed and selectable. A continue control is present.",
|
|
234
|
+
endResult: "Nothing is written by opening the step; reloading returns to the address step.",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
key: "checkout-extras-step",
|
|
238
|
+
label: "Continuing from the address reaches the extras-and-fees step with per-seller add-ons",
|
|
239
|
+
roles: ["buyer"],
|
|
240
|
+
startPage: "/checkout",
|
|
241
|
+
steps: [
|
|
242
|
+
SIGNIN_BUYER,
|
|
243
|
+
"Open /checkout with the Wizard Arrow line in the cart.",
|
|
244
|
+
"Select the saved 'Home' address.",
|
|
245
|
+
"Click the continue control.",
|
|
246
|
+
"Read the headings and controls on the step that appears.",
|
|
247
|
+
],
|
|
248
|
+
expectedBehaviour: "The extras step lists one card per seller in the cart, sourced from the cart itself rather than from the pricing preview, so the controls exist before any network round trip completes.",
|
|
249
|
+
expectedUiState: "A section for 'Beyblade Arena' is shown with its add-on choices and its own fee lines. An order summary shows a subtotal. The step is not blank while the preview loads.",
|
|
250
|
+
endResult: "Nothing is ordered by advancing; reloading /checkout returns to the address step.",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
key: "checkout-offers-cash-payment",
|
|
254
|
+
label: "The payment step offers the manual cash option",
|
|
255
|
+
roles: ["buyer"],
|
|
256
|
+
startPage: "/checkout",
|
|
257
|
+
steps: [
|
|
258
|
+
SIGNIN_BUYER,
|
|
259
|
+
"Open /checkout with the Wizard Arrow line in the cart.",
|
|
260
|
+
"Select the saved 'Home' address and continue.",
|
|
261
|
+
"Continue again from the extras step.",
|
|
262
|
+
"Read every payment option offered and the total shown.",
|
|
263
|
+
],
|
|
264
|
+
expectedBehaviour: "The payment step renders the options the site settings enable. The manual cash lane is offered by default.",
|
|
265
|
+
expectedUiState: "A cash or manual-payment option is present and selectable, with guidance describing paying outside the app and uploading proof. A total is displayed as a rupee amount rather than a blank or ₹0.00.",
|
|
266
|
+
endResult: "No order exists yet; reloading /checkout returns to the address step.",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
key: "place-cash-order-redirects-to-proof-upload",
|
|
270
|
+
label: "🛑 Placing a cash order creates the order AND lands the buyer on the proof-upload page",
|
|
271
|
+
roles: ["buyer"],
|
|
272
|
+
startPage: "/checkout",
|
|
273
|
+
steps: [
|
|
274
|
+
SIGNIN_BUYER,
|
|
275
|
+
"Open /checkout with the Wizard Arrow line in the cart.",
|
|
276
|
+
"Select the saved 'Home' address and continue.",
|
|
277
|
+
"Continue again from the extras step.",
|
|
278
|
+
"Select the cash payment option.",
|
|
279
|
+
"Click the place-order control.",
|
|
280
|
+
"Wait for the navigation to settle and read the URL.",
|
|
281
|
+
],
|
|
282
|
+
expectedBehaviour: "An order is created with paymentMethod cash and paymentStatus pending, the cart line is consumed, and the buyer is routed to the manual-payment upload page for that order.",
|
|
283
|
+
expectedUiState: "The URL is /user/orders/{orderId}/payment. The page shows an upload control for a payment screenshot and a countdown to the payment deadline. It does NOT read 'This order does not require manual payment upload.' — that message means paymentMethod never reached the client and the whole manual lane is dead.",
|
|
284
|
+
expectedData: { landsOnPaymentPage: true },
|
|
285
|
+
endResult: "After reloading /user/orders the new order is listed, and reopening its payment page still offers the upload rather than the not-required message.",
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
key: "order-row-names-the-product",
|
|
289
|
+
label: "The new order is listed by its product name, not a bare order id",
|
|
290
|
+
roles: ["buyer"],
|
|
291
|
+
startPage: "/user/orders",
|
|
292
|
+
steps: [
|
|
293
|
+
SIGNIN_BUYER,
|
|
294
|
+
"Open /user/orders.",
|
|
295
|
+
"Read the primary label and thumbnail of the newest row.",
|
|
296
|
+
],
|
|
297
|
+
expectedBehaviour: "Order rows render from the items array denormalised onto the order document, so no extra fetch is needed to name what was bought.",
|
|
298
|
+
expectedUiState: "The newest row's primary label reads 'Beyblade X BX-01 Wizard Arrow' and shows its thumbnail. The order id is present but de-emphasised. The row does not read only 'Order order-1-…'.",
|
|
299
|
+
endResult: "After a reload the row still names the product.",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
key: "order-detail-shows-payment-state",
|
|
303
|
+
label: "The order detail page shows the manual-payment panel and the real status",
|
|
304
|
+
roles: ["buyer"],
|
|
305
|
+
startPage: "/user/orders",
|
|
306
|
+
steps: [
|
|
307
|
+
SIGNIN_BUYER,
|
|
308
|
+
"Open /user/orders.",
|
|
309
|
+
"Open the newest order's detail page.",
|
|
310
|
+
"Read the status, the payment method and the panel offered.",
|
|
311
|
+
],
|
|
312
|
+
expectedBehaviour: "The order adapter carries paymentMethod, paymentStatus and the payment deadline through to the client, so the manual-payment panel can decide to render.",
|
|
313
|
+
expectedUiState: "The page names the product, shows a status of Pending, identifies the payment method as cash or manual, and offers a control to complete or re-upload the payment proof.",
|
|
314
|
+
endResult: "After a reload the same panel and status are shown.",
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
/* ── 3. Buyer addresses ────────────────────────────────────────────────── */
|
|
319
|
+
{
|
|
320
|
+
pageKey: "buyer-addresses",
|
|
321
|
+
pageLabel: "Buyer: addresses",
|
|
322
|
+
href: "/user/addresses",
|
|
323
|
+
cases: [
|
|
324
|
+
{
|
|
325
|
+
key: "create-address",
|
|
326
|
+
label: "A new address can be created and appears in the list",
|
|
327
|
+
roles: ["buyer"],
|
|
328
|
+
startPage: "/user/addresses",
|
|
329
|
+
steps: [
|
|
330
|
+
SIGNIN_BUYER,
|
|
331
|
+
"Open /user/addresses.",
|
|
332
|
+
"Click the control that adds a new address.",
|
|
333
|
+
"Enter the label QA Addr create.",
|
|
334
|
+
"Enter the full name QA Buyer.",
|
|
335
|
+
"Enter the phone 9876500011.",
|
|
336
|
+
"Enter the address line 12 Test Street.",
|
|
337
|
+
"Enter the postal code 452001.",
|
|
338
|
+
"Enter the city Indore.",
|
|
339
|
+
"Select the state Madhya Pradesh.",
|
|
340
|
+
"Click Save.",
|
|
341
|
+
"Reload /user/addresses.",
|
|
342
|
+
],
|
|
343
|
+
inputs: {
|
|
344
|
+
label: "QA Addr create",
|
|
345
|
+
fullName: "QA Buyer",
|
|
346
|
+
phone: "9876500011",
|
|
347
|
+
line1: "12 Test Street",
|
|
348
|
+
postalCode: "452001",
|
|
349
|
+
city: "Indore",
|
|
350
|
+
state: "Madhya Pradesh",
|
|
351
|
+
},
|
|
352
|
+
expectedBehaviour: "An address row is written against the signed-in buyer with ownerType user, and its PII fields are encrypted at rest.",
|
|
353
|
+
expectedUiState: "The drawer or form closes and a row labelled 'QA Addr create' is listed showing 12 Test Street, Indore, 452001.",
|
|
354
|
+
endResult: "After the reload the 'QA Addr create' row is still listed with the same street and postal code.",
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
key: "postal-code-fills-city-state",
|
|
358
|
+
label: "Entering a postal code fills the city and state",
|
|
359
|
+
roles: ["buyer"],
|
|
360
|
+
startPage: "/user/addresses",
|
|
361
|
+
steps: [
|
|
362
|
+
SIGNIN_BUYER,
|
|
363
|
+
"Open /user/addresses.",
|
|
364
|
+
"Click the control that adds a new address.",
|
|
365
|
+
"Enter the postal code 560001.",
|
|
366
|
+
"Move focus out of the postal-code field and wait for the lookup to settle.",
|
|
367
|
+
"Read the city and state fields.",
|
|
368
|
+
],
|
|
369
|
+
inputs: { postalCode: "560001" },
|
|
370
|
+
expectedBehaviour: "A postal-code lookup resolves the city and state and populates both fields without the buyer typing them.",
|
|
371
|
+
expectedUiState: "The city field reads Bangalore and the state field reads Karnataka. Neither is left blank, and neither reads a street or post-office name such as 'Rajbhavan' — that is the defect this case exists to catch.",
|
|
372
|
+
expectedData: { city: "Bangalore", state: "Karnataka" },
|
|
373
|
+
endResult: "Nothing is saved unless the form is submitted; leaving the page discards it.",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
key: "edit-address-persists",
|
|
377
|
+
label: "🛑 Editing an address saves — checked after a reload, not from the toast",
|
|
378
|
+
roles: ["buyer"],
|
|
379
|
+
startPage: "/user/addresses",
|
|
380
|
+
steps: [
|
|
381
|
+
SIGNIN_BUYER,
|
|
382
|
+
"Open /user/addresses.",
|
|
383
|
+
"Open the edit control on the 'QA Addr create' row.",
|
|
384
|
+
"Change the address line to 99 Revised Road.",
|
|
385
|
+
"Click Save.",
|
|
386
|
+
"Reload /user/addresses.",
|
|
387
|
+
"Read the 'QA Addr create' row.",
|
|
388
|
+
],
|
|
389
|
+
inputs: { newLine1: "99 Revised Road" },
|
|
390
|
+
expectedBehaviour: "The changed field is persisted. A success response that writes nothing is the most common defect shape in this codebase, so the reload is the only oracle that settles it.",
|
|
391
|
+
expectedUiState: "Immediately after saving the row reads 99 Revised Road. After the reload it STILL reads 99 Revised Road, not 12 Test Street.",
|
|
392
|
+
expectedData: { line1AfterReload: "99 Revised Road" },
|
|
393
|
+
endResult: "The row reads 99 Revised Road after any number of reloads.",
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
key: "set-default-address",
|
|
397
|
+
label: "Setting an address as default marks exactly one default",
|
|
398
|
+
roles: ["buyer"],
|
|
399
|
+
startPage: "/user/addresses",
|
|
400
|
+
steps: [
|
|
401
|
+
SIGNIN_BUYER,
|
|
402
|
+
"Open /user/addresses.",
|
|
403
|
+
"Click the set-as-default control on the 'QA Addr create' row.",
|
|
404
|
+
"Reload /user/addresses.",
|
|
405
|
+
"Read which rows carry a default marker.",
|
|
406
|
+
],
|
|
407
|
+
expectedBehaviour: "The chosen address becomes the default and the previously default address is cleared, so the flag stays singular.",
|
|
408
|
+
expectedUiState: "'QA Addr create' shows a Default badge. Exactly one row carries that badge — not two.",
|
|
409
|
+
expectedData: { defaultCount: 1 },
|
|
410
|
+
endResult: "After the reload 'QA Addr create' is still the only row marked default.",
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
key: "delete-address",
|
|
414
|
+
label: "Deleting an address removes it, and the deletion survives a reload",
|
|
415
|
+
roles: ["buyer"],
|
|
416
|
+
startPage: "/user/addresses",
|
|
417
|
+
steps: [
|
|
418
|
+
SIGNIN_BUYER,
|
|
419
|
+
"Open /user/addresses.",
|
|
420
|
+
"Open the delete control on the 'QA Addr create' row.",
|
|
421
|
+
"Confirm in the dialog.",
|
|
422
|
+
"Reload /user/addresses.",
|
|
423
|
+
],
|
|
424
|
+
expectedBehaviour: "The address document is deleted. A destructive action must ask before acting, so a delete that happens with no dialog is a FAIL even when the deletion itself is correct.",
|
|
425
|
+
expectedUiState: "A confirmation dialog appears before anything is removed. After confirming, the 'QA Addr create' row is gone from the list.",
|
|
426
|
+
endResult: "After the reload the row is still absent, and the seeded 'Home' address is untouched.",
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
/* ── 4. Seller: list a product ─────────────────────────────────────────── */
|
|
431
|
+
{
|
|
432
|
+
pageKey: "seller-listing",
|
|
433
|
+
pageLabel: "Seller: list a standard product",
|
|
434
|
+
href: "/store/products",
|
|
435
|
+
cases: [
|
|
436
|
+
{
|
|
437
|
+
key: "create-standard-listing",
|
|
438
|
+
label: "A seller can create and publish a standard listing",
|
|
439
|
+
roles: ["seller"],
|
|
440
|
+
startPage: "/store/products/new",
|
|
441
|
+
steps: [
|
|
442
|
+
SIGNIN_SELLER,
|
|
443
|
+
"Open /store/products/new.",
|
|
444
|
+
"Enter the title QA Listing seller-listing.",
|
|
445
|
+
"Enter the description A standard listing created by the QA happy-path run.",
|
|
446
|
+
"Enter the price 1250.",
|
|
447
|
+
"Enter the stock quantity 5.",
|
|
448
|
+
"Select the category Beyblade X Tops. Pick the LEAF, not the generation: the point of this case is that the repository derives the ancestor chain, and category-x-tops sits under category-beyblade-x, which is the page the next case checks. NOTE the picker's search box does not filter — scroll or use Load more to find it.",
|
|
449
|
+
"Upload a product image. It is a REQUIRED field and Publish is refused with 'Product image is required' without one; the crop dialog that opens must be confirmed with Save Crop.",
|
|
450
|
+
"Brand and condition are NOT on the quick-add form — they live behind 'Show all fields (advanced)'. Leave them at their defaults for this case.",
|
|
451
|
+
"Click the publish control.",
|
|
452
|
+
"Wait for the navigation to settle.",
|
|
453
|
+
],
|
|
454
|
+
inputs: {
|
|
455
|
+
title: "QA Listing seller-listing",
|
|
456
|
+
price: 1250,
|
|
457
|
+
stockQuantity: 5,
|
|
458
|
+
categoryName: "Beyblade X",
|
|
459
|
+
brandName: "Beyblade",
|
|
460
|
+
condition: "New",
|
|
461
|
+
},
|
|
462
|
+
expectedBehaviour: "A product is written against store-beyblade-arena with listingType standard and status published. The repository derives the full ancestor chain onto categorySlugs from the chosen leaf, which is what makes the listing reachable from its category later.",
|
|
463
|
+
expectedUiState: "The form leaves the create screen and lands on /store/products, where a row reads 'QA Listing seller-listing' with 'standard' and 'new · published'. Once the image is attached no validation error is displayed.",
|
|
464
|
+
endResult: "After reloading /store/products the listing is present with status Published.",
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
key: "listing-appears-in-seller-list",
|
|
468
|
+
label: "The new listing appears in the seller's own products list",
|
|
469
|
+
roles: ["seller"],
|
|
470
|
+
startPage: "/store/products",
|
|
471
|
+
steps: [
|
|
472
|
+
SIGNIN_SELLER,
|
|
473
|
+
"Open /store/products.",
|
|
474
|
+
"Search the list for QA Listing seller-listing.",
|
|
475
|
+
"Read its row — title, price, status and stock.",
|
|
476
|
+
],
|
|
477
|
+
expectedBehaviour: "The seller's product list is scoped to their own store and includes the newly created listing.",
|
|
478
|
+
expectedUiState: "A row reads 'QA Listing seller-listing', ₹1,250, status Published, stock 5.",
|
|
479
|
+
expectedData: { price: 1250, stockQuantity: 5 },
|
|
480
|
+
endResult: "After a reload the row is unchanged.",
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
key: "listing-is-publicly-reachable",
|
|
484
|
+
label: "🛑 The new listing is reachable from its own category by a signed-out visitor",
|
|
485
|
+
roles: ["guest", "seller"],
|
|
486
|
+
startPage: "/categories/category-beyblade-x",
|
|
487
|
+
steps: [
|
|
488
|
+
"Open /categories/category-beyblade-x signed out.",
|
|
489
|
+
"Wait for the product grid to render.",
|
|
490
|
+
"Look for a card titled QA Listing seller-listing.",
|
|
491
|
+
],
|
|
492
|
+
expectedBehaviour: "A UI-created listing carries its full ancestor chain on categorySlugs, so it is matched by its own leaf category as well as by every ancestor. A listing created through the form that is invisible on its own category page means the chain was stripped on write and is a FAIL.",
|
|
493
|
+
expectedUiState: "A card titled 'QA Listing seller-listing' is present in the grid. Its price row reads the guest sign-in prompt rather than ₹1,250.",
|
|
494
|
+
endResult: "The card is still present after a reload.",
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
key: "edit-listing-persists",
|
|
498
|
+
label: "Editing the listing's price saves — checked after a reload",
|
|
499
|
+
roles: ["seller"],
|
|
500
|
+
startPage: "/store/products",
|
|
501
|
+
steps: [
|
|
502
|
+
SIGNIN_SELLER,
|
|
503
|
+
"Open /store/products.",
|
|
504
|
+
"Open the edit screen for QA Listing seller-listing.",
|
|
505
|
+
"Change the price to 1350.",
|
|
506
|
+
"Click Save.",
|
|
507
|
+
"Reload the edit screen.",
|
|
508
|
+
"Read the price field.",
|
|
509
|
+
],
|
|
510
|
+
inputs: { newPrice: 1350 },
|
|
511
|
+
expectedBehaviour: "The price is persisted on the product document. The editor must be seeded from the unwrapped product rather than from an action envelope, or every field reads blank and saving writes defaults over the live listing.",
|
|
512
|
+
expectedUiState: "The form is populated with the listing's real values when opened — title, price and category are filled, not blank. After saving and reloading, the price field reads 1350.",
|
|
513
|
+
expectedData: { priceAfterReload: 1350 },
|
|
514
|
+
endResult: "The listing is still ₹1,350 and still Published — a save must not silently flip status to draft.",
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
key: "delete-listing",
|
|
518
|
+
label: "The seller can delete the listing they created",
|
|
519
|
+
roles: ["seller"],
|
|
520
|
+
startPage: "/store/products",
|
|
521
|
+
steps: [
|
|
522
|
+
SIGNIN_SELLER,
|
|
523
|
+
"Open /store/products.",
|
|
524
|
+
"Open the row actions for QA Listing seller-listing.",
|
|
525
|
+
"Click the delete action.",
|
|
526
|
+
"Confirm in the dialog.",
|
|
527
|
+
"Reload /store/products.",
|
|
528
|
+
],
|
|
529
|
+
expectedBehaviour: "The listing is removed or archived so it no longer appears in the seller's active list.",
|
|
530
|
+
expectedUiState: "A confirmation dialog appears before anything is deleted. After confirming, the row is gone.",
|
|
531
|
+
endResult: "After the reload the row is still absent, and the seeded listings are untouched.",
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
},
|
|
535
|
+
/* ── 5. Seller: fulfil an order ────────────────────────────────────────── */
|
|
536
|
+
{
|
|
537
|
+
pageKey: "seller-fulfil",
|
|
538
|
+
pageLabel: "Seller: fulfil an order",
|
|
539
|
+
href: "/store/orders",
|
|
540
|
+
cases: [
|
|
541
|
+
{
|
|
542
|
+
key: "order-appears-for-seller",
|
|
543
|
+
label: "The buyer's order appears in the owning seller's orders list",
|
|
544
|
+
roles: ["seller"],
|
|
545
|
+
startPage: "/store/orders",
|
|
546
|
+
steps: [
|
|
547
|
+
SIGNIN_SELLER,
|
|
548
|
+
"Open /store/orders.",
|
|
549
|
+
"Read the newest row's product name, buyer and status.",
|
|
550
|
+
],
|
|
551
|
+
expectedBehaviour: "Orders are split per store at creation, so a seller sees only orders containing their own listings.",
|
|
552
|
+
expectedUiState: "The newest row names the purchased product rather than only an order id, and shows a status of Pending.",
|
|
553
|
+
endResult: "After a reload the row is still listed.",
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
key: "seller-order-detail-opens",
|
|
557
|
+
label: "🛑 The seller can OPEN the order, not only act on it from the row",
|
|
558
|
+
roles: ["seller"],
|
|
559
|
+
startPage: "/store/orders",
|
|
560
|
+
steps: [
|
|
561
|
+
SIGNIN_SELLER,
|
|
562
|
+
"Open /store/orders.",
|
|
563
|
+
"Open the newest order's detail view.",
|
|
564
|
+
"Read the items, the quantities and the shipping destination.",
|
|
565
|
+
],
|
|
566
|
+
expectedBehaviour: "A listing row has a detail affordance. A menu of pure mutations lets a seller act on a record they were never able to read, which is not the same as the record being reachable.",
|
|
567
|
+
expectedUiState: "A detail view opens showing the ordered item, its quantity, the order total and the delivery city. It is not a blank shell.",
|
|
568
|
+
endResult: "Nothing is written by viewing; reloading shows the same detail.",
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
key: "seller-sees-no-payment-screenshot",
|
|
572
|
+
label: "The seller sees the payment status but NOT the buyer's payment screenshot",
|
|
573
|
+
roles: ["seller"],
|
|
574
|
+
startPage: "/store/orders",
|
|
575
|
+
steps: [
|
|
576
|
+
SIGNIN_SELLER,
|
|
577
|
+
"Open /store/orders.",
|
|
578
|
+
"Open the newest order's detail view.",
|
|
579
|
+
"Look for the payment section and read what it exposes.",
|
|
580
|
+
],
|
|
581
|
+
expectedBehaviour: "Manual-payment review is an admin and moderator responsibility. The screenshot is a bank or UPI capture, so the seller gets the status and the reference only.",
|
|
582
|
+
expectedUiState: "A payment status is shown. No payment screenshot image is rendered, and no verify or reject control is offered to the seller.",
|
|
583
|
+
expectedData: { screenshotVisibleToSeller: false },
|
|
584
|
+
endResult: "Nothing is written by viewing.",
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
key: "mark-shipped-with-tracking",
|
|
588
|
+
label: "Marking an order shipped with a carrier and tracking number persists",
|
|
589
|
+
roles: ["seller"],
|
|
590
|
+
startPage: "/store/orders",
|
|
591
|
+
steps: [
|
|
592
|
+
SIGNIN_SELLER,
|
|
593
|
+
"Open /store/orders.",
|
|
594
|
+
"Open the newest order and start the ship action.",
|
|
595
|
+
"Enter the carrier QA Carrier.",
|
|
596
|
+
"Enter the tracking number QA123456789.",
|
|
597
|
+
"Click the confirm control.",
|
|
598
|
+
"Reload /store/orders.",
|
|
599
|
+
],
|
|
600
|
+
inputs: { carrier: "QA Carrier", trackingNumber: "QA123456789" },
|
|
601
|
+
expectedBehaviour: "The order status moves to Shipped and the carrier and tracking number are written to the order document. The buyer receives an order-shipped notification.",
|
|
602
|
+
expectedUiState: "The row's status badge reads Shipped.",
|
|
603
|
+
expectedData: { trackingNumber: "QA123456789" },
|
|
604
|
+
endResult: "After the reload the status still reads Shipped and the tracking number is still QA123456789.",
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
key: "buyer-sees-shipped-status",
|
|
608
|
+
label: "The buyer's own order page reflects the shipped status and tracking",
|
|
609
|
+
roles: ["buyer"],
|
|
610
|
+
startPage: "/user/orders",
|
|
611
|
+
steps: [
|
|
612
|
+
SIGNIN_BUYER,
|
|
613
|
+
"Open /user/orders.",
|
|
614
|
+
"Open the order the seller has just shipped.",
|
|
615
|
+
"Read the status and look for the tracking number.",
|
|
616
|
+
],
|
|
617
|
+
expectedBehaviour: "The seller's status change is visible to the buyer on the same order, with the tracking details carried through the order adapter.",
|
|
618
|
+
expectedUiState: "The status reads Shipped and the tracking number QA123456789 is displayed. The page does not still read Pending.",
|
|
619
|
+
expectedData: { status: "Shipped", trackingNumber: "QA123456789" },
|
|
620
|
+
endResult: "After a reload the buyer still sees Shipped.",
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
},
|
|
624
|
+
/* ── 6. Admin: verify the payment ──────────────────────────────────────── */
|
|
625
|
+
{
|
|
626
|
+
pageKey: "admin-verify",
|
|
627
|
+
pageLabel: "Admin: verify a manual payment",
|
|
628
|
+
href: "/admin/orders",
|
|
629
|
+
cases: [
|
|
630
|
+
{
|
|
631
|
+
key: "admin-orders-list-loads",
|
|
632
|
+
label: "The admin orders list loads and names the products ordered",
|
|
633
|
+
roles: ["admin"],
|
|
634
|
+
startPage: "/admin/orders",
|
|
635
|
+
steps: [
|
|
636
|
+
SIGNIN_ADMIN,
|
|
637
|
+
"Open /admin/orders.",
|
|
638
|
+
"Wait for the table to render.",
|
|
639
|
+
"Read the newest row's primary label and status.",
|
|
640
|
+
],
|
|
641
|
+
expectedBehaviour: "The admin orders list is unscoped and renders every store's orders.",
|
|
642
|
+
expectedUiState: "Rows are listed and the newest names its product rather than only an order id. The table is not empty and shows no error banner.",
|
|
643
|
+
endResult: "Nothing is written; reloading shows the same rows.",
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
key: "manual-payment-queue-filters",
|
|
647
|
+
label: "The manual-payment filter narrows the list, proven against the unfiltered count",
|
|
648
|
+
roles: ["admin"],
|
|
649
|
+
startPage: "/admin/orders",
|
|
650
|
+
steps: [
|
|
651
|
+
SIGNIN_ADMIN,
|
|
652
|
+
"Open /admin/orders.",
|
|
653
|
+
"Count the rows shown with no filter applied.",
|
|
654
|
+
"Apply the manual-payment filter for orders awaiting verification.",
|
|
655
|
+
"Count the rows shown now.",
|
|
656
|
+
],
|
|
657
|
+
expectedBehaviour: "The queue is refined in memory over a bounded query rather than by a Firestore inequality, because 'has a proof' excludes every document where the field was never written — which is exactly the awaiting-payment set.",
|
|
658
|
+
expectedUiState: "The filtered count is smaller than the unfiltered count, and every remaining row shows a manual payment method. An identical count means the filter is not being applied and is a FAIL.",
|
|
659
|
+
endResult: "The filter is carried in the URL, so reloading keeps the same scope.",
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
key: "admin-sees-payment-proof",
|
|
663
|
+
label: "The admin can open the order and see the buyer's uploaded proof",
|
|
664
|
+
roles: ["admin"],
|
|
665
|
+
startPage: "/admin/orders",
|
|
666
|
+
steps: [
|
|
667
|
+
SIGNIN_ADMIN,
|
|
668
|
+
"Open /admin/orders.",
|
|
669
|
+
"Open the detail view for the cash order placed earlier in this run.",
|
|
670
|
+
"Read the payment section.",
|
|
671
|
+
],
|
|
672
|
+
expectedBehaviour: "The admin review surface shows the uploaded screenshot, the reported reference, the expected UPI id and any mismatch between them.",
|
|
673
|
+
expectedUiState: "The payment screenshot is rendered, and verify, request-re-upload and reject controls are offered. The section is not empty.",
|
|
674
|
+
endResult: "Nothing is written by viewing.",
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
key: "verify-payment-marks-paid",
|
|
678
|
+
label: "🛑 Verifying the proof marks the order paid, and the buyer sees it",
|
|
679
|
+
roles: ["admin", "buyer"],
|
|
680
|
+
startPage: "/admin/orders",
|
|
681
|
+
steps: [
|
|
682
|
+
SIGNIN_ADMIN,
|
|
683
|
+
"Open /admin/orders.",
|
|
684
|
+
"Open the detail view for the cash order placed earlier in this run.",
|
|
685
|
+
"Click the verify control.",
|
|
686
|
+
"Confirm in the dialog.",
|
|
687
|
+
"Reload the order detail.",
|
|
688
|
+
],
|
|
689
|
+
expectedBehaviour: "The payment status becomes paid and a status-history entry records the actor and the reason. The buyer's own order page stops offering the proof upload.",
|
|
690
|
+
expectedUiState: "The payment status badge reads Paid. Admin: the verify control is no longer offered. Buyer: /user/orders/{orderId}/payment no longer offers an upload.",
|
|
691
|
+
expectedData: { paymentStatus: "Paid" },
|
|
692
|
+
endResult: "After the reload the status still reads Paid — not Pending.",
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
key: "admin-cannot-be-bypassed-by-url",
|
|
696
|
+
label: "A signed-out visitor cannot reach the admin orders list",
|
|
697
|
+
roles: ["guest"],
|
|
698
|
+
startPage: "/admin/orders",
|
|
699
|
+
steps: [
|
|
700
|
+
"Open /admin/orders signed out.",
|
|
701
|
+
"Wait for the navigation to settle and read the URL and the page.",
|
|
702
|
+
],
|
|
703
|
+
expectedBehaviour: "The admin subtree reads the session in its layout, so an unauthenticated request is redirected rather than rendered.",
|
|
704
|
+
expectedUiState: "The browser lands on a sign-in or unauthorised page. No order rows, no buyer names and no email addresses are rendered at any point.",
|
|
705
|
+
expectedData: { orderRowsVisibleToGuest: 0 },
|
|
706
|
+
endResult: "Nothing is written; repeating the navigation behaves the same way.",
|
|
707
|
+
},
|
|
708
|
+
],
|
|
709
|
+
},
|
|
710
|
+
];
|