@omriashke/dynamico-validator 0.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.
Files changed (47) hide show
  1. package/LICENSE +184 -0
  2. package/dist/events.d.ts +10 -0
  3. package/dist/events.d.ts.map +1 -0
  4. package/dist/events.js +35 -0
  5. package/dist/events.js.map +1 -0
  6. package/dist/expect.d.ts +18 -0
  7. package/dist/expect.d.ts.map +1 -0
  8. package/dist/expect.js +69 -0
  9. package/dist/expect.js.map +1 -0
  10. package/dist/index.d.ts +24 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +24 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/mocks/react-native.d.ts +385 -0
  15. package/dist/mocks/react-native.d.ts.map +1 -0
  16. package/dist/mocks/react-native.js +181 -0
  17. package/dist/mocks/react-native.js.map +1 -0
  18. package/dist/mocks/safe-area-context.d.ts +50 -0
  19. package/dist/mocks/safe-area-context.d.ts.map +1 -0
  20. package/dist/mocks/safe-area-context.js +13 -0
  21. package/dist/mocks/safe-area-context.js.map +1 -0
  22. package/dist/queries.d.ts +16 -0
  23. package/dist/queries.d.ts.map +1 -0
  24. package/dist/queries.js +65 -0
  25. package/dist/queries.js.map +1 -0
  26. package/dist/render.d.ts +39 -0
  27. package/dist/render.d.ts.map +1 -0
  28. package/dist/render.js +34 -0
  29. package/dist/render.js.map +1 -0
  30. package/dist/runTest.d.ts +48 -0
  31. package/dist/runTest.d.ts.map +1 -0
  32. package/dist/runTest.js +339 -0
  33. package/dist/runTest.js.map +1 -0
  34. package/dist/timing.d.ts +7 -0
  35. package/dist/timing.d.ts.map +1 -0
  36. package/dist/timing.js +14 -0
  37. package/dist/timing.js.map +1 -0
  38. package/package.json +53 -0
  39. package/src/events.ts +38 -0
  40. package/src/expect.ts +70 -0
  41. package/src/index.ts +23 -0
  42. package/src/mocks/react-native.ts +203 -0
  43. package/src/mocks/safe-area-context.ts +15 -0
  44. package/src/queries.ts +66 -0
  45. package/src/render.ts +64 -0
  46. package/src/runTest.ts +402 -0
  47. package/src/timing.ts +15 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Find the first node whose direct text content matches `matcher`. Walks the
3
+ * tree and looks for Text nodes whose children stringify to the matcher.
4
+ *
5
+ * Mirrors the spirit of testing-library's getByText without pulling the
6
+ * whole library in.
7
+ */
8
+ export function findByText(root, matcher) {
9
+ const node = queryByText(root, matcher);
10
+ if (!node) {
11
+ throw new Error(`findByText(): no node found matching ${matcher instanceof RegExp ? matcher.toString() : JSON.stringify(matcher)}`);
12
+ }
13
+ return node;
14
+ }
15
+ export function queryByText(root, matcher) {
16
+ const matches = (haystack) => typeof matcher === "string" ? haystack === matcher : matcher.test(haystack);
17
+ const visit = (node) => {
18
+ const typeName = typeof node.type === "string" ? node.type : node.type.displayName ?? "";
19
+ if (typeName === "Text") {
20
+ const direct = textOf(node.props?.children);
21
+ if (matches(direct))
22
+ return node;
23
+ }
24
+ for (const child of node.children ?? []) {
25
+ if (typeof child === "string") {
26
+ if (typeName === "Text" && matches(child))
27
+ return node;
28
+ continue;
29
+ }
30
+ const found = visit(child);
31
+ if (found)
32
+ return found;
33
+ }
34
+ return null;
35
+ };
36
+ return visit(root);
37
+ }
38
+ /**
39
+ * Find all rendered nodes whose displayName/type matches the given name.
40
+ * E.g. findAllByType(root, 'Text') returns every Text node.
41
+ */
42
+ export function findAllByType(root, typeName) {
43
+ const out = [];
44
+ const visit = (node) => {
45
+ const t = typeof node.type === "string" ? node.type : node.type.displayName ?? "";
46
+ if (t === typeName)
47
+ out.push(node);
48
+ for (const child of node.children ?? []) {
49
+ if (typeof child !== "string")
50
+ visit(child);
51
+ }
52
+ };
53
+ visit(root);
54
+ return out;
55
+ }
56
+ function textOf(value) {
57
+ if (value == null)
58
+ return "";
59
+ if (typeof value === "string" || typeof value === "number")
60
+ return String(value);
61
+ if (Array.isArray(value))
62
+ return value.map(textOf).join("");
63
+ return "";
64
+ }
65
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import * as React from "react";
2
+ import type { ReactTestInstance, ReactTestRenderer } from "react-test-renderer";
3
+ export interface RenderOptions {
4
+ /**
5
+ * Override or extend the auto-stubbed scope for this render. Keys are bare
6
+ * specifiers that the component imports (e.g. '@newscast/app-hooks'); values
7
+ * are the modules the component will receive when it require()s that key.
8
+ *
9
+ * The runner already auto-stubs everything the component imports with empty
10
+ * objects; use this to set specific return values (e.g. simulate
11
+ * `useAuth() === { isAuthenticated: true }`).
12
+ */
13
+ scope?: Record<string, unknown>;
14
+ }
15
+ export interface RenderResult {
16
+ root: ReactTestInstance;
17
+ renderer: ReactTestRenderer;
18
+ /** Re-render with new props. */
19
+ update: (next: React.ReactElement) => void;
20
+ /** Tear down. Tests don't usually need to call this; the runner does it. */
21
+ unmount: () => void;
22
+ /**
23
+ * Convenience: get the rendered tree as a serializable JSON snapshot. Useful
24
+ * when a test wants to assert on overall shape rather than poking specific
25
+ * nodes.
26
+ */
27
+ toJSON: () => unknown;
28
+ }
29
+ /**
30
+ * Mount a component using react-test-renderer with `act()` semantics.
31
+ *
32
+ * `RenderOptions.scope` is currently a hint surface only — the actual scope
33
+ * is wired up by the runner before render() is called (see runTest.ts), so
34
+ * passing scope here when calling render() directly in a test file is a no-op
35
+ * unless the runner is honoring it. The runner DOES merge scope from the
36
+ * second arg, so use it to control what hooks / modules the component sees.
37
+ */
38
+ export declare function render(element: React.ReactElement, _opts?: RenderOptions): RenderResult;
39
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC5B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,gCAAgC;IAChC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,KAAK,IAAI,CAAC;IAC3C,4EAA4E;IAC5E,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;OAIG;IACH,MAAM,EAAE,MAAM,OAAO,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,GAAE,aAAkB,GAAG,YAAY,CAsB3F"}
package/dist/render.js ADDED
@@ -0,0 +1,34 @@
1
+ import TestRenderer from "react-test-renderer";
2
+ /**
3
+ * Mount a component using react-test-renderer with `act()` semantics.
4
+ *
5
+ * `RenderOptions.scope` is currently a hint surface only — the actual scope
6
+ * is wired up by the runner before render() is called (see runTest.ts), so
7
+ * passing scope here when calling render() directly in a test file is a no-op
8
+ * unless the runner is honoring it. The runner DOES merge scope from the
9
+ * second arg, so use it to control what hooks / modules the component sees.
10
+ */
11
+ export function render(element, _opts = {}) {
12
+ let renderer;
13
+ TestRenderer.act(() => {
14
+ renderer = TestRenderer.create(element);
15
+ });
16
+ return {
17
+ root: renderer.root,
18
+ renderer,
19
+ update(next) {
20
+ TestRenderer.act(() => {
21
+ renderer.update(next);
22
+ });
23
+ },
24
+ unmount() {
25
+ TestRenderer.act(() => {
26
+ renderer.unmount();
27
+ });
28
+ },
29
+ toJSON() {
30
+ return renderer.toJSON();
31
+ },
32
+ };
33
+ }
34
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,qBAAqB,CAAC;AA+B/C;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CAAC,OAA2B,EAAE,QAAuB,EAAE;IAC3E,IAAI,QAA4B,CAAC;IACjC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE;QACpB,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ;QACR,MAAM,CAAC,IAAwB;YAC7B,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE;gBACpB,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO;YACL,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE;gBACpB,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM;YACJ,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { type Scope } from "@omriashke/dynamico-core";
2
+ export interface RunTestInput {
3
+ /** Component name (e.g. "HomeScreen"). Only used in error messages. */
4
+ name: string;
5
+ /** Compiled CommonJS code for the component itself. */
6
+ componentCode: string;
7
+ /** Compiled CommonJS code for the .test.tsx file. */
8
+ testCode: string;
9
+ /**
10
+ * Optional explicit scope provided by the registry. The runner merges this
11
+ * over its built-in defaults (react, react-native, safe-area-context).
12
+ * Pass anything the registry knows the host will provide; everything else
13
+ * is auto-stubbed with an empty object.
14
+ */
15
+ hostScope?: Scope;
16
+ /**
17
+ * Optional whitelist of bare specifiers the production host will provide
18
+ * via DynamicoProvider scope. When set, any component import that resolves
19
+ * to a specifier OUTSIDE this list causes the test to fail with phase
20
+ * "scope" — mirroring the runtime error the user would see on device.
21
+ * Relative imports (./, ../) are not checked.
22
+ *
23
+ * Pass the keys of the host's scope object. If undefined, the test runner
24
+ * auto-stubs unknown modules (permissive — useful for v1 onboarding).
25
+ */
26
+ allowedScope?: readonly string[];
27
+ /**
28
+ * Maximum wall-clock time the test may take, in ms. Default 5000. The
29
+ * registry's worker enforces this by terminating the worker on timeout;
30
+ * this field exists so authors can opt INTO faster timeouts, not slower.
31
+ */
32
+ timeoutMs?: number;
33
+ }
34
+ export interface RunTestResult {
35
+ ok: boolean;
36
+ durationMs: number;
37
+ /** When ok=false, the message thrown by the test (or load failure). */
38
+ error?: {
39
+ message: string;
40
+ stack?: string;
41
+ /** Which phase failed: 'load' (require), 'test' (test threw), or 'no-default-export'. */
42
+ phase: "load" | "scope" | "test" | "no-default-export" | "no-test-export";
43
+ };
44
+ }
45
+ export declare function setHostScope(scope: Scope): void;
46
+ export declare function getHostScope(): Scope;
47
+ export declare function runTest(input: RunTestInput): Promise<RunTestResult>;
48
+ //# sourceMappingURL=runTest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTest.d.ts","sourceRoot":"","sources":["../src/runTest.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAIlE,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,yFAAyF;QACzF,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;KAC3E,CAAC;CACH;AAgBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAE/C;AACD,wBAAgB,YAAY,IAAI,KAAK,CAEpC;AAmOD,wBAAsB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAyGzE"}
@@ -0,0 +1,339 @@
1
+ import * as React from "react";
2
+ import { loadModule } from "@omriashke/dynamico-core";
3
+ import * as RNMock from "./mocks/react-native.js";
4
+ import * as SafeAreaMock from "./mocks/safe-area-context.js";
5
+ const BUILT_IN_SCOPE = {
6
+ react: React,
7
+ "react-native": RNMock,
8
+ "react-native-safe-area-context": SafeAreaMock,
9
+ };
10
+ /**
11
+ * Test-time scope override. Tests can call setHostScope({...}) at the top of
12
+ * their default async function to provide real return shapes for hooks that
13
+ * the auto-stub can't fake convincingly.
14
+ *
15
+ * Resets between tests because each test runs in a fresh worker thread.
16
+ */
17
+ let mutableHostScope = {};
18
+ export function setHostScope(scope) {
19
+ mutableHostScope = { ...mutableHostScope, ...scope };
20
+ }
21
+ export function getHostScope() {
22
+ return mutableHostScope;
23
+ }
24
+ /**
25
+ * Auto-stub policy: if a component require()s a key that isn't in BUILT_IN_SCOPE
26
+ * and isn't in the registry-supplied hostScope, return an empty object. This
27
+ * keeps tests authoring lightweight: a component that imports
28
+ * `@newscast/app-hooks` and only ever calls `useFeed()` from it will get
29
+ * `useFeed === undefined`, which the test author surfaces explicitly when they
30
+ * pass a real stub via hostScope.
31
+ *
32
+ * The empty-object stub is wrapped in a Proxy so that `pkg.someThing` returns
33
+ * an empty function (not undefined), preventing "X is not a function" errors
34
+ * for callable shapes the test doesn't care about. This is the right default
35
+ * for a smoke test: "the component renders and doesn't throw, given that
36
+ * nothing in the host scope returns anything interesting".
37
+ */
38
+ /**
39
+ * Build a scope where each module is a Proxy that resolves *every* property
40
+ * access lazily. Resolution order at access time:
41
+ * 1. mutableHostScope[moduleName][key] (test-time override via setHostScope)
42
+ * 2. registry-supplied hostScope[moduleName][key]
43
+ * 3. BUILT_IN_SCOPE[moduleName][key] (react, react-native, ...)
44
+ * 4. deep no-op stub
45
+ *
46
+ * This means setHostScope() works even AFTER the component has been loaded,
47
+ * because the destructured `useFeed` (or whatever) is itself a Proxy-callable
48
+ * that re-resolves on every invocation.
49
+ */
50
+ function makeAutoStubScope(_componentExports, hostScope, allowedScope) {
51
+ const builtIn = BUILT_IN_SCOPE;
52
+ const supplied = hostScope;
53
+ // When allowedScope is provided, the runner mirrors the production loader's
54
+ // strictness: any specifier not in the union of {allowedScope, BUILT_IN_SCOPE,
55
+ // hostScope, '__component__'} causes the loader's `name in scope` check to
56
+ // return false and throw "is not in host scope" — exactly what the device
57
+ // would see at runtime.
58
+ const allowedSet = allowedScope
59
+ ? new Set([
60
+ ...allowedScope,
61
+ ...Object.keys(builtIn),
62
+ ...Object.keys(supplied),
63
+ ])
64
+ : undefined;
65
+ const lookupModule = (moduleName) => mutableHostScope[moduleName] ?? supplied[moduleName] ?? builtIn[moduleName];
66
+ // For modules that have a real value (e.g. 'react' = React), we want to
67
+ // read straight through. We only Proxy the *unknown* modules — modules that
68
+ // either don't have a hostScope entry or are user packages where hooks may
69
+ // be overridden via setHostScope at test time.
70
+ //
71
+ // Strategy: ALWAYS go through a Proxy. If the underlying module exists, the
72
+ // Proxy reads from it; if not, fall back to deep stubs. This lets a test
73
+ // override individual exports of even the built-in 'react-native' if it
74
+ // really wants to, while keeping the common case (no override) trivially
75
+ // pass-through.
76
+ const moduleProxyCache = new Map();
77
+ const moduleProxyFor = (moduleName) => {
78
+ const cached = moduleProxyCache.get(moduleName);
79
+ if (cached)
80
+ return cached;
81
+ // Use a callable target so the Proxy is also callable (some modules are
82
+ // CommonJS exports that are themselves functions, e.g. some libraries).
83
+ const target = function moduleStub() { };
84
+ const prop = (key) => {
85
+ // Symbol props (Symbol.iterator etc.) — only meaningful when the
86
+ // underlying real module has them; otherwise undefined.
87
+ if (typeof key === "symbol") {
88
+ const real = lookupModule(moduleName);
89
+ return real ? real[key] : undefined;
90
+ }
91
+ // Test-time override (setHostScope) wins over everything.
92
+ const override = mutableHostScope[moduleName];
93
+ if (override && Object.prototype.hasOwnProperty.call(override, key)) {
94
+ return override[key];
95
+ }
96
+ // Registry-supplied scope.
97
+ const fromSupplied = supplied[moduleName];
98
+ if (fromSupplied && Object.prototype.hasOwnProperty.call(fromSupplied, key)) {
99
+ return fromSupplied[key];
100
+ }
101
+ // Built-in scope (real react, react-native mock, etc.).
102
+ const fromBuiltIn = builtIn[moduleName];
103
+ if (fromBuiltIn && Object.prototype.hasOwnProperty.call(fromBuiltIn, key)) {
104
+ return fromBuiltIn[key];
105
+ }
106
+ // Special CommonJS interop properties.
107
+ if (key === "__esModule")
108
+ return true;
109
+ if (key === "default")
110
+ return moduleProxyFor(moduleName);
111
+ // Final fallback: a deep no-op stub. Crucially this is wrapped so it
112
+ // checks mutableHostScope on EACH call (so the test can override the
113
+ // *callable's* return value via setHostScope at any point).
114
+ return makeLiveStub(moduleName, String(key));
115
+ };
116
+ const proxy = new Proxy(target, {
117
+ get(_t, key) {
118
+ return prop(key);
119
+ },
120
+ has() { return true; },
121
+ });
122
+ moduleProxyCache.set(moduleName, proxy);
123
+ return proxy;
124
+ };
125
+ return new Proxy({}, {
126
+ get(_t, key) {
127
+ return moduleProxyFor(key);
128
+ },
129
+ has(_t, key) {
130
+ if (allowedSet && typeof key === "string") {
131
+ return allowedSet.has(key);
132
+ }
133
+ return true;
134
+ },
135
+ });
136
+ }
137
+ /**
138
+ * A stub function that, on each call, re-checks mutableHostScope so that
139
+ * `useFeed` (destructured at module-load time) still respects late
140
+ * setHostScope() overrides set inside the test body.
141
+ */
142
+ function makeLiveStub(moduleName, propName) {
143
+ const fn = function liveStub(...args) {
144
+ const override = mutableHostScope[moduleName];
145
+ if (override && Object.prototype.hasOwnProperty.call(override, propName)) {
146
+ const real = override[propName];
147
+ if (typeof real === "function") {
148
+ return real(...args);
149
+ }
150
+ return real;
151
+ }
152
+ return makeStubModule(`${moduleName}.${propName}()`);
153
+ };
154
+ return new Proxy(fn, {
155
+ get(_t, key) {
156
+ if (key === Symbol.toPrimitive)
157
+ return (_hint) => "";
158
+ if (typeof key === "symbol")
159
+ return undefined;
160
+ if (key === "__esModule")
161
+ return true;
162
+ if (key === "default")
163
+ return fn;
164
+ return makeStubModule(`${moduleName}.${propName}.${String(key)}`);
165
+ },
166
+ has() { return true; },
167
+ });
168
+ }
169
+ const stubCache = new Map();
170
+ /**
171
+ * Make a Proxy that's plausibly anything: callable, indexable, iterable as an
172
+ * empty array, destructurable into more stubs.
173
+ *
174
+ * Why so flexible? Tests for screens never want to fully simulate the host's
175
+ * data layer — they want to know the screen MOUNTS without throwing given
176
+ * "boring" data. So `useFeed()` returns this stub; destructuring
177
+ * `{ articles }` gives back another stub; `articles.map(x => ...)` returns
178
+ * `[]`; `.length === 0`. Real return shapes can be supplied explicitly via
179
+ * setHostScope() when the test cares.
180
+ */
181
+ function makeStubModule(name) {
182
+ if (stubCache.has(name))
183
+ return stubCache.get(name);
184
+ const noop = () => makeStubModule(`${name}()`);
185
+ const stub = new Proxy(noop, {
186
+ get(_t, key) {
187
+ if (key === "__esModule")
188
+ return true;
189
+ if (key === "default")
190
+ return stub;
191
+ // Iterable protocol: pretend to be an empty array so `.map(...)`,
192
+ // `for (const x of ...)`, and spread `[...]` all work.
193
+ if (key === Symbol.iterator)
194
+ return function* () { };
195
+ if (key === Symbol.asyncIterator)
196
+ return async function* () { };
197
+ if (key === "length")
198
+ return 0;
199
+ if (key === "map" || key === "filter" || key === "forEach" || key === "reduce" || key === "flatMap") {
200
+ return () => [];
201
+ }
202
+ if (key === "find" || key === "findIndex" || key === "indexOf" || key === "lastIndexOf") {
203
+ return () => -1;
204
+ }
205
+ if (key === "some" || key === "every" || key === "includes") {
206
+ return () => false;
207
+ }
208
+ if (key === "join" || key === "toString" || key === "toJSON") {
209
+ return () => "";
210
+ }
211
+ if (key === "valueOf") {
212
+ return () => 0;
213
+ }
214
+ if (key === "then" || key === "catch" || key === "finally") {
215
+ // not a thenable — these are sometimes accessed by frameworks
216
+ return undefined;
217
+ }
218
+ // Allow string/number/default coercion (e.g. `${stub}`, +stub) so stubs
219
+ // can flow through StyleSheet.create and other code paths that touch
220
+ // primitives. Returning undefined would also work for hint==="number"
221
+ // but we prefer a stable empty string representation.
222
+ if (key === Symbol.toPrimitive)
223
+ return (_hint) => "";
224
+ if (typeof key === "symbol")
225
+ return undefined;
226
+ return makeStubModule(`${name}.${String(key)}`);
227
+ },
228
+ apply() {
229
+ return makeStubModule(`${name}()`);
230
+ },
231
+ construct() {
232
+ return makeStubModule(`new ${name}()`);
233
+ },
234
+ has() {
235
+ return true;
236
+ },
237
+ });
238
+ stubCache.set(name, stub);
239
+ return stub;
240
+ }
241
+ export async function runTest(input) {
242
+ const start = performance.now();
243
+ // Phase 1: load the component module
244
+ let componentExports;
245
+ try {
246
+ componentExports = loadModule(input.componentCode, makeAutoStubScope(undefined, input.hostScope ?? {}, input.allowedScope), (specifier) => {
247
+ // Components may relative-import sibling components (`./Foo`) or
248
+ // static assets (`../assets/loginImage.png`). The registry resolves
249
+ // sibling components against its store, but in tests we don't have
250
+ // the rest of the registry available, so we hand back a deep stub
251
+ // for both. The component's behavior with a missing sibling is then
252
+ // exactly the same as if the sibling rendered no UI.
253
+ return makeStubModule(`relative:${specifier}`);
254
+ });
255
+ }
256
+ catch (err) {
257
+ const msg = err instanceof Error ? err.message : String(err);
258
+ // Surface scope-misses with a clearer phase so the registry log/CLI
259
+ // output makes it obvious the component needs a host scope addition.
260
+ const phase = /is not in host scope/.test(msg) ? "scope" : "load";
261
+ return {
262
+ ok: false,
263
+ durationMs: performance.now() - start,
264
+ error: {
265
+ phase,
266
+ message: msg,
267
+ stack: err instanceof Error ? err.stack : undefined,
268
+ },
269
+ };
270
+ }
271
+ const defaultExport = componentExports?.default ?? componentExports;
272
+ if (typeof defaultExport !== "function") {
273
+ return {
274
+ ok: false,
275
+ durationMs: performance.now() - start,
276
+ error: {
277
+ phase: "no-default-export",
278
+ message: `component '${input.name}' has no default export of a function/class`,
279
+ },
280
+ };
281
+ }
282
+ // Phase 2: load the test module. The test imports the component via the
283
+ // synthetic specifier '__component__' which we inject into scope.
284
+ const testScope = makeAutoStubScope(componentExports, {
285
+ ...(input.hostScope ?? {}),
286
+ __component__: componentExports,
287
+ "@omriashke/dynamico-validator": await import("./index.js"),
288
+ });
289
+ let testExports;
290
+ try {
291
+ testExports = loadModule(input.testCode, testScope, (specifier) => {
292
+ // Resolve relative imports to the component (the typical pattern is
293
+ // `import Foo from './Foo'` inside Foo.test.tsx).
294
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
295
+ return componentExports;
296
+ }
297
+ throw new Error(`unsupported relative import '${specifier}' in test`);
298
+ });
299
+ }
300
+ catch (err) {
301
+ return {
302
+ ok: false,
303
+ durationMs: performance.now() - start,
304
+ error: {
305
+ phase: "load",
306
+ message: `test file failed to load: ${err instanceof Error ? err.message : String(err)}`,
307
+ stack: err instanceof Error ? err.stack : undefined,
308
+ },
309
+ };
310
+ }
311
+ const testFn = testExports?.default;
312
+ if (typeof testFn !== "function") {
313
+ return {
314
+ ok: false,
315
+ durationMs: performance.now() - start,
316
+ error: {
317
+ phase: "no-test-export",
318
+ message: `${input.name}.test.tsx must export default an async function (got ${typeof testFn})`,
319
+ },
320
+ };
321
+ }
322
+ // Phase 3: execute the test
323
+ try {
324
+ await testFn();
325
+ }
326
+ catch (err) {
327
+ return {
328
+ ok: false,
329
+ durationMs: performance.now() - start,
330
+ error: {
331
+ phase: "test",
332
+ message: err instanceof Error ? err.message : String(err),
333
+ stack: err instanceof Error ? err.stack : undefined,
334
+ },
335
+ };
336
+ }
337
+ return { ok: true, durationMs: performance.now() - start };
338
+ }
339
+ //# sourceMappingURL=runTest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTest.js","sourceRoot":"","sources":["../src/runTest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function sleep(ms: number): Promise<void>;
2
+ /**
3
+ * Yield to React so any pending state updates / useEffect / promises commit
4
+ * before the next assertion. Use after firing events that schedule work.
5
+ */
6
+ export declare function flush(): Promise<void>;
7
+ //# sourceMappingURL=timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../src/timing.ts"],"names":[],"mappings":"AAEA,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;GAGG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3C"}
package/dist/timing.js ADDED
@@ -0,0 +1,14 @@
1
+ import TestRenderer from "react-test-renderer";
2
+ export function sleep(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ /**
6
+ * Yield to React so any pending state updates / useEffect / promises commit
7
+ * before the next assertion. Use after firing events that schedule work.
8
+ */
9
+ export async function flush() {
10
+ await TestRenderer.act(async () => {
11
+ await Promise.resolve();
12
+ });
13
+ }
14
+ //# sourceMappingURL=timing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.js","sourceRoot":"","sources":["../src/timing.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAE/C,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,YAAY,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;QAChC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@omriashke/dynamico-validator",
3
+ "version": "0.1.1",
4
+ "description": "Server-side validator for dynamico components. Provides render(), press(), findByText() with auto-stubbed react-native + host scope so the registry can execute each component's co-located *.test.tsx in a worker thread and reject pushes that throw, miss a scope binding, or render incorrectly.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./mocks/react-native": {
16
+ "types": "./dist/mocks/react-native.d.ts",
17
+ "import": "./dist/mocks/react-native.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/omriaskenazi/dynamico.git",
27
+ "directory": "packages/dynamico-validator"
28
+ },
29
+ "homepage": "https://github.com/omriaskenazi/dynamico#readme",
30
+ "keywords": [
31
+ "dynamico",
32
+ "validator",
33
+ "react-native",
34
+ "push-gate"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "react": "^19.0.0",
41
+ "react-test-renderer": "^19.0.0",
42
+ "@omriashke/dynamico-core": "0.1.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/react": "^18.3.0",
46
+ "@types/react-test-renderer": "^18.3.0",
47
+ "typescript": "^5.6.3"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc -p tsconfig.json",
51
+ "typecheck": "tsc -p tsconfig.json --noEmit"
52
+ }
53
+ }