foldkit 0.43.2 → 0.44.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.
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/test/apps/counter.d.ts +38 -0
- package/dist/test/apps/counter.d.ts.map +1 -0
- package/dist/test/apps/counter.js +25 -0
- package/dist/test/apps/formChild.d.ts +69 -0
- package/dist/test/apps/formChild.d.ts.map +1 -0
- package/dist/test/apps/formChild.js +84 -0
- package/dist/test/apps/keypress.d.ts +23 -0
- package/dist/test/apps/keypress.d.ts.map +1 -0
- package/dist/test/apps/keypress.js +39 -0
- package/dist/test/apps/login.d.ts +46 -0
- package/dist/test/apps/login.d.ts.map +1 -0
- package/dist/test/apps/login.js +83 -0
- package/dist/test/apps/logoutButton.d.ts +17 -0
- package/dist/test/apps/logoutButton.d.ts.map +1 -0
- package/dist/test/apps/logoutButton.js +22 -0
- package/dist/test/internal.d.ts +25 -0
- package/dist/test/internal.d.ts.map +1 -0
- package/dist/test/internal.js +39 -0
- package/dist/test/matchers.d.ts +94 -0
- package/dist/test/matchers.d.ts.map +1 -0
- package/dist/test/matchers.js +283 -0
- package/dist/test/public.d.ts +2 -2
- package/dist/test/public.d.ts.map +1 -1
- package/dist/test/public.js +2 -1
- package/dist/test/query.d.ts +87 -0
- package/dist/test/query.d.ts.map +1 -0
- package/dist/test/query.js +226 -0
- package/dist/test/scene.d.ts +106 -0
- package/dist/test/scene.d.ts.map +1 -0
- package/dist/test/scene.js +373 -0
- package/dist/test/{index.d.ts → story.d.ts} +13 -17
- package/dist/test/story.d.ts.map +1 -0
- package/dist/test/{index.js → story.js} +8 -36
- package/dist/test/vitest-setup.d.ts +19 -0
- package/dist/test/vitest-setup.d.ts.map +1 -0
- package/dist/test/vitest-setup.js +3 -0
- package/package.json +5 -5
- package/dist/test/index.d.ts.map +0 -1
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { Array, Effect, Function, Option, Predicate, pipe } from 'effect';
|
|
2
|
+
import { dual } from 'effect/Function';
|
|
3
|
+
import { Dispatch } from '../runtime';
|
|
4
|
+
import { assertAllCommandsResolved, assertNoUnresolvedCommands, resolveByName, } from './internal';
|
|
5
|
+
import { attr, resolveTarget, textContent } from './query';
|
|
6
|
+
export { find, findAll, textContent, attr, getByRole, getAllByRole, getByText, getByPlaceholder, getByLabel, role, placeholder, label, selector, text, within, } from './query';
|
|
7
|
+
export { sceneMatchers } from './matchers';
|
|
8
|
+
const UNINITIALIZED = Symbol('uninitialized');
|
|
9
|
+
const toInternal = (simulation) =>
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
11
|
+
simulation;
|
|
12
|
+
// CAPTURING DISPATCH
|
|
13
|
+
const createCapturingDispatch = () => {
|
|
14
|
+
let capturedMessage;
|
|
15
|
+
return {
|
|
16
|
+
dispatch: Dispatch.of({
|
|
17
|
+
dispatchAsync: () => Effect.void,
|
|
18
|
+
dispatchSync: (dispatchedMessage) => {
|
|
19
|
+
capturedMessage = dispatchedMessage;
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
getCapturedMessage: () => capturedMessage,
|
|
23
|
+
reset: () => {
|
|
24
|
+
capturedMessage = undefined;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
// RENDERING
|
|
29
|
+
const renderView = (viewFn, model, dispatch) => {
|
|
30
|
+
const maybeVNode = Effect.runSync(Effect.provideService(viewFn(model), Dispatch, dispatch));
|
|
31
|
+
if (Predicate.isNull(maybeVNode)) {
|
|
32
|
+
throw new Error('The view function returned null.\n\n' +
|
|
33
|
+
'Scene tests require a non-null view. ' +
|
|
34
|
+
'If you need to test null-view states, use Story.story instead.');
|
|
35
|
+
}
|
|
36
|
+
return maybeVNode;
|
|
37
|
+
};
|
|
38
|
+
// INTERACTION HELPERS
|
|
39
|
+
const EVENT_NAMES = {
|
|
40
|
+
click: 'OnClick',
|
|
41
|
+
submit: 'OnSubmit',
|
|
42
|
+
input: 'OnInput',
|
|
43
|
+
keydown: 'OnKeyDown or OnKeyDownPreventDefault',
|
|
44
|
+
};
|
|
45
|
+
const invokeAndCapture = (simulation, target, eventName, invokeHandler) => {
|
|
46
|
+
const internal = toInternal(simulation);
|
|
47
|
+
const { maybeElement, description } = resolveTarget(internal.html, target);
|
|
48
|
+
if (Option.isNone(maybeElement)) {
|
|
49
|
+
throw new Error(`I could not find an element matching ${description}.\n\n` +
|
|
50
|
+
'Check that your selector matches an element in the current view.');
|
|
51
|
+
}
|
|
52
|
+
const element = maybeElement.value;
|
|
53
|
+
const maybeHandler = Option.fromNullable(element.data?.on?.[eventName]);
|
|
54
|
+
if (Option.isNone(maybeHandler)) {
|
|
55
|
+
const attributeName = EVENT_NAMES[eventName] ?? eventName;
|
|
56
|
+
throw new Error(`I found an element matching ${description} but it has no ${eventName} handler.\n\n` +
|
|
57
|
+
`Make sure the element has an ${attributeName} attribute.`);
|
|
58
|
+
}
|
|
59
|
+
internal.capturingDispatch.reset();
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
61
|
+
invokeHandler(maybeHandler.value);
|
|
62
|
+
const captured = internal.capturingDispatch.getCapturedMessage();
|
|
63
|
+
if (Predicate.isUndefined(captured)) {
|
|
64
|
+
return simulation;
|
|
65
|
+
}
|
|
66
|
+
assertNoUnresolvedCommands(internal.commands, 'when an interaction dispatched a new Message');
|
|
67
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
68
|
+
const capturedMessage = captured;
|
|
69
|
+
const result = internal.updateFn(internal.model, capturedMessage);
|
|
70
|
+
const [nextModel, commands] = result;
|
|
71
|
+
const outMessage = result.length === 3 ? result[2] : internal.outMessage;
|
|
72
|
+
return {
|
|
73
|
+
...internal,
|
|
74
|
+
model: nextModel,
|
|
75
|
+
message: capturedMessage,
|
|
76
|
+
commands: Array.appendAll(internal.commands, commands),
|
|
77
|
+
outMessage,
|
|
78
|
+
};
|
|
79
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
80
|
+
};
|
|
81
|
+
const DEFAULT_KEYBOARD_MODIFIERS = {
|
|
82
|
+
shiftKey: false,
|
|
83
|
+
ctrlKey: false,
|
|
84
|
+
altKey: false,
|
|
85
|
+
metaKey: false,
|
|
86
|
+
};
|
|
87
|
+
// STEPS
|
|
88
|
+
/** Sets the initial Model for a scene test. */
|
|
89
|
+
export { with_ as with };
|
|
90
|
+
const with_ = (model) => {
|
|
91
|
+
const step = (simulation) => {
|
|
92
|
+
const internal = toInternal(simulation);
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
94
|
+
return { ...internal, model };
|
|
95
|
+
};
|
|
96
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
97
|
+
return Object.assign(step, {
|
|
98
|
+
_phantomModel: undefined,
|
|
99
|
+
});
|
|
100
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
101
|
+
};
|
|
102
|
+
/** Resolves a specific pending Command with the given result Message. */
|
|
103
|
+
export const resolve = (definition, resultMessage, toParentMessage) => (simulation) => {
|
|
104
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
105
|
+
const internal = toInternal(simulation);
|
|
106
|
+
const messageForUpdate = (Predicate.isUndefined(toParentMessage)
|
|
107
|
+
? resultMessage
|
|
108
|
+
: toParentMessage(resultMessage));
|
|
109
|
+
const next = resolveByName(internal, definition.name, messageForUpdate);
|
|
110
|
+
if (Predicate.isUndefined(next)) {
|
|
111
|
+
const pending = Array.isNonEmptyReadonlyArray(internal.commands)
|
|
112
|
+
? pipe(internal.commands, Array.map(({ name }) => ` ${name}`), Array.join('\n'))
|
|
113
|
+
: ' (none)';
|
|
114
|
+
throw new Error(`I tried to resolve "${definition.name}" but it wasn't in the pending Commands.\n\n` +
|
|
115
|
+
`Pending Commands:\n${pending}\n\n` +
|
|
116
|
+
'Make sure the previous Message produced this Command.');
|
|
117
|
+
}
|
|
118
|
+
return next;
|
|
119
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
120
|
+
};
|
|
121
|
+
/** Resolves all listed Commands with their result Messages. Handles cascading resolution. */
|
|
122
|
+
export const resolveAll = (pairs) => (simulation) => {
|
|
123
|
+
const internal = toInternal(simulation);
|
|
124
|
+
const resolvers = {};
|
|
125
|
+
for (const [definition, resultMessage] of pairs) {
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
127
|
+
resolvers[definition.name] = resultMessage;
|
|
128
|
+
}
|
|
129
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
130
|
+
let current = {
|
|
131
|
+
...internal,
|
|
132
|
+
resolvers: { ...internal.resolvers, ...resolvers },
|
|
133
|
+
};
|
|
134
|
+
const MAX_CASCADE_DEPTH = 100;
|
|
135
|
+
for (let depth = 0; depth < MAX_CASCADE_DEPTH; depth++) {
|
|
136
|
+
const resolvable = current.commands.find(({ name }) => name in current.resolvers);
|
|
137
|
+
if (Predicate.isUndefined(resolvable)) {
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
const next = resolveByName(current, resolvable.name, current.resolvers[resolvable.name]);
|
|
141
|
+
if (Predicate.isUndefined(next)) {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
current = next;
|
|
145
|
+
if (depth === MAX_CASCADE_DEPTH - 1) {
|
|
146
|
+
throw new Error('resolveAll hit the maximum cascade depth (100). ' +
|
|
147
|
+
'This usually means Commands are producing Commands in an infinite cycle.');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return current;
|
|
151
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
152
|
+
};
|
|
153
|
+
/** Runs a function for side effects (e.g. assertions) without breaking the step chain. */
|
|
154
|
+
export const tap = (f) => (simulation) => {
|
|
155
|
+
f(simulation);
|
|
156
|
+
return simulation;
|
|
157
|
+
};
|
|
158
|
+
// INTERACTION STEPS
|
|
159
|
+
/** Simulates a click on the element matching the target. */
|
|
160
|
+
export const click = (target) => (simulation) => invokeAndCapture(simulation, target, 'click', handler => {
|
|
161
|
+
handler();
|
|
162
|
+
});
|
|
163
|
+
/** Simulates form submission on the element matching the target. */
|
|
164
|
+
export const submit = (target) => (simulation) => invokeAndCapture(simulation, target, 'submit', handler => {
|
|
165
|
+
handler({ preventDefault: Function.constVoid });
|
|
166
|
+
});
|
|
167
|
+
/** Simulates typing a value into the input matching the target.
|
|
168
|
+
* Dual: `type(target, value)` or `type(value)` for data-last piping. */
|
|
169
|
+
export { type_ as type };
|
|
170
|
+
const type_ = dual(2, (target, value) => (simulation) => invokeAndCapture(simulation, target, 'input', handler => {
|
|
171
|
+
handler({ target: { value } });
|
|
172
|
+
}));
|
|
173
|
+
/** Simulates a keydown event on the element matching the target.
|
|
174
|
+
* Dual: `keydown(target, key, modifiers?)` or `keydown(key, modifiers?)` for data-last piping. */
|
|
175
|
+
export const keydown = dual((args) => args.length >= 2 && typeof args[1] === 'string', (target, key, modifiers) => (simulation) => invokeAndCapture(simulation, target, 'keydown', handler => {
|
|
176
|
+
handler({
|
|
177
|
+
key,
|
|
178
|
+
...DEFAULT_KEYBOARD_MODIFIERS,
|
|
179
|
+
...modifiers,
|
|
180
|
+
preventDefault: Function.constVoid,
|
|
181
|
+
});
|
|
182
|
+
}));
|
|
183
|
+
const wrapAssertion = (locator, assertion, isNot) => (simulation) => {
|
|
184
|
+
const internal = toInternal(simulation);
|
|
185
|
+
assertion(locator(internal.html), locator.description, isNot);
|
|
186
|
+
return simulation;
|
|
187
|
+
};
|
|
188
|
+
const assertOnElement = (check, expectation) => (maybeElement, description, isNot) => {
|
|
189
|
+
if (Option.isNone(maybeElement)) {
|
|
190
|
+
if (!isNot) {
|
|
191
|
+
throw new Error(`Expected element matching ${description} to ${expectation} but the element does not exist.`);
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const { pass, actual } = check(maybeElement.value);
|
|
196
|
+
if (isNot ? pass : !pass) {
|
|
197
|
+
throw new Error(isNot
|
|
198
|
+
? `Expected element matching ${description} not to ${expectation} but it does.`
|
|
199
|
+
: `Expected element matching ${description} to ${expectation} but ${actual}.`);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const assertExists = (maybeElement, description, isNot) => {
|
|
203
|
+
const exists = Option.isSome(maybeElement);
|
|
204
|
+
if (isNot ? exists : !exists) {
|
|
205
|
+
throw new Error(isNot
|
|
206
|
+
? `Expected element matching ${description} not to exist but it does.`
|
|
207
|
+
: `Expected element matching ${description} to exist but it does not.`);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const assertAbsent = (maybeElement, description, isNot) => {
|
|
211
|
+
const absent = Option.isNone(maybeElement);
|
|
212
|
+
if (isNot ? absent : !absent) {
|
|
213
|
+
throw new Error(isNot
|
|
214
|
+
? `Expected element matching ${description} not to be absent but it is.`
|
|
215
|
+
: `Expected element matching ${description} to be absent but it exists.`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const assertHasText = (expected) => assertOnElement(vnode => ({
|
|
219
|
+
pass: textContent(vnode) === expected,
|
|
220
|
+
actual: `received "${textContent(vnode)}"`,
|
|
221
|
+
}), `have text "${expected}"`);
|
|
222
|
+
const assertContainsText = (expected) => assertOnElement(vnode => ({
|
|
223
|
+
pass: textContent(vnode).includes(expected),
|
|
224
|
+
actual: `received "${textContent(vnode)}"`,
|
|
225
|
+
}), `contain text "${expected}"`);
|
|
226
|
+
const assertHasAttr = (name, value) => assertOnElement(vnode => {
|
|
227
|
+
const actualValue = attr(vnode, name);
|
|
228
|
+
if (Predicate.isUndefined(value)) {
|
|
229
|
+
return {
|
|
230
|
+
pass: Option.isSome(actualValue),
|
|
231
|
+
actual: 'the attribute is not present',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return Option.match(actualValue, {
|
|
235
|
+
onNone: () => ({
|
|
236
|
+
pass: false,
|
|
237
|
+
actual: 'the attribute is not present',
|
|
238
|
+
}),
|
|
239
|
+
onSome: actual => ({
|
|
240
|
+
pass: actual === value,
|
|
241
|
+
actual: `received "${actual}"`,
|
|
242
|
+
}),
|
|
243
|
+
});
|
|
244
|
+
}, Predicate.isUndefined(value)
|
|
245
|
+
? `have attribute "${name}"`
|
|
246
|
+
: `have attribute ${name}="${value}"`);
|
|
247
|
+
const assertHasClass = (expected) => assertOnElement(vnode => ({
|
|
248
|
+
pass: vnode.data?.class?.[expected] === true,
|
|
249
|
+
actual: 'it does not',
|
|
250
|
+
}), `have class "${expected}"`);
|
|
251
|
+
const assertHasStyle = (name, value) => assertOnElement(vnode => {
|
|
252
|
+
const maybeActualValue = Option.fromNullable(vnode.data?.style?.[name]);
|
|
253
|
+
if (Predicate.isUndefined(value)) {
|
|
254
|
+
return {
|
|
255
|
+
pass: Option.isSome(maybeActualValue),
|
|
256
|
+
actual: 'it is not present',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return Option.match(maybeActualValue, {
|
|
260
|
+
onNone: () => ({ pass: false, actual: 'it is not present' }),
|
|
261
|
+
onSome: actualValue => ({
|
|
262
|
+
pass: String(actualValue) === value,
|
|
263
|
+
actual: `received "${actualValue}"`,
|
|
264
|
+
}),
|
|
265
|
+
});
|
|
266
|
+
}, Predicate.isUndefined(value)
|
|
267
|
+
? `have style "${name}"`
|
|
268
|
+
: `have style ${name}="${value}"`);
|
|
269
|
+
const assertHasHook = (name) => assertOnElement(vnode => {
|
|
270
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
271
|
+
const hooks = vnode.data?.hook;
|
|
272
|
+
return {
|
|
273
|
+
pass: typeof hooks?.[name] === 'function',
|
|
274
|
+
actual: 'it is not present',
|
|
275
|
+
};
|
|
276
|
+
}, `have hook "${name}"`);
|
|
277
|
+
const assertHasHandler = (name) => assertOnElement(vnode => ({
|
|
278
|
+
pass: vnode.data?.on?.[name] !== undefined,
|
|
279
|
+
actual: 'it is not present',
|
|
280
|
+
}), `have handler "${name}"`);
|
|
281
|
+
const assertHasValue = (expected) => assertOnElement(vnode => {
|
|
282
|
+
const actualValue = attr(vnode, 'value');
|
|
283
|
+
return Option.match(actualValue, {
|
|
284
|
+
onNone: () => ({
|
|
285
|
+
pass: false,
|
|
286
|
+
actual: 'the element has no value',
|
|
287
|
+
}),
|
|
288
|
+
onSome: actual => ({
|
|
289
|
+
pass: actual === expected,
|
|
290
|
+
actual: `received "${actual}"`,
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
}, `have value "${expected}"`);
|
|
294
|
+
const isDisabled = (vnode) => {
|
|
295
|
+
const disabled = attr(vnode, 'disabled');
|
|
296
|
+
if (Option.isSome(disabled) && disabled.value !== 'false') {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
const ariaDisabled = attr(vnode, 'aria-disabled');
|
|
300
|
+
return Option.isSome(ariaDisabled) && ariaDisabled.value === 'true';
|
|
301
|
+
};
|
|
302
|
+
const assertIsDisabled = assertOnElement(vnode => ({
|
|
303
|
+
pass: isDisabled(vnode),
|
|
304
|
+
actual: 'it is not disabled',
|
|
305
|
+
}), 'be disabled');
|
|
306
|
+
const assertIsEnabled = assertOnElement(vnode => ({
|
|
307
|
+
pass: !isDisabled(vnode),
|
|
308
|
+
actual: 'it is disabled',
|
|
309
|
+
}), 'be enabled');
|
|
310
|
+
const assertIsChecked = assertOnElement(vnode => {
|
|
311
|
+
const checked = attr(vnode, 'checked');
|
|
312
|
+
const ariaChecked = attr(vnode, 'aria-checked');
|
|
313
|
+
const pass = (Option.isSome(checked) && checked.value !== 'false') ||
|
|
314
|
+
(Option.isSome(ariaChecked) && ariaChecked.value === 'true');
|
|
315
|
+
return { pass, actual: 'it is not checked' };
|
|
316
|
+
}, 'be checked');
|
|
317
|
+
const buildExpectChain = (locator, isNot) => ({
|
|
318
|
+
toExist: () => wrapAssertion(locator, assertExists, isNot),
|
|
319
|
+
toBeAbsent: () => wrapAssertion(locator, assertAbsent, isNot),
|
|
320
|
+
toHaveText: (expected) => wrapAssertion(locator, assertHasText(expected), isNot),
|
|
321
|
+
toContainText: (expected) => wrapAssertion(locator, assertContainsText(expected), isNot),
|
|
322
|
+
toHaveAttr: (name, value) => wrapAssertion(locator, assertHasAttr(name, value), isNot),
|
|
323
|
+
toHaveClass: (expected) => wrapAssertion(locator, assertHasClass(expected), isNot),
|
|
324
|
+
toHaveStyle: (name, value) => wrapAssertion(locator, assertHasStyle(name, value), isNot),
|
|
325
|
+
toHaveHook: (name) => wrapAssertion(locator, assertHasHook(name), isNot),
|
|
326
|
+
toHaveHandler: (name) => wrapAssertion(locator, assertHasHandler(name), isNot),
|
|
327
|
+
toHaveValue: (expected) => wrapAssertion(locator, assertHasValue(expected), isNot),
|
|
328
|
+
toBeDisabled: () => wrapAssertion(locator, assertIsDisabled, isNot),
|
|
329
|
+
toBeEnabled: () => wrapAssertion(locator, assertIsEnabled, isNot),
|
|
330
|
+
toBeChecked: () => wrapAssertion(locator, assertIsChecked, isNot),
|
|
331
|
+
});
|
|
332
|
+
/** Creates an inline assertion step. Resolves the Locator against
|
|
333
|
+
* the current view and asserts on the result. */
|
|
334
|
+
export { expect_ as expect };
|
|
335
|
+
const expect_ = (locator) => ({
|
|
336
|
+
...buildExpectChain(locator, false),
|
|
337
|
+
not: buildExpectChain(locator, true),
|
|
338
|
+
});
|
|
339
|
+
// SUBMODEL VIEW ADAPTER
|
|
340
|
+
/** Adapts a submodel view for Scene testing. In the Submodel pattern, the view
|
|
341
|
+
* takes a `toParentMessage` function that maps child Messages to parent Messages.
|
|
342
|
+
* Scene tests the child in isolation, so `childView` passes the identity function
|
|
343
|
+
* and erases the parent type. */
|
|
344
|
+
export const childView = (viewFn) => model => viewFn(model, message => message);
|
|
345
|
+
// SCENE
|
|
346
|
+
/** Executes a scene test. Throws if any Commands remain unresolved. */
|
|
347
|
+
export const scene = (config, ...steps) => {
|
|
348
|
+
const capturingDispatch = createCapturingDispatch();
|
|
349
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
350
|
+
const seed = {
|
|
351
|
+
model: UNINITIALIZED,
|
|
352
|
+
message: undefined,
|
|
353
|
+
commands: [],
|
|
354
|
+
outMessage: undefined,
|
|
355
|
+
updateFn: config.update,
|
|
356
|
+
resolvers: {},
|
|
357
|
+
html: undefined,
|
|
358
|
+
viewFn: config.view,
|
|
359
|
+
capturingDispatch,
|
|
360
|
+
};
|
|
361
|
+
const result = steps.reduce((current, step) => {
|
|
362
|
+
const next = step(current);
|
|
363
|
+
const internal = toInternal(next);
|
|
364
|
+
if (internal.model !== UNINITIALIZED) {
|
|
365
|
+
const html = renderView(internal.viewFn, internal.model, internal.capturingDispatch.dispatch);
|
|
366
|
+
return { ...internal, html };
|
|
367
|
+
}
|
|
368
|
+
return next;
|
|
369
|
+
}, seed);
|
|
370
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
371
|
+
const internal = toInternal(result);
|
|
372
|
+
assertAllCommandsResolved(internal.commands);
|
|
373
|
+
};
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { CommandDefinition } from '../command';
|
|
2
|
-
|
|
3
|
-
export type AnyCommand
|
|
4
|
-
name: string;
|
|
5
|
-
}>;
|
|
2
|
+
import type { AnyCommand, ResolverPair } from './internal';
|
|
3
|
+
export type { AnyCommand, ResolverPair };
|
|
6
4
|
/** An immutable test simulation of a Foldkit program. */
|
|
7
|
-
export type
|
|
5
|
+
export type StorySimulation<Model, Message, OutMessage = undefined> = Readonly<{
|
|
8
6
|
model: Model;
|
|
9
7
|
message: Message | undefined;
|
|
10
8
|
commands: ReadonlyArray<AnyCommand>;
|
|
@@ -13,28 +11,26 @@ export type Simulation<Model, Message, OutMessage = undefined> = Readonly<{
|
|
|
13
11
|
/** A callable step that sets the initial Model. Carries phantom type for compile-time validation. */
|
|
14
12
|
export type WithStep<Model> = Readonly<{
|
|
15
13
|
_phantomModel: Model;
|
|
16
|
-
}> & (<M, Message, OutMessage = undefined>(simulation:
|
|
14
|
+
}> & (<M, Message, OutMessage = undefined>(simulation: StorySimulation<M, Message, OutMessage>) => StorySimulation<M, Message, OutMessage>);
|
|
15
|
+
/** A single step in a story — either a {@link WithStep} or a simulation transform. */
|
|
16
|
+
export type StoryStep<Model, Message, OutMessage> = WithStep<NoInfer<Model>> | ((simulation: StorySimulation<Model, Message, OutMessage>) => StorySimulation<Model, Message, OutMessage>);
|
|
17
17
|
/** Sets the initial Model for a test story. */
|
|
18
18
|
export { with_ as with };
|
|
19
19
|
declare const with_: <Model>(model: Model) => WithStep<Model>;
|
|
20
20
|
/** Sends a Message through update. Commands stay pending until resolve or resolveAll. */
|
|
21
|
-
export declare const message: <Message>(message_: NoInfer<Message>) => <Model, OutMessage = undefined>(simulation:
|
|
21
|
+
export declare const message: <Message>(message_: NoInfer<Message>) => <Model, OutMessage = undefined>(simulation: StorySimulation<Model, Message, OutMessage>) => StorySimulation<Model, Message, OutMessage>;
|
|
22
22
|
/** Resolves a specific pending Command with the given result Message. */
|
|
23
23
|
export declare const resolve: {
|
|
24
|
-
<Name extends string, ResultMessage>(definition: CommandDefinition<Name, ResultMessage>, resultMessage: ResultMessage): <Model, Message, OutMessage = undefined>(simulation:
|
|
25
|
-
<Name extends string, ResultMessage, ParentMessage>(definition: CommandDefinition<Name, ResultMessage>, resultMessage: ResultMessage, toParentMessage: (message: ResultMessage) => ParentMessage): <Model, Message, OutMessage = undefined>(simulation:
|
|
24
|
+
<Name extends string, ResultMessage>(definition: CommandDefinition<Name, ResultMessage>, resultMessage: ResultMessage): <Model, Message, OutMessage = undefined>(simulation: StorySimulation<Model, Message, OutMessage>) => StorySimulation<Model, Message, OutMessage>;
|
|
25
|
+
<Name extends string, ResultMessage, ParentMessage>(definition: CommandDefinition<Name, ResultMessage>, resultMessage: ResultMessage, toParentMessage: (message: ResultMessage) => ParentMessage): <Model, Message, OutMessage = undefined>(simulation: StorySimulation<Model, Message, OutMessage>) => StorySimulation<Model, Message, OutMessage>;
|
|
26
26
|
};
|
|
27
|
-
/** A Command definition paired with the result Message to resolve it with. */
|
|
28
|
-
export type ResolverPair<Name extends string = string, ResultMessage = unknown> = readonly [CommandDefinition<Name, ResultMessage>, ResultMessage];
|
|
29
27
|
/** Resolves all listed Commands with their result Messages. Handles cascading resolution. */
|
|
30
|
-
export declare const resolveAll: (pairs: ReadonlyArray<ResolverPair>) => <Model, Message, OutMessage = undefined>(simulation:
|
|
31
|
-
/** Runs a function for side effects (e.g. assertions) without breaking the
|
|
32
|
-
export declare const tap: <Model, Message, OutMessage = undefined>(f: (simulation:
|
|
33
|
-
/** A single step in a test story — either a {@link WithStep} or a simulation transform. */
|
|
34
|
-
export type StoryStep<Model, Message, OutMessage> = WithStep<NoInfer<Model>> | ((simulation: Simulation<Model, Message, OutMessage>) => Simulation<Model, Message, OutMessage>);
|
|
28
|
+
export declare const resolveAll: (pairs: ReadonlyArray<ResolverPair>) => <Model, Message, OutMessage = undefined>(simulation: StorySimulation<Model, Message, OutMessage>) => StorySimulation<Model, Message, OutMessage>;
|
|
29
|
+
/** Runs a function for side effects (e.g. assertions) without breaking the step chain. */
|
|
30
|
+
export declare const tap: <Model, Message, OutMessage = undefined>(f: (simulation: StorySimulation<Model, Message, OutMessage>) => void) => (simulation: StorySimulation<Model, Message, OutMessage>) => StorySimulation<Model, Message, OutMessage>;
|
|
35
31
|
/** Executes a test story. Throws if any Commands remain unresolved. */
|
|
36
32
|
export declare const story: {
|
|
37
33
|
<Model, Message, OutMessage>(updateFn: (model: Model, message: Message) => readonly [Model, ReadonlyArray<AnyCommand>, OutMessage], ...steps: ReadonlyArray<StoryStep<Model, Message, OutMessage>>): void;
|
|
38
34
|
<Model, Message>(updateFn: (model: Model, message: Message) => readonly [Model, ReadonlyArray<AnyCommand>], ...steps: ReadonlyArray<StoryStep<Model, Message, undefined>>): void;
|
|
39
35
|
};
|
|
40
|
-
//# sourceMappingURL=
|
|
36
|
+
//# sourceMappingURL=story.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story.d.ts","sourceRoot":"","sources":["../../src/test/story.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import { Array, Predicate, pipe } from 'effect';
|
|
2
|
+
import { assertAllCommandsResolved, assertNoUnresolvedCommands, resolveByName, } from './internal';
|
|
2
3
|
const toInternal = (simulation) =>
|
|
3
4
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
4
5
|
simulation;
|
|
5
|
-
|
|
6
|
-
const commandIndex = internal.commands.findIndex(({ name }) => name === commandName);
|
|
7
|
-
if (commandIndex === -1) {
|
|
8
|
-
return undefined;
|
|
9
|
-
}
|
|
10
|
-
const remainingCommands = Array.remove(internal.commands, commandIndex);
|
|
11
|
-
const result = internal.updateFn(internal.model, resolverMessage);
|
|
12
|
-
const nextModel = result[0];
|
|
13
|
-
const newCommands = result[1];
|
|
14
|
-
const outMessage = result.length === 3 ? result[2] : internal.outMessage;
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
16
|
-
return {
|
|
17
|
-
...internal,
|
|
18
|
-
model: nextModel,
|
|
19
|
-
message: resolverMessage,
|
|
20
|
-
commands: Array.appendAll(remainingCommands, newCommands),
|
|
21
|
-
outMessage,
|
|
22
|
-
};
|
|
23
|
-
};
|
|
6
|
+
// STEPS
|
|
24
7
|
/** Sets the initial Model for a test story. */
|
|
25
8
|
export { with_ as with };
|
|
26
9
|
const with_ = (model) => {
|
|
@@ -38,13 +21,7 @@ const with_ = (model) => {
|
|
|
38
21
|
/** Sends a Message through update. Commands stay pending until resolve or resolveAll. */
|
|
39
22
|
export const message = (message_) => (simulation) => {
|
|
40
23
|
const internal = toInternal(simulation);
|
|
41
|
-
|
|
42
|
-
const names = pipe(internal.commands, Array.map(({ name }) => ` ${name}`), Array.join('\n'));
|
|
43
|
-
throw new Error(`I found unresolved Commands when you sent a new Message:\n\n${names}\n\n` +
|
|
44
|
-
'Resolve all Commands before sending the next Message.\n' +
|
|
45
|
-
'Use Test.resolve(Definition, ResultMessage) for each one,\n' +
|
|
46
|
-
'or Test.resolveAll([...pairs]) to resolve them all at once.');
|
|
47
|
-
}
|
|
24
|
+
assertNoUnresolvedCommands(internal.commands, 'when you sent a new Message');
|
|
48
25
|
const result = internal.updateFn(internal.model, message_);
|
|
49
26
|
const nextModel = result[0];
|
|
50
27
|
const commands = result[1];
|
|
@@ -85,6 +62,7 @@ export const resolveAll = (pairs) => (simulation) => {
|
|
|
85
62
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
86
63
|
resolvers[definition.name] = resultMessage;
|
|
87
64
|
}
|
|
65
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
88
66
|
let current = {
|
|
89
67
|
...internal,
|
|
90
68
|
resolvers: { ...internal.resolvers, ...resolvers },
|
|
@@ -99,21 +77,21 @@ export const resolveAll = (pairs) => (simulation) => {
|
|
|
99
77
|
if (Predicate.isUndefined(next)) {
|
|
100
78
|
break;
|
|
101
79
|
}
|
|
102
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
103
80
|
current = next;
|
|
104
81
|
if (depth === MAX_CASCADE_DEPTH - 1) {
|
|
105
82
|
throw new Error('resolveAll hit the maximum cascade depth (100). ' +
|
|
106
83
|
'This usually means Commands are producing Commands in an infinite cycle.');
|
|
107
84
|
}
|
|
108
85
|
}
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
110
86
|
return current;
|
|
87
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
111
88
|
};
|
|
112
|
-
/** Runs a function for side effects (e.g. assertions) without breaking the
|
|
89
|
+
/** Runs a function for side effects (e.g. assertions) without breaking the step chain. */
|
|
113
90
|
export const tap = (f) => (simulation) => {
|
|
114
91
|
f(simulation);
|
|
115
92
|
return simulation;
|
|
116
93
|
};
|
|
94
|
+
// STORY
|
|
117
95
|
/** Executes a test story. Throws if any Commands remain unresolved. */
|
|
118
96
|
export const story = (updateFn, ...steps) => {
|
|
119
97
|
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
@@ -128,11 +106,5 @@ export const story = (updateFn, ...steps) => {
|
|
|
128
106
|
const result = steps.reduce((current, step) => step(current), seed);
|
|
129
107
|
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
130
108
|
const internal = toInternal(result);
|
|
131
|
-
|
|
132
|
-
const names = pipe(internal.commands, Array.map(({ name }) => ` ${name}`), Array.join('\n'));
|
|
133
|
-
throw new Error(`I found Commands without resolvers:\n\n${names}\n\n` +
|
|
134
|
-
'Every Command produced by update needs to be resolved.\n' +
|
|
135
|
-
'Use Test.resolve(Definition, ResultMessage) for each one,\n' +
|
|
136
|
-
'or Test.resolveAll([...pairs]) to resolve them all at once.');
|
|
137
|
-
}
|
|
109
|
+
assertAllCommandsResolved(internal.commands);
|
|
138
110
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare module 'vitest' {
|
|
2
|
+
interface Assertion<T> {
|
|
3
|
+
toHaveText(expected: string): this;
|
|
4
|
+
toContainText(expected: string): this;
|
|
5
|
+
toHaveClass(expected: string): this;
|
|
6
|
+
toHaveAttr(name: string, value?: string): this;
|
|
7
|
+
toHaveStyle(name: string, value?: string): this;
|
|
8
|
+
toHaveHook(name: string): this;
|
|
9
|
+
toHaveHandler(name: string): this;
|
|
10
|
+
toHaveValue(expected: string): this;
|
|
11
|
+
toBeDisabled(): this;
|
|
12
|
+
toBeEnabled(): this;
|
|
13
|
+
toBeChecked(): this;
|
|
14
|
+
toExist(): this;
|
|
15
|
+
toBeAbsent(): this;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=vitest-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest-setup.d.ts","sourceRoot":"","sources":["../../src/test/vitest-setup.ts"],"names":[],"mappings":"AAIA,OAAO,QAAQ,QAAQ,CAAC;IAEtB,UAAU,SAAS,CAAC,CAAC;QACnB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;QAClC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;QACrC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;QACnC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QAC9C,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;QAC9B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;QACjC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;QACnC,YAAY,IAAI,IAAI,CAAA;QACpB,WAAW,IAAI,IAAI,CAAA;QACnB,WAAW,IAAI,IAAI,CAAA;QACnB,OAAO,IAAI,IAAI,CAAA;QACf,UAAU,IAAI,IAAI,CAAA;KACnB;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foldkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "A frontend framework for TypeScript, built on Effect, using The Elm Architecture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -174,10 +174,10 @@
|
|
|
174
174
|
},
|
|
175
175
|
"scripts": {
|
|
176
176
|
"clean": "rimraf dist *.tsbuildinfo",
|
|
177
|
-
"build": "pnpm run clean && node scripts/inline-css.js && tsc -b",
|
|
178
|
-
"watch": "node scripts/inline-css.js && tsc -b --watch",
|
|
179
|
-
"lint": "eslint src
|
|
180
|
-
"typecheck": "node scripts/inline-css.js && tsc -b --noEmit",
|
|
177
|
+
"build": "pnpm run clean && node scripts/inline-css.js && tsc -b tsconfig.build.json",
|
|
178
|
+
"watch": "node scripts/inline-css.js && tsc -b tsconfig.build.json --watch",
|
|
179
|
+
"lint": "eslint src",
|
|
180
|
+
"typecheck": "node scripts/inline-css.js && tsc -b tsconfig.build.json --noEmit",
|
|
181
181
|
"test": "vitest run",
|
|
182
182
|
"docs": "typedoc"
|
|
183
183
|
}
|
package/dist/test/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"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"}
|