@react-native-harness/runtime 1.0.0-alpha.20 → 1.0.0-alpha.22

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 (340) hide show
  1. package/dist/index.js +2 -1
  2. package/package.json +20 -2
  3. package/src/bundler/errors.ts +2 -1
  4. package/src/client/factory.ts +9 -2
  5. package/src/client/store.ts +16 -0
  6. package/src/expect/expect.ts +127 -0
  7. package/src/expect/index.ts +1 -123
  8. package/src/expect/matchers/toMatchImageSnapshot.ts +50 -0
  9. package/src/globals.ts +12 -0
  10. package/src/index.ts +2 -1
  11. package/src/polyfills.ts +14 -0
  12. package/src/render/TestComponentOverlay.tsx +22 -2
  13. package/src/render/index.ts +8 -6
  14. package/src/runner/context.ts +16 -0
  15. package/src/runner/factory.ts +7 -1
  16. package/src/runner/index.ts +5 -0
  17. package/src/runner/runSuite.ts +4 -0
  18. package/src/runner/types.ts +7 -1
  19. package/src/ui/ReadyScreen.tsx +1 -0
  20. package/.babelrc.js +0 -23
  21. package/assets/harness-module-system.js +0 -73
  22. package/dist/bundler/bundle.d.ts +0 -2
  23. package/dist/bundler/bundle.d.ts.map +0 -1
  24. package/dist/bundler/bundle.js +0 -21
  25. package/dist/bundler/errors.d.ts +0 -15
  26. package/dist/bundler/errors.d.ts.map +0 -1
  27. package/dist/bundler/errors.js +0 -28
  28. package/dist/bundler/evaluate.d.ts +0 -2
  29. package/dist/bundler/evaluate.d.ts.map +0 -1
  30. package/dist/bundler/evaluate.js +0 -18
  31. package/dist/bundler/factory.d.ts +0 -3
  32. package/dist/bundler/factory.d.ts.map +0 -1
  33. package/dist/bundler/factory.js +0 -36
  34. package/dist/bundler/index.d.ts +0 -4
  35. package/dist/bundler/index.d.ts.map +0 -1
  36. package/dist/bundler/index.js +0 -2
  37. package/dist/bundler/types.d.ts +0 -7
  38. package/dist/bundler/types.d.ts.map +0 -1
  39. package/dist/bundler/types.js +0 -1
  40. package/dist/client/factory.d.ts +0 -2
  41. package/dist/client/factory.d.ts.map +0 -1
  42. package/dist/client/factory.js +0 -69
  43. package/dist/client/getDeviceDescriptor.d.ts +0 -8
  44. package/dist/client/getDeviceDescriptor.d.ts.map +0 -1
  45. package/dist/client/getDeviceDescriptor.js +0 -32
  46. package/dist/client/getWSServer.d.ts +0 -2
  47. package/dist/client/getWSServer.d.ts.map +0 -1
  48. package/dist/client/getWSServer.js +0 -8
  49. package/dist/client/index.d.ts +0 -2
  50. package/dist/client/index.d.ts.map +0 -1
  51. package/dist/client/index.js +0 -1
  52. package/dist/client/setup-files.d.ts +0 -12
  53. package/dist/client/setup-files.d.ts.map +0 -1
  54. package/dist/client/setup-files.js +0 -60
  55. package/dist/client/store.d.ts +0 -4
  56. package/dist/client/store.d.ts.map +0 -1
  57. package/dist/client/store.js +0 -10
  58. package/dist/collector/errors.d.ts +0 -8
  59. package/dist/collector/errors.d.ts.map +0 -1
  60. package/dist/collector/errors.js +0 -20
  61. package/dist/collector/factory.d.ts +0 -3
  62. package/dist/collector/factory.d.ts.map +0 -1
  63. package/dist/collector/factory.js +0 -25
  64. package/dist/collector/functions.d.ts +0 -22
  65. package/dist/collector/functions.d.ts.map +0 -1
  66. package/dist/collector/functions.js +0 -279
  67. package/dist/collector/index.d.ts +0 -5
  68. package/dist/collector/index.d.ts.map +0 -1
  69. package/dist/collector/index.js +0 -3
  70. package/dist/collector/types.d.ts +0 -10
  71. package/dist/collector/types.d.ts.map +0 -1
  72. package/dist/collector/types.js +0 -1
  73. package/dist/collector/validation.d.ts +0 -4
  74. package/dist/collector/validation.d.ts.map +0 -1
  75. package/dist/collector/validation.js +0 -15
  76. package/dist/constants.d.ts +0 -2
  77. package/dist/constants.d.ts.map +0 -1
  78. package/dist/constants.js +0 -1
  79. package/dist/disableHMRWhenReady.d.ts +0 -2
  80. package/dist/disableHMRWhenReady.d.ts.map +0 -1
  81. package/dist/disableHMRWhenReady.js +0 -20
  82. package/dist/entry-point.d.ts +0 -2
  83. package/dist/entry-point.d.ts.map +0 -1
  84. package/dist/entry-point.js +0 -4
  85. package/dist/errors.d.ts +0 -6
  86. package/dist/errors.d.ts.map +0 -1
  87. package/dist/errors.js +0 -13
  88. package/dist/expect/expect.d.ts +0 -9
  89. package/dist/expect/expect.d.ts.map +0 -1
  90. package/dist/expect/expect.js +0 -77
  91. package/dist/expect/index.d.ts +0 -9
  92. package/dist/expect/index.d.ts.map +0 -1
  93. package/dist/expect/index.js +0 -73
  94. package/dist/expect/matchers/toMatchImageSnapshot.d.ts +0 -12
  95. package/dist/expect/matchers/toMatchImageSnapshot.d.ts.map +0 -1
  96. package/dist/expect/matchers/toMatchImageSnapshot.js +0 -11
  97. package/dist/expect/setup.d.ts +0 -2
  98. package/dist/expect/setup.d.ts.map +0 -1
  99. package/dist/expect/setup.js +0 -7
  100. package/dist/filtering/index.d.ts +0 -2
  101. package/dist/filtering/index.d.ts.map +0 -1
  102. package/dist/filtering/index.js +0 -1
  103. package/dist/filtering/testNameFilter.d.ts +0 -12
  104. package/dist/filtering/testNameFilter.d.ts.map +0 -1
  105. package/dist/filtering/testNameFilter.js +0 -56
  106. package/dist/globals.d.ts +0 -9
  107. package/dist/globals.d.ts.map +0 -1
  108. package/dist/globals.js +0 -7
  109. package/dist/index.d.ts +0 -10
  110. package/dist/index.d.ts.map +0 -1
  111. package/dist/initialize.d.ts +0 -2
  112. package/dist/initialize.d.ts.map +0 -1
  113. package/dist/initialize.js +0 -24
  114. package/dist/jest-mock.d.ts +0 -2
  115. package/dist/jest-mock.d.ts.map +0 -1
  116. package/dist/jest-mock.js +0 -25
  117. package/dist/mocker/index.d.ts +0 -2
  118. package/dist/mocker/index.d.ts.map +0 -1
  119. package/dist/mocker/index.js +0 -1
  120. package/dist/mocker/registry.d.ts +0 -6
  121. package/dist/mocker/registry.d.ts.map +0 -1
  122. package/dist/mocker/registry.js +0 -50
  123. package/dist/mocker/types.d.ts +0 -6
  124. package/dist/mocker/types.d.ts.map +0 -1
  125. package/dist/mocker/types.js +0 -1
  126. package/dist/namespace.d.ts +0 -18
  127. package/dist/namespace.d.ts.map +0 -1
  128. package/dist/namespace.js +0 -19
  129. package/dist/render/ErrorBoundary.d.ts +0 -17
  130. package/dist/render/ErrorBoundary.d.ts.map +0 -1
  131. package/dist/render/ErrorBoundary.js +0 -73
  132. package/dist/render/TestComponentOverlay.d.ts +0 -3
  133. package/dist/render/TestComponentOverlay.d.ts.map +0 -1
  134. package/dist/render/TestComponentOverlay.js +0 -36
  135. package/dist/render/cleanup.d.ts +0 -2
  136. package/dist/render/cleanup.d.ts.map +0 -1
  137. package/dist/render/cleanup.js +0 -6
  138. package/dist/render/index.d.ts +0 -6
  139. package/dist/render/index.d.ts.map +0 -1
  140. package/dist/render/index.js +0 -66
  141. package/dist/render/queries.d.ts +0 -7
  142. package/dist/render/queries.d.ts.map +0 -1
  143. package/dist/render/queries.js +0 -99
  144. package/dist/render/setup.d.ts +0 -2
  145. package/dist/render/setup.d.ts.map +0 -1
  146. package/dist/render/setup.js +0 -7
  147. package/dist/render/types.d.ts +0 -12
  148. package/dist/render/types.d.ts.map +0 -1
  149. package/dist/render/types.js +0 -1
  150. package/dist/runner/context.d.ts +0 -10
  151. package/dist/runner/context.d.ts.map +0 -1
  152. package/dist/runner/context.js +0 -6
  153. package/dist/runner/errors.d.ts +0 -11
  154. package/dist/runner/errors.d.ts.map +0 -1
  155. package/dist/runner/errors.js +0 -41
  156. package/dist/runner/factory.d.ts +0 -3
  157. package/dist/runner/factory.d.ts.map +0 -1
  158. package/dist/runner/factory.js +0 -22
  159. package/dist/runner/hooks.d.ts +0 -4
  160. package/dist/runner/hooks.d.ts.map +0 -1
  161. package/dist/runner/hooks.js +0 -39
  162. package/dist/runner/index.d.ts +0 -4
  163. package/dist/runner/index.d.ts.map +0 -1
  164. package/dist/runner/index.js +0 -2
  165. package/dist/runner/runSuite.d.ts +0 -4
  166. package/dist/runner/runSuite.d.ts.map +0 -1
  167. package/dist/runner/runSuite.js +0 -199
  168. package/dist/runner/types.d.ts +0 -13
  169. package/dist/runner/types.d.ts.map +0 -1
  170. package/dist/runner/types.js +0 -1
  171. package/dist/screen/index.d.ts +0 -8
  172. package/dist/screen/index.d.ts.map +0 -1
  173. package/dist/screen/index.js +0 -18
  174. package/dist/spy/index.d.ts +0 -2
  175. package/dist/spy/index.d.ts.map +0 -1
  176. package/dist/spy/index.js +0 -2
  177. package/dist/symbolicate.d.ts +0 -3
  178. package/dist/symbolicate.d.ts.map +0 -1
  179. package/dist/symbolicate.js +0 -19
  180. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  181. package/dist/ui/ReadyScreen.d.ts +0 -2
  182. package/dist/ui/ReadyScreen.d.ts.map +0 -1
  183. package/dist/ui/ReadyScreen.js +0 -103
  184. package/dist/ui/WrongEnvironmentScreen.d.ts +0 -2
  185. package/dist/ui/WrongEnvironmentScreen.d.ts.map +0 -1
  186. package/dist/ui/WrongEnvironmentScreen.js +0 -79
  187. package/dist/ui/index.d.ts +0 -2
  188. package/dist/ui/index.d.ts.map +0 -1
  189. package/dist/ui/index.js +0 -3
  190. package/dist/ui/state.d.ts +0 -20
  191. package/dist/ui/state.d.ts.map +0 -1
  192. package/dist/ui/state.js +0 -28
  193. package/dist/userEvent/index.d.ts +0 -6
  194. package/dist/userEvent/index.d.ts.map +0 -1
  195. package/dist/userEvent/index.js +0 -10
  196. package/dist/utils/dev-server.d.ts +0 -2
  197. package/dist/utils/dev-server.d.ts.map +0 -1
  198. package/dist/utils/dev-server.js +0 -5
  199. package/dist/utils/emitter.d.ts +0 -16
  200. package/dist/utils/emitter.d.ts.map +0 -1
  201. package/dist/utils/emitter.js +0 -39
  202. package/dist/waitFor.d.ts +0 -21
  203. package/dist/waitFor.d.ts.map +0 -1
  204. package/dist/waitFor.js +0 -137
  205. package/eslint.config.mjs +0 -10
  206. package/out-tsc/vitest/src/__tests__/collector.test.d.ts +0 -2
  207. package/out-tsc/vitest/src/__tests__/collector.test.d.ts.map +0 -1
  208. package/out-tsc/vitest/src/__tests__/error-handling.test.d.ts +0 -2
  209. package/out-tsc/vitest/src/__tests__/error-handling.test.d.ts.map +0 -1
  210. package/out-tsc/vitest/src/__tests__/expect.test.d.ts +0 -2
  211. package/out-tsc/vitest/src/__tests__/expect.test.d.ts.map +0 -1
  212. package/out-tsc/vitest/src/__tests__/spy.test.d.ts +0 -2
  213. package/out-tsc/vitest/src/__tests__/spy.test.d.ts.map +0 -1
  214. package/out-tsc/vitest/src/bundler/bundle.d.ts +0 -2
  215. package/out-tsc/vitest/src/bundler/bundle.d.ts.map +0 -1
  216. package/out-tsc/vitest/src/bundler/errors.d.ts +0 -15
  217. package/out-tsc/vitest/src/bundler/errors.d.ts.map +0 -1
  218. package/out-tsc/vitest/src/bundler/evaluate.d.ts +0 -2
  219. package/out-tsc/vitest/src/bundler/evaluate.d.ts.map +0 -1
  220. package/out-tsc/vitest/src/bundler/factory.d.ts +0 -3
  221. package/out-tsc/vitest/src/bundler/factory.d.ts.map +0 -1
  222. package/out-tsc/vitest/src/bundler/index.d.ts +0 -4
  223. package/out-tsc/vitest/src/bundler/index.d.ts.map +0 -1
  224. package/out-tsc/vitest/src/bundler/types.d.ts +0 -7
  225. package/out-tsc/vitest/src/bundler/types.d.ts.map +0 -1
  226. package/out-tsc/vitest/src/client/factory.d.ts +0 -2
  227. package/out-tsc/vitest/src/client/factory.d.ts.map +0 -1
  228. package/out-tsc/vitest/src/client/getDeviceDescriptor.d.ts +0 -8
  229. package/out-tsc/vitest/src/client/getDeviceDescriptor.d.ts.map +0 -1
  230. package/out-tsc/vitest/src/client/getWSServer.d.ts +0 -2
  231. package/out-tsc/vitest/src/client/getWSServer.d.ts.map +0 -1
  232. package/out-tsc/vitest/src/client/index.d.ts +0 -2
  233. package/out-tsc/vitest/src/client/index.d.ts.map +0 -1
  234. package/out-tsc/vitest/src/client/setup-files.d.ts +0 -12
  235. package/out-tsc/vitest/src/client/setup-files.d.ts.map +0 -1
  236. package/out-tsc/vitest/src/client/store.d.ts +0 -4
  237. package/out-tsc/vitest/src/client/store.d.ts.map +0 -1
  238. package/out-tsc/vitest/src/collector/errors.d.ts +0 -8
  239. package/out-tsc/vitest/src/collector/errors.d.ts.map +0 -1
  240. package/out-tsc/vitest/src/collector/factory.d.ts +0 -3
  241. package/out-tsc/vitest/src/collector/factory.d.ts.map +0 -1
  242. package/out-tsc/vitest/src/collector/functions.d.ts +0 -22
  243. package/out-tsc/vitest/src/collector/functions.d.ts.map +0 -1
  244. package/out-tsc/vitest/src/collector/index.d.ts +0 -5
  245. package/out-tsc/vitest/src/collector/index.d.ts.map +0 -1
  246. package/out-tsc/vitest/src/collector/types.d.ts +0 -10
  247. package/out-tsc/vitest/src/collector/types.d.ts.map +0 -1
  248. package/out-tsc/vitest/src/collector/validation.d.ts +0 -4
  249. package/out-tsc/vitest/src/collector/validation.d.ts.map +0 -1
  250. package/out-tsc/vitest/src/constants.d.ts +0 -2
  251. package/out-tsc/vitest/src/constants.d.ts.map +0 -1
  252. package/out-tsc/vitest/src/entry-point.d.ts +0 -2
  253. package/out-tsc/vitest/src/entry-point.d.ts.map +0 -1
  254. package/out-tsc/vitest/src/errors.d.ts +0 -6
  255. package/out-tsc/vitest/src/errors.d.ts.map +0 -1
  256. package/out-tsc/vitest/src/expect/index.d.ts +0 -14
  257. package/out-tsc/vitest/src/expect/index.d.ts.map +0 -1
  258. package/out-tsc/vitest/src/expect/matchers/toMatchImageSnapshot.d.ts +0 -7
  259. package/out-tsc/vitest/src/expect/matchers/toMatchImageSnapshot.d.ts.map +0 -1
  260. package/out-tsc/vitest/src/expect/setup.d.ts +0 -2
  261. package/out-tsc/vitest/src/expect/setup.d.ts.map +0 -1
  262. package/out-tsc/vitest/src/filtering/index.d.ts +0 -2
  263. package/out-tsc/vitest/src/filtering/index.d.ts.map +0 -1
  264. package/out-tsc/vitest/src/filtering/testNameFilter.d.ts +0 -12
  265. package/out-tsc/vitest/src/filtering/testNameFilter.d.ts.map +0 -1
  266. package/out-tsc/vitest/src/globals.d.ts +0 -8
  267. package/out-tsc/vitest/src/globals.d.ts.map +0 -1
  268. package/out-tsc/vitest/src/index.d.ts +0 -13
  269. package/out-tsc/vitest/src/index.d.ts.map +0 -1
  270. package/out-tsc/vitest/src/initialize.d.ts +0 -2
  271. package/out-tsc/vitest/src/initialize.d.ts.map +0 -1
  272. package/out-tsc/vitest/src/jest-mock.d.ts +0 -2
  273. package/out-tsc/vitest/src/jest-mock.d.ts.map +0 -1
  274. package/out-tsc/vitest/src/mocker/index.d.ts +0 -2
  275. package/out-tsc/vitest/src/mocker/index.d.ts.map +0 -1
  276. package/out-tsc/vitest/src/mocker/registry.d.ts +0 -7
  277. package/out-tsc/vitest/src/mocker/registry.d.ts.map +0 -1
  278. package/out-tsc/vitest/src/mocker/types.d.ts +0 -6
  279. package/out-tsc/vitest/src/mocker/types.d.ts.map +0 -1
  280. package/out-tsc/vitest/src/namespace.d.ts +0 -18
  281. package/out-tsc/vitest/src/namespace.d.ts.map +0 -1
  282. package/out-tsc/vitest/src/render/ErrorBoundary.d.ts +0 -17
  283. package/out-tsc/vitest/src/render/ErrorBoundary.d.ts.map +0 -1
  284. package/out-tsc/vitest/src/render/TestComponentOverlay.d.ts +0 -3
  285. package/out-tsc/vitest/src/render/TestComponentOverlay.d.ts.map +0 -1
  286. package/out-tsc/vitest/src/render/cleanup.d.ts +0 -2
  287. package/out-tsc/vitest/src/render/cleanup.d.ts.map +0 -1
  288. package/out-tsc/vitest/src/render/index.d.ts +0 -6
  289. package/out-tsc/vitest/src/render/index.d.ts.map +0 -1
  290. package/out-tsc/vitest/src/render/setup.d.ts +0 -2
  291. package/out-tsc/vitest/src/render/setup.d.ts.map +0 -1
  292. package/out-tsc/vitest/src/render/types.d.ts +0 -12
  293. package/out-tsc/vitest/src/render/types.d.ts.map +0 -1
  294. package/out-tsc/vitest/src/runner/errors.d.ts +0 -11
  295. package/out-tsc/vitest/src/runner/errors.d.ts.map +0 -1
  296. package/out-tsc/vitest/src/runner/factory.d.ts +0 -3
  297. package/out-tsc/vitest/src/runner/factory.d.ts.map +0 -1
  298. package/out-tsc/vitest/src/runner/hooks.d.ts +0 -4
  299. package/out-tsc/vitest/src/runner/hooks.d.ts.map +0 -1
  300. package/out-tsc/vitest/src/runner/index.d.ts +0 -4
  301. package/out-tsc/vitest/src/runner/index.d.ts.map +0 -1
  302. package/out-tsc/vitest/src/runner/runSuite.d.ts +0 -4
  303. package/out-tsc/vitest/src/runner/runSuite.d.ts.map +0 -1
  304. package/out-tsc/vitest/src/runner/types.d.ts +0 -13
  305. package/out-tsc/vitest/src/runner/types.d.ts.map +0 -1
  306. package/out-tsc/vitest/src/screen/index.d.ts +0 -8
  307. package/out-tsc/vitest/src/screen/index.d.ts.map +0 -1
  308. package/out-tsc/vitest/src/spy/index.d.ts +0 -2
  309. package/out-tsc/vitest/src/spy/index.d.ts.map +0 -1
  310. package/out-tsc/vitest/src/symbolicate.d.ts +0 -3
  311. package/out-tsc/vitest/src/symbolicate.d.ts.map +0 -1
  312. package/out-tsc/vitest/src/ui/ReadyScreen.d.ts +0 -2
  313. package/out-tsc/vitest/src/ui/ReadyScreen.d.ts.map +0 -1
  314. package/out-tsc/vitest/src/ui/WrongEnvironmentScreen.d.ts +0 -2
  315. package/out-tsc/vitest/src/ui/WrongEnvironmentScreen.d.ts.map +0 -1
  316. package/out-tsc/vitest/src/ui/index.d.ts +0 -2
  317. package/out-tsc/vitest/src/ui/index.d.ts.map +0 -1
  318. package/out-tsc/vitest/src/ui/state.d.ts +0 -20
  319. package/out-tsc/vitest/src/ui/state.d.ts.map +0 -1
  320. package/out-tsc/vitest/src/userEvent/index.d.ts +0 -6
  321. package/out-tsc/vitest/src/userEvent/index.d.ts.map +0 -1
  322. package/out-tsc/vitest/src/utils/dev-server.d.ts +0 -2
  323. package/out-tsc/vitest/src/utils/dev-server.d.ts.map +0 -1
  324. package/out-tsc/vitest/src/utils/emitter.d.ts +0 -16
  325. package/out-tsc/vitest/src/utils/emitter.d.ts.map +0 -1
  326. package/out-tsc/vitest/src/waitFor.d.ts +0 -21
  327. package/out-tsc/vitest/src/waitFor.d.ts.map +0 -1
  328. package/out-tsc/vitest/tsconfig.spec.tsbuildinfo +0 -1
  329. package/out-tsc/vitest/vite.config.d.ts +0 -3
  330. package/out-tsc/vitest/vite.config.d.ts.map +0 -1
  331. package/src/__tests__/collector.test.ts +0 -553
  332. package/src/__tests__/error-handling.test.ts +0 -132
  333. package/src/__tests__/expect.test.ts +0 -627
  334. package/src/__tests__/initialize.test.ts +0 -24
  335. package/src/__tests__/spy.test.ts +0 -538
  336. package/tsconfig.json +0 -16
  337. package/tsconfig.lib.json +0 -33
  338. package/tsconfig.spec.json +0 -34
  339. package/tsconfig.tsbuildinfo +0 -1
  340. package/vite.config.ts +0 -27
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import './globals.d.ts';
1
+ import './polyfills.js';
2
+ import './globals.js';
2
3
  export { UI as ReactNativeHarness } from './ui/index.js';
3
4
  export * from './spy/index.js';
4
5
  export * from './expect/index.js';
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "@react-native-harness/runtime",
3
- "version": "1.0.0-alpha.20",
3
+ "description": "The core test runtime that executes on React Native devices, providing Jest-compatible APIs (describe, it, expect) and managing test collection, execution, and result reporting in native environments.",
4
+ "version": "1.0.0-alpha.22",
4
5
  "type": "module",
5
6
  "main": "./dist/index.js",
6
7
  "module": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "src",
11
+ "lib",
12
+ "!**/__tests__",
13
+ "!**/__fixtures__",
14
+ "!**/__mocks__",
15
+ "!**/.*"
16
+ ],
8
17
  "exports": {
9
18
  "./package.json": "./package.json",
10
19
  ".": {
@@ -35,12 +44,21 @@
35
44
  "event-target-shim": "^6.0.2",
36
45
  "use-sync-external-store": "^1.6.0",
37
46
  "zustand": "^5.0.5",
38
- "@react-native-harness/bridge": "1.0.0-alpha.20"
47
+ "@react-native-harness/bridge": "1.0.0-alpha.22"
39
48
  },
40
49
  "devDependencies": {
41
50
  "@types/chai": "^5.2.2",
42
51
  "react": "*",
43
52
  "react-native": "*"
44
53
  },
54
+ "author": {
55
+ "name": "Szymon Chmal",
56
+ "email": "szymon.chmal@callstack.com"
57
+ },
58
+ "homepage": "https://github.com/callstackincubator/react-native-harness",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "https://github.com/callstackincubator/react-native-harness.git"
62
+ },
45
63
  "license": "MIT"
46
64
  }
@@ -20,7 +20,8 @@ export class BundlingFailedError extends Error {
20
20
  public readonly modulePath: string,
21
21
  public readonly reason: string
22
22
  ) {
23
- super(`Bundling of ${modulePath} failed`);
23
+ const reasonMessage = JSON.parse(reason).message ?? reason;
24
+ super(`Bundling of ${modulePath} failed with error:\n\n${reasonMessage}\n`);
24
25
  this.name = 'BundlingFailedError';
25
26
  }
26
27
  }
@@ -14,6 +14,7 @@ import { getBundler, evaluateModule, Bundler } from '../bundler/index.js';
14
14
  import { markTestsAsSkippedByName } from '../filtering/index.js';
15
15
  import { setup } from '../render/setup.js';
16
16
  import { runSetupFiles } from './setup-files.js';
17
+ import { setClient } from './store.js';
17
18
 
18
19
  export const getClient = async () => {
19
20
  const client = await getBridgeClient(getWSServer(), {
@@ -22,9 +23,11 @@ export const getClient = async () => {
22
23
  },
23
24
  });
24
25
 
26
+ setClient(client);
27
+
25
28
  client.rpc.$functions.runTests = async (
26
29
  path: string,
27
- options: TestExecutionOptions = {}
30
+ options: TestExecutionOptions
28
31
  ) => {
29
32
  if (store.getState().status === 'running') {
30
33
  throw new Error('Already running tests');
@@ -84,7 +87,11 @@ export const getClient = async () => {
84
87
  )
85
88
  : collectionResult.testSuite;
86
89
 
87
- const result = await runner.run(processedTestSuite, path);
90
+ const result = await runner.run({
91
+ testSuite: processedTestSuite,
92
+ testFilePath: path,
93
+ runner: options.runner,
94
+ });
88
95
  return result;
89
96
  } finally {
90
97
  collector?.dispose();
@@ -0,0 +1,16 @@
1
+ import type { BridgeClient } from '@react-native-harness/bridge/client';
2
+
3
+ let clientInstance: BridgeClient | null = null;
4
+
5
+ export const setClient = (client: BridgeClient): void => {
6
+ clientInstance = client;
7
+ };
8
+
9
+ export const getClientInstance = (): BridgeClient => {
10
+ if (!clientInstance) {
11
+ throw new Error(
12
+ 'Bridge client not initialized. This should not happen in normal operation.'
13
+ );
14
+ }
15
+ return clientInstance;
16
+ };
@@ -0,0 +1,127 @@
1
+ // This is adapted version of https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/integrations/chai/index.ts
2
+ // Credits to Vitest team for the original implementation.
3
+
4
+ import type { Assertion, ExpectStatic, MatcherState } from '@vitest/expect';
5
+ import {
6
+ addCustomEqualityTesters,
7
+ ASYMMETRIC_MATCHERS_OBJECT,
8
+ customMatchers,
9
+ getState,
10
+ GLOBAL_EXPECT,
11
+ setState,
12
+ } from '@vitest/expect';
13
+ import * as chai from 'chai';
14
+
15
+ // Setup additional matchers
16
+ import './setup.js';
17
+ import { toMatchImageSnapshot } from './matchers/toMatchImageSnapshot.js';
18
+
19
+ export function createExpect(): ExpectStatic {
20
+ const expect = ((value: unknown, message?: string): Assertion => {
21
+ const { assertionCalls } = getState(expect);
22
+ setState({ assertionCalls: assertionCalls + 1 }, expect);
23
+ return chai.expect(value, message) as unknown as Assertion;
24
+ }) as ExpectStatic;
25
+ Object.assign(expect, chai.expect);
26
+ Object.assign(
27
+ expect,
28
+ globalThis[ASYMMETRIC_MATCHERS_OBJECT as unknown as keyof typeof globalThis]
29
+ );
30
+
31
+ expect.getState = () => getState<MatcherState>(expect);
32
+ expect.setState = (state) => setState(state as Partial<MatcherState>, expect);
33
+
34
+ // @ts-expect-error global is not typed
35
+ const globalState = getState(globalThis[GLOBAL_EXPECT]) || {};
36
+
37
+ setState<MatcherState>(
38
+ {
39
+ // this should also add "snapshotState" that is added conditionally
40
+ ...globalState,
41
+ assertionCalls: 0,
42
+ isExpectingAssertions: false,
43
+ isExpectingAssertionsError: null,
44
+ expectedAssertionsNumber: null,
45
+ expectedAssertionsNumberErrorGen: null,
46
+ },
47
+ expect
48
+ );
49
+
50
+ // @ts-expect-error untyped
51
+ expect.extend = (matchers) => chai.expect.extend(expect, matchers);
52
+ // @ts-expect-error untyped
53
+ expect.addEqualityTesters = (customTesters) =>
54
+ addCustomEqualityTesters(customTesters);
55
+
56
+ // @ts-expect-error untyped
57
+ expect.soft = (...args) => {
58
+ // @ts-expect-error private soft access
59
+ return expect(...args).withContext({ soft: true }) as Assertion;
60
+ };
61
+
62
+ // @ts-expect-error untyped
63
+ expect.unreachable = (message?: string) => {
64
+ chai.assert.fail(
65
+ `expected${message ? ` "${message}" ` : ' '}not to be reached`
66
+ );
67
+ };
68
+
69
+ function assertions(expected: number) {
70
+ const errorGen = () =>
71
+ new Error(
72
+ `expected number of assertions to be ${expected}, but got ${
73
+ expect.getState().assertionCalls
74
+ }`
75
+ );
76
+ if (Error.captureStackTrace) {
77
+ Error.captureStackTrace(errorGen(), assertions);
78
+ }
79
+
80
+ expect.setState({
81
+ expectedAssertionsNumber: expected,
82
+ expectedAssertionsNumberErrorGen: errorGen,
83
+ });
84
+ }
85
+
86
+ function hasAssertions() {
87
+ const error = new Error('expected any number of assertion, but got none');
88
+ if (Error.captureStackTrace) {
89
+ Error.captureStackTrace(error, hasAssertions);
90
+ }
91
+
92
+ expect.setState({
93
+ isExpectingAssertions: true,
94
+ isExpectingAssertionsError: error,
95
+ });
96
+ }
97
+
98
+ chai.util.addMethod(expect, 'assertions', assertions);
99
+ chai.util.addMethod(expect, 'hasAssertions', hasAssertions);
100
+
101
+ expect.extend(customMatchers);
102
+ expect.extend({
103
+ toMatchImageSnapshot,
104
+ });
105
+
106
+ return expect;
107
+ }
108
+
109
+ const globalExpect: ExpectStatic = createExpect();
110
+
111
+ Object.defineProperty(globalThis, GLOBAL_EXPECT, {
112
+ value: globalExpect,
113
+ writable: true,
114
+ configurable: true,
115
+ });
116
+
117
+ export { assert, should } from 'chai';
118
+ export { chai, globalExpect as expect };
119
+
120
+ export type {
121
+ Assertion,
122
+ AsymmetricMatchersContaining,
123
+ DeeplyAllowMatchers,
124
+ ExpectStatic,
125
+ JestAssertion,
126
+ Matchers,
127
+ } from '@vitest/expect';
@@ -1,123 +1 @@
1
- // This is adapted version of https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/integrations/chai/index.ts
2
- // Credits to Vitest team for the original implementation.
3
-
4
- import type { Assertion, ExpectStatic, MatcherState } from '@vitest/expect';
5
- import {
6
- addCustomEqualityTesters,
7
- ASYMMETRIC_MATCHERS_OBJECT,
8
- customMatchers,
9
- getState,
10
- GLOBAL_EXPECT,
11
- setState,
12
- } from '@vitest/expect';
13
- import * as chai from 'chai';
14
-
15
- // Setup additional matchers
16
- import './setup.js';
17
-
18
- export function createExpect(): ExpectStatic {
19
- const expect = ((value: unknown, message?: string): Assertion => {
20
- const { assertionCalls } = getState(expect);
21
- setState({ assertionCalls: assertionCalls + 1 }, expect);
22
- return chai.expect(value, message) as unknown as Assertion;
23
- }) as ExpectStatic;
24
- Object.assign(expect, chai.expect);
25
- Object.assign(
26
- expect,
27
- globalThis[ASYMMETRIC_MATCHERS_OBJECT as unknown as keyof typeof globalThis]
28
- );
29
-
30
- expect.getState = () => getState<MatcherState>(expect);
31
- expect.setState = (state) => setState(state as Partial<MatcherState>, expect);
32
-
33
- // @ts-expect-error global is not typed
34
- const globalState = getState(globalThis[GLOBAL_EXPECT]) || {};
35
-
36
- setState<MatcherState>(
37
- {
38
- // this should also add "snapshotState" that is added conditionally
39
- ...globalState,
40
- assertionCalls: 0,
41
- isExpectingAssertions: false,
42
- isExpectingAssertionsError: null,
43
- expectedAssertionsNumber: null,
44
- expectedAssertionsNumberErrorGen: null,
45
- },
46
- expect
47
- );
48
-
49
- // @ts-expect-error untyped
50
- expect.extend = (matchers) => chai.expect.extend(expect, matchers);
51
- // @ts-expect-error untyped
52
- expect.addEqualityTesters = (customTesters) =>
53
- addCustomEqualityTesters(customTesters);
54
-
55
- // @ts-expect-error untyped
56
- expect.soft = (...args) => {
57
- // @ts-expect-error private soft access
58
- return expect(...args).withContext({ soft: true }) as Assertion;
59
- };
60
-
61
- // @ts-expect-error untyped
62
- expect.unreachable = (message?: string) => {
63
- chai.assert.fail(
64
- `expected${message ? ` "${message}" ` : ' '}not to be reached`
65
- );
66
- };
67
-
68
- function assertions(expected: number) {
69
- const errorGen = () =>
70
- new Error(
71
- `expected number of assertions to be ${expected}, but got ${
72
- expect.getState().assertionCalls
73
- }`
74
- );
75
- if (Error.captureStackTrace) {
76
- Error.captureStackTrace(errorGen(), assertions);
77
- }
78
-
79
- expect.setState({
80
- expectedAssertionsNumber: expected,
81
- expectedAssertionsNumberErrorGen: errorGen,
82
- });
83
- }
84
-
85
- function hasAssertions() {
86
- const error = new Error('expected any number of assertion, but got none');
87
- if (Error.captureStackTrace) {
88
- Error.captureStackTrace(error, hasAssertions);
89
- }
90
-
91
- expect.setState({
92
- isExpectingAssertions: true,
93
- isExpectingAssertionsError: error,
94
- });
95
- }
96
-
97
- chai.util.addMethod(expect, 'assertions', assertions);
98
- chai.util.addMethod(expect, 'hasAssertions', hasAssertions);
99
-
100
- expect.extend(customMatchers);
101
-
102
- return expect;
103
- }
104
-
105
- const globalExpect: ExpectStatic = createExpect();
106
-
107
- Object.defineProperty(globalThis, GLOBAL_EXPECT, {
108
- value: globalExpect,
109
- writable: true,
110
- configurable: true,
111
- });
112
-
113
- export { assert, should } from 'chai';
114
- export { chai, globalExpect as expect };
115
-
116
- export type {
117
- Assertion,
118
- AsymmetricMatchersContaining,
119
- DeeplyAllowMatchers,
120
- ExpectStatic,
121
- JestAssertion,
122
- Matchers,
123
- } from '@vitest/expect';
1
+ export * from './expect.js';
@@ -0,0 +1,50 @@
1
+ import { getClientInstance } from '../../client/store.js';
2
+ import type { MatcherState } from '@vitest/expect';
3
+ import {
4
+ type ImageSnapshotOptions,
5
+ generateTransferId,
6
+ } from '@react-native-harness/bridge';
7
+ import { getHarnessContext } from '../../runner/index.js';
8
+
9
+ type ScreenshotResult = {
10
+ data: Uint8Array;
11
+ width: number;
12
+ height: number;
13
+ };
14
+
15
+ export async function toMatchImageSnapshot(
16
+ this: MatcherState,
17
+ received: ScreenshotResult,
18
+ options: ImageSnapshotOptions
19
+ ): Promise<{ pass: boolean; message: () => string }> {
20
+ const client = getClientInstance();
21
+ const context = getHarnessContext();
22
+
23
+ const transferId = generateTransferId();
24
+ client.sendBinary(transferId, received.data);
25
+
26
+ const screenshotFile = await client.rpc['device.screenshot.receive'](
27
+ {
28
+ type: 'binary',
29
+ transferId,
30
+ size: received.data.length,
31
+ mimeType: 'image/png',
32
+ },
33
+ {
34
+ width: received.width,
35
+ height: received.height,
36
+ }
37
+ );
38
+
39
+ const result = await client.rpc['test.matchImageSnapshot'](
40
+ screenshotFile,
41
+ context.testFilePath,
42
+ options,
43
+ context.runner
44
+ );
45
+
46
+ return {
47
+ pass: result.pass,
48
+ message: () => result.message,
49
+ };
50
+ }
package/src/globals.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { ImageSnapshotOptions } from '@react-native-harness/bridge';
2
+
1
3
  export type HarnessGlobal = {
2
4
  appRegistryComponentName: string;
3
5
  webSocketPort?: number;
@@ -7,6 +9,16 @@ declare global {
7
9
  var RN_HARNESS: HarnessGlobal | undefined;
8
10
  }
9
11
 
12
+ declare module '@vitest/expect' {
13
+ interface Matchers {
14
+ /**
15
+ * Match the received screenshot against a stored snapshot.
16
+ * Creates a new snapshot if one doesn't exist.
17
+ */
18
+ toMatchImageSnapshot(options: ImageSnapshotOptions): Promise<void>;
19
+ }
20
+ }
21
+
10
22
  export const getHarnessGlobal = (): HarnessGlobal => {
11
23
  const harnessGlobal = global.RN_HARNESS;
12
24
 
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- import './globals.d.ts';
1
+ import './polyfills.js';
2
+ import './globals.js';
2
3
 
3
4
  export { UI as ReactNativeHarness } from './ui/index.js';
4
5
  export * from './spy/index.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Polyfills for ES2022+ features not supported by JSC (JavaScriptCore).
3
+ *
4
+ * JSC, used in React Native when Hermes is disabled, doesn't support
5
+ * Object.hasOwn (ES2022). This causes runtime errors when @vitest/expect
6
+ * v4.x initializes.
7
+ *
8
+ * This polyfill must be loaded before any code that uses Object.hasOwn.
9
+ */
10
+
11
+ if (typeof Object.hasOwn !== 'function') {
12
+ Object.hasOwn = (obj: object, prop: PropertyKey): boolean =>
13
+ Object.prototype.hasOwnProperty.call(obj, prop);
14
+ }
@@ -4,6 +4,21 @@ import { useRenderedElement } from '../ui/state.js';
4
4
  import { store } from '../ui/state.js';
5
5
  import { ErrorBoundary } from './ErrorBoundary.js';
6
6
 
7
+ /**
8
+ * Waits for the native view hierarchy to be fully updated.
9
+ * Uses double requestAnimationFrame to ensure native has processed
10
+ * all view creation commands after React's commit phase.
11
+ */
12
+ const waitForNativeViewHierarchy = (): Promise<void> => {
13
+ return new Promise((resolve) => {
14
+ requestAnimationFrame(() => {
15
+ requestAnimationFrame(() => {
16
+ resolve();
17
+ });
18
+ });
19
+ });
20
+ };
21
+
7
22
  export const TestComponentOverlay = (): React.ReactElement | null => {
8
23
  const { element, key } = useRenderedElement();
9
24
 
@@ -12,8 +27,13 @@ export const TestComponentOverlay = (): React.ReactElement | null => {
12
27
  const callback = store.getState().onRenderCallback;
13
28
 
14
29
  if (callback) {
15
- callback();
16
- store.getState().setOnRenderCallback(null);
30
+ // Wait for native view hierarchy to be fully updated before calling callback.
31
+ // useEffect fires after React commits, but native processes commands async.
32
+ // Double rAF ensures native has finished processing all view creation.
33
+ waitForNativeViewHierarchy().then(() => {
34
+ callback();
35
+ store.getState().setOnRenderCallback(null);
36
+ });
17
37
  }
18
38
  }, [element]);
19
39
 
@@ -25,16 +25,18 @@ export const render = async (
25
25
  store.getState().setOnRenderCallback(null);
26
26
  }
27
27
 
28
- // Create a promise that resolves when the element is laid out
29
- const layoutPromise = new Promise<void>((resolve, reject) => {
28
+ // Create a promise that resolves when the element is rendered.
29
+ // We use onRenderCallback which fires in useEffect, guaranteeing that
30
+ // React has committed all children to the native view hierarchy.
31
+ const renderPromise = new Promise<void>((resolve, reject) => {
30
32
  const timeoutId = setTimeout(() => {
31
- store.getState().setOnLayoutCallback(null);
33
+ store.getState().setOnRenderCallback(null);
32
34
  reject(
33
35
  new Error(`Render timeout: Element did not mount within ${timeout}ms`)
34
36
  );
35
37
  }, timeout);
36
38
 
37
- store.getState().setOnLayoutCallback(() => {
39
+ store.getState().setOnRenderCallback(() => {
38
40
  clearTimeout(timeoutId);
39
41
  resolve();
40
42
  });
@@ -44,8 +46,8 @@ export const render = async (
44
46
  const wrappedElement = wrapElement(element, wrapper);
45
47
  store.getState().setRenderedElement(wrappedElement);
46
48
 
47
- // Wait for layout
48
- await layoutPromise;
49
+ // Wait for useEffect to fire, ensuring all children are committed
50
+ await renderPromise;
49
51
 
50
52
  const rerender = async (newElement: React.ReactElement): Promise<void> => {
51
53
  if (store.getState().renderedElement === null) {
@@ -0,0 +1,16 @@
1
+ export type HarnessContext = {
2
+ testFilePath: string;
3
+ runner: string;
4
+ };
5
+
6
+ declare global {
7
+ var HARNESS_CONTEXT: HarnessContext;
8
+ }
9
+
10
+ export const getHarnessContext = (): HarnessContext => {
11
+ return globalThis['HARNESS_CONTEXT'];
12
+ };
13
+
14
+ export const setHarnessContext = (context: HarnessContext): void => {
15
+ globalThis['HARNESS_CONTEXT'] = context;
16
+ };
@@ -2,13 +2,19 @@ import type { TestRunnerEvents } from '@react-native-harness/bridge';
2
2
  import { getEmitter } from '../utils/emitter.js';
3
3
  import { runSuite } from './runSuite.js';
4
4
  import { TestRunner } from './types.js';
5
+ import { setHarnessContext } from './context.js';
5
6
 
6
7
  export const getTestRunner = (): TestRunner => {
7
8
  const events = getEmitter<TestRunnerEvents>();
8
9
 
9
10
  return {
10
11
  events,
11
- run: async (testSuite, testFilePath) => {
12
+ run: async ({ testSuite, testFilePath, runner }) => {
13
+ setHarnessContext({
14
+ testFilePath,
15
+ runner,
16
+ });
17
+
12
18
  const result = await runSuite(testSuite, {
13
19
  events,
14
20
  testFilePath,
@@ -5,3 +5,8 @@ export type {
5
5
  } from './types.js';
6
6
  export { TestExecutionError } from './errors.js';
7
7
  export { getTestRunner } from './factory.js';
8
+ export {
9
+ getHarnessContext,
10
+ setHarnessContext,
11
+ type HarnessContext,
12
+ } from './context.js';
@@ -8,6 +8,10 @@ import { runHooks } from './hooks.js';
8
8
  import { getTestExecutionError } from './errors.js';
9
9
  import { TestRunnerContext } from './types.js';
10
10
 
11
+ declare global {
12
+ var HARNESS_TEST_PATH: string;
13
+ }
14
+
11
15
  const runTest = async (
12
16
  test: TestCase,
13
17
  suite: TestSuite,
@@ -12,8 +12,14 @@ export type TestRunnerContext = {
12
12
  testFilePath: string;
13
13
  };
14
14
 
15
+ export type RunTestsOptions = {
16
+ testSuite: TestSuite;
17
+ testFilePath: string;
18
+ runner: string;
19
+ };
20
+
15
21
  export type TestRunner = {
16
22
  events: TestRunnerEventsEmitter;
17
- run: (testSuite: TestSuite, testFilePath: string) => Promise<TestSuiteResult>;
23
+ run: (options: RunTestsOptions) => Promise<TestSuiteResult>;
18
24
  dispose: () => void;
19
25
  };
@@ -16,6 +16,7 @@ export const ReadyScreen = () => {
16
16
 
17
17
  return (
18
18
  <View style={styles.container}>
19
+ <StatusBar hidden={true} />
19
20
  <View style={styles.contentContainer}>
20
21
  <Text style={styles.title}>React Native Harness</Text>
21
22
 
package/.babelrc.js DELETED
@@ -1,23 +0,0 @@
1
- module.exports = function (api) {
2
- api.cache(true);
3
-
4
- return {
5
- presets: [
6
- [
7
- '@nx/react/babel',
8
- {
9
- runtime: 'automatic',
10
- useBuiltIns: 'usage',
11
- },
12
- ],
13
- ],
14
- plugins: [],
15
- env: {
16
- test: {
17
- presets: [
18
- ['module:@react-native/babel-preset', { useTransformReactJSX: true }],
19
- ],
20
- },
21
- },
22
- };
23
- };