@playfast/reform-proof 1.0.1 → 1.1.1
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/package.json +1 -1
- package/src/assertions.ts +65 -0
- package/src/driverTypes.ts +19 -0
- package/src/engine.ts +8 -589
- package/src/errors.ts +0 -14
- package/src/execution.ts +100 -0
- package/src/facade.ts +258 -0
- package/src/index.ts +53 -294
- package/src/product.ts +77 -0
- package/src/runner.ts +1 -17
- package/src/runtimeFacade.ts +269 -0
- package/src/runtimeTreeDispose.ts +21 -0
- package/src/runtimeTreeDriver.ts +285 -0
- package/src/runtimeTreeFacade.ts +134 -0
- package/src/runtimeTreeSettle.ts +34 -0
- package/src/runtimeTreeTypes.ts +99 -0
- package/src/sink.ts +73 -0
- package/src/structure-events.test.ts +0 -11
- package/src/structure-locators.test.ts +0 -23
- package/src/treeBindings.ts +121 -0
- package/src/typed-seams.test.ts +0 -11
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playfast/reform-proof",
|
|
3
3
|
"playbook": "./playbook",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Headless testing toolkit for reform — drive a scene's compositions and assert on the props they compute, with no DOM, timers, or text matching.",
|
|
7
7
|
"keywords": [
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Array as Arr, Effect, Option, Record as Rec } from 'effect'
|
|
2
|
+
import { AssertionFailed } from './errors'
|
|
3
|
+
|
|
4
|
+
interface ComparePair {
|
|
5
|
+
readonly left: unknown
|
|
6
|
+
readonly right: unknown
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// oxlint-disable-next-line reform-rules/no-json-parse-stringify -- arbitrary unknown assertion values, not Schema-typed data
|
|
10
|
+
const show = (subject: unknown): string => JSON.stringify(subject)
|
|
11
|
+
|
|
12
|
+
const deepEqual = ({ left, right }: ComparePair): boolean =>
|
|
13
|
+
Object.is(left, right) || show(left) === show(right)
|
|
14
|
+
|
|
15
|
+
const fail = (message: string): Effect.Effect<never> =>
|
|
16
|
+
Effect.dieMessage(new AssertionFailed({ detail: message }).message)
|
|
17
|
+
|
|
18
|
+
export const expect = <A>(actual: A) => ({
|
|
19
|
+
toBe: (expected: A): Effect.Effect<void> =>
|
|
20
|
+
Object.is(actual, expected)
|
|
21
|
+
? Effect.void
|
|
22
|
+
: fail(`expected ${show(actual)} to be ${show(expected)}`),
|
|
23
|
+
toEqual: (expected: A): Effect.Effect<void> =>
|
|
24
|
+
deepEqual({ left: actual, right: expected })
|
|
25
|
+
? Effect.void
|
|
26
|
+
: fail(`expected ${show(actual)} to equal ${show(expected)}`),
|
|
27
|
+
toContain: (expected: A extends ReadonlyArray<infer E> ? E : unknown): Effect.Effect<void> =>
|
|
28
|
+
Array.isArray(actual) && actual.some((element) => deepEqual({ left: element, right: expected }))
|
|
29
|
+
? Effect.void
|
|
30
|
+
: fail(`expected ${show(actual)} to contain ${show(expected)}`),
|
|
31
|
+
toMatchObject: (expected: Partial<A>): Effect.Effect<void> => {
|
|
32
|
+
const mismatch = matchPartial({ actual, expected })
|
|
33
|
+
return mismatch === undefined ? Effect.void : fail(mismatch)
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const isRecord = (candidate: unknown): candidate is Record<string, unknown> =>
|
|
38
|
+
typeof candidate === 'object' && candidate !== null
|
|
39
|
+
|
|
40
|
+
interface MatchInput {
|
|
41
|
+
readonly actual: unknown
|
|
42
|
+
readonly expected: unknown
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const matchPartial = ({ actual, expected }: MatchInput): string | undefined => {
|
|
46
|
+
if (!isRecord(expected)) {
|
|
47
|
+
return deepEqual({ left: actual, right: expected })
|
|
48
|
+
? undefined
|
|
49
|
+
: `expected ${show(actual)} to match ${show(expected)}`
|
|
50
|
+
}
|
|
51
|
+
if (!isRecord(actual)) {
|
|
52
|
+
return `expected ${show(actual)} to be an object matching ${show(expected)}`
|
|
53
|
+
}
|
|
54
|
+
const mismatch = Arr.findFirst(
|
|
55
|
+
Rec.keys(expected),
|
|
56
|
+
(key) => !deepEqual({ left: actual[key], right: expected[key] }),
|
|
57
|
+
)
|
|
58
|
+
return Option.match(mismatch, {
|
|
59
|
+
onNone: () => undefined,
|
|
60
|
+
onSome: (key) =>
|
|
61
|
+
`expected key ${show(key)} to match ${show(expected[key])}, got ${show(actual[key])}`,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const matchProps: (input: MatchInput) => string | undefined = matchPartial
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { UiCapture } from '@playfast/reform'
|
|
2
|
+
|
|
3
|
+
export interface StepFrame {
|
|
4
|
+
readonly index: number
|
|
5
|
+
readonly captures: ReadonlyArray<UiCapture>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface DriveResult {
|
|
9
|
+
readonly requirement: string
|
|
10
|
+
readonly frames: ReadonlyArray<StepFrame>
|
|
11
|
+
readonly ok: boolean
|
|
12
|
+
// oxlint-disable-next-line reform-rules/no-optional-fields -- serialized result DTO read as `error ?? …` by the editor timeline (external); optional kept for wire compat
|
|
13
|
+
readonly error?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ProofDriver {
|
|
17
|
+
readonly requirement: string
|
|
18
|
+
readonly run: () => Promise<DriveResult>
|
|
19
|
+
}
|