@jterrazz/test 16.1.0 → 17.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 (304) hide show
  1. package/README.md +45 -171
  2. package/dist/browser/index.js +2 -2867
  3. package/dist/browser/services.js +2 -0
  4. package/dist/browser/stubs.js +2 -0
  5. package/dist/checker.d.ts +6 -1
  6. package/dist/checker.js +4551 -2612
  7. package/dist/config.d.ts +40 -0
  8. package/dist/config.js +309 -0
  9. package/dist/in-place.global-setup.d.ts +11 -0
  10. package/dist/in-place.global-setup.js +21 -0
  11. package/dist/index.d.ts +1653 -3002
  12. package/dist/index.js +5226 -3845
  13. package/dist/mobile-session.global-setup.d.ts +14 -0
  14. package/dist/mobile-session.global-setup.js +31 -0
  15. package/dist/oxlint.cjs +6073 -5616
  16. package/dist/oxlint.d.cts +43 -7212
  17. package/dist/oxlint.d.ts +43 -7212
  18. package/dist/oxlint.js +6065 -5609
  19. package/dist/route-coverage.global-setup.d.ts +42 -0
  20. package/dist/route-coverage.global-setup.js +2 -0
  21. package/dist/services.d.ts +3 -0
  22. package/dist/services.global-setup.d.ts +43 -0
  23. package/dist/services.global-setup.js +2 -0
  24. package/dist/services.js +597 -0
  25. package/dist/shared-appium.adapter.js +428 -0
  26. package/dist/shared-artifacts.js +32 -0
  27. package/dist/shared-chapter.js +7 -0
  28. package/dist/shared-container.port.d.ts +31 -0
  29. package/dist/shared-derived-projects.js +58 -0
  30. package/dist/shared-device.port.d.ts +189 -0
  31. package/dist/shared-exchange.js +447 -0
  32. package/dist/shared-in-place.js +172 -0
  33. package/dist/shared-index.browser.js +3191 -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-literate-plugin.js +183 -0
  40. package/dist/shared-manifest.js +1321 -0
  41. package/dist/shared-pause.js +11 -0
  42. package/dist/shared-peer.js +88 -0
  43. package/dist/shared-playwright.adapter.js +330 -0
  44. package/dist/shared-process.adapter.js +74 -0
  45. package/dist/shared-refusal.js +35 -0
  46. package/dist/shared-resolve.js +24 -0
  47. package/dist/shared-route-coverage.global-setup.js +187 -0
  48. package/dist/shared-scenario-failure.js +198 -0
  49. package/dist/shared-serve.js +365 -0
  50. package/dist/shared-service-images.js +10 -0
  51. package/dist/shared-services.global-setup.js +179 -0
  52. package/dist/shared-session-ledger.js +301 -0
  53. package/dist/shared-setup-entry.js +12 -0
  54. package/dist/shared-source-scan.js +133 -0
  55. package/dist/shared-spec-document.js +365 -0
  56. package/dist/shared-specifications.js +26 -0
  57. package/dist/shared-stream-body.js +130 -0
  58. package/dist/shared-stub-server.js +190 -0
  59. package/dist/shared-temp-root.global-setup.js +80 -0
  60. package/dist/shared-test-baseline.js +26 -0
  61. package/dist/shared-test-section.js +58 -0
  62. package/dist/shared-testcontainers.adapter.js +203 -0
  63. package/dist/shared-tokens.js +55 -0
  64. package/dist/shared-yaml-document.js +119 -0
  65. package/dist/stubs.d.ts +2 -0
  66. package/dist/stubs.js +657 -0
  67. package/dist/temp-root.global-setup.d.ts +7 -0
  68. package/dist/temp-root.global-setup.js +2 -0
  69. package/dist/vitest.d.ts +97 -163
  70. package/dist/vitest.js +374 -507
  71. package/docs/README.md +82 -36
  72. package/docs/concepts/architecture.md +118 -0
  73. package/docs/concepts/conventions.md +333 -0
  74. package/docs/guides/api-specs.md +374 -0
  75. package/docs/guides/cli-specs.md +564 -0
  76. package/docs/guides/component-specs.md +252 -0
  77. package/docs/guides/develop.md +94 -0
  78. package/docs/guides/integration-specs.md +181 -0
  79. package/docs/guides/job-specs.md +180 -0
  80. package/docs/guides/lint-specs.md +174 -0
  81. package/docs/guides/mobile-specs.md +319 -0
  82. package/docs/guides/module-tests.md +149 -0
  83. package/docs/guides/release.md +43 -0
  84. package/docs/guides/spec-documents.md +189 -0
  85. package/docs/guides/website-specs.md +413 -0
  86. package/docs/reference/assertions.md +345 -0
  87. package/docs/reference/glossary.md +20 -0
  88. package/docs/reference/linting.md +483 -0
  89. package/docs/reference/services.md +236 -0
  90. package/docs/reference/stubs.md +498 -0
  91. package/docs/reference/time-and-doubles.md +137 -0
  92. package/docs/reference/tokens.md +258 -0
  93. package/docs/reference/visitor.md +221 -0
  94. package/docs/start/getting-started.md +187 -0
  95. package/docs/start/set-up-a-project.md +214 -0
  96. package/package.json +50 -30
  97. package/schema/spec.schema.json +43 -17
  98. package/dist/appium.adapter.js +0 -489
  99. package/dist/artifacts.js +0 -32
  100. package/dist/fixtures.js +0 -110
  101. package/dist/literate.js +0 -1747
  102. package/dist/match.js +0 -804
  103. package/dist/playwright.adapter.js +0 -349
  104. package/dist/server.js +0 -160
  105. package/dist/stream.js +0 -321
  106. package/dist/substring-warning.js +0 -244
  107. package/docs/01-architecture.md +0 -95
  108. package/docs/02-developing.md +0 -351
  109. package/docs/03-testing.md +0 -495
  110. package/docs/04-operating.md +0 -101
  111. package/docs/05-module-tests.md +0 -152
  112. package/docs/06-integration.md +0 -180
  113. package/docs/07-component.md +0 -246
  114. package/docs/08-website.md +0 -404
  115. package/docs/09-mobile.md +0 -270
  116. package/docs/10-api.md +0 -264
  117. package/docs/11-jobs.md +0 -157
  118. package/docs/12-cli.md +0 -677
  119. package/docs/13-elements.md +0 -242
  120. package/docs/14-assertions.md +0 -314
  121. package/docs/15-tokens.md +0 -253
  122. package/docs/16-contracts.md +0 -438
  123. package/docs/17-services.md +0 -188
  124. package/docs/18-conventions.md +0 -305
  125. package/docs/19-linting.md +0 -510
  126. package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
  127. package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
  128. package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
  129. package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
  130. package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
  131. package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
  132. package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
  133. package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
  134. package/docs/decisions/_template.md +0 -22
  135. package/docs/reference/classes/Matcher.md +0 -120
  136. package/docs/reference/classes/ProcessHandle.md +0 -368
  137. package/docs/reference/functions/defineContract.md +0 -39
  138. package/docs/reference/functions/defineContracts.md +0 -36
  139. package/docs/reference/functions/disabled.md +0 -28
  140. package/docs/reference/functions/enabled.md +0 -20
  141. package/docs/reference/functions/focused.md +0 -26
  142. package/docs/reference/functions/mockOf.md +0 -79
  143. package/docs/reference/functions/postgres.md +0 -31
  144. package/docs/reference/functions/process.md +0 -33
  145. package/docs/reference/functions/redis.md +0 -28
  146. package/docs/reference/functions/required.md +0 -41
  147. package/docs/reference/functions/selected.md +0 -26
  148. package/docs/reference/functions/sqlite.md +0 -33
  149. package/docs/reference/functions/testId.md +0 -20
  150. package/docs/reference/functions/text.md +0 -40
  151. package/docs/reference/functions/valued.md +0 -28
  152. package/docs/reference/functions/waitUntil.md +0 -46
  153. package/docs/reference/functions/within.md +0 -29
  154. package/docs/reference/index.md +0 -196
  155. package/docs/reference/interfaces/BaseResult.md +0 -87
  156. package/docs/reference/interfaces/CallResult.md +0 -160
  157. package/docs/reference/interfaces/CaptureScope.md +0 -82
  158. package/docs/reference/interfaces/CliResult.md +0 -253
  159. package/docs/reference/interfaces/ContainerAccessor.md +0 -166
  160. package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
  161. package/docs/reference/interfaces/FetchResult.md +0 -180
  162. package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
  163. package/docs/reference/interfaces/HttpResult.md +0 -126
  164. package/docs/reference/interfaces/JsonAccessor.md +0 -46
  165. package/docs/reference/interfaces/PageResult.md +0 -356
  166. package/docs/reference/interfaces/RenderResult.md +0 -72
  167. package/docs/reference/interfaces/RenderedText.md +0 -110
  168. package/docs/reference/interfaces/ResponseAccessor.md +0 -47
  169. package/docs/reference/interfaces/ScreenResult.md +0 -128
  170. package/docs/reference/interfaces/TableAccessor.md +0 -26
  171. package/docs/reference/interfaces/TextAccessor.md +0 -96
  172. package/docs/reference/type-aliases/ApiHandle.md +0 -68
  173. package/docs/reference/type-aliases/ApiSpecification.md +0 -213
  174. package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
  175. package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
  176. package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
  177. package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
  178. package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
  179. package/docs/reference/type-aliases/BrowserPage.md +0 -132
  180. package/docs/reference/type-aliases/BrowserPort.md +0 -51
  181. package/docs/reference/type-aliases/CliEnv.md +0 -11
  182. package/docs/reference/type-aliases/CliHandle.md +0 -80
  183. package/docs/reference/type-aliases/CliInput.md +0 -36
  184. package/docs/reference/type-aliases/CliOutput.md +0 -46
  185. package/docs/reference/type-aliases/CliPort.md +0 -58
  186. package/docs/reference/type-aliases/CliSpecification.md +0 -144
  187. package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
  188. package/docs/reference/type-aliases/ComponentChain.md +0 -156
  189. package/docs/reference/type-aliases/ComponentScenario.md +0 -20
  190. package/docs/reference/type-aliases/ComponentUi.md +0 -47
  191. package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
  192. package/docs/reference/type-aliases/ContainerPort.md +0 -113
  193. package/docs/reference/type-aliases/Contract.md +0 -68
  194. package/docs/reference/type-aliases/ContractInput.md +0 -13
  195. package/docs/reference/type-aliases/ContractRequest.md +0 -95
  196. package/docs/reference/type-aliases/ContractResponder.md +0 -23
  197. package/docs/reference/type-aliases/ContractResponse.md +0 -78
  198. package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
  199. package/docs/reference/type-aliases/Contracts.md +0 -48
  200. package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
  201. package/docs/reference/type-aliases/DatabasePort.md +0 -72
  202. package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
  203. package/docs/reference/type-aliases/DevicePort.md +0 -51
  204. package/docs/reference/type-aliases/DeviceScreen.md +0 -32
  205. package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
  206. package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
  207. package/docs/reference/type-aliases/DomMount.md +0 -21
  208. package/docs/reference/type-aliases/ElementKind.md +0 -27
  209. package/docs/reference/type-aliases/ElementMatch.md +0 -74
  210. package/docs/reference/type-aliases/ElementOptions.md +0 -29
  211. package/docs/reference/type-aliases/ElementRef.md +0 -128
  212. package/docs/reference/type-aliases/ExecOptions.md +0 -37
  213. package/docs/reference/type-aliases/FileAccessor.md +0 -54
  214. package/docs/reference/type-aliases/HonoApp.md +0 -31
  215. package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
  216. package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
  217. package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
  218. package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
  219. package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
  220. package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
  221. package/docs/reference/type-aliases/Intercept.md +0 -19
  222. package/docs/reference/type-aliases/InterceptOptions.md +0 -28
  223. package/docs/reference/type-aliases/InterceptScope.md +0 -11
  224. package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
  225. package/docs/reference/type-aliases/JobHandle.md +0 -34
  226. package/docs/reference/type-aliases/JobsHandle.md +0 -57
  227. package/docs/reference/type-aliases/JobsResult.md +0 -16
  228. package/docs/reference/type-aliases/JobsSpecification.md +0 -94
  229. package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
  230. package/docs/reference/type-aliases/LandmarkKind.md +0 -20
  231. package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
  232. package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
  233. package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
  234. package/docs/reference/type-aliases/MatchableRequest.md +0 -61
  235. package/docs/reference/type-aliases/MatcherKind.md +0 -34
  236. package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
  237. package/docs/reference/type-aliases/MobileElementKind.md +0 -14
  238. package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
  239. package/docs/reference/type-aliases/MobileElementRef.md +0 -20
  240. package/docs/reference/type-aliases/MobileHandle.md +0 -65
  241. package/docs/reference/type-aliases/MobileScenario.md +0 -20
  242. package/docs/reference/type-aliases/MobileSpecification.md +0 -51
  243. package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
  244. package/docs/reference/type-aliases/MobileVisitor.md +0 -81
  245. package/docs/reference/type-aliases/MockOfOptions.md +0 -26
  246. package/docs/reference/type-aliases/MockPort.md +0 -10
  247. package/docs/reference/type-aliases/PinnedClock.md +0 -10
  248. package/docs/reference/type-aliases/PostgresOptions.md +0 -32
  249. package/docs/reference/type-aliases/ProcessOptions.md +0 -112
  250. package/docs/reference/type-aliases/RedisOptions.md +0 -20
  251. package/docs/reference/type-aliases/RenderSubject.md +0 -12
  252. package/docs/reference/type-aliases/ScreenNode.md +0 -67
  253. package/docs/reference/type-aliases/ServerPort.md +0 -36
  254. package/docs/reference/type-aliases/ServerResponse.md +0 -46
  255. package/docs/reference/type-aliases/ServerSpec.md +0 -21
  256. package/docs/reference/type-aliases/ServiceHandle.md +0 -216
  257. package/docs/reference/type-aliases/ServiceRecord.md +0 -12
  258. package/docs/reference/type-aliases/SpecDocument.md +0 -84
  259. package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
  260. package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
  261. package/docs/reference/type-aliases/SpecFixture.md +0 -30
  262. package/docs/reference/type-aliases/SpecKind.md +0 -8
  263. package/docs/reference/type-aliases/SpecRun.md +0 -108
  264. package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
  265. package/docs/reference/type-aliases/SpecStream.md +0 -32
  266. package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
  267. package/docs/reference/type-aliases/SqliteOptions.md +0 -36
  268. package/docs/reference/type-aliases/SseEvent.md +0 -58
  269. package/docs/reference/type-aliases/StreamBody.md +0 -61
  270. package/docs/reference/type-aliases/TextFilter.md +0 -22
  271. package/docs/reference/type-aliases/VisitScenario.md +0 -20
  272. package/docs/reference/type-aliases/Visitor.md +0 -215
  273. package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
  274. package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
  275. package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
  276. package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
  277. package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
  278. package/docs/reference/variables/anthropic.md +0 -19
  279. package/docs/reference/variables/banner.md +0 -10
  280. package/docs/reference/variables/button.md +0 -10
  281. package/docs/reference/variables/clock.md +0 -16
  282. package/docs/reference/variables/complementary.md +0 -10
  283. package/docs/reference/variables/component.md +0 -10
  284. package/docs/reference/variables/content.md +0 -10
  285. package/docs/reference/variables/contentinfo.md +0 -10
  286. package/docs/reference/variables/dialog.md +0 -10
  287. package/docs/reference/variables/field.md +0 -10
  288. package/docs/reference/variables/form.md +0 -10
  289. package/docs/reference/variables/heading.md +0 -10
  290. package/docs/reference/variables/http.md +0 -40
  291. package/docs/reference/variables/intercept.md +0 -11
  292. package/docs/reference/variables/link.md +0 -10
  293. package/docs/reference/variables/listitem.md +0 -10
  294. package/docs/reference/variables/main.md +0 -10
  295. package/docs/reference/variables/match.md +0 -8
  296. package/docs/reference/variables/navigation.md +0 -10
  297. package/docs/reference/variables/openai.md +0 -21
  298. package/docs/reference/variables/option.md +0 -17
  299. package/docs/reference/variables/region.md +0 -10
  300. package/docs/reference/variables/row.md +0 -10
  301. package/docs/reference/variables/search.md +0 -10
  302. package/docs/reference/variables/specification.md +0 -8
  303. package/docs/reference/variables/status.md +0 -10
  304. package/docs/reference/variables/table.md +0 -10
@@ -0,0 +1,301 @@
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 { l 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) {
231
+ const sorted = Object.entries(requested).toSorted(([a], [b]) => a.localeCompare(b));
232
+ return JSON.stringify([serverUrl, sorted]);
233
+ }
234
+ function isEntry(value) {
235
+ return isRecord(value) && [
236
+ value.key,
237
+ value.serverUrl,
238
+ value.sessionId
239
+ ].every((field) => typeof field === "string") && isRecord(value.capabilities);
240
+ }
241
+ function entryFiles(directory) {
242
+ try {
243
+ return readdirSync(directory).filter((name) => name.endsWith(".json"));
244
+ } catch {
245
+ return [];
246
+ }
247
+ }
248
+ function entries(directory) {
249
+ const parsed = (name) => JSON.parse(readFileSync(join(directory, name), "utf8"));
250
+ return entryFiles(directory).map((name) => parsed(name)).filter((entry) => isEntry(entry));
251
+ }
252
+ function sessionLedger(directory) {
253
+ return {
254
+ forget: (sessionId) => {
255
+ rmSync(join(directory, `${sessionId}.json`), { force: true });
256
+ },
257
+ lookup: (serverUrl, requested) => {
258
+ const key = keyOf(serverUrl, requested);
259
+ return entries(directory).filter((entry) => entry.key === key).map(({ capabilities, serverUrl: url, sessionId }) => ({
260
+ capabilities,
261
+ serverUrl: url,
262
+ sessionId
263
+ }));
264
+ },
265
+ record: (requested, session) => {
266
+ const entry = {
267
+ ...session,
268
+ key: keyOf(session.serverUrl, requested)
269
+ };
270
+ mkdirSync(directory, { recursive: true });
271
+ writeFileSync(join(directory, `${session.sessionId}.json`), `${JSON.stringify(entry)}\n`);
272
+ }
273
+ };
274
+ }
275
+ /** A server that no longer answers has taken its sessions with it. */
276
+ async function endSessions(directory) {
277
+ await Promise.all(entries(directory).map(async (entry) => {
278
+ try {
279
+ await (await fetch(`${entry.serverUrl}/session/${entry.sessionId}`, {
280
+ method: "DELETE",
281
+ signal: AbortSignal.timeout(SERVER_CALL_TIMEOUT_MS)
282
+ })).body?.cancel();
283
+ } catch {}
284
+ }));
285
+ rmSync(directory, {
286
+ force: true,
287
+ recursive: true
288
+ });
289
+ }
290
+ async function mobileSessions() {
291
+ try {
292
+ const { inject } = await import("vitest");
293
+ return inject("mobileSessions");
294
+ } catch {
295
+ return;
296
+ }
297
+ }
298
+ //#endregion
299
+ 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 };
300
+
301
+ //# 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