test(frontend): replay real messages instead of hand-built state literals (WP-70)
Every machine spec redefined its own throwaway fixture helper (editing1/2/3,
editingWith), hardcoding fields like errors: {} that assert against shapes
the reducer may never actually produce. given(reduce, initial)(...msgs)
(libs/shared/src/testing/machine.ts) replaces them by replaying real Msgs
through the real reduce, so a fixture is provably reachable. Adds the same
idiom for value objects (unwrapOk) and RemoteData (loading/success/failure),
plus intake.acceptance.spec.ts as a worked full-journey example.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Test-only DSL for the Elm-store idiom (CLAUDE.md #3). A fixture is built by
|
||||
* replaying real `Msg`s through the real `reduce` — never by hand-assembling a
|
||||
* state object field-by-field. That closes off illegal states the reducer would
|
||||
* never actually produce: if a spec can't reach a state via messages, it can't
|
||||
* assert on it either.
|
||||
*
|
||||
* `given(reduce, initial)` partially applies a machine's reducer + starting
|
||||
* state; the result is a variadic replay function a spec calls with the exact
|
||||
* message sequence a real user/flow would send.
|
||||
*/
|
||||
export const given =
|
||||
<S, M>(reduce: (s: S, m: M) => S, initial: S) =>
|
||||
(...msgs: M[]): S =>
|
||||
msgs.reduce(reduce, initial);
|
||||
@@ -0,0 +1,13 @@
|
||||
import { RemoteData } from '@shared/application/remote-data';
|
||||
|
||||
/**
|
||||
* Test-only constructors for `RemoteData` — one line per variant, so a spec
|
||||
* builds fixtures through the same tagged-union shape production code renders
|
||||
* (`foldRemote`/`<app-async>`), never a hand-rolled literal that could drift
|
||||
* from the real type.
|
||||
*/
|
||||
export const loading = <E = never, T = never>(): RemoteData<E, T> => ({ tag: 'Loading' });
|
||||
|
||||
export const success = <T, E = never>(value: T): RemoteData<E, T> => ({ tag: 'Success', value });
|
||||
|
||||
export const failure = <E, T = never>(error: E): RemoteData<E, T> => ({ tag: 'Failure', error });
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Result } from '@shared/kernel/fp';
|
||||
|
||||
/**
|
||||
* Unwrap a `Result` produced by a REAL `parse*` value-object parser, throwing
|
||||
* if it isn't `ok`. This is the only sanctioned way for a spec to obtain a
|
||||
* branded value-object type — it closes off the `'garbage' as Postcode` cast
|
||||
* route, since the only door to the branded type is the parser itself.
|
||||
*/
|
||||
export function unwrapOk<E, T>(result: Result<E, T>): T {
|
||||
if (!result.ok) {
|
||||
throw new Error(`unwrapOk: expected ok, got error: ${JSON.stringify(result.error)}`);
|
||||
}
|
||||
return result.value;
|
||||
}
|
||||
Reference in New Issue
Block a user