@jterrazz/test 16.1.1 → 18.0.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 (309) hide show
  1. package/README.md +45 -171
  2. package/dist/browser/index.js +1 -2980
  3. package/dist/browser/services.js +1 -0
  4. package/dist/browser/stubs.js +1 -0
  5. package/dist/browser-shared-index.browser.js +3233 -0
  6. package/dist/checker.d.ts +6 -1
  7. package/dist/checker.js +3721 -2624
  8. package/dist/config.d.ts +40 -0
  9. package/dist/config.js +309 -0
  10. package/dist/in-place.global-setup.d.ts +12 -0
  11. package/dist/in-place.global-setup.js +21 -0
  12. package/dist/index.d.ts +1641 -3004
  13. package/dist/index.js +5095 -3835
  14. package/dist/mobile-session.global-setup.d.ts +15 -0
  15. package/dist/mobile-session.global-setup.js +31 -0
  16. package/dist/oxlint.cjs +6069 -5634
  17. package/dist/oxlint.d.cts +43 -7212
  18. package/dist/oxlint.d.ts +43 -7212
  19. package/dist/oxlint.js +3979 -5799
  20. package/dist/route-coverage.global-setup.d.ts +43 -0
  21. package/dist/route-coverage.global-setup.js +1 -0
  22. package/dist/services.d.ts +3 -0
  23. package/dist/services.global-setup.d.ts +43 -0
  24. package/dist/services.global-setup.js +1 -0
  25. package/dist/services.js +597 -0
  26. package/dist/shared-appium.adapter.js +428 -0
  27. package/dist/shared-artifacts.js +34 -0
  28. package/dist/shared-chapter.js +7 -0
  29. package/dist/shared-container.port.d.ts +31 -0
  30. package/dist/shared-derived-projects.js +73 -0
  31. package/dist/shared-device.port.d.ts +189 -0
  32. package/dist/shared-exchange.js +447 -0
  33. package/dist/shared-in-place.js +172 -0
  34. package/dist/shared-index.services.d.ts +346 -0
  35. package/dist/shared-index.stubs.d.ts +287 -0
  36. package/dist/shared-json-shape.js +17 -0
  37. package/dist/shared-landmarks.js +71 -0
  38. package/dist/shared-ledger.js +68 -0
  39. package/dist/shared-listened-port.js +19 -0
  40. package/dist/shared-literate-plugin.js +184 -0
  41. package/dist/shared-manifest.js +1321 -0
  42. package/dist/shared-pause.js +22 -0
  43. package/dist/shared-peer.js +88 -0
  44. package/dist/shared-playwright.adapter.js +328 -0
  45. package/dist/shared-process-alive.js +13 -0
  46. package/dist/shared-process.adapter.js +74 -0
  47. package/dist/shared-record-key.js +13 -0
  48. package/dist/shared-refusal.js +35 -0
  49. package/dist/shared-resolve.js +24 -0
  50. package/dist/shared-route-coverage.global-setup.js +187 -0
  51. package/dist/shared-scenario-failure.js +198 -0
  52. package/dist/shared-serve.js +407 -0
  53. package/dist/shared-service-images.js +10 -0
  54. package/dist/shared-services.global-setup.js +174 -0
  55. package/dist/shared-session-ledger.js +305 -0
  56. package/dist/shared-setup-entry.js +12 -0
  57. package/dist/shared-source-scan.js +133 -0
  58. package/dist/shared-spec-document.js +303 -0
  59. package/dist/shared-spec-grammar.js +66 -0
  60. package/dist/shared-specifications.js +26 -0
  61. package/dist/shared-specs-modules.js +1033 -0
  62. package/dist/shared-stub-reply.js +167 -0
  63. package/dist/shared-stub-server.js +142 -0
  64. package/dist/shared-temp-root.global-setup.js +73 -0
  65. package/dist/shared-test-baseline.js +26 -0
  66. package/dist/shared-test-section.js +58 -0
  67. package/dist/shared-testcontainers.adapter.js +306 -0
  68. package/dist/shared-tokens.js +55 -0
  69. package/dist/shared-yaml-document.js +119 -0
  70. package/dist/stubs.d.ts +2 -0
  71. package/dist/stubs.js +657 -0
  72. package/dist/temp-root.global-setup.d.ts +7 -0
  73. package/dist/temp-root.global-setup.js +1 -0
  74. package/dist/vitest.d.ts +93 -164
  75. package/dist/vitest.js +382 -550
  76. package/docs/README.md +82 -36
  77. package/docs/concepts/architecture.md +118 -0
  78. package/docs/concepts/conventions.md +333 -0
  79. package/docs/guides/api-specs.md +374 -0
  80. package/docs/guides/cli-specs.md +566 -0
  81. package/docs/guides/component-specs.md +252 -0
  82. package/docs/guides/develop.md +97 -0
  83. package/docs/guides/integration-specs.md +181 -0
  84. package/docs/guides/job-specs.md +180 -0
  85. package/docs/guides/lint-specs.md +174 -0
  86. package/docs/guides/mobile-specs.md +319 -0
  87. package/docs/guides/module-tests.md +149 -0
  88. package/docs/guides/release.md +44 -0
  89. package/docs/guides/spec-documents.md +189 -0
  90. package/docs/guides/website-specs.md +413 -0
  91. package/docs/reference/assertions.md +345 -0
  92. package/docs/reference/glossary.md +20 -0
  93. package/docs/reference/linting.md +483 -0
  94. package/docs/reference/services.md +236 -0
  95. package/docs/reference/stubs.md +500 -0
  96. package/docs/reference/time-and-doubles.md +137 -0
  97. package/docs/reference/tokens.md +258 -0
  98. package/docs/reference/visitor.md +221 -0
  99. package/docs/start/getting-started.md +187 -0
  100. package/docs/start/set-up-a-project.md +228 -0
  101. package/package.json +56 -36
  102. package/schema/spec.schema.json +43 -17
  103. package/dist/appium.adapter.js +0 -489
  104. package/dist/artifacts.js +0 -32
  105. package/dist/fixtures.js +0 -110
  106. package/dist/literate.js +0 -1747
  107. package/dist/match.js +0 -804
  108. package/dist/playwright.adapter.js +0 -438
  109. package/dist/server.js +0 -160
  110. package/dist/stream.js +0 -321
  111. package/dist/substring-warning.js +0 -282
  112. package/docs/01-architecture.md +0 -95
  113. package/docs/02-developing.md +0 -351
  114. package/docs/03-testing.md +0 -495
  115. package/docs/04-operating.md +0 -101
  116. package/docs/05-module-tests.md +0 -152
  117. package/docs/06-integration.md +0 -180
  118. package/docs/07-component.md +0 -246
  119. package/docs/08-website.md +0 -404
  120. package/docs/09-mobile.md +0 -270
  121. package/docs/10-api.md +0 -264
  122. package/docs/11-jobs.md +0 -157
  123. package/docs/12-cli.md +0 -677
  124. package/docs/13-elements.md +0 -248
  125. package/docs/14-assertions.md +0 -314
  126. package/docs/15-tokens.md +0 -253
  127. package/docs/16-contracts.md +0 -438
  128. package/docs/17-services.md +0 -188
  129. package/docs/18-conventions.md +0 -332
  130. package/docs/19-linting.md +0 -512
  131. package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
  132. package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
  133. package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
  134. package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
  135. package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
  136. package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
  137. package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
  138. package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
  139. package/docs/decisions/_template.md +0 -22
  140. package/docs/reference/classes/Matcher.md +0 -120
  141. package/docs/reference/classes/ProcessHandle.md +0 -368
  142. package/docs/reference/functions/defineContract.md +0 -39
  143. package/docs/reference/functions/defineContracts.md +0 -36
  144. package/docs/reference/functions/disabled.md +0 -28
  145. package/docs/reference/functions/enabled.md +0 -20
  146. package/docs/reference/functions/focused.md +0 -26
  147. package/docs/reference/functions/mockOf.md +0 -79
  148. package/docs/reference/functions/postgres.md +0 -31
  149. package/docs/reference/functions/process.md +0 -33
  150. package/docs/reference/functions/redis.md +0 -28
  151. package/docs/reference/functions/required.md +0 -41
  152. package/docs/reference/functions/selected.md +0 -26
  153. package/docs/reference/functions/sqlite.md +0 -33
  154. package/docs/reference/functions/testId.md +0 -20
  155. package/docs/reference/functions/text.md +0 -40
  156. package/docs/reference/functions/valued.md +0 -28
  157. package/docs/reference/functions/waitUntil.md +0 -46
  158. package/docs/reference/functions/within.md +0 -29
  159. package/docs/reference/index.md +0 -196
  160. package/docs/reference/interfaces/BaseResult.md +0 -87
  161. package/docs/reference/interfaces/CallResult.md +0 -160
  162. package/docs/reference/interfaces/CaptureScope.md +0 -82
  163. package/docs/reference/interfaces/CliResult.md +0 -253
  164. package/docs/reference/interfaces/ContainerAccessor.md +0 -166
  165. package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
  166. package/docs/reference/interfaces/FetchResult.md +0 -180
  167. package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
  168. package/docs/reference/interfaces/HttpResult.md +0 -126
  169. package/docs/reference/interfaces/JsonAccessor.md +0 -46
  170. package/docs/reference/interfaces/PageResult.md +0 -356
  171. package/docs/reference/interfaces/RenderResult.md +0 -72
  172. package/docs/reference/interfaces/RenderedText.md +0 -110
  173. package/docs/reference/interfaces/ResponseAccessor.md +0 -47
  174. package/docs/reference/interfaces/ScreenResult.md +0 -128
  175. package/docs/reference/interfaces/TableAccessor.md +0 -26
  176. package/docs/reference/interfaces/TextAccessor.md +0 -96
  177. package/docs/reference/type-aliases/ApiHandle.md +0 -68
  178. package/docs/reference/type-aliases/ApiSpecification.md +0 -213
  179. package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
  180. package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
  181. package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
  182. package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
  183. package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
  184. package/docs/reference/type-aliases/BrowserPage.md +0 -132
  185. package/docs/reference/type-aliases/BrowserPort.md +0 -51
  186. package/docs/reference/type-aliases/CliEnv.md +0 -11
  187. package/docs/reference/type-aliases/CliHandle.md +0 -80
  188. package/docs/reference/type-aliases/CliInput.md +0 -36
  189. package/docs/reference/type-aliases/CliOutput.md +0 -46
  190. package/docs/reference/type-aliases/CliPort.md +0 -58
  191. package/docs/reference/type-aliases/CliSpecification.md +0 -144
  192. package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
  193. package/docs/reference/type-aliases/ComponentChain.md +0 -156
  194. package/docs/reference/type-aliases/ComponentScenario.md +0 -20
  195. package/docs/reference/type-aliases/ComponentUi.md +0 -47
  196. package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
  197. package/docs/reference/type-aliases/ContainerPort.md +0 -113
  198. package/docs/reference/type-aliases/Contract.md +0 -68
  199. package/docs/reference/type-aliases/ContractInput.md +0 -13
  200. package/docs/reference/type-aliases/ContractRequest.md +0 -95
  201. package/docs/reference/type-aliases/ContractResponder.md +0 -23
  202. package/docs/reference/type-aliases/ContractResponse.md +0 -78
  203. package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
  204. package/docs/reference/type-aliases/Contracts.md +0 -48
  205. package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
  206. package/docs/reference/type-aliases/DatabasePort.md +0 -72
  207. package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
  208. package/docs/reference/type-aliases/DevicePort.md +0 -51
  209. package/docs/reference/type-aliases/DeviceScreen.md +0 -32
  210. package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
  211. package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
  212. package/docs/reference/type-aliases/DomMount.md +0 -21
  213. package/docs/reference/type-aliases/ElementKind.md +0 -27
  214. package/docs/reference/type-aliases/ElementMatch.md +0 -74
  215. package/docs/reference/type-aliases/ElementOptions.md +0 -29
  216. package/docs/reference/type-aliases/ElementRef.md +0 -128
  217. package/docs/reference/type-aliases/ExecOptions.md +0 -37
  218. package/docs/reference/type-aliases/FileAccessor.md +0 -54
  219. package/docs/reference/type-aliases/HonoApp.md +0 -31
  220. package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
  221. package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
  222. package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
  223. package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
  224. package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
  225. package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
  226. package/docs/reference/type-aliases/Intercept.md +0 -19
  227. package/docs/reference/type-aliases/InterceptOptions.md +0 -28
  228. package/docs/reference/type-aliases/InterceptScope.md +0 -11
  229. package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
  230. package/docs/reference/type-aliases/JobHandle.md +0 -34
  231. package/docs/reference/type-aliases/JobsHandle.md +0 -57
  232. package/docs/reference/type-aliases/JobsResult.md +0 -16
  233. package/docs/reference/type-aliases/JobsSpecification.md +0 -94
  234. package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
  235. package/docs/reference/type-aliases/LandmarkKind.md +0 -20
  236. package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
  237. package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
  238. package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
  239. package/docs/reference/type-aliases/MatchableRequest.md +0 -61
  240. package/docs/reference/type-aliases/MatcherKind.md +0 -34
  241. package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
  242. package/docs/reference/type-aliases/MobileElementKind.md +0 -14
  243. package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
  244. package/docs/reference/type-aliases/MobileElementRef.md +0 -20
  245. package/docs/reference/type-aliases/MobileHandle.md +0 -65
  246. package/docs/reference/type-aliases/MobileScenario.md +0 -20
  247. package/docs/reference/type-aliases/MobileSpecification.md +0 -51
  248. package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
  249. package/docs/reference/type-aliases/MobileVisitor.md +0 -81
  250. package/docs/reference/type-aliases/MockOfOptions.md +0 -26
  251. package/docs/reference/type-aliases/MockPort.md +0 -10
  252. package/docs/reference/type-aliases/PinnedClock.md +0 -10
  253. package/docs/reference/type-aliases/PostgresOptions.md +0 -32
  254. package/docs/reference/type-aliases/ProcessOptions.md +0 -112
  255. package/docs/reference/type-aliases/RedisOptions.md +0 -20
  256. package/docs/reference/type-aliases/RenderSubject.md +0 -12
  257. package/docs/reference/type-aliases/ScreenNode.md +0 -67
  258. package/docs/reference/type-aliases/ServerPort.md +0 -36
  259. package/docs/reference/type-aliases/ServerResponse.md +0 -46
  260. package/docs/reference/type-aliases/ServerSpec.md +0 -21
  261. package/docs/reference/type-aliases/ServiceHandle.md +0 -216
  262. package/docs/reference/type-aliases/ServiceRecord.md +0 -12
  263. package/docs/reference/type-aliases/SpecDocument.md +0 -84
  264. package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
  265. package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
  266. package/docs/reference/type-aliases/SpecFixture.md +0 -30
  267. package/docs/reference/type-aliases/SpecKind.md +0 -8
  268. package/docs/reference/type-aliases/SpecRun.md +0 -108
  269. package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
  270. package/docs/reference/type-aliases/SpecStream.md +0 -32
  271. package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
  272. package/docs/reference/type-aliases/SqliteOptions.md +0 -36
  273. package/docs/reference/type-aliases/SseEvent.md +0 -58
  274. package/docs/reference/type-aliases/StreamBody.md +0 -61
  275. package/docs/reference/type-aliases/TextFilter.md +0 -22
  276. package/docs/reference/type-aliases/VisitScenario.md +0 -20
  277. package/docs/reference/type-aliases/Visitor.md +0 -215
  278. package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
  279. package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
  280. package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
  281. package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
  282. package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
  283. package/docs/reference/variables/anthropic.md +0 -19
  284. package/docs/reference/variables/banner.md +0 -10
  285. package/docs/reference/variables/button.md +0 -10
  286. package/docs/reference/variables/clock.md +0 -16
  287. package/docs/reference/variables/complementary.md +0 -10
  288. package/docs/reference/variables/component.md +0 -10
  289. package/docs/reference/variables/content.md +0 -10
  290. package/docs/reference/variables/contentinfo.md +0 -10
  291. package/docs/reference/variables/dialog.md +0 -10
  292. package/docs/reference/variables/field.md +0 -10
  293. package/docs/reference/variables/form.md +0 -10
  294. package/docs/reference/variables/heading.md +0 -10
  295. package/docs/reference/variables/http.md +0 -40
  296. package/docs/reference/variables/intercept.md +0 -11
  297. package/docs/reference/variables/link.md +0 -10
  298. package/docs/reference/variables/listitem.md +0 -10
  299. package/docs/reference/variables/main.md +0 -10
  300. package/docs/reference/variables/match.md +0 -8
  301. package/docs/reference/variables/navigation.md +0 -10
  302. package/docs/reference/variables/openai.md +0 -21
  303. package/docs/reference/variables/option.md +0 -17
  304. package/docs/reference/variables/region.md +0 -10
  305. package/docs/reference/variables/row.md +0 -10
  306. package/docs/reference/variables/search.md +0 -10
  307. package/docs/reference/variables/specification.md +0 -8
  308. package/docs/reference/variables/status.md +0 -10
  309. package/docs/reference/variables/table.md +0 -10
@@ -0,0 +1,305 @@
1
+ import { t as ServedProcess } from "./shared-serve.js";
2
+ import { t as isRecord } from "./shared-json-shape.js";
3
+ import { n as refusal } from "./shared-refusal.js";
4
+ import { u as VITEST_ARTIFACTS_DIR } from "./shared-artifacts.js";
5
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
6
+ import { join, resolve } from "node:path";
7
+ import { execFile } from "node:child_process";
8
+ //#region src/specifications/mobile/server/simulator.ts
9
+ async function run(command, args, options = {}) {
10
+ return await new Promise((resolve, reject) => {
11
+ execFile(command, args, {
12
+ encoding: "utf8",
13
+ ...options
14
+ }, (error, stdout) => {
15
+ if (error === null) resolve({ stdout });
16
+ else reject(error);
17
+ });
18
+ });
19
+ }
20
+ let firstLaunch;
21
+ async function simctl(args, options = {}) {
22
+ firstLaunch ??= run("xcodebuild", ["-checkFirstLaunchStatus"]);
23
+ await firstLaunch;
24
+ return await run("xcrun", ["simctl", ...args], options);
25
+ }
26
+ const BOOT_TIMEOUT_MS = 12e4;
27
+ const isSimctlDevices = (value) => isRecord(value) && Object.values(value).every((devices) => Array.isArray(devices));
28
+ const RUNTIME_KEY = /^.*SimRuntime\.(?<platform>[A-Za-z]+)-(?<version>[\d-]+)$/u;
29
+ const WORDS = /\s+/u;
30
+ function runtimeLabel(runtime) {
31
+ return runtime.replace(RUNTIME_KEY, (_key, platform, version) => `${platform} ${version.replaceAll("-", ".")}`);
32
+ }
33
+ function listSimulators(listJson) {
34
+ const parsed = JSON.parse(listJson);
35
+ const devices = isRecord(parsed) && isSimctlDevices(parsed.devices) ? parsed.devices : {};
36
+ return Object.entries(devices).flatMap(([runtime, listed]) => listed.filter((device) => device.isAvailable !== false).map((device) => ({
37
+ name: device.name,
38
+ os: runtimeLabel(runtime),
39
+ state: device.state,
40
+ udid: device.udid
41
+ })));
42
+ }
43
+ function runtimeOf(os) {
44
+ const words = os.trim().split(WORDS);
45
+ const platform = String(words[0]).toLowerCase();
46
+ return {
47
+ platform: words.length > 1 ? platform : void 0,
48
+ version: String(words.at(-1))
49
+ };
50
+ }
51
+ /** `'iOS 26.5'` and `'26.5'` name one runtime; two naming platforms must name the same one. */
52
+ function sameRuntime(one, other) {
53
+ const [a, b] = [runtimeOf(one), runtimeOf(other)];
54
+ return a.version === b.version && (a.platform === void 0 || b.platform === void 0 || a.platform === b.platform);
55
+ }
56
+ function formatListing(devices) {
57
+ return devices.map((device) => ` ${device.name}: ${device.os} (${device.state}) ${device.udid}`).join("\n");
58
+ }
59
+ function selectSimulator(listJson, criteria) {
60
+ const available = listSimulators(listJson);
61
+ const matches = available.filter((device) => device.name === criteria.name && (criteria.os === void 0 || sameRuntime(device.os, criteria.os)));
62
+ const wanted = criteria.os === void 0 ? `"${criteria.name}"` : `"${criteria.name}" on ${criteria.os}`;
63
+ const [only] = matches;
64
+ if (only === void 0) throw new Error(refusal({
65
+ fix: "name one of the available devices, or create the simulator in Xcode",
66
+ found: `specification.mobile(): no simulator matches ${wanted}.\navailable devices:\n${formatListing(available)}`,
67
+ read: "docs/guides/mobile-specs.md#options",
68
+ why: "Resolution refuses rather than guesses: a device names exactly one simulator."
69
+ }));
70
+ if (matches.length > 1) throw new Error(refusal({
71
+ fix: "add `os:` or `udid:` to pick one",
72
+ found: `specification.mobile(): ${matches.length} simulators match ${wanted}.\nmatched:\n${formatListing(matches)}`,
73
+ read: "docs/guides/mobile-specs.md#options",
74
+ why: "Resolution refuses rather than guesses: a device names exactly one simulator."
75
+ }));
76
+ return only;
77
+ }
78
+ async function resolveSimulatorUdid(criteria) {
79
+ const { stdout } = await simctl([
80
+ "list",
81
+ "devices",
82
+ "--json"
83
+ ], { maxBuffer: 16777216 });
84
+ return selectSimulator(stdout, criteria).udid;
85
+ }
86
+ /** Idempotent: an already-booted device is tolerated. */
87
+ async function ensureBooted(udid) {
88
+ try {
89
+ await simctl(["boot", udid]);
90
+ } catch (error) {
91
+ const message = error instanceof Error ? error.message : String(error);
92
+ if (!message.includes("current state: Booted")) throw new Error(refusal({
93
+ fix: "finish Xcode's first launch if it asks (`sudo xcodebuild -runFirstLaunch`), boot it by hand (`xcrun simctl boot <udid>`) and correct what simctl names",
94
+ found: `specification.mobile(): could not boot simulator ${udid}.\n${message}`,
95
+ read: "docs/guides/mobile-specs.md#requirements-what-the-machine-must-have",
96
+ why: "A mobile spec runs on a simulator the machine can boot."
97
+ }), { cause: error });
98
+ }
99
+ await simctl([
100
+ "bootstatus",
101
+ udid,
102
+ "-b"
103
+ ], { timeout: BOOT_TIMEOUT_MS });
104
+ }
105
+ //#endregion
106
+ //#region src/specifications/mobile/server/appium-server.ts
107
+ const APPIUM_READY_TIMEOUT_MS = 6e4;
108
+ const APPIUM_STATUS = "/status";
109
+ /** outside: the XCUITest driver runs WebDriverAgent's xcodebuild in a group of its own and keeps it past the session and past its own exit, beside the runner app it launches inside the simulator. */
110
+ function webDriverAgentOf(udid) {
111
+ return [{
112
+ holds: ["WebDriverAgent", `id=${udid}`],
113
+ program: "xcodebuild"
114
+ }, {
115
+ holds: [`/Devices/${udid}/`],
116
+ program: "WebDriverAgentRunner-Runner"
117
+ }];
118
+ }
119
+ /** Appium is an optional peer resolved from `node_modules/.bin`; the served process owns port, readiness, the group kill, and the WebDriverAgent the server spawned for the simulator. */
120
+ async function startAppiumServer(root, udid) {
121
+ const bin = resolve(root, "node_modules", ".bin", "appium");
122
+ if (!existsSync(bin)) throw new Error(refusal("seam-is-an-optional-peer", {
123
+ fix: "npm install -D appium webdriverio && npx appium driver install xcuitest",
124
+ found: `specification.mobile() requires \`appium\`, and ${bin} does not exist`
125
+ }));
126
+ const serve = new ServedProcess({
127
+ command: `"${bin}" --port "$PORT"`,
128
+ ready: APPIUM_STATUS,
129
+ strays: webDriverAgentOf(udid),
130
+ timeout: APPIUM_READY_TIMEOUT_MS
131
+ }, root, "mobile");
132
+ return {
133
+ stop: async () => {
134
+ await serve.stop();
135
+ },
136
+ url: await serve.start()
137
+ };
138
+ }
139
+ /** The booted simulator is the machine's and outlives the server; its WebDriverAgent is the server's, and does not. */
140
+ async function serveAppium(device, root) {
141
+ const udid = device.udid ?? await resolveSimulatorUdid(device);
142
+ await ensureBooted(udid);
143
+ return {
144
+ server: await startAppiumServer(root, udid),
145
+ udid
146
+ };
147
+ }
148
+ /** The run's Appium server, provided by a `mobile({ device })` project's setup. */
149
+ async function mobileServer() {
150
+ try {
151
+ const { inject } = await import("vitest");
152
+ return inject("mobileServer");
153
+ } catch {
154
+ return;
155
+ }
156
+ }
157
+ function notOurs(url) {
158
+ return {
159
+ stop: async () => {},
160
+ url
161
+ };
162
+ }
163
+ function named({ name, os, udid }) {
164
+ return [
165
+ `'${name}'`,
166
+ os === void 0 ? "" : ` (${os})`,
167
+ udid === void 0 ? "" : ` [${udid}]`
168
+ ].join("");
169
+ }
170
+ /** Never another device: no other simulator was booted for the run. */
171
+ function runDevice(device, run) {
172
+ if (device === void 0) return {
173
+ ...run.device,
174
+ udid: run.udid
175
+ };
176
+ if (!(device.name === run.device.name && (device.os === void 0 || run.device.os !== void 0 && sameRuntime(device.os, run.device.os)) && (device.udid === void 0 || device.udid === run.udid))) throw new Error(refusal({
177
+ fix: "name that device, or declare no device on mobile() so each file serves its own",
178
+ found: `specification.mobile() names the device ${named(device)}, and the run's Appium server drives ${named({
179
+ ...run.device,
180
+ udid: run.udid
181
+ })}, the device its mobile({ device }) project declares`,
182
+ read: "docs/guides/mobile-specs.md#one-session-per-run",
183
+ why: "The run's setup booted the project's device alone."
184
+ }));
185
+ return {
186
+ ...run.device,
187
+ udid: run.udid
188
+ };
189
+ }
190
+ /** A declared server owns its devices; the run's server drives the one its setup booted; otherwise resolved, booted and spawned here for this file. */
191
+ async function reachAppium({ appium: declared, device }, root, run) {
192
+ if (declared !== void 0) {
193
+ if (device === void 0) throw new Error(refusal({
194
+ fix: "name the simulator with `device: { name }`",
195
+ found: `specification.mobile() names no device to ask the Appium server at ${declared.url} for`,
196
+ read: "docs/guides/mobile-specs.md#a-declared-appium-server",
197
+ why: "A declared server is handed its device by capabilities."
198
+ }));
199
+ return {
200
+ appium: notOurs(declared.url),
201
+ device,
202
+ outlivesFile: true
203
+ };
204
+ }
205
+ if (run !== void 0) return {
206
+ appium: notOurs(run.url),
207
+ device: runDevice(device, run),
208
+ outlivesFile: true
209
+ };
210
+ if (device === void 0) throw new Error(refusal({
211
+ fix: "name the simulator with `device: { name }`, or declare it once on the project, mobile({ device, root }), so every file drives the run's",
212
+ found: "specification.mobile() names no device, and no mobile({ device }) project serves one",
213
+ read: "docs/guides/mobile-specs.md#one-session-per-run",
214
+ why: "A mobile spec drives one simulator, named by the runner or by its project."
215
+ }));
216
+ const { server, udid } = await serveAppium(device, root);
217
+ return {
218
+ appium: server,
219
+ device: {
220
+ ...device,
221
+ udid
222
+ },
223
+ outlivesFile: false
224
+ };
225
+ }
226
+ //#endregion
227
+ //#region src/specifications/mobile/session/session-ledger.ts
228
+ const SERVER_CALL_TIMEOUT_MS = 1e4;
229
+ const MOBILE_SESSIONS_DIR = `${VITEST_ARTIFACTS_DIR}/mobile-sessions`;
230
+ function keyOf(serverUrl, requested, worker) {
231
+ const sorted = Object.entries(requested).toSorted(([a], [b]) => a.localeCompare(b));
232
+ return JSON.stringify([
233
+ serverUrl,
234
+ sorted,
235
+ worker
236
+ ]);
237
+ }
238
+ function isEntry(value) {
239
+ return isRecord(value) && [
240
+ value.key,
241
+ value.serverUrl,
242
+ value.sessionId
243
+ ].every((field) => typeof field === "string") && isRecord(value.capabilities);
244
+ }
245
+ function entryFiles(directory) {
246
+ try {
247
+ return readdirSync(directory).filter((name) => name.endsWith(".json"));
248
+ } catch {
249
+ return [];
250
+ }
251
+ }
252
+ function entries(directory) {
253
+ const parsed = (name) => JSON.parse(readFileSync(join(directory, name), "utf8"));
254
+ return entryFiles(directory).map((name) => parsed(name)).filter((entry) => isEntry(entry));
255
+ }
256
+ function sessionLedger(directory, worker = process.env.VITEST_POOL_ID ?? "") {
257
+ return {
258
+ forget: (sessionId) => {
259
+ rmSync(join(directory, `${sessionId}.json`), { force: true });
260
+ },
261
+ lookup: (serverUrl, requested) => {
262
+ const key = keyOf(serverUrl, requested, worker);
263
+ return entries(directory).filter((entry) => entry.key === key).map(({ capabilities, serverUrl: url, sessionId }) => ({
264
+ capabilities,
265
+ serverUrl: url,
266
+ sessionId
267
+ }));
268
+ },
269
+ record: (requested, session) => {
270
+ const entry = {
271
+ ...session,
272
+ key: keyOf(session.serverUrl, requested, worker)
273
+ };
274
+ mkdirSync(directory, { recursive: true });
275
+ writeFileSync(join(directory, `${session.sessionId}.json`), `${JSON.stringify(entry)}\n`);
276
+ }
277
+ };
278
+ }
279
+ /** A server that no longer answers has taken its sessions with it. */
280
+ async function endSessions(directory) {
281
+ await Promise.all(entries(directory).map(async (entry) => {
282
+ try {
283
+ await (await fetch(`${entry.serverUrl}/session/${entry.sessionId}`, {
284
+ method: "DELETE",
285
+ signal: AbortSignal.timeout(SERVER_CALL_TIMEOUT_MS)
286
+ })).body?.cancel();
287
+ } catch {}
288
+ }));
289
+ rmSync(directory, {
290
+ force: true,
291
+ recursive: true
292
+ });
293
+ }
294
+ async function mobileSessions() {
295
+ try {
296
+ const { inject } = await import("vitest");
297
+ return inject("mobileSessions");
298
+ } catch {
299
+ return;
300
+ }
301
+ }
302
+ //#endregion
303
+ export { sessionLedger as a, serveAppium as c, mobileSessions as i, SERVER_CALL_TIMEOUT_MS as n, mobileServer as o, endSessions as r, reachAppium as s, MOBILE_SESSIONS_DIR as t };
304
+
305
+ //# sourceMappingURL=shared-session-ledger.js.map
@@ -0,0 +1,12 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ //#region src/chain/paths/setup-entry.ts
4
+ /** A global setup's source in a checkout, else the entry the build names after it: `<name>.global-setup.js`, beside its owner's chunk. */
5
+ function setupEntry(directory, name) {
6
+ const source = join(directory, `${name}.global-setup.ts`);
7
+ return existsSync(source) ? source : join(directory, `${name}.global-setup.js`);
8
+ }
9
+ //#endregion
10
+ export { setupEntry as t };
11
+
12
+ //# sourceMappingURL=shared-setup-entry.js.map
@@ -0,0 +1,133 @@
1
+ function skipString(text, start) {
2
+ const quote = text[start];
3
+ let index = start + 1;
4
+ while (index < text.length) {
5
+ const character = text[index];
6
+ if (character === "\\") {
7
+ index += 2;
8
+ continue;
9
+ }
10
+ if (character === quote) return index + 1;
11
+ if (quote === "`" && character === "$" && text[index + 1] === "{") {
12
+ index = closingOf(text, index + 1) + 1;
13
+ continue;
14
+ }
15
+ index += 1;
16
+ }
17
+ return index;
18
+ }
19
+ const BEFORE_REGEX = /(?:^|[(,=:[!&|?{};+\-*%<>~^]|\breturn|\btypeof|\bcase)\s*$/u;
20
+ function opensRegex(text, start) {
21
+ return text[start] === "/" && text[start + 1] !== "/" && text[start + 1] !== "*" && BEFORE_REGEX.test(text.slice(Math.max(0, start - 16), start));
22
+ }
23
+ function pastFlags(text, close) {
24
+ let end = close + 1;
25
+ while (/[a-z]/u.test(text[end] ?? "")) end += 1;
26
+ return end;
27
+ }
28
+ function regexEnd(text, start) {
29
+ if (!opensRegex(text, start)) return start;
30
+ let inClass = false;
31
+ for (let index = start + 1; index < text.length; index += 1) {
32
+ const character = text[index];
33
+ if (character === "\\") index += 1;
34
+ else if (character === "\n") return start;
35
+ else if (character === "[" || character === "]") inClass = character === "[";
36
+ else if (character === "/" && !inClass) return pastFlags(text, index);
37
+ }
38
+ return start;
39
+ }
40
+ function literalEnd(text, start) {
41
+ const character = text[start];
42
+ if (character === "'" || character === "\"" || character === "`") return skipString(text, start);
43
+ return regexEnd(text, start);
44
+ }
45
+ function closingOf(text, open) {
46
+ const pairs = {
47
+ "(": ")",
48
+ "[": "]",
49
+ "{": "}"
50
+ };
51
+ const stack = [];
52
+ let index = open;
53
+ while (index < text.length) {
54
+ const character = text[index] ?? "";
55
+ const past = index === open ? index : literalEnd(text, index);
56
+ if (past !== index) {
57
+ index = past;
58
+ continue;
59
+ }
60
+ if (character in pairs) stack.push(pairs[character] ?? "");
61
+ else if (character === stack.at(-1)) {
62
+ stack.pop();
63
+ if (stack.length === 0) return index;
64
+ }
65
+ index += 1;
66
+ }
67
+ return text.length;
68
+ }
69
+ /** The offsets of the brackets still open at `index`, outermost first; a literal's brackets are none. */
70
+ function openersAt(text, index) {
71
+ const closers = {
72
+ ")": "(",
73
+ "]": "[",
74
+ "}": "{"
75
+ };
76
+ const stack = [];
77
+ let at = 0;
78
+ while (at < index) {
79
+ const past = literalEnd(text, at);
80
+ if (past !== at) {
81
+ at = past;
82
+ continue;
83
+ }
84
+ const character = text[at] ?? "";
85
+ if (character === "(" || character === "[" || character === "{") stack.push(at);
86
+ else if (character in closers && text[stack.at(-1) ?? -1] === closers[character]) stack.pop();
87
+ at += 1;
88
+ }
89
+ return stack;
90
+ }
91
+ /** Each comma-separated argument between `start` and `end`, trimmed, an empty one dropped. */
92
+ function argumentSpans(text, start = 0, end = text.length) {
93
+ const spans = [];
94
+ let from = start;
95
+ const close = (to) => {
96
+ const raw = text.slice(from, to);
97
+ if (raw.trim() !== "") {
98
+ const lead = raw.length - raw.trimStart().length;
99
+ spans.push({
100
+ end: from + raw.trimEnd().length,
101
+ start: from + lead
102
+ });
103
+ }
104
+ from = to + 1;
105
+ };
106
+ let index = start;
107
+ while (index < end) {
108
+ const past = literalEnd(text, index);
109
+ const character = text[index];
110
+ if (past !== index) index = past;
111
+ else if (character === "(" || character === "[" || character === "{") index = closingOf(text, index) + 1;
112
+ else {
113
+ if (character === ",") close(index);
114
+ index += 1;
115
+ }
116
+ }
117
+ close(end);
118
+ return spans;
119
+ }
120
+ function argumentsOf(text) {
121
+ return argumentSpans(text).map(({ end, start }) => text.slice(start, end));
122
+ }
123
+ function literalOf(argument) {
124
+ const text = argument?.trim() ?? "";
125
+ const quote = text[0];
126
+ if (quote !== "'" && quote !== "\"" && quote !== "`" || skipString(text, 0) !== text.length) return;
127
+ const body = text.slice(1, -1);
128
+ return quote === "`" ? body.replaceAll(/\$\{[^}]*\}/gu, "\0") : body;
129
+ }
130
+ //#endregion
131
+ export { literalOf as a, literalEnd as i, argumentsOf as n, openersAt as o, closingOf as r, skipString as s, argumentSpans as t };
132
+
133
+ //# sourceMappingURL=shared-source-scan.js.map