@mohasinac/appkit 4.42.1 → 4.42.3

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.
Files changed (182) hide show
  1. package/dist/_internal/server/features/offers/adapters.d.ts +29 -7
  2. package/dist/_internal/server/features/offers/adapters.js +14 -3
  3. package/dist/_internal/server/features/orders/adapters.js +54 -1
  4. package/dist/client.d.ts +1 -0
  5. package/dist/client.js +1 -0
  6. package/dist/features/account/components/UserOrdersView.js +30 -2
  7. package/dist/features/account/hooks/useAddresses.d.ts +21 -6
  8. package/dist/features/account/hooks/useAddresses.js +1 -1
  9. package/dist/features/addresses/hooks/usePostalLookup.js +84 -12
  10. package/dist/features/admin/components/AdminCategoriesView.js +37 -2
  11. package/dist/features/admin/components/AdminOffersView.js +2 -2
  12. package/dist/features/admin/components/AdminSupportTicketsView.js +27 -1
  13. package/dist/features/admin/constants/filter-tabs.d.ts +25 -0
  14. package/dist/features/admin/constants/filter-tabs.js +30 -0
  15. package/dist/features/admin/hooks/useAdminListingData.d.ts +35 -0
  16. package/dist/features/admin/hooks/useAdminListingData.js +45 -9
  17. package/dist/features/cart/schemas/index.d.ts +15 -0
  18. package/dist/features/cart/schemas/index.js +2 -0
  19. package/dist/features/cart/types/index.d.ts +22 -0
  20. package/dist/features/categories/components/CategoriesIndexListing.js +19 -1
  21. package/dist/features/categories/components/CategoriesIndexPageView.js +14 -1
  22. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +8 -0
  23. package/dist/features/classified/components/ClassifiedDetailPageView.js +1 -0
  24. package/dist/features/events/actions/event-actions.d.ts +13 -0
  25. package/dist/features/events/actions/event-actions.js +75 -1
  26. package/dist/features/events/repository/event-entry.repository.d.ts +17 -0
  27. package/dist/features/events/repository/event-entry.repository.js +36 -0
  28. package/dist/features/homepage/components/BrandsSection.js +1 -1
  29. package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
  30. package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
  31. package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
  32. package/dist/features/media/upload/MediaUploadField.js +21 -3
  33. package/dist/features/orders/components/OrdersList.js +2 -1
  34. package/dist/features/orders/index.d.ts +1 -0
  35. package/dist/features/orders/index.js +1 -0
  36. package/dist/features/orders/types/index.d.ts +2 -0
  37. package/dist/features/orders/utils/order-ref.d.ts +45 -0
  38. package/dist/features/orders/utils/order-ref.js +58 -0
  39. package/dist/features/products/components/MakeOfferButton.d.ts +18 -1
  40. package/dist/features/products/components/MakeOfferButton.js +23 -1
  41. package/dist/features/products/components/ProductDetailPageView.d.ts +5 -0
  42. package/dist/features/products/components/ProductDetailPageView.js +3 -2
  43. package/dist/features/products/repository/products.repository.js +29 -2
  44. package/dist/features/seller/actions/offer-actions.js +32 -0
  45. package/dist/features/seller/components/QuickProductForm.js +15 -1
  46. package/dist/features/seller/components/SellerBidsView.js +3 -1
  47. package/dist/features/seller/components/SellerOffersView.js +2 -2
  48. package/dist/features/seller/components/SellerOrdersView.js +49 -6
  49. package/dist/features/seller/components/SellerProductsCards.js +23 -1
  50. package/dist/features/seller/components/SellerProductsView.js +27 -2
  51. package/dist/features/seller/hooks/useSellerListingData.d.ts +0 -1
  52. package/dist/features/seller/hooks/useSellerListingData.js +17 -19
  53. package/dist/features/shell/SectionForm.js +33 -2
  54. package/dist/features/tester/seed-data/_happy-path.d.ts +23 -0
  55. package/dist/features/tester/seed-data/_happy-path.js +710 -0
  56. package/dist/features/tester/seed-data/_money-flows.js +34 -23
  57. package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +22 -3
  58. package/dist/features/tester/seed-data/authored/addresses__address-filters.js +17 -12
  59. package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +7 -6
  60. package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +10 -4
  61. package/dist/features/tester/seed-data/authored/addresses__state-picker.js +13 -8
  62. package/dist/features/tester/seed-data/authored/addresses__unban-request.js +49 -11
  63. package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +10 -6
  64. package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +71 -32
  65. package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +80 -31
  66. package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.js +62 -25
  67. package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +61 -31
  68. package/dist/features/tester/seed-data/authored/admin__bundles.js +84 -37
  69. package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +90 -57
  70. package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +38 -26
  71. package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +47 -28
  72. package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +118 -31
  73. package/dist/features/tester/seed-data/authored/admin__content-deletes.js +30 -24
  74. package/dist/features/tester/seed-data/authored/admin__content-marketing.js +83 -46
  75. package/dist/features/tester/seed-data/authored/admin__coupons.js +8 -8
  76. package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +9 -9
  77. package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +19 -4
  78. package/dist/features/tester/seed-data/authored/admin__media-watermark.js +77 -33
  79. package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +5 -5
  80. package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +29 -5
  81. package/dist/features/tester/seed-data/authored/admin__site-system.js +45 -23
  82. package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +53 -28
  83. package/dist/features/tester/seed-data/authored/admin__users-trust.js +20 -20
  84. package/dist/features/tester/seed-data/authored/buying__bidding.js +19 -2
  85. package/dist/features/tester/seed-data/authored/buying__browsing-search.js +20 -5
  86. package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +28 -10
  87. package/dist/features/tester/seed-data/authored/buying__cart.js +10 -3
  88. package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +0 -16
  89. package/dist/features/tester/seed-data/authored/buying__my-orders.js +27 -5
  90. package/dist/features/tester/seed-data/authored/buying__offers.js +22 -7
  91. package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +3 -3
  92. package/dist/features/tester/seed-data/authored/buying__product-detail.js +35 -4
  93. package/dist/features/tester/seed-data/authored/buying__return-request.js +19 -6
  94. package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +36 -18
  95. package/dist/features/tester/seed-data/authored/buying__reviews.js +16 -1
  96. package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +2 -1
  97. package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +16 -1
  98. package/dist/features/tester/seed-data/authored/community-support__public-profile.js +3 -2
  99. package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +4 -4
  100. package/dist/features/tester/seed-data/authored/content-discovery__blog.js +19 -2
  101. package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +4 -4
  102. package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +5 -5
  103. package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +41 -6
  104. package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +63 -8
  105. package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +29 -29
  106. package/dist/features/tester/seed-data/authored/content-discovery__events.js +2 -2
  107. package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +23 -1
  108. package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +6 -6
  109. package/dist/features/tester/seed-data/authored/content-discovery__search.js +1 -1
  110. package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +1 -1
  111. package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +31 -6
  112. package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +34 -10
  113. package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +35 -8
  114. package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +31 -9
  115. package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +29 -3
  116. package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +1 -1
  117. package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +10 -10
  118. package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +3 -3
  119. package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +2 -2
  120. package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +2 -2
  121. package/dist/features/tester/seed-data/authored/design-ux__general-design.js +4 -4
  122. package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +1 -1
  123. package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +8 -8
  124. package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +2 -2
  125. package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +2 -2
  126. package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +39 -16
  127. package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +43 -8
  128. package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +40 -14
  129. package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +31 -14
  130. package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +7 -6
  131. package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +14 -5
  132. package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +3 -3
  133. package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +1 -1
  134. package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +4 -2
  135. package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +3 -3
  136. package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +5 -5
  137. package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +5 -5
  138. package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +3 -3
  139. package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +45 -11
  140. package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -17
  141. package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +36 -5
  142. package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +29 -4
  143. package/dist/features/tester/seed-data/authored/selling__become-seller.js +23 -0
  144. package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +14 -13
  145. package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +14 -9
  146. package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +34 -7
  147. package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +17 -7
  148. package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +9 -9
  149. package/dist/features/tester/seed-data/authored/selling__media-limits.js +9 -4
  150. package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +3 -3
  151. package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +3 -3
  152. package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +25 -1
  153. package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +27 -6
  154. package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +19 -1
  155. package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +63 -22
  156. package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +9 -5
  157. package/dist/features/tester/seed-data/authored/selling__seller-guide.js +7 -6
  158. package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +3 -2
  159. package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +18 -18
  160. package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +13 -12
  161. package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +19 -3
  162. package/dist/features/tester/seed-data/authored/selling__seller-orders.js +22 -4
  163. package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +20 -8
  164. package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +4 -4
  165. package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +2 -2
  166. package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +5 -5
  167. package/dist/features/tester/seed-data/authored/seo__og-images.js +1 -1
  168. package/dist/features/tester/seed-data/authored/seo__page-metadata.js +7 -7
  169. package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +3 -3
  170. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +123 -21
  171. package/dist/features/tester/utils/phases.d.ts +17 -0
  172. package/dist/features/tester/utils/phases.js +29 -2
  173. package/dist/index.d.ts +1 -0
  174. package/dist/index.js +2 -0
  175. package/dist/seo/json-ld.d.ts +23 -0
  176. package/dist/seo/json-ld.js +3 -2
  177. package/dist/server.d.ts +1 -0
  178. package/dist/server.js +2 -0
  179. package/dist/styles.css +1 -1
  180. package/dist/tailwind-utilities.css +1 -1
  181. package/dist/ui/components/PaginatedSelect.js +0 -0
  182. package/package.json +1 -1
@@ -80,11 +80,11 @@ export const authored = {
80
80
  },
81
81
  "checklist-admin-users-trust-roles-crud": {
82
82
  roles: ["admin"],
83
- startPage: "/admin/team",
83
+ startPage: "/admin/roles",
84
84
  steps: [
85
85
  "Sign in as admin@letitrip.in / TempPass123!.",
86
- "Open the roles or permissions surface and read every role and its permissions.",
87
- "Read whether a permissions catalogue page exists and is reachable from the sidebar.",
86
+ "Open /admin/roles and read every role and its permissions.",
87
+ "Open /admin/permissions and confirm it redirects to /admin/roles?tab=permissions — it is a redirect-only shim, so the catalogue is a TAB rather than a page of its own. Check that tab is reachable from the sidebar under one name or the other.",
88
88
  "Create a role named 'QA Role users-trust' with two permissions and save.",
89
89
  "RELOAD and read its permissions.",
90
90
  "Edit it to add a third, save, RELOAD, and check the first two are still there.",
@@ -135,7 +135,7 @@ export const authored = {
135
135
  startPage: "/admin/addresses",
136
136
  steps: [
137
137
  "Sign in as admin@letitrip.in / TempPass123!.",
138
- "Open the banned-addresses surface and read every column.",
138
+ "Open /admin/banned-addresses and read every column.",
139
139
  "Check each row shows the address, who banned it, when, and why.",
140
140
  "Read whether an unban request from the owner is visible on the row.",
141
141
  "Open one and read the requester's full note before deciding anything.",
@@ -151,7 +151,7 @@ export const authored = {
151
151
  startPage: "/admin/addresses",
152
152
  steps: [
153
153
  "Sign in as admin@letitrip.in / TempPass123!.",
154
- "Open the address clusters surface and read what a cluster row represents.",
154
+ "Open /admin/address-clusters and read what a cluster row represents.",
155
155
  "Check each row states how many accounts share the address and names them.",
156
156
  "Open a cluster and read the accounts it groups.",
157
157
  "Read whether the shared address itself is shown in full or masked.",
@@ -167,7 +167,7 @@ export const authored = {
167
167
  startPage: "/admin/moderation",
168
168
  steps: [
169
169
  "Sign in as admin@letitrip.in / TempPass123!.",
170
- "Open the moderation queue and read every row.",
170
+ "Open /admin/moderation and read every row.",
171
171
  "Check each row identifies what is being moderated and shows enough to judge it.",
172
172
  "Open one item and read its full content — text, images, whatever was submitted.",
173
173
  "Return to the list and check the same is reachable from the row itself.",
@@ -183,7 +183,7 @@ export const authored = {
183
183
  startPage: "/admin/moderation",
184
184
  steps: [
185
185
  "Sign in as admin@letitrip.in / TempPass123!.",
186
- "Open the moderation queue and open an item.",
186
+ "Open /admin/moderation and open an item.",
187
187
  "Read its content, then choose Reject.",
188
188
  "Submit with the reason box empty and read what happens.",
189
189
  "Type 'Too short' and submit, reading what happens.",
@@ -216,7 +216,7 @@ export const authored = {
216
216
  startPage: "/admin/support-tickets",
217
217
  steps: [
218
218
  "Sign in as admin@letitrip.in / TempPass123!.",
219
- "Open the admin support tickets surface and read every column.",
219
+ "Open /admin/support-tickets and read every column.",
220
220
  "Read every status filter and select each, noting any that returns nothing.",
221
221
  "Open a ticket and read the full message thread.",
222
222
  "Reply to it and change its status in the same action.",
@@ -232,7 +232,7 @@ export const authored = {
232
232
  startPage: "/admin/item-requests",
233
233
  steps: [
234
234
  "Sign in as admin@letitrip.in / TempPass123!.",
235
- "Open the item-requests surface and read every row.",
235
+ "Open /admin/item-requests and read every row.",
236
236
  "Check each row shows the requested item's title and description rather than only a requester.",
237
237
  "Open one and read the full request before acting.",
238
238
  "Read the actions offered and check a view exists alongside them.",
@@ -250,7 +250,7 @@ export const authored = {
250
250
  startPage: "/user",
251
251
  steps: [
252
252
  "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
253
- "Open the item-request form from the buyer side.",
253
+ "Open /item-requests/new as the buyer — the public item-request form, not the admin queue at /admin/item-requests.",
254
254
  "Submit it completely empty and read where the errors appear.",
255
255
  "Type a title only and submit, reading what happens.",
256
256
  "Type a one-word description and submit, reading what happens.",
@@ -267,7 +267,7 @@ export const authored = {
267
267
  startPage: "/admin/reports",
268
268
  steps: [
269
269
  "Sign in as admin@letitrip.in / TempPass123!.",
270
- "Open the reports surface and read every row.",
270
+ "Open /admin/reports and read every row.",
271
271
  "Check each row names what was reported and by whom, and shows the reporter's stated detail.",
272
272
  "Open one and read the full report before acting.",
273
273
  "Read every status filter and select each, noting any that returns nothing.",
@@ -299,7 +299,7 @@ export const authored = {
299
299
  startPage: "/admin/reports",
300
300
  steps: [
301
301
  "Sign in as admin@letitrip.in / TempPass123!.",
302
- "Open the reports surface and open the report created by the buyer case.",
302
+ "Open /admin/reports and open the report created by the buyer case.",
303
303
  "Read its full detail, then choose Dismiss.",
304
304
  "Submit with the reason empty and read what happens.",
305
305
  "Type 'QA Dismissal users-trust — reviewed, no policy breach.' and submit.",
@@ -315,7 +315,7 @@ export const authored = {
315
315
  startPage: "/admin/reports",
316
316
  steps: [
317
317
  "Sign in as admin@letitrip.in / TempPass123!.",
318
- "Open the reports surface and open an open report.",
318
+ "Open /admin/reports and open an open report.",
319
319
  "Note the current date and time.",
320
320
  "Close the report with a resolution note.",
321
321
  "RELOAD and read the closed date shown.",
@@ -331,10 +331,10 @@ export const authored = {
331
331
  startPage: "/admin/reports",
332
332
  steps: [
333
333
  "Sign in as admin@letitrip.in / TempPass123!.",
334
- "Open the reports surface and read every status the status control offers.",
334
+ "Open /admin/reports and read every status the status control offers.",
335
335
  "Write the list down.",
336
336
  "Select each in turn on a test report and confirm each is accepted.",
337
- "Open the reports list and read every status filter chip.",
337
+ "Open /admin/reports and read every status filter chip.",
338
338
  "Compare the chip list against the status list from the editor.",
339
339
  "Note any chip naming a status the editor cannot set, or any status the editor sets that has no chip.",
340
340
  ],
@@ -347,7 +347,7 @@ export const authored = {
347
347
  startPage: "/admin/catalogue-approvals",
348
348
  steps: [
349
349
  "Sign in as admin@letitrip.in / TempPass123!.",
350
- "Open the catalogue approvals surface and read every row.",
350
+ "Open /admin/catalogue-approvals and read every row.",
351
351
  "Check each row shows the submitted item's photos, description and price.",
352
352
  "Read every status filter and select each.",
353
353
  "Open one submission and read everything the user submitted.",
@@ -362,7 +362,7 @@ export const authored = {
362
362
  startPage: "/admin/catalogue-approvals",
363
363
  steps: [
364
364
  "Sign in as admin@letitrip.in / TempPass123!.",
365
- "Open the catalogue approvals surface and find a pending submission.",
365
+ "Open /admin/catalogue-approvals and find a pending submission.",
366
366
  "WITHOUT using Approve or Reject, find a way to open the submission in full.",
367
367
  "Read every photo at full size and the complete description.",
368
368
  "Read the price the submitter set and any linked marketplace listing.",
@@ -377,7 +377,7 @@ export const authored = {
377
377
  startPage: "/admin/payment-methods",
378
378
  steps: [
379
379
  "Sign in as admin@letitrip.in / TempPass123!.",
380
- "Open the payment-methods clusters surface and read what a row represents.",
380
+ "Open /admin/payment-methods and read what a row represents.",
381
381
  "Check each row states how many accounts share the instrument and names them.",
382
382
  "Read how the payment identifier itself is displayed.",
383
383
  "Search the page source for a full card or UPI identifier.",
@@ -522,7 +522,7 @@ export const authored = {
522
522
  startPage: "/admin",
523
523
  steps: [
524
524
  "Sign in as admin@letitrip.in / TempPass123!.",
525
- "Open the homepage sections editor and open the section type picker.",
525
+ "Open /admin/sections and open the section type picker.",
526
526
  "Write down every type offered.",
527
527
  "Compare the list against the section types the homepage can render.",
528
528
  "Create one of each type not already present, saving after each.",
@@ -538,7 +538,7 @@ export const authored = {
538
538
  startPage: "/admin/site",
539
539
  steps: [
540
540
  "Sign in as admin@letitrip.in / TempPass123!.",
541
- "Open the analytics alert settings and find the threshold field.",
541
+ "Open /admin/analytics and find the threshold field.",
542
542
  "Type abc into it and attempt to save.",
543
543
  "Read where any error appears.",
544
544
  "Type -5 and attempt to save, reading what happens.",
@@ -168,8 +168,25 @@ export const authored = {
168
168
  "Read the bidder identity, amount and timestamp on every visible row.",
169
169
  ],
170
170
  expectedBehaviour: "Bidder names are masked on the way out of the server, not hidden in the renderer. The masking helper for bids once returned its input unchanged while being named as though it masked — every bidder's real name was in the public payload and nothing displayed it, so the leak was invisible from the screen. Displaying the masked name is what makes that checkable by eye.",
171
- expectedUiState: "Each row reads in the shape '₹3,199.00 · M*** B*** · 4 Sept, 17:06'. The name is genuinely masked — asterisks in the middle — and is not a full readable name, and not a placeholder like 'Bidder'. The timestamp carries both a date and a clock time, not a date alone.",
172
- endResult: "The same masked names and timestamps appear after reload. A real full name — 'Meera Bey', 'Rohit Collector' or 'Ananya Collector', the three seeded bidders on this auction — anywhere in these rows is a PII leak and fails the case outright.",
171
+ /*
172
+ * 🛑 THE FORBIDDEN NAMES WERE FICTIONAL, SO THE PII CHECK PASSED VACUOUSLY.
173
+ *
174
+ * This asserted that 'Meera Bey', 'Rohit Collector' or 'Ananya Collector'
175
+ * must not appear. Those three strings occur ZERO times in the seed: the
176
+ * personas' displayName values are "Mock User 11", "Mock User 14" and
177
+ * "Mock User 9". So the assertion could never fail — it would report a
178
+ * clean PII check whether masking worked or not, which is the one outcome
179
+ * worse than no check at all.
180
+ *
181
+ * The masked shape was wrong for the same reason: 'M*** B***' is the mask
182
+ * of "Meera Bey". maskName splits on whitespace and keeps each word's first
183
+ * character, so "Mock User 11" becomes "M*** U*** 11***".
184
+ *
185
+ * Bidder uids are right (meera-bey, rohit-collector, ananya-collector all
186
+ * bid on this auction) — only the names they resolve to were invented.
187
+ */
188
+ expectedUiState: "Each row reads in the shape '₹3,199.00 · M*** U*** 11*** · 4 Sept, 17:06'. The name is genuinely masked — every word reduced to its first character plus asterisks — and is not a full readable name, and not a placeholder like 'Bidder'. The timestamp carries both a date and a clock time, not a date alone.",
189
+ endResult: "The same masked names and timestamps appear after reload. An unmasked display name — 'Mock User 9', 'Mock User 11' or 'Mock User 14', the three seeded bidders on this auction — anywhere in these rows is a PII leak and fails the case outright.",
173
190
  },
174
191
  "checklist-buying-bidding-bid-increment-live-tier-change": {
175
192
  roles: ["buyer"],
@@ -621,14 +621,29 @@ export const authored = {
621
621
  "Open /auctions.",
622
622
  "Click the 'Ended' tab.",
623
623
  "Scan the grid for 'Beyblade Burst B-128 Spriggan Requiem (Ended — Bought Out)' (auction-beyblade-burst-spriggan-requiem-bought-out).",
624
- "Click the 'Live' tab.",
624
+ /*
625
+ * 🛑 'Available', not 'Live' — and on the next page 'Ended', not 'Closed'.
626
+ *
627
+ * `availabilityTabsFor` builds exactly three tabs and only the MIDDLE one
628
+ * varies: its label comes from HIDE_DEFAULT_NOUN, which maps every
629
+ * hideDefault onto "Sold" or "Ended" and nothing else. Prize-draw's
630
+ * `hideDefault: "closed"` maps to "Ended", so /prize-draws reads
631
+ * Available / Ended / All.
632
+ *
633
+ * The first tab is ALWAYS 'Available'. This case named it 'Live' on
634
+ * /auctions and 'Open' on /prize-draws, and called the prize-draw middle
635
+ * tab 'Closed' — three controls that render under different names, so the
636
+ * tester would hunt, fail to find them, and answer "could not test" on a
637
+ * feature that works.
638
+ */
639
+ "Click the 'Available' tab.",
625
640
  "Open /prize-draws.",
626
- "Click the 'Closed' tab.",
641
+ "Click the 'Ended' tab.",
627
642
  "Scan the grid for 'Metal Fight Mystery Draw — Ended (Revealed)' (prizedraw-beyblade-metal-closed-revealed).",
628
- "Click the 'Open' tab.",
643
+ "Click the 'Available' tab.",
629
644
  ],
630
- expectedBehaviour: "The 'Available' / 'Live' / 'Open' tab hides sold-out, ended, and closed items by default. The 'Sold & Ended' / 'Ended' / 'Closed' tab reveals them.",
631
- expectedUiState: "'Beyblade X BX-34 Dran Buster (Sold Out)' appears on the 'Sold & Ended' tab and is absent on 'Available'. 'Beyblade Burst B-128 Spriggan Requiem (Ended — Bought Out)' appears on 'Ended' and is absent on 'Live'. 'Metal Fight Mystery Draw — Ended (Revealed)' appears on 'Closed' and is absent on 'Open'.",
645
+ expectedBehaviour: "The 'Available' tab hides sold-out, ended and closed items on every listing page; the middle tab reveals them. Only the middle label varies by listing type — 'Sold & Ended' on /products, 'Ended' on /auctions and on /prize-draws.",
646
+ expectedUiState: "'Beyblade X BX-34 Dran Buster (Sold Out)' appears on the 'Sold & Ended' tab and is absent on 'Available'. 'Beyblade Burst B-128 Spriggan Requiem (Ended — Bought Out)' appears on /auctions' 'Ended' tab and is absent on 'Available'. 'Metal Fight Mystery Draw — Ended (Revealed)' appears on /prize-draws' 'Ended' tab and is absent on 'Available'. No page offers a tab labelled 'Live', 'Open' or 'Closed'.",
632
647
  endResult: "Nothing is persisted. Tab state is URL state only.",
633
648
  },
634
649
  "checklist-buying-browsing-search-sort-options-per-listing-type": {
@@ -337,14 +337,30 @@ export const authored = {
337
337
  roles: ["buyer"],
338
338
  startPage: "/checkout",
339
339
  steps: [
340
- "Sign in as admin@letitrip.in / TempPass123! and set Site Settings → Checkout → Out-of-stock policy to 'Cancel whole order'.",
340
+ /*
341
+ * 🛑 THE BUYER CHOOSES THIS AT CHECKOUT — it is not an admin setting.
342
+ *
343
+ * This step said "Sign in as admin and set Site Settings → Checkout →
344
+ * Out-of-stock policy". There is no such setting: `outOfStockPolicy` is a
345
+ * POLICY_SECTIONS field rendered by CheckoutRouteClient and passed
346
+ * straight into createCheckoutOrder. It appears nowhere in the admin
347
+ * feature. A tester would have hunted through Site Settings, not found
348
+ * it, and answered "could not test".
349
+ *
350
+ * The option labels were wrong too — 'Cancel whole order' is the app's
351
+ * "Cancel my whole order", and the sibling case's 'Skip unavailable
352
+ * items' is actually "Ship what's available and refund the rest", which
353
+ * shares no words with what the case asked for.
354
+ */
341
355
  "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
342
- "Add product-beyblade-burst-valkyrie (₹999, stock 10) and product-beyblade-original-dranzer-s (₹149) to the cart.",
343
- "While on the checkout payment step (before submitting), have the admin or another session reduce product-beyblade-burst-valkyrie's stock to 0.",
356
+ "Add product-beyblade-burst-valkyrie (₹999, stock 10) and product-beyblade-original-dranzer-s (₹1,499) to the cart.",
357
+ "Open /checkout and advance to the payment step.",
358
+ "Under 'If an item goes out of stock', select 'Cancel my whole order'.",
359
+ "Before submitting, have another session reduce product-beyblade-burst-valkyrie's stock to 0.",
344
360
  "Submit the checkout.",
345
361
  ],
346
- inputs: { policy: "Cancel whole order", goesOutOfStock: "product-beyblade-burst-valkyrie" },
347
- expectedBehaviour: "With the 'cancel whole order' policy, if any item in the batch goes out of stock mid-checkout, the entire checkout is rejected. No order is created and the buyer receives an error.",
362
+ inputs: { policy: "Cancel my whole order", goesOutOfStock: "product-beyblade-burst-valkyrie" },
363
+ expectedBehaviour: "With the cancel-whole-order policy, if any item in the batch goes out of stock mid-checkout, the entire checkout is rejected. No order is created and the buyer receives an error.",
348
364
  expectedUiState: "After submitting, an error message is shown indicating that an item went out of stock and the entire order was cancelled. No order confirmation page is shown.",
349
365
  expectedData: { ordersCreated: 0 },
350
366
  endResult: "After reloading /cart, both items are still present in the cart (or the buyer is able to restart the checkout). No order record exists for this attempt.",
@@ -353,14 +369,16 @@ export const authored = {
353
369
  roles: ["buyer"],
354
370
  startPage: "/checkout",
355
371
  steps: [
356
- "Sign in as admin@letitrip.in / TempPass123! and set Site Settings → Checkout → Out-of-stock policy to 'Skip unavailable items'.",
372
+ /* Buyer-chosen at checkout, not an admin setting — see the sibling case. */
357
373
  "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
358
- "Add product-beyblade-burst-valkyrie (₹999, stock 10) and product-beyblade-original-dranzer-s (₹149) to the cart.",
359
- "While on the checkout payment step (before submitting), have the admin or another session reduce product-beyblade-original-dranzer-s's stock to 0.",
374
+ "Add product-beyblade-burst-valkyrie (₹999, stock 10) and product-beyblade-original-dranzer-s (₹1,499) to the cart.",
375
+ "Open /checkout and advance to the payment step.",
376
+ "Under 'If an item goes out of stock', select 'Ship what's available and refund the rest'.",
377
+ "Before submitting, have another session reduce product-beyblade-original-dranzer-s's stock to 0.",
360
378
  "Submit the checkout.",
361
379
  ],
362
- inputs: { policy: "Skip unavailable items", goesOutOfStock: "product-beyblade-original-dranzer-s" },
363
- expectedBehaviour: "With the 'skip unavailable items' policy, the out-of-stock item is silently dropped and an order is placed for only the still-available item(s). The buyer is informed which item was skipped.",
380
+ inputs: { policy: "Ship what's available and refund the rest", goesOutOfStock: "product-beyblade-original-dranzer-s" },
381
+ expectedBehaviour: "With the ship-what's-available policy, the out-of-stock item is dropped and an order is placed for only the still-available item(s). The buyer is informed which item was skipped.",
364
382
  expectedUiState: "After submitting, the order confirmation page shows only product-beyblade-burst-valkyrie (₹999). A notice is shown indicating that product-beyblade-original-dranzer-s was unavailable and was removed from the order.",
365
383
  expectedData: { orderLineCount: 1 },
366
384
  endResult: "After reloading the order detail page, only product-beyblade-burst-valkyrie is listed in the order. product-beyblade-original-dranzer-s is not included.",
@@ -601,7 +601,7 @@ export const authored = {
601
601
  startPage: "/store/bundles",
602
602
  steps: [
603
603
  "Sign in as tyson@beybladearena.in / TempPass123!.",
604
- "Open the store's bundles surface and start creating one.",
604
+ "Open /store/bundles and start creating one.",
605
605
  "Add a member from this store and one from a different store.",
606
606
  "Save and read the message.",
607
607
  "Check the message names the offending member or store.",
@@ -729,7 +729,7 @@ export const authored = {
729
729
  "Open /cart and read the Won Auctions tab.",
730
730
  "Check the line is gone.",
731
731
  "Open /user/bids and read that bid's status.",
732
- "Open the notification list and read the forfeiture entry.",
732
+ "Open /user/notifications and read the forfeiture entry.",
733
733
  "Check the Cart tab's checkout is now enabled again.",
734
734
  ],
735
735
  expectedBehaviour: "An unpaid win past its deadline is forfeited, the locked line cleared and the buyer notified. Clearing the line is what unblocks the rest of the cart — a leftover locked line keeps the auction lane non-empty and that lane outranks the others, so the buyer's whole cart stays blocked indefinitely.",
@@ -945,7 +945,14 @@ export const authored = {
945
945
  startPage: "/cart",
946
946
  steps: [
947
947
  "Sign in as rehan.sheikh@gmail.com / TempPass123! with items from TWO sellers.",
948
- "Open /cart and tick 'WhatsApp order updates' on ONE seller's card only.",
948
+ /*
949
+ * 'WhatsApp updates' — the label StoreAddonsPicker actually renders,
950
+ * under an 'Add-ons' heading on each seller's card. This said 'WhatsApp
951
+ * order updates', which appears nowhere; CartPriceBreakdown's matching
952
+ * FEE LINE reads 'WhatsApp updates' too, so neither surface uses the
953
+ * longer form.
954
+ */
955
+ "Open /cart and tick 'WhatsApp updates' under 'Add-ons' on ONE seller's card only.",
949
956
  "Read that seller's fee lines and the aggregate breakdown.",
950
957
  "Check the fee is charged once at ₹10.00, not ₹20.00.",
951
958
  "Check the other seller's card shows no such fee.",
@@ -87,22 +87,6 @@ export const authored = {
87
87
  expectedUiState: "Every tile shows its image above its caption, all at the same width, at both viewport sizes. An image sitting beside its caption and squeezed narrow is the row-direction failure.",
88
88
  endResult: "Read-only; restore the window width afterwards.",
89
89
  },
90
- "checklist-buying-image-tile-layout-concern-card-icon-above-label": {
91
- roles: ["guest"],
92
- startPage: "/",
93
- steps: [
94
- "Open / in a private window at 1280 pixels wide.",
95
- "Find the concern or trust cards and look at each one.",
96
- "Check each card's icon sits above its label rather than beside it.",
97
- "Compare the icon's size against the card's own height.",
98
- "Resize to 390 pixels and check the arrangement holds.",
99
- "Switch to dark mode and check the icons are still visible.",
100
- ],
101
- inputs: { desktopWidth: 1280, mobileWidth: 390 },
102
- expectedBehaviour: "These cards carry an explicitly-sized icon box, which is what kept them from collapsing entirely when the wrapper defect hit — but an explicit size does not fix the direction, so they still laid the icon out beside the label. Sizing and direction are two separate failures and this card had only one of them.",
103
- expectedUiState: "Each card shows its icon above its label at both widths, with the icon proportionate to the card. An icon beside its label, or one at the platform font's fallback size, are different failures and should be reported as such.",
104
- endResult: "Read-only; return to light mode and restore the width.",
105
- },
106
90
  "checklist-buying-image-tile-layout-media-picker-existing-grid": {
107
91
  roles: ["seller"],
108
92
  startPage: "/store/products/new",
@@ -242,11 +242,33 @@ export const authored = {
242
242
  roles: ["buyer"],
243
243
  startPage: "/user/orders",
244
244
  steps: [
245
- "Sign in as an account whose inbox you can open and confirm order emails are enabled in its notification settings.",
246
- "Place an order and note the time.",
247
- "Open the inbox and wait up to five minutes for a confirmation email.",
248
- "Read its From name, its subject and its body.",
249
- "Have the seller mark the order shipped, then check the inbox again.",
245
+ /*
246
+ * 🛑 THE ACCOUNT MUST BE THE HARNESS MAILBOX, AND NO SEEDED PERSONA IS.
247
+ *
248
+ * This step used to read "an account whose inbox you can open", which is
249
+ * not a literal value and quietly hid a real constraint. Two mechanisms
250
+ * can settle an email case, and BOTH key on TESTER_EMAIL_ID:
251
+ *
252
+ * - check-inbox.mjs reads that mailbox over IMAP
253
+ * - the emailEvents recorder writes a row ONLY when the recipient
254
+ * equals TESTER_EMAIL_ID (or EMAIL_RECORDER_ALL is set)
255
+ *
256
+ * Every seeded persona uses a real-looking address the harness does not
257
+ * own, so an order placed as rehan produces nothing either instrument can
258
+ * see. Gmail plus-addressing is the way in: a fresh signup at
259
+ * <harness-mailbox>+run3@gmail.com is delivered to the same inbox.
260
+ *
261
+ * Capture the timestamp BEFORE placing the order. The mailbox is shared
262
+ * across every case in every run, so "newest matching message" will
263
+ * happily return one an earlier case left behind — which reads as a pass
264
+ * for an email this action never sent.
265
+ */
266
+ "Register a new account at /auth/register using the harness mailbox with a run-stamped plus-address (the value of TESTER_EMAIL_ID in tester/.env, e.g. name+run3@gmail.com), and confirm order emails are enabled in its notification settings.",
267
+ "Run `node -e \"console.log(Date.now())\"` and keep that number.",
268
+ "Place an order as that account.",
269
+ "Run `node tester/scripts/check-inbox.mjs --since <that number> --subject \"order\"` and read the exit code: 0 the mail arrived, 1 it did not, 2 the mailbox could not be opened.",
270
+ "Read the message's From name, its subject and its body.",
271
+ "Have the seller mark the order shipped, capture a fresh timestamp, and run check-inbox.mjs again with --subject \"shipped\".",
250
272
  "Click a link inside each email and read where it lands.",
251
273
  "Compare each email's figures against the order itself.",
252
274
  ],
@@ -52,10 +52,10 @@ export const authored = {
52
52
  "Read the row — product, offered amount, listed price, status and round.",
53
53
  "Open the row action menu and read every action offered.",
54
54
  "Use Counter, then cancel out of the form without submitting.",
55
- "Use Decline on a DIFFERENT pending offer and read what happens.",
55
+ "Use Reject on a DIFFERENT pending offer and read what happens.",
56
56
  "Reload and read that offer's status.",
57
57
  ],
58
- expectedBehaviour: "The seller's list shows incoming offers with the context needed to decide, and Accept, Decline and Counter all act. The store keeps sole authority over its own pricing — this is the surface where that authority lives.",
58
+ expectedBehaviour: "The seller's list shows incoming offers with the context needed to decide, and Accept, Reject and Counter all act. The store keeps sole authority over its own pricing — this is the surface where that authority lives.",
59
59
  expectedUiState: "Rows show product, amounts, status and round. All three actions are present and act. The declined offer reads as declined after the reload.",
60
60
  endResult: "One offer is declined; QA Offer make-offer is untouched.",
61
61
  },
@@ -82,7 +82,7 @@ export const authored = {
82
82
  steps: [
83
83
  "Sign in as tyson@beybladearena.in / TempPass123!.",
84
84
  "Open /store/offers and find a pending offer.",
85
- "WITHOUT using Accept, Decline or Counter, find a way to open its details.",
85
+ "WITHOUT using Accept, Reject or Counter, find a way to open its details.",
86
86
  "Read the buyer's note, the listed price, the offered amount and the round.",
87
87
  "Read any earlier rounds in the same negotiation.",
88
88
  "Return to the list and check the same is reachable from the row.",
@@ -243,12 +243,12 @@ export const authored = {
243
243
  "Sign in as admin@letitrip.in / TempPass123!.",
244
244
  "Open the admin offers surface and open a pending offer's row menu.",
245
245
  "Read every action offered and write the list down.",
246
- "Check for Accept, Counter or Decline.",
246
+ "Check for Accept, Counter or Reject.",
247
247
  "Select several rows and read any bulk action bar that appears.",
248
248
  "Read whether a bulk cancel is offered.",
249
249
  ],
250
250
  expectedBehaviour: "Admin is a coordinator, not a participant: only View and Cancel. The store keeps sole authority over its own pricing. There is deliberately no bulk cancel — one shared reason across a heterogeneous selection is worse audit data than none, and a registry-backed control that reads as working while doing nothing is worse than an absent one.",
251
- expectedUiState: "The menu offers View and Cancel only. No Accept, Counter or Decline. No bulk cancel appears — and if one does, it must actually act rather than only clearing the selection.",
251
+ expectedUiState: "The menu offers View and Cancel only. No Accept, Counter or Reject. No bulk cancel appears — and if one does, it must actually act rather than only clearing the selection.",
252
252
  expectedData: { adminActions: 2 },
253
253
  endResult: "Read-only; act on nothing.",
254
254
  },
@@ -259,11 +259,26 @@ export const authored = {
259
259
  "Sign in as tyson@beybladearena.in / TempPass123!.",
260
260
  "Open /store/offers and open a pending offer's row menu.",
261
261
  "Read every action offered.",
262
- "Check Accept, Counter and Decline are all present.",
262
+ "Check Accept, Counter and Reject are all present.",
263
263
  "Use Accept on one and reload.",
264
264
  "Read the offer's status and check the buyer's cart gains a line for it.",
265
265
  ],
266
- expectedBehaviour: "Adding an admin surface must not have removed the store's own actions. The seller retains Accept, Counter and Decline — an admin coordinating a dispute is a different capability from the seller deciding their own price.",
266
+ expectedBehaviour:
267
+ /*
268
+ * 'Reject', not 'Decline'. SellerOffersView renders its row menu from
269
+ * ACTIONS.STORE["accept-offer" | "counter-offer" | "reject-offer"], whose
270
+ * labels are Accept / Counter / Reject. Nothing in the menu says Decline.
271
+ *
272
+ * The confusion is real and worth naming: the offer STATUS is `declined`,
273
+ * and this project's own docs describe the seller as able to
274
+ * "accept / counter / decline". Both are true of the outcome — but a case
275
+ * tells a tester which BUTTON to click, and that button says Reject.
276
+ *
277
+ * Note SELLER_ROW_ACTIONS.offers ([VIEW, APPROVE, REJECT]) is NOT what
278
+ * this view uses, so reading the action-defs preset would give a third
279
+ * answer again.
280
+ */
281
+ "Adding an admin surface must not have removed the store's own actions. The seller retains Accept, Counter and Reject — an admin coordinating a dispute is a different capability from the seller deciding their own price.",
267
282
  expectedUiState: "All three actions are present and Accept works. The accepted offer reads accepted and produces a cart line in the buyer's offer lane.",
268
283
  endResult: "One offer is accepted.",
269
284
  },
@@ -33,7 +33,7 @@ export const authored = {
33
33
  "Read what the browser does with the response.",
34
34
  ],
35
35
  expectedBehaviour: "The invoice is generated on demand and streamed by a route that checks the order belongs to the caller. It is not a stored file and has no public URL.",
36
- expectedUiState: "A file downloads rather than rendering in the tab, and its name contains the order id. Not a JSON blob and not an error page.",
36
+ expectedUiState: "The invoice opens as a readable print view with a Print / Save as PDF control — it is not a JSON blob and not an error page. It does NOT download a file, and that is by design. Check the MONEY: every fee in the total must have its own row, so Subtotal + Shipping + each fee equals the stated Total exactly.",
37
37
  expectedData: { invoiceDownloaded: true },
38
38
  endResult: "The order is unchanged by downloading its invoice; the row still shows the same status after a reload.",
39
39
  },
@@ -106,12 +106,12 @@ export const authored = {
106
106
  startPage: "/user/orders",
107
107
  steps: [
108
108
  "Sign in as rehan.sheikh@gmail.com / TempPass123! — this persona owns the seeded SHIPPED order.",
109
- "Open /user/orders and open order-1-20251104-aevnlw.",
109
+ "Open /user/orders and open order-1-20251122-481j4x.",
110
110
  "Use the Track action.",
111
111
  "Read every step on the timeline and note which carry a date and which do not.",
112
112
  "Compare the dates shown against the order's own placed and shipped dates.",
113
113
  ],
114
- inputs: { orderId: "order-1-20251104-aevnlw" },
114
+ inputs: { orderId: "order-1-20251122-481j4x" },
115
115
  expectedBehaviour: "Each timeline step shows the date actually recorded for it. A step with no recorded date renders an em-dash rather than a guess — an invented or estimated date is worse than a blank, because it cannot be told apart from a real one.",
116
116
  expectedUiState: "The placed and shipped steps carry dates matching the order. Steps the order has not reached show no date rather than today's.",
117
117
  expectedData: { fabricatedDatesShown: false },
@@ -126,7 +126,22 @@ export const authored = {
126
126
  ],
127
127
  inputs: { productId: "digitalcode-beyblade-x-app-starter-pack", price: 199, codesAvailable: 25, paymentMethod: "Cash on Delivery" },
128
128
  expectedBehaviour: "This fixture is auto-claim, so the code is allocated and delivered as soon as the order is confirmed, with no seller action in between. The pool's remaining count drops by one. The listing advertises 25 codes on the product document, but the codes themselves live in a subcollection the seed does not write — so the pool must be stocked first from the seller's Digital Content pool manager on /store/products, or the purchase completes and delivers nothing, which is the exact defect this case exists to catch.",
129
- expectedUiState: "The order page shows a digital-code panel containing an actual code string that can be selected and copied. It does not read 'Pending delivery' or 'Waiting for seller' — for an auto-claim listing those states mean the delivery never fired.",
129
+ /*
130
+ * 🛑 THE FORBIDDEN STATES DID NOT EXIST, SO THE ASSERTION COULD NOT FAIL.
131
+ *
132
+ * This forbade 'Pending delivery' and 'Waiting for seller'. Neither string
133
+ * is anywhere in the source: CodeRevealPanel renders 'Your Digital Code',
134
+ * 'Your Digital Content', 'Your download', 'Copied!' and one error —
135
+ * 'Could not retrieve your code. Please try again.'
136
+ *
137
+ * That matters more here than on most pages. Root Cause #103 is exactly
138
+ * this failure: the pool had no writer, so every purchase logged "code pool
139
+ * exhausted", returned silently, completed the order normally, and left the
140
+ * reveal answering 404. The real symptom is an ABSENT or ERRORING panel —
141
+ * and forbidding two states the app never renders meant this case would
142
+ * have passed straight through it.
143
+ */
144
+ expectedUiState: "The order page shows a digital-code panel headed 'Your Digital Code' (or 'Your Digital Content') containing an actual code string that can be selected and copied. The panel is present at all — an absent panel, a 404 from the reveal request, or the text 'Could not retrieve your code. Please try again.' each mean the delivery never fired, which for an auto-claim listing is the defect this case exists to catch.",
130
145
  expectedData: { codeDelivered: true },
131
146
  endResult: "Reloading the order still shows the same delivered code. A code that appears once and is blank after reload was never persisted against the order.",
132
147
  },
@@ -183,7 +198,16 @@ export const authored = {
183
198
  "Look at the gallery thumbnail strip.",
184
199
  ],
185
200
  expectedBehaviour: "A live listing renders its species-specific fields — species, breed, sex, age and the jurisdiction restrictions — which no other listing type carries, and offers a cart path once those restrictions are satisfied.",
186
- expectedUiState: "Heading 'Test Live Item — Golden Retriever Puppy'. A species line in the shape 'Dog (Golden Retriever) · male · 6mo'. BEFORE: the price read '₹1,500.00'; AFTER it reads 'Sign in to see price' and the digits 1,500 appear nowhere, and the 'Handling:' fee on the transport line is absent too. Delivery restrictions are stated rather than blank. The thumbnail strip carries a 'View video' entry alongside 'View image 1'. An 'Add to Cart' button is present.",
201
+ expectedUiState:
202
+ /*
203
+ * Title and price taken from the seed, not from the deleted sandbox.
204
+ * This read 'Test Live Item — Golden Retriever Puppy' at '₹1,500.00' —
205
+ * a fixture from the tester-sandbox catalogue that was removed with
206
+ * testerSandboxRefresh. The real row is live-golden-retriever-puppy at
207
+ * ₹25,000. The guest-gate half of this expectation was migrated
208
+ * correctly; only the literals were left pointing at the old catalogue.
209
+ */
210
+ "Heading 'Golden Retriever Puppy — 6 Months, Vaccinated'. A species line in the shape 'Dog (Golden Retriever) · male · 6mo'. BEFORE: the price read '₹25,000.00'; AFTER it reads 'Sign in to see price' and the digits 25,000 appear nowhere, and the 'Handling:' fee on the transport line is absent too. Delivery restrictions are stated rather than blank. The thumbnail strip carries a 'View video' entry alongside 'View image 1'. An 'Add to Cart' button is present.",
187
211
  endResult: "Nothing persists — read-only for a guest.",
188
212
  },
189
213
  "checklist-buying-product-detail-live-item-video-mandatory": {
@@ -221,7 +245,14 @@ export const authored = {
221
245
  "Read the note beside the 'Reserve Now' button.",
222
246
  ],
223
247
  expectedBehaviour: "A pre-order shows an estimated delivery date and takes a deposit rather than the full price, which is what distinguishes it from a standard listing that happens to be out of stock.",
224
- expectedUiState: "Heading 'Test Pre-order — Reserve Me!'. BEFORE: the price read '₹299.00' with a 'Reserve with ₹74.75' note; AFTER the price reads 'Sign in to see price', the deposit note is absent entirely, and neither 299 nor 74.75 appears anywhere on the page. An 'Estimated delivery:' label with an actual date beside it, not an empty value or the word 'undefined'. A 'Reserve Now' button is still present. Status badge 'In Production'.",
248
+ expectedUiState:
249
+ /*
250
+ * Same sandbox leftover as the live-item case above. The real row is
251
+ * preorder-beyblade-x-bx-08-wave: ₹799 at preOrderDepositPercent 25, so
252
+ * the deposit is ₹199.75 — figures a tester can check against the seed
253
+ * rather than against a fixture that no longer exists.
254
+ */
255
+ "Heading 'Beyblade X BX-08 Booster — Next Wave'. BEFORE: the price read '₹799.00' with a 'Reserve with ₹199.75' note; AFTER the price reads 'Sign in to see price', the deposit note is absent entirely, and neither 799 nor 199.75 appears anywhere on the page. An 'Estimated delivery:' label with an actual date beside it, not an empty value or the word 'undefined'. A 'Reserve Now' button is still present. Status badge 'In Production'.",
225
256
  endResult: "Nothing persists — read-only for a guest.",
226
257
  },
227
258
  "checklist-buying-product-detail-prizedraw-buy-reveal": {
@@ -261,7 +292,7 @@ export const authored = {
261
292
  groupTitle: "QA Set valkyrie-lineage",
262
293
  },
263
294
  expectedBehaviour: "The group panel appears on the parent and on the child, because membership is symmetric — a child knows its groupId and the parent knows its child slugs. A panel that renders only on the parent means the children's side of the link was never written. The group is BUILT by this case rather than read from the seed: no seeded product carries groupId, isGroupParent or groupChildSlugs, so there is no fixture to read.",
264
- expectedUiState: "The panel is present on both pages. Its expand control renders as a real triangle or chevron glyph, not a literal character like '▸' rendered at font-fallback size and not a missing box. Expanded, it lists 'Beyblade Burst B-01 Valkyrie' and 'Beyblade Burst B-135 Regalia Genesis' with image tiles. 'View whole group' opens a modal or drawer showing both members.",
295
+ expectedUiState: "The panel is present on both pages. Its expand control renders as a real triangle or chevron glyph, not a literal character like '▸' rendered at font-fallback size and not a missing box. Expanded, it lists 'Beyblade Burst B-01 Valkyrie' and 'Beyblade Burst B-59 Regalia Genesis' with image tiles. 'View whole group' opens a modal or drawer showing both members.",
265
296
  expectedData: { groupMemberCount: 2 },
266
297
  endResult: "Clear the group settings on both listings afterwards so the seeded catalogue is left as it was. A panel that survives on the parent but never appeared on the child is the failure.",
267
298
  },
@@ -47,14 +47,27 @@ export const authored = {
47
47
  startPage: "/user/orders",
48
48
  steps: [
49
49
  "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
50
- "Open /user/orders and find a delivered order whose item was marked final sale.",
50
+ "Open /user/orders and open any delivered order — every seeded listing is final sale, because ProductDocument.finalSale is ABSENT on all of them and absent means true.",
51
51
  "Confirm the order or its item shows the final-sale term.",
52
52
  "Open the return control and read every reason it offers.",
53
- "Select the not-received reason.",
53
+ /*
54
+ * 🛑 THE RENDERED LABEL, NOT THE ENUM VALUE OR A PARAPHRASE.
55
+ *
56
+ * These steps said "the not-received reason" and "the change-of-mind
57
+ * reason". The stored codes really are `not_received` and `changed_mind`
58
+ * — but RETURN_REASON_LABEL renders them as 'It never arrived' and
59
+ * 'I changed my mind', and neither paraphrase appears on screen.
60
+ *
61
+ * A tester scanning a select for "not received" does not find it. Same
62
+ * shape as the Decline/Reject button on buying/offers: the code value and
63
+ * the label are two different registers, and a step names the one the
64
+ * user clicks.
65
+ */
66
+ "Select 'It never arrived'.",
54
67
  "Type 'QA Return not-received — the parcel never arrived.' and submit.",
55
68
  "RELOAD the order and read its status.",
56
69
  ],
57
- inputs: { reason: "not received", note: "QA Return not-received — the parcel never arrived." },
70
+ inputs: { reason: "It never arrived", note: "QA Return not-received — the parcel never arrived." },
58
71
  expectedBehaviour: "Final sale does not override non-delivery. It is a statement about changing your mind, not about whether the seller has to deliver — so a not-received claim must be accepted on a final-sale item, and refusing it converts a delivery failure into a term the buyer never agreed to.",
59
72
  expectedUiState: "The not-received reason is offered and accepted despite the final-sale term. After the reload the order reads as return-requested. A blanket refusal citing final sale is the failure.",
60
73
  expectedData: { accepted: true },
@@ -65,13 +78,13 @@ export const authored = {
65
78
  startPage: "/user/orders",
66
79
  steps: [
67
80
  "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
68
- "Open /user/orders and find a different delivered final-sale order.",
81
+ "Open /user/orders and open a DIFFERENT delivered order — it is final sale too, for the same reason.",
69
82
  "Open the return control and read every reason offered.",
70
- "Select the change-of-mind reason.",
83
+ "Select 'I changed my mind'.",
71
84
  "Read what happens — whether it is refused, and what the message says.",
72
85
  "Read whether the message names FINAL SALE as the reason for the refusal.",
73
86
  ],
74
- inputs: { reason: "change of mind" },
87
+ inputs: { reason: "I changed my mind" },
75
88
  expectedBehaviour: "Change of mind is refused on a final-sale item and the refusal NAMES final sale. A generic 'returns are not available' leaves the buyer believing the item is broken or the site is; naming the term tells them it was disclosed before purchase and points them at where they agreed to it.",
76
89
  expectedUiState: "The refusal names final sale specifically rather than reading as a generic unavailability. The reason is either not offered at all for this item, or offered and refused with that explanation.",
77
90
  expectedData: { accepted: false },