@ng-native/testing 0.14.0 → 0.16.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/test-utils.js +8 -4
- package/dist/test-utils.js.map +1 -1
- package/package.json +7 -7
package/dist/test-utils.js
CHANGED
|
@@ -11,6 +11,8 @@ function withHandle(node, instanceHandle) {
|
|
|
11
11
|
});
|
|
12
12
|
return node;
|
|
13
13
|
}
|
|
14
|
+
/** The props JSI hands native: a key whose value is undefined is not there at all. */
|
|
15
|
+
const defined = (props) => Object.fromEntries(Object.entries(props).filter(([, value]) => value !== undefined));
|
|
14
16
|
export function createFakeFabric() {
|
|
15
17
|
let handle = 0;
|
|
16
18
|
let committed = [];
|
|
@@ -108,16 +110,18 @@ export function createFakeFabric() {
|
|
|
108
110
|
};
|
|
109
111
|
const clone = (n, props, keepChildren = false) => {
|
|
110
112
|
const source = n;
|
|
111
|
-
|
|
113
|
+
const given = props ? defined(props) : undefined;
|
|
114
|
+
noteModal(source, given);
|
|
112
115
|
return withHandle({
|
|
113
116
|
...source,
|
|
114
117
|
handle: ++handle,
|
|
115
|
-
// Fabric merges raw props onto the source props rather than replacing them
|
|
116
|
-
|
|
118
|
+
// Fabric merges raw props onto the source props rather than replacing them, and JSI
|
|
119
|
+
// drops a key whose value is undefined on the way (JSIDynamic.cpp), so it changes nothing.
|
|
120
|
+
props: given ? { ...source.props, ...given } : source.props,
|
|
117
121
|
children: keepChildren ? source.children : [],
|
|
118
122
|
}, source.instanceHandle);
|
|
119
123
|
};
|
|
120
|
-
const node = (reactTag, viewName, props, instanceHandle) => withHandle({ handle: ++handle, reactTag, viewName, props:
|
|
124
|
+
const node = (reactTag, viewName, props, instanceHandle) => withHandle({ handle: ++handle, reactTag, viewName, props: defined(props), children: [] }, instanceHandle);
|
|
121
125
|
// Keys sorted at every depth, so a golden does not depend on the order props were set in. An
|
|
122
126
|
// array of keys as the replacer would sort too, but it is an allow-list for nested objects as
|
|
123
127
|
// well, and printed `accessibilityState: { disabled: true }` as `{}`.
|
package/dist/test-utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fake `global.nativeFabricUIManager`. Records every host call so tests can assert on\n * the committed tree without a simulator. This is what lets the whole seam be tested in Node.\n */\nimport type { FabricNode, FabricNodeSet, FabricUIManager } from '@ng-native/fabric';\n\nexport interface FakeFabricNode {\n handle: number;\n reactTag: number;\n viewName: string;\n props: Record<string, unknown>;\n children: FakeFabricNode[];\n instanceHandle: unknown;\n /**\n * The node this one is under, as of the last query: null at the top. Set by the queries as they\n * walk the committed tree. A commit shares the nodes it did not change with the one before, so\n * on a node held across commits a later query may set it again, to the parent in that commit.\n */\n readonly parent?: FakeFabricNode | null;\n}\n\nexport interface FakeFabric extends FabricUIManager {\n /** Root child set from the most recent `completeRoot`. */\n readonly committed: FakeFabricNode[];\n readonly calls: {\n createNode: number;\n cloneWithChildren: number;\n cloneWithProps: number;\n cloneWithChildrenAndProps: number;\n completeRoot: number;\n };\n /**\n * Indented view-name tree, for golden assertions. `props: true` includes the flattened\n * prop payload, so a parity test catches prop regressions as well as shape ones.\n */\n render(options?: { props?: boolean }): string;\n find(viewName: string): FakeFabricNode | undefined;\n /** Drive the registered Fabric event handler, as the C++ side would. */\n emit(target: FakeFabricNode, topLevelType: string, nativeEvent?: unknown): void;\n /** Every `setIsJSResponder` the renderer made, in order. */\n readonly responderCalls: { viewName: string; isResponder: boolean; block: boolean }[];\n /**\n * Every `dispatchCommand`, in order. `node` is the node it was sent to, as a non-enumerable\n * property: there to read, and left out of a `deepEqual`.\n */\n readonly commands: {\n viewName: string;\n name: string;\n args: readonly unknown[];\n readonly node?: FakeFabricNode;\n }[];\n /**\n * What `measureInWindow` answers, keyed by `nativeID` and falling back to view name.\n *\n * A test sets the frames it cares about; a node with no entry is not measured at all, which is\n * what the real thing does for a view the platform has not laid out. The two keys are for the\n * two shapes a test comes in: one candidate on screen, where the view name says all there is to\n * say, and several, where which one got measured is the whole question.\n */\n readonly frames: Map<string, { x: number; y: number; width: number; height: number }>;\n reset(): void;\n}\n\n/**\n * Keep the instance handle off the node's enumerable props. It is the engine's own node, which\n * reaches the whole application, and a node a query returns ends up printed in any failed\n * assertion about it: Node's inspector and Vitest's formatter both walk enumerable keys only.\n */\nfunction withHandle(node: Omit<FakeFabricNode, 'instanceHandle'>, instanceHandle: unknown) {\n Object.defineProperty(node, 'instanceHandle', {\n value: instanceHandle,\n enumerable: false,\n writable: true,\n });\n return node as FakeFabricNode;\n}\n\nexport function createFakeFabric(): FakeFabric {\n let handle = 0;\n let committed: FakeFabricNode[] = [];\n let handler: ((h: unknown, t: string, e: unknown) => void) | null = null;\n const responderCalls: { viewName: string; isResponder: boolean; block: boolean }[] = [];\n const commands: { viewName: string; name: string; args: readonly unknown[] }[] = [];\n const frames = new Map<string, { x: number; y: number; width: number; height: number }>();\n const calls = {\n createNode: 0,\n cloneWithChildren: 0,\n cloneWithProps: 0,\n cloneWithChildrenAndProps: 0,\n completeRoot: 0,\n };\n\n /*\n * The parent each view has been appended to, by tag. Native keeps one parent per view for as\n * long as it lives - `ShadowNodeFamily::setParent` asserts it, in a debug build, and a release\n * build goes on with the old parent and measures and lays out against the wrong ancestry - so a\n * view that moves has to be created again, as React does. This fails the test that tries.\n */\n const parents = new Map<number, number>();\n const ROOT = -1;\n const adopt = (parent: number, child: FakeFabricNode): void => {\n const was = parents.get(child.reactTag);\n if (was !== undefined && was !== parent) {\n throw new Error(\n `${child.viewName} ${child.reactTag} was appended to ${parent} after ${was}: native ` +\n 'never re-parents a view, and asserts on it. Create it again instead.',\n );\n }\n parents.set(child.reactTag, parent);\n };\n\n /*\n * The views that have been unmounted, by tag. Fabric drops a view's event target once no\n * committed tree holds it (`EventEmitter::setEnabled`) and never makes a new one, so a view\n * committed again after leaving the tree dispatches every event with a null instance handle, as\n * `emit` does here. A view that comes back has to be created again, as React does.\n */\n const unmounted = new Set<number>();\n const unmount = (n: FakeFabricNode): void => {\n unmounted.add(n.reactTag);\n for (const child of n.children) unmount(child);\n };\n // Walks only what changed, as `updateMountedFlag` does: a subtree committed as it was is skipped.\n const diff = (before: FakeFabricNode[], after: FakeFabricNode[]): void => {\n if (before === after) return;\n const next = new Map(after.map((n) => [n.reactTag, n]));\n for (const was of before) {\n const now = next.get(was.reactTag);\n if (!now) unmount(was);\n else if (now !== was) diff(was.children, now.children);\n }\n };\n\n /*\n * iOS animates a presented modal out when it is committed with `visible: false`, then reports\n * `topDismiss` once, and React Native's Modal.js keeps it in the tree until then. The fake\n * reports it as soon as that commit is done, and lets one dismissal through per close, so a test\n * that also sends it by hand does not see `(dismiss)` twice.\n */\n const dismissing = new Set<number>();\n const dismissed = new Set<number>();\n const dismissAfterCommit = (): void => {\n if (dismissing.size === 0 || !handler) return;\n const tags = [...dismissing];\n dismissing.clear();\n queueMicrotask(() => {\n for (const tag of tags) {\n const host = findByTag(committed, tag);\n if (host && host.props['visible'] === false) fake.emit(host, 'topDismiss');\n }\n });\n };\n const findByTag = (nodes: FakeFabricNode[], tag: number): FakeFabricNode | undefined => {\n for (const n of nodes) {\n const hit = n.reactTag === tag ? n : findByTag(n.children, tag);\n if (hit) return hit;\n }\n return undefined;\n };\n /** A modal host committed as closing, or that left the tree closing. */\n const closing = (target: FakeFabricNode): boolean =>\n (findByTag(committed, target.reactTag) ?? target).props['visible'] === false;\n /** A modal host's props are changing: a close owes one dismissal, and a show resets it. */\n const noteModal = (source: FakeFabricNode, props: Record<string, unknown> | undefined): void => {\n if (source.viewName !== 'ModalHostView' || !props || !('visible' in props)) return;\n if (props['visible'] !== false) dismissed.delete(source.reactTag);\n else if (source.props['visible'] !== false) dismissing.add(source.reactTag);\n };\n\n const clone = (n: FabricNode, props?: object, keepChildren = false): FabricNode => {\n const source = n as unknown as FakeFabricNode;\n noteModal(source, props as Record<string, unknown> | undefined);\n return withHandle(\n {\n ...source,\n handle: ++handle,\n // Fabric merges raw props onto the source props rather than replacing them.\n props: props ? { ...source.props, ...props } : source.props,\n children: keepChildren ? source.children : [],\n },\n source.instanceHandle,\n ) as unknown as FabricNode;\n };\n\n const node = (\n reactTag: number,\n viewName: string,\n props: object,\n instanceHandle: unknown,\n ): FakeFabricNode =>\n withHandle(\n { handle: ++handle, reactTag, viewName, props: { ...props }, children: [] },\n instanceHandle,\n );\n\n // Keys sorted at every depth, so a golden does not depend on the order props were set in. An\n // array of keys as the replacer would sort too, but it is an allow-list for nested objects as\n // well, and printed `accessibilityState: { disabled: true }` as `{}`.\n const sorted = (_key: string, value: unknown): unknown =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)))\n : value;\n\n const walk = (nodes: FakeFabricNode[], depth: number, withProps: boolean): string[] =>\n nodes.flatMap((n) => {\n // The text is printed for itself, so the payload beside it is every other prop: a text\n // field's placeholder and keyboard, where a `RawText` has nothing more to say.\n const { text: said, ...rest } = n.props;\n const text = typeof said === 'string' ? ` \"${said}\"` : '';\n const shown = text ? rest : n.props;\n const props =\n withProps && Object.keys(shown).length ? ' ' + JSON.stringify(shown, sorted) : '';\n return [\n ' '.repeat(depth) + n.viewName + text + props,\n ...walk(n.children, depth + 1, withProps),\n ];\n });\n\n const fake: FakeFabric = {\n createNode(reactTag, viewName, _rootTag, props, instanceHandle) {\n calls.createNode++;\n return node(reactTag, viewName, props, instanceHandle) as unknown as FabricNode;\n },\n cloneNodeWithNewChildren(n) {\n calls.cloneWithChildren++;\n return clone(n);\n },\n cloneNodeWithNewProps(n, newProps) {\n calls.cloneWithProps++;\n return clone(n, newProps, true);\n },\n cloneNodeWithNewChildrenAndProps(n, newProps) {\n calls.cloneWithChildrenAndProps++;\n return clone(n, newProps);\n },\n appendChild(parent, child) {\n const into = parent as unknown as FakeFabricNode;\n adopt(into.reactTag, child as unknown as FakeFabricNode);\n into.children.push(child as unknown as FakeFabricNode);\n return parent;\n },\n createChildSet() {\n return [] as FabricNodeSet;\n },\n appendChildToSet(set, child) {\n adopt(ROOT, child as unknown as FakeFabricNode);\n (set as FakeFabricNode[]).push(child as unknown as FakeFabricNode);\n },\n completeRoot(_rootTag, set) {\n calls.completeRoot++;\n diff(committed, set as FakeFabricNode[]);\n committed = set as FakeFabricNode[];\n dismissAfterCommit();\n },\n registerEventHandler(fn) {\n handler = fn;\n },\n setIsJSResponder(node, isResponder, block) {\n responderCalls.push({\n viewName: (node as unknown as FakeFabricNode).viewName,\n isResponder,\n block,\n });\n },\n measureInWindow(node, onSuccess) {\n // By `nativeID` first, so a test that cares which of two views was measured can say so; by\n // view name otherwise, which is enough when there is only one candidate on screen.\n const fake = node as unknown as FakeFabricNode;\n const frame = frames.get(String(fake.props['nativeID'])) ?? frames.get(fake.viewName);\n if (frame) onSuccess(frame.x, frame.y, frame.width, frame.height);\n },\n dispatchCommand(node, name, args) {\n const command = { viewName: (node as unknown as FakeFabricNode).viewName, name, args };\n // Which node, for a test that has several of one view; off the enumerable keys, so a\n // `deepEqual` against `{ viewName, name, args }` still holds.\n Object.defineProperty(command, 'node', { value: node, enumerable: false });\n commands.push(command as typeof command & { node: FakeFabricNode });\n },\n responderCalls,\n commands,\n frames,\n get committed() {\n return committed;\n },\n calls,\n render(options) {\n return walk(committed, 0, options?.props === true).join('\\n');\n },\n find(viewName) {\n const search = (nodes: FakeFabricNode[]): FakeFabricNode | undefined => {\n for (const n of nodes) {\n if (n.viewName === viewName) return n;\n const hit = search(n.children);\n if (hit) return hit;\n }\n return undefined;\n };\n return search(committed);\n },\n emit(target, type, nativeEvent = {}) {\n if (!handler) throw new Error('no Fabric event handler registered');\n if (type === 'topDismiss' && target.viewName === 'ModalHostView') {\n if (dismissed.has(target.reactTag)) return;\n if (closing(target)) dismissed.add(target.reactTag);\n }\n handler(unmounted.has(target.reactTag) ? null : target.instanceHandle, type, nativeEvent);\n },\n reset() {\n calls.createNode = 0;\n calls.cloneWithChildren = 0;\n calls.cloneWithProps = 0;\n calls.cloneWithChildrenAndProps = 0;\n calls.completeRoot = 0;\n },\n };\n\n return fake;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fake `global.nativeFabricUIManager`. Records every host call so tests can assert on\n * the committed tree without a simulator. This is what lets the whole seam be tested in Node.\n */\nimport type { FabricNode, FabricNodeSet, FabricUIManager } from '@ng-native/fabric';\n\nexport interface FakeFabricNode {\n handle: number;\n reactTag: number;\n viewName: string;\n props: Record<string, unknown>;\n children: FakeFabricNode[];\n instanceHandle: unknown;\n /**\n * The node this one is under, as of the last query: null at the top. Set by the queries as they\n * walk the committed tree. A commit shares the nodes it did not change with the one before, so\n * on a node held across commits a later query may set it again, to the parent in that commit.\n */\n readonly parent?: FakeFabricNode | null;\n}\n\nexport interface FakeFabric extends FabricUIManager {\n /** Root child set from the most recent `completeRoot`. */\n readonly committed: FakeFabricNode[];\n readonly calls: {\n createNode: number;\n cloneWithChildren: number;\n cloneWithProps: number;\n cloneWithChildrenAndProps: number;\n completeRoot: number;\n };\n /**\n * Indented view-name tree, for golden assertions. `props: true` includes the flattened\n * prop payload, so a parity test catches prop regressions as well as shape ones.\n */\n render(options?: { props?: boolean }): string;\n find(viewName: string): FakeFabricNode | undefined;\n /** Drive the registered Fabric event handler, as the C++ side would. */\n emit(target: FakeFabricNode, topLevelType: string, nativeEvent?: unknown): void;\n /** Every `setIsJSResponder` the renderer made, in order. */\n readonly responderCalls: { viewName: string; isResponder: boolean; block: boolean }[];\n /**\n * Every `dispatchCommand`, in order. `node` is the node it was sent to, as a non-enumerable\n * property: there to read, and left out of a `deepEqual`.\n */\n readonly commands: {\n viewName: string;\n name: string;\n args: readonly unknown[];\n readonly node?: FakeFabricNode;\n }[];\n /**\n * What `measureInWindow` answers, keyed by `nativeID` and falling back to view name.\n *\n * A test sets the frames it cares about; a node with no entry is not measured at all, which is\n * what the real thing does for a view the platform has not laid out. The two keys are for the\n * two shapes a test comes in: one candidate on screen, where the view name says all there is to\n * say, and several, where which one got measured is the whole question.\n */\n readonly frames: Map<string, { x: number; y: number; width: number; height: number }>;\n reset(): void;\n}\n\n/**\n * Keep the instance handle off the node's enumerable props. It is the engine's own node, which\n * reaches the whole application, and a node a query returns ends up printed in any failed\n * assertion about it: Node's inspector and Vitest's formatter both walk enumerable keys only.\n */\nfunction withHandle(node: Omit<FakeFabricNode, 'instanceHandle'>, instanceHandle: unknown) {\n Object.defineProperty(node, 'instanceHandle', {\n value: instanceHandle,\n enumerable: false,\n writable: true,\n });\n return node as FakeFabricNode;\n}\n\n/** The props JSI hands native: a key whose value is undefined is not there at all. */\nconst defined = (props: object): Record<string, unknown> =>\n Object.fromEntries(Object.entries(props).filter(([, value]) => value !== undefined));\n\nexport function createFakeFabric(): FakeFabric {\n let handle = 0;\n let committed: FakeFabricNode[] = [];\n let handler: ((h: unknown, t: string, e: unknown) => void) | null = null;\n const responderCalls: { viewName: string; isResponder: boolean; block: boolean }[] = [];\n const commands: { viewName: string; name: string; args: readonly unknown[] }[] = [];\n const frames = new Map<string, { x: number; y: number; width: number; height: number }>();\n const calls = {\n createNode: 0,\n cloneWithChildren: 0,\n cloneWithProps: 0,\n cloneWithChildrenAndProps: 0,\n completeRoot: 0,\n };\n\n /*\n * The parent each view has been appended to, by tag. Native keeps one parent per view for as\n * long as it lives - `ShadowNodeFamily::setParent` asserts it, in a debug build, and a release\n * build goes on with the old parent and measures and lays out against the wrong ancestry - so a\n * view that moves has to be created again, as React does. This fails the test that tries.\n */\n const parents = new Map<number, number>();\n const ROOT = -1;\n const adopt = (parent: number, child: FakeFabricNode): void => {\n const was = parents.get(child.reactTag);\n if (was !== undefined && was !== parent) {\n throw new Error(\n `${child.viewName} ${child.reactTag} was appended to ${parent} after ${was}: native ` +\n 'never re-parents a view, and asserts on it. Create it again instead.',\n );\n }\n parents.set(child.reactTag, parent);\n };\n\n /*\n * The views that have been unmounted, by tag. Fabric drops a view's event target once no\n * committed tree holds it (`EventEmitter::setEnabled`) and never makes a new one, so a view\n * committed again after leaving the tree dispatches every event with a null instance handle, as\n * `emit` does here. A view that comes back has to be created again, as React does.\n */\n const unmounted = new Set<number>();\n const unmount = (n: FakeFabricNode): void => {\n unmounted.add(n.reactTag);\n for (const child of n.children) unmount(child);\n };\n // Walks only what changed, as `updateMountedFlag` does: a subtree committed as it was is skipped.\n const diff = (before: FakeFabricNode[], after: FakeFabricNode[]): void => {\n if (before === after) return;\n const next = new Map(after.map((n) => [n.reactTag, n]));\n for (const was of before) {\n const now = next.get(was.reactTag);\n if (!now) unmount(was);\n else if (now !== was) diff(was.children, now.children);\n }\n };\n\n /*\n * iOS animates a presented modal out when it is committed with `visible: false`, then reports\n * `topDismiss` once, and React Native's Modal.js keeps it in the tree until then. The fake\n * reports it as soon as that commit is done, and lets one dismissal through per close, so a test\n * that also sends it by hand does not see `(dismiss)` twice.\n */\n const dismissing = new Set<number>();\n const dismissed = new Set<number>();\n const dismissAfterCommit = (): void => {\n if (dismissing.size === 0 || !handler) return;\n const tags = [...dismissing];\n dismissing.clear();\n queueMicrotask(() => {\n for (const tag of tags) {\n const host = findByTag(committed, tag);\n if (host && host.props['visible'] === false) fake.emit(host, 'topDismiss');\n }\n });\n };\n const findByTag = (nodes: FakeFabricNode[], tag: number): FakeFabricNode | undefined => {\n for (const n of nodes) {\n const hit = n.reactTag === tag ? n : findByTag(n.children, tag);\n if (hit) return hit;\n }\n return undefined;\n };\n /** A modal host committed as closing, or that left the tree closing. */\n const closing = (target: FakeFabricNode): boolean =>\n (findByTag(committed, target.reactTag) ?? target).props['visible'] === false;\n /** A modal host's props are changing: a close owes one dismissal, and a show resets it. */\n const noteModal = (source: FakeFabricNode, props: Record<string, unknown> | undefined): void => {\n if (source.viewName !== 'ModalHostView' || !props || !('visible' in props)) return;\n if (props['visible'] !== false) dismissed.delete(source.reactTag);\n else if (source.props['visible'] !== false) dismissing.add(source.reactTag);\n };\n\n const clone = (n: FabricNode, props?: object, keepChildren = false): FabricNode => {\n const source = n as unknown as FakeFabricNode;\n const given = props ? defined(props) : undefined;\n noteModal(source, given);\n return withHandle(\n {\n ...source,\n handle: ++handle,\n // Fabric merges raw props onto the source props rather than replacing them, and JSI\n // drops a key whose value is undefined on the way (JSIDynamic.cpp), so it changes nothing.\n props: given ? { ...source.props, ...given } : source.props,\n children: keepChildren ? source.children : [],\n },\n source.instanceHandle,\n ) as unknown as FabricNode;\n };\n\n const node = (\n reactTag: number,\n viewName: string,\n props: object,\n instanceHandle: unknown,\n ): FakeFabricNode =>\n withHandle(\n { handle: ++handle, reactTag, viewName, props: defined(props), children: [] },\n instanceHandle,\n );\n\n // Keys sorted at every depth, so a golden does not depend on the order props were set in. An\n // array of keys as the replacer would sort too, but it is an allow-list for nested objects as\n // well, and printed `accessibilityState: { disabled: true }` as `{}`.\n const sorted = (_key: string, value: unknown): unknown =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)))\n : value;\n\n const walk = (nodes: FakeFabricNode[], depth: number, withProps: boolean): string[] =>\n nodes.flatMap((n) => {\n // The text is printed for itself, so the payload beside it is every other prop: a text\n // field's placeholder and keyboard, where a `RawText` has nothing more to say.\n const { text: said, ...rest } = n.props;\n const text = typeof said === 'string' ? ` \"${said}\"` : '';\n const shown = text ? rest : n.props;\n const props =\n withProps && Object.keys(shown).length ? ' ' + JSON.stringify(shown, sorted) : '';\n return [\n ' '.repeat(depth) + n.viewName + text + props,\n ...walk(n.children, depth + 1, withProps),\n ];\n });\n\n const fake: FakeFabric = {\n createNode(reactTag, viewName, _rootTag, props, instanceHandle) {\n calls.createNode++;\n return node(reactTag, viewName, props, instanceHandle) as unknown as FabricNode;\n },\n cloneNodeWithNewChildren(n) {\n calls.cloneWithChildren++;\n return clone(n);\n },\n cloneNodeWithNewProps(n, newProps) {\n calls.cloneWithProps++;\n return clone(n, newProps, true);\n },\n cloneNodeWithNewChildrenAndProps(n, newProps) {\n calls.cloneWithChildrenAndProps++;\n return clone(n, newProps);\n },\n appendChild(parent, child) {\n const into = parent as unknown as FakeFabricNode;\n adopt(into.reactTag, child as unknown as FakeFabricNode);\n into.children.push(child as unknown as FakeFabricNode);\n return parent;\n },\n createChildSet() {\n return [] as FabricNodeSet;\n },\n appendChildToSet(set, child) {\n adopt(ROOT, child as unknown as FakeFabricNode);\n (set as FakeFabricNode[]).push(child as unknown as FakeFabricNode);\n },\n completeRoot(_rootTag, set) {\n calls.completeRoot++;\n diff(committed, set as FakeFabricNode[]);\n committed = set as FakeFabricNode[];\n dismissAfterCommit();\n },\n registerEventHandler(fn) {\n handler = fn;\n },\n setIsJSResponder(node, isResponder, block) {\n responderCalls.push({\n viewName: (node as unknown as FakeFabricNode).viewName,\n isResponder,\n block,\n });\n },\n measureInWindow(node, onSuccess) {\n // By `nativeID` first, so a test that cares which of two views was measured can say so; by\n // view name otherwise, which is enough when there is only one candidate on screen.\n const fake = node as unknown as FakeFabricNode;\n const frame = frames.get(String(fake.props['nativeID'])) ?? frames.get(fake.viewName);\n if (frame) onSuccess(frame.x, frame.y, frame.width, frame.height);\n },\n dispatchCommand(node, name, args) {\n const command = { viewName: (node as unknown as FakeFabricNode).viewName, name, args };\n // Which node, for a test that has several of one view; off the enumerable keys, so a\n // `deepEqual` against `{ viewName, name, args }` still holds.\n Object.defineProperty(command, 'node', { value: node, enumerable: false });\n commands.push(command as typeof command & { node: FakeFabricNode });\n },\n responderCalls,\n commands,\n frames,\n get committed() {\n return committed;\n },\n calls,\n render(options) {\n return walk(committed, 0, options?.props === true).join('\\n');\n },\n find(viewName) {\n const search = (nodes: FakeFabricNode[]): FakeFabricNode | undefined => {\n for (const n of nodes) {\n if (n.viewName === viewName) return n;\n const hit = search(n.children);\n if (hit) return hit;\n }\n return undefined;\n };\n return search(committed);\n },\n emit(target, type, nativeEvent = {}) {\n if (!handler) throw new Error('no Fabric event handler registered');\n if (type === 'topDismiss' && target.viewName === 'ModalHostView') {\n if (dismissed.has(target.reactTag)) return;\n if (closing(target)) dismissed.add(target.reactTag);\n }\n handler(unmounted.has(target.reactTag) ? null : target.instanceHandle, type, nativeEvent);\n },\n reset() {\n calls.createNode = 0;\n calls.cloneWithChildren = 0;\n calls.cloneWithProps = 0;\n calls.cloneWithChildrenAndProps = 0;\n calls.completeRoot = 0;\n },\n };\n\n return fake;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-native/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Test Angular Native components in Node with no simulator: a fake Fabric, Testing Library queries and events, and Vitest and node:test integration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@ng-native/fabric": "0.
|
|
38
|
-
"@ng-native/metro": "0.
|
|
39
|
-
"@ng-native/platform": "0.
|
|
37
|
+
"@ng-native/fabric": "0.16.0",
|
|
38
|
+
"@ng-native/metro": "0.16.0",
|
|
39
|
+
"@ng-native/platform": "0.16.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@angular/compiler": "^22.0.0",
|
|
43
43
|
"@angular/core": "^22.0.0",
|
|
44
44
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
|
|
45
45
|
"vitest": "^4.0.8 || ^5.0.0",
|
|
46
|
-
"@ng-native/components": "0.
|
|
46
|
+
"@ng-native/components": "0.16.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
49
|
"@angular/compiler": {
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"@angular/core": "22.2.0",
|
|
63
63
|
"@angular/forms": "22.2.0",
|
|
64
64
|
"@angular/router": "22.2.0",
|
|
65
|
-
"@ng-native/components": "0.
|
|
66
|
-
"@ng-native/router": "0.
|
|
65
|
+
"@ng-native/components": "0.16.0",
|
|
66
|
+
"@ng-native/router": "0.16.0",
|
|
67
67
|
"vite": "^8.3.1",
|
|
68
68
|
"vitest": "^5.0.2"
|
|
69
69
|
},
|