@ng-native/testing 0.3.0 → 0.4.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.
Files changed (41) hide show
  1. package/dist/compile-css.d.ts +17 -0
  2. package/dist/compile-css.js +14 -0
  3. package/dist/compile-css.js.map +1 -0
  4. package/dist/events.d.ts +9 -0
  5. package/dist/events.js +14 -0
  6. package/dist/events.js.map +1 -1
  7. package/dist/gesture-handler.d.ts +3 -39
  8. package/dist/gesture-handler.js +2 -41
  9. package/dist/gesture-handler.js.map +1 -1
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/inject-service.d.ts +11 -2
  14. package/dist/inject-service.js +26 -6
  15. package/dist/inject-service.js.map +1 -1
  16. package/dist/memory-database.d.ts +23 -0
  17. package/dist/memory-database.js +60 -0
  18. package/dist/memory-database.js.map +1 -0
  19. package/dist/queries.d.ts +5 -0
  20. package/dist/queries.js +21 -3
  21. package/dist/queries.js.map +1 -1
  22. package/dist/reanimated-library.d.ts +5 -59
  23. package/dist/reanimated-library.js +5 -92
  24. package/dist/reanimated-library.js.map +1 -1
  25. package/dist/reanimated.d.ts +3 -0
  26. package/dist/reanimated.js +24 -7
  27. package/dist/reanimated.js.map +1 -1
  28. package/dist/render.js +49 -4
  29. package/dist/render.js.map +1 -1
  30. package/dist/test-utils.d.ts +6 -0
  31. package/dist/test-utils.js +6 -4
  32. package/dist/test-utils.js.map +1 -1
  33. package/dist/worklets-library.d.ts +2 -10
  34. package/dist/worklets-library.js +2 -22
  35. package/dist/worklets-library.js.map +1 -1
  36. package/package.json +8 -7
  37. package/runner/compile.mjs +10 -1
  38. package/runner/register.mjs +14 -0
  39. package/runner/setup.mjs +13 -0
  40. package/runner/vitest.d.mts +6 -0
  41. package/runner/vitest.mjs +22 -14
@@ -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\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 const text = typeof n.props['text'] === 'string' ? ` \"${n.props['text']}\"` : '';\n const props =\n withProps && Object.keys(n.props).length && !text\n ? ' ' + JSON.stringify(n.props, sorted)\n : '';\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\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,10 +1,2 @@
1
- /**
2
- * What a test gets for `react-native-worklets`, whose source Node cannot load. There is one
3
- * runtime in a test, so scheduling a function on either runtime calls it at once.
4
- */
5
- export declare function scheduleOnRN<A extends unknown[]>(fn: (...args: A) => unknown, ...args: A): void;
6
- export declare function scheduleOnUI<A extends unknown[]>(fn: (...args: A) => unknown, ...args: A): void;
7
- export declare function runOnUISync<A extends unknown[], R>(fn: (...args: A) => R, ...args: A): R;
8
- export declare function runOnJS<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
9
- export declare function runOnUI<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
10
- export declare function isWorkletFunction(_value: unknown): boolean;
1
+ /** What a test gets for `react-native-worklets`, whose source Node cannot load. */
2
+ export * from '@ng-native/components/stand-ins/worklets';
@@ -1,23 +1,3 @@
1
- /**
2
- * What a test gets for `react-native-worklets`, whose source Node cannot load. There is one
3
- * runtime in a test, so scheduling a function on either runtime calls it at once.
4
- */
5
- export function scheduleOnRN(fn, ...args) {
6
- fn(...args);
7
- }
8
- export function scheduleOnUI(fn, ...args) {
9
- fn(...args);
10
- }
11
- export function runOnUISync(fn, ...args) {
12
- return fn(...args);
13
- }
14
- export function runOnJS(fn) {
15
- return fn;
16
- }
17
- export function runOnUI(fn) {
18
- return fn;
19
- }
20
- export function isWorkletFunction(_value) {
21
- return false;
22
- }
1
+ /** What a test gets for `react-native-worklets`, whose source Node cannot load. */
2
+ export * from '@ng-native/components/stand-ins/worklets';
23
3
  //# sourceMappingURL=worklets-library.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worklets-library.js","sourceRoot":"","sources":["../src/worklets-library.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAsB,EAA2B,EAAE,GAAG,IAAO;IACvF,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAsB,EAA2B,EAAE,GAAG,IAAO;IACvF,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAyB,EAAqB,EAAE,GAAG,IAAO;IACnF,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,OAAO,CAAyB,EAAqB;IACnE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,OAAO,CAAyB,EAAqB;IACnE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * What a test gets for `react-native-worklets`, whose source Node cannot load. There is one\n * runtime in a test, so scheduling a function on either runtime calls it at once.\n */\nexport function scheduleOnRN<A extends unknown[]>(fn: (...args: A) => unknown, ...args: A): void {\n fn(...args);\n}\n\nexport function scheduleOnUI<A extends unknown[]>(fn: (...args: A) => unknown, ...args: A): void {\n fn(...args);\n}\n\nexport function runOnUISync<A extends unknown[], R>(fn: (...args: A) => R, ...args: A): R {\n return fn(...args);\n}\n\nexport function runOnJS<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R {\n return fn;\n}\n\nexport function runOnUI<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R {\n return fn;\n}\n\nexport function isWorkletFunction(_value: unknown): boolean {\n return false;\n}\n"]}
1
+ {"version":3,"file":"worklets-library.js","sourceRoot":"","sources":["../src/worklets-library.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,cAAc,0CAA0C,CAAC","sourcesContent":["/** What a test gets for `react-native-worklets`, whose source Node cannot load. */\nexport * from '@ng-native/components/stand-ins/worklets';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-native/testing",
3
- "version": "0.3.0",
3
+ "version": "0.4.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,15 +34,16 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@ng-native/fabric": "0.3.0",
38
- "@ng-native/metro": "0.3.0",
39
- "@ng-native/platform": "0.3.0"
37
+ "@ng-native/fabric": "0.4.0",
38
+ "@ng-native/metro": "0.4.0",
39
+ "@ng-native/platform": "0.4.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
- "vitest": "^4.0.8 || ^5.0.0"
45
+ "vitest": "^4.0.8 || ^5.0.0",
46
+ "@ng-native/components": "0.4.0"
46
47
  },
47
48
  "peerDependenciesMeta": {
48
49
  "@angular/compiler": {
@@ -61,8 +62,8 @@
61
62
  "@angular/core": "22.2.0",
62
63
  "@angular/forms": "22.2.0",
63
64
  "@angular/router": "22.2.0",
64
- "@ng-native/components": "0.3.0",
65
- "@ng-native/router": "0.3.0",
65
+ "@ng-native/components": "0.4.0",
66
+ "@ng-native/router": "0.4.0",
66
67
  "vite": "^8.3.1",
67
68
  "vitest": "^5.0.2"
68
69
  },
@@ -63,7 +63,7 @@ export function compileAngular(source, file, options = {}) {
63
63
  : result.code;
64
64
  const directory = path.dirname(file);
65
65
  return {
66
- code,
66
+ code: stubFonts(code),
67
67
  dependencies: result.dependencies.map((dependency) => path.resolve(directory, dependency)),
68
68
  map: result.map,
69
69
  };
@@ -78,5 +78,14 @@ export function compileAngular(source, file, options = {}) {
78
78
  const ASSET =
79
79
  /\brequire\(\s*(['"])([^'"]+\.(?:png|jpe?g|gif|webp|bmp|svg|ttf|otf|mp3|mp4|wav|m4a|mov|json5?))\1\s*\)/g;
80
80
  /** @param {string} code */
81
+ /**
82
+ * The `require` the compiler writes for the file a stylesheet's `@font-face` names, which is in no
83
+ * source a caller could have stubbed and has no `require` to run with either. Matched by where it
84
+ * sits in a compiled sheet, so whatever the file is called, and nothing a package wrote itself.
85
+ */
86
+ const FONT = /"source":require\(("(?:[^"\\]|\\.)*")\)/g;
87
+ /** @param {string} code */
88
+ const stubFonts = (code) => code.replace(FONT, (_, path) => `"source":({ testUri: ${path} })`);
89
+
81
90
  export const stubAssets = (code) =>
82
91
  code.replace(ASSET, (_, _quote, path) => `({ testUri: ${JSON.stringify(path)} })`);
@@ -15,3 +15,17 @@ createRequire(import.meta.url)('@ng-native/metro/polyfills/animation-globals.js'
15
15
  globalThis.__DEV__ ??= true;
16
16
 
17
17
  register('./loader.mjs', import.meta.url);
18
+
19
+ // Tells `injectService` which test is running. See `setup.mjs`. Only under `node --test`: a hook
20
+ // starts the test runner, which a script run through this file is not for.
21
+ if (process.env.NODE_TEST_CONTEXT || process.execArgv.some((arg) => arg.startsWith('--test'))) {
22
+ const { afterEach, beforeEach } = await import('node:test');
23
+ const tests = (globalThis[Symbol.for('ng-native.testing.tests')] ??= { finished: 0, running: 0 });
24
+ beforeEach(() => {
25
+ tests.running++;
26
+ });
27
+ afterEach(() => {
28
+ tests.running--;
29
+ tests.finished++;
30
+ });
31
+ }
package/runner/setup.mjs CHANGED
@@ -4,7 +4,20 @@
4
4
  * matters and why `ngDevMode` is left alone.
5
5
  */
6
6
  import { createRequire } from 'node:module';
7
+ import { afterEach, beforeEach } from 'vitest';
7
8
 
8
9
  createRequire(import.meta.url)('@ng-native/metro/polyfills/animation-globals.js');
9
10
  // Metro's prelude defines this in every bundle, and app code reads it bare.
10
11
  globalThis.__DEV__ ??= true;
12
+
13
+ // Tells `injectService` which test is running, so the app the calls in a test share is never
14
+ // another test's: how many tests have finished, and how many are running now. On the global, since
15
+ // this file and the package a test imports need not be one module instance.
16
+ const tests = (globalThis[Symbol.for('ng-native.testing.tests')] ??= { finished: 0, running: 0 });
17
+ beforeEach(() => {
18
+ tests.running++;
19
+ });
20
+ afterEach(() => {
21
+ tests.running--;
22
+ tests.finished++;
23
+ });
@@ -10,3 +10,9 @@ export interface NgNativeOptions {
10
10
 
11
11
  /** Compile Angular for Vitest: AOT for decorated sources, the linker for partial-compiled packages. */
12
12
  export declare function ngNative(options?: NgNativeOptions): Plugin;
13
+
14
+ /**
15
+ * Where a stand-in module is: the source in the repository, the compiled file in the published
16
+ * package. `from` is the runner's own location.
17
+ */
18
+ export declare function standIn(name: string, from?: string | URL): string;
package/runner/vitest.mjs CHANGED
@@ -20,7 +20,7 @@
20
20
  * The environment is Vitest's default, `node`. There is no DOM to emulate: the renderer talks to
21
21
  * a fake Fabric, and jsdom would only give Angular a `document` to misread.
22
22
  */
23
- import { readFileSync } from 'node:fs';
23
+ import { existsSync, readFileSync } from 'node:fs';
24
24
  import path from 'node:path';
25
25
  import { fileURLToPath } from 'node:url';
26
26
  import { defaultClientConditions, defaultServerConditions } from 'vite';
@@ -35,19 +35,27 @@ const SETUP = fileURLToPath(new URL('./setup.mjs', import.meta.url));
35
35
  * an app imports for `withTiming` and `scheduleOnRN`. See each stand-in for what it does and does
36
36
  * not do.
37
37
  */
38
- const STAND_INS = {
39
- '@ng-native/components/gestures': fileURLToPath(new URL('../src/gestures.ts', import.meta.url)),
40
- 'react-native-gesture-handler': fileURLToPath(
41
- new URL('../src/gesture-handler.ts', import.meta.url),
42
- ),
43
- '@ng-native/components/reanimated': fileURLToPath(
44
- new URL('../src/reanimated.ts', import.meta.url),
45
- ),
46
- 'react-native-reanimated': fileURLToPath(
47
- new URL('../src/reanimated-library.ts', import.meta.url),
48
- ),
49
- 'react-native-worklets': fileURLToPath(new URL('../src/worklets-library.ts', import.meta.url)),
50
- };
38
+ const STAND_INS = Object.fromEntries(
39
+ Object.entries({
40
+ '@ng-native/components/gestures': 'gestures',
41
+ 'react-native-gesture-handler': 'gesture-handler',
42
+ '@ng-native/components/reanimated': 'reanimated',
43
+ 'react-native-reanimated': 'reanimated-library',
44
+ 'react-native-worklets': 'worklets-library',
45
+ }).map(([source, name]) => [source, standIn(name)]),
46
+ );
47
+
48
+ /**
49
+ * Where a stand-in is: the source beside this file in the repository, and the compiled file in
50
+ * the published package, which ships `dist` and no `src`.
51
+ *
52
+ * @param {string} name
53
+ * @param {string | URL} [from] the runner's own directory
54
+ */
55
+ export function standIn(name, from = import.meta.url) {
56
+ const source = fileURLToPath(new URL(`../src/${name}.ts`, from));
57
+ return existsSync(source) ? source : fileURLToPath(new URL(`../dist/${name}.js`, from));
58
+ }
51
59
 
52
60
  /**
53
61
  * ponytail: inlining is by package name, so a third-party Angular library other than these has