@lynx-js/react 0.106.4 → 0.107.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 (56) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/package.json +15 -2
  3. package/refresh/.turbo/turbo-build.log +1 -1
  4. package/refresh/package.json +1 -1
  5. package/runtime/jsx-dev-runtime/index.d.ts +12 -0
  6. package/runtime/jsx-runtime/index.d.ts +12 -0
  7. package/runtime/lib/index.js +7 -4
  8. package/runtime/lib/index.js.map +1 -1
  9. package/runtime/lib/lifecycle/patch/commit.d.ts +7 -1
  10. package/runtime/lib/lifecycle/patch/commit.js +44 -38
  11. package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
  12. package/runtime/lib/lifecycle/patch/updateMainThread.js +6 -4
  13. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  14. package/runtime/lib/list.d.ts +13 -6
  15. package/runtime/lib/list.js.map +1 -1
  16. package/runtime/lib/lynx/calledByNative.js +2 -2
  17. package/runtime/lib/lynx/calledByNative.js.map +1 -1
  18. package/runtime/lib/lynx/component.js +1 -1
  19. package/runtime/lib/lynx/component.js.map +1 -1
  20. package/runtime/lib/lynx/performance.d.ts +24 -14
  21. package/runtime/lib/lynx/performance.js +42 -23
  22. package/runtime/lib/lynx/performance.js.map +1 -1
  23. package/runtime/lib/lynx/runWithForce.d.ts +1 -0
  24. package/runtime/lib/lynx/runWithForce.js +45 -0
  25. package/runtime/lib/lynx/runWithForce.js.map +1 -0
  26. package/runtime/lib/lynx/tt.d.ts +2 -1
  27. package/runtime/lib/lynx/tt.js +33 -61
  28. package/runtime/lib/lynx/tt.js.map +1 -1
  29. package/runtime/lib/lynx.d.ts +1 -0
  30. package/runtime/lib/lynx.js +1 -0
  31. package/runtime/lib/lynx.js.map +1 -1
  32. package/runtime/src/index.ts +7 -4
  33. package/runtime/src/lifecycle/patch/commit.ts +54 -41
  34. package/runtime/src/lifecycle/patch/updateMainThread.ts +6 -4
  35. package/runtime/src/list.ts +18 -10
  36. package/runtime/src/lynx/calledByNative.ts +2 -2
  37. package/runtime/src/lynx/component.ts +1 -1
  38. package/runtime/src/lynx/performance.ts +48 -22
  39. package/runtime/src/lynx/runWithForce.ts +52 -0
  40. package/runtime/src/lynx/tt.ts +47 -70
  41. package/runtime/src/lynx.ts +1 -0
  42. package/testing-library/README.md +70 -0
  43. package/testing-library/dist/env/vitest.js +548 -0
  44. package/testing-library/dist/index.d.ts +1504 -0
  45. package/testing-library/dist/index.js +12 -0
  46. package/testing-library/dist/pure.js +14729 -0
  47. package/testing-library/dist/pure.js.LICENSE.txt +17 -0
  48. package/testing-library/dist/vitest-global-setup.js +115 -0
  49. package/testing-library/dist/vitest.config.js +90 -0
  50. package/testing-library/types/entry.d.ts +257 -0
  51. package/testing-library/types/index.d.ts +15 -0
  52. package/testing-library/types/pure.d.ts +2 -0
  53. package/testing-library/types/vitest-config.d.ts +12 -0
  54. package/transform/dist/wasm.cjs +1 -1
  55. package/types/react.d.ts +31 -0
  56. package/LICENSE +0 -202
@@ -15,14 +15,22 @@ export interface ListUpdateInfo {
15
15
  onSetAttribute(child: SnapshotInstance, attr: any, oldAttr: any): void;
16
16
  }
17
17
 
18
+ interface InsertAction {
19
+ position: number;
20
+ type: string;
21
+ }
22
+
18
23
  interface UpdateAction {
19
- insertAction: {
20
- position: number;
21
- type: string;
22
- }[];
24
+ from: number;
25
+ to: number;
26
+ type: string;
27
+ flush: boolean;
28
+ }
29
+
30
+ interface ListOperations {
31
+ insertAction: InsertAction[];
23
32
  removeAction: number[];
24
- // TODO: type `updateAction`
25
- updateAction: any[];
33
+ updateAction: UpdateAction[];
26
34
  }
27
35
 
28
36
  // class ListUpdateInfoDiffing implements ListUpdateInfo {
@@ -115,12 +123,12 @@ export class ListUpdateInfoRecording implements ListUpdateInfo {
115
123
  this.platformInfoUpdate.set(child, attr);
116
124
  }
117
125
 
118
- private __toAttribute(): UpdateAction {
126
+ private __toAttribute(): ListOperations {
119
127
  const { removeChild, insertBefore, appendChild, platformInfoUpdate } = this;
120
128
 
121
129
  const removals: number[] = [];
122
- const insertions: { position: number; type: string }[] = [];
123
- const updates: any[] = [];
130
+ const insertions: InsertAction[] = [];
131
+ const updates: UpdateAction[] = [];
124
132
 
125
133
  let j = 0;
126
134
  for (let i = 0; i < this.oldChildNodes.length; i++, j++) {
@@ -199,7 +207,7 @@ export class ListUpdateInfoRecording implements ListUpdateInfo {
199
207
  };
200
208
  }
201
209
 
202
- toJSON(): [UpdateAction] {
210
+ toJSON(): [ListOperations] {
203
211
  // if (this.__pendingAttributes) {
204
212
  // return [...this.__pendingAttributes, this.__toAttribute()];
205
213
  // } else {
@@ -115,7 +115,7 @@ function updatePage(data: any, options?: UpdatePageOption): void {
115
115
  __root.__jsx = oldRoot.__jsx;
116
116
 
117
117
  setPipeline(options?.pipelineOptions);
118
- markTiming(PerformanceTimingKeys.update_diff_vdom_start);
118
+ markTiming(PerformanceTimingKeys.updateDiffVdomStart);
119
119
  {
120
120
  __pendingListUpdates.clear();
121
121
 
@@ -133,7 +133,7 @@ function updatePage(data: any, options?: UpdatePageOption): void {
133
133
  // always call this before `__FlushElementTree`
134
134
  __pendingListUpdates.flush();
135
135
  }
136
- markTiming(PerformanceTimingKeys.update_diff_vdom_end);
136
+ markTiming(PerformanceTimingKeys.updateDiffVdomEnd);
137
137
  }
138
138
 
139
139
  if (options) {
@@ -109,7 +109,7 @@ if (__JS__) {
109
109
  const timingFlag = this[NEXT_STATE][PerfSpecificKey];
110
110
  if (timingFlag) {
111
111
  globalFlushOptions.__lynx_timing_flag = timingFlag;
112
- markTimingLegacy(PerformanceTimingKeys.update_set_state_trigger, timingFlag);
112
+ markTimingLegacy(PerformanceTimingKeys.updateSetStateTrigger, timingFlag);
113
113
  this[NEXT_STATE][PerfSpecificKey] = '';
114
114
  }
115
115
  };
@@ -8,22 +8,35 @@ import { DIFF } from '../renderToOpcodes/constants.js';
8
8
  import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
9
9
 
10
10
  enum PerformanceTimingKeys {
11
- update_set_state_trigger,
12
- update_diff_vdom_start,
13
- update_diff_vdom_end,
14
- // update_set_state_trigger, update_diff_vdom_start and update_diff_vdom_end is deprecated
15
- diff_vdom_start,
16
- diff_vdom_end,
17
- pack_changes_start,
18
- pack_changes_end,
19
- parse_changes_start,
20
- parse_changes_end,
21
- patch_changes_start,
22
- patch_changes_end,
23
- hydrate_parse_snapshot_start,
24
- hydrate_parse_snapshot_end,
11
+ updateSetStateTrigger,
12
+ updateDiffVdomStart,
13
+ updateDiffVdomEnd,
14
+ // updateSetStateTrigger, updateDiffVdomStart and updateDiffVdomEnd is deprecated
15
+ diffVdomStart,
16
+ diffVdomEnd,
17
+ packChangesStart,
18
+ packChangesEnd,
19
+ parseChangesStart,
20
+ parseChangesEnd,
21
+ patchChangesStart,
22
+ patchChangesEnd,
23
+ hydrateParseSnapshotStart,
24
+ hydrateParseSnapshotEnd,
25
+ mtsRenderStart,
26
+ mtsRenderEnd,
25
27
  }
26
28
 
29
+ const PerformanceTimingFlags = {
30
+ reactLynxHydrate: 'react_lynx_hydrate',
31
+ } as const;
32
+
33
+ const PipelineOrigins = {
34
+ reactLynxHydrate: 'reactLynxHydrate',
35
+ updateTriggeredByBts: 'updateTriggeredByBts',
36
+ } as const;
37
+
38
+ type PipelineOrigin = typeof PipelineOrigins[keyof typeof PipelineOrigins];
39
+
27
40
  /**
28
41
  * @deprecated used by old timing api(setState timing flag)
29
42
  */
@@ -39,13 +52,13 @@ let globalPipelineOptions: PipelineOptions | undefined;
39
52
  */
40
53
  function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): void {
41
54
  switch (key) {
42
- case PerformanceTimingKeys.update_set_state_trigger: {
55
+ case PerformanceTimingKeys.updateSetStateTrigger: {
43
56
  shouldMarkDiffVdomStart = true;
44
57
  shouldMarkDiffVdomEnd = true;
45
58
  timingFlag = timingFlag_;
46
59
  break;
47
60
  }
48
- case PerformanceTimingKeys.update_diff_vdom_start: {
61
+ case PerformanceTimingKeys.updateDiffVdomStart: {
49
62
  /* v8 ignore start */
50
63
  if (!shouldMarkDiffVdomStart) {
51
64
  return;
@@ -54,7 +67,7 @@ function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): voi
54
67
  shouldMarkDiffVdomStart = false;
55
68
  break;
56
69
  }
57
- case PerformanceTimingKeys.update_diff_vdom_end: {
70
+ case PerformanceTimingKeys.updateDiffVdomEnd: {
58
71
  if (!shouldMarkDiffVdomEnd) {
59
72
  return;
60
73
  }
@@ -65,11 +78,22 @@ function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): voi
65
78
  lynx.getNativeApp().markTiming?.(timingFlag!, PerformanceTimingKeys[key]);
66
79
  }
67
80
 
68
- function beginPipeline(needTimestamps: boolean, timingFlag?: string): void {
81
+ function beginPipeline(needTimestamps: boolean, pipelineOrigin: PipelineOrigin, timingFlag?: string): void {
69
82
  globalPipelineOptions = lynx.performance?._generatePipelineOptions?.();
70
83
  if (globalPipelineOptions) {
71
84
  globalPipelineOptions.needTimestamps = needTimestamps;
72
- lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID);
85
+ globalPipelineOptions.pipelineOrigin = pipelineOrigin;
86
+ globalPipelineOptions.dsl = 'reactLynx';
87
+ switch (pipelineOrigin) {
88
+ case PipelineOrigins.reactLynxHydrate:
89
+ globalPipelineOptions.stage = 'hydrate';
90
+ break;
91
+ case PipelineOrigins.updateTriggeredByBts:
92
+ globalPipelineOptions.stage = 'update';
93
+ break;
94
+ }
95
+
96
+ lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID, globalPipelineOptions);
73
97
  if (timingFlag) {
74
98
  lynx.performance?._bindPipelineIdWithTimingFlag?.(globalPipelineOptions.pipelineID, timingFlag);
75
99
  }
@@ -92,11 +116,11 @@ function initTimingAPI(): void {
92
116
  // check `__globalSnapshotPatch` to make sure this only runs after hydrate
93
117
  if (__JS__ && __globalSnapshotPatch) {
94
118
  if (!globalPipelineOptions) {
95
- beginPipeline(false);
96
- markTiming(PerformanceTimingKeys.diff_vdom_start, true);
119
+ beginPipeline(false, PipelineOrigins.updateTriggeredByBts);
120
+ markTiming(PerformanceTimingKeys.diffVdomStart, true);
97
121
  }
98
122
  if (shouldMarkDiffVdomStart) {
99
- markTimingLegacy(PerformanceTimingKeys.update_diff_vdom_start);
123
+ markTimingLegacy(PerformanceTimingKeys.updateDiffVdomStart);
100
124
  }
101
125
  }
102
126
  oldDiff?.(vnode);
@@ -108,6 +132,8 @@ function initTimingAPI(): void {
108
132
  */
109
133
  export {
110
134
  PerformanceTimingKeys,
135
+ PerformanceTimingFlags,
136
+ PipelineOrigins,
111
137
  PerfSpecificKey,
112
138
  markTimingLegacy,
113
139
  initTimingAPI,
@@ -0,0 +1,52 @@
1
+ import { options } from 'preact';
2
+ import type { VNode } from 'preact';
3
+ import { COMPONENT, DIFF, DIFFED, FORCE } from '../renderToOpcodes/constants.js';
4
+
5
+ export function runWithForce(cb: () => void): void {
6
+ // save vnode and its `_component` in WeakMap
7
+ const m = new WeakMap<VNode, any>();
8
+
9
+ const oldDiff = options[DIFF];
10
+
11
+ options[DIFF] = (vnode: VNode) => {
12
+ if (oldDiff) {
13
+ oldDiff(vnode);
14
+ }
15
+
16
+ // when `options[DIFF]` is called, a newVnode is passed in
17
+ // so its `vnode[COMPONENT]` should be null,
18
+ // but it will be set later
19
+ Object.defineProperty(vnode, COMPONENT, {
20
+ configurable: true,
21
+ set(c) {
22
+ m.set(vnode, c);
23
+ if (c) {
24
+ c[FORCE] = true;
25
+ }
26
+ },
27
+ get() {
28
+ return m.get(vnode);
29
+ },
30
+ });
31
+ };
32
+
33
+ const oldDiffed = options[DIFFED];
34
+
35
+ options[DIFFED] = (vnode: VNode) => {
36
+ if (oldDiffed) {
37
+ oldDiffed(vnode);
38
+ }
39
+
40
+ // delete is a reverse operation of previous `Object.defineProperty`
41
+ delete vnode[COMPONENT];
42
+ // restore
43
+ vnode[COMPONENT] = m.get(vnode);
44
+ };
45
+
46
+ try {
47
+ cb();
48
+ } finally {
49
+ options[DIFF] = oldDiff as (vnode: VNode) => void;
50
+ options[DIFFED] = oldDiffed as (vnode: VNode) => void;
51
+ }
52
+ }
@@ -1,72 +1,35 @@
1
1
  // Copyright 2024 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import { options } from 'preact';
5
- import type { VNode } from 'preact';
6
-
7
4
  import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
8
- import { PerformanceTimingKeys, beginPipeline, markTiming } from './performance.js';
5
+ import {
6
+ PerformanceTimingKeys,
7
+ PerformanceTimingFlags,
8
+ PipelineOrigins,
9
+ beginPipeline,
10
+ markTiming,
11
+ } from './performance.js';
9
12
  import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
10
13
  import { destroyBackground } from '../lifecycle/destroy.js';
11
14
  import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js';
12
15
  import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js';
13
- import { commitPatchUpdate, genCommitTaskId, globalCommitTaskMap } from '../lifecycle/patch/commit.js';
16
+ import {
17
+ clearPatchesToCommit,
18
+ commitPatchUpdate,
19
+ genCommitTaskId,
20
+ globalCommitTaskMap,
21
+ patchesToCommit,
22
+ type PatchList,
23
+ } from '../lifecycle/patch/commit.js';
14
24
  import { reloadBackground } from '../lifecycle/reload.js';
15
25
  import { renderBackground } from '../lifecycle/render.js';
16
- import { CHILDREN, COMPONENT, DIFF, DIFFED, FORCE } from '../renderToOpcodes/constants.js';
26
+ import { CHILDREN } from '../renderToOpcodes/constants.js';
17
27
  import { __root } from '../root.js';
18
28
  import { globalRefsToSet, updateBackgroundRefs } from '../snapshot/ref.js';
19
29
  import { backgroundSnapshotInstanceManager } from '../snapshot.js';
20
30
  import { destroyWorklet } from '../worklet/destroy.js';
21
-
22
- export function runWithForce(cb: () => void): void {
23
- // save vnode and its `_component` in WeakMap
24
- const m = new WeakMap<VNode, any>();
25
-
26
- const oldDiff = options[DIFF];
27
-
28
- options[DIFF] = (vnode: VNode) => {
29
- if (oldDiff) {
30
- oldDiff(vnode);
31
- }
32
-
33
- // when `options[DIFF]` is called, a newVnode is passed in
34
- // so its `vnode[COMPONENT]` should be null,
35
- // but it will be set later
36
- Object.defineProperty(vnode, COMPONENT, {
37
- configurable: true,
38
- set(c) {
39
- m.set(vnode, c);
40
- if (c) {
41
- c[FORCE] = true;
42
- }
43
- },
44
- get() {
45
- return m.get(vnode);
46
- },
47
- });
48
- };
49
-
50
- const oldDiffed = options[DIFFED];
51
-
52
- options[DIFFED] = (vnode: VNode) => {
53
- if (oldDiffed) {
54
- oldDiffed(vnode);
55
- }
56
-
57
- // delete is a reverse operation of previous `Object.defineProperty`
58
- delete vnode[COMPONENT];
59
- // restore
60
- vnode[COMPONENT] = m.get(vnode);
61
- };
62
-
63
- try {
64
- cb();
65
- } finally {
66
- options[DIFF] = oldDiff as (vnode: VNode) => void;
67
- options[DIFFED] = oldDiffed as (vnode: VNode) => void;
68
- }
69
- }
31
+ import { runWithForce } from './runWithForce.js';
32
+ export { runWithForce };
70
33
 
71
34
  function injectTt(): void {
72
35
  // @ts-ignore
@@ -100,7 +63,7 @@ function onLifecycleEvent([type, data]: [string, any]) {
100
63
  }
101
64
 
102
65
  try {
103
- void onLifecycleEventImpl(type, data);
66
+ onLifecycleEventImpl(type, data);
104
67
  } catch (e) {
105
68
  lynx.reportError(e as Error);
106
69
  }
@@ -110,18 +73,18 @@ function onLifecycleEvent([type, data]: [string, any]) {
110
73
  }
111
74
  }
112
75
 
113
- async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
76
+ function onLifecycleEventImpl(type: string, data: any): void {
114
77
  switch (type) {
115
78
  case LifecycleConstant.firstScreen: {
116
79
  const { root: lepusSide, refPatch, jsReadyEventIdSwap } = data;
117
80
  if (__PROFILE__) {
118
81
  console.profile('hydrate');
119
82
  }
120
- beginPipeline(true, 'react_lynx_hydrate');
121
- markTiming(PerformanceTimingKeys.hydrate_parse_snapshot_start);
83
+ beginPipeline(true, PipelineOrigins.reactLynxHydrate, PerformanceTimingFlags.reactLynxHydrate);
84
+ markTiming(PerformanceTimingKeys.hydrateParseSnapshotStart);
122
85
  const before = JSON.parse(lepusSide);
123
- markTiming(PerformanceTimingKeys.hydrate_parse_snapshot_end);
124
- markTiming(PerformanceTimingKeys.diff_vdom_start);
86
+ markTiming(PerformanceTimingKeys.hydrateParseSnapshotEnd);
87
+ markTiming(PerformanceTimingKeys.diffVdomStart);
125
88
  const snapshotPatch = hydrate(
126
89
  before,
127
90
  __root as BackgroundSnapshotInstance,
@@ -129,7 +92,7 @@ async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
129
92
  if (__PROFILE__) {
130
93
  console.profileEnd();
131
94
  }
132
- markTiming(PerformanceTimingKeys.diff_vdom_end);
95
+ markTiming(PerformanceTimingKeys.diffVdomEnd);
133
96
 
134
97
  // TODO: It seems `delayedEvents` and `delayedLifecycleEvents` should be merged into one array to ensure the proper order of events.
135
98
  flushDelayedLifecycleEvents();
@@ -166,14 +129,23 @@ async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
166
129
  console.profile('commitChanges');
167
130
  }
168
131
  const commitTaskId = genCommitTaskId();
169
- await commitPatchUpdate({ patchList: [{ snapshotPatch, id: commitTaskId }] }, { isHydration: true });
170
- updateBackgroundRefs(commitTaskId);
171
- globalCommitTaskMap.forEach((commitTask, id) => {
172
- if (id > commitTaskId) {
173
- return;
174
- }
175
- commitTask();
176
- globalCommitTaskMap.delete(id);
132
+ patchesToCommit.push(
133
+ { snapshotPatch, id: commitTaskId },
134
+ );
135
+ const patchList: PatchList = {
136
+ patchList: patchesToCommit,
137
+ };
138
+ clearPatchesToCommit();
139
+ const obj = commitPatchUpdate(patchList, { isHydration: true });
140
+ lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
141
+ updateBackgroundRefs(commitTaskId);
142
+ globalCommitTaskMap.forEach((commitTask, id) => {
143
+ if (id > commitTaskId) {
144
+ return;
145
+ }
146
+ commitTask();
147
+ globalCommitTaskMap.delete(id);
148
+ });
177
149
  });
178
150
  break;
179
151
  }
@@ -195,13 +167,18 @@ async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
195
167
  }
196
168
  }
197
169
 
170
+ let flushingDelayedLifecycleEvents = false;
198
171
  function flushDelayedLifecycleEvents(): void {
172
+ // avoid stackoverflow
173
+ if (flushingDelayedLifecycleEvents) return;
174
+ flushingDelayedLifecycleEvents = true;
199
175
  if (delayedLifecycleEvents) {
200
176
  delayedLifecycleEvents.forEach((e) => {
201
177
  onLifecycleEvent(e);
202
178
  });
203
179
  delayedLifecycleEvents.length = 0;
204
180
  }
181
+ flushingDelayedLifecycleEvents = false;
205
182
  }
206
183
 
207
184
  function publishEvent(handlerName: string, data: unknown) {
@@ -15,6 +15,7 @@ import { setupLynxEnv } from './lynx/env.js';
15
15
  import { injectLepusMethods } from './lynx/injectLepusMethods.js';
16
16
  import { initTimingAPI } from './lynx/performance.js';
17
17
  import { injectTt } from './lynx/tt.js';
18
+ export { runWithForce } from './lynx/runWithForce.js';
18
19
 
19
20
  // @ts-expect-error Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
20
21
  if (__LEPUS__ && typeof globalThis.processEvalResult === 'undefined') {
@@ -0,0 +1,70 @@
1
+ # @lynx-js/react/testing-library
2
+
3
+ ReactLynx Testing Library is a simple and complete ReactLynx unit testing library that encourages good testing practices.
4
+
5
+ > Inspired completely by [react-testing-library](https://github.com/testing-library/react-testing-library)
6
+
7
+ Similar to [react-testing-library](https://github.com/testing-library/react-testing-library), this library is designed to test your ReactLynx components in the same way you would test React components using react-testing-library.
8
+
9
+ ## Setup
10
+
11
+ Setup vitest:
12
+
13
+ ```js
14
+ // vitest.config.js
15
+ import { defineConfig, mergeConfig } from 'vitest/config';
16
+ import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config';
17
+
18
+ const defaultConfig = createVitestConfig();
19
+ const config = defineConfig({
20
+ test: {
21
+ // ...
22
+ },
23
+ });
24
+
25
+ export default mergeConfig(defaultConfig, config);
26
+ ```
27
+
28
+ Then you can start writing tests and run them with vitest!
29
+
30
+ ## Usage
31
+
32
+ ```js
33
+ import '@testing-library/jest-dom';
34
+ import { test, expect } from 'vitest';
35
+ import { render } from '@lynx-js/react/testing-library';
36
+
37
+ test('renders options.wrapper around node', async () => {
38
+ const WrapperComponent = ({ children }) => (
39
+ <view data-testid='wrapper'>{children}</view>
40
+ );
41
+ const Comp = () => {
42
+ return <view data-testid='inner' style='background-color: yellow;' />;
43
+ };
44
+ const { container, getByTestId } = render(<Comp />, {
45
+ wrapper: WrapperComponent,
46
+ });
47
+ expect(getByTestId('wrapper')).toBeInTheDocument();
48
+ expect(container.firstChild).toMatchInlineSnapshot(`
49
+ <view
50
+ data-testid="wrapper"
51
+ >
52
+ <view
53
+ data-testid="inner"
54
+ style="background-color: yellow;"
55
+ />
56
+ </view>
57
+ `);
58
+ });
59
+ ```
60
+
61
+ 💡 Since our testing environment (`@lynx-js/test-environment`) is based on jsdom, You may also be interested in installing `@testing-library/jest-dom` so you can use
62
+ [the custom jest matchers](https://github.com/testing-library/jest-dom).
63
+
64
+ ## Examples
65
+
66
+ See our [examples](https://github.com/lynx-family/lynx-stack/tree/main/packages/react/testing-library/src/__tests__) for more usage.
67
+
68
+ ## Credits
69
+
70
+ - [Testing Library](https://testing-library.com/) for the testing utilities and good practices for React testing.