@mohasinac/appkit 4.42.1 → 4.42.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/features/offers/adapters.d.ts +29 -7
- package/dist/_internal/server/features/offers/adapters.js +14 -3
- package/dist/_internal/server/features/orders/adapters.js +54 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +1 -0
- package/dist/features/account/components/UserOrdersView.js +30 -2
- package/dist/features/account/hooks/useAddresses.d.ts +21 -6
- package/dist/features/account/hooks/useAddresses.js +1 -1
- package/dist/features/addresses/hooks/usePostalLookup.js +84 -12
- package/dist/features/admin/components/AdminCategoriesView.js +37 -2
- package/dist/features/admin/components/AdminOffersView.js +2 -2
- package/dist/features/admin/components/AdminSupportTicketsView.js +27 -1
- package/dist/features/admin/constants/filter-tabs.d.ts +25 -0
- package/dist/features/admin/constants/filter-tabs.js +30 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +35 -0
- package/dist/features/admin/hooks/useAdminListingData.js +45 -9
- package/dist/features/cart/schemas/index.d.ts +15 -0
- package/dist/features/cart/schemas/index.js +2 -0
- package/dist/features/cart/types/index.d.ts +22 -0
- package/dist/features/categories/components/CategoriesIndexListing.js +19 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +14 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +8 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +1 -0
- package/dist/features/events/actions/event-actions.d.ts +13 -0
- package/dist/features/events/actions/event-actions.js +75 -1
- package/dist/features/events/repository/event-entry.repository.d.ts +17 -0
- package/dist/features/events/repository/event-entry.repository.js +36 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +21 -3
- package/dist/features/orders/components/OrdersList.js +2 -1
- package/dist/features/orders/index.d.ts +1 -0
- package/dist/features/orders/index.js +1 -0
- package/dist/features/orders/types/index.d.ts +2 -0
- package/dist/features/orders/utils/order-ref.d.ts +45 -0
- package/dist/features/orders/utils/order-ref.js +58 -0
- package/dist/features/products/components/MakeOfferButton.d.ts +18 -1
- package/dist/features/products/components/MakeOfferButton.js +23 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +5 -0
- package/dist/features/products/components/ProductDetailPageView.js +3 -2
- package/dist/features/products/repository/products.repository.js +29 -2
- package/dist/features/seller/actions/offer-actions.js +32 -0
- package/dist/features/seller/components/QuickProductForm.js +15 -1
- package/dist/features/seller/components/SellerBidsView.js +3 -1
- package/dist/features/seller/components/SellerOffersView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +49 -6
- package/dist/features/seller/components/SellerProductsCards.js +23 -1
- package/dist/features/seller/components/SellerProductsView.js +27 -2
- package/dist/features/seller/hooks/useSellerListingData.d.ts +0 -1
- package/dist/features/seller/hooks/useSellerListingData.js +17 -19
- package/dist/features/shell/SectionForm.js +33 -2
- package/dist/features/tester/seed-data/_happy-path.d.ts +23 -0
- package/dist/features/tester/seed-data/_happy-path.js +710 -0
- package/dist/features/tester/seed-data/_money-flows.js +34 -23
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +22 -3
- package/dist/features/tester/seed-data/authored/addresses__address-filters.js +17 -12
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +7 -6
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +10 -4
- package/dist/features/tester/seed-data/authored/addresses__state-picker.js +13 -8
- package/dist/features/tester/seed-data/authored/addresses__unban-request.js +49 -11
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +10 -6
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +71 -32
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +80 -31
- package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.js +62 -25
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +61 -31
- package/dist/features/tester/seed-data/authored/admin__bundles.js +84 -37
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +90 -57
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +38 -26
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +47 -28
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +118 -31
- package/dist/features/tester/seed-data/authored/admin__content-deletes.js +30 -24
- package/dist/features/tester/seed-data/authored/admin__content-marketing.js +83 -46
- package/dist/features/tester/seed-data/authored/admin__coupons.js +8 -8
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +9 -9
- package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +19 -4
- package/dist/features/tester/seed-data/authored/admin__media-watermark.js +77 -33
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +5 -5
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +29 -5
- package/dist/features/tester/seed-data/authored/admin__site-system.js +45 -23
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +53 -28
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +20 -20
- package/dist/features/tester/seed-data/authored/buying__bidding.js +19 -2
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +20 -5
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +28 -10
- package/dist/features/tester/seed-data/authored/buying__cart.js +10 -3
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +0 -16
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +27 -5
- package/dist/features/tester/seed-data/authored/buying__offers.js +22 -7
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +3 -3
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +35 -4
- package/dist/features/tester/seed-data/authored/buying__return-request.js +19 -6
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +36 -18
- package/dist/features/tester/seed-data/authored/buying__reviews.js +16 -1
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +2 -1
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +16 -1
- package/dist/features/tester/seed-data/authored/community-support__public-profile.js +3 -2
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__blog.js +19 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +5 -5
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +41 -6
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +63 -8
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +29 -29
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +2 -2
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +23 -1
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +6 -6
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +1 -1
- package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +1 -1
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +31 -6
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +34 -10
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +35 -8
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +31 -9
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +29 -3
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +10 -10
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +3 -3
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +8 -8
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +2 -2
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +39 -16
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +43 -8
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +40 -14
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +31 -14
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +7 -6
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +14 -5
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +1 -1
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +4 -2
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +3 -3
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +45 -11
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -17
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +36 -5
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +29 -4
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +23 -0
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +14 -13
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +14 -9
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +34 -7
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +17 -7
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +9 -9
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +9 -4
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +25 -1
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +27 -6
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +19 -1
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +63 -22
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +9 -5
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +7 -6
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +3 -2
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +18 -18
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +13 -12
- package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +19 -3
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +22 -4
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +20 -8
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +4 -4
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +2 -2
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +5 -5
- package/dist/features/tester/seed-data/authored/seo__og-images.js +1 -1
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +7 -7
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +3 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +123 -21
- package/dist/features/tester/utils/phases.d.ts +17 -0
- package/dist/features/tester/utils/phases.js +29 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/seo/json-ld.d.ts +23 -0
- package/dist/seo/json-ld.js +3 -2
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/package.json +1 -1
|
@@ -15,6 +15,29 @@
|
|
|
15
15
|
* within the platform's compute budget, so the mark is a client overlay and its
|
|
16
16
|
* settings have to reach a second consumer.
|
|
17
17
|
*
|
|
18
|
+
* 🛑 …AND THAT SECOND CONSUMER IS NOT ON THE PRODUCT PAGE (found 2026-09-29).
|
|
19
|
+
* `<MediaVideo>`, which owns the overlay, is mounted in exactly three places:
|
|
20
|
+
* `HeroCarousel` and the two admin upload previews. A product video plays in
|
|
21
|
+
* `ImageLightbox`, which renders a bare `<video controls>` — no overlay layer at
|
|
22
|
+
* all. So the poster frame is watermarked (it is a still, proxied like any
|
|
23
|
+
* image) and the playing video is not. The parity case now expects that and
|
|
24
|
+
* names the homepage hero as the surface where the overlay does apply.
|
|
25
|
+
*
|
|
26
|
+
* The watermark tab is on /admin/site → 'Watermark'. Its real controls:
|
|
27
|
+
* a 'Watermark type' select (Text | Image), 'Watermark text' or a
|
|
28
|
+
* 'Watermark image' upload, a `Size — N% of image width` slider (5-100 step 5),
|
|
29
|
+
* an `Opacity — N%` slider (5-100 step 5), a 'Position' select, and a
|
|
30
|
+
* 'Use a custom X/Y offset instead' toggle revealing two labelled offset
|
|
31
|
+
* inputs measured in percent FROM CENTER (+right/+down).
|
|
32
|
+
*
|
|
33
|
+
* 🛑 THEME RECOLOUR READS `defaultLightThemeId` AND NOTHING ELSE.
|
|
34
|
+
* `resolveThemeGradientStops` looks up the theme record named by
|
|
35
|
+
* `theme.defaultLightThemeId` (falling back to "default-light") and takes its
|
|
36
|
+
* `--appkit-gradient-logo` stops. Changing the DARK default, or a viewer's own
|
|
37
|
+
* mode, does not move the composited mark's colour — so the case must change
|
|
38
|
+
* the default LIGHT theme or it will report a failure that is really a
|
|
39
|
+
* mis-aimed step.
|
|
40
|
+
*
|
|
18
41
|
* @tag domain:tester
|
|
19
42
|
* @tag layer:seed
|
|
20
43
|
* @tag pattern:none
|
|
@@ -28,14 +51,20 @@ export const authored = {
|
|
|
28
51
|
startPage: "/admin/site",
|
|
29
52
|
steps: [
|
|
30
53
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
31
|
-
"Open /admin/site
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
54
|
+
"Open /admin/site, go to its 'Watermark' tab, and write down the current values of the `Size — N% of image width` and `Opacity — N%` sliders.",
|
|
55
|
+
"Sign out and open /products/product-beyblade-burst-valkyrie, then open its main image and note how the mark looks.",
|
|
56
|
+
"Sign back in and set Size to 100 and Opacity to 100, then save.",
|
|
57
|
+
"Sign out, reload that product image with a HARD reload, and compare.",
|
|
58
|
+
"Sign back in, restore both sliders to the values written down in step 2, save, and reload the image again.",
|
|
36
59
|
],
|
|
60
|
+
inputs: {
|
|
61
|
+
product: "product-beyblade-burst-valkyrie",
|
|
62
|
+
sizeAfter: 100,
|
|
63
|
+
opacityAfter: 100,
|
|
64
|
+
sliderStep: 5,
|
|
65
|
+
},
|
|
37
66
|
expectedBehaviour: "Size and opacity reach the served image. The mark is composited server-side as the image is proxied, so a setting that saves without changing the output means the render path is not reading it — and because images are cached aggressively, a hard reload may be needed before concluding either way.",
|
|
38
|
-
expectedUiState: "The mark is visibly larger and more opaque after the change, and back to its original appearance after the restore. A settings page that saves with no visible effect on the served image
|
|
67
|
+
expectedUiState: "The mark is visibly larger and more opaque after the change, and back to its original appearance after the restore. Both sliders move in steps of 5 between 5 and 100, so a value typed outside that range is not reachable and is not a finding. A settings page that saves with no visible effect on the served image IS the failure.",
|
|
39
68
|
endResult: "Both settings are restored. Note whether a hard reload was required — that is cache behaviour rather than a defect, but it changes how the next tester reads this page.",
|
|
40
69
|
},
|
|
41
70
|
"checklist-admin-media-watermark-watermark-position-presets": {
|
|
@@ -43,12 +72,13 @@ export const authored = {
|
|
|
43
72
|
startPage: "/admin/site",
|
|
44
73
|
steps: [
|
|
45
74
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
46
|
-
"Open
|
|
47
|
-
"Select each preset in turn
|
|
48
|
-
"After each save,
|
|
49
|
-
"
|
|
50
|
-
"Restore the
|
|
75
|
+
"Open /admin/site → 'Watermark' and read every option in the 'Position' select, writing down which is currently set.",
|
|
76
|
+
"Select each preset in turn — the four corners and center — saving after each. Leave 'custom' to the offset case.",
|
|
77
|
+
"After each save, sign out and open /products/product-beyblade-burst-valkyrie's main image, noting where the mark sits.",
|
|
78
|
+
"Repeat one corner preset against a portrait image and a landscape one.",
|
|
79
|
+
"Restore the preset written down in step 2.",
|
|
51
80
|
],
|
|
81
|
+
inputs: { product: "product-beyblade-burst-valkyrie" },
|
|
52
82
|
expectedBehaviour: "Every preset places the mark somewhere different and every one keeps it inside the frame. A corner preset that pushes part of the mark off the edge is the likely failure, since the offset is computed from the image's dimensions and a portrait image behaves differently from a landscape one.",
|
|
53
83
|
expectedUiState: "Each preset moves the mark to its named position and the whole mark remains visible within the image at all of them. Test at least one portrait and one landscape image — a preset correct on one aspect and clipped on the other is the finding.",
|
|
54
84
|
endResult: "The original preset is restored.",
|
|
@@ -58,14 +88,20 @@ export const authored = {
|
|
|
58
88
|
startPage: "/admin/site",
|
|
59
89
|
steps: [
|
|
60
90
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
61
|
-
"Open
|
|
62
|
-
"
|
|
63
|
-
"Set
|
|
64
|
-
"
|
|
65
|
-
"
|
|
91
|
+
"Open /admin/site → 'Watermark' and write down the current Position and any offsets.",
|
|
92
|
+
"Turn on 'Use a custom X/Y offset instead' and read the two inputs it reveals — 'X offset (%) — + right / − left of center' and 'Y offset (%) — + down / − up from center'.",
|
|
93
|
+
"Set X to 10 and Y to 10, save, sign out, and read /products/product-beyblade-burst-valkyrie's main image.",
|
|
94
|
+
"Sign back in, set X to 100 and Y to 100 — far enough to push the mark past the edge — save, sign out, and read the image again.",
|
|
95
|
+
"Read whether the mark is clipped, gone entirely, or clamped so it stays fully inside the frame.",
|
|
96
|
+
"Sign back in and restore the Position and offsets written down in step 2.",
|
|
66
97
|
],
|
|
98
|
+
inputs: {
|
|
99
|
+
product: "product-beyblade-burst-valkyrie",
|
|
100
|
+
modestOffset: 10,
|
|
101
|
+
extremeOffset: 100,
|
|
102
|
+
},
|
|
67
103
|
expectedBehaviour: "A custom offset moves the mark, and an offset that would push it out of frame is clamped rather than obeyed. Obeying it produces images with no visible watermark at all — which is indistinguishable from the proxy being bypassed and would mask a much larger problem.",
|
|
68
|
-
expectedUiState: "The modest offset moves the mark by the amount set. The extreme offset leaves the mark still visible, clamped to the frame, rather than pushing it off the image. A silently unwatermarked image is the failure.",
|
|
104
|
+
expectedUiState: "The modest offset moves the mark by the amount set. The extreme offset leaves the mark still visible, clamped flush to the frame, rather than pushing it off the image. A silently unwatermarked image is the failure. This is expected to PASS — the offset is clamped twice, once when the setting is read and again when the overlay is placed — so a clipped mark here would mean one of those clamps stopped running.",
|
|
69
105
|
endResult: "The original offset is restored.",
|
|
70
106
|
},
|
|
71
107
|
"checklist-admin-media-watermark-watermark-theme-recolor": {
|
|
@@ -73,15 +109,17 @@ export const authored = {
|
|
|
73
109
|
startPage: "/admin/site",
|
|
74
110
|
steps: [
|
|
75
111
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
76
|
-
"Open
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
112
|
+
"Open /admin/site → 'Watermark' and confirm 'Watermark type' has no uploaded 'Watermark image' override — with none set the mark falls through to the bundled brand glyph, which is the only tier the theme can recolour.",
|
|
113
|
+
"Sign out, open /products/product-beyblade-burst-valkyrie's main image, and note the mark's colour.",
|
|
114
|
+
"Sign back in, open the 'Themes' tab, and write down which theme is set as the DEFAULT LIGHT theme.",
|
|
115
|
+
"Change the default LIGHT theme to one with different brand colours and save. Changing the dark default will not move the mark — the resolver reads the light one only.",
|
|
116
|
+
"Sign out, hard-reload the product image, and compare the mark's colour.",
|
|
117
|
+
"Compare a dark product photo and a light one and check the mark is legible on both.",
|
|
118
|
+
"Sign back in and restore the default light theme written down in step 4.",
|
|
82
119
|
],
|
|
120
|
+
inputs: { product: "product-beyblade-burst-valkyrie", themeSetting: "defaultLightThemeId" },
|
|
83
121
|
expectedBehaviour: "With no image override the mark falls through to the bundled brand glyph, whose gradient stops come from theme colour tokens — so changing the theme recolours it without a second asset. It must stay legible over both dark and light photographs, which is the part a colour change can quietly break.",
|
|
84
|
-
expectedUiState: "The mark's colour follows the theme. It remains
|
|
122
|
+
expectedUiState: "The mark's colour follows the DEFAULT LIGHT theme's logo gradient. It remains legible on both a dark and a light product photo after the change. A mark that vanishes into one of the two is the finding. A mark that does not change at all is only a finding if the default LIGHT theme was the one changed — say which you changed.",
|
|
85
123
|
endResult: "The original theme is restored.",
|
|
86
124
|
},
|
|
87
125
|
"checklist-admin-media-watermark-watermark-video-overlay-parity": {
|
|
@@ -89,16 +127,22 @@ export const authored = {
|
|
|
89
127
|
startPage: "/admin/site",
|
|
90
128
|
steps: [
|
|
91
129
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
92
|
-
"Open
|
|
93
|
-
"
|
|
94
|
-
"Look at the video's POSTER frame in the gallery strip and note the mark.",
|
|
95
|
-
"
|
|
96
|
-
"Compare
|
|
97
|
-
"
|
|
130
|
+
"Open /admin/site → 'Watermark' and write down the size, opacity and position.",
|
|
131
|
+
"Sign out and open /products/product-beyblade-burst-spryzen-video-demo.",
|
|
132
|
+
"Look at the video's POSTER frame in the gallery strip and note whether it carries the mark.",
|
|
133
|
+
"Click it to open the lightbox, play the video, and look for the mark while it is playing.",
|
|
134
|
+
"Compare that against a still image on the same page.",
|
|
135
|
+
"Now open / and find the 'Live auctions' hero slide, whose background is a video, and look for the mark on it while it plays.",
|
|
136
|
+
"Sign back in, change the watermark position, save, sign out, and check which of the three — the poster, the product video, the hero video — followed.",
|
|
98
137
|
"Restore the position.",
|
|
99
138
|
],
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
139
|
+
inputs: {
|
|
140
|
+
product: "product-beyblade-burst-spryzen-video-demo",
|
|
141
|
+
heroSlide: "Live auctions",
|
|
142
|
+
},
|
|
143
|
+
expectedBehaviour: "A still goes through the proxy and is composited server-side. A video cannot be, within the platform's compute budget, so its mark has to be a client overlay reading the same settings — two consumers, one configuration. The case is whether every surface that plays a video has that second consumer.",
|
|
144
|
+
expectedUiState: "The poster frame carries the mark, and follows a position change.\n\n🛑 EXPECT THE PRODUCT VIDEO TO CARRY NO MARK, AND RECORD IT AS A FAILURE. The overlay lives in `<MediaVideo>`, which is mounted only by the homepage hero carousel and the two admin upload previews; a product video plays in the lightbox, which renders a bare `<video controls>` with no overlay layer. So expect: poster marked, product video unmarked, hero video marked. Report which of the three followed the position change — that comparison is the finding, and a bare 'the video has no watermark' does not distinguish a missing overlay from a broken one.",
|
|
145
|
+
expectedData: { posterMarked: true, productVideoMarked: false, heroVideoMarked: true },
|
|
146
|
+
endResult: "The position is restored. Parity between the paths is the case — a mark on the poster alone proves nothing about the frames a buyer actually watches.",
|
|
103
147
|
},
|
|
104
148
|
};
|
|
@@ -77,11 +77,11 @@ export const authored = {
|
|
|
77
77
|
steps: [
|
|
78
78
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
79
79
|
"Open /admin/orders and find the manual-payment filter chips.",
|
|
80
|
-
"Read every chip offered and write the list down.",
|
|
80
|
+
"Read every chip offered and write the list down — there are three: All, 'Awaiting payment' and 'Awaiting verification'.",
|
|
81
81
|
"Select each in turn and read the rows returned.",
|
|
82
82
|
"Check the awaiting-payment chip returns orders with NO proof uploaded.",
|
|
83
83
|
"Check the awaiting-verification chip returns orders WITH a proof and no decision.",
|
|
84
|
-
"Read the URL and confirm the selection is encoded there.",
|
|
84
|
+
"Read the URL and confirm the selection is encoded there as ?paymentReview=awaiting_proof or ?paymentReview=awaiting_verification — a query param of its own, NOT a Sieve f= filter, which is the whole point of the case.",
|
|
85
85
|
],
|
|
86
86
|
expectedBehaviour: "The queue is not an ordinary Sieve filter. 'Has a proof' cannot be expressed as a query predicate — an inequality on the proof field excludes every document where it was never written, which IS the awaiting-payment set, so the query would return only the opposite of what was asked. The queue runs one bounded query and refines in memory instead, reached through its own query parameter.",
|
|
87
87
|
expectedUiState: "The awaiting-payment chip returns orders with no proof, and the awaiting-verification chip returns orders with a proof and no decision — two visibly different sets. A chip returning the inverse of its name is the predicate failure.",
|
|
@@ -206,7 +206,7 @@ export const authored = {
|
|
|
206
206
|
startPage: "/admin/orders",
|
|
207
207
|
steps: [
|
|
208
208
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
209
|
-
"Open
|
|
209
|
+
"Open /admin/return-requests and read the rows.",
|
|
210
210
|
"Check each row shows the item, the buyer's stated reason and the order it belongs to.",
|
|
211
211
|
"Read the scope tabs and check which scope a return-requested order sits under.",
|
|
212
212
|
"Open one and read the full reason before acting.",
|
|
@@ -222,7 +222,7 @@ export const authored = {
|
|
|
222
222
|
startPage: "/admin/orders",
|
|
223
223
|
steps: [
|
|
224
224
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
225
|
-
"Open
|
|
225
|
+
"Open /admin/fulfillment and read every row's primary label.",
|
|
226
226
|
"Check each names the ITEM and shows its thumbnail rather than only an order id.",
|
|
227
227
|
"Read a multi-item order's row and check it indicates there is more than one item.",
|
|
228
228
|
"Read the queue's ordering and check the oldest unfulfilled order is first.",
|
|
@@ -237,7 +237,7 @@ export const authored = {
|
|
|
237
237
|
startPage: "/admin/shipments",
|
|
238
238
|
steps: [
|
|
239
239
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
240
|
-
"Open
|
|
240
|
+
"Open /admin/shipments and read the existing shipments.",
|
|
241
241
|
"Create one named 'QA Shipment admin-crud' with a supplier and a date, and save.",
|
|
242
242
|
"RELOAD and read every field.",
|
|
243
243
|
"Add a lot and an item to it, save, RELOAD, and read them back.",
|
|
@@ -29,7 +29,7 @@ export const authored = {
|
|
|
29
29
|
"Create a prize draw titled 'QA Prize Draw admin-create' at 80 per entry, 25 entries, instant-reveal mode.",
|
|
30
30
|
"Define at least two prizes with names and save.",
|
|
31
31
|
"RELOAD the editor and read the mode, the per-entry price, the entry count and the prizes.",
|
|
32
|
-
"Open the public page in a private window and read the purchase panel's wording.",
|
|
32
|
+
"Open the prize draw's public page at /prize-draws/{its id} in a private window and read the purchase panel's wording.",
|
|
33
33
|
"Read whether the panel describes buying an ENTRY rather than buying the prize.",
|
|
34
34
|
],
|
|
35
35
|
inputs: { title: "QA Prize Draw admin-create", pricePerEntry: 80, entryCount: 25 },
|
|
@@ -44,7 +44,7 @@ export const authored = {
|
|
|
44
44
|
"Sign in as rehan.sheikh@gmail.com / TempPass123! and buy one entry in 'QA Prize Draw admin-create' with Cash on Delivery.",
|
|
45
45
|
"Read what the order page shows immediately after payment.",
|
|
46
46
|
"Sign out and sign in as admin@letitrip.in / TempPass123!.",
|
|
47
|
-
"Open
|
|
47
|
+
"Open /admin/prize-draws/{its id}/edit and read the entries and any reveal control.",
|
|
48
48
|
"Trigger the reveal if the mode requires it, and read the assigned prize.",
|
|
49
49
|
"Sign back in as the buyer and read the prize on their order.",
|
|
50
50
|
"Compare the prize the admin sees against the one the buyer sees.",
|
|
@@ -74,11 +74,11 @@ export const authored = {
|
|
|
74
74
|
startPage: "/admin/products",
|
|
75
75
|
steps: [
|
|
76
76
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
77
|
-
"Open a
|
|
77
|
+
"Open /admin/prize-draws/prizedraw-beyblade-burst-collectors-draw/edit — a seeded draw that is still open — and read what the reveal mechanism is described as.",
|
|
78
78
|
"Read whether the admin can choose WHO wins a specific prize.",
|
|
79
79
|
"Read whether the admin can see, before revealing, which prize an entry will draw.",
|
|
80
80
|
"Trigger a reveal and read whether the outcome was predictable from anything on screen beforehand.",
|
|
81
|
-
"Open the public page and read whether the odds or prize pool are disclosed to buyers.",
|
|
81
|
+
"Open the prize draw's public page at /prize-draws/{its id} and read whether the odds or prize pool are disclosed to buyers.",
|
|
82
82
|
],
|
|
83
83
|
expectedBehaviour: "The draw is not steerable. Prizes are assigned by cryptographic randomness at reveal, so an admin cannot pick a winner and cannot see the outcome in advance — a prize draw an operator can steer is a rigged one, whatever else the page does correctly.",
|
|
84
84
|
expectedUiState: "No control assigns a specific prize to a specific entry or buyer. Nothing on the admin screen discloses an entry's outcome before it is revealed. The public page states the prize pool so buyers know what they are entering.",
|
|
@@ -89,7 +89,7 @@ export const authored = {
|
|
|
89
89
|
startPage: "/admin/products",
|
|
90
90
|
steps: [
|
|
91
91
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
92
|
-
"Open a
|
|
92
|
+
"Open /admin/prize-draws/prizedraw-beyblade-metal-closed-revealed/entries — a CLOSED and revealed draw, so it has entries and a recorded winner to read rather than an empty table.",
|
|
93
93
|
"Check each row shows enough to identify the entry without opening it.",
|
|
94
94
|
"Read whether buyer identities are shown in full or masked.",
|
|
95
95
|
"Read the row actions offered and check at least one lets you VIEW the entry.",
|
|
@@ -102,4 +102,28 @@ export const authored = {
|
|
|
102
102
|
expectedData: { nonsenseResultCount: 0 },
|
|
103
103
|
endResult: "Read-only. Delete 'QA Prize Draw admin-create' as the last action of this page.",
|
|
104
104
|
},
|
|
105
|
+
"checklist-admin-prize-draws-lotteries-lottery-edit-preserves-bookings": {
|
|
106
|
+
roles: ["admin"],
|
|
107
|
+
startPage: "/admin/lotteries",
|
|
108
|
+
steps: [
|
|
109
|
+
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
110
|
+
"Open /admin/lotteries and find event-pokemon-number-draw-july-2026 — 25 total slots, of which FIVE are already booked in the seed.",
|
|
111
|
+
"Open its slot editor at /admin/lotteries/event-pokemon-number-draw-july-2026/edit.",
|
|
112
|
+
"WRITE DOWN which slot numbers show as booked and the buyer named against each.",
|
|
113
|
+
"Change something harmless and unrelated — rename ONE unbooked slot's prize — and save.",
|
|
114
|
+
"RELOAD the editor and read the booked slots again.",
|
|
115
|
+
"Compare every booked slot number and buyer against what you wrote down.",
|
|
116
|
+
"Read the prices shown on the slots too: they must be the real stored amounts, not zeros.",
|
|
117
|
+
"Restore the renamed slot and save again.",
|
|
118
|
+
],
|
|
119
|
+
inputs: {
|
|
120
|
+
eventId: "event-pokemon-number-draw-july-2026",
|
|
121
|
+
totalSlots: 25,
|
|
122
|
+
bookedSlots: 5,
|
|
123
|
+
},
|
|
124
|
+
expectedBehaviour: "Editing a lottery's prizes leaves its bookings alone. This is the one save in the application that has destroyed buyer data: the editor once sent isBooked:false and weight:0 for every slot and dropped the buyer fields entirely, and because the event PATCH was passthrough, the first save of a live lottery marked every purchased slot available again and erased who had bought them — with a 200 and no error anywhere. The write shape now cannot even express booking state, and the merge reattaches bookings by slotNumber rather than by array position, so deleting a slot cannot hand slot 7's buyer slot 8's prize.",
|
|
125
|
+
expectedUiState: "All five booked slots are still booked after the reload, against the same buyers, and only the renamed prize differs. Prices read as their stored amounts. 🛑 EVERY SLOT SHOWING AS AVAILABLE is the catastrophic outcome and it looks like a successful save — the page re-renders, the toast is green, and only the buyers know. Every price reading zero is the same defect's twin, from seeding the form off the public projection, which strips price and weight.",
|
|
126
|
+
expectedData: { bookedSlotsAfterSave: 5, slotsPricedZero: 0 },
|
|
127
|
+
endResult: "The renamed slot is restored and all five bookings survive. If any booking is gone, STOP and report it immediately rather than continuing the batch — there is no undo, and the remaining cases on this page would be run against corrupted data.",
|
|
128
|
+
},
|
|
105
129
|
};
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* WHY: Authored six-part procedures for the admin/site-system checklist page.
|
|
3
|
-
* WHAT:
|
|
3
|
+
* WHAT: 37 case(s), keyed by full checklist id.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 SITE SETTINGS IS PRESERVE-TIER, AND THE TESTER RULES SAY NEVER MODIFY IT.
|
|
6
|
+
* That rule and this page are not in conflict, but the reconciliation has to be
|
|
7
|
+
* stated or a tester following the rules literally answers `null` to all of them.
|
|
8
|
+
*
|
|
9
|
+
* The rule exists because `siteSettings` is NEVER wiped and NEVER reseeded — the
|
|
10
|
+
* lifecycle preserves it by design, so unlike the catalogue there is nothing to
|
|
11
|
+
* put it back. What it forbids is leaving it CHANGED, not touching it.
|
|
12
|
+
*
|
|
13
|
+
* So every mutating case here does three things, and a case that skips any of
|
|
14
|
+
* them is the defect rather than the coverage:
|
|
15
|
+
*
|
|
16
|
+
* 1. read and write down the existing value BEFORE editing,
|
|
17
|
+
* 2. make the smallest change that proves the point,
|
|
18
|
+
* 3. RESTORE the recorded value — not "clear the field", which is a different
|
|
19
|
+
* destructive edit wearing the same clothes.
|
|
20
|
+
*
|
|
21
|
+
* Three cases had no restore at all until 2026-09-29, and one of them overwrote
|
|
22
|
+
* the PhonePe Client ID, Client Secret and both webhook credentials with QA
|
|
23
|
+
* values and walked away. Running it would have disabled online payment for real
|
|
24
|
+
* buyers, silently, until somebody with the real keys noticed.
|
|
4
25
|
*
|
|
5
26
|
* Written by hand, case by case, against the label each one states. There is no
|
|
6
27
|
* generator: the authoring scripts were deleted once it was clear they were
|
|
@@ -51,18 +72,18 @@ export const authored = {
|
|
|
51
72
|
steps: [
|
|
52
73
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
53
74
|
"Open /admin/site and go to the credentials tab.",
|
|
54
|
-
"Read how existing credentials are displayed.",
|
|
55
|
-
"Type QA-CREDENTIAL-118427 into
|
|
75
|
+
"Read how existing credentials are displayed, and WRITE DOWN the masked value of every field on the tab before touching anything.",
|
|
76
|
+
"Pick a field whose masked value shows a *_PLACEHOLDER or is empty — never one holding a real key. Type QA-CREDENTIAL-118427 into that field and save.",
|
|
56
77
|
"Read the confirmation and RELOAD the tab.",
|
|
57
78
|
"Read how that credential now displays.",
|
|
58
79
|
"Open /api/site-settings in a private window and search the response for QA-CREDENTIAL-118427.",
|
|
59
|
-
"
|
|
80
|
+
"RESTORE the field to exactly what step 3 recorded — clearing it is NOT restoring it, and siteSettings survives every run, so a cleared real key is gone for good. Save and reload to confirm the original masked value is back.",
|
|
60
81
|
],
|
|
61
82
|
inputs: { credential: "QA-CREDENTIAL-118427" },
|
|
62
83
|
expectedBehaviour: "A credential is encrypted before storage and returned masked. The encryption helper THROWS when its key is unset — so a save that fails outright is a missing-key problem rather than a form bug, and a save that succeeds while storing plaintext is worse than either.",
|
|
63
84
|
expectedUiState: "After the reload the credential shows masked rather than as the typed value or as raw ciphertext. The public settings response contains no trace of it. A save that errors should name the missing configuration rather than showing a stack trace.",
|
|
64
85
|
expectedData: { credentialInPublicResponse: 0 },
|
|
65
|
-
endResult: "The field
|
|
86
|
+
endResult: "The field holds exactly what it held before the case ran — restored, not cleared. A hit in the public response is a credential leak and fails the case outright.",
|
|
66
87
|
},
|
|
67
88
|
"checklist-admin-site-system-site-settings-credentials-partial-save-keeps-others": {
|
|
68
89
|
roles: ["admin"],
|
|
@@ -120,11 +141,12 @@ export const authored = {
|
|
|
120
141
|
steps: [
|
|
121
142
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
122
143
|
"Open /admin/site and switch to the Integrations tab.",
|
|
123
|
-
"
|
|
144
|
+
"WRITE DOWN the current PhonePe Client ID, Client Secret, Client Version, Environment and both webhook credential fields — the masked form of each. You must be able to put them back; nothing else will.",
|
|
124
145
|
"Type QA-PHONEPE-CLIENT-991 into the Client ID field, QA-PHONEPE-SECRET-991 into the Client Secret field, and QA-WEBHOOK-USER-991 / QA-WEBHOOK-PASS-991 into the two webhook fields.",
|
|
125
146
|
"Save, then hard-reload the page.",
|
|
126
147
|
"Read how the Client Secret and both webhook fields display.",
|
|
127
148
|
"Change only the PhonePe Environment dropdown, save again, and reload.",
|
|
149
|
+
"RESTORE every one of the six fields to exactly what step 3 recorded, save, and reload to confirm. This is not optional housekeeping: siteSettings is never reseeded, so QA values left in the PhonePe Client ID and Secret disable online payment for real buyers until someone notices and has the real keys to hand.",
|
|
128
150
|
],
|
|
129
151
|
inputs: {
|
|
130
152
|
clientId: "QA-PHONEPE-CLIENT-991",
|
|
@@ -142,11 +164,11 @@ export const authored = {
|
|
|
142
164
|
steps: [
|
|
143
165
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
144
166
|
"Open /admin/site and switch to the Shipping tab.",
|
|
145
|
-
"Read the current state of the 'PhonePe (online card/UPI) enabled' toggle.",
|
|
146
|
-
"Turn it
|
|
147
|
-
"Return to Site Settings,
|
|
148
|
-
"
|
|
149
|
-
"Reopen /checkout and confirm 'Pay Online (PhonePe)' is
|
|
167
|
+
"Read the current state of the 'PhonePe (online card/UPI) enabled' toggle. It is seeded OFF (phonepeEnabled: false), so ON is the direction that proves anything here — turning OFF something already off would leave PhonePe absent from checkout for the reason it was always absent, and pass trivially.",
|
|
168
|
+
"Turn it ON, save, and open /checkout in another tab (with an item in the cart) to confirm 'Pay Online (PhonePe)' is now offered in the payment step.",
|
|
169
|
+
"Return to Site Settings, RELOAD, and confirm the toggle still reads ON — the save is not proven until the reload.",
|
|
170
|
+
"Turn it back OFF and save, restoring the seeded state.",
|
|
171
|
+
"Reopen /checkout and confirm 'Pay Online (PhonePe)' is absent again.",
|
|
150
172
|
],
|
|
151
173
|
expectedBehaviour: "The toggle lives under the Shipping tab's 'Payment methods' section, not a dedicated Payments tab — the same admin field checkout's payment-method list reads from. It gates whether the PhonePe option is offered at all, independent of the manual-UPI and COD toggles beside it.",
|
|
152
174
|
expectedUiState: "With the toggle OFF, /checkout offers only whichever of manual UPI/cash and COD are separately enabled — no 'Pay Online (PhonePe)' button. With it back ON, the button reappears.",
|
|
@@ -157,12 +179,12 @@ export const authored = {
|
|
|
157
179
|
startPage: "/admin/site",
|
|
158
180
|
steps: [
|
|
159
181
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
160
|
-
"Open the
|
|
182
|
+
"Open /admin/site, switch to the Integrations tab, and WRITE DOWN every WhatsApp field and its current masked state before touching anything.",
|
|
161
183
|
"Type QA-WA-TOKEN-552310 into the access-token field and fill any account identifier fields.",
|
|
162
184
|
"Save and RELOAD, then read how each field displays.",
|
|
163
185
|
"Open /api/site-settings in a private window and search for QA-WA-TOKEN-552310.",
|
|
164
186
|
"Open a public store page's source and search for it too.",
|
|
165
|
-
"
|
|
187
|
+
"RESTORE every field to exactly what step 3 recorded — clearing is not restoring, and a real Meta token cleared here cannot be recovered from the seed. Save and reload to confirm.",
|
|
166
188
|
],
|
|
167
189
|
inputs: { token: "QA-WA-TOKEN-552310" },
|
|
168
190
|
expectedBehaviour: "The token is encrypted at rest and masked on read. It is also decrypted on every read of a store document, so any page handing a raw store document to a client component serialises it into public HTML — which is why both the settings response and a public store page are checked rather than the admin form alone.",
|
|
@@ -175,8 +197,8 @@ export const authored = {
|
|
|
175
197
|
startPage: "/admin/site",
|
|
176
198
|
steps: [
|
|
177
199
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
178
|
-
"Open the
|
|
179
|
-
"Turn one OFF and
|
|
200
|
+
"Open /admin/site, switch to the Notifications tab, and read every channel toggle and its state. Four are listed: in-app, email, WhatsApp and SMS.",
|
|
201
|
+
"Turn the WHATSAPP channel off and save. Pick that one specifically — SMS is seeded OFF (and has no sender at all), so toggling it off would be a no-op that proves nothing.",
|
|
180
202
|
"RELOAD and read every toggle.",
|
|
181
203
|
"Check only the one changed.",
|
|
182
204
|
"Turn it back on, save, and reload to confirm.",
|
|
@@ -285,7 +307,7 @@ export const authored = {
|
|
|
285
307
|
startPage: "/admin/analytics",
|
|
286
308
|
steps: [
|
|
287
309
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
288
|
-
"Open
|
|
310
|
+
"Open /admin/analytics and read every card and chart.",
|
|
289
311
|
"Write down any figure reading zero or a dash.",
|
|
290
312
|
"Change the date range if one is offered and read the figures again.",
|
|
291
313
|
"Reload and confirm the figures are stable.",
|
|
@@ -316,7 +338,7 @@ export const authored = {
|
|
|
316
338
|
steps: [
|
|
317
339
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
318
340
|
"Open the browser console and clear it.",
|
|
319
|
-
"Open
|
|
341
|
+
"Open /admin/analytics and wait for every card to settle.",
|
|
320
342
|
"Read the console for any permission-denied message.",
|
|
321
343
|
"Open the admin dashboard and read the console again.",
|
|
322
344
|
"Note which card or widget was loading when any such message appeared.",
|
|
@@ -361,7 +383,7 @@ export const authored = {
|
|
|
361
383
|
startPage: "/admin/maintenance",
|
|
362
384
|
steps: [
|
|
363
385
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
364
|
-
"Open
|
|
386
|
+
"Open /admin/maintenance and read every page it links to.",
|
|
365
387
|
"Open each in turn and confirm it renders content rather than an error.",
|
|
366
388
|
"Note any that shows a failed-precondition or missing-index error.",
|
|
367
389
|
"Note any that shows a permission or credentials error.",
|
|
@@ -376,10 +398,10 @@ export const authored = {
|
|
|
376
398
|
startPage: "/admin/maintenance",
|
|
377
399
|
steps: [
|
|
378
400
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
379
|
-
"Open
|
|
401
|
+
"Open /admin/maintenance/server-errors and read the rows.",
|
|
380
402
|
"Read whether rows carry a source, a code, a message and a time.",
|
|
381
|
-
"Open
|
|
382
|
-
"Open
|
|
403
|
+
"Open /admin/maintenance/client-errors and read its rows.",
|
|
404
|
+
"Open /admin/maintenance/function-errors and read its rows.",
|
|
383
405
|
"Note which of the three lists is empty.",
|
|
384
406
|
"Check the sort order puts the newest first.",
|
|
385
407
|
],
|
|
@@ -392,7 +414,7 @@ export const authored = {
|
|
|
392
414
|
startPage: "/admin/maintenance",
|
|
393
415
|
steps: [
|
|
394
416
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
395
|
-
"Open
|
|
417
|
+
"Open /admin/maintenance/cloud-logs and read what renders.",
|
|
396
418
|
"Read whether it shows log entries, an empty state, or an error.",
|
|
397
419
|
"If it errors, read whether the message names the missing configuration.",
|
|
398
420
|
"Reload and confirm the behaviour is consistent.",
|
|
@@ -407,7 +429,7 @@ export const authored = {
|
|
|
407
429
|
startPage: "/admin",
|
|
408
430
|
steps: [
|
|
409
431
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
410
|
-
"Open
|
|
432
|
+
"Open /admin/copilot and read what it offers.",
|
|
411
433
|
"Read whether it states which model or provider it uses.",
|
|
412
434
|
"Submit a simple request and read the response.",
|
|
413
435
|
"Read whether any error surfaces a provider key or a raw response body.",
|
|
@@ -12,6 +12,22 @@
|
|
|
12
12
|
* speculative cases against a screen nobody has opened is how a checklist grows
|
|
13
13
|
* cases that measure nothing.
|
|
14
14
|
*
|
|
15
|
+
* Checked 2026-09-29, and three descriptions were wrong about the affordance:
|
|
16
|
+
*
|
|
17
|
+
* · /admin/featured is "Featured Products" — `buildFilters: () => "featured==true"`
|
|
18
|
+
* — so the row action REMOVES a product from the list rather than toggling a
|
|
19
|
+
* two-state control. Re-featuring is done from /admin/products' Quick edit,
|
|
20
|
+
* which is the only surface with a Featured toggle.
|
|
21
|
+
* · /admin/deals is "Deals (Promoted Products)" and its rows DO open — via
|
|
22
|
+
* `rowHrefTemplate` to /admin/products/{id}/edit, not to a detail of its own.
|
|
23
|
+
* · /admin/stickers has exactly ONE facet, `filterKeys: ["status"]`, so "apply a
|
|
24
|
+
* different filter" means a different status VALUE. Only `published` stickers
|
|
25
|
+
* are seeded, so the other three statuses returning nothing is correct.
|
|
26
|
+
*
|
|
27
|
+
* All twelve guide routes named in the guides case exist, and all twelve are in
|
|
28
|
+
* ADMIN_NAV_GROUPS — verified rather than assumed, because the hrefs audit
|
|
29
|
+
* checks `href`/`startPage` only and cannot see a route named in step prose.
|
|
30
|
+
*
|
|
15
31
|
* @tag domain:tester
|
|
16
32
|
* @tag layer:seed
|
|
17
33
|
* @tag pattern:none
|
|
@@ -130,13 +146,14 @@ export const authored = {
|
|
|
130
146
|
startPage: "/admin/settings/actions",
|
|
131
147
|
steps: [
|
|
132
148
|
SIGN_IN_ADMIN,
|
|
133
|
-
"Open /admin/settings/actions.",
|
|
134
|
-
"
|
|
135
|
-
"Change
|
|
149
|
+
"Open /admin/settings/actions and read its two panels — the action-permissions manager and the nav-permissions panel.",
|
|
150
|
+
"Write down the current value of the one control you are about to change.",
|
|
151
|
+
"Change ONE action's permission — not a nav permission — and save.",
|
|
136
152
|
"Reload and read whether the change held.",
|
|
137
|
-
"Restore the
|
|
153
|
+
"Restore the value written down in step 3 and reload once more to confirm it went back.",
|
|
138
154
|
],
|
|
139
|
-
|
|
155
|
+
inputs: { panel: "action permissions" },
|
|
156
|
+
expectedBehaviour: "A change made here persists. A settings save that returns success without writing is the most dangerous failure a settings screen has — there is no stale display to notice and no error to read, and it has happened on this codebase before.\n\n🛑 Change an ACTION permission, not a nav permission. The nav panel drives the disabled-routes gate that runs in the proxy, so disabling the wrong entry 404s a real route for every later case in the run.",
|
|
140
157
|
expectedUiState: "The changed value is still changed after the reload. A value that reverts is the finding, even though the save reported success.",
|
|
141
158
|
endResult: "The original value is restored.",
|
|
142
159
|
},
|
|
@@ -160,14 +177,16 @@ export const authored = {
|
|
|
160
177
|
startPage: "/admin/stickers",
|
|
161
178
|
steps: [
|
|
162
179
|
SIGN_IN_ADMIN,
|
|
163
|
-
"Open /admin/stickers.",
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
180
|
+
"Open /admin/stickers and read the rows listed, counting them — five sticker listings are seeded.",
|
|
181
|
+
"Open the filter drawer: this page has ONE facet, Status.",
|
|
182
|
+
"Select Published and read the rows.",
|
|
183
|
+
"Select Draft, then In Review, then Archived, reading the rows each time.",
|
|
184
|
+
"Type zzzznope into the search box and read the count.",
|
|
168
185
|
],
|
|
186
|
+
inputs: { seededStickerCount: 5, nonsenseQuery: "zzzznope" },
|
|
169
187
|
expectedBehaviour: "The list and its filters return real sticker listings. A filter whose value does not exactly match a stored one returns zero rows forever with no error — the single most common silent defect in this product's listing surfaces, and it looks identical to an empty catalogue.",
|
|
170
|
-
expectedUiState: "Rows on the default view, and
|
|
188
|
+
expectedUiState: "Rows on the default view, and Published returning them. Draft, In Review and Archived are EXPECTED to return nothing — only published stickers are seeded, so an empty result there is correct and is not a finding. What would be a finding is Published returning nothing while the unfiltered list holds rows, which is the always-empty-filter shape. 'zzzznope' returns none.",
|
|
189
|
+
expectedData: { nonsenseResultCount: 0 },
|
|
171
190
|
endResult: "Read-only.",
|
|
172
191
|
},
|
|
173
192
|
"checklist-admin-uncovered-admin-pages-admin-deals-renders": {
|
|
@@ -175,14 +194,16 @@ export const authored = {
|
|
|
175
194
|
startPage: "/admin/deals",
|
|
176
195
|
steps: [
|
|
177
196
|
SIGN_IN_ADMIN,
|
|
178
|
-
"Open /admin/deals.",
|
|
179
|
-
"Read
|
|
180
|
-
"
|
|
181
|
-
"Read
|
|
197
|
+
"Open /admin/deals and read its title — it lists promoted products.",
|
|
198
|
+
"Read the rows listed.",
|
|
199
|
+
"Click one row and read the URL it lands on.",
|
|
200
|
+
"Read whether the page it opened shows that product, and whether it is a product editor rather than a deal detail.",
|
|
182
201
|
"Read the console for errors.",
|
|
183
202
|
],
|
|
203
|
+
inputs: { expectedRowDestination: "/admin/products/{id}/edit" },
|
|
184
204
|
expectedBehaviour: "Rows can be opened. A row offering only mutations, or no way in at all, is a dead end — an admin can act on a record they were never able to read.",
|
|
185
|
-
expectedUiState: "Rows render and one opens to a
|
|
205
|
+
expectedUiState: "Rows render and one opens — to the product editor at /admin/products/{id}/edit, which is this page's detail affordance rather than a deal-specific view. That is a pass: the record being listed IS a product. A row that does nothing on click is the finding.",
|
|
206
|
+
expectedData: { consoleErrors: 0 },
|
|
186
207
|
endResult: "Read-only.",
|
|
187
208
|
},
|
|
188
209
|
"checklist-admin-uncovered-admin-pages-admin-featured-renders": {
|
|
@@ -190,15 +211,19 @@ export const authored = {
|
|
|
190
211
|
startPage: "/admin/featured",
|
|
191
212
|
steps: [
|
|
192
213
|
SIGN_IN_ADMIN,
|
|
193
|
-
"Open /admin/featured and
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
"
|
|
214
|
+
"Open /admin/featured, read its title and note which products it lists — every row is a product whose featured flag is set.",
|
|
215
|
+
"Write down the name of one of them.",
|
|
216
|
+
"Sign out and open /, find the 'Featured Products' section, and confirm that product is in it.",
|
|
217
|
+
"Sign back in, use the row action that removes that product from featured, and reload /admin/featured to confirm it is gone from the list.",
|
|
218
|
+
"Sign out and reload /, checking the 'Featured Products' section no longer carries it.",
|
|
219
|
+
"Sign back in, open /admin/products, find that product, and use the row menu's 'Quick edit' to turn its Featured toggle back on — this page can only remove, so restoring is done there.",
|
|
220
|
+
"Reload /admin/featured and confirm the product is listed again.",
|
|
198
221
|
],
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
222
|
+
inputs: { publicSection: "Featured Products", restoreVia: "/admin/products Quick edit" },
|
|
223
|
+
expectedBehaviour: "The featured flag persists and the homepage follows it, in both directions. This page is a FILTERED VIEW of products where `featured == true`, not a toggle board, so removal happens here and restoring happens on /admin/products — a case that expects one control to do both would report a missing affordance that was never there. A list endpoint that omits a field the edit form sends back is how such a flag silently resets: saving any other field on the record re-sends a wrong default for the one the list never returned.",
|
|
224
|
+
expectedUiState: "The removal survives a reload and the homepage's 'Featured Products' section loses the product; re-featuring from /admin/products brings it back to both. A state that reverts on its own is the finding. Note the section shows at most twelve products, so a product that does not appear may simply be beyond the cap — pick one that was visible in step 3 before concluding anything.",
|
|
225
|
+
expectedData: { removedFromPublicSection: true, restoredAfterQuickEdit: true },
|
|
226
|
+
endResult: "The product is featured again and back in the homepage section.",
|
|
202
227
|
},
|
|
203
228
|
"checklist-admin-uncovered-admin-pages-admin-features-renders": {
|
|
204
229
|
roles: ["admin"],
|
|
@@ -222,13 +247,13 @@ export const authored = {
|
|
|
222
247
|
steps: [
|
|
223
248
|
SIGN_IN_ADMIN,
|
|
224
249
|
"Open each of these by URL in turn: /admin/guide, /admin/guide/analytics, /admin/guide/catalog, /admin/guide/content, /admin/guide/orders, /admin/guide/payments, /admin/guide/site, /admin/guide/stores, /admin/guide/team, /admin/guide/trust, /admin/guide/users and /admin/guide/whatsapp.",
|
|
225
|
-
"Check every one of them is also
|
|
250
|
+
"Check every one of them is also reachable from the admin sidebar's Guides group rather than only by URL — all twelve have a nav entry, so a missing one is a finding.",
|
|
226
251
|
"Record any that 404 or render empty.",
|
|
227
|
-
"
|
|
228
|
-
"Record any guide describing a screen or a control that is no longer there.",
|
|
252
|
+
"Pick the Site Configuration and Content & Marketing guides and, for each screen or control they name, open it and check it still exists under that name.",
|
|
253
|
+
"Record any guide describing a screen or a control that is no longer there, naming both the guide and the screen.",
|
|
229
254
|
],
|
|
230
255
|
expectedBehaviour: "Every guide page renders and describes something that still exists. A guide is documentation with no compiler behind it, so it rots silently — and a guide naming a deleted screen is worse than none, because it sends an employee looking for something that is gone.",
|
|
231
|
-
expectedUiState: "Every guide loads with real content. A 404 is a finding; so is a guide describing a screen that no longer exists, recorded with which guide and which screen.",
|
|
256
|
+
expectedUiState: "Every guide loads with real content and every one is in the sidebar's Guides group. A 404 is a finding; so is a guide describing a screen that no longer exists, recorded with which guide and which screen. The second is the likelier of the two and the reason this case exists — the routes are checked by an audit on every build, the PROSE inside them is checked by nobody.",
|
|
232
257
|
expectedData: { guidePagesNotFound: 0 },
|
|
233
258
|
endResult: "Read-only.",
|
|
234
259
|
},
|