@ng-native/testing 0.0.1 → 0.1.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -1
  3. package/dist/events.d.ts +45 -0
  4. package/dist/events.js +144 -0
  5. package/dist/events.js.map +1 -0
  6. package/dist/gesture-handler.d.ts +39 -0
  7. package/dist/gesture-handler.js +42 -0
  8. package/dist/gesture-handler.js.map +1 -0
  9. package/dist/gesture-of.d.ts +8 -0
  10. package/dist/gesture-of.js +29 -0
  11. package/dist/gesture-of.js.map +1 -0
  12. package/dist/gestures.d.ts +16 -0
  13. package/dist/gestures.js +46 -0
  14. package/dist/gestures.js.map +1 -0
  15. package/dist/index.d.ts +15 -0
  16. package/dist/index.js +14 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/queries.d.ts +73 -0
  19. package/dist/queries.js +148 -0
  20. package/dist/queries.js.map +1 -0
  21. package/dist/reanimated-library.d.ts +62 -0
  22. package/dist/reanimated-library.js +95 -0
  23. package/dist/reanimated-library.js.map +1 -0
  24. package/dist/reanimated.d.ts +30 -0
  25. package/dist/reanimated.js +80 -0
  26. package/dist/reanimated.js.map +1 -0
  27. package/dist/render.d.ts +92 -0
  28. package/dist/render.js +182 -0
  29. package/dist/render.js.map +1 -0
  30. package/dist/test-utils.d.ts +66 -0
  31. package/dist/test-utils.js +162 -0
  32. package/dist/test-utils.js.map +1 -0
  33. package/dist/wait-for.d.ts +25 -0
  34. package/dist/wait-for.js +21 -0
  35. package/dist/wait-for.js.map +1 -0
  36. package/dist/worklets-library.d.ts +10 -0
  37. package/dist/worklets-library.js +23 -0
  38. package/dist/worklets-library.js.map +1 -0
  39. package/package.json +86 -3
  40. package/runner/compile.mjs +82 -0
  41. package/runner/loader.mjs +73 -0
  42. package/runner/register.mjs +17 -0
  43. package/runner/setup.mjs +10 -0
  44. package/runner/vitest.d.mts +12 -0
  45. package/runner/vitest.mjs +142 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Testing Library's queries, over the tree the renderer committed to the fake Fabric.
3
+ *
4
+ * There is no DOM here, so `@testing-library/dom` has nothing to query: a query reads the props a
5
+ * native view would actually receive. That makes the names mean what React Native Testing Library
6
+ * means by them rather than what the web does - a role is `accessibilityRole`, a label is
7
+ * `accessibilityLabel`, text is what a `Paragraph` holds.
8
+ *
9
+ * Every query re-reads the most recent commit. The fake builds a fresh node object per commit, so
10
+ * a node held across an interaction is a snapshot of the frame it was found in; query again to see
11
+ * what changed.
12
+ */
13
+ import type { FakeFabricNode } from './test-utils.ts';
14
+ import { type WaitForOptions } from './wait-for.ts';
15
+ /** What a query matches against: a whole string (normalised), or a RegExp tested against it. */
16
+ export type Matcher = string | RegExp;
17
+ /** How a string `Matcher` compares. */
18
+ export interface TextMatchOptions {
19
+ /** `false` matches a case-insensitive substring instead of the whole, trimmed string. */
20
+ exact?: boolean;
21
+ /**
22
+ * Also find what nobody could see or reach: views under `display: none`, and those hidden from
23
+ * accessibility. React Native Testing Library's option, and its default of `false`.
24
+ */
25
+ includeHiddenElements?: boolean;
26
+ }
27
+ /** `ByRole`'s options: a role alone matches every node with it, `name` narrows it to one. */
28
+ export interface ByRoleOptions extends TextMatchOptions {
29
+ /** The accessible name: `accessibilityLabel`, or failing that the text inside the node. */
30
+ name?: Matcher;
31
+ }
32
+ interface QueryArgs {
33
+ Role: [role: Matcher, options?: ByRoleOptions];
34
+ Text: [text: Matcher, options?: TextMatchOptions];
35
+ TestId: [testId: Matcher, options?: TextMatchOptions];
36
+ LabelText: [label: Matcher, options?: TextMatchOptions];
37
+ PlaceholderText: [placeholder: Matcher, options?: TextMatchOptions];
38
+ DisplayValue: [value: Matcher, options?: TextMatchOptions];
39
+ }
40
+ type Name = keyof QueryArgs;
41
+ type FindArgs<K extends Name> = [...QueryArgs[K], waitForOptions?: WaitForOptions];
42
+ /**
43
+ * The query matrix: `getBy`, `getAllBy`, `queryBy`, `queryAllBy`, `findBy` and `findAllBy`, for
44
+ * each of `Role`, `Text`, `TestId`, `LabelText`, `PlaceholderText` and `DisplayValue`.
45
+ */
46
+ export type BoundQueries = {
47
+ [K in Name as `getBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode;
48
+ } & {
49
+ [K in Name as `getAllBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode[];
50
+ } & {
51
+ [K in Name as `queryBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode | null;
52
+ } & {
53
+ [K in Name as `queryAllBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode[];
54
+ } & {
55
+ [K in Name as `findBy${K}`]: (...args: FindArgs<K>) => Promise<FakeFabricNode>;
56
+ } & {
57
+ [K in Name as `findAllBy${K}`]: (...args: FindArgs<K>) => Promise<FakeFabricNode[]>;
58
+ };
59
+ export declare const flatten: (nodes: readonly FakeFabricNode[]) => FakeFabricNode[];
60
+ /** What a screen reader would read out: every `RawText` under the node, in order. */
61
+ export declare function textContent(node: FakeFabricNode): string;
62
+ /** A text field under any of the names the platforms register it by. */
63
+ export declare const isTextInput: (node: FakeFabricNode) => boolean;
64
+ /**
65
+ * Every query, bound to a root that is looked up again on each call.
66
+ *
67
+ * `found` sees every node a query hands back, so an event fired on one later knows which render
68
+ * it came from.
69
+ */
70
+ export declare function bindQueries(roots: () => readonly FakeFabricNode[], tree: () => string, found: (node: FakeFabricNode) => void): BoundQueries;
71
+ /** The debug tree: view names, text, and the props a query can find a node by. */
72
+ export declare function describeTree(nodes: readonly FakeFabricNode[], depth?: number): string;
73
+ export {};
@@ -0,0 +1,148 @@
1
+ import { waitFor } from "./wait-for.js";
2
+ const normalize = (text) => text.replace(/\s+/g, ' ').trim();
3
+ function matches(value, matcher, exact = true) {
4
+ if (typeof value !== 'string')
5
+ return false;
6
+ const text = normalize(value);
7
+ if (matcher instanceof RegExp)
8
+ return matcher.test(text);
9
+ const wanted = normalize(matcher);
10
+ return exact ? text === wanted : text.toLowerCase().includes(wanted.toLowerCase());
11
+ }
12
+ const show = (matcher) => matcher instanceof RegExp ? String(matcher) : JSON.stringify(matcher);
13
+ export const flatten = (nodes) => nodes.flatMap((node) => [node, ...flatten(node.children)]);
14
+ /**
15
+ * A view that takes itself and everything under it out of sight or out of the accessibility tree,
16
+ * as React Native Testing Library's `isHiddenFromAccessibility` decides it.
17
+ */
18
+ function hides(node) {
19
+ const props = node.props;
20
+ return (props['display'] === 'none' ||
21
+ props['accessibilityElementsHidden'] === true ||
22
+ props['aria-hidden'] === true ||
23
+ props['importantForAccessibility'] === 'no-hide-descendants');
24
+ }
25
+ /** Every node someone using the app could see or reach. */
26
+ const reachable = (nodes) => nodes.flatMap((node) => (hides(node) ? [] : [node, ...reachable(node.children)]));
27
+ /** What a screen reader would read out: every `RawText` under the node, in order. */
28
+ export function textContent(node) {
29
+ if (node.viewName === 'RawText')
30
+ return String(node.props['text'] ?? '');
31
+ return node.children.map(textContent).join('');
32
+ }
33
+ /** A text field under any of the names the platforms register it by. */
34
+ export const isTextInput = (node) => /TextInput/.test(node.viewName);
35
+ /** What a screen reader reads out: the label, or failing that the text inside. */
36
+ const nameOf = (node) => typeof node.props['accessibilityLabel'] === 'string'
37
+ ? node.props['accessibilityLabel']
38
+ : textContent(node);
39
+ const QUERIES = {
40
+ Role: (role, options = {}) => ({
41
+ describe: `role ${show(role)}` + (options.name === undefined ? '' : ` and name ${show(options.name)}`),
42
+ select: (node) => matches(node.props['accessibilityRole'], role) &&
43
+ (options.name === undefined || matches(nameOf(node), options.name, options.exact)),
44
+ }),
45
+ // `Paragraph` alone: a nested `<text>` commits as a `Text` span inside one, so the paragraph is
46
+ // the whole run of text and the span is only a part of it.
47
+ Text: (text, options = {}) => ({
48
+ describe: `text ${show(text)}`,
49
+ select: (node) => node.viewName === 'Paragraph' && matches(textContent(node), text, options.exact),
50
+ }),
51
+ // Both, because the components commit both: `testID` from `testID`, `nativeID` from `nativeID`
52
+ // or its web spelling `id`, which is what most templates in this project reach for.
53
+ TestId: (testId, options = {}) => ({
54
+ describe: `testID ${show(testId)}`,
55
+ select: (node) => matches(node.props['testID'], testId, options.exact) ||
56
+ matches(node.props['nativeID'], testId, options.exact),
57
+ }),
58
+ LabelText: (label, options = {}) => ({
59
+ describe: `accessibilityLabel ${show(label)}`,
60
+ select: (node) => matches(node.props['accessibilityLabel'], label, options.exact),
61
+ }),
62
+ PlaceholderText: (placeholder, options = {}) => ({
63
+ describe: `placeholder ${show(placeholder)}`,
64
+ select: (node) => matches(node.props['placeholder'], placeholder, options.exact),
65
+ }),
66
+ // A text field's value is its `text` prop, which is what `<text-input>` binds its model to.
67
+ DisplayValue: (value, options = {}) => ({
68
+ describe: `display value ${show(value)}`,
69
+ select: (node) => isTextInput(node) && matches(node.props['text'], value, options.exact),
70
+ }),
71
+ };
72
+ /**
73
+ * Every query, bound to a root that is looked up again on each call.
74
+ *
75
+ * `found` sees every node a query hands back, so an event fired on one later knows which render
76
+ * it came from.
77
+ */
78
+ export function bindQueries(roots, tree, found) {
79
+ const bound = {};
80
+ for (const name of Object.keys(QUERIES)) {
81
+ const all = (...args) => {
82
+ const query = QUERIES[name](...args);
83
+ const options = args[1];
84
+ const walk = options?.includeHiddenElements ? flatten : reachable;
85
+ const hits = walk(roots()).filter(query.select);
86
+ hits.forEach(found);
87
+ return hits;
88
+ };
89
+ const describe = (args) => QUERIES[name](...args).describe;
90
+ const one = (...args) => {
91
+ const hits = all(...args);
92
+ if (hits.length === 1)
93
+ return hits[0];
94
+ const problem = hits.length
95
+ ? `Found ${hits.length} nodes with ${describe(args)}, and expected one. Use getAllBy${name} if more than one is expected.`
96
+ : `Unable to find a node with ${describe(args)}.`;
97
+ throw new Error(`${problem}\n\n${tree()}`);
98
+ };
99
+ // The last argument of a `findBy` is the `waitFor` options, and it is only ever that when the
100
+ // query itself was given its options too.
101
+ const split = (args) => args.length > 2 ? [args.slice(0, 2), args[2]] : [args, undefined];
102
+ bound[`getAllBy${name}`] = (...args) => {
103
+ const hits = all(...args);
104
+ if (!hits.length)
105
+ throw new Error(`Unable to find a node with ${describe(args)}.\n\n${tree()}`);
106
+ return hits;
107
+ };
108
+ bound[`queryAllBy${name}`] = all;
109
+ bound[`getBy${name}`] = one;
110
+ bound[`queryBy${name}`] = (...args) => {
111
+ const hits = all(...args);
112
+ if (hits.length > 1)
113
+ return one(...args);
114
+ return hits[0] ?? null;
115
+ };
116
+ bound[`findBy${name}`] = (...args) => {
117
+ const [query, options] = split(args);
118
+ return waitFor(() => one(...query), options);
119
+ };
120
+ bound[`findAllBy${name}`] = (...args) => {
121
+ const [query, options] = split(args);
122
+ return waitFor(() => bound[`getAllBy${name}`](...query), options);
123
+ };
124
+ }
125
+ return bound;
126
+ }
127
+ /** The debug tree: view names, text, and the props a query can find a node by. */
128
+ export function describeTree(nodes, depth = 0) {
129
+ return nodes
130
+ .map((node) => {
131
+ const text = node.viewName === 'RawText' ? ` ${JSON.stringify(node.props['text'])}` : '';
132
+ const props = IDENTIFYING.filter((key) => node.props[key] != null && node.props[key] !== '')
133
+ .map((key) => ` ${key === 'text' ? 'value' : key}=${JSON.stringify(node.props[key])}`)
134
+ .join('');
135
+ const line = ' '.repeat(depth) + node.viewName + text + (text ? '' : props);
136
+ return node.children.length ? `${line}\n${describeTree(node.children, depth + 1)}` : line;
137
+ })
138
+ .join('\n');
139
+ }
140
+ const IDENTIFYING = [
141
+ 'testID',
142
+ 'nativeID',
143
+ 'accessibilityRole',
144
+ 'accessibilityLabel',
145
+ 'placeholder',
146
+ 'text',
147
+ ];
148
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Testing Library's queries, over the tree the renderer committed to the fake Fabric.\n *\n * There is no DOM here, so `@testing-library/dom` has nothing to query: a query reads the props a\n * native view would actually receive. That makes the names mean what React Native Testing Library\n * means by them rather than what the web does - a role is `accessibilityRole`, a label is\n * `accessibilityLabel`, text is what a `Paragraph` holds.\n *\n * Every query re-reads the most recent commit. The fake builds a fresh node object per commit, so\n * a node held across an interaction is a snapshot of the frame it was found in; query again to see\n * what changed.\n */\nimport type { FakeFabricNode } from './test-utils.ts';\nimport { waitFor, type WaitForOptions } from './wait-for.ts';\n\n/** What a query matches against: a whole string (normalised), or a RegExp tested against it. */\nexport type Matcher = string | RegExp;\n\n/** How a string `Matcher` compares. */\nexport interface TextMatchOptions {\n /** `false` matches a case-insensitive substring instead of the whole, trimmed string. */\n exact?: boolean;\n /**\n * Also find what nobody could see or reach: views under `display: none`, and those hidden from\n * accessibility. React Native Testing Library's option, and its default of `false`.\n */\n includeHiddenElements?: boolean;\n}\n\n/** `ByRole`'s options: a role alone matches every node with it, `name` narrows it to one. */\nexport interface ByRoleOptions extends TextMatchOptions {\n /** The accessible name: `accessibilityLabel`, or failing that the text inside the node. */\n name?: Matcher;\n}\n\ninterface QueryArgs {\n Role: [role: Matcher, options?: ByRoleOptions];\n Text: [text: Matcher, options?: TextMatchOptions];\n TestId: [testId: Matcher, options?: TextMatchOptions];\n LabelText: [label: Matcher, options?: TextMatchOptions];\n PlaceholderText: [placeholder: Matcher, options?: TextMatchOptions];\n DisplayValue: [value: Matcher, options?: TextMatchOptions];\n}\n\ntype Name = keyof QueryArgs;\ntype FindArgs<K extends Name> = [...QueryArgs[K], waitForOptions?: WaitForOptions];\n\n/**\n * The query matrix: `getBy`, `getAllBy`, `queryBy`, `queryAllBy`, `findBy` and `findAllBy`, for\n * each of `Role`, `Text`, `TestId`, `LabelText`, `PlaceholderText` and `DisplayValue`.\n */\nexport type BoundQueries = {\n [K in Name as `getBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode;\n} & {\n [K in Name as `getAllBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode[];\n} & {\n [K in Name as `queryBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode | null;\n} & {\n [K in Name as `queryAllBy${K}`]: (...args: QueryArgs[K]) => FakeFabricNode[];\n} & {\n [K in Name as `findBy${K}`]: (...args: FindArgs<K>) => Promise<FakeFabricNode>;\n} & {\n [K in Name as `findAllBy${K}`]: (...args: FindArgs<K>) => Promise<FakeFabricNode[]>;\n};\n\nconst normalize = (text: string): string => text.replace(/\\s+/g, ' ').trim();\n\nfunction matches(value: unknown, matcher: Matcher, exact = true): boolean {\n if (typeof value !== 'string') return false;\n const text = normalize(value);\n if (matcher instanceof RegExp) return matcher.test(text);\n const wanted = normalize(matcher);\n return exact ? text === wanted : text.toLowerCase().includes(wanted.toLowerCase());\n}\n\nconst show = (matcher: Matcher): string =>\n matcher instanceof RegExp ? String(matcher) : JSON.stringify(matcher);\n\nexport const flatten = (nodes: readonly FakeFabricNode[]): FakeFabricNode[] =>\n nodes.flatMap((node) => [node, ...flatten(node.children)]);\n\n/**\n * A view that takes itself and everything under it out of sight or out of the accessibility tree,\n * as React Native Testing Library's `isHiddenFromAccessibility` decides it.\n */\nfunction hides(node: FakeFabricNode): boolean {\n const props = node.props;\n return (\n props['display'] === 'none' ||\n props['accessibilityElementsHidden'] === true ||\n props['aria-hidden'] === true ||\n props['importantForAccessibility'] === 'no-hide-descendants'\n );\n}\n\n/** Every node someone using the app could see or reach. */\nconst reachable = (nodes: readonly FakeFabricNode[]): FakeFabricNode[] =>\n nodes.flatMap((node) => (hides(node) ? [] : [node, ...reachable(node.children)]));\n\n/** What a screen reader would read out: every `RawText` under the node, in order. */\nexport function textContent(node: FakeFabricNode): string {\n if (node.viewName === 'RawText') return String(node.props['text'] ?? '');\n return node.children.map(textContent).join('');\n}\n\n/** A text field under any of the names the platforms register it by. */\nexport const isTextInput = (node: FakeFabricNode): boolean => /TextInput/.test(node.viewName);\n\ntype Query = { describe: string; select: (node: FakeFabricNode) => boolean };\n\n/** What a screen reader reads out: the label, or failing that the text inside. */\nconst nameOf = (node: FakeFabricNode): string =>\n typeof node.props['accessibilityLabel'] === 'string'\n ? node.props['accessibilityLabel']\n : textContent(node);\n\nconst QUERIES: { [K in Name]: (...args: QueryArgs[K]) => Query } = {\n Role: (role, options = {}) => ({\n describe:\n `role ${show(role)}` + (options.name === undefined ? '' : ` and name ${show(options.name)}`),\n select: (node) =>\n matches(node.props['accessibilityRole'], role) &&\n (options.name === undefined || matches(nameOf(node), options.name, options.exact)),\n }),\n // `Paragraph` alone: a nested `<text>` commits as a `Text` span inside one, so the paragraph is\n // the whole run of text and the span is only a part of it.\n Text: (text, options = {}) => ({\n describe: `text ${show(text)}`,\n select: (node) =>\n node.viewName === 'Paragraph' && matches(textContent(node), text, options.exact),\n }),\n // Both, because the components commit both: `testID` from `testID`, `nativeID` from `nativeID`\n // or its web spelling `id`, which is what most templates in this project reach for.\n TestId: (testId, options = {}) => ({\n describe: `testID ${show(testId)}`,\n select: (node) =>\n matches(node.props['testID'], testId, options.exact) ||\n matches(node.props['nativeID'], testId, options.exact),\n }),\n LabelText: (label, options = {}) => ({\n describe: `accessibilityLabel ${show(label)}`,\n select: (node) => matches(node.props['accessibilityLabel'], label, options.exact),\n }),\n PlaceholderText: (placeholder, options = {}) => ({\n describe: `placeholder ${show(placeholder)}`,\n select: (node) => matches(node.props['placeholder'], placeholder, options.exact),\n }),\n // A text field's value is its `text` prop, which is what `<text-input>` binds its model to.\n DisplayValue: (value, options = {}) => ({\n describe: `display value ${show(value)}`,\n select: (node) => isTextInput(node) && matches(node.props['text'], value, options.exact),\n }),\n};\n\n/**\n * Every query, bound to a root that is looked up again on each call.\n *\n * `found` sees every node a query hands back, so an event fired on one later knows which render\n * it came from.\n */\nexport function bindQueries(\n roots: () => readonly FakeFabricNode[],\n tree: () => string,\n found: (node: FakeFabricNode) => void,\n): BoundQueries {\n const bound: Record<string, unknown> = {};\n\n for (const name of Object.keys(QUERIES) as Name[]) {\n const all = (...args: unknown[]): FakeFabricNode[] => {\n const query = (QUERIES[name] as (...a: unknown[]) => Query)(...args);\n const options = args[1] as TextMatchOptions | undefined;\n const walk = options?.includeHiddenElements ? flatten : reachable;\n const hits = walk(roots()).filter(query.select);\n hits.forEach(found);\n return hits;\n };\n const describe = (args: unknown[]): string =>\n (QUERIES[name] as (...a: unknown[]) => Query)(...args).describe;\n const one = (...args: unknown[]): FakeFabricNode => {\n const hits = all(...args);\n if (hits.length === 1) return hits[0]!;\n const problem = hits.length\n ? `Found ${hits.length} nodes with ${describe(args)}, and expected one. Use getAllBy${name} if more than one is expected.`\n : `Unable to find a node with ${describe(args)}.`;\n throw new Error(`${problem}\\n\\n${tree()}`);\n };\n // The last argument of a `findBy` is the `waitFor` options, and it is only ever that when the\n // query itself was given its options too.\n const split = (args: unknown[]): [unknown[], WaitForOptions | undefined] =>\n args.length > 2 ? [args.slice(0, 2), args[2] as WaitForOptions] : [args, undefined];\n\n bound[`getAllBy${name}`] = (...args: unknown[]) => {\n const hits = all(...args);\n if (!hits.length)\n throw new Error(`Unable to find a node with ${describe(args)}.\\n\\n${tree()}`);\n return hits;\n };\n bound[`queryAllBy${name}`] = all;\n bound[`getBy${name}`] = one;\n bound[`queryBy${name}`] = (...args: unknown[]) => {\n const hits = all(...args);\n if (hits.length > 1) return one(...args);\n return hits[0] ?? null;\n };\n bound[`findBy${name}`] = (...args: unknown[]) => {\n const [query, options] = split(args);\n return waitFor(() => one(...query), options);\n };\n bound[`findAllBy${name}`] = (...args: unknown[]) => {\n const [query, options] = split(args);\n return waitFor(() => (bound[`getAllBy${name}`] as typeof all)(...query), options);\n };\n }\n\n return bound as BoundQueries;\n}\n\n/** The debug tree: view names, text, and the props a query can find a node by. */\nexport function describeTree(nodes: readonly FakeFabricNode[], depth = 0): string {\n return nodes\n .map((node) => {\n const text = node.viewName === 'RawText' ? ` ${JSON.stringify(node.props['text'])}` : '';\n const props = IDENTIFYING.filter((key) => node.props[key] != null && node.props[key] !== '')\n .map((key) => ` ${key === 'text' ? 'value' : key}=${JSON.stringify(node.props[key])}`)\n .join('');\n const line = ' '.repeat(depth) + node.viewName + text + (text ? '' : props);\n return node.children.length ? `${line}\\n${describeTree(node.children, depth + 1)}` : line;\n })\n .join('\\n');\n}\n\nconst IDENTIFYING = [\n 'testID',\n 'nativeID',\n 'accessibilityRole',\n 'accessibilityLabel',\n 'placeholder',\n 'text',\n];\n"]}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * What a test gets for `react-native-reanimated`, whose source Node cannot load.
3
+ *
4
+ * An animation finishes the moment it is assigned: `withTiming(100)` is `100`, and its callback
5
+ * is called with `true` at once, so a test sees where an animation ends and what ends it does.
6
+ * `interpolate` and `Extrapolation` are the library's own arithmetic; `Easing` curves are the
7
+ * identity, since nothing in a test is between its ends.
8
+ *
9
+ * ponytail: the functions apps call from components, not the React hooks; add one when an app
10
+ * under test imports it.
11
+ */
12
+ import { sharedValue } from './reanimated.ts';
13
+ export type { SharedValue } from './reanimated.ts';
14
+ type Done = ((finished?: boolean) => void) | undefined;
15
+ export declare const makeMutable: typeof sharedValue;
16
+ export declare function withTiming<T>(to: T, _config?: object, callback?: Done): T;
17
+ export declare function withSpring<T>(to: T, _config?: object, callback?: Done): T;
18
+ export declare function withDecay(_config: {
19
+ velocity?: number;
20
+ }, callback?: Done): number;
21
+ export declare function withDelay<T>(_delay: number, animation: T): T;
22
+ export declare function withSequence<T>(...animations: T[]): T;
23
+ export declare function withRepeat<T>(animation: T, _times?: number, _reverse?: boolean, callback?: Done): T;
24
+ export declare function cancelAnimation(_value: unknown): void;
25
+ /** What `@ng-native/components/reanimated` itself starts a style with; nothing runs it here. */
26
+ export declare function startMapper(_worklet: () => void, _inputs?: unknown[]): number;
27
+ export declare function stopMapper(_id: number): void;
28
+ export declare const Extrapolation: {
29
+ readonly IDENTITY: "identity";
30
+ readonly CLAMP: "clamp";
31
+ readonly EXTEND: "extend";
32
+ };
33
+ type Extrapolate = (typeof Extrapolation)[keyof typeof Extrapolation];
34
+ export declare function interpolate(x: number, input: readonly number[], output: readonly number[], extrapolate?: Extrapolate | {
35
+ extrapolateLeft?: Extrapolate;
36
+ extrapolateRight?: Extrapolate;
37
+ }): number;
38
+ export declare const Easing: {
39
+ linear: (t: number) => number;
40
+ ease: (t: number) => number;
41
+ quad: (t: number) => number;
42
+ cubic: (t: number) => number;
43
+ sin: (t: number) => number;
44
+ circle: (t: number) => number;
45
+ exp: (t: number) => number;
46
+ bounce: (t: number) => number;
47
+ poly: () => (t: number) => number;
48
+ elastic: () => (t: number) => number;
49
+ back: () => (t: number) => number;
50
+ bezier: () => (t: number) => number;
51
+ bezierFn: () => (t: number) => number;
52
+ in: (easing: (t: number) => number) => (t: number) => number;
53
+ out: (easing: (t: number) => number) => (t: number) => number;
54
+ inOut: (easing: (t: number) => number) => (t: number) => number;
55
+ };
56
+ export declare const ReduceMotion: {
57
+ readonly System: "system";
58
+ readonly Always: "always";
59
+ readonly Never: "never";
60
+ };
61
+ export declare function runOnJS<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
62
+ export declare function runOnUI<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * What a test gets for `react-native-reanimated`, whose source Node cannot load.
3
+ *
4
+ * An animation finishes the moment it is assigned: `withTiming(100)` is `100`, and its callback
5
+ * is called with `true` at once, so a test sees where an animation ends and what ends it does.
6
+ * `interpolate` and `Extrapolation` are the library's own arithmetic; `Easing` curves are the
7
+ * identity, since nothing in a test is between its ends.
8
+ *
9
+ * ponytail: the functions apps call from components, not the React hooks; add one when an app
10
+ * under test imports it.
11
+ */
12
+ import { sharedValue } from "./reanimated.js";
13
+ const settle = (to, callback) => {
14
+ callback?.(true);
15
+ return to;
16
+ };
17
+ export const makeMutable = sharedValue;
18
+ export function withTiming(to, _config, callback) {
19
+ return settle(to, callback);
20
+ }
21
+ export function withSpring(to, _config, callback) {
22
+ return settle(to, callback);
23
+ }
24
+ export function withDecay(_config, callback) {
25
+ return settle(0, callback);
26
+ }
27
+ export function withDelay(_delay, animation) {
28
+ return animation;
29
+ }
30
+ export function withSequence(...animations) {
31
+ return animations[animations.length - 1];
32
+ }
33
+ export function withRepeat(animation, _times, _reverse, callback) {
34
+ return settle(animation, callback);
35
+ }
36
+ export function cancelAnimation(_value) { }
37
+ /** What `@ng-native/components/reanimated` itself starts a style with; nothing runs it here. */
38
+ export function startMapper(_worklet, _inputs) {
39
+ return 0;
40
+ }
41
+ export function stopMapper(_id) { }
42
+ export const Extrapolation = { IDENTITY: 'identity', CLAMP: 'clamp', EXTEND: 'extend' };
43
+ /** Past an end of the input range: the end's output, the input itself, or `undefined` to extend. */
44
+ function beyond(x, output, how) {
45
+ if (how === 'clamp')
46
+ return output;
47
+ if (how === 'identity')
48
+ return x;
49
+ return undefined;
50
+ }
51
+ export function interpolate(x, input, output, extrapolate) {
52
+ const left = typeof extrapolate === 'object' ? extrapolate.extrapolateLeft : extrapolate;
53
+ const right = typeof extrapolate === 'object' ? extrapolate.extrapolateRight : extrapolate;
54
+ const last = input.length - 1;
55
+ const outside = x < input[0]
56
+ ? beyond(x, output[0], left)
57
+ : x > input[last]
58
+ ? beyond(x, output[last], right)
59
+ : undefined;
60
+ if (outside !== undefined)
61
+ return outside;
62
+ let i = 0;
63
+ while (i < last - 1 && x > input[i + 1])
64
+ i++;
65
+ const [a, b, p, q] = [input[i], input[i + 1], output[i], output[i + 1]];
66
+ return b === a ? p : p + ((x - a) / (b - a)) * (q - p);
67
+ }
68
+ const identity = (t) => t;
69
+ const curve = () => identity;
70
+ export const Easing = {
71
+ linear: identity,
72
+ ease: identity,
73
+ quad: identity,
74
+ cubic: identity,
75
+ sin: identity,
76
+ circle: identity,
77
+ exp: identity,
78
+ bounce: identity,
79
+ poly: curve,
80
+ elastic: curve,
81
+ back: curve,
82
+ bezier: curve,
83
+ bezierFn: curve,
84
+ in: (easing) => easing,
85
+ out: (easing) => easing,
86
+ inOut: (easing) => easing,
87
+ };
88
+ export const ReduceMotion = { System: 'system', Always: 'always', Never: 'never' };
89
+ export function runOnJS(fn) {
90
+ return fn;
91
+ }
92
+ export function runOnUI(fn) {
93
+ return fn;
94
+ }
95
+ //# sourceMappingURL=reanimated-library.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reanimated-library.js","sourceRoot":"","sources":["../src/reanimated-library.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a test gets for `react-native-reanimated`, whose source Node cannot load.\n *\n * An animation finishes the moment it is assigned: `withTiming(100)` is `100`, and its callback\n * is called with `true` at once, so a test sees where an animation ends and what ends it does.\n * `interpolate` and `Extrapolation` are the library's own arithmetic; `Easing` curves are the\n * identity, since nothing in a test is between its ends.\n *\n * ponytail: the functions apps call from components, not the React hooks; add one when an app\n * under test imports it.\n */\nimport { sharedValue } from './reanimated.ts';\n\nexport type { SharedValue } from './reanimated.ts';\n\ntype Done = ((finished?: boolean) => void) | undefined;\n\nconst settle = <T>(to: T, callback: Done): T => {\n callback?.(true);\n return to;\n};\n\nexport const makeMutable = sharedValue;\n\nexport function withTiming<T>(to: T, _config?: object, callback?: Done): T {\n return settle(to, callback);\n}\n\nexport function withSpring<T>(to: T, _config?: object, callback?: Done): T {\n return settle(to, callback);\n}\n\nexport function withDecay(_config: { velocity?: number }, callback?: Done): number {\n return settle(0, callback);\n}\n\nexport function withDelay<T>(_delay: number, animation: T): T {\n return animation;\n}\n\nexport function withSequence<T>(...animations: T[]): T {\n return animations[animations.length - 1]!;\n}\n\nexport function withRepeat<T>(\n animation: T,\n _times?: number,\n _reverse?: boolean,\n callback?: Done,\n): T {\n return settle(animation, callback);\n}\n\nexport function cancelAnimation(_value: unknown): void {}\n\n/** What `@ng-native/components/reanimated` itself starts a style with; nothing runs it here. */\nexport function startMapper(_worklet: () => void, _inputs?: unknown[]): number {\n return 0;\n}\n\nexport function stopMapper(_id: number): void {}\n\nexport const Extrapolation = { IDENTITY: 'identity', CLAMP: 'clamp', EXTEND: 'extend' } as const;\ntype Extrapolate = (typeof Extrapolation)[keyof typeof Extrapolation];\n\n/** Past an end of the input range: the end's output, the input itself, or `undefined` to extend. */\nfunction beyond(x: number, output: number, how: Extrapolate | undefined): number | undefined {\n if (how === 'clamp') return output;\n if (how === 'identity') return x;\n return undefined;\n}\n\nexport function interpolate(\n x: number,\n input: readonly number[],\n output: readonly number[],\n extrapolate?: Extrapolate | { extrapolateLeft?: Extrapolate; extrapolateRight?: Extrapolate },\n): number {\n const left = typeof extrapolate === 'object' ? extrapolate.extrapolateLeft : extrapolate;\n const right = typeof extrapolate === 'object' ? extrapolate.extrapolateRight : extrapolate;\n const last = input.length - 1;\n const outside =\n x < input[0]!\n ? beyond(x, output[0]!, left)\n : x > input[last]!\n ? beyond(x, output[last]!, right)\n : undefined;\n if (outside !== undefined) return outside;\n let i = 0;\n while (i < last - 1 && x > input[i + 1]!) i++;\n const [a, b, p, q] = [input[i]!, input[i + 1]!, output[i]!, output[i + 1]!];\n return b === a ? p : p + ((x - a) / (b - a)) * (q - p);\n}\n\nconst identity = (t: number) => t;\nconst curve = () => identity;\nexport const Easing = {\n linear: identity,\n ease: identity,\n quad: identity,\n cubic: identity,\n sin: identity,\n circle: identity,\n exp: identity,\n bounce: identity,\n poly: curve,\n elastic: curve,\n back: curve,\n bezier: curve,\n bezierFn: curve,\n in: (easing: (t: number) => number) => easing,\n out: (easing: (t: number) => number) => easing,\n inOut: (easing: (t: number) => number) => easing,\n};\n\nexport const ReduceMotion = { System: 'system', Always: 'always', Never: 'never' } as const;\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"]}
@@ -0,0 +1,30 @@
1
+ import * as i0 from "@angular/core";
2
+ /** A value both runtimes can see; here there is one runtime, and it is a signal. */
3
+ export interface SharedValue<T = number> {
4
+ value: T;
5
+ get(): T;
6
+ set(value: T | ((current: T) => T)): void;
7
+ }
8
+ export interface WorkletStyleSpec {
9
+ readonly values: readonly unknown[];
10
+ updater(...values: readonly unknown[]): Record<string, unknown>;
11
+ }
12
+ export interface WorkletScrollSpec {
13
+ readonly values: readonly unknown[];
14
+ handler(event: never, ...values: readonly unknown[]): void;
15
+ }
16
+ export declare function sharedValue<T>(initial: T): SharedValue<T>;
17
+ export declare function workletStyle<const T extends readonly SharedValue<unknown>[]>(values: T, updater: (...values: T) => Record<string, unknown>): WorkletStyleSpec;
18
+ export declare function workletScroll<const T extends readonly SharedValue<unknown>[]>(values: T, handler: (event: never, ...values: T) => void): WorkletScrollSpec;
19
+ export declare class WorkletStyle {
20
+ readonly workletStyle: import("@angular/core").InputSignal<WorkletStyleSpec>;
21
+ constructor();
22
+ static ɵfac: i0.ɵɵFactoryDeclaration<WorkletStyle, never>;
23
+ static ɵdir: i0.ɵɵDirectiveDeclaration<WorkletStyle, "[workletStyle]", never, { "workletStyle": { "alias": "workletStyle"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
24
+ }
25
+ export declare class WorkletScroll {
26
+ readonly workletScroll: import("@angular/core").InputSignal<WorkletScrollSpec>;
27
+ constructor();
28
+ static ɵfac: i0.ɵɵFactoryDeclaration<WorkletScroll, never>;
29
+ static ɵdir: i0.ɵɵDirectiveDeclaration<WorkletScroll, "[workletScroll]", never, { "workletScroll": { "alias": "workletScroll"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
30
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * What a test gets for `@ng-native/components/reanimated`.
3
+ *
4
+ * The real entry point reaches Reanimated's and react-native-worklets' source, which Node cannot
5
+ * load, so a test of any component with a `[workletStyle]` or a `[workletScroll]` failed to import
6
+ * before it ran. `ngNative()` resolves the entry point here instead, with the same selectors,
7
+ * inputs and factories, and the one runtime a test has standing in for the UI thread's.
8
+ *
9
+ * A shared value is a signal. A `[workletStyle]` writes what its worklet returns onto its view,
10
+ * again whenever a value it read changes; a `[workletScroll]` runs its worklet for every scroll
11
+ * event the view emits. So a test sees the style an animation settles on, not the frames between:
12
+ * `react-native-reanimated`'s stand-in finishes every animation at once.
13
+ */
14
+ import { Directive, ElementRef, Renderer2, effect, inject, input, signal } from '@angular/core';
15
+ import { HostEngine } from '@ng-native/fabric';
16
+ import * as i0 from "@angular/core";
17
+ export function sharedValue(initial) {
18
+ const state = signal(initial, /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
20
+ return {
21
+ get value() {
22
+ return state();
23
+ },
24
+ set value(next) {
25
+ state.set(next);
26
+ },
27
+ get: () => state(),
28
+ set: (next) => state.set(typeof next === 'function' ? next(state()) : next),
29
+ };
30
+ }
31
+ export function workletStyle(values, updater) {
32
+ return { values, updater };
33
+ }
34
+ export function workletScroll(values, handler) {
35
+ return { values, handler };
36
+ }
37
+ export class WorkletStyle {
38
+ workletStyle = input.required(/* @ts-ignore */
39
+ ...(ngDevMode ? [{ debugName: "workletStyle" }] : /* istanbul ignore next */ []));
40
+ constructor() {
41
+ const node = inject(ElementRef).nativeElement;
42
+ const renderer = inject(Renderer2);
43
+ let written = [];
44
+ effect(() => {
45
+ const { values, updater } = this.workletStyle();
46
+ const style = updater(...values);
47
+ for (const key of written)
48
+ if (!(key in style))
49
+ renderer.setProperty(node, key, undefined);
50
+ for (const [key, value] of Object.entries(style))
51
+ renderer.setProperty(node, key, value);
52
+ written = Object.keys(style);
53
+ });
54
+ }
55
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: WorkletStyle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
56
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.0", type: WorkletStyle, isStandalone: true, selector: "[workletStyle]", inputs: { workletStyle: { classPropertyName: "workletStyle", publicName: "workletStyle", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
57
+ }
58
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: WorkletStyle, decorators: [{
59
+ type: Directive,
60
+ args: [{ selector: '[workletStyle]' }]
61
+ }], ctorParameters: () => [], propDecorators: { workletStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "workletStyle", required: true }] }] } });
62
+ export class WorkletScroll {
63
+ workletScroll = input.required(/* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "workletScroll" }] : /* istanbul ignore next */ []));
65
+ constructor() {
66
+ const node = inject(ElementRef).nativeElement;
67
+ const engine = inject(HostEngine);
68
+ effect((onCleanup) => {
69
+ const { values, handler } = this.workletScroll();
70
+ onCleanup(engine.setEventListener(node, 'topScroll', (event) => handler(event.nativeEvent, ...values)));
71
+ });
72
+ }
73
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: WorkletScroll, deps: [], target: i0.ɵɵFactoryTarget.Directive });
74
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.0", type: WorkletScroll, isStandalone: true, selector: "[workletScroll]", inputs: { workletScroll: { classPropertyName: "workletScroll", publicName: "workletScroll", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
75
+ }
76
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: WorkletScroll, decorators: [{
77
+ type: Directive,
78
+ args: [{ selector: '[workletScroll]' }]
79
+ }], ctorParameters: () => [], propDecorators: { workletScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "workletScroll", required: true }] }] } });
80
+ //# sourceMappingURL=reanimated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reanimated.js","sourceRoot":"","sources":["../src/reanimated.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a test gets for `@ng-native/components/reanimated`.\n *\n * The real entry point reaches Reanimated's and react-native-worklets' source, which Node cannot\n * load, so a test of any component with a `[workletStyle]` or a `[workletScroll]` failed to import\n * before it ran. `ngNative()` resolves the entry point here instead, with the same selectors,\n * inputs and factories, and the one runtime a test has standing in for the UI thread's.\n *\n * A shared value is a signal. A `[workletStyle]` writes what its worklet returns onto its view,\n * again whenever a value it read changes; a `[workletScroll]` runs its worklet for every scroll\n * event the view emits. So a test sees the style an animation settles on, not the frames between:\n * `react-native-reanimated`'s stand-in finishes every animation at once.\n */\nimport { Directive, ElementRef, Renderer2, effect, inject, input, signal } from '@angular/core';\nimport { HostEngine, type HostNode } from '@ng-native/fabric';\n\n/** A value both runtimes can see; here there is one runtime, and it is a signal. */\nexport interface SharedValue<T = number> {\n value: T;\n get(): T;\n set(value: T | ((current: T) => T)): void;\n}\n\nexport interface WorkletStyleSpec {\n readonly values: readonly unknown[];\n updater(...values: readonly unknown[]): Record<string, unknown>;\n}\n\nexport interface WorkletScrollSpec {\n readonly values: readonly unknown[];\n handler(event: never, ...values: readonly unknown[]): void;\n}\n\nexport function sharedValue<T>(initial: T): SharedValue<T> {\n const state = signal(initial);\n return {\n get value() {\n return state();\n },\n set value(next: T) {\n state.set(next);\n },\n get: () => state(),\n set: (next) =>\n state.set(typeof next === 'function' ? (next as (current: T) => T)(state()) : next),\n };\n}\n\nexport function workletStyle<const T extends readonly SharedValue<unknown>[]>(\n values: T,\n updater: (...values: T) => Record<string, unknown>,\n): WorkletStyleSpec {\n return { values, updater } as WorkletStyleSpec;\n}\n\nexport function workletScroll<const T extends readonly SharedValue<unknown>[]>(\n values: T,\n handler: (event: never, ...values: T) => void,\n): WorkletScrollSpec {\n return { values, handler } as WorkletScrollSpec;\n}\n\n@Directive({ selector: '[workletStyle]' })\nexport class WorkletStyle {\n readonly workletStyle = input.required<WorkletStyleSpec>();\n\n constructor() {\n const node = inject(ElementRef).nativeElement as HostNode;\n const renderer = inject(Renderer2);\n let written: readonly string[] = [];\n effect(() => {\n const { values, updater } = this.workletStyle();\n const style = updater(...values);\n for (const key of written) if (!(key in style)) renderer.setProperty(node, key, undefined);\n for (const [key, value] of Object.entries(style)) renderer.setProperty(node, key, value);\n written = Object.keys(style);\n });\n }\n}\n\n@Directive({ selector: '[workletScroll]' })\nexport class WorkletScroll {\n readonly workletScroll = input.required<WorkletScrollSpec>();\n\n constructor() {\n const node = inject(ElementRef).nativeElement as HostNode;\n const engine = inject(HostEngine);\n effect((onCleanup) => {\n const { values, handler } = this.workletScroll();\n onCleanup(\n engine.setEventListener(node, 'topScroll', (event) =>\n handler((event as { nativeEvent: never }).nativeEvent, ...values),\n ),\n );\n });\n }\n}\n"]}