@propriety/court-calendar 1.0.171 → 1.0.173
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/.claude/skills/gen-test/SKILL.md +17 -0
- package/CLAUDE.md +6 -0
- package/README.md +1 -1
- package/dist/__tests__/hooks/UseCourtDatesDeletion.test.d.ts +14 -0
- package/dist/helpers/api/apiFailures.d.ts +14 -18
- package/dist/hooks/UseCourtDates.d.ts +2 -1
- package/dist/index.mjs +4053 -4046
- package/package.json +1 -1
- package/src/__tests__/hooks/UseCourtDatesDeletion.test.ts +150 -0
- package/src/helpers/api/apiFailures.ts +10 -0
- package/src/helpers/api/courtDates.ts +5 -1
- package/src/hooks/UseCourtDates.ts +81 -16
|
@@ -5,6 +5,23 @@ description: Generate Vitest + React Testing Library tests for a court-calendar
|
|
|
5
5
|
|
|
6
6
|
Generate tests for the `@propriety/court-calendar` library targeting the file or subject passed as $ARGUMENTS.
|
|
7
7
|
|
|
8
|
+
## The bar — read before writing a single test
|
|
9
|
+
|
|
10
|
+
**More tests is not better.** A PR has been rejected in this codebase family for carrying too many
|
|
11
|
+
machine-generated tests. Your job is the *fewest* tests that would actually catch a regression, not
|
|
12
|
+
broad coverage of every prop permutation.
|
|
13
|
+
|
|
14
|
+
- **If you cannot name the bug a test would catch, do not write it.**
|
|
15
|
+
- Every test carries a comment saying **what failure it catches and why that failure is plausible** —
|
|
16
|
+
not a restatement of the assertion. A group of cases sharing one rationale carries it on the
|
|
17
|
+
`describe`.
|
|
18
|
+
- Never write: a test asserting on the **text of a file** · a test of an internal helper when the
|
|
19
|
+
behaviour is reachable by rendering the component · a test restating its neighbour with different
|
|
20
|
+
props and the same failure mode · a test that a constant equals itself · a test written for coverage.
|
|
21
|
+
- Do keep the negative case of any guard protecting a destructive action or a permission.
|
|
22
|
+
|
|
23
|
+
Report how many tests you wrote and why each earns its place.
|
|
24
|
+
|
|
8
25
|
## Project Test Conventions
|
|
9
26
|
- Test files live in `src/__tests__/` named `<subject>.test.ts(x)`
|
|
10
27
|
- Shared fixtures: `src/__tests__/fixtures.ts` — read this first for reusable test data
|
package/CLAUDE.md
CHANGED
|
@@ -38,4 +38,10 @@ Context is at `src/context/ReferenceDataContext.tsx` and provides: `allUsers`, `
|
|
|
38
38
|
|
|
39
39
|
- **Formatter/Linter**: Biome — not Prettier or ESLint
|
|
40
40
|
- **Tests**: Vitest + @testing-library/react. Files: `src/__tests__/`, fixtures: `src/__tests__/fixtures.ts`
|
|
41
|
+
- **Tests must earn their place**: more tests is not better — a PR has been rejected in this codebase
|
|
42
|
+
family for machine-generated test bloat. Cover the behaviour that changed with the fewest tests
|
|
43
|
+
that would catch a regression, and give each a comment saying what failure it catches and why that
|
|
44
|
+
failure is plausible. If you cannot name the bug, do not write the test. Never assert on the text
|
|
45
|
+
of a file, a private helper's internals, or a constant equalling itself. `/test-audit` applies this
|
|
46
|
+
retroactively.
|
|
41
47
|
- **Pre-commit**: Husky runs on commit — run `npx biome format --write` before staging
|
package/README.md
CHANGED
|
@@ -480,7 +480,7 @@ Publishing is automated via GitHub Actions (`.github/workflows/publish.yml`).
|
|
|
480
480
|
## Known limitations
|
|
481
481
|
|
|
482
482
|
- **Port 8000 only:** The backend API enforces CORS that only allows requests from `localhost:8000`. Applies to the dev server and any locally running consumer.
|
|
483
|
-
- **Cache is not invalidated on external changes:** If another user modifies a court date, the change won't appear until the local cache expires (1 minute for dates, 2 minutes for cases).
|
|
483
|
+
- **Cache is not invalidated on external changes:** If another user *modifies* a court date, the change won't appear until the local cache expires (1 minute for dates, 2 minutes for cases). A *deletion* does propagate: the poll drops any in-window date the server stops returning, and a save answered with 404 removes the row immediately — which matters because polling is suspended while a modal is open.
|
|
484
484
|
- **No pagination for court dates:** `getAllDates` fetches every court date in a single request.
|
|
485
485
|
- **Single API key:** No built-in token refresh or rotation.
|
|
486
486
|
- **No error UI for API failures:** Network errors are logged to the console but not surfaced to the user (except for bootstrap errors, which show a brief snackbar).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for how useCourtDates reconciles a date that no longer exists server-side.
|
|
3
|
+
*
|
|
4
|
+
* On 2026-08-24 court date 499 was deleted at 13:41. At 15:31 a user still saw it in the
|
|
5
|
+
* table view, clicked save on it twice, and got HTTP 404 both times. The calendar had polled
|
|
6
|
+
* for it about twenty-two times in between and every response correctly omitted the row.
|
|
7
|
+
*
|
|
8
|
+
* The reason it survived: the merge was a union — `[...fresh, ...prev.filter(notInFresh)]`.
|
|
9
|
+
* A deletion *is* an absence, so that expression cannot represent one; each poll re-added the
|
|
10
|
+
* row it had just been told about. The union was there for a reason (loadRange merges
|
|
11
|
+
* out-of-window months into the same array and a plain replacement would wipe them), which is
|
|
12
|
+
* why both directions are pinned here.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* A cross-cutting sink for API failures, so a request the server rejected always reaches the
|
|
3
|
-
* screen instead of only the console.
|
|
4
|
-
*
|
|
5
|
-
* Every helper in this folder reports failure by returning `false`/`null` and writing a
|
|
6
|
-
* `console.error` — which is why a delete the server refused looked identical to one that
|
|
7
|
-
* worked. Rather than change each helper's signature (and every call site, and every
|
|
8
|
-
* `.then(ok => …)` that depends on the boolean), each failing branch now *also* publishes an
|
|
9
|
-
* `ApiFailure` here. `ApiFailureReporter` subscribes on mount and renders whatever arrives.
|
|
10
|
-
*
|
|
11
|
-
* A module-level bus rather than React context because these fire from plain async functions
|
|
12
|
-
* that have no access to a provider — the same reason crash reporters are built this way.
|
|
13
|
-
*
|
|
14
|
-
* NOTE: nothing published here may contain the API key. The report is written to be pasted
|
|
15
|
-
* into a chat or ticket, so it carries the request *shape* (method, URL, status, response
|
|
16
|
-
* body) but never a credential.
|
|
17
|
-
*/
|
|
1
|
+
import { DateType } from '../../types';
|
|
18
2
|
export interface ApiFailure {
|
|
19
3
|
/** What the user was trying to do, in their words: "Delete court date". */
|
|
20
4
|
operation: string;
|
|
@@ -29,6 +13,15 @@ export interface ApiFailure {
|
|
|
29
13
|
/** Raw response body (or the thrown error's message), truncated. For the tech team. */
|
|
30
14
|
body?: string;
|
|
31
15
|
at: Date;
|
|
16
|
+
/**
|
|
17
|
+
* Which record the request was about, in machine-readable form. `subject` is the same thing
|
|
18
|
+
* for a human to read; this is what lets a subscriber *act* on the failure — a 404 on a save
|
|
19
|
+
* means the row is gone server-side and should leave the screen.
|
|
20
|
+
*/
|
|
21
|
+
record?: {
|
|
22
|
+
id: number;
|
|
23
|
+
dateType?: DateType;
|
|
24
|
+
};
|
|
32
25
|
}
|
|
33
26
|
type Listener = (failure: ApiFailure) => void;
|
|
34
27
|
/** Subscribe to API failures. Returns an unsubscribe function suitable for a `useEffect`. */
|
|
@@ -36,7 +29,10 @@ export declare function onApiFailure(listener: Listener): () => void;
|
|
|
36
29
|
/** Publish a failure to every subscriber. A throwing listener must not break the others. */
|
|
37
30
|
export declare function publishApiFailure(failure: ApiFailure): void;
|
|
38
31
|
/** Log and publish a non-2xx response. */
|
|
39
|
-
export declare function reportHttpFailure(operation: string, method: string, url: string, res: Response, subject?: string
|
|
32
|
+
export declare function reportHttpFailure(operation: string, method: string, url: string, res: Response, subject?: string, record?: {
|
|
33
|
+
id: number;
|
|
34
|
+
dateType?: DateType;
|
|
35
|
+
}): Promise<void>;
|
|
40
36
|
/**
|
|
41
37
|
* Log and publish a request that never completed.
|
|
42
38
|
*
|
|
@@ -8,7 +8,8 @@ import { CourtDate } from '../types';
|
|
|
8
8
|
* restoring from cache.
|
|
9
9
|
*
|
|
10
10
|
* Optimistic updates are applied immediately to both React state and the cache;
|
|
11
|
-
* chair-assignment changes are rolled back if the API call fails.
|
|
11
|
+
* chair-assignment changes are rolled back if the API call fails. A date the server
|
|
12
|
+
* reports as gone — absent from a refetch, or answering a save with 404 — is removed.
|
|
12
13
|
*
|
|
13
14
|
* @param apiKey API key forwarded to court-date helpers.
|
|
14
15
|
* @param activeUser Current user ID, forwarded to `updateCourtDate`.
|