@mohasinac/appkit 4.39.1 → 4.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/_internal/server/jobs/core/countersReconcile.js +21 -0
  2. package/dist/_internal/server/jobs/core/onProductWrite.js +35 -1
  3. package/dist/features/categories/components/CategoryDetailPageView.js +17 -5
  4. package/dist/features/categories/repository/categories.repository.d.ts +65 -1
  5. package/dist/features/categories/repository/categories.repository.js +245 -77
  6. package/dist/features/categories/types/index.d.ts +2 -2
  7. package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.d.ts +2 -0
  8. package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +145 -0
  9. package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.d.ts +2 -0
  10. package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +234 -0
  11. package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +32 -0
  12. package/dist/features/tester/seed-data/authored/buying__cart.js +60 -0
  13. package/dist/features/tester/seed-data/authored/buying__order-detail-actions.d.ts +2 -0
  14. package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +138 -0
  15. package/dist/features/tester/seed-data/authored/buying__wishlist-history.js +30 -0
  16. package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.d.ts +2 -0
  17. package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +145 -0
  18. package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.d.ts +2 -0
  19. package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +154 -0
  20. package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.d.ts +2 -0
  21. package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +88 -0
  22. package/dist/features/tester/seed-data/authored/index.js +22 -0
  23. package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +134 -7
  24. package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +89 -0
  25. package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.d.ts +2 -0
  26. package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +141 -0
  27. package/dist/features/tester/seed-data/authored/public-pages__store-tabs.d.ts +2 -0
  28. package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +115 -0
  29. package/dist/features/tester/seed-data/authored/selling__digital-content-delivery.d.ts +2 -0
  30. package/dist/features/tester/seed-data/authored/selling__digital-content-delivery.js +148 -0
  31. package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.d.ts +2 -0
  32. package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +118 -0
  33. package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.d.ts +2 -0
  34. package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +69 -0
  35. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +471 -0
  36. package/dist/styles.css +1 -1
  37. package/dist/tailwind-utilities.css +1 -1
  38. package/dist/ui/components/tabs-overflow.js +30 -7
  39. package/package.json +1 -1
@@ -56,13 +56,24 @@ export const authored = {
56
56
  roles: ["guest"],
57
57
  startPage: "/help",
58
58
  steps: [
59
- "Open /how-it-works/auctions in a private window and read its content.",
60
- "Open /how-it-works/checkout and read its content.",
61
- "Open /how-it-works/offers and read its content.",
62
- "Open /how-it-works/orders and read its content.",
63
- "Open /how-it-works/payouts and read its content.",
64
- "Open /how-it-works/pre-orders and read its content.",
65
- "Open /how-it-works/reviews and read its content.",
59
+ /*
60
+ * 🛑 CORRECTED 2026-09-14. These seven steps named /how-it-works/<topic>,
61
+ * a route family that does not exist — every one was a 404, so the case
62
+ * could only ever be answered "no" for a reason that was not a product
63
+ * defect. The real routes are flat: /how-<topic>-work(s). That is also why
64
+ * these seven pages read as zero-coverage in a gap sweep despite having a
65
+ * case: the case pointed at nothing.
66
+ *
67
+ * audit-tester-checklist-hrefs did not catch it because it validates the
68
+ * `href` and `startPage` FIELDS, and these paths live inside step strings.
69
+ */
70
+ "Open /how-auctions-work in a private window and read its content.",
71
+ "Open /how-checkout-works and read its content.",
72
+ "Open /how-offers-work and read its content.",
73
+ "Open /how-orders-work and read its content.",
74
+ "Open /how-payouts-work and read its content.",
75
+ "Open /how-pre-orders-work and read its content.",
76
+ "Open /how-reviews-work and read its content.",
66
77
  "Write down any of the seven that renders empty, 404s, or repeats another page's copy.",
67
78
  ],
68
79
  expectedBehaviour: "All seven guides render their own distinct copy. Sharing one renderer is correct; sharing one body of text is not — two guides with identical content means the page key is not reaching the content lookup.",
@@ -99,4 +110,120 @@ export const authored = {
99
110
  expectedUiState: "The stated threshold matches the price at which checkout starts offering EMI, and the worked example's instalment figures match the real ones. Either mismatch is a finding, with both numbers.",
100
111
  endResult: "Empty the cart; place no order.",
101
112
  },
113
+ /* ── Added 2026-09-14 (F3d) ──────────────────────────────────────────────────
114
+ * The blanket "how-it-works-pages" case above asserts all seven LOAD. These
115
+ * assert each one is TRUE: read the guide, then open the feature and compare.
116
+ * A guide is documentation with no compiler behind it, so nothing catches
117
+ * drift except doing both halves in one sitting — "it loads" passes against
118
+ * every stale sentence on the page.
119
+ */
120
+ "checklist-public-pages-help-how-it-works-how-auctions-work-matches-product": {
121
+ roles: ["guest"],
122
+ startPage: "/how-auctions-work",
123
+ steps: [
124
+ "Open /how-auctions-work signed out and read it end to end.",
125
+ "Write down what it says about the minimum bid increment, about bidding in the final minutes, and about what happens when a reserve is not met.",
126
+ "Open /auctions/auction-beyblade-original-dragoon-storm in a second tab.",
127
+ "Compare each of those three claims against what the auction page shows and what its bid form states.",
128
+ ],
129
+ expectedBehaviour: "Every claim the guide makes is true of the live auction: the increment it names is the increment the bid form enforces, the end-of-auction behaviour it describes is what the page shows, and an anti-snipe extension it promises actually exists.",
130
+ expectedUiState: "The guide renders its own headings and body text, not a placeholder. Each of the three claims matches. Quote verbatim any sentence that does not, next to what the auction page actually showed.",
131
+ endResult: "Read-only; nothing persists. Prices are gated signed out, so compare the RULES rather than the amounts. A mismatch is a real defect even though both pages load.",
132
+ },
133
+ "checklist-public-pages-help-how-it-works-how-checkout-works-matches-product": {
134
+ roles: ["buyer"],
135
+ startPage: "/how-checkout-works",
136
+ steps: [
137
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
138
+ "Open /how-checkout-works and write down the checkout steps it names, in order.",
139
+ "Add product-beyblade-burst-valkyrie to the cart and open /checkout.",
140
+ "Write down the steps checkout actually presents, in order.",
141
+ "Compare the two lists.",
142
+ ],
143
+ expectedBehaviour: "The guide names the same steps in the same order as the product: Address, then Add-ons and fees, then Payment. A guide still describing a two-step checkout sends a buyer looking for a screen that is not there; one that omits the add-ons step hides where the fees are chosen.",
144
+ expectedUiState: "The checkout step indicator shows three steps and the guide's list matches it. Any extra, missing or reordered step is quoted exactly from the guide.",
145
+ expectedData: { checkoutSteps: 3 },
146
+ endResult: "A cart line is left behind — remove it afterwards. Place no order. The guide itself is read-only.",
147
+ },
148
+ "checklist-public-pages-help-how-it-works-how-offers-work-matches-product": {
149
+ roles: ["buyer"],
150
+ startPage: "/how-offers-work",
151
+ steps: [
152
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
153
+ "Open /how-offers-work and write down what it says about how many counter-rounds are allowed and when an offer expires.",
154
+ "Open /products/product-beyblade-burst-valkyrie and open the Make-an-Offer form.",
155
+ "Read the limits and expiry the form itself states.",
156
+ "Compare them against the guide, then close the form without submitting.",
157
+ ],
158
+ expectedBehaviour: "The guide does not promise more counter-rounds than the product allows, and names the same expiry the offer actually gets. Over-promising here costs a buyer a negotiation they believed was still open.",
159
+ expectedUiState: "The offer form states its own limits. Every number in the guide matches a number the product enforces; any that does not is quoted from both places.",
160
+ endResult: "Do not submit the offer — cancel out of the form. Nothing persists.",
161
+ },
162
+ "checklist-public-pages-help-how-it-works-how-orders-work-matches-product": {
163
+ roles: ["buyer"],
164
+ startPage: "/how-orders-work",
165
+ steps: [
166
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
167
+ "Open /how-orders-work and list every order status it names.",
168
+ "Open /user/orders and list every status offered by the Active, Closed and All tabs and by the status filter.",
169
+ "Compare the two lists in both directions.",
170
+ ],
171
+ expectedBehaviour: "Every status the guide names is one an order can actually hold, and the guide omits none a buyer will see. A status that exists only in the guide teaches a vocabulary the product never uses.",
172
+ expectedUiState: "Both lists are written down and compared. Any status present in one and absent from the other is named explicitly, with which side it came from.",
173
+ endResult: "Read-only; nothing persists.",
174
+ },
175
+ "checklist-public-pages-help-how-it-works-how-payouts-work-matches-product": {
176
+ roles: ["seller"],
177
+ startPage: "/how-payouts-work",
178
+ steps: [
179
+ "Sign in as tyson@beybladearena.in / TempPass123!.",
180
+ "Open /how-payouts-work and write down the payout schedule and every deduction it names.",
181
+ "Open /store/payouts and read the schedule and deductions shown there.",
182
+ "Open one payout's detail and compare its deduction lines against the guide's list.",
183
+ ],
184
+ expectedBehaviour: "The schedule and the deductions match. This page describes money a seller is owed, so a stale figure is a seller expecting the wrong amount on the wrong day — not merely stale prose.",
185
+ expectedUiState: "Each deduction named in the guide appears on a real payout, and each deduction on the payout is explained by the guide. Mismatches are quoted with both numbers.",
186
+ endResult: "Read-only; nothing persists.",
187
+ },
188
+ "checklist-public-pages-help-how-it-works-how-pre-orders-work-matches-product": {
189
+ roles: ["guest"],
190
+ startPage: "/how-pre-orders-work",
191
+ steps: [
192
+ "Open /how-pre-orders-work signed out and write down what it says about the deposit and about whether a pre-order can be cancelled.",
193
+ "Open /pre-orders/preorder-beyblade-x-bx-08-wave.",
194
+ "Read what that listing states about its deposit terms and cancellability.",
195
+ "Compare the two.",
196
+ ],
197
+ expectedBehaviour: "The deposit wording and the cancellation rule match the listing. These are the two claims that drift, and both commit the buyer's money.",
198
+ expectedUiState: "The listing states deposit terms and a cancellation stance; the guide describes the same ones. Any difference is quoted from both pages.",
199
+ endResult: "Read-only; nothing persists. Prices are gated signed out, so compare the TERMS rather than the amounts.",
200
+ },
201
+ "checklist-public-pages-help-how-it-works-how-reviews-work-matches-product": {
202
+ roles: ["buyer"],
203
+ startPage: "/how-reviews-work",
204
+ steps: [
205
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
206
+ "Open /how-reviews-work and write down who it says may leave a review and when.",
207
+ "Open /products/product-beyblade-burst-valkyrie and try to leave a review.",
208
+ "Read what the form allows or refuses, and the reason it gives.",
209
+ ],
210
+ expectedBehaviour: "What the form enforces is what the guide describes. If the guide says only verified purchasers may review, the form refuses a non-purchaser and says so; if the form allows it anyway, the guide is wrong.",
211
+ expectedUiState: "Either the review form opens, or a refusal states its reason. That outcome matches the guide's sentence, which is quoted in the answer.",
212
+ endResult: "Do not submit a review. Nothing persists.",
213
+ },
214
+ "checklist-public-pages-help-how-it-works-track-order-page-works": {
215
+ roles: ["guest"],
216
+ startPage: "/track",
217
+ steps: [
218
+ "Open the site footer signed out and find the Support group.",
219
+ "Click the track-order link and note where it lands.",
220
+ "Type 9ab3f1-not-a-real-order into the tracking field and submit.",
221
+ "Note exactly what is shown.",
222
+ "Now submit the field left empty and note exactly what is shown.",
223
+ ],
224
+ inputs: { nonsenseOrderId: "9ab3f1-not-a-real-order" },
225
+ expectedBehaviour: "The footer link resolves to a real page, and a nonsense id produces a clear not-found outcome rather than a blank panel or a spinner that never resolves. The nonsense id is the control: a page answering the same way to everything is not looking anything up, and a tracking page that never fails is indistinguishable from one that never works.",
226
+ expectedUiState: "The page renders a tracking form. The nonsense id yields an explicit not-found message; the empty submit yields a field-level validation error rather than a silent no-op. Screenshot both outcomes.",
227
+ endResult: "Read-only; nothing persists. The id above is INVENTED and is expected not to resolve — that is the point of the control.",
228
+ },
102
229
  };
@@ -231,4 +231,93 @@ export const authored = {
231
231
  expectedUiState: "The form is open on arrival, the request appears in /item-requests, and is still there after a reload. A blank page on arrival is the finding.",
232
232
  endResult: "The QA item request is removed.",
233
233
  },
234
+ /* ── Added 2026-09-14 (F3d) ──────────────────────────────────────────────────
235
+ * The blanket cases above cover the four help sub-pages AS A SET — they load,
236
+ * they are linked both ways, their content differs from the parent's. These six
237
+ * address each page on its own terms and against the feature it documents,
238
+ * which is the half no "does it load" case reaches.
239
+ */
240
+ "checklist-public-pages-help-scams-guides-subpages-help-account-matches-product": {
241
+ roles: ["buyer"],
242
+ startPage: "/help/account",
243
+ steps: [
244
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
245
+ "Open /help/account and write down what it says about changing a password, changing an email, and closing an account.",
246
+ "Open /user/settings and find the controls for each of those three.",
247
+ "Compare what the help page describes against what the settings page actually offers.",
248
+ ],
249
+ expectedBehaviour: "Each of the three is described as the product actually implements it. Password change is a Firebase reset LINK sent to the account's inbox, not an in-page old/new password form — a help page describing an in-page form sends the user hunting for a control that does not exist.",
250
+ expectedUiState: "For each of the three actions, either the settings page offers what the help page describes, or the difference is quoted from both. An action the help page names with no control anywhere is the strongest finding.",
251
+ endResult: "Read-only — do not actually change the password or email. Nothing persists.",
252
+ },
253
+ "checklist-public-pages-help-scams-guides-subpages-help-auctions-matches-product": {
254
+ roles: ["buyer"],
255
+ startPage: "/help/auctions",
256
+ steps: [
257
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
258
+ "Open /help/auctions and write down what it tells a buyer about placing a bid, about being outbid, and about what happens if they lose.",
259
+ "Open /auctions/auction-beyblade-original-dragoon-storm and read what the bid form states.",
260
+ "Open /user/bids and read what it shows for a bid that is no longer winning.",
261
+ "Compare all three claims.",
262
+ ],
263
+ expectedBehaviour: "The help page's account of bidding, being outbid and losing matches what the auction page and the bids list actually do — including whether a losing bidder is notified at all.",
264
+ expectedUiState: "Each claim is matched against a real screen. Any claim with no corresponding behaviour is quoted verbatim.",
265
+ endResult: "Place no bid. Nothing persists.",
266
+ },
267
+ "checklist-public-pages-help-scams-guides-subpages-help-orders-matches-product": {
268
+ roles: ["buyer"],
269
+ startPage: "/help/orders",
270
+ steps: [
271
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
272
+ "Open /help/orders and list every action it says a buyer can take on an order — cancelling, returning, tracking, downloading an invoice.",
273
+ "Open /user/orders and open one order's detail page.",
274
+ "List the actions actually offered there.",
275
+ "Compare the two lists in both directions.",
276
+ ],
277
+ expectedBehaviour: "Every action the help page names is offered somewhere on a real order, and the order page offers nothing significant the help page omits. An action named in help but absent from the order page is the finding.",
278
+ expectedUiState: "Both lists are written down. Each missing or extra action is named, with the order id it was checked against.",
279
+ endResult: "Read-only — do not cancel or return anything. Nothing persists.",
280
+ },
281
+ "checklist-public-pages-help-scams-guides-subpages-help-shopping-matches-product": {
282
+ roles: ["buyer"],
283
+ startPage: "/help/shopping",
284
+ steps: [
285
+ "Sign in as rehan.sheikh@gmail.com / TempPass123!.",
286
+ "Open /help/shopping and write down what it says about carts, wishlists and coupons.",
287
+ "Add product-beyblade-burst-valkyrie to the cart and open /cart.",
288
+ "Open /wishlist.",
289
+ "Check each claim the help page made against those two screens, including any coupon rule it states.",
290
+ ],
291
+ expectedBehaviour: "The help page's description of the cart, the wishlist and how coupons combine matches the product. The coupon rule is the one most likely to be stale: at most one seller coupon per store plus at most one platform-wide coupon.",
292
+ expectedUiState: "Each claim is matched against the cart or wishlist screen. Any stale sentence is quoted, with what the screen actually did.",
293
+ endResult: "Remove the cart line afterwards. Place no order.",
294
+ },
295
+ "checklist-public-pages-help-scams-guides-subpages-seller-guide-bundles-matches-product": {
296
+ roles: ["seller"],
297
+ startPage: "/seller-guide/bundles",
298
+ steps: [
299
+ "Sign in as tyson@beybladearena.in / TempPass123!.",
300
+ "Open /seller-guide/bundles and write down every rule it states about what a bundle may contain and how it is priced.",
301
+ "Open the seller bundle editor from /store and begin creating a bundle.",
302
+ "Check each rule from the guide against what the editor allows or refuses.",
303
+ "In particular, check whether the guide says anything about all members having to belong to one store.",
304
+ ],
305
+ expectedBehaviour: "Every rule in the guide is enforced by the editor, and the editor enforces no significant rule the guide fails to mention. A bundle's members must all belong to a single store — that is refused at save time — so a guide silent on it invites a seller to build a bundle that cannot be saved.",
306
+ expectedUiState: "Each rule is matched against the editor's behaviour. A rule the guide states that the editor does not enforce, or a refusal the guide never warned about, is quoted.",
307
+ endResult: "Do not save the bundle — abandon the editor. Nothing persists.",
308
+ },
309
+ "checklist-public-pages-help-scams-guides-subpages-seller-guide-prize-draws-matches-product": {
310
+ roles: ["seller"],
311
+ startPage: "/seller-guide/prize-draws",
312
+ steps: [
313
+ "Sign in as tyson@beybladearena.in / TempPass123!.",
314
+ "Open /seller-guide/prize-draws and write down what it promises about entries, about how a winner is revealed, and about refunds if the draw does not fill.",
315
+ "Open the prize-draw creation flow from /store and read the options actually offered.",
316
+ "Open /prize-draws and pick a live draw to see what a buyer is shown.",
317
+ "Compare all three promises against what you saw.",
318
+ ],
319
+ expectedBehaviour: "The entry mechanism, the reveal and the refund behaviour described in the guide are the ones the product implements. Refunds are the one that matters most: a seller who believes unfilled draws auto-refund, when they do not, has made a promise to buyers on the product's behalf.",
320
+ expectedUiState: "Each of the three promises is matched against a real screen, and any that is unsupported is quoted verbatim from the guide.",
321
+ endResult: "Do not create a draw — abandon the flow. Nothing persists.",
322
+ },
234
323
  };
@@ -0,0 +1,2 @@
1
+ import type { AuthoredCase } from "./_types";
2
+ export declare const authored: Record<string, AuthoredCase>;
@@ -0,0 +1,141 @@
1
+ /*
2
+ * WHY: Authored six-part procedures for public-pages/newly-wired-browse-indexes.
3
+ * WHAT: 8 cases, keyed by full checklist id.
4
+ *
5
+ * 🛑 SIX PAGES THAT WERE REACHABLE ONLY BY TYPING THE URL. /classified,
6
+ * /digital-codes, /live, /lottery, /brands and /sellers each shipped with a real
7
+ * page and a real ROUTES.PUBLIC constant and NO link from anywhere on the site,
8
+ * so none has ever had a case (Root Cause #37). They were wired into the footer
9
+ * on 2026-09-14; the first case is therefore about the LINK, not the page.
10
+ *
11
+ * Every one carries a guest dimension because a signed-out visitor is their
12
+ * commonest caller, and because prices are gated for guests — a browse page that
13
+ * breaks rather than gates is the failure mode to look for.
14
+ *
15
+ * @tag domain:tester
16
+ * @tag layer:seed
17
+ * @tag pattern:none
18
+ * @tag access:server-only
19
+ * @tag consumers:seed-data/authored/index.ts
20
+ * @tag sideEffects:none
21
+ */
22
+ export const authored = {
23
+ "checklist-public-pages-newly-wired-browse-indexes-all-six-reachable-from-footer": {
24
+ roles: ["guest"],
25
+ startPage: "/",
26
+ steps: [
27
+ "Open the homepage and scroll to the footer.",
28
+ "Read the Shop column and list every link in it.",
29
+ "Click Classifieds and confirm where it lands.",
30
+ "Go back and repeat for Digital Codes, Live Items, Lotteries, Brands and Verified Sellers.",
31
+ ],
32
+ expectedBehaviour: "A page with no inbound link is reachable only by someone who already knows the URL, and a crawler has nothing to follow to it. Wiring it into the footer is what makes it part of the site rather than an orphan.",
33
+ expectedUiState: "All six appear in the footer Shop column and each opens its own page. Verified Sellers is labelled distinctly from Stores so the two do not read as the same destination.",
34
+ expectedData: { footerLinksFound: 6 },
35
+ endResult: "Nothing is changed; this case only reads.",
36
+ },
37
+ "checklist-public-pages-newly-wired-browse-indexes-classified-lists-only-classifieds": {
38
+ roles: ["guest"],
39
+ startPage: "/classified",
40
+ steps: [
41
+ "Open /classified.",
42
+ "Read the badge on each of the first six cards.",
43
+ "Open the first card and read the badge on its detail page.",
44
+ "Note the URL the card led to.",
45
+ ],
46
+ expectedBehaviour: "A type-specific index lists exactly that type. A listing type is filtered through an alias map that is NOT compile-checked against the union, so an unmapped type has its filter silently dropped and the page returns everything (Root Cause #58).",
47
+ expectedUiState: "Every card reads Classified. The detail page is at /classified/<slug>, not /products/<slug>.",
48
+ expectedData: { foreignTypeCardsFound: 0 },
49
+ endResult: "Nothing is changed; this case only reads.",
50
+ },
51
+ "checklist-public-pages-newly-wired-browse-indexes-digital-codes-lists-only-digital": {
52
+ roles: ["guest"],
53
+ startPage: "/digital-codes",
54
+ steps: [
55
+ "Open /digital-codes.",
56
+ "Read the badge on each of the first six cards.",
57
+ "Open the first card and confirm the URL it leads to.",
58
+ "Read whether the detail page states how many codes remain.",
59
+ ],
60
+ expectedBehaviour: "Same alias-map exposure as the classifieds index, plus the availability count: a digital-code listing reports its remaining pool, and that number is derived from the pool rather than typed by the seller.",
61
+ expectedUiState: "Every card reads Digital Code and each detail page is at /digital-codes/<slug>.",
62
+ expectedData: { foreignTypeCardsFound: 0 },
63
+ endResult: "Nothing is changed; this case only reads.",
64
+ },
65
+ "checklist-public-pages-newly-wired-browse-indexes-live-lists-only-live": {
66
+ roles: ["guest"],
67
+ startPage: "/live",
68
+ steps: [
69
+ "Open /live.",
70
+ "Read the badge on every card shown.",
71
+ "Open the first card and confirm the URL.",
72
+ "Read whether the detail page shows the species and jurisdiction fields a live item carries.",
73
+ ],
74
+ expectedBehaviour: "The live index lists only live items. This type carries fields no other type has, so a card of another type here is visibly wrong on the detail page as well as on the badge.",
75
+ expectedUiState: "Every card reads Live Item and each detail page is at /live/<slug>.",
76
+ expectedData: { foreignTypeCardsFound: 0 },
77
+ endResult: "Nothing is changed; this case only reads.",
78
+ },
79
+ "checklist-public-pages-newly-wired-browse-indexes-lottery-lists-active-lotteries": {
80
+ roles: ["guest"],
81
+ startPage: "/lottery",
82
+ steps: [
83
+ "Open /lottery.",
84
+ "Count the lotteries listed and read the slot information on each.",
85
+ "Open event-pokemon-number-draw-july-2026 from the list.",
86
+ "Read how many slots it has and how many are taken.",
87
+ "Go back and look for event-beyblade-slot-raffle-draft, which is a DRAFT.",
88
+ ],
89
+ inputs: {
90
+ activeLottery: "event-pokemon-number-draw-july-2026",
91
+ draftLottery: "event-beyblade-slot-raffle-draft",
92
+ },
93
+ expectedBehaviour: "The index lists ACTIVE lotteries. A draft is unfinished work and must not be public — and a slot count shown publicly must never expose a slot's price or weight, which are server-side fields.",
94
+ expectedUiState: "The active lottery is listed with its slot counts. The draft one is absent.",
95
+ expectedData: { draftLotteryListed: false },
96
+ endResult: "Nothing is changed; this case only reads.",
97
+ },
98
+ "checklist-public-pages-newly-wired-browse-indexes-brands-tiles-open-their-brand": {
99
+ roles: ["guest"],
100
+ startPage: "/brands",
101
+ steps: [
102
+ "Open /brands and write down each brand's name and the product count on its tile.",
103
+ "Open the first brand.",
104
+ "Read the total the brand page reports.",
105
+ "Read the brand name on two of its product cards.",
106
+ ],
107
+ expectedBehaviour: "A brand tile's count promises what its page lists. Products are matched to a brand by DISPLAY NAME rather than by id, so a brand whose name and whose products' brand strings have drifted apart lists nothing while still showing a count.",
108
+ expectedUiState: "The tile count equals the brand page's total, and the products shown carry that brand's name.",
109
+ expectedData: { tileCountMatchesPage: true },
110
+ endResult: "Nothing is changed; this case only reads.",
111
+ },
112
+ "checklist-public-pages-newly-wired-browse-indexes-sellers-is-not-a-copy-of-stores": {
113
+ roles: ["guest"],
114
+ startPage: "/sellers",
115
+ steps: [
116
+ "Open /sellers and write down what each row shows.",
117
+ "Open /stores in a second tab and write down what each card shows.",
118
+ "Compare the two: what does one show that the other does not.",
119
+ "Click through from each to a seller or store and note where you land.",
120
+ ],
121
+ expectedBehaviour: "These are two pages with two purposes — verified sellers, and shopfronts. If they are indistinguishable then one of them is a duplicate and the footer now has two links to the same thing, which is worse than the single unlinked page it replaced.",
122
+ expectedUiState: "The two pages present different information. Each link lands somewhere coherent with the page it came from.",
123
+ expectedData: { pagesAreDistinguishable: true },
124
+ endResult: "Nothing is changed; this case only reads.",
125
+ },
126
+ "checklist-public-pages-newly-wired-browse-indexes-guest-can-browse-all-six": {
127
+ roles: ["guest"],
128
+ startPage: "/classified",
129
+ steps: [
130
+ "Open a private window with no session.",
131
+ "Open each of /classified, /digital-codes, /live, /lottery, /brands and /sellers in turn.",
132
+ "On each, confirm cards render and note what appears where a price would be.",
133
+ "Open one listing detail page from a price-bearing index.",
134
+ "Read the price area on that detail page.",
135
+ ],
136
+ expectedBehaviour: "Prices are hidden from signed-out visitors, and the listing itself is not. The gate replaces the amount with a prompt; it does not remove the row, blank the card or break the page — a card whose price row vanishes reads as a rendering fault and changes the row height.",
137
+ expectedUiState: "All six pages render for a guest. Where a price would be, a sign-in prompt appears in its place rather than an empty gap or a broken layout.",
138
+ expectedData: { pagesBrokenForGuest: 0 },
139
+ endResult: "No session is created and nothing is changed.",
140
+ },
141
+ };
@@ -0,0 +1,2 @@
1
+ import type { AuthoredCase } from "./_types";
2
+ export declare const authored: Record<string, AuthoredCase>;
@@ -0,0 +1,115 @@
1
+ /*
2
+ * WHY: Authored six-part procedures for public-pages/store-tabs.
3
+ * WHAT: 6 cases, keyed by full checklist id.
4
+ *
5
+ * 🛑 THE DEFAULT TAB HAD NO CASE. Nine `/stores/[storeSlug]/*` tabs were
6
+ * untested, `products` among them — which is the first thing anyone sees after
7
+ * clicking a store.
8
+ *
9
+ * The reviews tab is included deliberately: it answered HTTP 200 with zero
10
+ * reviews for EVERY store until 2026-09-14 (Root Cause #100), and the only
11
+ * symptom was a sentence that reads as a fact about the store rather than as a
12
+ * defect. That is why the case names a store known to have reviews.
13
+ *
14
+ * @tag domain:tester
15
+ * @tag layer:seed
16
+ * @tag pattern:none
17
+ * @tag access:server-only
18
+ * @tag consumers:seed-data/authored/index.ts
19
+ * @tag sideEffects:none
20
+ */
21
+ export const authored = {
22
+ "checklist-public-pages-store-tabs-default-tab-lists-products": {
23
+ roles: ["guest"],
24
+ startPage: "/stores",
25
+ steps: [
26
+ "Open /stores and click through to Beyblade Arena.",
27
+ "Note which tab is selected on arrival.",
28
+ "Count the listings shown and read the total the pager reports.",
29
+ "Open two of the cards and read which store each says it belongs to.",
30
+ ],
31
+ inputs: { store: "store-beyblade-arena" },
32
+ expectedBehaviour: "A store opens on its Products tab with that store's listings. A listing carries its store as a denormalised field, so a card showing no seller — or the wrong one — means the listing was written without a store rather than that the tab filtered wrongly.",
33
+ expectedUiState: "Products is the selected tab on arrival, cards are listed, and each card names Beyblade Arena.",
34
+ expectedData: { defaultTab: "products" },
35
+ endResult: "Nothing is changed; this case only reads.",
36
+ },
37
+ "checklist-public-pages-store-tabs-each-tab-lists-only-its-type": {
38
+ roles: ["guest"],
39
+ startPage: "/stores/store-beyblade-arena",
40
+ steps: [
41
+ "Open Beyblade Arena and open the Auctions tab.",
42
+ "Read the badge on each card shown.",
43
+ "Open the Pre-orders tab and read its badges.",
44
+ "Open the Digital Codes tab and read its badges.",
45
+ "Open the Art tab and read its badges.",
46
+ ],
47
+ inputs: { store: "store-beyblade-arena" },
48
+ expectedBehaviour: "Each tab is a type filter over that one store. Tabs key on a slug that is a DIFFERENT id space from the listing type, so conflating the two either breaks the URL or silently returns nothing — and nothing looks exactly like an empty tab.",
49
+ expectedUiState: "Every card under a tab carries that tab's badge. No card of another type appears under any tab.",
50
+ expectedData: { mismatchedBadges: 0 },
51
+ endResult: "Nothing is changed; this case only reads.",
52
+ },
53
+ "checklist-public-pages-store-tabs-empty-tab-says-empty": {
54
+ roles: ["guest"],
55
+ startPage: "/stores/store-beyblade-arena",
56
+ steps: [
57
+ "Open Beyblade Arena and work through every tab in turn.",
58
+ "Find one that lists nothing.",
59
+ "Wait for it to finish loading before judging it.",
60
+ "Read what it shows.",
61
+ ],
62
+ inputs: { store: "store-beyblade-arena" },
63
+ expectedBehaviour: "An empty tab says it is empty. A spinner that never resolves and a blank panel both read as a failure, and an empty state is also how a genuinely broken query presents — so the wording should say what is absent rather than merely showing nothing.",
64
+ expectedUiState: "A worded empty state. Not a permanent skeleton and not a blank area below the tab bar.",
65
+ expectedData: { blankPanelFound: false },
66
+ endResult: "Nothing is changed; this case only reads.",
67
+ },
68
+ "checklist-public-pages-store-tabs-tab-survives-a-reload": {
69
+ roles: ["guest"],
70
+ startPage: "/stores/store-beyblade-arena",
71
+ steps: [
72
+ "Open Beyblade Arena and switch to the Auctions tab.",
73
+ "Copy the URL from the address bar.",
74
+ "Reload the page.",
75
+ "Note which tab is selected.",
76
+ "Open the copied URL in a new tab and note which is selected there.",
77
+ ],
78
+ inputs: { store: "store-beyblade-arena" },
79
+ expectedBehaviour: "The tab is in the URL, so a reload and a shared link both land on it. A tab held only in component state bounces to the default on reload, which makes the URL unshareable without any visible error.",
80
+ expectedUiState: "Both the reload and the fresh tab land on Auctions.",
81
+ expectedData: { tabAfterReload: "auctions" },
82
+ endResult: "Nothing is changed; this case only reads.",
83
+ },
84
+ "checklist-public-pages-store-tabs-store-tabs-hide-other-stores-items": {
85
+ roles: ["guest"],
86
+ startPage: "/stores/store-letitrip-official",
87
+ steps: [
88
+ "Open /stores/store-letitrip-official — a store with a small catalogue, so a leak is obvious.",
89
+ "Work through every tab and read the store name on each card.",
90
+ "Note any card naming a different store.",
91
+ "Open one card and confirm on its detail page which store it belongs to.",
92
+ ],
93
+ inputs: { store: "store-letitrip-official" },
94
+ expectedBehaviour: "A store page is scoped to one store on every tab. A small catalogue is chosen on purpose: on a store with sixty listings a handful of foreign cards is easy to miss.",
95
+ expectedUiState: "Every card on every tab names LetItRip Official.",
96
+ expectedData: { foreignStoreCards: 0 },
97
+ endResult: "Nothing is changed; this case only reads.",
98
+ },
99
+ "checklist-public-pages-store-tabs-coupons-tab-shows-this-stores-coupons": {
100
+ roles: ["guest"],
101
+ startPage: "/stores/store-beyblade-arena",
102
+ steps: [
103
+ "Open Beyblade Arena and open its Coupons tab.",
104
+ "Write down every coupon code shown.",
105
+ "Open /stores/store-letitrip-official and open its Coupons tab.",
106
+ "Write down those codes.",
107
+ "Compare the two lists.",
108
+ ],
109
+ inputs: { sellerCoupon: "ARENA25", otherStoreCoupon: "OFFICIAL10" },
110
+ expectedBehaviour: "A store's coupons tab shows that store's own coupons. A seller coupon is scoped to one storeId and a platform coupon has none, so a tab that does not filter shows every seller's codes to every other seller's customers.",
111
+ expectedUiState: "ARENA25 appears under Beyblade Arena and not under LetItRip Official. OFFICIAL10 appears under LetItRip Official and not under Beyblade Arena.",
112
+ expectedData: { crossStoreCouponShown: false },
113
+ endResult: "Nothing is changed; this case only reads.",
114
+ },
115
+ };
@@ -0,0 +1,2 @@
1
+ import type { AuthoredCase } from "./_types";
2
+ export declare const authored: Record<string, AuthoredCase>;