@voltro/testing 0.85.0 → 0.86.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.
@@ -0,0 +1,65 @@
1
+ import { fixtureRow as e, nextSequence as t } from "./fixtureRow.js";
2
+ import { missingRequiredColumns as n } from "@voltro/database";
3
+ //#region src/factory.ts
4
+ var r = (e) => {
5
+ let n = {};
6
+ for (let [r, i] of Object.entries(e)) n[r] = typeof i == "function" ? i(t()) : i;
7
+ return n;
8
+ }, i = (e) => {
9
+ for (let [t, n] of Object.entries(e.fields)) if (n.type === "id") return t;
10
+ return "id";
11
+ }, a = async (e, t, r, a, s) => {
12
+ let c = {};
13
+ for (let l of n(t, r)) {
14
+ let n = t.fields[l];
15
+ if (n === void 0 || n.type !== "reference" || n.references === void 0) continue;
16
+ let r = n.references();
17
+ if (s.includes(r.tableName)) throw Error(`defineFactory: creating '${t.tableName}' requires '${r.tableName}' via '${l}', and that reference is cyclic (${[...s, r.tableName].join(" → ")}). Insert one side first and pass the id: create(store, { ${l}: existing.id }).`);
18
+ let u = a[l];
19
+ c[l] = (u === void 0 ? await o(e, r, {}, {}, [...s, r.tableName]) : await u.create(e, {}))[n.referenceColumn ?? i(r)];
20
+ }
21
+ return c;
22
+ }, o = async (t, n, r, i, o) => {
23
+ let s = {
24
+ ...await a(t, n, r, i, o),
25
+ ...r
26
+ };
27
+ return t.insert(n.tableName, e(n, s));
28
+ }, s = (e, t) => typeof e == "function" ? e(t) : e ?? {}, c = (t, n = {}) => {
29
+ let i = n.traits ?? {}, a = n.associations ?? {}, c = (n) => {
30
+ let l = (e = {}) => ({
31
+ ...r(n),
32
+ ...e
33
+ }), u = (n = {}) => e(t, l(n));
34
+ return {
35
+ table: t,
36
+ build: u,
37
+ buildList: (e, t) => Array.from({ length: e }, (e, n) => u(s(t, n))),
38
+ create: (e, n = {}) => o(e, t, l(n), a, [t.tableName]),
39
+ createList: async (e, n, r) => {
40
+ let i = [];
41
+ for (let c = 0; c < n; c++) i.push(await o(e, t, l(s(r, c)), a, [t.tableName]));
42
+ return i;
43
+ },
44
+ with: (...e) => {
45
+ let r = n;
46
+ for (let n of e) {
47
+ let e = i[n];
48
+ if (e === void 0) throw Error(`defineFactory: '${t.tableName}' has no trait '${n}'. Declared: ${Object.keys(i).join(", ") || "(none)"}.`);
49
+ r = {
50
+ ...r,
51
+ ...e
52
+ };
53
+ }
54
+ return c(r);
55
+ },
56
+ extend: (e) => c({
57
+ ...n,
58
+ ...e
59
+ })
60
+ };
61
+ };
62
+ return c(n.defaults ?? {});
63
+ };
64
+ //#endregion
65
+ export { c as defineFactory };
@@ -0,0 +1,30 @@
1
+ import { AUTO_FILLED_COLUMNS as e, missingRequiredColumns as t } from "@voltro/database";
2
+ //#region src/fixtureRow.ts
3
+ var n = new Set(e), r = 0, i = () => ++r, a = (e, t) => {
4
+ if (t.oneOf && t.oneOf.length > 0) return t.oneOf[0];
5
+ if (t.enumValues && t.enumValues.length > 0) return t.enumValues[0];
6
+ let n = t.unique === !0;
7
+ switch (t.type) {
8
+ case "text":
9
+ case "id":
10
+ case "enum": return n ? `${e}-${++r}` : e;
11
+ case "reference": return t.referenceStorage === "numeric" ? n ? ++r : 0 : n ? `${e}-${++r}` : e;
12
+ case "integer":
13
+ case "real": return n ? ++r : 0;
14
+ case "decimal":
15
+ case "bigint": return n ? String(++r) : "0";
16
+ case "boolean": return !1;
17
+ case "timestamp":
18
+ case "date": return /* @__PURE__ */ new Date(0);
19
+ default: throw Error(`fixtureRow: column '${e}' is a required '${t.type}' with no default, and fixtureRow can't synthesize a safe placeholder for that type. Pass it explicitly: fixtureRow(table, { ${e}: … }).`);
20
+ }
21
+ }, o = (e, r = {}) => {
22
+ let i = {};
23
+ for (let o of t(e, r)) n.has(o) || (i[o] = a(o, e.fields[o]));
24
+ return {
25
+ ...i,
26
+ ...r
27
+ };
28
+ };
29
+ //#endregion
30
+ export { o as fixtureRow, i as nextSequence };
@@ -0,0 +1,46 @@
1
+ import { makeVoltroTestClient as e } from "./client.js";
2
+ import { act as t, createElement as n } from "react";
3
+ import { useFormBinding as r } from "@voltro/client";
4
+ //#region src/formBindingHarness.tsx
5
+ var i = async (i, a = {}) => {
6
+ if (globalThis.document === void 0) throw Error("renderFormBinding needs a DOM to render hooks — add `// @vitest-environment jsdom` to the spec.");
7
+ let { createRoot: o } = await import("react-dom/client"), s = a.apiName ?? "app", c = e({
8
+ apiName: s,
9
+ ...a.client ?? {},
10
+ mutations: {
11
+ [i]: a.mutation ?? (() => ({ ok: !0 })),
12
+ ...a.client?.mutations ?? {}
13
+ }
14
+ }), l, u = () => (l = r(s, i, a.binding ?? {}), null), d = o(globalThis.document.createElement("div"));
15
+ return await t(async () => {
16
+ d.render(n(c.Provider, null, n(u)));
17
+ }), {
18
+ binding: () => l,
19
+ fill: (e, n) => {
20
+ t(() => {
21
+ l.field(e).setValue(n);
22
+ });
23
+ },
24
+ blur: (e) => {
25
+ t(() => {
26
+ l.field(e).onBlur();
27
+ });
28
+ },
29
+ submit: async () => {
30
+ let e;
31
+ return await t(async () => {
32
+ e = await l.submit();
33
+ }), e;
34
+ },
35
+ errors: () => l.errors,
36
+ state: () => l.state,
37
+ client: c,
38
+ unmount: () => {
39
+ t(() => {
40
+ d.unmount();
41
+ });
42
+ }
43
+ };
44
+ };
45
+ //#endregion
46
+ export { i as renderFormBinding };
@@ -0,0 +1,22 @@
1
+ import { Result as e, Schema as t } from "effect";
2
+ //#region src/hydrateFixtureRows.ts
3
+ var n = t.decodeUnknownResult(t.DateFromString), r = /^\d{4}-\d{2}-\d{2}$/, i = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/, a = (t, a) => {
4
+ if (typeof t != "string" || !i.test(t) && !(a === "date" && r.test(t))) return !1;
5
+ let o = t.slice(0, 10), s = n(o);
6
+ return e.isSuccess(s) && Number.isFinite(s.success.getTime()) && s.success.toISOString().slice(0, 10) === o;
7
+ }, o = (t, r) => {
8
+ let i = Object.entries(t.fields).filter(([, e]) => e.type === "timestamp" || e.type === "date");
9
+ return r.map((r, o) => {
10
+ let s = { ...r };
11
+ for (let [c, l] of i) {
12
+ let i = r[c];
13
+ if (i == null || i instanceof Date && Number.isFinite(i.getTime())) continue;
14
+ let u = n(i);
15
+ if (!a(i, l.type) || e.isFailure(u) || !Number.isFinite(u.success.getTime())) throw TypeError(`hydrateFixtureRows: table '${t.tableName}', row ${o}, column '${c}' requires a valid Date or ISO date-time with Z or an offset${l.type === "date" ? " (YYYY-MM-DD is also accepted as UTC midnight)" : ""}.`);
16
+ s[c] = u.success;
17
+ }
18
+ return s;
19
+ });
20
+ };
21
+ //#endregion
22
+ export { o as hydrateFixtureRows };
package/dist/index.d.ts CHANGED
@@ -1094,13 +1094,13 @@ export declare interface TestResponse {
1094
1094
  readonly headers: Readonly<Record<string, string | ReadonlyArray<string>>>;
1095
1095
  /** The raw response body as text (empty string when there was none). */
1096
1096
  readonly text: string;
1097
- /** The body parsed as JSON when the route answered with a JSON content-type,
1098
- * otherwise `undefined`. Every framework REST response is JSON; a streaming
1099
- * (SSE) route answers with neither — see `stream`. */
1097
+ /** Byte-exact buffered body, including a consumed finite byte stream. SSE remains unconsumed. */
1098
+ readonly bytes?: Uint8Array;
1099
+ /** Parsed JSON when the response has a JSON content-type; otherwise
1100
+ * `undefined`. SSE responses remain unconsumed. */
1100
1101
  readonly body: unknown;
1101
- /** `true` for a streaming (SSE) route: the response has no buffered body, so
1102
- * `text` is empty and `body` is `undefined`. Stated rather than silently
1103
- * looking like an empty 200. */
1102
+ /** True for SSE and byte streams. Finite byte streams populate `bytes` and
1103
+ * `text`; SSE remains unconsumed, with empty text and undefined body. */
1104
1104
  readonly stream: boolean;
1105
1105
  }
1106
1106