@fluentui-react-native/desktop-driver 0.2.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 (235) hide show
  1. package/.cache/tsconfig.tsbuildinfo +1 -0
  2. package/AGENTS.md +68 -0
  3. package/CHANGELOG.md +16 -0
  4. package/PLAN.md +1394 -0
  5. package/README.md +223 -0
  6. package/SPEC.md +105 -0
  7. package/config/cli.cjs +8 -0
  8. package/jest.config.cjs +9 -0
  9. package/lib/agent/DesktopAgent.d.ts +56 -0
  10. package/lib/agent/DesktopAgent.d.ts.map +1 -0
  11. package/lib/agent/DesktopAgent.js +118 -0
  12. package/lib/agent/DesktopAgent.js.map +1 -0
  13. package/lib/agent/DesktopAgent.test.d.ts +2 -0
  14. package/lib/agent/DesktopAgent.test.d.ts.map +1 -0
  15. package/lib/agent/DesktopAgent.test.js +97 -0
  16. package/lib/agent/DesktopAgent.test.js.map +1 -0
  17. package/lib/agent/agent.contract.cjs +66 -0
  18. package/lib/agent/agent.contract.cjs.map +1 -0
  19. package/lib/agent/agent.contract.d.cts +2 -0
  20. package/lib/agent/agent.contract.d.cts.map +1 -0
  21. package/lib/agent/index.d.ts +3 -0
  22. package/lib/agent/index.d.ts.map +1 -0
  23. package/lib/agent/index.js +2 -0
  24. package/lib/agent/index.js.map +1 -0
  25. package/lib/artifacts/ArtifactManager.d.ts +13 -0
  26. package/lib/artifacts/ArtifactManager.d.ts.map +1 -0
  27. package/lib/artifacts/ArtifactManager.js +88 -0
  28. package/lib/artifacts/ArtifactManager.js.map +1 -0
  29. package/lib/artifacts/ArtifactManager.test.d.ts +2 -0
  30. package/lib/artifacts/ArtifactManager.test.d.ts.map +1 -0
  31. package/lib/artifacts/ArtifactManager.test.js +19 -0
  32. package/lib/artifacts/ArtifactManager.test.js.map +1 -0
  33. package/lib/artifacts/index.d.ts +2 -0
  34. package/lib/artifacts/index.d.ts.map +1 -0
  35. package/lib/artifacts/index.js +2 -0
  36. package/lib/artifacts/index.js.map +1 -0
  37. package/lib/authoring/index.d.ts +4 -0
  38. package/lib/authoring/index.d.ts.map +1 -0
  39. package/lib/authoring/index.js +2 -0
  40. package/lib/authoring/index.js.map +1 -0
  41. package/lib/authoring/results.d.ts +43 -0
  42. package/lib/authoring/results.d.ts.map +1 -0
  43. package/lib/authoring/results.js +2 -0
  44. package/lib/authoring/results.js.map +1 -0
  45. package/lib/authoring/storyTests.d.ts +81 -0
  46. package/lib/authoring/storyTests.d.ts.map +1 -0
  47. package/lib/authoring/storyTests.js +272 -0
  48. package/lib/authoring/storyTests.js.map +1 -0
  49. package/lib/authoring/storyTests.test.d.ts +2 -0
  50. package/lib/authoring/storyTests.test.d.ts.map +1 -0
  51. package/lib/authoring/storyTests.test.js +46 -0
  52. package/lib/authoring/storyTests.test.js.map +1 -0
  53. package/lib/cli/DesktopDriverCli.test.d.ts +2 -0
  54. package/lib/cli/DesktopDriverCli.test.d.ts.map +1 -0
  55. package/lib/cli/DesktopDriverCli.test.js +141 -0
  56. package/lib/cli/DesktopDriverCli.test.js.map +1 -0
  57. package/lib/cli/createDesktopDriverCommand.d.ts +8 -0
  58. package/lib/cli/createDesktopDriverCommand.d.ts.map +1 -0
  59. package/lib/cli/createDesktopDriverCommand.js +242 -0
  60. package/lib/cli/createDesktopDriverCommand.js.map +1 -0
  61. package/lib/cli/index.d.ts +3 -0
  62. package/lib/cli/index.d.ts.map +1 -0
  63. package/lib/cli/index.js +2 -0
  64. package/lib/cli/index.js.map +1 -0
  65. package/lib/client/DesktopDriverClient.d.ts +60 -0
  66. package/lib/client/DesktopDriverClient.d.ts.map +1 -0
  67. package/lib/client/DesktopDriverClient.js +155 -0
  68. package/lib/client/DesktopDriverClient.js.map +1 -0
  69. package/lib/client/index.d.ts +3 -0
  70. package/lib/client/index.d.ts.map +1 -0
  71. package/lib/client/index.js +2 -0
  72. package/lib/client/index.js.map +1 -0
  73. package/lib/host/types.d.ts +153 -0
  74. package/lib/host/types.d.ts.map +1 -0
  75. package/lib/host/types.js +2 -0
  76. package/lib/host/types.js.map +1 -0
  77. package/lib/hosts/fake/FakeDesktopHost.d.ts +72 -0
  78. package/lib/hosts/fake/FakeDesktopHost.d.ts.map +1 -0
  79. package/lib/hosts/fake/FakeDesktopHost.js +452 -0
  80. package/lib/hosts/fake/FakeDesktopHost.js.map +1 -0
  81. package/lib/index.d.ts +23 -0
  82. package/lib/index.d.ts.map +1 -0
  83. package/lib/index.js +11 -0
  84. package/lib/index.js.map +1 -0
  85. package/lib/protocol/actions.d.ts +11 -0
  86. package/lib/protocol/actions.d.ts.map +1 -0
  87. package/lib/protocol/actions.js +137 -0
  88. package/lib/protocol/actions.js.map +1 -0
  89. package/lib/protocol/capabilities.d.ts +11 -0
  90. package/lib/protocol/capabilities.d.ts.map +1 -0
  91. package/lib/protocol/capabilities.js +103 -0
  92. package/lib/protocol/capabilities.js.map +1 -0
  93. package/lib/protocol/constants.d.ts +2 -0
  94. package/lib/protocol/constants.d.ts.map +1 -0
  95. package/lib/protocol/constants.js +2 -0
  96. package/lib/protocol/constants.js.map +1 -0
  97. package/lib/protocol/errors.d.ts +37 -0
  98. package/lib/protocol/errors.d.ts.map +1 -0
  99. package/lib/protocol/errors.js +66 -0
  100. package/lib/protocol/errors.js.map +1 -0
  101. package/lib/protocol/timeouts.d.ts +2 -0
  102. package/lib/protocol/timeouts.d.ts.map +1 -0
  103. package/lib/protocol/timeouts.js +23 -0
  104. package/lib/protocol/timeouts.js.map +1 -0
  105. package/lib/protocol/types.d.ts +46 -0
  106. package/lib/protocol/types.d.ts.map +1 -0
  107. package/lib/protocol/types.js +2 -0
  108. package/lib/protocol/types.js.map +1 -0
  109. package/lib/runner/StoryTestRunner.d.ts +38 -0
  110. package/lib/runner/StoryTestRunner.d.ts.map +1 -0
  111. package/lib/runner/StoryTestRunner.js +497 -0
  112. package/lib/runner/StoryTestRunner.js.map +1 -0
  113. package/lib/runner/StoryTestRunner.test.d.ts +2 -0
  114. package/lib/runner/StoryTestRunner.test.d.ts.map +1 -0
  115. package/lib/runner/StoryTestRunner.test.js +349 -0
  116. package/lib/runner/StoryTestRunner.test.js.map +1 -0
  117. package/lib/runner/index.d.ts +3 -0
  118. package/lib/runner/index.d.ts.map +1 -0
  119. package/lib/runner/index.js +2 -0
  120. package/lib/runner/index.js.map +1 -0
  121. package/lib/server/SessionManager.d.ts +52 -0
  122. package/lib/server/SessionManager.d.ts.map +1 -0
  123. package/lib/server/SessionManager.js +208 -0
  124. package/lib/server/SessionManager.js.map +1 -0
  125. package/lib/server/TargetRegistry.d.ts +9 -0
  126. package/lib/server/TargetRegistry.d.ts.map +1 -0
  127. package/lib/server/TargetRegistry.js +24 -0
  128. package/lib/server/TargetRegistry.js.map +1 -0
  129. package/lib/server/createDesktopDriverServer.d.ts +19 -0
  130. package/lib/server/createDesktopDriverServer.d.ts.map +1 -0
  131. package/lib/server/createDesktopDriverServer.js +609 -0
  132. package/lib/server/createDesktopDriverServer.js.map +1 -0
  133. package/lib/server/desktopDriver.test.d.ts +2 -0
  134. package/lib/server/desktopDriver.test.d.ts.map +1 -0
  135. package/lib/server/desktopDriver.test.js +389 -0
  136. package/lib/server/desktopDriver.test.js.map +1 -0
  137. package/lib/server/index.d.ts +6 -0
  138. package/lib/server/index.d.ts.map +1 -0
  139. package/lib/server/index.js +4 -0
  140. package/lib/server/index.js.map +1 -0
  141. package/lib/server/webdriverio.contract.cjs +35 -0
  142. package/lib/server/webdriverio.contract.cjs.map +1 -0
  143. package/lib/server/webdriverio.contract.d.cts +2 -0
  144. package/lib/server/webdriverio.contract.d.cts.map +1 -0
  145. package/lib/server/webdriverio.test.d.ts +2 -0
  146. package/lib/server/webdriverio.test.d.ts.map +1 -0
  147. package/lib/server/webdriverio.test.js +40 -0
  148. package/lib/server/webdriverio.test.js.map +1 -0
  149. package/lib/storybook.d.ts +36 -0
  150. package/lib/storybook.d.ts.map +1 -0
  151. package/lib/storybook.js +2 -0
  152. package/lib/storybook.js.map +1 -0
  153. package/lib/testing/FakeStoryOrchestrator.d.ts +21 -0
  154. package/lib/testing/FakeStoryOrchestrator.d.ts.map +1 -0
  155. package/lib/testing/FakeStoryOrchestrator.js +44 -0
  156. package/lib/testing/FakeStoryOrchestrator.js.map +1 -0
  157. package/lib/testing/fakeStoryElements.d.ts +4 -0
  158. package/lib/testing/fakeStoryElements.d.ts.map +1 -0
  159. package/lib/testing/fakeStoryElements.js +143 -0
  160. package/lib/testing/fakeStoryElements.js.map +1 -0
  161. package/lib/testing/fakeStoryElements.test.d.ts +2 -0
  162. package/lib/testing/fakeStoryElements.test.d.ts.map +1 -0
  163. package/lib/testing/fakeStoryElements.test.js +38 -0
  164. package/lib/testing/fakeStoryElements.test.js.map +1 -0
  165. package/lib/testing/index.d.ts +7 -0
  166. package/lib/testing/index.d.ts.map +1 -0
  167. package/lib/testing/index.js +5 -0
  168. package/lib/testing/index.js.map +1 -0
  169. package/lib/testing/protocolHarness.d.ts +17 -0
  170. package/lib/testing/protocolHarness.d.ts.map +1 -0
  171. package/lib/testing/protocolHarness.js +46 -0
  172. package/lib/testing/protocolHarness.js.map +1 -0
  173. package/lib/wdio/DesktopWebdriver.d.ts +47 -0
  174. package/lib/wdio/DesktopWebdriver.d.ts.map +1 -0
  175. package/lib/wdio/DesktopWebdriver.js +76 -0
  176. package/lib/wdio/DesktopWebdriver.js.map +1 -0
  177. package/lib/wdio/DesktopWebdriver.test.d.ts +2 -0
  178. package/lib/wdio/DesktopWebdriver.test.d.ts.map +1 -0
  179. package/lib/wdio/DesktopWebdriver.test.js +74 -0
  180. package/lib/wdio/DesktopWebdriver.test.js.map +1 -0
  181. package/lib/wdio/index.d.ts +3 -0
  182. package/lib/wdio/index.d.ts.map +1 -0
  183. package/lib/wdio/index.js +2 -0
  184. package/lib/wdio/index.js.map +1 -0
  185. package/lib/wdio/wdioRunner.contract.cjs +64 -0
  186. package/lib/wdio/wdioRunner.contract.cjs.map +1 -0
  187. package/lib/wdio/wdioRunner.contract.d.cts +2 -0
  188. package/lib/wdio/wdioRunner.contract.d.cts.map +1 -0
  189. package/package.json +96 -0
  190. package/src/agent/DesktopAgent.test.ts +102 -0
  191. package/src/agent/DesktopAgent.ts +170 -0
  192. package/src/agent/agent.contract.cjs +32 -0
  193. package/src/agent/index.ts +8 -0
  194. package/src/artifacts/ArtifactManager.test.ts +20 -0
  195. package/src/artifacts/ArtifactManager.ts +105 -0
  196. package/src/artifacts/index.ts +1 -0
  197. package/src/authoring/index.ts +26 -0
  198. package/src/authoring/results.ts +47 -0
  199. package/src/authoring/storyTests.test.ts +58 -0
  200. package/src/authoring/storyTests.ts +345 -0
  201. package/src/cli/DesktopDriverCli.test.ts +150 -0
  202. package/src/cli/createDesktopDriverCommand.ts +290 -0
  203. package/src/cli/index.ts +2 -0
  204. package/src/client/DesktopDriverClient.ts +218 -0
  205. package/src/client/index.ts +2 -0
  206. package/src/host/types.ts +158 -0
  207. package/src/hosts/fake/FakeDesktopHost.ts +558 -0
  208. package/src/index.ts +101 -0
  209. package/src/protocol/actions.ts +163 -0
  210. package/src/protocol/capabilities.ts +129 -0
  211. package/src/protocol/constants.ts +1 -0
  212. package/src/protocol/errors.ts +76 -0
  213. package/src/protocol/timeouts.ts +26 -0
  214. package/src/protocol/types.ts +55 -0
  215. package/src/runner/StoryTestRunner.test.ts +362 -0
  216. package/src/runner/StoryTestRunner.ts +624 -0
  217. package/src/runner/index.ts +9 -0
  218. package/src/server/SessionManager.ts +302 -0
  219. package/src/server/TargetRegistry.ts +29 -0
  220. package/src/server/createDesktopDriverServer.ts +775 -0
  221. package/src/server/desktopDriver.test.ts +421 -0
  222. package/src/server/index.ts +5 -0
  223. package/src/server/webdriverio.contract.cjs +38 -0
  224. package/src/server/webdriverio.test.ts +41 -0
  225. package/src/storybook.ts +40 -0
  226. package/src/testing/FakeStoryOrchestrator.ts +55 -0
  227. package/src/testing/fakeStoryElements.test.ts +40 -0
  228. package/src/testing/fakeStoryElements.ts +167 -0
  229. package/src/testing/index.ts +6 -0
  230. package/src/testing/protocolHarness.ts +66 -0
  231. package/src/wdio/DesktopWebdriver.test.ts +76 -0
  232. package/src/wdio/DesktopWebdriver.ts +130 -0
  233. package/src/wdio/index.ts +2 -0
  234. package/src/wdio/wdioRunner.contract.cjs +30 -0
  235. package/tsconfig.json +15 -0
@@ -0,0 +1,74 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { createDesktopDriverStoryHarness } from '../testing/protocolHarness.js';
6
+ describe('sanctioned WebdriverIO API', () => {
7
+ test('lists and runs an authored plan through registered browser commands', async () => {
8
+ const manifest = {
9
+ endpoint: 'windows',
10
+ entries: [
11
+ {
12
+ id: 'components-button--default',
13
+ name: 'Default',
14
+ packageName: '@fluentui-react-native/components',
15
+ sourcePath: 'src/components/button/button.stories.tsx',
16
+ tags: ['e2e', 'story'],
17
+ tests: {
18
+ version: 1,
19
+ tests: [
20
+ {
21
+ id: 'click',
22
+ steps: [
23
+ { action: 'click', target: { testId: 'button-primary' } },
24
+ { expect: { state: 'focused', target: { testId: 'button-primary' }, value: true } },
25
+ ],
26
+ },
27
+ ],
28
+ },
29
+ title: 'Components/Button',
30
+ },
31
+ ],
32
+ platformManifestDigest: 'platform-digest',
33
+ portablePlanDigest: 'portable-digest',
34
+ schemaVersion: 1,
35
+ };
36
+ const harness = await createDesktopDriverStoryHarness(manifest);
37
+ const artifactsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-driver-wdio-'));
38
+ try {
39
+ const response = await runContract(harness.server.url, harness.target.id, artifactsRoot);
40
+ expect(response).toMatchObject({
41
+ manifestEntries: 1,
42
+ result: {
43
+ status: 'passed',
44
+ tests: [{ status: 'passed', storyId: 'components-button--default', testId: 'click' }],
45
+ },
46
+ });
47
+ expect(fs.existsSync(path.join(artifactsRoot, 'run.json'))).toBe(true);
48
+ }
49
+ finally {
50
+ await harness.close();
51
+ fs.rmSync(artifactsRoot, { force: true, recursive: true });
52
+ }
53
+ });
54
+ });
55
+ function runContract(url, targetId, artifactsRoot) {
56
+ return new Promise((resolve, reject) => {
57
+ const child = spawn(process.execPath, [path.join(__dirname, 'wdioRunner.contract.cjs'), url, targetId, artifactsRoot], {
58
+ stdio: ['ignore', 'pipe', 'pipe'],
59
+ });
60
+ const stdout = [];
61
+ const stderr = [];
62
+ child.stdout.on('data', (chunk) => stdout.push(chunk));
63
+ child.stderr.on('data', (chunk) => stderr.push(chunk));
64
+ child.once('error', reject);
65
+ child.once('exit', (code) => {
66
+ if (code !== 0) {
67
+ reject(new Error(Buffer.concat(stderr).toString('utf8') || `WebdriverIO runner exited with code ${code}.`));
68
+ return;
69
+ }
70
+ resolve(JSON.parse(Buffer.concat(stdout).toString('utf8')));
71
+ });
72
+ });
73
+ }
74
+ //# sourceMappingURL=DesktopWebdriver.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DesktopWebdriver.test.js","sourceRoot":"","sources":["../../src/wdio/DesktopWebdriver.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAEhF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,IAAI,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,QAAQ,GAAyB;YACrC,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE;gBACP;oBACE,EAAE,EAAE,4BAA4B;oBAChC,IAAI,EAAE,SAAS;oBACf,WAAW,EAAE,mCAAmC;oBAChD,UAAU,EAAE,0CAA0C;oBACtD,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;oBACtB,KAAK,EAAE;wBACL,OAAO,EAAE,CAAC;wBACV,KAAK,EAAE;4BACL;gCACE,EAAE,EAAE,OAAO;gCACX,KAAK,EAAE;oCACL,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE;oCACzD,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;iCACpF;6BACF;yBACF;qBACF;oBACD,KAAK,EAAE,mBAAmB;iBAC3B;aACF;YACD,sBAAsB,EAAE,iBAAiB;YACzC,kBAAkB,EAAE,iBAAiB;YACrC,aAAa,EAAE,CAAC;SACjB,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,+BAA+B,CAAC,QAAQ,CAAC,CAAC;QAChE,MAAM,aAAa,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;YACzF,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC;gBAC7B,eAAe,EAAE,CAAC;gBAClB,MAAM,EAAE;oBACN,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,4BAA4B,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;iBACtF;aACF,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACtB,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,GAAW,EAAE,QAAgB,EAAE,aAAqB;IACvE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,aAAa,CAAC,EAAE;YACrH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QACH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC1B,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,uCAAuC,IAAI,GAAG,CAAC,CAAC,CAAC;gBAC5G,OAAO;YACT,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA4B,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { connectDesktopWebdriver, DesktopWebdriverSession } from './DesktopWebdriver.js';
2
+ export type { DesktopWebdriverOptions, DesktopWebdriverRunOptions } from './DesktopWebdriver.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wdio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACzF,YAAY,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { connectDesktopWebdriver, DesktopWebdriverSession } from './DesktopWebdriver.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wdio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ const path = require('node:path');
36
+ const { pathToFileURL } = require('node:url');
37
+ async function main() {
38
+ const moduleUrl = pathToFileURL(path.resolve(__dirname, '..', '..', 'lib', 'wdio', 'index.js')).href;
39
+ const { connectDesktopWebdriver } = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
40
+ const [url, targetId, artifactsRoot] = process.argv.slice(2);
41
+ const desktop = await connectDesktopWebdriver({
42
+ platformName: 'windows',
43
+ targetId,
44
+ url,
45
+ });
46
+ try {
47
+ const manifest = await desktop.browser.desktopListStories();
48
+ await desktop.browser.desktopExpect({
49
+ state: 'enabled',
50
+ target: { testId: 'button-primary' },
51
+ value: true,
52
+ });
53
+ const result = await desktop.browser.desktopRunStoryTests({ artifactsRoot });
54
+ process.stdout.write(JSON.stringify({ manifestEntries: manifest.entries.length, result }));
55
+ }
56
+ finally {
57
+ await desktop.delete();
58
+ }
59
+ }
60
+ main().catch((error) => {
61
+ process.stderr.write(error.stack ?? error.message);
62
+ process.exitCode = 1;
63
+ });
64
+ //# sourceMappingURL=wdioRunner.contract.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wdioRunner.contract.cjs","sourceRoot":"","sources":["../../src/wdio/wdioRunner.contract.cjs"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAClC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAE9C,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACrG,MAAM,EAAE,uBAAuB,EAAE,GAAG,yBAAa,SAAS,uCAAC,CAAC;IAC5D,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,MAAM,uBAAuB,CAAC;QAC5C,YAAY,EAAE,SAAS;QACvB,QAAQ;QACR,GAAG;KACJ,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC5D,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;YAClC,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE;YACpC,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7F,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=wdioRunner.contract.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wdioRunner.contract.d.cts","sourceRoot":"","sources":["../../src/wdio/wdioRunner.contract.cjs"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "@fluentui-react-native/desktop-driver",
3
+ "version": "0.2.0",
4
+ "description": "W3C WebDriver-compatible automation for React Native desktop applications",
5
+ "license": "MIT",
6
+ "author": "",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/microsoft/fluentui-react-native.git",
10
+ "directory": "packages/agentic/desktop-driver"
11
+ },
12
+ "bin": "./config/cli.cjs",
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "module": "lib/index.js",
16
+ "types": "lib/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./lib/index.d.ts",
20
+ "import": "./lib/index.js",
21
+ "default": "./src/index.ts"
22
+ },
23
+ "./client": {
24
+ "types": "./lib/client/index.d.ts",
25
+ "import": "./lib/client/index.js",
26
+ "default": "./src/client/index.ts"
27
+ },
28
+ "./cli": {
29
+ "types": "./lib/cli/index.d.ts",
30
+ "import": "./lib/cli/index.js",
31
+ "default": "./src/cli/index.ts"
32
+ },
33
+ "./authoring": {
34
+ "types": "./lib/authoring/index.d.ts",
35
+ "import": "./lib/authoring/index.js",
36
+ "default": "./src/authoring/index.ts"
37
+ },
38
+ "./agent": {
39
+ "types": "./lib/agent/index.d.ts",
40
+ "import": "./lib/agent/index.js",
41
+ "default": "./src/agent/index.ts"
42
+ },
43
+ "./artifacts": {
44
+ "types": "./lib/artifacts/index.d.ts",
45
+ "import": "./lib/artifacts/index.js",
46
+ "default": "./src/artifacts/index.ts"
47
+ },
48
+ "./server": {
49
+ "types": "./lib/server/index.d.ts",
50
+ "import": "./lib/server/index.js",
51
+ "default": "./src/server/index.ts"
52
+ },
53
+ "./storybook": {
54
+ "types": "./lib/storybook.d.ts",
55
+ "import": "./lib/storybook.js",
56
+ "default": "./src/storybook.ts"
57
+ },
58
+ "./testing": {
59
+ "types": "./lib/testing/index.d.ts",
60
+ "import": "./lib/testing/index.js",
61
+ "default": "./src/testing/index.ts"
62
+ },
63
+ "./wdio": {
64
+ "types": "./lib/wdio/index.d.ts",
65
+ "import": "./lib/wdio/index.js",
66
+ "default": "./src/wdio/index.ts"
67
+ },
68
+ "./runner": {
69
+ "types": "./lib/runner/index.d.ts",
70
+ "import": "./lib/runner/index.js",
71
+ "default": "./src/runner/index.ts"
72
+ },
73
+ "./package.json": "./package.json"
74
+ },
75
+ "scripts": {
76
+ "build": "tsc -b",
77
+ "clean": "fluentui-scripts clean",
78
+ "format": "fluentui-scripts format",
79
+ "lint": "fluentui-scripts lint",
80
+ "test": "fluentui-scripts jest"
81
+ },
82
+ "dependencies": {
83
+ "commander": "^14.0.2",
84
+ "webdriverio": "^9.24.0"
85
+ },
86
+ "devDependencies": {
87
+ "@fluentui-react-native/scripts": "0.1.0"
88
+ },
89
+ "furn": {
90
+ "jestPlatform": "react"
91
+ },
92
+ "rnx-kit": {
93
+ "kitType": "library",
94
+ "extends": "@fluentui-react-native/scripts/kit-config"
95
+ }
96
+ }
@@ -0,0 +1,102 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+
6
+ import { createDesktopDriverClient } from '../client/DesktopDriverClient.js';
7
+ import type { DesktopStoryManifest } from '../storybook.js';
8
+ import { createDesktopDriverTestHarness } from '../testing/protocolHarness.js';
9
+ import { createDesktopDriverStoryHarness } from '../testing/protocolHarness.js';
10
+ import { connectDesktopAgent } from './DesktopAgent.js';
11
+
12
+ describe('DesktopAgent', () => {
13
+ test('lists, explains, inspects, acts, checks, captures, and runs the same authored plan', async () => {
14
+ const manifest: DesktopStoryManifest = {
15
+ endpoint: 'windows',
16
+ entries: [
17
+ {
18
+ id: 'components-button--default',
19
+ name: 'Default',
20
+ packageName: '@fluentui-react-native/components',
21
+ sourcePath: 'src/components/button/button.stories.tsx',
22
+ tags: ['e2e', 'story'],
23
+ tests: {
24
+ version: 1,
25
+ tests: [
26
+ {
27
+ id: 'agent-plan',
28
+ steps: [{ expect: { state: 'enabled', target: { testId: 'button-primary' }, value: true } }],
29
+ },
30
+ ],
31
+ },
32
+ title: 'Components/Button',
33
+ },
34
+ ],
35
+ platformManifestDigest: 'platform-digest',
36
+ portablePlanDigest: 'portable-digest',
37
+ schemaVersion: 1,
38
+ };
39
+ const harness = await createDesktopDriverStoryHarness(manifest);
40
+ const artifactsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-driver-agent-'));
41
+ try {
42
+ const response = await runContract(harness.server.url, harness.target.id, artifactsRoot);
43
+ expect(response).toMatchObject({
44
+ check: { passed: true },
45
+ run: { status: 'passed', tests: [{ testId: 'agent-plan' }] },
46
+ screenshot: { kind: 'screenshot', name: 'agent-button' },
47
+ stories: 1,
48
+ tree: 1,
49
+ });
50
+ expect(fs.existsSync(path.join(artifactsRoot, 'run.json'))).toBe(true);
51
+ } finally {
52
+ await harness.close();
53
+ fs.rmSync(artifactsRoot, { force: true, recursive: true });
54
+ }
55
+ });
56
+
57
+ test('validates the artifact root before reserving a target session', async () => {
58
+ const harness = await createDesktopDriverTestHarness();
59
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-driver-agent-invalid-'));
60
+ const invalidRoot = path.join(temporaryDirectory, 'file');
61
+ fs.writeFileSync(invalidRoot, 'not a directory');
62
+ try {
63
+ await expect(
64
+ connectDesktopAgent({
65
+ artifactsRoot: invalidRoot,
66
+ platformName: 'windows',
67
+ targetId: harness.target.id,
68
+ url: harness.server.url,
69
+ }),
70
+ ).rejects.toThrow();
71
+
72
+ const client = createDesktopDriverClient({ url: harness.server.url });
73
+ const session = await client.newSession({
74
+ alwaysMatch: { platformName: 'windows', 'furn:target': harness.target.id },
75
+ });
76
+ await session.delete();
77
+ } finally {
78
+ await harness.close();
79
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
80
+ }
81
+ });
82
+ });
83
+
84
+ function runContract(url: string, targetId: string, artifactsRoot: string): Promise<Record<string, unknown>> {
85
+ return new Promise((resolve, reject) => {
86
+ const child = spawn(process.execPath, [path.join(__dirname, 'agent.contract.cjs'), url, targetId, artifactsRoot], {
87
+ stdio: ['ignore', 'pipe', 'pipe'],
88
+ });
89
+ const stdout: Buffer[] = [];
90
+ const stderr: Buffer[] = [];
91
+ child.stdout.on('data', (chunk: Buffer) => stdout.push(chunk));
92
+ child.stderr.on('data', (chunk: Buffer) => stderr.push(chunk));
93
+ child.once('error', reject);
94
+ child.once('exit', (code) => {
95
+ if (code !== 0) {
96
+ reject(new Error(Buffer.concat(stderr).toString('utf8') || `Desktop agent exited with code ${code}.`));
97
+ return;
98
+ }
99
+ resolve(JSON.parse(Buffer.concat(stdout).toString('utf8')) as Record<string, unknown>);
100
+ });
101
+ });
102
+ }
@@ -0,0 +1,170 @@
1
+ import type { DesktopArtifact, DesktopStoryRunResult } from '../authoring/results.js';
2
+ import type { DesktopStoryExpectation, DesktopStorySelector } from '../authoring/storyTests.js';
3
+ import { ArtifactManager } from '../artifacts/ArtifactManager.js';
4
+ import type { DesktopTreeNode } from '../host/types.js';
5
+ import { DesktopAssertionError, assertDesktopExpectation, findDesktopElement } from '../runner/StoryTestRunner.js';
6
+ import { WebDriverError } from '../protocol/errors.js';
7
+ import type { DesktopStoryManifestEntry } from '../storybook.js';
8
+ import { connectDesktopWebdriver } from '../wdio/DesktopWebdriver.js';
9
+ import type { DesktopWebdriverOptions, DesktopWebdriverSession } from '../wdio/DesktopWebdriver.js';
10
+
11
+ export type DesktopAgentOptions = DesktopWebdriverOptions & {
12
+ artifactsRoot: string;
13
+ };
14
+
15
+ export type DesktopAgentStory = Pick<DesktopStoryManifestEntry, 'id' | 'name' | 'tags' | 'title'> & {
16
+ tests: readonly { id: string; title: string }[];
17
+ };
18
+
19
+ export type DesktopAgentElement = {
20
+ accessibleName: unknown;
21
+ enabled: boolean;
22
+ id: string;
23
+ role: string;
24
+ selected: boolean;
25
+ text: string;
26
+ };
27
+
28
+ export type DesktopAgentCheckResult = {
29
+ message?: string;
30
+ passed: boolean;
31
+ };
32
+
33
+ export type DesktopAgentDescribeOptions = {
34
+ depth?: number;
35
+ maxNodes?: number;
36
+ scope?: 'application' | 'story';
37
+ };
38
+
39
+ export class DesktopAgent {
40
+ private readonly artifacts: ArtifactManager;
41
+ private readonly desktop: DesktopWebdriverSession;
42
+ private readonly captured: DesktopArtifact[] = [];
43
+
44
+ constructor(desktop: DesktopWebdriverSession, artifacts: ArtifactManager) {
45
+ this.desktop = desktop;
46
+ this.artifacts = artifacts;
47
+ }
48
+
49
+ async listStories(): Promise<DesktopAgentStory[]> {
50
+ const manifest = await this.desktop.listStories();
51
+ return manifest.entries.map((entry) => ({
52
+ id: entry.id,
53
+ name: entry.name,
54
+ tags: entry.tags,
55
+ tests: (entry.tests?.tests ?? []).map((test) => ({ id: test.id, title: test.title ?? test.id })),
56
+ title: entry.title,
57
+ }));
58
+ }
59
+
60
+ async explainStory(storyId: string): Promise<DesktopAgentStory> {
61
+ const story = (await this.listStories()).find(({ id }) => id === storyId);
62
+ if (!story) {
63
+ throw new Error(`Story "${storyId}" is not present in the active platform manifest.`);
64
+ }
65
+ return story;
66
+ }
67
+
68
+ openStory(storyId: string): Promise<{ previewGeneration: number; runId: string; storyId: string }> {
69
+ return this.desktop.openStory(storyId);
70
+ }
71
+
72
+ async describe(options: DesktopAgentDescribeOptions = {}): Promise<DesktopTreeNode[]> {
73
+ const depth = options.depth ?? 3;
74
+ const maxNodes = options.maxNodes ?? 100;
75
+ if (!Number.isInteger(depth) || depth < 0 || !Number.isInteger(maxNodes) || maxNodes < 1) {
76
+ throw new TypeError('Agent tree depth must be non-negative and maxNodes must be positive.');
77
+ }
78
+ const roots = await this.desktop.session.getTree();
79
+ const scoped =
80
+ options.scope === 'story'
81
+ ? roots.flatMap((root) =>
82
+ findTreeNodes(root, (node) => node.testId === 'story-root' || (node.testId?.endsWith('-story-root') ?? false)),
83
+ )
84
+ : roots;
85
+ let remaining = maxNodes;
86
+ return scoped.flatMap((root) => {
87
+ const projected = projectTree(root, depth, () => remaining-- > 0);
88
+ return projected ? [projected] : [];
89
+ });
90
+ }
91
+
92
+ async find(selector: DesktopStorySelector): Promise<DesktopAgentElement> {
93
+ const element = await findDesktopElement(this.desktop.session, selector);
94
+ return {
95
+ accessibleName: await element.getAttribute('name'),
96
+ enabled: await element.isEnabled(),
97
+ id: element.id,
98
+ role: await element.getTagName(),
99
+ selected: await element.isSelected(),
100
+ text: await element.getText(),
101
+ };
102
+ }
103
+
104
+ async click(selector: DesktopStorySelector): Promise<void> {
105
+ await (await findDesktopElement(this.desktop.session, selector)).click();
106
+ }
107
+
108
+ async type(selector: DesktopStorySelector, text: string): Promise<void> {
109
+ await (await findDesktopElement(this.desktop.session, selector)).sendKeys(text);
110
+ }
111
+
112
+ async check(expectation: DesktopStoryExpectation): Promise<DesktopAgentCheckResult> {
113
+ try {
114
+ await assertDesktopExpectation(this.desktop.session, expectation);
115
+ return { passed: true };
116
+ } catch (error) {
117
+ if (error instanceof DesktopAssertionError || (error instanceof WebDriverError && error.code === 'no such element')) {
118
+ return { message: error.message, passed: false };
119
+ }
120
+ throw error;
121
+ }
122
+ }
123
+
124
+ async screenshot(name: string): Promise<DesktopArtifact> {
125
+ const artifact = this.artifacts.writeScreenshot('agent', name, await this.desktop.session.takeScreenshot());
126
+ this.captured.push(artifact);
127
+ return artifact;
128
+ }
129
+
130
+ runStoryTest(storyId: string, testId: string): Promise<DesktopStoryRunResult> {
131
+ return this.desktop.runStoryTests({
132
+ artifactsRoot: this.artifacts.root,
133
+ selection: { story: storyId, test: testId },
134
+ });
135
+ }
136
+
137
+ getArtifacts(): readonly DesktopArtifact[] {
138
+ return [...this.captured];
139
+ }
140
+
141
+ delete(): Promise<void> {
142
+ return this.desktop.delete();
143
+ }
144
+ }
145
+
146
+ export async function connectDesktopAgent(options: DesktopAgentOptions): Promise<DesktopAgent> {
147
+ const artifacts = new ArtifactManager(options.artifactsRoot);
148
+ const desktop = await connectDesktopWebdriver(options);
149
+ return new DesktopAgent(desktop, artifacts);
150
+ }
151
+
152
+ function findTreeNodes(node: DesktopTreeNode, predicate: (node: DesktopTreeNode) => boolean): DesktopTreeNode[] {
153
+ return [...(predicate(node) ? [node] : []), ...node.children.flatMap((child) => findTreeNodes(child, predicate))];
154
+ }
155
+
156
+ function projectTree(node: DesktopTreeNode, depth: number, take: () => boolean): DesktopTreeNode | undefined {
157
+ if (!take()) {
158
+ return undefined;
159
+ }
160
+ return {
161
+ ...node,
162
+ children:
163
+ depth === 0
164
+ ? []
165
+ : node.children.flatMap((child) => {
166
+ const projected = projectTree(child, depth - 1, take);
167
+ return projected ? [projected] : [];
168
+ }),
169
+ };
170
+ }
@@ -0,0 +1,32 @@
1
+ const path = require('node:path');
2
+ const { pathToFileURL } = require('node:url');
3
+
4
+ async function main() {
5
+ const moduleUrl = pathToFileURL(path.resolve(__dirname, '..', '..', 'lib', 'agent', 'index.js')).href;
6
+ const { connectDesktopAgent } = await import(moduleUrl);
7
+ const [url, targetId, artifactsRoot] = process.argv.slice(2);
8
+ const agent = await connectDesktopAgent({
9
+ artifactsRoot,
10
+ platformName: 'windows',
11
+ targetId,
12
+ url,
13
+ });
14
+ try {
15
+ const stories = await agent.listStories();
16
+ const story = await agent.explainStory('components-button--default');
17
+ await agent.openStory(story.id);
18
+ const tree = await agent.describe({ depth: 2, maxNodes: 10, scope: 'story' });
19
+ await agent.click({ testId: 'button-primary' });
20
+ const check = await agent.check({ state: 'focused', target: { testId: 'button-primary' }, value: true });
21
+ const screenshot = await agent.screenshot('agent-button');
22
+ const run = await agent.runStoryTest(story.id, 'agent-plan');
23
+ process.stdout.write(JSON.stringify({ check, run, screenshot, stories: stories.length, tree: tree.length }));
24
+ } finally {
25
+ await agent.delete();
26
+ }
27
+ }
28
+
29
+ main().catch((error) => {
30
+ process.stderr.write(error.stack ?? error.message);
31
+ process.exitCode = 1;
32
+ });
@@ -0,0 +1,8 @@
1
+ export { connectDesktopAgent, DesktopAgent } from './DesktopAgent.js';
2
+ export type {
3
+ DesktopAgentCheckResult,
4
+ DesktopAgentDescribeOptions,
5
+ DesktopAgentElement,
6
+ DesktopAgentOptions,
7
+ DesktopAgentStory,
8
+ } from './DesktopAgent.js';
@@ -0,0 +1,20 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ import { ArtifactManager } from './ArtifactManager.js';
6
+
7
+ describe('ArtifactManager', () => {
8
+ test('confines and atomically writes artifacts beneath the run root', () => {
9
+ const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-driver-artifacts-'));
10
+ try {
11
+ const manager = new ArtifactManager(temporaryDirectory);
12
+ const artifact = manager.writeSource('../story:id', '../source', '<application />');
13
+
14
+ expect(artifact.path).toBe('tests/-story-id/-source.xml');
15
+ expect(fs.readFileSync(path.join(temporaryDirectory, ...artifact.path.split('/')), 'utf8')).toBe('<application />');
16
+ } finally {
17
+ fs.rmSync(temporaryDirectory, { force: true, recursive: true });
18
+ }
19
+ });
20
+ });