@sigx/lynx-testing 0.2.6 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mt/index.js CHANGED
@@ -1,114 +1,241 @@
1
- import { n as e, t } from "../setup-BUzWSuVx.js";
2
- import { transformReactLynxSync as n } from "@lynx-js/react/transform";
3
- //#region src/mt/index.ts
4
- function r(e, t = 1) {
5
- return {
6
- current: e,
7
- _wvid: t
8
- };
1
+ /**
2
+ * Public JS API for the main-thread (MT) gesture/worklet test harness.
3
+ *
4
+ * Pair with `@sigx/lynx-testing/mt/setup` in your vitest setupFiles. See
5
+ * the package README for a full example.
6
+ *
7
+ * The harness lets you write tests that:
8
+ * 1. Compile a `.tsx` source file with `transformReactLynxSync`
9
+ * (target: `LEPUS`) to extract the `registerWorkletInternal(...)`
10
+ * calls the SWC transform emits for `'main thread'` worklets.
11
+ * 2. Eval those registrations into the upstream worklet runtime that
12
+ * `setup.ts` boots, populating `lynxWorkletImpl._workletMap`.
13
+ * 3. Call each worklet directly with a fabricated `_c` capture and a
14
+ * synthetic gesture event, asserting the worklet's MT-side state
15
+ * mutations and any `setStyleProperties` / `runOnBackground` calls.
16
+ *
17
+ * Use `compileMTWorklets()` for the common case (one source file, get
18
+ * the registered worklets back). Drop down to `extractRegistrations()`
19
+ * if you need finer control.
20
+ */
21
+ import { transformReactLynxSync } from '@lynx-js/react/transform';
22
+ import { _getJsContext, _resetJsContextSpy } from './setup.js';
23
+ /**
24
+ * Synthetic `MainThreadRef` shape — `{ current, _wvid }`. Worklets read
25
+ * `ref.current.value` and may mutate `ref.current.value`.
26
+ */
27
+ export function makeRef(current, id = 1) {
28
+ return { current, _wvid: id };
9
29
  }
10
- function i(e) {
11
- let t = {
12
- timestamp: Date.now(),
13
- type: "touchmove",
14
- x: e.pageX,
15
- y: e.pageY ?? 0,
16
- pageX: e.pageX,
17
- pageY: e.pageY ?? 0,
18
- clientX: e.pageX,
19
- clientY: e.pageY ?? 0,
20
- scrollX: 0,
21
- scrollY: 0,
22
- isAtStart: 0,
23
- isAtEnd: 0
24
- };
25
- return {
26
- type: "onUpdate",
27
- timestamp: Date.now(),
28
- currentTarget: { element: null },
29
- target: { element: null },
30
- params: t,
31
- detail: t
32
- };
30
+ /**
31
+ * Fabricate a Lynx pan-gesture event payload as the iOS arena would
32
+ * deliver it. The payload is nested under `e.params` (NOT top-level on
33
+ * `e`) — your worklet should read pageX/pageY from `e.params.pageX`,
34
+ * mirroring real device behaviour.
35
+ *
36
+ * Verified against `LynxBaseGestureHandler.m::eventParamsFromTouchEvent`
37
+ * + `LynxPanGestureHandler.m::eventParamsInActive` on iOS Lynx 3.5.
38
+ */
39
+ export function fabricatePanEvent(opts) {
40
+ const params = {
41
+ timestamp: Date.now(),
42
+ type: 'touchmove',
43
+ x: opts.pageX,
44
+ y: opts.pageY ?? 0,
45
+ pageX: opts.pageX,
46
+ pageY: opts.pageY ?? 0,
47
+ clientX: opts.pageX,
48
+ clientY: opts.pageY ?? 0,
49
+ scrollX: 0,
50
+ scrollY: 0,
51
+ isAtStart: 0,
52
+ isAtEnd: 0,
53
+ };
54
+ return {
55
+ type: 'onUpdate',
56
+ timestamp: Date.now(),
57
+ currentTarget: { element: null },
58
+ target: { element: null },
59
+ params,
60
+ detail: params,
61
+ };
33
62
  }
34
- function a(e = {}) {
35
- let t = {
36
- timestamp: Date.now(),
37
- type: "touchend",
38
- x: e.pageX ?? 0,
39
- y: e.pageY ?? 0,
40
- pageX: e.pageX ?? 0,
41
- pageY: e.pageY ?? 0,
42
- clientX: e.pageX ?? 0,
43
- clientY: e.pageY ?? 0
44
- };
45
- return {
46
- type: "onStart",
47
- timestamp: Date.now(),
48
- currentTarget: { element: null },
49
- target: { element: null },
50
- params: t,
51
- detail: t
52
- };
63
+ /**
64
+ * Fabricate a Lynx tap-gesture event payload. Same shape as the pan event
65
+ * minus the scroll-related fields the pan handler adds; tests rarely read
66
+ * either, so the difference is mostly cosmetic.
67
+ */
68
+ export function fabricateTapEvent(opts = {}) {
69
+ const params = {
70
+ timestamp: Date.now(),
71
+ type: 'touchend',
72
+ x: opts.pageX ?? 0,
73
+ y: opts.pageY ?? 0,
74
+ pageX: opts.pageX ?? 0,
75
+ pageY: opts.pageY ?? 0,
76
+ clientX: opts.pageX ?? 0,
77
+ clientY: opts.pageY ?? 0,
78
+ };
79
+ return {
80
+ type: 'onStart',
81
+ timestamp: Date.now(),
82
+ currentTarget: { element: null },
83
+ target: { element: null },
84
+ params,
85
+ detail: params,
86
+ };
53
87
  }
54
- function o(e) {
55
- let t = [], n = 0;
56
- for (;;) {
57
- let r = e.indexOf("registerWorkletInternal(", n);
58
- if (r === -1) break;
59
- let i = 0, a = r + 24 - 1;
60
- for (; a < e.length; a++) {
61
- let t = e[a];
62
- if (t === "(") i++;
63
- else if (t === ")" && (i--, i === 0)) break;
64
- }
65
- let o = a + 1;
66
- o < e.length && e[o] === ";" && o++, t.push(e.slice(r, o)), n = o;
67
- }
68
- return t.join("\n");
88
+ /**
89
+ * Extract `registerWorkletInternal(...)` calls from a LEPUS-target
90
+ * transform output. Bracket-depth counting handles nested braces in the
91
+ * function body. Mirror of the lynx-plugin internal so tests don't pull
92
+ * a build-time package in as a runtime dep.
93
+ */
94
+ export function extractRegistrations(lepusCode) {
95
+ const out = [];
96
+ const marker = 'registerWorkletInternal(';
97
+ let from = 0;
98
+ while (true) {
99
+ const idx = lepusCode.indexOf(marker, from);
100
+ if (idx === -1)
101
+ break;
102
+ let depth = 0;
103
+ let i = idx + marker.length - 1;
104
+ for (; i < lepusCode.length; i++) {
105
+ const ch = lepusCode[i];
106
+ if (ch === '(')
107
+ depth++;
108
+ else if (ch === ')') {
109
+ depth--;
110
+ if (depth === 0)
111
+ break;
112
+ }
113
+ }
114
+ let end = i + 1;
115
+ if (end < lepusCode.length && lepusCode[end] === ';')
116
+ end++;
117
+ out.push(lepusCode.slice(idx, end));
118
+ from = end;
119
+ }
120
+ return out.join('\n');
69
121
  }
70
- function s() {
71
- let e = globalThis.lynxWorkletImpl;
72
- if (!e) throw Error("[lynx-testing/mt] lynxWorkletImpl is not initialized — add `@sigx/lynx-testing/mt/setup` to your vitest config's `setupFiles` array.");
73
- return e._workletMap;
122
+ /**
123
+ * Get the live `lynxWorkletImpl._workletMap` populated by the upstream
124
+ * worklet runtime that `mt/setup.ts` bootstrapped. Each entry is a
125
+ * `_wkltId` → callable mapping. After `compileMTWorklets()` evals new
126
+ * registrations, this map will include them.
127
+ *
128
+ * Throws if `mt/setup.ts` didn't run — typically because the consumer
129
+ * forgot to add it to `setupFiles`.
130
+ */
131
+ export function getWorkletMap() {
132
+ const impl = globalThis.lynxWorkletImpl;
133
+ if (!impl) {
134
+ throw new Error('[lynx-testing/mt] lynxWorkletImpl is not initialized — add ' +
135
+ '`@sigx/lynx-testing/mt/setup` to your vitest config\'s ' +
136
+ '`setupFiles` array.');
137
+ }
138
+ return impl._workletMap;
74
139
  }
75
- function c(e) {
76
- let { filename: t, source: r, runtimePkg: i = "@sigx/lynx-runtime-main" } = e, a = n(r, {
77
- pluginName: "sigx:test",
78
- filename: t,
79
- sourcemap: !1,
80
- cssScope: !1,
81
- shake: !1,
82
- compat: !1,
83
- refresh: !1,
84
- defineDCE: !1,
85
- directiveDCE: !1,
86
- snapshot: !1,
87
- worklet: {
88
- target: "LEPUS",
89
- filename: t,
90
- runtimePkg: i
91
- }
92
- });
93
- if (a.errors && a.errors.length > 0) throw Error("[lynx-testing/mt] LEPUS transform errors for " + t + ":\n" + a.errors.map((e) => " - " + (e.text ?? "<unknown>")).join("\n"));
94
- let c = o(a.code);
95
- Function(c)();
96
- let l = /registerWorkletInternal\(\s*"main-thread"\s*,\s*"([^"]+)"/g, u = [], d;
97
- for (; (d = l.exec(c)) !== null;) u.push(d[1]);
98
- let f = s();
99
- return u.map((e) => {
100
- let t = f[e];
101
- if (!t) throw Error("[lynx-testing/mt] worklet `" + e + "` was registered by the compile but is missing from lynxWorkletImpl._workletMap. The upstream worklet runtime may not have evaluated correctly.");
102
- return t;
103
- });
140
+ /**
141
+ * Compile a `.tsx` source file as a LEPUS worklet bundle, eval the
142
+ * resulting `registerWorkletInternal(...)` calls into the live runtime,
143
+ * and return the worklets that were just registered (in source order).
144
+ *
145
+ * The returned array indexes into `lynxWorkletImpl._workletMap`'s newest
146
+ * entries — i.e. `result[0]` is the first worklet registered by this
147
+ * compile. For most tests this is enough; for cross-test sharing or
148
+ * per-`_wkltId` access, fall back to `getWorkletMap()`.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import { readFileSync } from 'fs';
153
+ * import { compileMTWorklets, fabricatePanEvent, makeRef } from '@sigx/lynx-testing/mt';
154
+ *
155
+ * const SRC = path.resolve(__dirname, '../../src/components/Draggable.tsx');
156
+ * const worklets = compileMTWorklets({
157
+ * filename: SRC,
158
+ * source: readFileSync(SRC, 'utf8'),
159
+ * });
160
+ * // Source-order: onBegin (:1), onStart (:2), onUpdate (:3), onEnd (:4)
161
+ * const onUpdate = worklets[2]!;
162
+ *
163
+ * const drag = { startPageX: 100, startPageY: 50, ... };
164
+ * onUpdate.call({ _c: { drag: makeRef(drag, 1), ... } }, fabricatePanEvent({ pageX: 130, pageY: 55 }));
165
+ * expect(drag.startPageX).toBe(100);
166
+ * ```
167
+ */
168
+ export function compileMTWorklets(opts) {
169
+ const { filename, source, runtimePkg = '@sigx/lynx-runtime-main' } = opts;
170
+ const result = transformReactLynxSync(source, {
171
+ pluginName: 'sigx:test',
172
+ filename,
173
+ sourcemap: false,
174
+ cssScope: false,
175
+ shake: false,
176
+ compat: false,
177
+ refresh: false,
178
+ defineDCE: false,
179
+ directiveDCE: false,
180
+ snapshot: false,
181
+ worklet: { target: 'LEPUS', filename, runtimePkg },
182
+ });
183
+ if (result.errors && result.errors.length > 0) {
184
+ throw new Error('[lynx-testing/mt] LEPUS transform errors for ' + filename + ':\n' +
185
+ result.errors.map((e) => ' - ' + (e.text ?? '<unknown>')).join('\n'));
186
+ }
187
+ // Eval the registrations against `globalThis.registerWorkletInternal`
188
+ // (installed by `setup.ts`). SWC produces deterministic `_wkltId`s
189
+ // from the source content hash + index, so re-compiling the same
190
+ // source overwrites the same map entries — we can't diff by
191
+ // map-presence to find what this call registered. Instead parse the
192
+ // IDs directly out of the registration source.
193
+ const registrations = extractRegistrations(result.code);
194
+ new Function(registrations)();
195
+ // Each registration looks like:
196
+ // registerWorkletInternal("main-thread", "<wkltId>", function(...) {...});
197
+ // Extract the wkltId from the second string literal in source order.
198
+ const idRegex = /registerWorkletInternal\(\s*"main-thread"\s*,\s*"([^"]+)"/g;
199
+ const ids = [];
200
+ let m;
201
+ while ((m = idRegex.exec(registrations)) !== null) {
202
+ ids.push(m[1]);
203
+ }
204
+ const map = getWorkletMap();
205
+ return ids.map(id => {
206
+ const fn = map[id];
207
+ if (!fn) {
208
+ throw new Error('[lynx-testing/mt] worklet `' + id + '` was registered by the ' +
209
+ 'compile but is missing from lynxWorkletImpl._workletMap. The ' +
210
+ 'upstream worklet runtime may not have evaluated correctly.');
211
+ }
212
+ return fn;
213
+ });
104
214
  }
105
- function l() {
106
- return t();
215
+ /**
216
+ * Read the JS-context spy that `mt/setup.ts` installed on the lynx mock.
217
+ * Useful for asserting `runOnBackground` / `Lynx.Sigx.AvPublish` event
218
+ * dispatches from within a worklet.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * import { getJsContext } from '@sigx/lynx-testing/mt';
223
+ *
224
+ * onUpdate.call(ctx, fabricatePanEvent({ pageX: 130 }));
225
+ * const ctx = getJsContext();
226
+ * expect(ctx.dispatchEvent).toHaveBeenCalledWith(
227
+ * expect.objectContaining({ type: 'Lynx.Sigx.AvPublish' })
228
+ * );
229
+ * ```
230
+ */
231
+ export function getJsContext() {
232
+ return _getJsContext();
107
233
  }
108
- function u() {
109
- e();
234
+ /**
235
+ * Wipe the JS-context spy between tests so dispatchEvent / addEventListener
236
+ * call counts don't bleed across cases.
237
+ */
238
+ export function resetJsContextSpy() {
239
+ _resetJsContextSpy();
110
240
  }
111
- //#endregion
112
- export { c as compileMTWorklets, o as extractRegistrations, i as fabricatePanEvent, a as fabricateTapEvent, l as getJsContext, s as getWorkletMap, r as makeRef, u as resetJsContextSpy };
113
-
114
241
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/mt/index.ts"],"sourcesContent":["/**\n * Public JS API for the main-thread (MT) gesture/worklet test harness.\n *\n * Pair with `@sigx/lynx-testing/mt/setup` in your vitest setupFiles. See\n * the package README for a full example.\n *\n * The harness lets you write tests that:\n * 1. Compile a `.tsx` source file with `transformReactLynxSync`\n * (target: `LEPUS`) to extract the `registerWorkletInternal(...)`\n * calls the SWC transform emits for `'main thread'` worklets.\n * 2. Eval those registrations into the upstream worklet runtime that\n * `setup.ts` boots, populating `lynxWorkletImpl._workletMap`.\n * 3. Call each worklet directly with a fabricated `_c` capture and a\n * synthetic gesture event, asserting the worklet's MT-side state\n * mutations and any `setStyleProperties` / `runOnBackground` calls.\n *\n * Use `compileMTWorklets()` for the common case (one source file, get\n * the registered worklets back). Drop down to `extractRegistrations()`\n * if you need finer control.\n */\n\nimport { transformReactLynxSync } from '@lynx-js/react/transform';\nimport { _getJsContext, _resetJsContextSpy } from './setup.js';\n\n/**\n * Synthetic `MainThreadRef` shape — `{ current, _wvid }`. Worklets read\n * `ref.current.value` and may mutate `ref.current.value`.\n */\nexport function makeRef<T>(current: T, id = 1): { current: T; _wvid: number } {\n return { current, _wvid: id };\n}\n\n/**\n * Fabricate a Lynx pan-gesture event payload as the iOS arena would\n * deliver it. The payload is nested under `e.params` (NOT top-level on\n * `e`) — your worklet should read pageX/pageY from `e.params.pageX`,\n * mirroring real device behaviour.\n *\n * Verified against `LynxBaseGestureHandler.m::eventParamsFromTouchEvent`\n * + `LynxPanGestureHandler.m::eventParamsInActive` on iOS Lynx 3.5.\n */\nexport function fabricatePanEvent(opts: { pageX: number; pageY?: number }): MTGestureEvent {\n const params = {\n timestamp: Date.now(),\n type: 'touchmove',\n x: opts.pageX,\n y: opts.pageY ?? 0,\n pageX: opts.pageX,\n pageY: opts.pageY ?? 0,\n clientX: opts.pageX,\n clientY: opts.pageY ?? 0,\n scrollX: 0,\n scrollY: 0,\n isAtStart: 0,\n isAtEnd: 0,\n };\n return {\n type: 'onUpdate',\n timestamp: Date.now(),\n currentTarget: { element: null },\n target: { element: null },\n params,\n detail: params,\n };\n}\n\n/**\n * Fabricate a Lynx tap-gesture event payload. Same shape as the pan event\n * minus the scroll-related fields the pan handler adds; tests rarely read\n * either, so the difference is mostly cosmetic.\n */\nexport function fabricateTapEvent(opts: { pageX?: number; pageY?: number } = {}): MTGestureEvent {\n const params = {\n timestamp: Date.now(),\n type: 'touchend',\n x: opts.pageX ?? 0,\n y: opts.pageY ?? 0,\n pageX: opts.pageX ?? 0,\n pageY: opts.pageY ?? 0,\n clientX: opts.pageX ?? 0,\n clientY: opts.pageY ?? 0,\n };\n return {\n type: 'onStart',\n timestamp: Date.now(),\n currentTarget: { element: null },\n target: { element: null },\n params,\n detail: params,\n };\n}\n\n/**\n * Common shape for the events the iOS gesture arena delivers to MT\n * worklets. Top-level keys are dispatch metadata; the actual touch data\n * lives under `params` (and a duplicate `detail`).\n */\nexport interface MTGestureEvent {\n type: string;\n timestamp: number;\n currentTarget: { element: null };\n target: { element: null };\n params: Record<string, unknown>;\n detail: Record<string, unknown>;\n}\n\n/**\n * Extract `registerWorkletInternal(...)` calls from a LEPUS-target\n * transform output. Bracket-depth counting handles nested braces in the\n * function body. Mirror of the lynx-plugin internal so tests don't pull\n * a build-time package in as a runtime dep.\n */\nexport function extractRegistrations(lepusCode: string): string {\n const out: string[] = [];\n const marker = 'registerWorkletInternal(';\n let from = 0;\n\n while (true) {\n const idx = lepusCode.indexOf(marker, from);\n if (idx === -1) break;\n\n let depth = 0;\n let i = idx + marker.length - 1;\n for (; i < lepusCode.length; i++) {\n const ch = lepusCode[i];\n if (ch === '(') depth++;\n else if (ch === ')') {\n depth--;\n if (depth === 0) break;\n }\n }\n\n let end = i + 1;\n if (end < lepusCode.length && lepusCode[end] === ';') end++;\n out.push(lepusCode.slice(idx, end));\n from = end;\n }\n\n return out.join('\\n');\n}\n\n/**\n * Get the live `lynxWorkletImpl._workletMap` populated by the upstream\n * worklet runtime that `mt/setup.ts` bootstrapped. Each entry is a\n * `_wkltId` → callable mapping. After `compileMTWorklets()` evals new\n * registrations, this map will include them.\n *\n * Throws if `mt/setup.ts` didn't run — typically because the consumer\n * forgot to add it to `setupFiles`.\n */\nexport function getWorkletMap(): Record<string, Function> {\n interface WorkletImpl {\n _workletMap: Record<string, Function>;\n }\n const impl = (globalThis as { lynxWorkletImpl?: WorkletImpl }).lynxWorkletImpl;\n if (!impl) {\n throw new Error(\n '[lynx-testing/mt] lynxWorkletImpl is not initialized — add ' +\n '`@sigx/lynx-testing/mt/setup` to your vitest config\\'s ' +\n '`setupFiles` array.'\n );\n }\n return impl._workletMap;\n}\n\n/**\n * Compile a `.tsx` source file as a LEPUS worklet bundle, eval the\n * resulting `registerWorkletInternal(...)` calls into the live runtime,\n * and return the worklets that were just registered (in source order).\n *\n * The returned array indexes into `lynxWorkletImpl._workletMap`'s newest\n * entries — i.e. `result[0]` is the first worklet registered by this\n * compile. For most tests this is enough; for cross-test sharing or\n * per-`_wkltId` access, fall back to `getWorkletMap()`.\n *\n * @example\n * ```ts\n * import { readFileSync } from 'fs';\n * import { compileMTWorklets, fabricatePanEvent, makeRef } from '@sigx/lynx-testing/mt';\n *\n * const SRC = path.resolve(__dirname, '../../src/components/Draggable.tsx');\n * const worklets = compileMTWorklets({\n * filename: SRC,\n * source: readFileSync(SRC, 'utf8'),\n * });\n * // Source-order: onBegin (:1), onStart (:2), onUpdate (:3), onEnd (:4)\n * const onUpdate = worklets[2]!;\n *\n * const drag = { startPageX: 100, startPageY: 50, ... };\n * onUpdate.call({ _c: { drag: makeRef(drag, 1), ... } }, fabricatePanEvent({ pageX: 130, pageY: 55 }));\n * expect(drag.startPageX).toBe(100);\n * ```\n */\nexport function compileMTWorklets(opts: {\n filename: string;\n source: string;\n /**\n * Override the runtime package name passed to the SWC transform.\n * Defaults to `@sigx/lynx-runtime-main`, which matches what the\n * production build uses.\n */\n runtimePkg?: string;\n}): Function[] {\n const { filename, source, runtimePkg = '@sigx/lynx-runtime-main' } = opts;\n\n const result = transformReactLynxSync(source, {\n pluginName: 'sigx:test',\n filename,\n sourcemap: false,\n cssScope: false,\n shake: false,\n compat: false,\n refresh: false,\n defineDCE: false,\n directiveDCE: false,\n snapshot: false,\n worklet: { target: 'LEPUS', filename, runtimePkg },\n });\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(\n '[lynx-testing/mt] LEPUS transform errors for ' + filename + ':\\n' +\n result.errors.map((e) => ' - ' + (e.text ?? '<unknown>')).join('\\n')\n );\n }\n\n // Eval the registrations against `globalThis.registerWorkletInternal`\n // (installed by `setup.ts`). SWC produces deterministic `_wkltId`s\n // from the source content hash + index, so re-compiling the same\n // source overwrites the same map entries — we can't diff by\n // map-presence to find what this call registered. Instead parse the\n // IDs directly out of the registration source.\n const registrations = extractRegistrations(result.code);\n new Function(registrations)();\n\n // Each registration looks like:\n // registerWorkletInternal(\"main-thread\", \"<wkltId>\", function(...) {...});\n // Extract the wkltId from the second string literal in source order.\n const idRegex = /registerWorkletInternal\\(\\s*\"main-thread\"\\s*,\\s*\"([^\"]+)\"/g;\n const ids: string[] = [];\n let m: RegExpExecArray | null;\n while ((m = idRegex.exec(registrations)) !== null) {\n ids.push(m[1]!);\n }\n\n const map = getWorkletMap();\n return ids.map(id => {\n const fn = map[id];\n if (!fn) {\n throw new Error(\n '[lynx-testing/mt] worklet `' + id + '` was registered by the ' +\n 'compile but is missing from lynxWorkletImpl._workletMap. The ' +\n 'upstream worklet runtime may not have evaluated correctly.'\n );\n }\n return fn;\n });\n}\n\n/**\n * Read the JS-context spy that `mt/setup.ts` installed on the lynx mock.\n * Useful for asserting `runOnBackground` / `Lynx.Sigx.AvPublish` event\n * dispatches from within a worklet.\n *\n * @example\n * ```ts\n * import { getJsContext } from '@sigx/lynx-testing/mt';\n *\n * onUpdate.call(ctx, fabricatePanEvent({ pageX: 130 }));\n * const ctx = getJsContext();\n * expect(ctx.dispatchEvent).toHaveBeenCalledWith(\n * expect.objectContaining({ type: 'Lynx.Sigx.AvPublish' })\n * );\n * ```\n */\nexport function getJsContext(): { addEventListener: Function; dispatchEvent: Function } {\n return _getJsContext();\n}\n\n/**\n * Wipe the JS-context spy between tests so dispatchEvent / addEventListener\n * call counts don't bleed across cases.\n */\nexport function resetJsContextSpy(): void {\n _resetJsContextSpy();\n}\n"],"mappings":";;;AA4BA,SAAgB,EAAW,GAAY,IAAK,GAAkC;CAC1E,OAAO;EAAE;EAAS,OAAO;EAAI;;AAYjC,SAAgB,EAAkB,GAAyD;CACvF,IAAM,IAAS;EACX,WAAW,KAAK,KAAK;EACrB,MAAM;EACN,GAAG,EAAK;EACR,GAAG,EAAK,SAAS;EACjB,OAAO,EAAK;EACZ,OAAO,EAAK,SAAS;EACrB,SAAS,EAAK;EACd,SAAS,EAAK,SAAS;EACvB,SAAS;EACT,SAAS;EACT,WAAW;EACX,SAAS;EACZ;CACD,OAAO;EACH,MAAM;EACN,WAAW,KAAK,KAAK;EACrB,eAAe,EAAE,SAAS,MAAM;EAChC,QAAQ,EAAE,SAAS,MAAM;EACzB;EACA,QAAQ;EACX;;AAQL,SAAgB,EAAkB,IAA2C,EAAE,EAAkB;CAC7F,IAAM,IAAS;EACX,WAAW,KAAK,KAAK;EACrB,MAAM;EACN,GAAG,EAAK,SAAS;EACjB,GAAG,EAAK,SAAS;EACjB,OAAO,EAAK,SAAS;EACrB,OAAO,EAAK,SAAS;EACrB,SAAS,EAAK,SAAS;EACvB,SAAS,EAAK,SAAS;EAC1B;CACD,OAAO;EACH,MAAM;EACN,WAAW,KAAK,KAAK;EACrB,eAAe,EAAE,SAAS,MAAM;EAChC,QAAQ,EAAE,SAAS,MAAM;EACzB;EACA,QAAQ;EACX;;AAuBL,SAAgB,EAAqB,GAA2B;CAC5D,IAAM,IAAgB,EAAE,EAEpB,IAAO;CAEX,SAAa;EACT,IAAM,IAAM,EAAU,QAAQ,4BAAQ,EAAK;EAC3C,IAAI,MAAQ,IAAI;EAEhB,IAAI,IAAQ,GACR,IAAI,IAAM,KAAgB;EAC9B,OAAO,IAAI,EAAU,QAAQ,KAAK;GAC9B,IAAM,IAAK,EAAU;GACrB,IAAI,MAAO,KAAK;QACX,IAAI,MAAO,QACZ,KACI,MAAU,IAAG;;EAIzB,IAAI,IAAM,IAAI;EAGd,AAFI,IAAM,EAAU,UAAU,EAAU,OAAS,OAAK,KACtD,EAAI,KAAK,EAAU,MAAM,GAAK,EAAI,CAAC,EACnC,IAAO;;CAGX,OAAO,EAAI,KAAK,KAAK;;AAYzB,SAAgB,IAA0C;CAItD,IAAM,IAAQ,WAAiD;CAC/D,IAAI,CAAC,GACD,MAAU,MACN,uIAGH;CAEL,OAAO,EAAK;;AA+BhB,SAAgB,EAAkB,GASnB;CACX,IAAM,EAAE,aAAU,WAAQ,gBAAa,8BAA8B,GAE/D,IAAS,EAAuB,GAAQ;EAC1C,YAAY;EACZ;EACA,WAAW;EACX,UAAU;EACV,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,cAAc;EACd,UAAU;EACV,SAAS;GAAE,QAAQ;GAAS;GAAU;GAAY;EACrD,CAAC;CAEF,IAAI,EAAO,UAAU,EAAO,OAAO,SAAS,GACxC,MAAU,MACN,kDAAkD,IAAW,QAC7D,EAAO,OAAO,KAAK,MAAM,UAAU,EAAE,QAAQ,aAAa,CAAC,KAAK,KAAK,CACxE;CASL,IAAM,IAAgB,EAAqB,EAAO,KAAK;CACvD,AAAI,SAAS,EAAc,EAAE;CAK7B,IAAM,IAAU,8DACV,IAAgB,EAAE,EACpB;CACJ,QAAQ,IAAI,EAAQ,KAAK,EAAc,MAAM,OACzC,EAAI,KAAK,EAAE,GAAI;CAGnB,IAAM,IAAM,GAAe;CAC3B,OAAO,EAAI,KAAI,MAAM;EACjB,IAAM,IAAK,EAAI;EACf,IAAI,CAAC,GACD,MAAU,MACN,gCAAgC,IAAK,kJAGxC;EAEL,OAAO;GACT;;AAmBN,SAAgB,IAAwE;CACpF,OAAO,GAAe;;AAO1B,SAAgB,IAA0B;CACtC,GAAoB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mt/index.ts"],"names":[],"mappings":"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"}
package/dist/mt/setup.js CHANGED
@@ -1,2 +1,105 @@
1
- import { n as e, t } from "../setup-BUzWSuVx.js";
2
- export { t as _getJsContext, e as _resetJsContextSpy };
1
+ /**
2
+ * Side-effect bootstrap for the main-thread (MT) gesture/worklet test
3
+ * harness. Consumers list this in their `vitest.mt.config.ts`'s
4
+ * `setupFiles` array; it must run before any test that drives a worklet
5
+ * directly.
6
+ *
7
+ * ```ts
8
+ * // vitest.mt.config.ts
9
+ * import { defineConfig } from 'vitest/config';
10
+ *
11
+ * export default defineConfig({
12
+ * test: {
13
+ * environment: 'happy-dom',
14
+ * globals: true,
15
+ * include: ['__tests__/**\/*.mt.test.ts'],
16
+ * setupFiles: ['@sigx/lynx-testing/mt/setup'],
17
+ * },
18
+ * });
19
+ * ```
20
+ *
21
+ * Order matters and is encoded here:
22
+ * 1. Stub PAPI globals (`__SetAttribute` etc.) + `globalThis.lynx` +
23
+ * `globalThis.SystemInfo`. The worklet-runtime IIFE reads
24
+ * `SystemInfo.lynxSdkVersion` at init and reassigns lynx.setTimeout /
25
+ * lynx.requestAnimationFrame onto globalThis, so both must exist
26
+ * before its IIFE evaluates.
27
+ * 2. Side-effect import `@sigx/lynx-runtime-main` — its entry-main
28
+ * module installs `sigxRunOnMT`, `runOnBackground`, etc. (no PAPI
29
+ * calls fire at module init — only inside the renderPage /
30
+ * sigxPatchUpdate handlers we never invoke from these tests).
31
+ * 3. Side-effect import `@lynx-js/react/worklet-runtime` — IIFE that
32
+ * installs `globalThis.lynxWorkletImpl`, `registerWorkletInternal`,
33
+ * `runWorklet`.
34
+ * 4. Side-effect import `@sigx/lynx-runtime-main/install-hybrid-worklet`
35
+ * — registers the hybrid dispatcher into the now-populated
36
+ * `_workletMap`.
37
+ *
38
+ * The mocks are installed once per worker. `resetJsContextSpy()` from the
39
+ * companion `@sigx/lynx-testing/mt` module wipes the dispatchEvent /
40
+ * addEventListener spies between tests when needed.
41
+ */
42
+ import { vi } from 'vitest';
43
+ let jsContext = {
44
+ addEventListener: vi.fn(),
45
+ dispatchEvent: vi.fn(),
46
+ };
47
+ const lynxMock = {
48
+ SystemInfo: { lynxSdkVersion: '3.5.0' },
49
+ getJSContext: () => jsContext,
50
+ getCoreContext: () => jsContext,
51
+ setTimeout: globalThis.setTimeout.bind(globalThis),
52
+ setInterval: globalThis.setInterval.bind(globalThis),
53
+ clearTimeout: globalThis.clearTimeout.bind(globalThis),
54
+ clearInterval: globalThis.clearInterval.bind(globalThis),
55
+ requestAnimationFrame: (cb) => globalThis.setTimeout(() => cb(Date.now()), 16),
56
+ cancelAnimationFrame: (h) => globalThis.clearTimeout(h),
57
+ querySelector: (_sel) => null,
58
+ querySelectorAll: (_sel) => [],
59
+ };
60
+ globalThis['lynx'] = lynxMock;
61
+ globalThis['SystemInfo'] = { lynxSdkVersion: '3.5.0' };
62
+ const noopPapi = vi.fn();
63
+ const papiKeys = [
64
+ '__CreatePage',
65
+ '__CreateView',
66
+ '__SetCSSId',
67
+ '__AppendElement',
68
+ '__GetElementUniqueID',
69
+ '__SetInlineStyles',
70
+ '__SetStyle',
71
+ '__AddInlineStyle',
72
+ '__SetAttribute',
73
+ '__AddEvent',
74
+ '__GetAttributeByName',
75
+ '__GetAttributeNames',
76
+ '__GetComputedStyleByKey',
77
+ '__QuerySelector',
78
+ '__QuerySelectorAll',
79
+ '__InvokeUIMethod',
80
+ '__FlushElementTree',
81
+ '__GetPageElement',
82
+ '__ElementAnimate',
83
+ ];
84
+ for (const k of papiKeys) {
85
+ globalThis[k] = noopPapi;
86
+ }
87
+ await import('@sigx/lynx-runtime-main');
88
+ await import('@lynx-js/react/worklet-runtime');
89
+ await import('@sigx/lynx-runtime-main/install-hybrid-worklet');
90
+ // Internal — exposed through `@sigx/lynx-testing/mt` so tests can read or
91
+ // reset the JS-context spy between cases without re-stubbing the global.
92
+ export function _getJsContext() {
93
+ return jsContext;
94
+ }
95
+ export function _resetJsContextSpy() {
96
+ jsContext = {
97
+ addEventListener: vi.fn(),
98
+ dispatchEvent: vi.fn(),
99
+ };
100
+ // Re-bind so `lynxMock.getJSContext()` / `getCoreContext()` return the
101
+ // fresh spy instance (the closure captures `jsContext` by lexical
102
+ // reference, so re-assigning it above is enough — but document this
103
+ // here so future readers don't get confused if they refactor.)
104
+ }
105
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/mt/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAO5B,IAAI,SAAS,GAAkB;IAC3B,gBAAgB,EAAE,EAAE,CAAC,EAAE,EAAE;IACzB,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE;CACzB,CAAC;AAEF,MAAM,QAAQ,GAAG;IACb,UAAU,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE;IACvC,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;IAC7B,cAAc,EAAE,GAAG,EAAE,CAAC,SAAS;IAC/B,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC;IAClD,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC;IACpD,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;IACtD,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC;IACxD,qBAAqB,EAAE,CAAC,EAAwB,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAsB;IACzH,oBAAoB,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAwD,CAAC;IACtH,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI;IACrC,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,EAAE;CACzC,CAAC;AAED,UAAsC,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;AAC1D,UAAsC,CAAC,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;AAEpF,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;AACzB,MAAM,QAAQ,GAAG;IACb,cAAc;IACd,cAAc;IACd,YAAY;IACZ,iBAAiB;IACjB,sBAAsB;IACtB,mBAAmB;IACnB,YAAY;IACZ,kBAAkB;IAClB,gBAAgB;IAChB,YAAY;IACZ,sBAAsB;IACtB,qBAAqB;IACrB,yBAAyB;IACzB,iBAAiB;IACjB,oBAAoB;IACpB,kBAAkB;IAClB,oBAAoB;IACpB,kBAAkB;IAClB,kBAAkB;CACrB,CAAC;AACF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;IACtB,UAAsC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;AAC1D,CAAC;AAED,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;AACxC,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAC/C,MAAM,MAAM,CAAC,gDAAgD,CAAC,CAAC;AAE/D,0EAA0E;AAC1E,yEAAyE;AACzE,MAAM,UAAU,aAAa;IACzB,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAC9B,SAAS,GAAG;QACR,gBAAgB,EAAE,EAAE,CAAC,EAAE,EAAE;QACzB,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE;KACzB,CAAC;IACF,uEAAuE;IACvE,kEAAkE;IAClE,oEAAoE;IACpE,+DAA+D;AACnE,CAAC"}
@@ -0,0 +1,38 @@
1
+ export function getByType(container, type) {
2
+ const node = container.findByType(type);
3
+ if (!node)
4
+ throw new Error(`No element found with type "${type}"`);
5
+ return node;
6
+ }
7
+ export function getAllByType(container, type) {
8
+ return container.findAllByType(type);
9
+ }
10
+ export function getByText(container, text) {
11
+ const node = container.findByText(text);
12
+ if (!node)
13
+ throw new Error(`No element found with text "${text}"`);
14
+ return node;
15
+ }
16
+ export function queryByType(container, type) {
17
+ return container.findByType(type);
18
+ }
19
+ export function queryByText(container, text) {
20
+ return container.findByText(text);
21
+ }
22
+ export function getByProp(container, key, value) {
23
+ function find(node) {
24
+ if (node.props[key] === value)
25
+ return node;
26
+ for (const child of node.children) {
27
+ const found = find(child);
28
+ if (found)
29
+ return found;
30
+ }
31
+ return null;
32
+ }
33
+ const node = find(container);
34
+ if (!node)
35
+ throw new Error(`No element found with ${key}="${value}"`);
36
+ return node;
37
+ }
38
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,SAAS,CAAC,SAAmB,EAAE,IAAY;IACzD,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,GAAG,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAmB,EAAE,IAAY;IAC5D,OAAO,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,SAAmB,EAAE,IAAY;IACzD,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,GAAG,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAmB,EAAE,IAAY;IAC3D,OAAO,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAmB,EAAE,IAAY;IAC3D,OAAO,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,SAAmB,EACnB,GAAW,EACX,KAAc;IAEd,SAAS,IAAI,CAAC,IAAc;QAC1B,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,KAAK,KAAK,GAAG,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/render.js ADDED
@@ -0,0 +1,31 @@
1
+ import { testRenderer } from './test-renderer.js';
2
+ import { TestNode } from './test-node.js';
3
+ import * as queries from './queries.js';
4
+ /**
5
+ * Render a JSX element into an in-memory TestNode tree.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * const { getByText, container } = render(<MyComponent name="World" />);
10
+ * expect(getByText('Hello World')).toBeTruthy();
11
+ * ```
12
+ */
13
+ export function render(element, options) {
14
+ const container = new TestNode('root');
15
+ testRenderer.render(element, container, options?.appContext ?? undefined);
16
+ const unmount = () => {
17
+ testRenderer.render(null, container);
18
+ };
19
+ return {
20
+ container,
21
+ unmount,
22
+ getByType: (type) => queries.getByType(container, type),
23
+ getAllByType: (type) => queries.getAllByType(container, type),
24
+ getByText: (text) => queries.getByText(container, text),
25
+ queryByType: (type) => queries.queryByType(container, type),
26
+ queryByText: (text) => queries.queryByText(container, text),
27
+ getByProp: (key, value) => queries.getByProp(container, key, value),
28
+ debug: () => container.toDebugString(),
29
+ };
30
+ }
31
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAuBxC;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CACpB,OAAmB,EACnB,OAAqC;IAErC,MAAM,SAAS,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEvC,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,IAAI,SAAS,CAAC,CAAC;IAE1E,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,OAAO;QACL,SAAS;QACT,OAAO;QACP,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC;QACvD,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC;QAC7D,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC;QACvD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC;QAC3D,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC;QAC3D,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC;QACnE,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE;KACvC,CAAC;AACJ,CAAC"}