@mohasinac/appkit 4.34.1 → 4.35.1
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/lottery/LotteryAdminEditView.js +32 -2
- package/dist/features/auth/actions/profile-actions.d.ts +14 -1
- package/dist/features/auth/actions/profile-actions.js +16 -2
- package/dist/features/tester/components/TesterChecklistStepRow.d.ts +9 -1
- package/dist/features/tester/components/TesterChecklistStepRow.js +31 -2
- package/dist/features/tester/components/TesterHubView.js +8 -0
- package/dist/features/tester/schemas/firestore.d.ts +109 -0
- package/dist/features/tester/schemas/firestore.js +14 -0
- package/dist/features/tester/seed-data/_money-flows.d.ts +23 -0
- package/dist/features/tester/seed-data/_money-flows.js +430 -0
- package/dist/features/tester/seed-data/authored/_types.d.ts +33 -0
- package/dist/features/tester/seed-data/authored/_types.js +35 -0
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +207 -0
- package/dist/features/tester/seed-data/authored/account-auth__signup-login.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/account-auth__signup-login.js +298 -0
- package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +33 -0
- package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +138 -0
- package/dist/features/tester/seed-data/authored/addresses__address-filters.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/addresses__address-filters.js +36 -0
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +75 -0
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +72 -0
- package/dist/features/tester/seed-data/authored/addresses__state-picker.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/addresses__state-picker.js +54 -0
- package/dist/features/tester/seed-data/authored/addresses__unban-request.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/addresses__unban-request.js +39 -0
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +226 -0
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +82 -0
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +177 -0
- package/dist/features/tester/seed-data/authored/admin__bundles.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__bundles.js +97 -0
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +232 -0
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +306 -0
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +158 -0
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +78 -0
- package/dist/features/tester/seed-data/authored/admin__content-deletes.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__content-deletes.js +261 -0
- package/dist/features/tester/seed-data/authored/admin__content-marketing.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__content-marketing.js +151 -0
- package/dist/features/tester/seed-data/authored/admin__coupons.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__coupons.js +214 -0
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +160 -0
- package/dist/features/tester/seed-data/authored/admin__media-watermark.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__media-watermark.js +104 -0
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +350 -0
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +105 -0
- package/dist/features/tester/seed-data/authored/admin__site-system.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__site-system.js +590 -0
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +235 -0
- package/dist/features/tester/seed-data/authored/admin__users-trust.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +569 -0
- package/dist/features/tester/seed-data/authored/buying__bidding.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__bidding.js +417 -0
- package/dist/features/tester/seed-data/authored/buying__browsing-search.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +653 -0
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +533 -0
- package/dist/features/tester/seed-data/authored/buying__buying-coupons.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__buying-coupons.js +379 -0
- package/dist/features/tester/seed-data/authored/buying__cart.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__cart.js +1171 -0
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +140 -0
- package/dist/features/tester/seed-data/authored/buying__my-orders.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +257 -0
- package/dist/features/tester/seed-data/authored/buying__offers.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__offers.js +1111 -0
- package/dist/features/tester/seed-data/authored/buying__order-status-lifecycle.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__order-status-lifecycle.js +252 -0
- package/dist/features/tester/seed-data/authored/buying__product-detail.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +387 -0
- package/dist/features/tester/seed-data/authored/buying__return-request.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__return-request.js +152 -0
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +242 -0
- package/dist/features/tester/seed-data/authored/buying__reviews.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__reviews.js +113 -0
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.js +283 -0
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +167 -0
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +90 -0
- package/dist/features/tester/seed-data/authored/buying__wishlist-history.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/buying__wishlist-history.js +344 -0
- package/dist/features/tester/seed-data/authored/community-support__public-profile.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/community-support__public-profile.js +53 -0
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +76 -0
- package/dist/features/tester/seed-data/authored/content-discovery__blog.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__blog.js +93 -0
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +149 -0
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +121 -0
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +207 -0
- package/dist/features/tester/seed-data/authored/content-discovery__events.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +318 -0
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +65 -0
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +147 -0
- package/dist/features/tester/seed-data/authored/content-discovery__search.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +332 -0
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +53 -0
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +52 -0
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +35 -0
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +141 -0
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +35 -0
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +95 -0
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +191 -0
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +158 -0
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +106 -0
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +88 -0
- package/dist/features/tester/seed-data/authored/design-ux__general-design.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +204 -0
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +301 -0
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +407 -0
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +214 -0
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +275 -0
- package/dist/features/tester/seed-data/authored/index.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/index.js +237 -0
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +102 -0
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +50 -0
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +66 -0
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +115 -0
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +136 -0
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +69 -0
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +263 -0
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +102 -0
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +234 -0
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +126 -0
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +255 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +59 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +88 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +51 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +35 -0
- package/dist/features/tester/seed-data/authored/selling__become-seller.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +94 -0
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +107 -0
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +285 -0
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +326 -0
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +136 -0
- package/dist/features/tester/seed-data/authored/selling__media-limits.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +128 -0
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +167 -0
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +117 -0
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +88 -0
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +151 -0
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +105 -0
- package/dist/features/tester/seed-data/authored/selling__seller-guide.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +33 -0
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +347 -0
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +166 -0
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +89 -0
- package/dist/features/tester/seed-data/authored/selling__seller-orders.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +195 -0
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +180 -0
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +164 -0
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +116 -0
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +111 -0
- package/dist/features/tester/seed-data/authored/seo__og-images.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/seo__og-images.js +91 -0
- package/dist/features/tester/seed-data/authored/seo__page-metadata.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +122 -0
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +105 -0
- package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +3 -0
- package/dist/features/tester/seed-data/carts-tester-seed-data.js +70 -0
- package/dist/features/tester/seed-data/index.d.ts +1 -0
- package/dist/features/tester/seed-data/index.js +1 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +744 -1
- package/dist/seed/carousel-slides-seed-data.js +1 -1
- package/dist/seed/cart-seed-data.js +11 -0
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.js +2 -2
- package/dist/seed/notifications-seed-data.js +5 -1
- package/dist/seed/products-standard-seed-data.js +2 -2
- package/dist/seed/reviews-seed-data.js +6 -1
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +752 -746
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Authored six-part procedures for the buying/offers checklist page.
|
|
3
|
+
* WHAT: 66 case(s), keyed by full checklist id.
|
|
4
|
+
*
|
|
5
|
+
* Written by hand, case by case, against the label each one states. There is no
|
|
6
|
+
* generator: the authoring scripts were deleted once it was clear they were
|
|
7
|
+
* scaffolding around work that is simply writing.
|
|
8
|
+
*
|
|
9
|
+
* 🛑 THIS PAGE IS NOT ONLY ABOUT OFFERS. Its label set spans the offer lifecycle,
|
|
10
|
+
* the shared status-history primitive, the form-bar tier, validation across six
|
|
11
|
+
* editors, and the bid-detail portals. They sit together because they landed in
|
|
12
|
+
* one wave, and splitting them now would break every checklist id.
|
|
13
|
+
*
|
|
14
|
+
* 🛑 THE OFFER CHAIN IS THE SPINE. A counter creates a NEW offer document per
|
|
15
|
+
* round, linked backwards and forwards and denormalised to the chain's root so a
|
|
16
|
+
* row can say "Round 2" with no extra reads. `superseded` is a RENDER KEY, never a
|
|
17
|
+
* status: a withdrawn offer carrying a supersededBy link reads neutral, one
|
|
18
|
+
* without reads negative. Making it an eighth status would add a filter chip
|
|
19
|
+
* matching zero stored rows.
|
|
20
|
+
*
|
|
21
|
+
* @tag domain:tester
|
|
22
|
+
* @tag layer:seed
|
|
23
|
+
* @tag pattern:none
|
|
24
|
+
* @tag access:server-only
|
|
25
|
+
* @tag consumers:seed-data/authored/index.ts
|
|
26
|
+
* @tag sideEffects:none
|
|
27
|
+
*/
|
|
28
|
+
export const authored = {
|
|
29
|
+
"checklist-buying-offers-make-offer": {
|
|
30
|
+
roles: ["buyer", "seller"],
|
|
31
|
+
startPage: "/products/product-beyblade-original-driger-v",
|
|
32
|
+
steps: [
|
|
33
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
34
|
+
"Open /products/product-beyblade-original-driger-v, listed at ₹1,799.",
|
|
35
|
+
"Click 'Make Offer' and type 1450 as the amount and 'QA Offer make-offer' as the note.",
|
|
36
|
+
"Submit and read the confirmation.",
|
|
37
|
+
"Open /user/offers and find the offer.",
|
|
38
|
+
"Sign out and sign in as tyson@beybladearena.in / TempPass123!.",
|
|
39
|
+
"Open /store and read the notification list for a new-offer entry.",
|
|
40
|
+
],
|
|
41
|
+
inputs: { productId: "product-beyblade-original-driger-v", listedPrice: 1799, offerAmount: 1450 },
|
|
42
|
+
expectedBehaviour: "The offer is created against the product and its store, and the seller is notified. The notification's audience is a property of the EVENT rather than the person — the same account is a buyer elsewhere, so it must land in the store portal.",
|
|
43
|
+
expectedUiState: "The offer appears in /user/offers at ₹1,450 against a listed ₹1,799. The seller's notification names the offer and opens the store's offers list rather than the buyer's.",
|
|
44
|
+
endResult: "One pending offer exists; later cases read it.",
|
|
45
|
+
},
|
|
46
|
+
"checklist-buying-offers-seller-sees-and-can-act-on-offers": {
|
|
47
|
+
roles: ["seller"],
|
|
48
|
+
startPage: "/store/offers",
|
|
49
|
+
steps: [
|
|
50
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
51
|
+
"Open /store/offers and find the QA Offer make-offer entry.",
|
|
52
|
+
"Read the row — product, offered amount, listed price, status and round.",
|
|
53
|
+
"Open the row action menu and read every action offered.",
|
|
54
|
+
"Use Counter, then cancel out of the form without submitting.",
|
|
55
|
+
"Use Decline on a DIFFERENT pending offer and read what happens.",
|
|
56
|
+
"Reload and read that offer's status.",
|
|
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.",
|
|
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
|
+
endResult: "One offer is declined; QA Offer make-offer is untouched.",
|
|
61
|
+
},
|
|
62
|
+
"checklist-buying-offers-buyer-sees-offer-status-changes": {
|
|
63
|
+
roles: ["buyer", "seller"],
|
|
64
|
+
startPage: "/user/offers",
|
|
65
|
+
steps: [
|
|
66
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123! and open /user/offers, noting the QA offer's status.",
|
|
67
|
+
"Sign out and sign in as tyson@beybladearena.in / TempPass123!.",
|
|
68
|
+
"Counter that offer at 1600 with a note and submit.",
|
|
69
|
+
"Sign back in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
70
|
+
"Open /user/offers and read the offer's status and amount.",
|
|
71
|
+
"Read whether the seller's note is shown.",
|
|
72
|
+
"Check the notification list for an offer-responded entry.",
|
|
73
|
+
],
|
|
74
|
+
inputs: { counterAmount: 1600 },
|
|
75
|
+
expectedBehaviour: "The buyer sees the seller's response and its note. A counter creates a new round rather than editing the original, so the buyer's list must show the current round rather than the amount they first offered.",
|
|
76
|
+
expectedUiState: "The buyer's list shows the countered state at ₹1,600 with the seller's note, and a notification points back to the offers list.",
|
|
77
|
+
endResult: "The offer is at round 2, countered by the seller.",
|
|
78
|
+
},
|
|
79
|
+
"checklist-buying-offers-offer-seller-can-read-before-acting": {
|
|
80
|
+
roles: ["seller"],
|
|
81
|
+
startPage: "/store/offers",
|
|
82
|
+
steps: [
|
|
83
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
84
|
+
"Open /store/offers and find a pending offer.",
|
|
85
|
+
"WITHOUT using Accept, Decline or Counter, find a way to open its details.",
|
|
86
|
+
"Read the buyer's note, the listed price, the offered amount and the round.",
|
|
87
|
+
"Read any earlier rounds in the same negotiation.",
|
|
88
|
+
"Return to the list and check the same is reachable from the row.",
|
|
89
|
+
],
|
|
90
|
+
expectedBehaviour: "The seller reads the offer before deciding on it. A row of pure mutations lets a seller accept a price without seeing the note explaining it — the same acting-blind shape as the moderation queues.",
|
|
91
|
+
expectedUiState: "A view panel opens showing the note, both prices, the round and any earlier rounds, before any action is taken.",
|
|
92
|
+
endResult: "Read-only; decide nothing.",
|
|
93
|
+
},
|
|
94
|
+
"checklist-buying-offers-counter-offer-has-a-form": {
|
|
95
|
+
roles: ["seller"],
|
|
96
|
+
startPage: "/store/offers",
|
|
97
|
+
steps: [
|
|
98
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
99
|
+
"Open /store/offers and choose Counter on a pending offer.",
|
|
100
|
+
"Read what opens.",
|
|
101
|
+
"Check it asks for an amount rather than being a bare confirmation.",
|
|
102
|
+
"Submit with the amount empty and read where the error appears.",
|
|
103
|
+
"Type an amount and a note, submit, and reload.",
|
|
104
|
+
],
|
|
105
|
+
expectedBehaviour: "Countering requires an amount — a counter is a new price, and a confirmation dialog with no field cannot express one. The note is optional; the amount is not.",
|
|
106
|
+
expectedUiState: "A form opens with an amount field and an optional note. The empty submit marks the amount field. The submitted counter appears as a new round.",
|
|
107
|
+
endResult: "One offer has been countered.",
|
|
108
|
+
},
|
|
109
|
+
"checklist-buying-offers-offer-accept-checkout-charges-agreed-price": {
|
|
110
|
+
roles: ["buyer", "seller"],
|
|
111
|
+
startPage: "/store/offers",
|
|
112
|
+
steps: [
|
|
113
|
+
"Sign in as tyson@beybladearena.in / TempPass123! and Accept the QA offer on product-beyblade-original-driger-v.",
|
|
114
|
+
"Sign out and sign in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
115
|
+
"Open /cart and click the 'Accepted Offers' tab.",
|
|
116
|
+
"Read the line's price and compare it against the listing's ₹1,799.",
|
|
117
|
+
"Proceed to checkout and read the amount at every step, including the payment step.",
|
|
118
|
+
"Place the order with Cash on Delivery.",
|
|
119
|
+
"Open the order and read the price recorded.",
|
|
120
|
+
],
|
|
121
|
+
inputs: { listedPrice: 1799, agreedPrice: 1450 },
|
|
122
|
+
expectedBehaviour: "The agreed price is charged, everywhere. The rule that decides a line's price knows about locked prices — but three hand-written copies of that expression elsewhere reproduced only the bundle branch and omitted the locked one, so the gateway captured the LIST price while the cart showed the agreed one. The two figures never appear on the same screen.",
|
|
123
|
+
expectedUiState: "The cart line, every checkout step, the payment amount and the recorded order all read ₹1,450. Any surface showing ₹1,799 is the copied-rule failure.",
|
|
124
|
+
expectedData: { chargedPrice: 1450 },
|
|
125
|
+
endResult: "One order exists at the agreed price.",
|
|
126
|
+
},
|
|
127
|
+
"checklist-buying-offers-offer-flips-to-paid-after-order": {
|
|
128
|
+
roles: ["buyer"],
|
|
129
|
+
startPage: "/user/offers",
|
|
130
|
+
steps: [
|
|
131
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
132
|
+
"Open /user/offers and read the status of the offer just paid for.",
|
|
133
|
+
"Open /cart and check the Accepted Offers tab no longer holds that line.",
|
|
134
|
+
"Try to reach checkout for that offer again.",
|
|
135
|
+
"Reload /user/offers and read the status.",
|
|
136
|
+
"Check the order links back to the offer.",
|
|
137
|
+
],
|
|
138
|
+
expectedBehaviour: "Completing the order flips the offer to paid and clears its cart line. That terminal status had no server-side writer at all for a long time, so an accepted offer stayed re-orderable indefinitely — the buyer could check out the same negotiated price repeatedly.",
|
|
139
|
+
expectedUiState: "The offer reads paid after the reload and its line is gone from the Accepted Offers tab. It cannot be added to the cart again.",
|
|
140
|
+
expectedData: { offerStatus: "paid" },
|
|
141
|
+
endResult: "The offer is terminal and cannot be re-ordered.",
|
|
142
|
+
},
|
|
143
|
+
"checklist-buying-offers-offer-expired-accepted-cannot-checkout": {
|
|
144
|
+
roles: ["buyer"],
|
|
145
|
+
startPage: "/user/offers",
|
|
146
|
+
steps: [
|
|
147
|
+
"Sign in as tester@letitrip.in / TempPass123!.",
|
|
148
|
+
"Open /user/offers and find an accepted offer past its checkout deadline.",
|
|
149
|
+
"Read its status and any deadline shown.",
|
|
150
|
+
"Open /cart and check the Accepted Offers tab for that line.",
|
|
151
|
+
"Attempt to check out from that lane.",
|
|
152
|
+
"Read the notification list for an expiry entry.",
|
|
153
|
+
],
|
|
154
|
+
expectedBehaviour: "An accepted offer lapses at its checkout deadline and its cart line is cleared. Clearing matters beyond tidiness: a leftover line keeps the offer lane non-empty, and that lane outranks the standard one, so the buyer's entire cart stays blocked.",
|
|
155
|
+
expectedUiState: "The offer reads expired, the Accepted Offers tab is empty of it, and checkout is refused or unavailable for it. The buyer has an expiry notification.",
|
|
156
|
+
endResult: "Read-only. If no lapsed offer exists, answer null rather than waiting out a deadline.",
|
|
157
|
+
},
|
|
158
|
+
"checklist-buying-offers-offer-history-timeline-renders": {
|
|
159
|
+
roles: ["buyer"],
|
|
160
|
+
startPage: "/user/offers",
|
|
161
|
+
steps: [
|
|
162
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
163
|
+
"Open /user/offers and open an offer that has changed status at least twice.",
|
|
164
|
+
"Read its timeline from the top.",
|
|
165
|
+
"Check each entry names what changed, who changed it and when.",
|
|
166
|
+
"Check phases the offer has not reached render as upcoming.",
|
|
167
|
+
"Check no entry contains a name or an email address.",
|
|
168
|
+
],
|
|
169
|
+
expectedBehaviour: "Offers write history through one status choke point, so every transition is recorded with an actor and a trigger. The wrapper renders a KNOWN phase sequence — made, countered, accepted, paid — so unreached phases appear as upcoming rather than being absent.",
|
|
170
|
+
expectedUiState: "Entries name the change, an actor role and a timestamp, in order. Unreached phases show as upcoming. No entry carries a personal name or email.",
|
|
171
|
+
endResult: "Read-only; nothing persists.",
|
|
172
|
+
},
|
|
173
|
+
"checklist-buying-offers-offer-history-legacy-no-fabricated-date": {
|
|
174
|
+
roles: ["buyer"],
|
|
175
|
+
startPage: "/user/offers",
|
|
176
|
+
steps: [
|
|
177
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
178
|
+
"Open /user/offers and find an offer created before history recording existed — one that already reads expired or declined.",
|
|
179
|
+
"Open it and read its timeline.",
|
|
180
|
+
"Check a timeline renders at all rather than an empty panel.",
|
|
181
|
+
"Read the date against the Expired step specifically.",
|
|
182
|
+
"Check it is an em-dash rather than a plausible date.",
|
|
183
|
+
],
|
|
184
|
+
expectedBehaviour: "An offer with no recorded history still renders its phases, derived from the scalar dates it does have. A phase with no date shows an em-dash — not the current time, and not a proxy field: the record's updated stamp means 'last write of any kind' and the expiry field is a DEADLINE, not the moment it lapsed.",
|
|
185
|
+
expectedUiState: "The timeline renders. The Expired step shows an em-dash rather than a date. A plausible timestamp here is the fabrication failure and cannot be told from a real one later.",
|
|
186
|
+
endResult: "Read-only; nothing persists.",
|
|
187
|
+
},
|
|
188
|
+
"checklist-buying-offers-offer-chain-walks-three-rounds": {
|
|
189
|
+
roles: ["buyer", "seller"],
|
|
190
|
+
startPage: "/products/product-beyblade-metal-storm-pegasus",
|
|
191
|
+
steps: [
|
|
192
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123! and make an offer of 1000 on product-beyblade-metal-storm-pegasus, listed at ₹1,299.",
|
|
193
|
+
"Sign in as tyson@beybladearena.in / TempPass123! and counter at 1200.",
|
|
194
|
+
"Sign back in as the buyer and counter again at 1100.",
|
|
195
|
+
"Open that offer's detail and read the whole negotiation.",
|
|
196
|
+
"Check the rounds read oldest first as ONE story.",
|
|
197
|
+
"Read how rounds 1 and 2 are labelled.",
|
|
198
|
+
"Check neither reads as Withdrawn.",
|
|
199
|
+
],
|
|
200
|
+
inputs: { listedPrice: 1299, round1: 1000, round2: 1200, round3: 1100 },
|
|
201
|
+
expectedBehaviour: "Each round is its own document, linked both ways and denormalised to the chain root. Superseded is a RENDER KEY rather than a status: a withdrawn offer carrying a supersededBy link reads neutral because the buyer countered rather than walking away. Making it an eighth status would add a filter chip matching zero stored rows.",
|
|
202
|
+
expectedUiState: "Three rounds render oldest first as one negotiation. Rounds 1 and 2 read Superseded, not Withdrawn. Round 3 is the live one at ₹1,100.",
|
|
203
|
+
expectedData: { roundCount: 3 },
|
|
204
|
+
endResult: "A three-round chain exists. Withdraw or decline it afterwards.",
|
|
205
|
+
},
|
|
206
|
+
"checklist-buying-offers-offer-detail-opens-on-fresh-data": {
|
|
207
|
+
roles: ["seller"],
|
|
208
|
+
startPage: "/store/offers",
|
|
209
|
+
steps: [
|
|
210
|
+
"Sign in as tyson@beybladearena.in / TempPass123! in window A and open /store/offers.",
|
|
211
|
+
"In window B, sign in as the buyer and counter one of those offers.",
|
|
212
|
+
"In window A, WITHOUT reloading the list, open that offer's detail.",
|
|
213
|
+
"Read the amount and status shown.",
|
|
214
|
+
"Compare against what window B just submitted.",
|
|
215
|
+
"Reload window A's list and open it again.",
|
|
216
|
+
],
|
|
217
|
+
expectedBehaviour: "Opening a row fetches that offer rather than rendering whatever the list last cached. A detail served from a stale list shows the seller a superseded amount, and accepting from that view accepts a price that no longer stands.",
|
|
218
|
+
expectedUiState: "The detail shows the countered amount even though the list behind it is stale. A detail matching the stale list rather than the record is the failure.",
|
|
219
|
+
endResult: "Read-only; accept nothing.",
|
|
220
|
+
},
|
|
221
|
+
"checklist-buying-offers-admin-offer-cancel-requires-reason": {
|
|
222
|
+
roles: ["admin", "buyer"],
|
|
223
|
+
startPage: "/admin/offers",
|
|
224
|
+
steps: [
|
|
225
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
226
|
+
"Open the admin offers surface and open a pending offer's row menu.",
|
|
227
|
+
"Choose Cancel and submit with the reason empty.",
|
|
228
|
+
"Type 'Too short' and submit.",
|
|
229
|
+
"Type 'QA Cancel — cancelled by the tester checklist for review.' and submit.",
|
|
230
|
+
"RELOAD and read the offer's status and its history.",
|
|
231
|
+
"Sign in as the buyer and read their notification for the reason.",
|
|
232
|
+
"Open /cart and check no locked line remains for that offer.",
|
|
233
|
+
],
|
|
234
|
+
inputs: { reason: "QA Cancel — cancelled by the tester checklist for review." },
|
|
235
|
+
expectedBehaviour: "Cancel is a reasoned escalation: the reason is required, recorded in both the audit log and the offer's own history, and quoted to the buyer. The handler must remove the cart line FIRST — a leftover locked line keeps the buyer's offer lane non-empty, and that lane outranks the standard one, so their whole cart stays blocked.",
|
|
236
|
+
expectedUiState: "Empty and short reasons are refused on the field. After the reload the offer is cancelled with the reason in its history. The buyer's notification quotes it and their cart holds no line for it.",
|
|
237
|
+
endResult: "One offer is cancelled with its reason recorded.",
|
|
238
|
+
},
|
|
239
|
+
"checklist-buying-offers-admin-cannot-accept-or-counter": {
|
|
240
|
+
roles: ["admin"],
|
|
241
|
+
startPage: "/admin/offers",
|
|
242
|
+
steps: [
|
|
243
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
244
|
+
"Open the admin offers surface and open a pending offer's row menu.",
|
|
245
|
+
"Read every action offered and write the list down.",
|
|
246
|
+
"Check for Accept, Counter or Decline.",
|
|
247
|
+
"Select several rows and read any bulk action bar that appears.",
|
|
248
|
+
"Read whether a bulk cancel is offered.",
|
|
249
|
+
],
|
|
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.",
|
|
252
|
+
expectedData: { adminActions: 2 },
|
|
253
|
+
endResult: "Read-only; act on nothing.",
|
|
254
|
+
},
|
|
255
|
+
"checklist-buying-offers-store-still-accepts": {
|
|
256
|
+
roles: ["seller"],
|
|
257
|
+
startPage: "/store/offers",
|
|
258
|
+
steps: [
|
|
259
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
260
|
+
"Open /store/offers and open a pending offer's row menu.",
|
|
261
|
+
"Read every action offered.",
|
|
262
|
+
"Check Accept, Counter and Decline are all present.",
|
|
263
|
+
"Use Accept on one and reload.",
|
|
264
|
+
"Read the offer's status and check the buyer's cart gains a line for it.",
|
|
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.",
|
|
267
|
+
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
|
+
endResult: "One offer is accepted.",
|
|
269
|
+
},
|
|
270
|
+
"checklist-buying-offers-nav-item-invalid-href-inline": {
|
|
271
|
+
roles: ["admin"],
|
|
272
|
+
startPage: "/admin/navigation",
|
|
273
|
+
steps: [
|
|
274
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
275
|
+
"Open /admin/navigation and create a nav item labelled 'QA Nav invalid-href'.",
|
|
276
|
+
"Type /this-page-does-not-exist-qa as its destination and save.",
|
|
277
|
+
"Read where any error appears.",
|
|
278
|
+
"Type not a url as the destination and save, reading where the error appears.",
|
|
279
|
+
"Type /about and save.",
|
|
280
|
+
"Delete the item.",
|
|
281
|
+
],
|
|
282
|
+
inputs: { badHref: "/this-page-does-not-exist-qa", goodHref: "/about" },
|
|
283
|
+
expectedBehaviour: "A bad destination is refused ON THE FIELD rather than by a banner. A nav entry pointing at nothing is a dead link on every page that renders the nav, and a banner leaves the admin guessing which field caused it.",
|
|
284
|
+
expectedUiState: "Both bad destinations produce an error attached to the destination field. /about is accepted. A generic banner, or a silent save of a dead link, are both failures.",
|
|
285
|
+
endResult: "The item is deleted by the final step.",
|
|
286
|
+
},
|
|
287
|
+
"checklist-buying-offers-homepage-section-invalid-config-inline": {
|
|
288
|
+
roles: ["admin"],
|
|
289
|
+
startPage: "/admin",
|
|
290
|
+
steps: [
|
|
291
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
292
|
+
"Open the homepage sections editor and start a new section.",
|
|
293
|
+
"Type malformed JSON into its config field and save.",
|
|
294
|
+
"Read where the error appears.",
|
|
295
|
+
"Set the order to -1 and save, reading where that error appears.",
|
|
296
|
+
"Correct both and save.",
|
|
297
|
+
"Delete the section.",
|
|
298
|
+
],
|
|
299
|
+
inputs: { badOrder: -1 },
|
|
300
|
+
expectedBehaviour: "Malformed config and a negative order are both refused inline on their own fields. A saved section with unparseable config renders as nothing on the public homepage — and because each section fails silently to nothing by design, that is invisible rather than loud.",
|
|
301
|
+
expectedUiState: "Both errors attach to their fields. The corrected section saves. A section that saves with bad config is the failure, and its symptom is a missing homepage row.",
|
|
302
|
+
endResult: "The section is deleted by the final step.",
|
|
303
|
+
},
|
|
304
|
+
"checklist-buying-offers-classified-keeps-meetup-city": {
|
|
305
|
+
roles: ["seller"],
|
|
306
|
+
startPage: "/store/classified",
|
|
307
|
+
steps: [
|
|
308
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
309
|
+
"Open a classified listing's editor and note its meetup city.",
|
|
310
|
+
"Change ONLY the price and save.",
|
|
311
|
+
"RELOAD and read the meetup city.",
|
|
312
|
+
"Change the city to Pune, save, RELOAD, and read it.",
|
|
313
|
+
"Open the public page and read the city there.",
|
|
314
|
+
"Restore the original values.",
|
|
315
|
+
],
|
|
316
|
+
inputs: { city: "Pune" },
|
|
317
|
+
expectedBehaviour: "The meetup city survives an unrelated edit and reaches the public page. A form that cannot name a field sends undefined for it, and undefined overwrites — which is how a type-specific field disappears on every save that never touched it.",
|
|
318
|
+
expectedUiState: "After the price edit the city is unchanged. The changed city persists and appears publicly. A blank city after an unrelated edit is the failure.",
|
|
319
|
+
endResult: "The listing is restored.",
|
|
320
|
+
},
|
|
321
|
+
"checklist-buying-offers-live-item-keeps-species-and-cites": {
|
|
322
|
+
roles: ["seller"],
|
|
323
|
+
startPage: "/store/live",
|
|
324
|
+
steps: [
|
|
325
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
326
|
+
"Open a live listing's editor and note its species, transport method, permitted jurisdictions and any CITES reference.",
|
|
327
|
+
"Change ONLY the price and save.",
|
|
328
|
+
"RELOAD and read all four fields.",
|
|
329
|
+
"Change the jurisdiction list to a single region, save, RELOAD, and read it.",
|
|
330
|
+
"Open the public page and read the restrictions shown.",
|
|
331
|
+
"Restore the original values.",
|
|
332
|
+
],
|
|
333
|
+
expectedBehaviour: "All four survive an unrelated edit. The jurisdiction list matters most: it gates who may lawfully receive the animal or plant, so a list that saves empty silently REMOVES the restriction rather than failing loudly — a safety consequence rather than a formatting one.",
|
|
334
|
+
expectedUiState: "After the price edit all four fields are intact, jurisdictions included. The public page states the restriction. An emptied jurisdiction list is the finding.",
|
|
335
|
+
endResult: "The listing is restored.",
|
|
336
|
+
},
|
|
337
|
+
"checklist-buying-offers-digital-code-keeps-delivery-method": {
|
|
338
|
+
roles: ["seller"],
|
|
339
|
+
startPage: "/store/digital-codes",
|
|
340
|
+
steps: [
|
|
341
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
342
|
+
"Open a digital-code listing's editor and note its delivery method and pool size.",
|
|
343
|
+
"Change ONLY the price and save.",
|
|
344
|
+
"RELOAD and read both.",
|
|
345
|
+
"Change the delivery method, save, RELOAD, and read it.",
|
|
346
|
+
"Open the public page and read the availability shown.",
|
|
347
|
+
"Restore the original values.",
|
|
348
|
+
],
|
|
349
|
+
expectedBehaviour: "The delivery method and pool size survive an unrelated edit, and the public availability follows the NESTED pool count rather than a separate stock figure — a listing with stock remaining and an empty pool must read as unavailable.",
|
|
350
|
+
expectedUiState: "After the price edit both fields are intact. The changed method persists. Public availability reflects the pool.",
|
|
351
|
+
endResult: "The listing is restored.",
|
|
352
|
+
},
|
|
353
|
+
"checklist-buying-offers-prize-draw-keeps-entry-price": {
|
|
354
|
+
roles: ["seller"],
|
|
355
|
+
startPage: "/store/prize-draws",
|
|
356
|
+
steps: [
|
|
357
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
358
|
+
"Open a prize draw's editor and note its price per entry and maximum entries.",
|
|
359
|
+
"Change ONLY the title and save.",
|
|
360
|
+
"RELOAD and read both figures.",
|
|
361
|
+
"Change the per-entry price, save, RELOAD, and read it.",
|
|
362
|
+
"Open the public page and read the per-entry price offered.",
|
|
363
|
+
"Restore the original values.",
|
|
364
|
+
],
|
|
365
|
+
expectedBehaviour: "The per-entry price and entry cap survive an unrelated edit and reach the public purchase panel. A per-entry price that silently reverts is a pricing error on a page where the buyer is agreeing to that exact figure.",
|
|
366
|
+
expectedUiState: "After the title edit both figures are intact. The changed price persists and shows publicly as a per-entry price.",
|
|
367
|
+
endResult: "The draw is restored.",
|
|
368
|
+
},
|
|
369
|
+
"checklist-buying-offers-per-type-fields-survive-EDIT-too": {
|
|
370
|
+
roles: ["seller"],
|
|
371
|
+
startPage: "/store/classified",
|
|
372
|
+
steps: [
|
|
373
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
374
|
+
"Open a classified listing's editor and write down its meetup city, contact method and negotiable flag.",
|
|
375
|
+
"Change ONLY the TITLE and save.",
|
|
376
|
+
"RELOAD and read all three type-specific values.",
|
|
377
|
+
"Repeat on a live listing: change only its title and re-read its species and jurisdictions.",
|
|
378
|
+
"Repeat on a digital-code listing: change only its title and re-read its delivery method and pool.",
|
|
379
|
+
"Repeat on a prize draw: change only its title and re-read its per-entry price.",
|
|
380
|
+
],
|
|
381
|
+
expectedBehaviour: "A create-time fix does not cover the update path. Where the create handler applies a transform the update handler does not repeat, the listing saves correctly and then loses its per-type block the first time anyone edits something unrelated — so this case edits the one field every type has in common and re-reads the fields only that type has.",
|
|
382
|
+
expectedUiState: "After each title-only edit, every type-specific value is unchanged on all four listing types. A per-type block emptied by a title edit is the failure, and it is invisible without the reload.",
|
|
383
|
+
expectedData: { typeFieldsLostOnEdit: 0 },
|
|
384
|
+
endResult: "All four listings keep their type-specific fields. Restore any title that was changed.",
|
|
385
|
+
},
|
|
386
|
+
"checklist-buying-offers-notification-email-is-a-real-email": {
|
|
387
|
+
roles: ["buyer"],
|
|
388
|
+
startPage: "/user/settings",
|
|
389
|
+
steps: [
|
|
390
|
+
"Sign in as an account whose inbox you can open and confirm order emails are on.",
|
|
391
|
+
"Place an order to trigger a confirmation notification.",
|
|
392
|
+
"Open the inbox and wait up to five minutes.",
|
|
393
|
+
"Read the email's structure — heading, lead line and any button.",
|
|
394
|
+
"Compare it against the in-app notification's one-line message.",
|
|
395
|
+
"Click the button and read where it lands.",
|
|
396
|
+
],
|
|
397
|
+
expectedBehaviour: "The email renders a per-type template rather than wrapping the one-line message in a bare paragraph. For a long time not one caller supplied email HTML, so every type shipped the same unstyled sentence — the template is now filled in by the dispatcher rather than by each caller.",
|
|
398
|
+
expectedUiState: "The email has a heading, a lead line and a button, styled rather than plain. Its button opens the right page on the live site. A single bare paragraph is the failure.",
|
|
399
|
+
endResult: "One order and one email exist.",
|
|
400
|
+
},
|
|
401
|
+
"checklist-buying-offers-notification-click-lands-on-the-right-page": {
|
|
402
|
+
roles: ["buyer", "seller"],
|
|
403
|
+
startPage: "/user/notifications",
|
|
404
|
+
steps: [
|
|
405
|
+
"Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
|
|
406
|
+
"Open /user/notifications and click an ORDER notification, reading where it lands.",
|
|
407
|
+
"Go back and click an OFFER notification, reading where it lands.",
|
|
408
|
+
"Go back and click a BID notification, reading where it lands.",
|
|
409
|
+
"Sign out and sign in as tyson@beybladearena.in / TempPass123!.",
|
|
410
|
+
"Open the store's notifications and click an offer-received entry.",
|
|
411
|
+
"Read which portal it opens.",
|
|
412
|
+
],
|
|
413
|
+
expectedBehaviour: "The destination is resolved from the related record's type and id, and the AUDIENCE from the event. Bids, offers and reviews have no per-record page in any role, so those resolve to their list — a real destination, where a fabricated per-record URL would 404. A seller's offer-received notification opens the store portal even though that account is also a buyer.",
|
|
414
|
+
expectedUiState: "The order notification opens that order. Offer and bid notifications open their lists. The seller's entry opens /store/offers rather than /user/offers. Nothing 404s.",
|
|
415
|
+
expectedData: { notFoundCount: 0 },
|
|
416
|
+
endResult: "Read-only; nothing persists.",
|
|
417
|
+
},
|
|
418
|
+
"checklist-buying-offers-admin-can-filter-every-notification-type": {
|
|
419
|
+
roles: ["admin"],
|
|
420
|
+
startPage: "/admin/notifications",
|
|
421
|
+
steps: [
|
|
422
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
423
|
+
"Open /admin/notifications and read every type filter offered.",
|
|
424
|
+
"Write the list down and count it.",
|
|
425
|
+
"Compare against the full notification type list the system uses.",
|
|
426
|
+
"Select each filter and note any returning nothing.",
|
|
427
|
+
"For each empty one, check the unfiltered list for rows of that type.",
|
|
428
|
+
"Look specifically for offer-received and payment-review among the filters.",
|
|
429
|
+
],
|
|
430
|
+
expectedBehaviour: "Every real type is filterable and no invented one is offered. The filters were built from a nine-value copy of a twenty-seven-value union — eighteen types were unfilterable, and the copy that was PUBLIC was the seed factory's guess, which invented four types no notification has ever had.",
|
|
431
|
+
expectedUiState: "The filter list matches the real union, including offer-received and payment-review. No filter names a type nothing produces. Each returns its own rows or is empty with none unfiltered either.",
|
|
432
|
+
endResult: "Read-only; nothing persists beyond the URL.",
|
|
433
|
+
},
|
|
434
|
+
"checklist-buying-offers-admin-can-allowlist-every-type-per-channel": {
|
|
435
|
+
roles: ["admin"],
|
|
436
|
+
startPage: "/admin/site",
|
|
437
|
+
steps: [
|
|
438
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
439
|
+
"Open the notifications tab of Site Settings and read every type listed.",
|
|
440
|
+
"Count them against the real type list.",
|
|
441
|
+
"Find offer-received and allow-list it for email, then save.",
|
|
442
|
+
"Find payment-review and do the same.",
|
|
443
|
+
"RELOAD and check both settings held.",
|
|
444
|
+
"Restore both to their original states.",
|
|
445
|
+
],
|
|
446
|
+
expectedBehaviour: "Every type can be allow-listed per channel. Two types could not be configured for ANY channel because the allow-list was built from the short copy — so their emails were unreachable by configuration entirely.",
|
|
447
|
+
expectedUiState: "Both types appear and can be allow-listed for email, and both settings survive the reload. A type absent from this tab cannot be configured at all.",
|
|
448
|
+
endResult: "Both settings are restored.",
|
|
449
|
+
},
|
|
450
|
+
"checklist-buying-offers-notification-optout-is-honoured-for-emi-and-payment": {
|
|
451
|
+
roles: ["buyer"],
|
|
452
|
+
startPage: "/user/settings",
|
|
453
|
+
steps: [
|
|
454
|
+
"Sign in as an account whose inbox you can open.",
|
|
455
|
+
"Open /user/settings, go to Notifications, and turn email OFF for order updates.",
|
|
456
|
+
"Save and reload to confirm it held.",
|
|
457
|
+
"Trigger an EMI-installment or payment-review notification for that account.",
|
|
458
|
+
"Open /user/notifications and confirm the in-app entry arrived.",
|
|
459
|
+
"Open the inbox and wait five minutes.",
|
|
460
|
+
"Turn the setting back on.",
|
|
461
|
+
],
|
|
462
|
+
expectedBehaviour: "The opt-out suppresses email and nothing else. It depends on a COMPLETE map from type to preference key — three types were missing, so the key was undefined, the opt-out check never ran, and mail kept arriving while the toggle read as honoured. EMI and payment-review were two of the three.",
|
|
463
|
+
expectedUiState: "The in-app notification arrives. NO email arrives for it. An email arriving with the toggle off is the exact failure, and it is invisible from the settings page.",
|
|
464
|
+
expectedData: { emailsReceived: 0 },
|
|
465
|
+
endResult: "The preference is restored.",
|
|
466
|
+
},
|
|
467
|
+
"checklist-buying-offers-ticket-resolution-timestamp-stamped": {
|
|
468
|
+
roles: ["admin"],
|
|
469
|
+
startPage: "/admin/support",
|
|
470
|
+
steps: [
|
|
471
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
472
|
+
"Open the admin support tickets surface and open an open ticket.",
|
|
473
|
+
"Note the current time.",
|
|
474
|
+
"Resolve it with a closing message.",
|
|
475
|
+
"RELOAD and read the resolved timestamp shown.",
|
|
476
|
+
"Compare it against when the action was taken.",
|
|
477
|
+
"Read the ticket's history and check the transition names an actor.",
|
|
478
|
+
],
|
|
479
|
+
expectedBehaviour: "Resolving stamps a real timestamp at the moment of the action, recorded through the ticket's own status choke point with an actor. A status change that records no time leaves nothing to measure response times against.",
|
|
480
|
+
expectedUiState: "The resolved timestamp matches when the action was taken, and the history entry names the acting admin. A missing timestamp, or the record's generic updated stamp reused as one, are both failures.",
|
|
481
|
+
endResult: "One ticket is resolved.",
|
|
482
|
+
},
|
|
483
|
+
"checklist-buying-offers-payout-utr-actually-saves": {
|
|
484
|
+
roles: ["admin"],
|
|
485
|
+
startPage: "/admin/payouts",
|
|
486
|
+
steps: [
|
|
487
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
488
|
+
"Open /admin/payouts and open a pending payout.",
|
|
489
|
+
"Mark it paid, typing QAUTR118427 as the transaction reference.",
|
|
490
|
+
"Read the confirmation.",
|
|
491
|
+
"RELOAD the payout and read the reference.",
|
|
492
|
+
"Open its history and check the transition is recorded with the actor.",
|
|
493
|
+
"Check /admin/audit-log holds an entry for the payout being marked paid.",
|
|
494
|
+
],
|
|
495
|
+
inputs: { utr: "QAUTR118427" },
|
|
496
|
+
expectedBehaviour: "The reference is stored with the status change, recorded in the payout's history and in the audit log. Marking a payout paid is one of the instrumented privileged actions — a status that moves while the reference is dropped leaves no way to reconcile the transfer.",
|
|
497
|
+
expectedUiState: "After the reload the payout reads paid and holds QAUTR118427. Its history names the actor and the audit log carries a matching entry.",
|
|
498
|
+
expectedData: { utr: "QAUTR118427" },
|
|
499
|
+
endResult: "One payout is marked paid with its reference.",
|
|
500
|
+
},
|
|
501
|
+
"checklist-buying-offers-store-approval-makes-it-public": {
|
|
502
|
+
roles: ["admin", "guest"],
|
|
503
|
+
startPage: "/admin/stores",
|
|
504
|
+
steps: [
|
|
505
|
+
"Open /stores in a private window and confirm store-blader-bazaar is NOT listed.",
|
|
506
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
507
|
+
"Open /admin/stores, find that pending store, and approve it.",
|
|
508
|
+
"RELOAD and read its status.",
|
|
509
|
+
"Open /stores in the private window and look for it.",
|
|
510
|
+
"Open its own store page and check it renders.",
|
|
511
|
+
"Set it back to pending and confirm it disappears from /stores.",
|
|
512
|
+
],
|
|
513
|
+
inputs: { storeId: "store-blader-bazaar" },
|
|
514
|
+
expectedBehaviour: "Approval flips BOTH the store's own status and its public flag. Public visibility checks read the store's status, so flipping only the user's seller status leaves the seller with a working dashboard and a store nothing public can see.",
|
|
515
|
+
expectedUiState: "The store appears on /stores after approval and its page renders. Reverting removes it. A store approved in the admin but absent from /stores is the half-flip failure.",
|
|
516
|
+
endResult: "The store is returned to pending.",
|
|
517
|
+
},
|
|
518
|
+
"checklist-buying-offers-store-timeline-shows-who-suspended": {
|
|
519
|
+
roles: ["admin"],
|
|
520
|
+
startPage: "/admin/stores",
|
|
521
|
+
steps: [
|
|
522
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
523
|
+
"Open /admin/stores and find store-vintage-vault-co, which is suspended.",
|
|
524
|
+
"Open its detail and read its status history.",
|
|
525
|
+
"Check the suspension entry names who did it, when, and the reason.",
|
|
526
|
+
"Suspend a different store with a reason, then reload and read its history.",
|
|
527
|
+
"Restore that store's original status and check the history keeps BOTH transitions.",
|
|
528
|
+
],
|
|
529
|
+
inputs: { storeId: "store-vintage-vault-co" },
|
|
530
|
+
expectedBehaviour: "Store status changes are recorded through the repository's own status writer, so who, when and why are all captured. Restoring the status appends a second entry rather than replacing the first — a history that only shows the current state is not a history.",
|
|
531
|
+
expectedUiState: "The suspension entry names an actor, a timestamp and a reason. After restoring, both transitions are listed in order.",
|
|
532
|
+
endResult: "The second store is back to its original status, with both entries recorded.",
|
|
533
|
+
},
|
|
534
|
+
"checklist-buying-offers-payout-failure-reasons-survive-retries": {
|
|
535
|
+
roles: ["admin"],
|
|
536
|
+
startPage: "/admin/payouts",
|
|
537
|
+
steps: [
|
|
538
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
539
|
+
"Open /admin/payouts and find a payout that has failed more than once.",
|
|
540
|
+
"Open its history and read every entry.",
|
|
541
|
+
"Check BOTH failure reasons are present rather than only the most recent.",
|
|
542
|
+
"Check the pending-to-processing transitions are also recorded.",
|
|
543
|
+
"Count the entries against the number of attempts.",
|
|
544
|
+
],
|
|
545
|
+
expectedBehaviour: "Batch payout writes thread a RUNNING copy of the record rather than the original snapshot. Diffing the last write against the first snapshot loses everything in between — a pending, processing, failed sequence would record only the final state and the dispatch attempt would vanish.",
|
|
546
|
+
expectedUiState: "Every attempt appears with its own reason, oldest first, including the intermediate processing transitions. Only the latest reason showing is the snapshot-diff failure.",
|
|
547
|
+
endResult: "Read-only. If no twice-failed payout exists, answer null rather than forcing a failure.",
|
|
548
|
+
},
|
|
549
|
+
"checklist-buying-offers-bid-timeline-explains-forfeited": {
|
|
550
|
+
roles: ["buyer"],
|
|
551
|
+
startPage: "/user/bids",
|
|
552
|
+
steps: [
|
|
553
|
+
"Sign in as tester@letitrip.in / TempPass123!.",
|
|
554
|
+
"Open /user/bids and find a forfeited bid.",
|
|
555
|
+
"Open its detail and read its history.",
|
|
556
|
+
"Check an entry explains WHY it was forfeited rather than only that it was.",
|
|
557
|
+
"Read whether the entry names the deadline that passed.",
|
|
558
|
+
"Check the bid's status reads forfeited rather than simply lost.",
|
|
559
|
+
],
|
|
560
|
+
expectedBehaviour: "A forfeited bid records the reason. Bid history is written through batch mutators that accept either a bare reference or a reference-plus-data pair — deliberately widened rather than made mandatory, because forcing a read per losing bid would push a large auction's settlement over the read budget.",
|
|
561
|
+
expectedUiState: "The history explains the forfeiture and names the missed deadline. A bare status with no explanation leaves the buyer unable to tell a forfeit from an ordinary loss.",
|
|
562
|
+
endResult: "Read-only. If no forfeited bid exists, answer null.",
|
|
563
|
+
},
|
|
564
|
+
"checklist-buying-offers-catalogue-rejection-reason-survives": {
|
|
565
|
+
roles: ["admin"],
|
|
566
|
+
startPage: "/admin/catalogue-approvals",
|
|
567
|
+
steps: [
|
|
568
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
569
|
+
"Open the catalogue approvals surface and reject a submission with 'QA Rejection one — photos unclear.'.",
|
|
570
|
+
"Have the owner resubmit it, or find one already rejected twice.",
|
|
571
|
+
"Reject it again with 'QA Rejection two — still unclear.'.",
|
|
572
|
+
"Open its history and read every entry.",
|
|
573
|
+
"Check BOTH reasons are present.",
|
|
574
|
+
"Check the owner can read both.",
|
|
575
|
+
],
|
|
576
|
+
inputs: { reason1: "QA Rejection one — photos unclear.", reason2: "QA Rejection two — still unclear." },
|
|
577
|
+
expectedBehaviour: "Each rejection appends its own entry. Catalogue status changes had no single write path at all — five scattered updates moved the field — so the funnel had to be built before history could be recorded, and a second rejection overwriting the first is what that absence produced.",
|
|
578
|
+
expectedUiState: "Both reasons appear in order in the history and both are visible to the owner. Only the latest surviving is the failure.",
|
|
579
|
+
endResult: "The submission carries both rejection reasons.",
|
|
580
|
+
},
|
|
581
|
+
"checklist-buying-offers-history-absent-renders-empty-not-invented": {
|
|
582
|
+
roles: ["admin"],
|
|
583
|
+
startPage: "/admin/orders",
|
|
584
|
+
steps: [
|
|
585
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
586
|
+
"Open a record created before history recording existed — an old order, store or payout.",
|
|
587
|
+
"Open its history block.",
|
|
588
|
+
"Read what is shown.",
|
|
589
|
+
"Check it states there is no recorded history rather than showing a date.",
|
|
590
|
+
"Check no step carries the record's created or updated timestamp presented as a transition.",
|
|
591
|
+
],
|
|
592
|
+
expectedBehaviour: "A record with no recorded history says so. Deriving a transition from the record's created or updated stamp invents an event that never happened, and a fabricated timestamp cannot be told from a real one afterwards.",
|
|
593
|
+
expectedUiState: "The block reads as having no recorded history, or renders the phases with em-dashes. It does not present the record's own timestamps as transitions.",
|
|
594
|
+
endResult: "Read-only; nothing persists.",
|
|
595
|
+
},
|
|
596
|
+
"checklist-buying-offers-history-carries-no-pii": {
|
|
597
|
+
roles: ["admin"],
|
|
598
|
+
startPage: "/admin/orders",
|
|
599
|
+
steps: [
|
|
600
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
601
|
+
"Open an order with several history entries and read every one.",
|
|
602
|
+
"Look for a name, an email address or a phone number.",
|
|
603
|
+
"Open the browser's View Source and search the page for an email address.",
|
|
604
|
+
"Repeat on an offer, a payout and a store.",
|
|
605
|
+
"Check every actor is shown as an identifier rather than a person's name.",
|
|
606
|
+
],
|
|
607
|
+
expectedBehaviour: "History carries no personal data, enforced in the primitive rather than by convention. The encryption pass is a flat top-level loop that never descends into arrays — a name nested inside a history entry would be stored in PLAINTEXT and never decrypted on the way out. Orders were safe only by luck; offers were not.",
|
|
608
|
+
expectedUiState: "No entry on any of the four records contains a name, email or phone, in the rendered view or the source. Actors are identifiers.",
|
|
609
|
+
expectedData: { piiInHistory: 0 },
|
|
610
|
+
endResult: "Read-only; nothing persists.",
|
|
611
|
+
},
|
|
612
|
+
"checklist-buying-offers-form-errors-wait-for-first-submit": {
|
|
613
|
+
roles: ["buyer"],
|
|
614
|
+
startPage: "/user/addresses/new",
|
|
615
|
+
steps: [
|
|
616
|
+
"Sign in as rehan.sheikh@gmail.com / TempPass123!.",
|
|
617
|
+
"Open /user/addresses/new and read the page WITHOUT typing anything.",
|
|
618
|
+
"Look for any error list and any red field.",
|
|
619
|
+
"Click into the city field and back out without typing, then read the page.",
|
|
620
|
+
"Press Save with everything empty.",
|
|
621
|
+
"Read what appears and count the errors.",
|
|
622
|
+
],
|
|
623
|
+
expectedBehaviour: "The summary appears only after a submit attempt. Eleven views call validation from a mount effect and the summary was deliberately un-gated by touch state — together those made every schema-driven form open by listing every empty required field, accusing the user before they had typed anything. The gate belongs on the DISPLAY: removing the mount validation would stop the list updating as fields are fixed.",
|
|
624
|
+
expectedUiState: "No summary and no red fields before Save. Touching and leaving the city field marks that ONE field, without opening the summary. After Save the summary lists every failing field.",
|
|
625
|
+
expectedData: { errorsBeforeSubmit: 0 },
|
|
626
|
+
endResult: "Nothing is saved.",
|
|
627
|
+
},
|
|
628
|
+
"checklist-buying-offers-form-errors-stay-live-after-first-submit": {
|
|
629
|
+
roles: ["buyer"],
|
|
630
|
+
startPage: "/user/addresses/new",
|
|
631
|
+
steps: [
|
|
632
|
+
"Sign in as rehan.sheikh@gmail.com / TempPass123!.",
|
|
633
|
+
"Open /user/addresses/new and press Save with everything empty.",
|
|
634
|
+
"Read the error count in the summary.",
|
|
635
|
+
"Fill the name field and read the count again without clicking anything.",
|
|
636
|
+
"Fill the street, then the city, reading the count after each.",
|
|
637
|
+
"Fill the remaining fields and check the summary disappears.",
|
|
638
|
+
],
|
|
639
|
+
expectedBehaviour: "Once a submit has been attempted the summary tracks every keystroke. The COUNT has to be part of the summary's own label — the panel is only republished when that label changes, and a static label would freeze its contents at the original count while the user fixes fields.",
|
|
640
|
+
expectedUiState: "The count falls by one per field filled — 'Fix 6 issues' becomes 'Fix 5 issues' — and each fixed field leaves the list. A count frozen until Save is pressed again is the failure.",
|
|
641
|
+
endResult: "Nothing is saved.",
|
|
642
|
+
},
|
|
643
|
+
"checklist-buying-offers-form-mobile-bar-pinned": {
|
|
644
|
+
roles: ["buyer"],
|
|
645
|
+
startPage: "/user/addresses/new",
|
|
646
|
+
steps: [
|
|
647
|
+
"Sign in as rehan.sheikh@gmail.com / TempPass123!.",
|
|
648
|
+
"Resize the browser to 390 pixels wide and open /user/addresses/new.",
|
|
649
|
+
"Look at the bottom of the screen for Save and Cancel.",
|
|
650
|
+
"Count the fixed bars stacked at the bottom.",
|
|
651
|
+
"Scroll the form to its end and check the last field is not hidden behind them.",
|
|
652
|
+
"Tap into a field so the keyboard opens and read where the bar sits.",
|
|
653
|
+
],
|
|
654
|
+
inputs: { mobileWidth: 390 },
|
|
655
|
+
expectedBehaviour: "The form's action row joins the measured bottom tier above the tab bar, so both are visible and neither has to know the other's height. With a keyboard open it becomes three tiers, and everything above reads all three.",
|
|
656
|
+
expectedUiState: "Save and Cancel sit above the tab bar with no overlap, exactly two bars without the keyboard. The last form field is reachable. With the keyboard open nothing floats over it.",
|
|
657
|
+
expectedData: { bottomBarCount: 2 },
|
|
658
|
+
endResult: "Nothing is saved; restore the window width.",
|
|
659
|
+
},
|
|
660
|
+
"checklist-buying-offers-form-mobile-error-sheet": {
|
|
661
|
+
roles: ["buyer"],
|
|
662
|
+
startPage: "/user/addresses/new",
|
|
663
|
+
steps: [
|
|
664
|
+
"Sign in as rehan.sheikh@gmail.com / TempPass123!.",
|
|
665
|
+
"Resize to 390 pixels wide and open /user/addresses/new.",
|
|
666
|
+
"Press Save with everything empty.",
|
|
667
|
+
"Read the label on the sheet control at the bottom.",
|
|
668
|
+
"Open the sheet and read the issues listed.",
|
|
669
|
+
"Close the sheet, fill one field, and read the label again.",
|
|
670
|
+
"Press Save again and check the sheet REOPENS.",
|
|
671
|
+
],
|
|
672
|
+
inputs: { mobileWidth: 390 },
|
|
673
|
+
expectedBehaviour: "The sheet's label carries the issue count, which is what pushes updated contents through. Reopening on a second failed Save is driven by a COUNTER rather than a boolean — a boolean stays true and reopens the sheet the user just closed on every re-render.",
|
|
674
|
+
expectedUiState: "The control reads 'Fix N issues' and the number falls as fields are filled. Closing it keeps it closed through ordinary typing, and a second Save reopens it.",
|
|
675
|
+
endResult: "Nothing is saved; restore the window width.",
|
|
676
|
+
},
|
|
677
|
+
"checklist-buying-offers-form-bar-restores-listing-bulk-bar": {
|
|
678
|
+
roles: ["admin"],
|
|
679
|
+
startPage: "/admin/products",
|
|
680
|
+
steps: [
|
|
681
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
682
|
+
"Open /admin/products and tick two rows so the bulk action bar appears.",
|
|
683
|
+
"Read the bar and its count.",
|
|
684
|
+
"Open a row's editor in a drawer over the listing.",
|
|
685
|
+
"Read what the bottom bar shows now.",
|
|
686
|
+
"Close the drawer WITHOUT saving.",
|
|
687
|
+
"Read the bottom bar again and check the bulk bar and its count are back.",
|
|
688
|
+
],
|
|
689
|
+
expectedBehaviour: "The bar is one shared instance and its claim is a STACK, not last-writer-wins. Each claimant claims on mount and releases on unmount, only the top may publish, and regaining the top republishes automatically. Releasing blanks the bar only when the stack empties — clearing on every release would flash an empty bar between the drawer closing and the listing republishing.",
|
|
690
|
+
expectedUiState: "The drawer's form bar replaces the bulk bar while open. On close the bulk bar returns with its original count, without an empty flash in between.",
|
|
691
|
+
endResult: "Clear the selection; nothing is saved.",
|
|
692
|
+
},
|
|
693
|
+
"checklist-buying-offers-form-bar-absent-inside-a-modal": {
|
|
694
|
+
roles: ["admin"],
|
|
695
|
+
startPage: "/admin/products",
|
|
696
|
+
steps: [
|
|
697
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
698
|
+
"Open /admin/products and open a form inside a MODAL rather than a page.",
|
|
699
|
+
"Read the modal's own footer buttons.",
|
|
700
|
+
"Look at the bottom of the SCREEN for a second action bar.",
|
|
701
|
+
"Resize to 390 pixels and look again.",
|
|
702
|
+
"Close the modal and check no orphaned bar remains.",
|
|
703
|
+
],
|
|
704
|
+
inputs: { mobileWidth: 390 },
|
|
705
|
+
expectedBehaviour: "A form inside a modal or drawer suppresses its own bar. An overlay owns its footer, and a viewport-fixed bar would render BEHIND the backdrop, below the dialog it belongs to. Neither overlay leaves a detectable DOM trace at the form's position, so the overlay has to declare itself.",
|
|
706
|
+
expectedUiState: "The modal has its own footer and there is no second bar at the bottom of the screen, at either width. Closing leaves nothing behind.",
|
|
707
|
+
expectedData: { screenBottomBars: 0 },
|
|
708
|
+
endResult: "Close without saving; restore the window width.",
|
|
709
|
+
},
|
|
710
|
+
"checklist-buying-offers-address-routes-normalised": {
|
|
711
|
+
roles: ["buyer"],
|
|
712
|
+
startPage: "/user/addresses",
|
|
713
|
+
steps: [
|
|
714
|
+
"Sign in as rehan.sheikh@gmail.com / TempPass123!.",
|
|
715
|
+
"Open /user/addresses and create an address, noting the URL of the create form.",
|
|
716
|
+
"Open an existing address's edit page and note its URL.",
|
|
717
|
+
"Try the older URL shapes for both create and edit directly in the address bar.",
|
|
718
|
+
"Read where each lands.",
|
|
719
|
+
"Check any old URL either resolves or redirects to the current one.",
|
|
720
|
+
"Delete the created address.",
|
|
721
|
+
],
|
|
722
|
+
expectedBehaviour: "Both the current and the previous URL shapes resolve. These URLs live in bookmarks and in guides, so a 404 breaks a link that was correct when it was written — and the editor page must open with its form ALREADY showing rather than as a blank page.",
|
|
723
|
+
expectedUiState: "Create and edit both work at their current URLs. Old shapes resolve or redirect rather than 404ing. Each editor opens with the form visible.",
|
|
724
|
+
endResult: "The created address is deleted.",
|
|
725
|
+
},
|
|
726
|
+
"checklist-buying-offers-custom-role-rejects-fake-permissions": {
|
|
727
|
+
roles: ["admin"],
|
|
728
|
+
startPage: "/admin/team",
|
|
729
|
+
steps: [
|
|
730
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
731
|
+
"Open the roles surface and create a role named 'QA Role fake-perms'.",
|
|
732
|
+
"Using browser DevTools, add a permission string this system does not define to the submitted payload.",
|
|
733
|
+
"Save and read what happens.",
|
|
734
|
+
"RELOAD and read the role's permissions.",
|
|
735
|
+
"Check the invented permission is absent.",
|
|
736
|
+
"Delete the role.",
|
|
737
|
+
],
|
|
738
|
+
inputs: { roleName: "QA Role fake-perms" },
|
|
739
|
+
expectedBehaviour: "Permissions are validated against the defined set server-side. An undefined permission stored on a role is a grant nothing checks — it reads as configured in the UI while granting nothing, or worse, matching a future permission name by accident.",
|
|
740
|
+
expectedUiState: "The save is refused, or the invented permission is stripped and absent after the reload. A role storing an undefined permission is the failure.",
|
|
741
|
+
endResult: "The role is deleted.",
|
|
742
|
+
},
|
|
743
|
+
"checklist-buying-offers-custom-role-edit-does-not-rewrite-creator": {
|
|
744
|
+
roles: ["admin"],
|
|
745
|
+
startPage: "/admin/team",
|
|
746
|
+
steps: [
|
|
747
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
748
|
+
"Open the roles surface and create a role named 'QA Role creator', noting who it records as creator.",
|
|
749
|
+
"Edit the role, changing only its permission list, and save.",
|
|
750
|
+
"RELOAD and read the creator field.",
|
|
751
|
+
"Compare it against what was recorded at creation.",
|
|
752
|
+
"Delete the role.",
|
|
753
|
+
],
|
|
754
|
+
inputs: { roleName: "QA Role creator" },
|
|
755
|
+
expectedBehaviour: "The creator is set once and never rewritten. An update handler that re-derives created-by from the current session rewrites provenance on every edit — so the last person to touch a role appears to have made it, and the audit trail is silently wrong.",
|
|
756
|
+
expectedUiState: "After the edit and reload the creator is unchanged. A creator matching the editing admin is the failure.",
|
|
757
|
+
endResult: "The role is deleted.",
|
|
758
|
+
},
|
|
759
|
+
"checklist-buying-offers-custom-role-rejects-empty": {
|
|
760
|
+
roles: ["admin"],
|
|
761
|
+
startPage: "/admin/team",
|
|
762
|
+
steps: [
|
|
763
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
764
|
+
"Open the roles surface and start creating a role.",
|
|
765
|
+
"Leave the name empty and save.",
|
|
766
|
+
"Read where any error appears.",
|
|
767
|
+
"Type only spaces as the name and save, reading what happens.",
|
|
768
|
+
"Type a real name and save, then delete the role.",
|
|
769
|
+
],
|
|
770
|
+
expectedBehaviour: "A nameless role is refused on the field. Whitespace-only must be refused too — trimming before validating is what separates a real check from one that passes a name nothing can display.",
|
|
771
|
+
expectedUiState: "Both the empty and whitespace-only names produce an error on the name field. A disabled button that says nothing is the failure — the admin cannot tell it from a broken page.",
|
|
772
|
+
endResult: "The role is deleted.",
|
|
773
|
+
},
|
|
774
|
+
"checklist-buying-offers-bid-row-opens-in-all-three-portals": {
|
|
775
|
+
roles: ["buyer", "seller", "admin"],
|
|
776
|
+
startPage: "/user/bids",
|
|
777
|
+
steps: [
|
|
778
|
+
"Sign in as tester@letitrip.in / TempPass123! and open /user/bids.",
|
|
779
|
+
"Click a bid row and read what opens.",
|
|
780
|
+
"Sign out and sign in as tyson@beybladearena.in / TempPass123!.",
|
|
781
|
+
"Open the store's bids listing and click a row, reading what opens.",
|
|
782
|
+
"Sign out and sign in as admin@letitrip.in / TempPass123!.",
|
|
783
|
+
"Open /admin/bids and click a row, reading what opens.",
|
|
784
|
+
"Check all three open something rather than being inert.",
|
|
785
|
+
],
|
|
786
|
+
expectedBehaviour: "A bid can be opened in every portal. A row that can be seen and never opened is a dead end, and this listing family had several — the fix was a shared detail component rather than three separate ones.",
|
|
787
|
+
expectedUiState: "All three portals open a bid detail. An inert row in any of them is the finding, named by portal.",
|
|
788
|
+
endResult: "Read-only; close without acting.",
|
|
789
|
+
},
|
|
790
|
+
"checklist-buying-offers-bid-detail-hides-bidder-from-buyer": {
|
|
791
|
+
roles: ["buyer", "seller", "admin"],
|
|
792
|
+
startPage: "/user/bids",
|
|
793
|
+
steps: [
|
|
794
|
+
"Sign in as tester@letitrip.in / TempPass123! and open a bid detail from /user/bids.",
|
|
795
|
+
"Read every name and identifier shown, and search the page source for a bidder name.",
|
|
796
|
+
"Sign in as tyson@beybladearena.in / TempPass123! and open the same bid from the store's listing.",
|
|
797
|
+
"Read what identities are shown there.",
|
|
798
|
+
"Sign in as admin@letitrip.in / TempPass123! and open the same bid from /admin/bids.",
|
|
799
|
+
"Read what identities are shown there.",
|
|
800
|
+
"Compare all three.",
|
|
801
|
+
],
|
|
802
|
+
expectedBehaviour: "One viewer argument decides what each portal reveals, in one place. The buyer's own view must not name other bidders — a competitor's identity is not theirs to see — while the seller and admin views may, because moderating a dispute requires it.",
|
|
803
|
+
expectedUiState: "The buyer's view shows their own bid with other bidders masked or absent, in the source as well as on screen. The seller and admin views may show more. A real name in the buyer's view is a leak.",
|
|
804
|
+
expectedData: { otherBidderNamesInBuyerView: 0 },
|
|
805
|
+
endResult: "Read-only; nothing persists.",
|
|
806
|
+
},
|
|
807
|
+
"checklist-buying-offers-admin-bid-view-before-cancel": {
|
|
808
|
+
roles: ["admin"],
|
|
809
|
+
startPage: "/admin/bids",
|
|
810
|
+
steps: [
|
|
811
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
812
|
+
"Open /admin/bids and open a row's action menu.",
|
|
813
|
+
"Read every action and the order they appear in.",
|
|
814
|
+
"Check View appears before Cancel.",
|
|
815
|
+
"Choose View and read the bid's detail.",
|
|
816
|
+
"Return and read whether Cancel opens a confirmation naming what it will do.",
|
|
817
|
+
"Cancel out of that dialog.",
|
|
818
|
+
],
|
|
819
|
+
expectedBehaviour: "View precedes Cancel in the menu, so reading is the path of least resistance. A menu of pure mutations lets an admin cancel a bid they never read, and cancelling a bid changes the standing price of a live auction.",
|
|
820
|
+
expectedUiState: "View is present and listed before Cancel. Cancel opens a confirmation naming the consequence rather than a generic prompt. Dismissing leaves the bid untouched.",
|
|
821
|
+
endResult: "Nothing is cancelled.",
|
|
822
|
+
},
|
|
823
|
+
"checklist-buying-offers-bid-row-keyboard-reachable": {
|
|
824
|
+
roles: ["buyer", "guest"],
|
|
825
|
+
startPage: "/user/bids",
|
|
826
|
+
steps: [
|
|
827
|
+
"Sign in as tester@letitrip.in / TempPass123! and open /user/bids.",
|
|
828
|
+
"Press Tab repeatedly and check each bid row receives focus with a visible ring.",
|
|
829
|
+
"Press Enter on a focused row and check it opens.",
|
|
830
|
+
"Open a public auction's bid history in a private window.",
|
|
831
|
+
"Press Tab through it and check whether the history ROWS take focus.",
|
|
832
|
+
"Check they do not, while any real controls still do.",
|
|
833
|
+
],
|
|
834
|
+
expectedBehaviour: "The buyer's own bid rows are interactive and must be keyboard reachable with a visible focus ring. Public bid-history rows are NOT interactive — they open nothing — so making them focusable adds tab stops that lead nowhere, which is worse than skipping them.",
|
|
835
|
+
expectedUiState: "Dashboard bid rows take focus and open on Enter. Public bid-history rows do not take focus, while genuine controls near them still do.",
|
|
836
|
+
endResult: "Read-only; nothing persists.",
|
|
837
|
+
},
|
|
838
|
+
"checklist-buying-offers-listing-template-edit-page-works": {
|
|
839
|
+
roles: ["seller"],
|
|
840
|
+
startPage: "/store/listing-templates",
|
|
841
|
+
steps: [
|
|
842
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
843
|
+
"Open /store/listing-templates and create one named 'QA Template edit-page'.",
|
|
844
|
+
"Open its edit page directly by URL.",
|
|
845
|
+
"Check it renders the form rather than a 404.",
|
|
846
|
+
"Change a field, save, RELOAD, and read it back.",
|
|
847
|
+
"Delete the template from that page and confirm it is gone.",
|
|
848
|
+
],
|
|
849
|
+
inputs: { name: "QA Template edit-page" },
|
|
850
|
+
expectedBehaviour: "The edit page exists, loads, saves and deletes. A nav entry or a row link pointing at a route that was never built is a dead link — and there are two template features in this codebase, one superseded by the other, so confirming which page is reachable matters as much as whether it renders.",
|
|
851
|
+
expectedUiState: "The edit URL renders the populated form. The change survives the reload and the delete removes it. A 404 is the failure.",
|
|
852
|
+
endResult: "The template is deleted.",
|
|
853
|
+
},
|
|
854
|
+
"checklist-buying-offers-listing-template-not-editable-across-stores": {
|
|
855
|
+
roles: ["seller"],
|
|
856
|
+
startPage: "/store/listing-templates",
|
|
857
|
+
steps: [
|
|
858
|
+
"Sign in as tyson@beybladearena.in / TempPass123! and create a template, copying its edit URL.",
|
|
859
|
+
"Sign out and sign in as tester@letitrip.in / TempPass123!, a different store's owner.",
|
|
860
|
+
"Paste that edit URL and read what happens.",
|
|
861
|
+
"Check whether the template's contents are shown at any point.",
|
|
862
|
+
"Attempt to save from that page.",
|
|
863
|
+
"Sign back in as tyson and confirm the template is unchanged, then delete it.",
|
|
864
|
+
],
|
|
865
|
+
expectedBehaviour: "Ownership is enforced server-side on read AND on write. Hiding the route from the other seller's navigation is not a control — the URL is guessable, and a page that renders the contents before refusing the save has already leaked them.",
|
|
866
|
+
expectedUiState: "The other seller is refused before any content renders. No template fields are shown. Any save attempt is refused. The template is unchanged.",
|
|
867
|
+
endResult: "The template is deleted.",
|
|
868
|
+
},
|
|
869
|
+
"checklist-buying-offers-payout-method-rejects-blank-bank-details": {
|
|
870
|
+
roles: ["seller"],
|
|
871
|
+
startPage: "/store/payouts",
|
|
872
|
+
steps: [
|
|
873
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
874
|
+
"Open the payout methods surface and add a BANK method.",
|
|
875
|
+
"Leave the account number, IFSC and holder name empty and save.",
|
|
876
|
+
"Read where the errors appear.",
|
|
877
|
+
"Fill only the account number and save, reading what happens.",
|
|
878
|
+
"Fill all three and save.",
|
|
879
|
+
"Delete the method.",
|
|
880
|
+
],
|
|
881
|
+
expectedBehaviour: "A bank method requires all three fields, each marked on its own field. A payout method missing its account number is one a real transfer will fail on later — and the failure surfaces at payout time, long after the seller believed they had configured it.",
|
|
882
|
+
expectedUiState: "The empty save marks all three fields individually rather than showing one banner. The partial save marks the two still missing. The complete one is accepted.",
|
|
883
|
+
endResult: "The method is deleted.",
|
|
884
|
+
},
|
|
885
|
+
"checklist-buying-offers-payout-method-rejects-bad-ifsc": {
|
|
886
|
+
roles: ["seller"],
|
|
887
|
+
startPage: "/store/payouts",
|
|
888
|
+
steps: [
|
|
889
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
890
|
+
"Open the payout methods surface and add a bank method.",
|
|
891
|
+
"Type ABC123 as the IFSC and save, reading where the error appears.",
|
|
892
|
+
"Type 12345678901 as the IFSC and save, reading what happens.",
|
|
893
|
+
"Type a correctly-shaped IFSC and save.",
|
|
894
|
+
"RELOAD and read how the stored details are displayed.",
|
|
895
|
+
"Delete the method.",
|
|
896
|
+
],
|
|
897
|
+
inputs: { badIfsc1: "ABC123", badIfsc2: "12345678901" },
|
|
898
|
+
expectedBehaviour: "The IFSC is validated by FORMAT rather than by length. A length-only rule accepts any eleven characters — the same class of check that once treated 'abcdef' as a valid six-digit postal code.",
|
|
899
|
+
expectedUiState: "Both malformed codes are refused on the IFSC field before any request. The correctly-shaped one is accepted and displays MASKED after the reload rather than in full.",
|
|
900
|
+
endResult: "The method is deleted.",
|
|
901
|
+
},
|
|
902
|
+
"checklist-buying-offers-shipping-config-rejects-rateless-rule": {
|
|
903
|
+
roles: ["seller"],
|
|
904
|
+
startPage: "/store/shipping",
|
|
905
|
+
steps: [
|
|
906
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
907
|
+
"Open the shipping settings and add a FLAT rule with no rate.",
|
|
908
|
+
"Save and read where the error appears.",
|
|
909
|
+
"Type -50 as the rate and save, reading what happens.",
|
|
910
|
+
"Type 0 and save, reading whether it is accepted.",
|
|
911
|
+
"Type 99 and save.",
|
|
912
|
+
"Delete the rule.",
|
|
913
|
+
],
|
|
914
|
+
inputs: { negativeRate: -50, zeroRate: 0, validRate: 99 },
|
|
915
|
+
expectedBehaviour: "A flat rule without a rate cannot price anything, and a negative rate would pay the buyer to order. Zero is legitimate — that is free shipping — so the rule is 'a number, not negative' rather than 'greater than zero', and conflating the two blocks a real configuration.",
|
|
916
|
+
expectedUiState: "The rateless and negative saves are refused on the rate field. Zero is ACCEPTED as free shipping. 99 is accepted. Refusing zero is a finding.",
|
|
917
|
+
endResult: "The rule is deleted.",
|
|
918
|
+
},
|
|
919
|
+
"checklist-buying-offers-grouped-listing-count-not-caller-settable": {
|
|
920
|
+
roles: ["seller"],
|
|
921
|
+
startPage: "/store/grouped-listings",
|
|
922
|
+
steps: [
|
|
923
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
924
|
+
"Open /store/grouped-listings and create a group with two members.",
|
|
925
|
+
"Save, RELOAD, and read the member count shown.",
|
|
926
|
+
"Add a third member, save, RELOAD, and read the count.",
|
|
927
|
+
"Using DevTools, submit a member count that disagrees with the member list.",
|
|
928
|
+
"RELOAD and read the count.",
|
|
929
|
+
"Delete the group.",
|
|
930
|
+
],
|
|
931
|
+
expectedBehaviour: "The count is DERIVED from the member list and never accepted from a caller. A caller-supplied count that disagrees with the array is the mirror-drift trap — and this count is what the visibility rule reads, so a wrong one hides or shows a group incorrectly.",
|
|
932
|
+
expectedUiState: "The count matches the member list after every save, including after the forced mismatch. A stored count that disagrees with the members is the failure.",
|
|
933
|
+
expectedData: { countMatchesMembers: true },
|
|
934
|
+
endResult: "The group is deleted.",
|
|
935
|
+
},
|
|
936
|
+
"checklist-buying-offers-store-feature-new-actually-creates": {
|
|
937
|
+
roles: ["seller"],
|
|
938
|
+
startPage: "/store/features",
|
|
939
|
+
steps: [
|
|
940
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
941
|
+
"Open the store features surface and note how many exist.",
|
|
942
|
+
"Open the create page directly by URL.",
|
|
943
|
+
"Check it renders the form rather than a blank page.",
|
|
944
|
+
"Fill it in with 'QA Feature actually-creates' and save.",
|
|
945
|
+
"RELOAD the list and count again.",
|
|
946
|
+
"Delete the feature.",
|
|
947
|
+
],
|
|
948
|
+
inputs: { name: "QA Feature actually-creates" },
|
|
949
|
+
expectedBehaviour: "The create page exists and actually creates. A page reached by URL must open with its form already showing — a blank page you have to click into is the shape these editor routes had before they were wired.",
|
|
950
|
+
expectedUiState: "The create URL renders a populated form. After saving the list count is one higher and survives a reload. A success message with no new row is the failure.",
|
|
951
|
+
endResult: "The feature is deleted.",
|
|
952
|
+
},
|
|
953
|
+
"checklist-buying-offers-store-feature-edit-page-exists": {
|
|
954
|
+
roles: ["seller"],
|
|
955
|
+
startPage: "/store/features",
|
|
956
|
+
steps: [
|
|
957
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
958
|
+
"Open the store features surface and create one named 'QA Feature edit-page'.",
|
|
959
|
+
"Open its edit page directly by URL.",
|
|
960
|
+
"Check it renders the populated form rather than a 404.",
|
|
961
|
+
"Change a field, save, RELOAD, and read it back.",
|
|
962
|
+
"Copy the URL, open it in a new tab, and check it opens the same record.",
|
|
963
|
+
"Delete the feature.",
|
|
964
|
+
],
|
|
965
|
+
inputs: { name: "QA Feature edit-page" },
|
|
966
|
+
expectedBehaviour: "The feature has its own edit page, not only a drawer. That is the whole reason these pages exist: a drawer cannot be bookmarked, shared with a colleague, or reopened after a crash.",
|
|
967
|
+
expectedUiState: "The edit URL renders the populated form and survives a reload and a fresh tab. A 404, or a page that opens empty, are both failures.",
|
|
968
|
+
endResult: "The feature is deleted.",
|
|
969
|
+
},
|
|
970
|
+
"checklist-buying-offers-feature-editor-validates": {
|
|
971
|
+
roles: ["seller"],
|
|
972
|
+
startPage: "/store/features",
|
|
973
|
+
steps: [
|
|
974
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
975
|
+
"Open the feature editor and save with every field empty.",
|
|
976
|
+
"Read whether errors appear or the Save button is simply disabled.",
|
|
977
|
+
"Type only spaces in the name and save.",
|
|
978
|
+
"Read what happens.",
|
|
979
|
+
"Fill the form correctly and save, then delete the feature.",
|
|
980
|
+
],
|
|
981
|
+
expectedBehaviour: "The editor reports what is wrong rather than disabling Save. A disabled button says nothing — the user cannot tell it from a broken page, and they have no way to learn which field is at fault.",
|
|
982
|
+
expectedUiState: "Save is enabled and pressing it puts errors on the offending fields. Whitespace-only is refused. A permanently disabled button with no explanation is the failure.",
|
|
983
|
+
endResult: "The feature is deleted.",
|
|
984
|
+
},
|
|
985
|
+
"checklist-buying-offers-store-category-rejects-empty": {
|
|
986
|
+
roles: ["seller"],
|
|
987
|
+
startPage: "/store/categories",
|
|
988
|
+
steps: [
|
|
989
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
990
|
+
"Open the storefront categories surface and start creating one.",
|
|
991
|
+
"Leave the label empty and save.",
|
|
992
|
+
"Read where any error appears.",
|
|
993
|
+
"Type only spaces and save, reading what happens.",
|
|
994
|
+
"Type a real label and save, then delete it.",
|
|
995
|
+
],
|
|
996
|
+
expectedBehaviour: "A category with no label is refused on the field. A nameless category renders as a blank entry in every picker that offers it, and nothing downstream can display or select it meaningfully.",
|
|
997
|
+
expectedUiState: "Empty and whitespace-only labels both produce a field error. A real label is accepted. A silently created blank category is the failure.",
|
|
998
|
+
endResult: "The category is deleted.",
|
|
999
|
+
},
|
|
1000
|
+
"checklist-buying-offers-blog-editor-sections-not-steps": {
|
|
1001
|
+
roles: ["admin"],
|
|
1002
|
+
startPage: "/admin/blog",
|
|
1003
|
+
steps: [
|
|
1004
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1005
|
+
"Open the blog editor for a new post.",
|
|
1006
|
+
"Read how the form is presented — sections or numbered steps.",
|
|
1007
|
+
"Try to reach the media area WITHOUT filling the title first.",
|
|
1008
|
+
"Expand every section and check each renders its fields.",
|
|
1009
|
+
"Check no section is gated behind completing another.",
|
|
1010
|
+
],
|
|
1011
|
+
expectedBehaviour: "The editor shows every field at once in collapsible sections. A wizard that blocks step two until step one validates prevents an author from filling fields in their own order — and there is no reason a blog post's media must wait on its title.",
|
|
1012
|
+
expectedUiState: "All sections are present and expandable immediately, with no step gating. A numbered wizard blocking progress is the failure.",
|
|
1013
|
+
endResult: "Leave without saving.",
|
|
1014
|
+
},
|
|
1015
|
+
"checklist-buying-offers-blog-existing-post-slug-is-valid": {
|
|
1016
|
+
roles: ["admin"],
|
|
1017
|
+
startPage: "/admin/blog",
|
|
1018
|
+
steps: [
|
|
1019
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1020
|
+
"Open /admin/blog and open an EXISTING published post's editor.",
|
|
1021
|
+
"Read the Slug field and any error attached to it.",
|
|
1022
|
+
"Check no error appears before anything is typed.",
|
|
1023
|
+
"Open two more existing posts and read the same.",
|
|
1024
|
+
"Save one without changing anything and check it still saves.",
|
|
1025
|
+
],
|
|
1026
|
+
expectedBehaviour: "An existing post's stored slug validates against the editor's own rule. Where the two disagree, every existing post opens showing an error on a field the author never touched — and the summary then accuses them of a mistake made by whatever wrote the slug.",
|
|
1027
|
+
expectedUiState: "No slug error on any existing post before typing. A no-op save succeeds. An error on an untouched field is the failure, and it will appear on every post rather than one.",
|
|
1028
|
+
endResult: "Nothing is changed.",
|
|
1029
|
+
},
|
|
1030
|
+
"checklist-buying-offers-blog-new-post-url-matches-existing": {
|
|
1031
|
+
roles: ["admin", "guest"],
|
|
1032
|
+
startPage: "/admin/blog",
|
|
1033
|
+
steps: [
|
|
1034
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1035
|
+
"Open /blog and note the URL shape of an existing post.",
|
|
1036
|
+
"Create a new post titled 'QA Blog url-shape' and publish it.",
|
|
1037
|
+
"Open it from /blog and read its URL.",
|
|
1038
|
+
"Compare the two URL shapes segment by segment.",
|
|
1039
|
+
"Check for a doubled prefix segment in the new one.",
|
|
1040
|
+
"Delete the post.",
|
|
1041
|
+
],
|
|
1042
|
+
inputs: { title: "QA Blog url-shape" },
|
|
1043
|
+
expectedBehaviour: "A new post's public URL has the same shape as an existing one. Where the slug generator adds a prefix the route already supplies, the URL gains a doubled segment — the page still resolves, so the only symptom is a URL that looks wrong and does not match anything already indexed.",
|
|
1044
|
+
expectedUiState: "Both URLs have the same number of segments and the same prefix pattern. A doubled prefix segment in the new post's URL is the failure.",
|
|
1045
|
+
endResult: "The post is deleted.",
|
|
1046
|
+
},
|
|
1047
|
+
"checklist-buying-offers-blog-readtime-updates-on-edit": {
|
|
1048
|
+
roles: ["admin", "guest"],
|
|
1049
|
+
startPage: "/admin/blog",
|
|
1050
|
+
steps: [
|
|
1051
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1052
|
+
"Open an existing post's editor and note its read-time estimate.",
|
|
1053
|
+
"Read its body length roughly.",
|
|
1054
|
+
"Add several paragraphs to the body and save.",
|
|
1055
|
+
"RELOAD and read the read-time estimate.",
|
|
1056
|
+
"Open the post publicly and read the estimate there.",
|
|
1057
|
+
"Restore the original body.",
|
|
1058
|
+
],
|
|
1059
|
+
expectedBehaviour: "The read-time is recomputed from the body on every save, not only at creation. Deriving it once freezes the estimate at the original length, so a post that doubles in size still claims its first estimate — a create-time transform the update path does not repeat.",
|
|
1060
|
+
expectedUiState: "The estimate rises after the body grows, in both the editor and the public post. An unchanged estimate is the failure.",
|
|
1061
|
+
endResult: "The post's body is restored.",
|
|
1062
|
+
},
|
|
1063
|
+
"checklist-buying-offers-blog-error-summary-jumps-to-section": {
|
|
1064
|
+
roles: ["admin"],
|
|
1065
|
+
startPage: "/admin/blog",
|
|
1066
|
+
steps: [
|
|
1067
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1068
|
+
"Open an existing post's editor and clear its title.",
|
|
1069
|
+
"Collapse the section containing the title.",
|
|
1070
|
+
"Save and read the error summary.",
|
|
1071
|
+
"Check the entry names the section it belongs to.",
|
|
1072
|
+
"Click the entry and read where the page lands.",
|
|
1073
|
+
"Check the section expanded and the title field is focused and marked.",
|
|
1074
|
+
],
|
|
1075
|
+
expectedBehaviour: "Clicking a summary entry expands the owning section and lands ON the field, focused. Landing on the section heading leaves the author hunting, and an error inside a collapsed section is otherwise unreachable — they are told something is wrong and cannot see what.",
|
|
1076
|
+
expectedUiState: "The entry is tagged with its section. Clicking it expands that section with the title field focused and marked. Landing on the heading is the failure.",
|
|
1077
|
+
endResult: "Restore the title without saving the empty one.",
|
|
1078
|
+
},
|
|
1079
|
+
"checklist-buying-offers-blog-media-survives-collapse": {
|
|
1080
|
+
roles: ["admin"],
|
|
1081
|
+
startPage: "/admin/blog",
|
|
1082
|
+
steps: [
|
|
1083
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1084
|
+
"Open a post's editor and start uploading public/test-media/sample-video.mp4 in the Media section.",
|
|
1085
|
+
"While the upload is still running, collapse the Media section.",
|
|
1086
|
+
"Wait a few seconds, then expand it again.",
|
|
1087
|
+
"Read whether the upload completed or was cancelled.",
|
|
1088
|
+
"Save and RELOAD, then read whether the media is attached.",
|
|
1089
|
+
"Remove the media and restore the post.",
|
|
1090
|
+
],
|
|
1091
|
+
inputs: { file: "public/test-media/sample-video.mp4" },
|
|
1092
|
+
expectedBehaviour: "Collapsing a section hides it without unmounting its work. A section that unmounts on collapse cancels the in-flight upload and discards the progress — and because collapsing feels like a purely visual action, the author has no reason to expect it.",
|
|
1093
|
+
expectedUiState: "The upload continues while collapsed and is complete on expanding. After the save and reload the media is attached. A cancelled upload is the failure.",
|
|
1094
|
+
endResult: "The post is restored to its original media.",
|
|
1095
|
+
},
|
|
1096
|
+
"checklist-buying-offers-event-editor-sections-not-steps": {
|
|
1097
|
+
roles: ["admin"],
|
|
1098
|
+
startPage: "/admin/events/new",
|
|
1099
|
+
steps: [
|
|
1100
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
1101
|
+
"Open /admin/events/new and read how the form is presented.",
|
|
1102
|
+
"Check Details, Media, Settings and Raffle are all present as sections.",
|
|
1103
|
+
"Expand each and confirm its fields render.",
|
|
1104
|
+
"Try to reach the Raffle section without completing Details.",
|
|
1105
|
+
"Check no section is gated behind another.",
|
|
1106
|
+
],
|
|
1107
|
+
expectedBehaviour: "All four areas are reachable at once as collapsible sections. A step wizard here forces an ordering the author did not choose, and the raffle settings often determine what the details should say.",
|
|
1108
|
+
expectedUiState: "Details, Media, Settings and Raffle are all present and expandable immediately, with no gating between them.",
|
|
1109
|
+
endResult: "Leave without saving.",
|
|
1110
|
+
},
|
|
1111
|
+
};
|