@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
|
@@ -14,6 +14,33 @@
|
|
|
14
14
|
* was unbanned a moment ago ā the fix is to re-read on navigation, not to widen
|
|
15
15
|
* the refresh interval.
|
|
16
16
|
*
|
|
17
|
+
* š A SOFT BAN DOES NOT BLOCK SIGN-IN (corrected 2026-09-29). Three cases and
|
|
18
|
+
* one catalogue label said it did. It does not, and the code is unambiguous:
|
|
19
|
+
*
|
|
20
|
+
* soft ban POST /api/admin/users/{uid}/soft-ban appends to `user.softBans[]`
|
|
21
|
+
* as `{ action, reason, bannedBy, bannedAt, expiresAt }`, where
|
|
22
|
+
* `action` is one of EIGHT: write_reviews, write_blog_comments,
|
|
23
|
+
* join_events, place_bids, create_listings, send_messages,
|
|
24
|
+
* create_support_tickets, report_scammers. It is a PER-ACTION
|
|
25
|
+
* restriction. Sign-in is untouched.
|
|
26
|
+
* lift DELETE /api/admin/users/{uid}/soft-ban/{action} ā one action at a
|
|
27
|
+
* time. `/unban` does NOT clear softBans.
|
|
28
|
+
* hard ban POST ā¦/hard-ban sets `disabled: true` on the Auth record and
|
|
29
|
+
* enqueues the 8-stage `hardBanCascade` job. `/api/auth/login`
|
|
30
|
+
* refuses on `userRecord.disabled` alone ā so THIS is the ban that
|
|
31
|
+
* blocks sign-in.
|
|
32
|
+
* unban POST ā¦/unban sets `disabled: false` on Auth and the user doc and
|
|
33
|
+
* clears the hard-ban fields only.
|
|
34
|
+
*
|
|
35
|
+
* So the sign-in case now exercises a HARD ban for the sign-in half and a soft
|
|
36
|
+
* ban for the restriction half, and says plainly that a soft-banned account
|
|
37
|
+
* signing in normally is CORRECT ā otherwise the first tester to run it reports
|
|
38
|
+
* a defect that is really the case being wrong.
|
|
39
|
+
*
|
|
40
|
+
* `reason` is `z.string().min(1)`, so a ONE-CHARACTER reason is accepted. The
|
|
41
|
+
* requires-reason case hedged on "if a minimum length applies"; it now states
|
|
42
|
+
* the contract so a one-character reason being accepted is not written up.
|
|
43
|
+
*
|
|
17
44
|
* @tag domain:tester
|
|
18
45
|
* @tag layer:seed
|
|
19
46
|
* @tag pattern:none
|
|
@@ -29,49 +56,58 @@ export const authored = {
|
|
|
29
56
|
steps: [
|
|
30
57
|
SIGN_IN_ADMIN,
|
|
31
58
|
"Open /admin/users and find the account rohit.collect@gmail.com.",
|
|
32
|
-
"Soft-ban
|
|
33
|
-
"Sign out.",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
59
|
+
"Soft-ban it from place_bids with the reason 'QA ban probe ā checklist case, will be lifted.'",
|
|
60
|
+
"Sign out and sign in as rohit.collect@gmail.com / TempPass123!. This must SUCCEED ā a soft ban restricts one action, it does not block sign-in.",
|
|
61
|
+
"Open any live auction and try to place a bid. Read the refusal and whether it quotes the reason the admin typed.",
|
|
62
|
+
"Sign back in as the admin and lift that soft ban.",
|
|
63
|
+
"Now HARD-ban the same account with the reason 'QA sign-in block probe ā checklist case, will be lifted.'",
|
|
64
|
+
"Sign out and try to sign in as rohit.collect@gmail.com / TempPass123!. Read exactly what is shown.",
|
|
65
|
+
"Sign back in as the admin and unban the account, then confirm it signs in again.",
|
|
37
66
|
],
|
|
38
|
-
inputs: {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
inputs: {
|
|
68
|
+
reason: "QA ban probe ā checklist case, will be lifted.",
|
|
69
|
+
softBanAction: "place_bids",
|
|
70
|
+
hardBanReason: "QA sign-in block probe ā checklist case, will be lifted.",
|
|
71
|
+
},
|
|
72
|
+
expectedBehaviour: "The two bans do different things and both must say why. A soft ban blocks ONE action and leaves sign-in alone; a hard ban disables the account outright and sign-in is refused. 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.",
|
|
73
|
+
expectedUiState: "The soft-banned account SIGNS IN NORMALLY ā that is correct and is not a finding ā and is refused only at the bid, with the admin's reason quoted. The hard-banned account is refused at sign-in with a message naming the disabled account, not an invalid-credentials error. Record the exact wording of both refusals.",
|
|
74
|
+
expectedData: { softBannedCanSignIn: true, hardBannedCanSignIn: false },
|
|
75
|
+
endResult: "The soft ban is lifted, the hard ban is lifted, and the account signs in and can bid again. š Both must be lifted separately ā /unban clears the hard-ban fields only and leaves softBans untouched.",
|
|
42
76
|
},
|
|
43
77
|
"checklist-admin-bans-and-trust-soft-ban-requires-reason": {
|
|
44
78
|
roles: ["admin"],
|
|
45
79
|
startPage: "/admin/users",
|
|
46
80
|
steps: [
|
|
47
81
|
SIGN_IN_ADMIN,
|
|
48
|
-
"Open /admin/users and start
|
|
82
|
+
"Open /admin/users and start a soft ban on the account rohit.collect@gmail.com.",
|
|
49
83
|
"Leave the reason field empty and submit.",
|
|
50
|
-
"Read what happens.",
|
|
51
|
-
"Type a
|
|
52
|
-
"Read whether that is accepted.",
|
|
53
|
-
"
|
|
84
|
+
"Read what happens and WHERE the error appears ā on the field, or as a banner.",
|
|
85
|
+
"Type a single character as the reason and submit.",
|
|
86
|
+
"Read whether that is accepted ā the schema is a minimum of one character, so it should be.",
|
|
87
|
+
"If it was accepted, lift that soft ban immediately.",
|
|
54
88
|
],
|
|
89
|
+
inputs: { minimumReasonLength: 1 },
|
|
55
90
|
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
|
|
57
|
-
|
|
91
|
+
expectedUiState: "The empty submission is refused with an inline error ON THE REASON FIELD, not a banner. A one-character reason IS accepted ā the contract is `z.string().min(1)` ā so do not record that as a finding; record instead whether a reason that short is useful to the person who reads it. What would be a finding is an empty reason being accepted, or the refusal appearing only as a banner with nothing marked on the field.",
|
|
92
|
+
expectedData: { emptyReasonRejected: true, oneCharReasonAccepted: true },
|
|
93
|
+
endResult: "Nobody is left banned ā if the one-character ban applied, it is lifted.",
|
|
58
94
|
},
|
|
59
95
|
"checklist-admin-bans-and-trust-unban-restores-access": {
|
|
60
96
|
roles: ["admin", "buyer"],
|
|
61
97
|
startPage: "/admin/users",
|
|
62
98
|
steps: [
|
|
63
99
|
SIGN_IN_ADMIN,
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
100
|
+
"In a second browser, sign in as rohit.collect@gmail.com / TempPass123! and leave that session open on a signed-in page.",
|
|
101
|
+
"As the admin, soft-ban that account from place_bids with the reason 'QA unban probe ā checklist case.'",
|
|
102
|
+
"In the second browser, WITHOUT signing out or reloading, open a live auction and confirm bidding is refused.",
|
|
103
|
+
"As the admin, lift that soft ban.",
|
|
104
|
+
"In the second browser, WITHOUT signing out or reloading, navigate to another page and try to bid again.",
|
|
105
|
+
"Read whether access is restored, and note how long it took if it was not immediate.",
|
|
70
106
|
],
|
|
71
|
-
inputs: { reason: "QA unban probe ā checklist case." },
|
|
107
|
+
inputs: { reason: "QA unban probe ā checklist case.", softBanAction: "place_bids" },
|
|
72
108
|
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: "
|
|
74
|
-
endResult: "The
|
|
109
|
+
expectedUiState: "Bidding 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 ā that is the cached-session-snapshot failure this case exists for, and it reads to a user as the unban not having worked.",
|
|
110
|
+
endResult: "The soft ban is lifted and the account can bid again. Note this case uses a SOFT ban deliberately: a hard ban ends the session outright, so there would be no live session left to observe the restoration on.",
|
|
75
111
|
},
|
|
76
112
|
"checklist-admin-bans-and-trust-hard-ban-cascade-runs": {
|
|
77
113
|
roles: ["admin"],
|
|
@@ -79,16 +115,19 @@ export const authored = {
|
|
|
79
115
|
steps: [
|
|
80
116
|
SIGN_IN_ADMIN,
|
|
81
117
|
"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
|
|
83
|
-
"Check it reports that work was queued rather than claiming completion.",
|
|
118
|
+
"Read what the interface says IMMEDIATELY after submitting ā the route returns a job id and a token, so it should report started work rather than a finished cascade.",
|
|
84
119
|
"Wait for the completion signal and read what it reports.",
|
|
85
|
-
"Open the account and read its state.",
|
|
86
|
-
"
|
|
120
|
+
"Open the account and read its state ā it should now be disabled.",
|
|
121
|
+
"Unban the account and confirm it is enabled again.",
|
|
87
122
|
],
|
|
88
|
-
inputs: {
|
|
123
|
+
inputs: {
|
|
124
|
+
reason: "QA hard-ban cascade probe ā checklist case.",
|
|
125
|
+
jobType: "hardBanCascade",
|
|
126
|
+
cascadeStages: 8,
|
|
127
|
+
},
|
|
89
128
|
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
|
|
91
|
-
endResult: "The ban is lifted.",
|
|
129
|
+
expectedUiState: "The submit reports started or queued work, a completion signal follows separately, and only then does the account's state reflect the finished cascade. An immediate unqualified 'user banned' with no later signal is the finding ā the eight-stage cascade runs in a background job and the request cannot know its outcome.",
|
|
130
|
+
endResult: "The ban is lifted and the account is enabled again. A hard ban left in place outlives every reseed ā users is PRESERVE tier ā and silently breaks whichever other page signs in as this persona.",
|
|
92
131
|
},
|
|
93
132
|
"checklist-admin-bans-and-trust-hard-ban-recorded-in-audit-log": {
|
|
94
133
|
roles: ["admin"],
|
|
@@ -9,8 +9,42 @@
|
|
|
9
9
|
* š A FAQ'S ANSWER IS NOT A STRING. The create path wraps a flat answer into a
|
|
10
10
|
* text-plus-format object and writes the slug to a nested path; an update path
|
|
11
11
|
* that spreads the body straight through writes a bare string into a field every
|
|
12
|
-
* reader expects to be an object.
|
|
13
|
-
*
|
|
12
|
+
* reader expects to be an object.
|
|
13
|
+
*
|
|
14
|
+
* VERIFIED FIXED 2026-09-29 before asserting anything about it: the PATCH at
|
|
15
|
+
* `src/app/api/admin/faqs/[id]/route.ts:53-57` destructures `answer` and `slug`
|
|
16
|
+
* out and applies the same transform as POST. So that half of the FAQ case
|
|
17
|
+
* should PASS, and a pass is the expected result rather than a surprise. It is
|
|
18
|
+
* kept because the failure is invisible in the admin form and only shows on the
|
|
19
|
+
* public page ā exactly the kind that returns.
|
|
20
|
+
*
|
|
21
|
+
* š THE FAQ CATEGORY PICKER OFFERS VALUES `FAQCategory` DOES NOT HAVE.
|
|
22
|
+
*
|
|
23
|
+
* `AdminFaqEditorView`'s CATEGORY_OPTIONS is shipping / returns / payments /
|
|
24
|
+
* auctions / pre-orders / general. The real union is orders_payment /
|
|
25
|
+
* shipping_delivery / returns_refunds / product_information / account_security /
|
|
26
|
+
* technical_support / general / scam_awareness. **Only `general` overlaps.**
|
|
27
|
+
* The API types category as a bare `z.string().min(1)` and then casts
|
|
28
|
+
* `as FAQCategory`, with a comment acknowledging it, so the wrong value is
|
|
29
|
+
* persisted unvalidated ā and `FAQPageContent` counts a FAQ only
|
|
30
|
+
* `if (faq.category in categoryCounts)`, so an admin-created FAQ is in no
|
|
31
|
+
* sidebar bucket and no category filter ever returns it. All 63 seeded FAQs use
|
|
32
|
+
* the real values, which is why this has never shown up. Root Cause #33/#34's
|
|
33
|
+
* shape. Recorded in OUTOFSCOPE; the case below now expects it.
|
|
34
|
+
*
|
|
35
|
+
* (CLAUDE.md's Seed Data Reference lists FAQ categories as
|
|
36
|
+
* "Shipping/Returns/Payments/Auctions/Pre-orders" ā it is describing this
|
|
37
|
+
* picker, not the schema, and is wrong about the data.)
|
|
38
|
+
*
|
|
39
|
+
* š THE BLOG SLUG IS EDITABLE, and this file used to claim the opposite.
|
|
40
|
+
* `updateBlogPostSchema` accepts `slug` and the editor's Content section has a
|
|
41
|
+
* 'Slug' field. What is true is narrower: the slug is derived from the title at
|
|
42
|
+
* creation and is never RECOMPUTED when the title changes. Those are different
|
|
43
|
+
* claims and only the second one holds.
|
|
44
|
+
*
|
|
45
|
+
* Blog vocabulary, all closed enums: category is news | tips | guides | updates
|
|
46
|
+
* | community; status is draft | published | archived. Editor sections are
|
|
47
|
+
* Content, Media, 'SEO & Tags' and Publish.
|
|
14
48
|
*
|
|
15
49
|
* @tag domain:tester
|
|
16
50
|
* @tag layer:seed
|
|
@@ -25,48 +59,62 @@ export const authored = {
|
|
|
25
59
|
startPage: "/admin/blog",
|
|
26
60
|
steps: [
|
|
27
61
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
28
|
-
"Open
|
|
29
|
-
"Save it as a DRAFT and read the status shown.",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
62
|
+
"Open /admin/blog/new and create a post titled 'QA Blog create-edit-publish' with an excerpt, category Guides, tags 'qa' and 'checklist', and body content in the rich-text editor. Write down the Slug the Content section shows.",
|
|
63
|
+
"Save it as a DRAFT and read the status shown in the Publish section.",
|
|
64
|
+
"Sign out and open /blog, confirming the draft is NOT listed and its slug URL does not render it.",
|
|
65
|
+
"Sign back in, open the post from /admin/blog, change ONLY the body, set status to Published, save, and RELOAD the editor.",
|
|
32
66
|
"Read every field to confirm only the body changed and the status is published.",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
67
|
+
"Change ONLY the Title to 'QA Blog renamed', save, RELOAD, and read the Slug field: it must still be the one written down in step 2.",
|
|
68
|
+
"Sign out, open /blog and open the post at that original slug.",
|
|
69
|
+
"Sign back in, delete the post, and confirm it disappears from /admin/blog and from /blog.",
|
|
35
70
|
],
|
|
36
|
-
inputs: {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
inputs: {
|
|
72
|
+
title: "QA Blog create-edit-publish",
|
|
73
|
+
titleAfter: "QA Blog renamed",
|
|
74
|
+
category: "Guides",
|
|
75
|
+
tags: "qa, checklist",
|
|
76
|
+
},
|
|
77
|
+
expectedBehaviour: "Draft and published are genuinely different: a draft is invisible publicly. The slug is derived from the title at creation and is never RECOMPUTED when the title changes, because recomputing it would break every existing link ā it stays directly editable in the Slug field, which is the deliberate escape hatch rather than an oversight.",
|
|
78
|
+
expectedUiState: "The draft appears nowhere on /blog and its URL does not render it. After publishing it does, carrying the edited body. After the title change every other field ā including Slug ā is untouched, and the post is still reachable at the original slug.",
|
|
79
|
+
expectedData: { statusAfterPublish: "published", slugUnchangedAfterRename: true },
|
|
80
|
+
endResult: "The post is deleted from /admin/blog and /blog. A draft visible publicly is the most serious failure here; a slug that silently followed the title is the second.",
|
|
40
81
|
},
|
|
41
82
|
"checklist-admin-blog-faqs-blog-media-step-save": {
|
|
42
83
|
roles: ["admin", "guest"],
|
|
43
84
|
startPage: "/admin/blog",
|
|
44
85
|
steps: [
|
|
45
86
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
46
|
-
"Open
|
|
47
|
-
"Upload
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
87
|
+
"Open /admin/blog, open blog-spot-genuine-takara-tomy-beyblade for editing, and write down its current Cover Image and YouTube Video ID from the Media section (the id may be empty ā no seeded post sets one).",
|
|
88
|
+
"Upload /test-media/sample-image.png as the Cover Image.",
|
|
89
|
+
"Set the YouTube Video ID to dQw4w9WgXcQ.",
|
|
90
|
+
"In the Content section's rich-text editor, insert /test-media/sample-image-2.png into the body itself.",
|
|
91
|
+
"Save, then RELOAD the editor and read the cover, the YouTube id and the body image.",
|
|
92
|
+
"Sign out, open the post on /blog, and read whether the cover, the video embed and the inline image all render.",
|
|
93
|
+
"Sign back in and restore the Cover Image and YouTube Video ID written down in step 1, and remove the inserted body image.",
|
|
52
94
|
],
|
|
53
|
-
inputs: {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
95
|
+
inputs: {
|
|
96
|
+
coverImage: "/test-media/sample-image.png",
|
|
97
|
+
bodyImage: "/test-media/sample-image-2.png",
|
|
98
|
+
youtubeId: "dQw4w9WgXcQ",
|
|
99
|
+
post: "blog-spot-genuine-takara-tomy-beyblade",
|
|
100
|
+
},
|
|
101
|
+
expectedBehaviour: "The cover image, the YouTube id and inline body images all persist and all render publicly. Extra images belong INSIDE the body HTML: `BlogPostDocument` does declare `contentImages` and `additionalImages` arrays and the PATCH accepts them, but no renderer anywhere reads either one, no mounted editor exposes them (only `BlogPostForm`, which has no consumer), and no seeded post sets them ā so a separate image array is not a path to anything visible. Newly uploaded media must also be finalised on update, not only on create, or the file is orphaned in temporary storage.",
|
|
102
|
+
expectedUiState: "After the reload the cover, the YouTube id and the inline image are all present in the editor, and all three render on the public post. An image that previews while editing and is gone after the reload was never finalised ā that is the orphaned-upload failure and is the thing this case is for.",
|
|
103
|
+
expectedData: { coverPersisted: true, youtubeIdPersisted: true, bodyImagePersisted: true },
|
|
104
|
+
endResult: "The post carries its original cover and YouTube id again and the inserted body image is gone. Record any image that vanished on reload, and whether it vanished in the editor, on the public post, or both.",
|
|
57
105
|
},
|
|
58
106
|
"checklist-admin-blog-faqs-faq-create-edit-category": {
|
|
59
107
|
roles: ["admin", "guest"],
|
|
60
108
|
startPage: "/admin/faqs",
|
|
61
109
|
steps: [
|
|
62
110
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
63
|
-
"Open
|
|
64
|
-
"Save, RELOAD, and read the answer as it is displayed.",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
111
|
+
"Open /admin/faqs/new and create one: question 'QA FAQ create-edit-category?', answer 'Created by the tester checklist.', and pick Shipping from the Category select.",
|
|
112
|
+
"Save, RELOAD the editor, and read the answer as it is displayed.",
|
|
113
|
+
"Sign out, open /faqs, and look for the question ā first under the category sidebar, then under All.",
|
|
114
|
+
"Sign back in, change ONLY the answer to 'Edited by the tester checklist.', and save.",
|
|
67
115
|
"RELOAD the editor and read the answer, then open /faqs again and read it there.",
|
|
68
|
-
"Change the
|
|
69
|
-
"Delete the FAQ and confirm it disappears from
|
|
116
|
+
"Change the Category to Payments, save, reload, and check whether it moved category on /faqs.",
|
|
117
|
+
"Delete the FAQ and confirm it disappears from /admin/faqs and /faqs.",
|
|
70
118
|
],
|
|
71
119
|
inputs: {
|
|
72
120
|
question: "QA FAQ create-edit-category?",
|
|
@@ -75,8 +123,9 @@ export const authored = {
|
|
|
75
123
|
categoryBefore: "Shipping",
|
|
76
124
|
categoryAfter: "Payments",
|
|
77
125
|
},
|
|
78
|
-
expectedBehaviour: "
|
|
79
|
-
expectedUiState: "
|
|
80
|
-
|
|
126
|
+
expectedBehaviour: "Two independent things, and they have different answers today. (1) The ANSWER round-trip: creating a FAQ wraps the flat answer into a text-plus-format object, and the update must apply the same transform rather than spreading a bare string into a field every reader expects to be an object. (2) The CATEGORY: whatever the picker offers must be a value the rest of the product can represent, or the FAQ lands in a category nothing renders.",
|
|
127
|
+
expectedUiState: "The answer half should PASS ā verified in source, the PATCH applies the same transform as create, so the answer reads as prose in both the admin editor and on /faqs, with no raw markup and no blank public answer.\n\nš EXPECT THE CATEGORY HALF TO FAIL, AND RECORD IT AS A FAILURE. The picker's 'Shipping' writes the string 'shipping', and FAQCategory has no such value ā it has 'shipping_delivery'. The API types category as a bare z.string() and casts it, so it is stored unvalidated, and /faqs counts a FAQ only if its category is a known one. So expect the question to be absent from every category in the sidebar while still appearing under All, and the move to Payments to change nothing visible. Say which categories you tried.",
|
|
128
|
+
expectedData: { answerRendersAsProse: true, appearsUnderAnyCategory: false },
|
|
129
|
+
endResult: "The FAQ is deleted from both surfaces. Keep the two halves apart in the comment: one mechanism works and one writes a value no reader understands.",
|
|
81
130
|
},
|
|
82
131
|
};
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* checklist-admin-bug-hunter-rewards-demo-fixture v1, isActive:false,
|
|
16
16
|
* bugConfirmed, credited to
|
|
17
|
-
* "Mock User
|
|
17
|
+
* "Mock User 3"
|
|
18
18
|
* checklist-admin-bug-hunter-rewards-demo-fixture-v2 v2, isActive:true
|
|
19
19
|
*
|
|
20
20
|
* That pair exists precisely so this flow can be read without spending a real
|
|
@@ -22,6 +22,24 @@
|
|
|
22
22
|
* confirming a bug ā it does so against v2, which is the fixture reopened for
|
|
23
23
|
* exactly that purpose.
|
|
24
24
|
*
|
|
25
|
+
* š THE CREDIT IS "Mock User 3", NOT "Mock User 18" (fixed 2026-09-29). The seed
|
|
26
|
+
* sets `bugHunterId: "user-yugi-muto"` / `bugHunterName: "Mock User 3"`, and the
|
|
27
|
+
* responses seed uses the same pair. The idempotency case asserted
|
|
28
|
+
* byte-identity against "Mock User 18", so it would have failed on a correct
|
|
29
|
+
* system ā the worst shape a case can have, because the failure looks like a
|
|
30
|
+
* product defect.
|
|
31
|
+
*
|
|
32
|
+
* š v1 CANNOT BE REOPENED AGAIN. It already carries `supersededByItemId`, and
|
|
33
|
+
* the route returns 409 "This case has already been reopened" on exactly that.
|
|
34
|
+
* The reopen case pointed at v1 and expected a new version to appear; it now
|
|
35
|
+
* asserts the 409 on v1 and performs the real reopen on **v2**, which the
|
|
36
|
+
* confirm-bug case above it has just made bug-confirmed. That is a deliberate
|
|
37
|
+
* dependency between two cases in one batch, which is why they run in order.
|
|
38
|
+
*
|
|
39
|
+
* Both response rows ARE seeded (`tester-responses-seed-data.ts`), the v2 one
|
|
40
|
+
* deliberately left `status: "new"` and unconfirmed so confirm-bug has
|
|
41
|
+
* something to do. So no case here has to file a report first.
|
|
42
|
+
*
|
|
25
43
|
* @tag domain:tester
|
|
26
44
|
* @tag layer:seed
|
|
27
45
|
* @tag pattern:none
|
|
@@ -39,15 +57,19 @@ export const authored = {
|
|
|
39
57
|
"Open /admin/tester-feedback and select the Main Issues tab.",
|
|
40
58
|
"Find the row for the case 'Demo fixture ā reported bug, already confirmed and reopened (v2, active)' and read the tester name on it.",
|
|
41
59
|
"Click 'Mark as Bug' on that row.",
|
|
42
|
-
"Read the confirmation dialog
|
|
60
|
+
"Read the confirmation dialog before accepting: it should be titled 'Confirm this bug?' and say the credit is awarded and the case disabled for other testers. Then accept it.",
|
|
43
61
|
"Read the badge that appears on the row afterwards.",
|
|
44
62
|
"Open /admin/tester-checklist, set the Status filter to 'All', and read the Status of that same case.",
|
|
45
63
|
],
|
|
46
|
-
inputs: {
|
|
64
|
+
inputs: {
|
|
65
|
+
caseId: "checklist-admin-bug-hunter-rewards-demo-fixture-v2",
|
|
66
|
+
dialogTitle: "Confirm this bug?",
|
|
67
|
+
creditedTo: "Mock User 3",
|
|
68
|
+
},
|
|
47
69
|
expectedBehaviour: "Confirming a bug does two things at once and both are irreversible from this screen: it credits exactly one tester, and it takes the case out of circulation so no second tester can answer it. A confirmation dialog must stand in front of it ā an accidental click here spends a credit on the wrong person and silently shrinks the catalogue.",
|
|
48
|
-
expectedUiState: "A dialog appears BEFORE anything commits. After accepting, the row shows a confirmed-bug badge naming the crediting tester ā the same name that was on the row beforehand, not a different one. In the catalog the case's Status reads
|
|
70
|
+
expectedUiState: "A dialog titled 'Confirm this bug?' appears BEFORE anything commits. After accepting, the row shows a confirmed-bug badge naming the crediting tester ā the same name that was on the row beforehand, 'Mock User 3', not a different one. In the catalog the case's Status reads 'Bug Confirmed' rather than 'Active'.",
|
|
49
71
|
expectedData: { creditedTesterUnchanged: true },
|
|
50
|
-
endResult: "Survives a reload: the badge and the credited name are still there, and the case is still out of the Active view.",
|
|
72
|
+
endResult: "Survives a reload: the badge and the credited name are still there, and the case is still out of the Active view. š Leave v2 confirmed ā the reopen case below needs a bug-confirmed case that has NOT already been superseded, and v2 is the only one a fresh seed can provide.",
|
|
51
73
|
},
|
|
52
74
|
"checklist-admin-bug-hunter-rewards-confirm-bug-idempotent": {
|
|
53
75
|
roles: ["admin"],
|
|
@@ -55,35 +77,50 @@ export const authored = {
|
|
|
55
77
|
steps: [
|
|
56
78
|
SIGN_IN_ADMIN,
|
|
57
79
|
"Open /admin/tester-feedback and select the All Submissions tab.",
|
|
58
|
-
"Find a row whose case is ALREADY bug-confirmed ā the demo fixture 'reported bug, already confirmed and reopened (v1, disabled)' is credited to Mock User
|
|
80
|
+
"Find a row whose case is ALREADY bug-confirmed ā the demo fixture 'reported bug, already confirmed and reopened (v1, disabled)' is credited to Mock User 3.",
|
|
59
81
|
"Read the tester name currently credited on it.",
|
|
60
82
|
"Attempt 'Mark as Bug' on that row.",
|
|
61
83
|
"Read whatever the screen says in response.",
|
|
62
84
|
"Read the credited tester name again and compare it against the one noted earlier.",
|
|
63
85
|
],
|
|
64
|
-
inputs: {
|
|
86
|
+
inputs: {
|
|
87
|
+
caseId: "checklist-admin-bug-hunter-rewards-demo-fixture",
|
|
88
|
+
creditedTo: "Mock User 3",
|
|
89
|
+
expectedRefusal: "This case has already been confirmed as a bug",
|
|
90
|
+
},
|
|
65
91
|
expectedBehaviour: "A second confirmation on an already-confirmed case is REFUSED, and refused loudly. The failure mode this guards is silent re-crediting: two testers report the same case, an admin confirms twice, and the second confirmation quietly moves the credit off the person who actually found it. Nothing about that is visible unless the screen says so.",
|
|
66
|
-
expectedUiState: "Either the action is absent/disabled on an already-confirmed row, or it is refused
|
|
67
|
-
expectedData: { creditedTo: "Mock User
|
|
68
|
-
endResult: "Reload the page: the credit still reads Mock User
|
|
92
|
+
expectedUiState: "Either the action is absent/disabled on an already-confirmed row, or it is refused ā the server answers 409 'This case has already been confirmed as a bug'. What must NOT happen is a success message. The credited name is byte-identical to the one read at the start.",
|
|
93
|
+
expectedData: { creditedTo: "Mock User 3" },
|
|
94
|
+
endResult: "Reload the page: the credit still reads Mock User 3, and the case has not gained a second confirmation.",
|
|
69
95
|
},
|
|
70
96
|
"checklist-admin-bug-hunter-rewards-reopen-case": {
|
|
71
97
|
roles: ["admin"],
|
|
72
98
|
startPage: "/admin/tester-checklist",
|
|
73
99
|
steps: [
|
|
74
100
|
SIGN_IN_ADMIN,
|
|
75
|
-
"Open /admin/tester-checklist and set
|
|
101
|
+
"Open /admin/tester-checklist and set 'Bug status' to 'Bug Confirmed'.",
|
|
76
102
|
"Find 'Demo fixture ā reported bug, already confirmed and reopened (v1, disabled)' and read its Status and its bug-hunter credit.",
|
|
77
|
-
"Use its 'Reopen as New Test Case' row action.",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
103
|
+
"Use its 'Reopen as New Test Case' row action and read what happens ā v1 has already been superseded once, so expect a refusal rather than a new case.",
|
|
104
|
+
"Now find the v2 fixture, which the confirm-bug case above has just made bug-confirmed, and use 'Reopen as New Test Case' on it.",
|
|
105
|
+
"Read the new case that appears ā its label, its Status, its version, and whether it carries any bug-hunter credit.",
|
|
106
|
+
"Go back to the v1 and v2 cases and read their Status and credit again.",
|
|
107
|
+
"Open /tester and search for the new case to confirm it is answerable.",
|
|
81
108
|
],
|
|
82
|
-
inputs: {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
inputs: {
|
|
110
|
+
v1Id: "checklist-admin-bug-hunter-rewards-demo-fixture",
|
|
111
|
+
v2Id: "checklist-admin-bug-hunter-rewards-demo-fixture-v2",
|
|
112
|
+
newIdPattern: "{v2Id}-v3",
|
|
113
|
+
v1Refusal: "This case has already been reopened",
|
|
114
|
+
},
|
|
115
|
+
expectedBehaviour: "Reopening creates a NEW version rather than resurrecting the old one, and a case can only be reopened ONCE ā a second reopen would fork the chain into two live retests of the same finding. The original must keep both its disabled state and its bug-hunter credit: that credit records who found the defect, and a retest of the same behaviour does not undo the finding. Mutating the old row in place would erase the history the whole feature exists to keep.",
|
|
116
|
+
expectedUiState: "v1 refuses the reopen ā 409 'This case has already been reopened' ā and is unchanged by the attempt. v2 reopens into a new row whose id ends '-v3' and whose version reads 3, Active and answerable from /tester, carrying NO bug-hunter credit of its own. Both older rows stay disabled and stay credited. A new case that inherited the credit, or an old row that lost it, is the failure.",
|
|
117
|
+
expectedData: {
|
|
118
|
+
originalStaysDisabled: true,
|
|
119
|
+
originalKeepsCredit: true,
|
|
120
|
+
newVersion: 3,
|
|
121
|
+
newCaseHasCredit: false,
|
|
122
|
+
},
|
|
123
|
+
endResult: "Survives a reload: v1 and v2 disabled with their credits, the reopened v3 active and answerable.",
|
|
87
124
|
},
|
|
88
125
|
"checklist-admin-bug-hunter-rewards-catalog-default-active-filter": {
|
|
89
126
|
roles: ["admin"],
|
|
@@ -91,15 +128,15 @@ export const authored = {
|
|
|
91
128
|
steps: [
|
|
92
129
|
SIGN_IN_ADMIN,
|
|
93
130
|
"Open /admin/tester-checklist without touching any filter and read the total count shown.",
|
|
94
|
-
"Search for 'Demo fixture
|
|
131
|
+
"Search for 'Demo fixture' and write down every matching row with its Status.",
|
|
95
132
|
"Switch the Status filter to 'All' and read the total count and the matching rows again.",
|
|
96
|
-
"Switch the Status filter to 'Inactive' and read which of the
|
|
133
|
+
"Switch the Status filter to 'Inactive' and read which of the demo fixtures appear.",
|
|
97
134
|
"Set Status back to its default and set 'Bug status' to 'Bug Confirmed'.",
|
|
98
|
-
"Read which rows appear under that filter.",
|
|
135
|
+
"Read which rows appear under that filter, and confirm every one of them was absent from the default view.",
|
|
99
136
|
],
|
|
100
|
-
inputs: { search: "Demo fixture
|
|
137
|
+
inputs: { search: "Demo fixture" },
|
|
101
138
|
expectedBehaviour: "The default view is the answerable catalogue, not everything ever written. A bug-confirmed case is deliberately out of circulation, so showing it by default would invite an admin to treat a closed finding as open work. Switching the filter must reveal it ā hidden is not the same as gone.",
|
|
102
|
-
expectedUiState: "
|
|
139
|
+
expectedUiState: "Every bug-confirmed row is absent from the default view and present under Status 'All', under 'Inactive', and under Bug status 'Bug Confirmed'. The totals move in the direction each filter implies. v1 is bug-confirmed on any fresh seed, so it is the row to anchor on.\n\nš Do NOT assert an absolute count of demo fixtures. The three cases above this one deliberately confirm v2 and reopen it as v3, so how many demo rows exist depends on whether they ran. The assertion is the FILTER SEMANTICS ā hidden is not gone ā not the arithmetic. If v2 and v3 exist, they must obey the same rule, which makes the case stronger rather than fragile.",
|
|
103
140
|
expectedData: { defaultViewHidesBugConfirmed: true },
|
|
104
141
|
endResult: "Read-only ā no case is modified.",
|
|
105
142
|
},
|