@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.
- package/LICENSE +184 -0
- package/dist/events.d.ts +10 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +35 -0
- package/dist/events.js.map +1 -0
- package/dist/expect.d.ts +18 -0
- package/dist/expect.d.ts.map +1 -0
- package/dist/expect.js +69 -0
- package/dist/expect.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/mocks/react-native.d.ts +385 -0
- package/dist/mocks/react-native.d.ts.map +1 -0
- package/dist/mocks/react-native.js +181 -0
- package/dist/mocks/react-native.js.map +1 -0
- package/dist/mocks/safe-area-context.d.ts +50 -0
- package/dist/mocks/safe-area-context.d.ts.map +1 -0
- package/dist/mocks/safe-area-context.js +13 -0
- package/dist/mocks/safe-area-context.js.map +1 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +65 -0
- package/dist/queries.js.map +1 -0
- package/dist/render.d.ts +39 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +34 -0
- package/dist/render.js.map +1 -0
- package/dist/runTest.d.ts +48 -0
- package/dist/runTest.d.ts.map +1 -0
- package/dist/runTest.js +339 -0
- package/dist/runTest.js.map +1 -0
- package/dist/timing.d.ts +7 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +14 -0
- package/dist/timing.js.map +1 -0
- package/package.json +53 -0
- package/src/events.ts +38 -0
- package/src/expect.ts +70 -0
- package/src/index.ts +23 -0
- package/src/mocks/react-native.ts +203 -0
- package/src/mocks/safe-area-context.ts +15 -0
- package/src/queries.ts +66 -0
- package/src/render.ts +64 -0
- package/src/runTest.ts +402 -0
- package/src/timing.ts +15 -0
package/dist/queries.js
ADDED
|
@@ -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":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAAuB,EAAE,OAAwB;IAC1E,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,wCAAwC,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CACnH,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAuB,EAAE,OAAwB;IAC3E,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAW,EAAE,CAC5C,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE9E,MAAM,KAAK,GAAG,CAAC,IAAuB,EAA4B,EAAE;QAClE,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,IAAiC,CAAC,WAAW,IAAI,EAAE,CAAC;QACvH,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC5C,IAAI,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QACnC,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;YACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,IAAI,QAAQ,KAAK,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC;oBAAE,OAAO,IAAI,CAAC;gBACvD,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3B,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAuB,EAAE,QAAgB;IACrE,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,CAAC,IAAuB,EAAE,EAAE;QACxC,MAAM,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,IAAiC,CAAC,WAAW,IAAI,EAAE,CAAC;QAChH,IAAI,CAAC,KAAK,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;YACxC,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACjF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/dist/render.d.ts
ADDED
|
@@ -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"}
|
package/dist/runTest.js
ADDED
|
@@ -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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"}
|
package/dist/timing.d.ts
ADDED
|
@@ -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
|
+
}
|