@tristan2828/ui-foundation 4.18.1 → 4.18.2

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.
@@ -511,6 +511,29 @@ is a filled-in example while the Widgets demo is still here).
511
511
  `onError` — and stub it through the override, never `page.route`,
512
512
  which cannot see requests the MSW service worker makes
513
513
  (`e2e/msw-contract.spec.ts` has an example).
514
+ **Forcing a mutation's success forces its refetch too.** A mutation
515
+ invalidates its queries when it answers, and the refetch reads the
516
+ mock store, which the override never changed, so the screen goes back
517
+ to the old record and the test fails on a value it just saw. Answer
518
+ the follow-up GET with the record the mutation answered:
519
+
520
+ ```ts
521
+ // The record as the server would save it: what the PATCH answers.
522
+ const saved = { ...WIRELESS_MOUSE, status: 'archived' }
523
+ await waitForMswReady(page)
524
+ await page.evaluate((saved) => {
525
+ const { worker, http, HttpResponse } = window.__msw
526
+ worker.use(
527
+ http.patch('*/api/widgets/1', () => HttpResponse.json(saved)),
528
+ // The invalidation refetch: without this it shows the old status.
529
+ http.get('*/api/widgets/1', () => HttpResponse.json(saved)),
530
+ )
531
+ }, saved)
532
+ ```
533
+
534
+ Answer the list's GET the same way if the screen shows the list. Only
535
+ a forced error needs no GET: a refused mutation changes nothing to
536
+ read back.
514
537
  9. **Register the nav entry and route names** in `e2e/shell.spec.ts`'s
515
538
  `NAV_ENTRIES` so the shell smoke test covers the new screen, add
516
539
  `/<entity>/new` to `e2e/a11y.spec.ts`'s `formRoutes`, and add the view
@@ -153,3 +153,40 @@ test('running, paused and done', async ({ page }) => {
153
153
  before the page loads:
154
154
  `page.addInitScript(() => localStorage.setItem('my-app:routine:muted', 'true'))`.
155
155
  - **Reduced motion:** `await page.emulateMedia({ reducedMotion: 'reduce' })`.
156
+
157
+ **An open-ended count-up: pause the fake clock.** After
158
+ `clock.install()` the fake clock still moves in real time between
159
+ steps, so every reading picks up however long the machine took since the
160
+ last `fastForward`. A count-up with no end (a stopwatch, a hold that
161
+ ends on a tap) has nothing to stop at, so under load its readings are
162
+ off by a second or more. Pause the clock once the session opens and
163
+ move it only with `fastForward`:
164
+
165
+ ```ts
166
+ test('the hold counts up until the tap', async ({ page }) => {
167
+ await page.clock.install()
168
+ await page.goto('/routine')
169
+ await page.getByRole('button', { name: 'Start' }).click()
170
+ // Slightly ahead, never Date.now() itself: the page's clock has already
171
+ // moved past the test's, and pauseAt() refuses an earlier time with
172
+ // "Cannot fast-forward to the past".
173
+ await page.clock.pauseAt(Date.now() + 50)
174
+
175
+ await page.clock.fastForward('00:42')
176
+ await expect(page.getByRole('timer', { name: 'Held for' })).toHaveText('0:42')
177
+
178
+ // A paused clock stops axe and the dialog's close animation: resume first.
179
+ await page.clock.resume()
180
+ await expectNoAxeViolations(page) // @tristan2828/ui-foundation/testing
181
+ await page.getByRole('button', { name: 'Close' }).click()
182
+ await expect(page.getByRole('dialog')).toBeHidden()
183
+ })
184
+ ```
185
+
186
+ - **`pauseAt(Date.now() + 50)`, not `pauseAt(Date.now())`.** The page's
187
+ fake time is already a little ahead of the test's `Date.now()`, and
188
+ `pauseAt` fast-forwards to the time it's given, so a time already
189
+ passed fails with "Cannot fast-forward to the past".
190
+ - **`clock.resume()` before axe or closing the view.** With the clock
191
+ paused, axe's scan never finishes and the dialog's close animation
192
+ never ends, so the test times out with nothing pointing at the clock.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tristan2828/ui-foundation",
3
- "version": "4.18.1",
3
+ "version": "4.18.2",
4
4
  "description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
5
5
  "license": "MIT",
6
6
  "repository": {