@mohasinac/appkit 4.34.0 → 4.35.0
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/_internal/server/features/seo/sitemap.js +2 -2
- package/dist/_internal/server/functions/scheduled.d.ts +1 -2
- package/dist/_internal/server/functions/scheduled.js +8 -9
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +15 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +15 -6
- package/dist/_internal/server/jobs/handlers/index.d.ts +0 -1
- package/dist/_internal/server/jobs/handlers/index.js +0 -1
- package/dist/features/auth/schemas/firestore.d.ts +1 -0
- 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/repository/tester-checklist-item.repository.d.ts +16 -1
- package/dist/features/tester/repository/tester-checklist-item.repository.js +32 -6
- 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/events-tester-seed-data.js +77 -5
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +23 -9
- package/dist/features/tester/seed-data/products-tester-seed-data.js +65 -49
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +744 -1
- package/dist/features/tester/seed-data/tester-window.d.ts +25 -0
- package/dist/features/tester/seed-data/tester-window.js +66 -0
- package/dist/seed/carousel-slides-seed-data.js +1 -1
- package/dist/seed/products-standard-seed-data.js +2 -2
- package/dist/seed/reviews-seed-data.js +6 -1
- package/dist/seed/users-seed-data.js +107 -4
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +6 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Authored six-part procedures for the admin/bans-and-trust page.
|
|
3
|
+
* WHAT: 12 case(s), keyed by full checklist id.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 THESE CASES BAN REAL SEEDED ACCOUNTS AND EVERY ONE RESTORES WHAT IT CHANGED.
|
|
6
|
+
* `users` is in the PRESERVE tier — a tester run never re-seeds it — so a ban left
|
|
7
|
+
* in place outlives every reseed and quietly breaks whichever other checklist page
|
|
8
|
+
* signs in as that persona. Each procedure names its restore step for that reason,
|
|
9
|
+
* and each uses a persona reserved for this page rather than one another page
|
|
10
|
+
* depends on.
|
|
11
|
+
*
|
|
12
|
+
* The unban case is the load-bearing one. Session role fields refresh only
|
|
13
|
+
* periodically, so a guard reading the cached snapshot keeps blocking a user who
|
|
14
|
+
* was unbanned a moment ago — the fix is to re-read on navigation, not to widen
|
|
15
|
+
* the refresh interval.
|
|
16
|
+
*
|
|
17
|
+
* @tag domain:tester
|
|
18
|
+
* @tag layer:seed
|
|
19
|
+
* @tag pattern:none
|
|
20
|
+
* @tag access:server-only
|
|
21
|
+
* @tag consumers:seed-data/authored/index.ts
|
|
22
|
+
* @tag sideEffects:none
|
|
23
|
+
*/
|
|
24
|
+
const SIGN_IN_ADMIN = "Sign in as admin@letitrip.in / TempPass123!.";
|
|
25
|
+
export const authored = {
|
|
26
|
+
"checklist-admin-bans-and-trust-soft-ban-blocks-signin": {
|
|
27
|
+
roles: ["admin", "buyer"],
|
|
28
|
+
startPage: "/admin/users",
|
|
29
|
+
steps: [
|
|
30
|
+
SIGN_IN_ADMIN,
|
|
31
|
+
"Open /admin/users and find the account rohit.collect@gmail.com.",
|
|
32
|
+
"Soft-ban the account with the reason 'QA ban probe — checklist case, will be lifted.'",
|
|
33
|
+
"Sign out.",
|
|
34
|
+
"Try to sign in as rohit.collect@gmail.com / TempPass123!.",
|
|
35
|
+
"Read what is shown.",
|
|
36
|
+
"Sign back in as the admin and lift the ban.",
|
|
37
|
+
],
|
|
38
|
+
inputs: { reason: "QA ban probe — checklist case, will be lifted." },
|
|
39
|
+
expectedBehaviour: "A soft-banned account cannot sign in and is told why. A refusal with a generic credentials error is worse than a block — the user believes their password is wrong and resets it, repeatedly, against an account that would refuse them either way.",
|
|
40
|
+
expectedUiState: "The sign-in is refused with a message naming the ban, not an invalid-credentials error. The reason the admin typed is visible to the user.",
|
|
41
|
+
endResult: "The ban is lifted and the account signs in again.",
|
|
42
|
+
},
|
|
43
|
+
"checklist-admin-bans-and-trust-soft-ban-requires-reason": {
|
|
44
|
+
roles: ["admin"],
|
|
45
|
+
startPage: "/admin/users",
|
|
46
|
+
steps: [
|
|
47
|
+
SIGN_IN_ADMIN,
|
|
48
|
+
"Open /admin/users and start banning the account rohit.collect@gmail.com.",
|
|
49
|
+
"Leave the reason field empty and submit.",
|
|
50
|
+
"Read what happens.",
|
|
51
|
+
"Type a one-character reason and submit.",
|
|
52
|
+
"Read whether that is accepted.",
|
|
53
|
+
"Cancel without banning.",
|
|
54
|
+
],
|
|
55
|
+
expectedBehaviour: "A ban carries a reason, enforced rather than requested. The reason is what the banned user reads and what the audit log records — a ban with none leaves both the user and the next admin with an action and no explanation.",
|
|
56
|
+
expectedUiState: "The empty submission is refused with an inline error on the reason field, not a banner. A one-character reason is refused too if a minimum length applies.",
|
|
57
|
+
endResult: "Nobody is banned.",
|
|
58
|
+
},
|
|
59
|
+
"checklist-admin-bans-and-trust-unban-restores-access": {
|
|
60
|
+
roles: ["admin", "buyer"],
|
|
61
|
+
startPage: "/admin/users",
|
|
62
|
+
steps: [
|
|
63
|
+
SIGN_IN_ADMIN,
|
|
64
|
+
"Soft-ban the account rohit.collect@gmail.com with the reason 'QA unban probe — checklist case.'",
|
|
65
|
+
"In a second browser, sign in as rohit.collect@gmail.com / TempPass123! and confirm the block.",
|
|
66
|
+
"As the admin, lift the ban.",
|
|
67
|
+
"In the second browser, WITHOUT signing out or reloading, navigate to another page.",
|
|
68
|
+
"Read whether access is restored.",
|
|
69
|
+
"Note how long it took, if it was not immediate.",
|
|
70
|
+
],
|
|
71
|
+
inputs: { reason: "QA unban probe — checklist case." },
|
|
72
|
+
expectedBehaviour: "A lifted ban takes effect on the next navigation. Role and status fields on a session refresh only periodically, so a guard reading that cached snapshot keeps blocking for minutes after the ban is gone — which reads to the user as the unban not having worked. The guard re-reads on navigation into a gated area for exactly this reason.",
|
|
73
|
+
expectedUiState: "Access is restored on the next navigation, without a sign-out or a hard reload. A block persisting for minutes is the finding, recorded with how long.",
|
|
74
|
+
endResult: "The account is unbanned and working.",
|
|
75
|
+
},
|
|
76
|
+
"checklist-admin-bans-and-trust-hard-ban-cascade-runs": {
|
|
77
|
+
roles: ["admin"],
|
|
78
|
+
startPage: "/admin/users",
|
|
79
|
+
steps: [
|
|
80
|
+
SIGN_IN_ADMIN,
|
|
81
|
+
"Open /admin/users and hard-ban the account arjun.builder@gmail.com with the reason 'QA hard-ban cascade probe — checklist case.'",
|
|
82
|
+
"Read what the interface says immediately after submitting.",
|
|
83
|
+
"Check it reports that work was queued rather than claiming completion.",
|
|
84
|
+
"Wait for the completion signal and read what it reports.",
|
|
85
|
+
"Open the account and read its state.",
|
|
86
|
+
"Lift the ban afterwards.",
|
|
87
|
+
],
|
|
88
|
+
inputs: { reason: "QA hard-ban cascade probe — checklist case." },
|
|
89
|
+
expectedBehaviour: "A hard ban runs a multi-stage cascade that exceeds a single request's time budget, so it is queued as a background job and its completion is reported back. A screen that claims success at submit time is claiming an outcome it cannot know — the job may still fail.",
|
|
90
|
+
expectedUiState: "The submit reports queued work, a completion signal follows, and the account's state reflects the finished cascade. An immediate unqualified success message is the finding.",
|
|
91
|
+
endResult: "The ban is lifted.",
|
|
92
|
+
},
|
|
93
|
+
"checklist-admin-bans-and-trust-hard-ban-recorded-in-audit-log": {
|
|
94
|
+
roles: ["admin"],
|
|
95
|
+
startPage: "/admin/audit-log",
|
|
96
|
+
steps: [
|
|
97
|
+
SIGN_IN_ADMIN,
|
|
98
|
+
"Soft-ban the account rohit.collect@gmail.com with the reason 'QA audit-log probe — checklist case.', then lift it.",
|
|
99
|
+
"Open /admin/audit-log.",
|
|
100
|
+
"Find the ban entry and the unban entry.",
|
|
101
|
+
"Read the acting admin, the target and the reason on each.",
|
|
102
|
+
"Check the reason is the one typed.",
|
|
103
|
+
"Check no personal name or email of the banned user appears in the entry's stored detail.",
|
|
104
|
+
],
|
|
105
|
+
inputs: { reason: "QA audit-log probe — checklist case." },
|
|
106
|
+
expectedBehaviour: "Bans, unbans and role changes are the privileged actions the audit log exists for, and each records who acted and why. The log is not encrypted, so it carries the target's identifier rather than their name or email — a readable name there is a leak into a surface that was never designed to hold one.",
|
|
107
|
+
expectedUiState: "Both entries present with the acting admin, the target and the exact reason. A missing entry is one finding; a readable email or personal name in the entry's detail is a different and more serious one.",
|
|
108
|
+
endResult: "The account is unbanned; the log entries remain.",
|
|
109
|
+
},
|
|
110
|
+
"checklist-admin-bans-and-trust-ban-status-visible-in-list": {
|
|
111
|
+
roles: ["admin"],
|
|
112
|
+
startPage: "/admin/users",
|
|
113
|
+
steps: [
|
|
114
|
+
SIGN_IN_ADMIN,
|
|
115
|
+
"Soft-ban the account rohit.collect@gmail.com with the reason 'QA list-marker probe — checklist case.'",
|
|
116
|
+
"Return to /admin/users without opening the account.",
|
|
117
|
+
"Find the row and read what marks it as banned.",
|
|
118
|
+
"Check the marker is readable against its background.",
|
|
119
|
+
"Switch to dark mode and read it again.",
|
|
120
|
+
"Lift the ban and check the marker clears.",
|
|
121
|
+
],
|
|
122
|
+
inputs: { reason: "QA list-marker probe — checklist case." },
|
|
123
|
+
expectedBehaviour: "A banned account is marked in the list without opening it, and the marker is legible in both themes. A status tint and its ink both invert with the theme, so a badge pairing a tint with fixed white text is invisible in exactly one of them.",
|
|
124
|
+
expectedUiState: "A readable badge on the row in both light and dark mode, cleared when the ban is lifted. White-on-near-white in either theme is the finding.",
|
|
125
|
+
endResult: "The account is unbanned and unmarked.",
|
|
126
|
+
},
|
|
127
|
+
"checklist-admin-bans-and-trust-ban-filter-returns-banned-only": {
|
|
128
|
+
roles: ["admin"],
|
|
129
|
+
startPage: "/admin/users",
|
|
130
|
+
steps: [
|
|
131
|
+
SIGN_IN_ADMIN,
|
|
132
|
+
"Soft-ban the account rohit.collect@gmail.com with the reason 'QA filter probe — checklist case.'",
|
|
133
|
+
"Open /admin/users and apply the filter for banned accounts.",
|
|
134
|
+
"Read the rows returned and check the account rohit.collect@gmail.com is among them.",
|
|
135
|
+
"Check no unbanned account is in the result.",
|
|
136
|
+
"Clear the filter and check the full list returns.",
|
|
137
|
+
"Lift the ban and re-apply the filter, checking the account has left it.",
|
|
138
|
+
],
|
|
139
|
+
inputs: { reason: "QA filter probe — checklist case." },
|
|
140
|
+
expectedBehaviour: "The filter returns exactly the banned accounts. A filter chip whose value does not byte-for-byte match a stored one matches nothing forever and reports no error — an empty result is indistinguishable from 'no banned users', which is why the case bans somebody first.",
|
|
141
|
+
expectedUiState: "The banned account is in the filtered result and unbanned accounts are not. An empty result with a known banned account present is the finding.",
|
|
142
|
+
endResult: "The account is unbanned.",
|
|
143
|
+
},
|
|
144
|
+
"checklist-admin-bans-and-trust-session-ip-masked": {
|
|
145
|
+
roles: ["admin"],
|
|
146
|
+
startPage: "/admin/sessions",
|
|
147
|
+
steps: [
|
|
148
|
+
SIGN_IN_ADMIN,
|
|
149
|
+
"Open /admin/sessions and read a session's IP address as displayed.",
|
|
150
|
+
"Check it is partially hidden rather than complete.",
|
|
151
|
+
"Open the browser's network panel and read the response that populated the list.",
|
|
152
|
+
"Search that payload for a complete IP address.",
|
|
153
|
+
"Search the page's own embedded data for one as well.",
|
|
154
|
+
],
|
|
155
|
+
expectedBehaviour: "The IP is masked before it leaves the server, not on the way to the screen. A masked display over an unmasked payload is still a leak — the value is in the page and in the browser's history, and only the rendering hides it.",
|
|
156
|
+
expectedUiState: "The displayed value is partial AND no complete IP appears in the payload or the page data. A complete address anywhere is the finding, with where it was found.",
|
|
157
|
+
endResult: "Read-only.",
|
|
158
|
+
},
|
|
159
|
+
"checklist-admin-bans-and-trust-sessions-list-device-details": {
|
|
160
|
+
roles: ["admin"],
|
|
161
|
+
startPage: "/admin/sessions",
|
|
162
|
+
steps: [
|
|
163
|
+
SIGN_IN_ADMIN,
|
|
164
|
+
"Open /admin/sessions.",
|
|
165
|
+
"Read each row's browser, operating system and device.",
|
|
166
|
+
"Read the last-activity time and check it is a real timestamp rather than a placeholder.",
|
|
167
|
+
"Sort or filter by activity and check the order follows.",
|
|
168
|
+
"Open a session's detail, if one is offered, and read the same fields there.",
|
|
169
|
+
],
|
|
170
|
+
expectedBehaviour: "Each session states which device it belongs to and when it was last used, because that is what an admin needs to decide whether to revoke it. A row showing only an identifier makes the decision impossible.",
|
|
171
|
+
expectedUiState: "Browser, operating system, device and a real last-activity timestamp on every row, with sorting that follows. A dash where a device should be is a finding.",
|
|
172
|
+
endResult: "Read-only.",
|
|
173
|
+
},
|
|
174
|
+
"checklist-admin-bans-and-trust-revoke-session-signs-out": {
|
|
175
|
+
roles: ["admin", "buyer"],
|
|
176
|
+
startPage: "/admin/sessions",
|
|
177
|
+
steps: [
|
|
178
|
+
"In a second browser, sign in as rohit.collect@gmail.com / TempPass123! and leave a page open.",
|
|
179
|
+
SIGN_IN_ADMIN,
|
|
180
|
+
"Open /admin/sessions and find that account's session.",
|
|
181
|
+
"Revoke it.",
|
|
182
|
+
"In the second browser, navigate to any signed-in page.",
|
|
183
|
+
"Read whether the account is still signed in.",
|
|
184
|
+
"Reload the admin sessions list and check the row is gone.",
|
|
185
|
+
],
|
|
186
|
+
expectedBehaviour: "Revoking a session ends it. Removing the row without invalidating the credential is the failure that matters — it reads as done, and the device it was supposed to lock out keeps working indefinitely.",
|
|
187
|
+
expectedUiState: "The second browser is signed out on its next navigation and the row is gone from the list. A still-signed-in second browser is the finding, regardless of the row disappearing.",
|
|
188
|
+
endResult: "The session is ended; sign the account back in if another case needs it.",
|
|
189
|
+
},
|
|
190
|
+
"checklist-admin-bans-and-trust-unban-request-reaches-admin": {
|
|
191
|
+
roles: ["admin", "buyer"],
|
|
192
|
+
startPage: "/admin/users",
|
|
193
|
+
steps: [
|
|
194
|
+
SIGN_IN_ADMIN,
|
|
195
|
+
"Soft-ban the account rohit.collect@gmail.com with the reason 'QA unban-request probe — checklist case.'",
|
|
196
|
+
"In a second browser, sign in as rohit.collect@gmail.com / TempPass123! and follow whatever route the block offers for appealing.",
|
|
197
|
+
"Submit an unban request with the text 'QA unban request — checklist case, please ignore.'",
|
|
198
|
+
"As the admin, find that request in an admin surface.",
|
|
199
|
+
"Read whether it can be actioned from there.",
|
|
200
|
+
"Lift the ban afterwards.",
|
|
201
|
+
],
|
|
202
|
+
inputs: {
|
|
203
|
+
banReason: "QA unban-request probe — checklist case.",
|
|
204
|
+
requestText: "QA unban request — checklist case, please ignore.",
|
|
205
|
+
},
|
|
206
|
+
expectedBehaviour: "An appeal a banned user can submit must land somewhere an admin will see it. A submission form with no admin-side surface is the worst half-built shape available here — the user is told their appeal was received, and nobody will ever read it.",
|
|
207
|
+
expectedUiState: "The request is visible to the admin with its text and its author, and can be actioned. A form with no admin surface behind it is the finding.",
|
|
208
|
+
endResult: "The account is unbanned.",
|
|
209
|
+
},
|
|
210
|
+
"checklist-admin-bans-and-trust-banned-user-content-handling": {
|
|
211
|
+
roles: ["admin", "seller"],
|
|
212
|
+
startPage: "/admin/users",
|
|
213
|
+
steps: [
|
|
214
|
+
"Open /stores/store-beyblade-arena as a signed-out visitor and note that its listings are visible.",
|
|
215
|
+
SIGN_IN_ADMIN,
|
|
216
|
+
"Soft-ban the seller account tyson@beybladearena.in with the reason 'QA seller-ban content probe — checklist case.'",
|
|
217
|
+
"As a signed-out visitor, reload /stores/store-beyblade-arena and read what is shown.",
|
|
218
|
+
"Open /products and check whether that store's listings still appear.",
|
|
219
|
+
"Lift the ban and reload both.",
|
|
220
|
+
],
|
|
221
|
+
inputs: { storeId: "store-beyblade-arena", reason: "QA seller-ban content probe — checklist case." },
|
|
222
|
+
expectedBehaviour: "Banning a seller takes their storefront and listings out of public view, and lifting it restores them. Public visibility is gated on the store's own status rather than the user's, so flipping only the user record leaves a banned seller's catalogue fully purchasable.",
|
|
223
|
+
expectedUiState: "After the ban, the store page and its listings are not publicly reachable; after the lift, both return exactly as before. Listings still buyable during the ban is the finding.",
|
|
224
|
+
endResult: "The ban is lifted and the store is public again.",
|
|
225
|
+
},
|
|
226
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Authored six-part procedures for the admin/blog-faqs page.
|
|
3
|
+
* WHAT: 3 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
|
+
* 🛑 A FAQ'S ANSWER IS NOT A STRING. The create path wraps a flat answer into a
|
|
10
|
+
* text-plus-format object and writes the slug to a nested path; an update path
|
|
11
|
+
* that spreads the body straight through writes a bare string into a field every
|
|
12
|
+
* reader expects to be an object. That transform asymmetry is what the FAQ case
|
|
13
|
+
* below is really testing, and it only shows after a reload.
|
|
14
|
+
*
|
|
15
|
+
* @tag domain:tester
|
|
16
|
+
* @tag layer:seed
|
|
17
|
+
* @tag pattern:none
|
|
18
|
+
* @tag access:server-only
|
|
19
|
+
* @tag consumers:seed-data/authored/index.ts
|
|
20
|
+
* @tag sideEffects:none
|
|
21
|
+
*/
|
|
22
|
+
export const authored = {
|
|
23
|
+
"checklist-admin-blog-faqs-blog-create-edit-publish": {
|
|
24
|
+
roles: ["admin", "guest"],
|
|
25
|
+
startPage: "/admin/blog",
|
|
26
|
+
steps: [
|
|
27
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
28
|
+
"Open the admin blog list and create a post titled 'QA Blog create-edit-publish' with an excerpt, a category, two tags and body content.",
|
|
29
|
+
"Save it as a DRAFT and read the status shown.",
|
|
30
|
+
"Open /blog in a private window and check the draft is NOT listed.",
|
|
31
|
+
"Return to the admin editor, change the body, publish, and RELOAD the editor.",
|
|
32
|
+
"Read every field to confirm only the body changed and the status is published.",
|
|
33
|
+
"Open /blog in the private window and open the post.",
|
|
34
|
+
"Delete the post and confirm it disappears from both the admin list and /blog.",
|
|
35
|
+
],
|
|
36
|
+
inputs: { title: "QA Blog create-edit-publish" },
|
|
37
|
+
expectedBehaviour: "Draft and published are genuinely different: a draft is invisible publicly. The slug is derived from the title at creation and is then immutable even though the title stays editable — that is the deliberate convention across this codebase, because recomputing it would break existing links.",
|
|
38
|
+
expectedUiState: "The draft does not appear on /blog. After publishing it does, with the edited body. The editor's other fields are unchanged after the edit. The URL keeps its original slug after any title change.",
|
|
39
|
+
endResult: "The post is deleted from both surfaces. A draft visible publicly is the most serious failure here.",
|
|
40
|
+
},
|
|
41
|
+
"checklist-admin-blog-faqs-blog-media-step-save": {
|
|
42
|
+
roles: ["admin", "guest"],
|
|
43
|
+
startPage: "/admin/blog",
|
|
44
|
+
steps: [
|
|
45
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
46
|
+
"Open the admin blog editor for an existing published post and note its current cover image.",
|
|
47
|
+
"Upload public/test-media/sample-image.png as the cover image.",
|
|
48
|
+
"Insert a second image into the body content itself.",
|
|
49
|
+
"Save and RELOAD the editor, reading both the cover and the body.",
|
|
50
|
+
"Open the post on /blog in a private window and read both images.",
|
|
51
|
+
"Restore the original cover image and remove the inserted body image.",
|
|
52
|
+
],
|
|
53
|
+
inputs: { coverImage: "public/test-media/sample-image.png" },
|
|
54
|
+
expectedBehaviour: "The cover image and inline body images both persist and both render publicly. A blog post has NO gallery field — extra images live inside the body HTML — so a case looking for a separate image array is looking for something the document has never had. Newly uploaded media must also be finalised on update, not only on create, or the file is orphaned in temporary storage.",
|
|
55
|
+
expectedUiState: "After the reload the cover and the inline image are both present in the editor, and both render on the public post. An image that previews during editing and is missing after the reload was never finalised.",
|
|
56
|
+
endResult: "The post is restored to its original media. Record any image that vanished on reload — that is the orphaned-upload failure.",
|
|
57
|
+
},
|
|
58
|
+
"checklist-admin-blog-faqs-faq-create-edit-category": {
|
|
59
|
+
roles: ["admin", "guest"],
|
|
60
|
+
startPage: "/admin/faqs",
|
|
61
|
+
steps: [
|
|
62
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
63
|
+
"Open the admin FAQs list and create one: question 'QA FAQ create-edit-category?', answer 'Created by the tester checklist.', category Shipping.",
|
|
64
|
+
"Save, RELOAD, and read the answer as it is displayed.",
|
|
65
|
+
"Open /faqs in a private window and find the question under Shipping.",
|
|
66
|
+
"Return to the admin editor, change ONLY the answer to 'Edited by the tester checklist.', and save.",
|
|
67
|
+
"RELOAD the editor and read the answer, then open /faqs again and read it there.",
|
|
68
|
+
"Change the category to Payments, save, reload, and check it moved category on /faqs.",
|
|
69
|
+
"Delete the FAQ and confirm it disappears from both surfaces.",
|
|
70
|
+
],
|
|
71
|
+
inputs: {
|
|
72
|
+
question: "QA FAQ create-edit-category?",
|
|
73
|
+
answerBefore: "Created by the tester checklist.",
|
|
74
|
+
answerAfter: "Edited by the tester checklist.",
|
|
75
|
+
categoryBefore: "Shipping",
|
|
76
|
+
categoryAfter: "Payments",
|
|
77
|
+
},
|
|
78
|
+
expectedBehaviour: "The EDIT is the case. Creating a FAQ wraps the flat answer into an object and writes the slug to a nested path; an update that spreads the submitted body straight into storage writes a bare string into the field every reader expects to be an object. The admin form may still render it — the public page is where the wrong shape surfaces.",
|
|
79
|
+
expectedUiState: "After the edit and reload the answer reads as prose in BOTH the admin editor and on /faqs. What must not appear is raw markup, an object rendered as text, or a blank answer on the public page while the admin form looks fine.",
|
|
80
|
+
endResult: "The FAQ is deleted from both surfaces. Checking /faqs after the edit — not only the admin form — is what makes this case worth running.",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Authored six-part procedures for the admin/bulk-actions page.
|
|
3
|
+
* WHAT: 10 case(s), keyed by full checklist id.
|
|
4
|
+
*
|
|
5
|
+
* A bulk action multiplies both the work and the mistake. Two failure shapes recur
|
|
6
|
+
* and both read as success: an action whose handler does nothing but clear the
|
|
7
|
+
* selection, and a destructive action with no confirmation, which executes
|
|
8
|
+
* immediately on a selection the admin may have built by accident.
|
|
9
|
+
*
|
|
10
|
+
* The bar itself belongs to a measured bottom tier shared with every other fixed
|
|
11
|
+
* bar, and it must hide by COLLAPSING rather than by sliding away — a bar that
|
|
12
|
+
* keeps its layout height while invisible over-reports the tier on every page that
|
|
13
|
+
* has no bar, pushing floating controls up the screen site-wide.
|
|
14
|
+
*
|
|
15
|
+
* @tag domain:tester
|
|
16
|
+
* @tag layer:seed
|
|
17
|
+
* @tag pattern:none
|
|
18
|
+
* @tag access:server-only
|
|
19
|
+
* @tag consumers:seed-data/authored/index.ts
|
|
20
|
+
* @tag sideEffects:none
|
|
21
|
+
*/
|
|
22
|
+
const SIGN_IN_ADMIN = "Sign in as admin@letitrip.in / TempPass123!.";
|
|
23
|
+
export const authored = {
|
|
24
|
+
"checklist-admin-bulk-actions-select-all-count-matches-page": {
|
|
25
|
+
roles: ["admin"],
|
|
26
|
+
startPage: "/admin/products",
|
|
27
|
+
steps: [
|
|
28
|
+
SIGN_IN_ADMIN,
|
|
29
|
+
"Open /admin/products and count the rows on the page.",
|
|
30
|
+
"Use 'Select all'.",
|
|
31
|
+
"Read the count reported on the bulk bar.",
|
|
32
|
+
"Compare it against the number of visible rows.",
|
|
33
|
+
"Page forward and read whether the selection or its count followed.",
|
|
34
|
+
],
|
|
35
|
+
expectedBehaviour: "Select-all covers the rows on screen and says how many. A count larger than what is visible means it is selecting rows the admin cannot see — and the next destructive action then affects them.",
|
|
36
|
+
expectedUiState: "The reported count equals the visible row count. A larger number is the finding, with both figures.",
|
|
37
|
+
endResult: "Clear the selection.",
|
|
38
|
+
},
|
|
39
|
+
"checklist-admin-bulk-actions-bulk-bar-appears-on-selection": {
|
|
40
|
+
roles: ["admin"],
|
|
41
|
+
startPage: "/admin/products",
|
|
42
|
+
steps: [
|
|
43
|
+
SIGN_IN_ADMIN,
|
|
44
|
+
"Open /admin/products with nothing selected and note where the page's content ends at the bottom.",
|
|
45
|
+
"Select one row and read whether a bulk bar appears.",
|
|
46
|
+
"Deselect it and read whether the bar goes.",
|
|
47
|
+
"With the bar gone, check no empty strip, hairline or shadow is left where it was.",
|
|
48
|
+
"Scroll to the very bottom with nothing selected and check no reserved gap remains.",
|
|
49
|
+
],
|
|
50
|
+
expectedBehaviour: "The bar appears on the first selection and collapses to zero height on the last deselection. Hiding it by sliding it out of view leaves its height behind, so the shared bottom tier over-reports on every page with no bar — and the tier's background and border must sit INSIDE the collapsing box, or they paint a hairline across the screen at zero height.",
|
|
51
|
+
expectedUiState: "The bar appears and disappears with the selection, leaving no strip, hairline, shadow or reserved gap behind.",
|
|
52
|
+
endResult: "Nothing is selected.",
|
|
53
|
+
},
|
|
54
|
+
"checklist-admin-bulk-actions-bulk-destructive-confirms": {
|
|
55
|
+
roles: ["admin"],
|
|
56
|
+
startPage: "/admin/products",
|
|
57
|
+
steps: [
|
|
58
|
+
SIGN_IN_ADMIN,
|
|
59
|
+
"Open /admin/products and select three rows.",
|
|
60
|
+
"Trigger the destructive bulk action offered.",
|
|
61
|
+
"Read the dialog WITHOUT confirming.",
|
|
62
|
+
"Check it names the action and how many rows it will affect.",
|
|
63
|
+
"Cancel it and check all three rows are untouched.",
|
|
64
|
+
"Repeat on /admin/users and /admin/orders.",
|
|
65
|
+
],
|
|
66
|
+
expectedBehaviour: "Every destructive bulk action confirms first, naming the count. An action defined inline at its call site rather than in the shared registry has no confirmation configured at all, and so runs immediately and irreversibly — on the largest selection the admin has ever made, since bulk is where selections are largest.",
|
|
67
|
+
expectedUiState: "A confirmation naming the action and the row count appears on all three surfaces, and cancelling changes nothing. A destructive action that runs on click is the finding.",
|
|
68
|
+
endResult: "Nothing is deleted.",
|
|
69
|
+
},
|
|
70
|
+
"checklist-admin-bulk-actions-bulk-action-reports-result": {
|
|
71
|
+
roles: ["admin"],
|
|
72
|
+
startPage: "/admin/products",
|
|
73
|
+
steps: [
|
|
74
|
+
SIGN_IN_ADMIN,
|
|
75
|
+
"Open /admin/products and select three rows.",
|
|
76
|
+
"Run a non-destructive bulk action on them, such as a status or flag change.",
|
|
77
|
+
"Read the message shown when it completes.",
|
|
78
|
+
"Check it states how many rows succeeded and how many failed.",
|
|
79
|
+
"Check the affected rows show the change without a manual reload.",
|
|
80
|
+
],
|
|
81
|
+
expectedBehaviour: "The result names the counts. A bare 'Done' after an action over N rows tells the admin nothing about whether it was N — and a partial success reported as an unqualified success is how rows silently stay unchanged.",
|
|
82
|
+
expectedUiState: "A message naming the succeeded and failed counts, and the rows updated in place. A bare success message is the finding.",
|
|
83
|
+
endResult: "Restore whatever the action changed.",
|
|
84
|
+
},
|
|
85
|
+
"checklist-admin-bulk-actions-bulk-partial-failure-named": {
|
|
86
|
+
roles: ["admin"],
|
|
87
|
+
startPage: "/admin/products",
|
|
88
|
+
steps: [
|
|
89
|
+
SIGN_IN_ADMIN,
|
|
90
|
+
"Open /admin/products and build a selection mixing rows the action can apply to with rows it cannot — an archived row alongside published ones, for example.",
|
|
91
|
+
"Run the action.",
|
|
92
|
+
"Read the result message.",
|
|
93
|
+
"Check the rows that failed are named rather than only counted.",
|
|
94
|
+
"Check the rows that succeeded did in fact change.",
|
|
95
|
+
],
|
|
96
|
+
expectedBehaviour: "Failures are named. 'Two rows failed' out of a selection of twenty leaves the admin re-checking twenty rows by hand to find which two — and the usual next step is to run the action again on everything, which is worse.",
|
|
97
|
+
expectedUiState: "The failed rows are identified by title or id, with a reason. A bare failure count is the finding.",
|
|
98
|
+
endResult: "Restore whatever succeeded.",
|
|
99
|
+
},
|
|
100
|
+
"checklist-admin-bulk-actions-bulk-selection-clears-after-run": {
|
|
101
|
+
roles: ["admin"],
|
|
102
|
+
startPage: "/admin/products",
|
|
103
|
+
steps: [
|
|
104
|
+
SIGN_IN_ADMIN,
|
|
105
|
+
"Open /admin/products and select two rows.",
|
|
106
|
+
"Run a bulk action and let it complete.",
|
|
107
|
+
"Read whether the rows are still selected.",
|
|
108
|
+
"Read whether the bulk bar is still shown.",
|
|
109
|
+
"Read whether the list reflects the change without a reload.",
|
|
110
|
+
"Reload and check the change held.",
|
|
111
|
+
],
|
|
112
|
+
expectedBehaviour: "The selection clears when the action completes and the list reflects the result. A selection left in place invites the admin to run a second action on rows that have already been acted on, and on a destructive action that is unrecoverable.",
|
|
113
|
+
expectedUiState: "No rows selected, no bulk bar, the list updated in place, and the change still there after a reload.",
|
|
114
|
+
endResult: "Restore whatever the action changed.",
|
|
115
|
+
},
|
|
116
|
+
"checklist-admin-bulk-actions-bulk-selection-survives-filter-change": {
|
|
117
|
+
roles: ["admin"],
|
|
118
|
+
startPage: "/admin/products",
|
|
119
|
+
steps: [
|
|
120
|
+
SIGN_IN_ADMIN,
|
|
121
|
+
"Open /admin/products and select three rows.",
|
|
122
|
+
"Change a filter so that at least one selected row is no longer shown.",
|
|
123
|
+
"Read the bulk bar's count.",
|
|
124
|
+
"Read whether the selection was cleared or kept.",
|
|
125
|
+
"If it was kept, run a harmless action and check which rows it affected.",
|
|
126
|
+
],
|
|
127
|
+
expectedBehaviour: "Either the selection clears visibly, or it stays and the count keeps saying honestly how many rows it covers. What must not happen is an action running against rows the admin can no longer see — the confirmation names a count they cannot reconcile with the screen.",
|
|
128
|
+
expectedUiState: "The count matches what the bar claims, and any action affects exactly those rows. A silent carry-over of hidden rows into a destructive action is the finding.",
|
|
129
|
+
endResult: "Clear the selection.",
|
|
130
|
+
},
|
|
131
|
+
"checklist-admin-bulk-actions-bulk-actions-from-registry": {
|
|
132
|
+
roles: ["admin", "seller"],
|
|
133
|
+
startPage: "/admin/products",
|
|
134
|
+
steps: [
|
|
135
|
+
SIGN_IN_ADMIN,
|
|
136
|
+
"Open /admin/products, select a row, and write down every bulk action offered with its exact label.",
|
|
137
|
+
"Sign in as tyson@beybladearena.in / TempPass123!.",
|
|
138
|
+
"Open /store/products, select a row, and write down the same.",
|
|
139
|
+
"Compare the labels of any action that appears in both lists.",
|
|
140
|
+
"Check no label differs only in wording.",
|
|
141
|
+
],
|
|
142
|
+
expectedBehaviour: "Actions come from a shared registry, so an action offered in two places carries one label. Two hand-written copies drift in wording first and in behaviour second, and a differently-worded destructive action is one an admin will hesitate over or misread.",
|
|
143
|
+
expectedUiState: "Any action present on both surfaces reads identically. A wording difference is a finding, quoting both.",
|
|
144
|
+
endResult: "Clear both selections.",
|
|
145
|
+
},
|
|
146
|
+
"checklist-admin-bulk-actions-bulk-no-dead-actions": {
|
|
147
|
+
roles: ["admin"],
|
|
148
|
+
startPage: "/admin/offers",
|
|
149
|
+
steps: [
|
|
150
|
+
SIGN_IN_ADMIN,
|
|
151
|
+
"Open /admin/offers and select two rows.",
|
|
152
|
+
"Read every bulk action offered.",
|
|
153
|
+
"Run each one in turn, noting what changed.",
|
|
154
|
+
"Reload after each and check whether the records actually changed.",
|
|
155
|
+
"Record any action that only cleared the selection.",
|
|
156
|
+
],
|
|
157
|
+
expectedBehaviour: "Every offered action does what its label says. A destructively-labelled bulk action once existed here whose handler only cleared the selection — it read as working, cancelled nothing, and an admin using it believed a batch of offers had been cancelled.",
|
|
158
|
+
expectedUiState: "Each action produces a visible, persisted change matching its label. An action whose only effect is clearing the selection is the finding, named.",
|
|
159
|
+
endResult: "Restore whatever the actions changed.",
|
|
160
|
+
},
|
|
161
|
+
"checklist-admin-bulk-actions-bulk-users-actions": {
|
|
162
|
+
roles: ["admin"],
|
|
163
|
+
startPage: "/admin/users",
|
|
164
|
+
steps: [
|
|
165
|
+
SIGN_IN_ADMIN,
|
|
166
|
+
"Open /admin/users and select two accounts that no other checklist page signs in as.",
|
|
167
|
+
"Run a reversible bulk action on them.",
|
|
168
|
+
"Read the result message.",
|
|
169
|
+
"Open each account and check the change applied to both.",
|
|
170
|
+
"Open /admin/audit-log and check both changes are recorded with the acting admin.",
|
|
171
|
+
"Reverse the change on both accounts.",
|
|
172
|
+
],
|
|
173
|
+
expectedBehaviour: "A bulk action over accounts applies to every selected account and each change is audited individually. One log entry for a batch loses which accounts were affected, which is the only thing the log is there to answer.",
|
|
174
|
+
expectedUiState: "Both accounts changed, and both appear separately in the audit log with the acting admin. One entry for the batch, or a missing entry, are both findings.",
|
|
175
|
+
endResult: "Both accounts are restored.",
|
|
176
|
+
},
|
|
177
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Authored six-part procedures for the admin/bundles page.
|
|
3
|
+
* WHAT: 4 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
|
+
* 🛑 A BUNDLE IS A CATEGORY ROW, not a listing type, and its member list is
|
|
10
|
+
* MIRRORED onto the bundle for index-friendly reads. Every public reader consults
|
|
11
|
+
* that mirror, so a write path that updates the rule and forgets the mirror
|
|
12
|
+
* produces a bundle that renders as "0 items" with no error anywhere.
|
|
13
|
+
*
|
|
14
|
+
* @tag domain:tester
|
|
15
|
+
* @tag layer:seed
|
|
16
|
+
* @tag pattern:none
|
|
17
|
+
* @tag access:server-only
|
|
18
|
+
* @tag consumers:seed-data/authored/index.ts
|
|
19
|
+
* @tag sideEffects:none
|
|
20
|
+
*/
|
|
21
|
+
export const authored = {
|
|
22
|
+
"checklist-admin-bundles-bundle-create": {
|
|
23
|
+
roles: ["admin", "guest"],
|
|
24
|
+
startPage: "/admin/bundles",
|
|
25
|
+
steps: [
|
|
26
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
27
|
+
"Open /admin/bundles and create one named 'QA Bundle admin-create'.",
|
|
28
|
+
"Add product-beyblade-burst-valkyrie (₹999) and product-beyblade-x-wizard-arrow (₹899) as members.",
|
|
29
|
+
"Set the bundle price to 1500 and save.",
|
|
30
|
+
"RELOAD the editor and read the members, the price and the original total.",
|
|
31
|
+
"Open /bundles/{slug} in a private window and read the members, price and discount badge.",
|
|
32
|
+
"Attempt to add a member from a different store and read what happens.",
|
|
33
|
+
],
|
|
34
|
+
inputs: {
|
|
35
|
+
name: "QA Bundle admin-create",
|
|
36
|
+
member1: "product-beyblade-burst-valkyrie",
|
|
37
|
+
member2: "product-beyblade-x-wizard-arrow",
|
|
38
|
+
bundlePrice: 1500,
|
|
39
|
+
memberTotal: 1898,
|
|
40
|
+
},
|
|
41
|
+
expectedBehaviour: "The bundle stores its members, its locked price and the members' undiscounted total — the last of these is what drives the discount badge, so a bundle without it shows a price and no saving. Members must come from one store: the store id is the order-splitting key, and a cross-store bundle produces an order belonging to one seller holding another's products.",
|
|
42
|
+
expectedUiState: "After the reload both members are listed with a bundle price of ₹1,500 against ₹1,898. The public page shows both members and a discount badge. The cross-store member is refused at save time with a message naming the reason.",
|
|
43
|
+
expectedData: { memberCount: 2, bundlePrice: 1500 },
|
|
44
|
+
endResult: "Leave the bundle in place — the next three cases read it.",
|
|
45
|
+
},
|
|
46
|
+
"checklist-admin-bundles-bundle-stock-sync": {
|
|
47
|
+
roles: ["admin", "seller", "guest"],
|
|
48
|
+
startPage: "/admin/bundles",
|
|
49
|
+
steps: [
|
|
50
|
+
"Sign in as admin@letitrip.in / TempPass123! and open /bundles/{slug} for 'QA Bundle admin-create' in a private window, noting it is purchasable.",
|
|
51
|
+
"Sign in as tyson@beybladearena.in / TempPass123! and set product-beyblade-x-wizard-arrow's stock to 0.",
|
|
52
|
+
"Reload the bundle's public page and read its availability.",
|
|
53
|
+
"Open /admin/bundles and read the bundle's row.",
|
|
54
|
+
"Set the product's stock back to its original value.",
|
|
55
|
+
"Reload the bundle's public page and read its availability again.",
|
|
56
|
+
],
|
|
57
|
+
inputs: { member: "product-beyblade-x-wizard-arrow" },
|
|
58
|
+
expectedBehaviour: "A bundle is all-or-nothing, so one member going out of stock makes the whole bundle unavailable. That is kept in sync by a hook on the member's stock change rather than computed per request — which is what makes the reverse direction worth testing too: restoring the stock must restore the bundle.",
|
|
59
|
+
expectedUiState: "With the member at zero the bundle reads as unavailable and cannot be purchased. After the stock is restored it becomes purchasable again. A bundle still offering purchase with a zero-stock member is the failure.",
|
|
60
|
+
endResult: "The member's stock is restored. Both directions matter — a sync that only ever disables is half a sync.",
|
|
61
|
+
},
|
|
62
|
+
"checklist-admin-bundles-bundle-edit-delete": {
|
|
63
|
+
roles: ["admin", "guest"],
|
|
64
|
+
startPage: "/admin/bundles",
|
|
65
|
+
steps: [
|
|
66
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
67
|
+
"Open the editor for 'QA Bundle admin-create' and write down every field.",
|
|
68
|
+
"Change ONLY the bundle price to 1400 and save.",
|
|
69
|
+
"RELOAD and compare every field against what was written down.",
|
|
70
|
+
"Remove one member, save, RELOAD, and read the member list and the original total.",
|
|
71
|
+
"Open the public page and read the members and the discount.",
|
|
72
|
+
"Delete the bundle and confirm it is gone from /admin/bundles and from /bundles.",
|
|
73
|
+
],
|
|
74
|
+
inputs: { priceBefore: 1500, priceAfter: 1400 },
|
|
75
|
+
expectedBehaviour: "An edit writes back only what changed, and removing a member recomputes the undiscounted total — a stale total makes the discount badge claim a saving that no longer exists. The delete removes it from the public listing too.",
|
|
76
|
+
expectedUiState: "After the price edit only the price differs. After the member removal the list holds one member and the original total has fallen accordingly. After the delete the bundle is absent from both surfaces.",
|
|
77
|
+
endResult: "The bundle is deleted. A public page still rendering a deleted bundle is a cache or projection failure worth naming.",
|
|
78
|
+
},
|
|
79
|
+
"checklist-admin-bundles-bundle-brand-picker": {
|
|
80
|
+
roles: ["admin", "guest"],
|
|
81
|
+
startPage: "/admin/bundles",
|
|
82
|
+
steps: [
|
|
83
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
84
|
+
"Open /admin/bundles and create one named 'QA Bundle brand-picker' with two members from Beyblade Arena.",
|
|
85
|
+
"Find the brand picker and read what it offers.",
|
|
86
|
+
"Select Beyblade as the bundle's brand and save.",
|
|
87
|
+
"RELOAD and read the brand.",
|
|
88
|
+
"Open /brands/brand-beyblade in a private window and find the bundle in its Bundles tab.",
|
|
89
|
+
"Clear the brand, save, reload, and check the bundle leaves that tab.",
|
|
90
|
+
"Delete the bundle.",
|
|
91
|
+
],
|
|
92
|
+
inputs: { name: "QA Bundle brand-picker", brand: "Beyblade" },
|
|
93
|
+
expectedBehaviour: "A bundle's own brand tag is a DIFFERENT field from any brand filter inside a dynamic member rule — the tag says which brand the bundle belongs to regardless of how its members were resolved, and it is what the brand page's Bundles tab filters on. Not every bundle needs one; a genuinely cross-brand bundle leaves it unset.",
|
|
94
|
+
expectedUiState: "The picker offers real brands. With the brand set the bundle appears in that brand's Bundles tab; with it cleared it leaves. A bundle appearing on a brand page it was never tagged with means the tab is filtering on the member rule instead.",
|
|
95
|
+
endResult: "The bundle is deleted by the final step.",
|
|
96
|
+
},
|
|
97
|
+
};
|