trxa-tool 0.0.0-stage → 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 (155) hide show
  1. package/.claude/skills/replay-trxa-testplan/SKILL.md +627 -0
  2. package/LICENSE +120 -0
  3. package/MANUAL.md +556 -0
  4. package/README.md +91 -3
  5. package/SETUP.md +401 -0
  6. package/STUDIO.md +427 -0
  7. package/dist/analyze.d.ts +58 -0
  8. package/dist/analyze.js +122 -0
  9. package/dist/bpm.d.ts +40 -0
  10. package/dist/bpm.js +60 -0
  11. package/dist/build-test-script.d.ts +24 -0
  12. package/dist/build-test-script.js +40 -0
  13. package/dist/csv.d.ts +1 -0
  14. package/dist/csv.js +11 -0
  15. package/dist/extract.d.ts +14 -0
  16. package/dist/extract.js +34 -0
  17. package/dist/fixture-schema.d.ts +52 -0
  18. package/dist/fixture-schema.js +112 -0
  19. package/dist/fixture-store.d.ts +37 -0
  20. package/dist/fixture-store.js +75 -0
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.js +850 -0
  23. package/dist/library-archive.d.ts +37 -0
  24. package/dist/library-archive.js +106 -0
  25. package/dist/mapping.d.ts +25 -0
  26. package/dist/mapping.js +393 -0
  27. package/dist/mcp-bridge.d.ts +52 -0
  28. package/dist/mcp-bridge.js +290 -0
  29. package/dist/normalize.d.ts +21 -0
  30. package/dist/normalize.js +43 -0
  31. package/dist/parse.d.ts +3 -0
  32. package/dist/parse.js +233 -0
  33. package/dist/plan-runner.d.ts +27 -0
  34. package/dist/plan-runner.js +36 -0
  35. package/dist/plan-schema.d.ts +27 -0
  36. package/dist/plan-schema.js +45 -0
  37. package/dist/planrun-schema.d.ts +53 -0
  38. package/dist/planrun-schema.js +111 -0
  39. package/dist/planrun.d.ts +20 -0
  40. package/dist/planrun.js +46 -0
  41. package/dist/report-schema.d.ts +92 -0
  42. package/dist/report-schema.js +115 -0
  43. package/dist/report.d.ts +45 -0
  44. package/dist/report.js +77 -0
  45. package/dist/schema.d.ts +96 -0
  46. package/dist/schema.js +10 -0
  47. package/dist/scrub.d.ts +44 -0
  48. package/dist/scrub.js +166 -0
  49. package/dist/suggest-inputs.d.ts +19 -0
  50. package/dist/suggest-inputs.js +92 -0
  51. package/dist/suite-runner.d.ts +59 -0
  52. package/dist/suite-runner.js +204 -0
  53. package/dist/suite-schema.d.ts +59 -0
  54. package/dist/suite-schema.js +95 -0
  55. package/dist/testcase-schema.d.ts +102 -0
  56. package/dist/testcase-schema.js +132 -0
  57. package/dist/token-usage.d.ts +38 -0
  58. package/dist/token-usage.js +95 -0
  59. package/media/studio/fixtures-test-cases.png +0 -0
  60. package/media/studio/plans-editor.png +0 -0
  61. package/media/studio/recordings-create-test-case.png +0 -0
  62. package/media/studio/run-dialog.png +0 -0
  63. package/media/studio/settings-agent-runs.png +0 -0
  64. package/media/studio/settings-working-folder.png +0 -0
  65. package/media/studio/studio-recordings.png +0 -0
  66. package/media/studio/suites-canvas.png +0 -0
  67. package/media/studio/suites-inspector.png +0 -0
  68. package/media/studio/testcases-expectations.png +0 -0
  69. package/media/studio/testcases-outputs.png +0 -0
  70. package/media/studio/testrun-details.png +0 -0
  71. package/media/studio/testruns-list.png +0 -0
  72. package/package.json +62 -6
  73. package/studio/client-dist/assets/CreatePanel-C5bb5OIs.js +6 -0
  74. package/studio/client-dist/assets/FactTile-G1pURLys.js +1 -0
  75. package/studio/client-dist/assets/FieldLabel-DPmIEQ08.js +1 -0
  76. package/studio/client-dist/assets/FixtureNamePicker-137AAgwZ.js +1 -0
  77. package/studio/client-dist/assets/Fixtures-D0t-soph.js +1 -0
  78. package/studio/client-dist/assets/GeneralSection-CWkTeSX2.js +1 -0
  79. package/studio/client-dist/assets/ListPaneHeader-DXKqX36N.js +1 -0
  80. package/studio/client-dist/assets/Plans-C70evkGc.js +1 -0
  81. package/studio/client-dist/assets/Recordings-BT0dyVLs.js +1 -0
  82. package/studio/client-dist/assets/Settings-k-J3u5lH.js +1 -0
  83. package/studio/client-dist/assets/Suites-DLioOiRN.css +1 -0
  84. package/studio/client-dist/assets/Suites-DQhhMxjT.js +7 -0
  85. package/studio/client-dist/assets/TestCases-BdiopVGI.js +1 -0
  86. package/studio/client-dist/assets/TestRuns-CylAe-3t.js +9 -0
  87. package/studio/client-dist/assets/api-DJZ703_F.js +24 -0
  88. package/studio/client-dist/assets/bucket-18-viuwGcaj.js +1 -0
  89. package/studio/client-dist/assets/fixtureName-CTZT_jKu.js +1 -0
  90. package/studio/client-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  91. package/studio/client-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  92. package/studio/client-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  93. package/studio/client-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  94. package/studio/client-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  95. package/studio/client-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  96. package/studio/client-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  97. package/studio/client-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  98. package/studio/client-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  99. package/studio/client-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  100. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-300-normal-LoaTnByX.woff +0 -0
  101. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-300-normal-RhT0Ayi3.woff2 +0 -0
  102. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-italic-Bf1n82Tb.woff +0 -0
  103. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-italic-D9J6q4_O.woff2 +0 -0
  104. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
  105. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
  106. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
  107. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
  108. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-300-normal-Bbd5v1xK.woff +0 -0
  109. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-300-normal-DqmgDkIN.woff2 +0 -0
  110. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-italic-BELOEI8R.woff2 +0 -0
  111. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-italic-CfSXsIbO.woff +0 -0
  112. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
  113. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
  114. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
  115. package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
  116. package/studio/client-dist/assets/ibm-plex-sans-greek-300-normal-J-CBqZoJ.woff2 +0 -0
  117. package/studio/client-dist/assets/ibm-plex-sans-greek-300-normal-STc8XiDr.woff +0 -0
  118. package/studio/client-dist/assets/ibm-plex-sans-greek-400-italic-BEZwmy7D.woff2 +0 -0
  119. package/studio/client-dist/assets/ibm-plex-sans-greek-400-italic-CTYLy0lX.woff +0 -0
  120. package/studio/client-dist/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
  121. package/studio/client-dist/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
  122. package/studio/client-dist/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
  123. package/studio/client-dist/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
  124. package/studio/client-dist/assets/ibm-plex-sans-latin-300-normal-C9Of02-5.woff +0 -0
  125. package/studio/client-dist/assets/ibm-plex-sans-latin-300-normal-b4i7YzLG.woff2 +0 -0
  126. package/studio/client-dist/assets/ibm-plex-sans-latin-400-italic-CZTNEAuW.woff2 +0 -0
  127. package/studio/client-dist/assets/ibm-plex-sans-latin-400-italic-CsGl1sm0.woff +0 -0
  128. package/studio/client-dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  129. package/studio/client-dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  130. package/studio/client-dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  131. package/studio/client-dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  132. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-300-normal-B8Zgwjh3.woff +0 -0
  133. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-300-normal-BLFuR0P1.woff2 +0 -0
  134. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-italic-Du9zzoL2.woff2 +0 -0
  135. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-italic-V_9i1P_m.woff +0 -0
  136. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
  137. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
  138. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
  139. package/studio/client-dist/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
  140. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-300-normal-CkYRZD2a.woff +0 -0
  141. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-300-normal-SDfX6ktj.woff2 +0 -0
  142. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-italic-78jqtfZH.woff +0 -0
  143. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-italic-BbRnwouv.woff2 +0 -0
  144. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
  145. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
  146. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
  147. package/studio/client-dist/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
  148. package/studio/client-dist/assets/index-cxBc2iRv.js +10 -0
  149. package/studio/client-dist/assets/index-wjWuVHXN.css +1 -0
  150. package/studio/client-dist/assets/status-DUGxC7G7.js +1 -0
  151. package/studio/client-dist/assets/testCaseTree-Cl1-bc5O.js +1 -0
  152. package/studio/client-dist/axtr-icon.svg +2 -0
  153. package/studio/client-dist/favicon.svg +2 -0
  154. package/studio/client-dist/index.html +15 -0
  155. package/studio/server/dist/index.js +3176 -0
package/STUDIO.md ADDED
@@ -0,0 +1,427 @@
1
+ # Use TRXA Studio
2
+
3
+ TRXA Studio is the visual workspace for TRXA-TOOL. Use it to turn Dynamics 365 Finance and Supply
4
+ Chain Management Task Recorder recordings into automated tests, organize the tests into suites and
5
+ plans, run them with an AI agent, and review the results. You don't need to edit any files by hand.
6
+
7
+ Studio is available as a Windows app and as a browser page that the npm package starts. Both work
8
+ the same way. This article applies to both, except where a section says otherwise.
9
+
10
+ In this article, you learn how to:
11
+
12
+ - Start Studio and choose a working folder
13
+ - Create a test case from a recording
14
+ - Define what a test captures and checks
15
+ - Manage test data with fixtures
16
+ - Build suites and plans
17
+ - Run a test with an AI agent
18
+ - Review test runs
19
+
20
+ ## Prerequisites
21
+
22
+ - TRXA-TOOL installed, either the Windows app or the npm package. For instructions, see
23
+ [Step 1: Install TRXA-TOOL](SETUP.md#step-1-install-trxa-tool).
24
+ - One or more Task Recorder recordings (`.axtr` files). Record them in Dynamics 365 with Task
25
+ recorder, or export them from a Business Process Modeler library in Lifecycle Services.
26
+ - To run tests with an AI agent: an agent and a connection to Dynamics 365. Complete every step in
27
+ [Set up TRXA Studio for agent runs](SETUP.md).
28
+
29
+ ## Key concepts
30
+
31
+ Studio works with seven kinds of records. Each one builds on the one before it.
32
+
33
+ | Record | What it is | Folder |
34
+ |---|---|---|
35
+ | **Recording** | A Task Recorder file (`.axtr`) that captures the steps someone performed in Dynamics 365. | `recordings` |
36
+ | **Test script** | The recording's steps, translated into actions that an agent can perform. Studio creates it for you. | `testscripts` |
37
+ | **Test case** | One business scenario that passes or fails, such as "Create purchase order". It defines the test's inputs, the values to capture (outputs) and the checks to make (expectations). | `testcases` |
38
+ | **Fixture** | A named set of test data, such as one company or one scenario. It holds input values for many test cases. | `testdata` |
39
+ | **Suite** | Test cases that run together, in an order you define. A suite also says where each test case gets its input values. | `suites` |
40
+ | **Plan** | A group of suites that run and report together, such as every suite for Accounts payable. | `plans` |
41
+ | **Test run** | The result of running a test case, a suite or a plan. | `testruns`, `runs` |
42
+
43
+ All records are files in Studio's *working folder*, so you can keep them in source control and use
44
+ them with the `trxa` command line too. For the file formats, see the [TRXA-TOOL manual](MANUAL.md).
45
+
46
+ ## Start Studio
47
+
48
+ ### Start the Windows app
49
+
50
+ - If you used the installer, open **TRXA Studio** from the **Start** menu.
51
+ - If you use the portable version, run `TRXA Studio.exe` from the folder where you extracted it.
52
+
53
+ The app opens in its own window. The first time, its working folder is `Documents\TRXA Studio`.
54
+
55
+ ### Start Studio in your browser
56
+
57
+ 1. Open a terminal in the folder that holds, or will hold, your recordings and tests.
58
+ 1. Run:
59
+
60
+ ```powershell
61
+ trxa studio
62
+ ```
63
+
64
+ Studio starts in the background and opens `http://127.0.0.1:4321` in your browser. If Studio is
65
+ already running, the command opens the browser page again instead of starting a second copy.
66
+
67
+ 1. To stop Studio, run:
68
+
69
+ ```powershell
70
+ trxa studio stop
71
+ ```
72
+
73
+ The first time Studio starts on a computer, the folder you ran `trxa studio` from becomes the
74
+ working folder. After that, Studio opens the working folder you last chose.
75
+
76
+ > [!WARNING]
77
+ > Studio accepts connections only from your own computer, and it reads and writes files without
78
+ > asking you to sign in. Don't make it reachable from a network.
79
+
80
+ ### Choose the working folder
81
+
82
+ The working folder is where Studio reads and writes all your records. To change it:
83
+
84
+ 1. Select **Settings** (the gear icon in the upper-right corner).
85
+ 1. In **Folder path**, enter the full path of the folder you want.
86
+ 1. Select **Save**.
87
+
88
+ Studio switches to the new folder right away, without a restart.
89
+
90
+ ![The Settings page, with the Working folder and Appearance sections.](media/studio/settings-working-folder.png)
91
+
92
+ On the same page, **Appearance** sets the theme: **Dark**, **Light**, or **Match the operating
93
+ system**.
94
+
95
+ ## Find your way around Studio
96
+
97
+ The rail on the left side opens each page:
98
+
99
+ | Page | Use it to |
100
+ |---|---|
101
+ | **Recordings** | Review a recording's steps and create a test case and fixture from it. |
102
+ | **Test Cases** | Edit a test case's outputs and expectations, and see which fixtures hold its data. |
103
+ | **Fixtures** | Create and edit sets of test data. |
104
+ | **Suites** | Arrange test cases in run order and connect their data. |
105
+ | **Plans** | Group suites, and see a combined result for each run. |
106
+ | **Test Runs** | Review the results of every run. |
107
+
108
+ The buttons in the upper-right corner are, from left to right:
109
+
110
+ - **Run activity**: shows the runs in progress and the most recent runs.
111
+ - **Export library**: downloads all your records as one zip file, for a backup or to move them to
112
+ another computer.
113
+ - **Import library**: restores records from such a zip file.
114
+ - **Settings**: the working folder, the theme, and agent readiness.
115
+
116
+ Most pages share one layout: a searchable list on the left and the selected record on the right.
117
+ Each record opens with a **General** section that holds its name and description. Changes aren't
118
+ saved until you select **Save**, or press **Ctrl+S**. If you try to leave a page with unsaved
119
+ changes, Studio asks you first.
120
+
121
+ The address bar always reflects the page and record you have open. You can bookmark a record,
122
+ refresh the page, or use the browser's **Back** button, and Studio returns to the same place.
123
+
124
+ ## Create a test case from a recording
125
+
126
+ Studio creates a test case, its test script and its first fixture values from a recording in one
127
+ step.
128
+
129
+ 1. Copy your `.axtr` files into the working folder's `recordings` folder. You can use subfolders,
130
+ for example one per business area.
131
+ 1. Select **Recordings**, and then select a recording in the list.
132
+
133
+ Studio reads the recording and shows its steps. For each step, the **Tool** column shows the
134
+ action that the agent will use to replay it, and **Confidence** shows how sure Studio is about
135
+ that action.
136
+
137
+ ![The Recordings page, with the steps of a recording and the Suggest inputs button.](media/studio/studio-recordings.png)
138
+
139
+ 1. Select **Suggest inputs**. Studio finds the values that were typed or selected in the recording.
140
+ These become the test case's *inputs*, the values that can change from one run to the next.
141
+ 1. Select **Create test case & fixture**.
142
+ 1. In the panel that opens, enter or check these values:
143
+
144
+ | Field | Description |
145
+ |---|---|
146
+ | **Test case ID** | A short, unique ID, such as `create-purchase-order`. Studio uses it as the file name. |
147
+ | **Test case name** | A readable name. Studio fills it in from the recording. |
148
+ | **Description** | Optional. |
149
+ | **Fixture name** | Select an existing fixture to add this test case's values to, or enter a new name to create a fixture. |
150
+ | *One field per input* | The value to use for each input in this fixture. Studio fills them in from the recording. |
151
+
152
+ ![The Create test case and fixture panel, with the test case fields and the input values.](media/studio/recordings-create-test-case.png)
153
+
154
+ 1. Select **Create**.
155
+
156
+ Studio creates the test case and a notification links to it.
157
+
158
+ > [!WARNING]
159
+ > Input values come straight from the recording. If the recording was made with real business
160
+ > data, such as real vendor accounts or amounts, replace those values before you share or commit
161
+ > the fixture.
162
+
163
+ ## Define what a test case captures and checks
164
+
165
+ A test case passes only if its *expectations* hold. It can also capture *outputs*, values that a
166
+ later test case in a suite needs, such as the number of a new purchase order.
167
+
168
+ Select **Test Cases**, and then select a test case. The page shows how many inputs, outputs,
169
+ expectations and steps it has, and four tabs:
170
+
171
+ - **Inputs** lists the test case's inputs. To use an input's value in a filter or an expected
172
+ value, type its name in double braces, for example `{{vendorAccount}}`. Inputs come from the
173
+ recording, so you change them on the **Recordings** page.
174
+ - **Outputs** and **Expectations** are described in the following sections.
175
+ - **Fixtures** lists the fixtures that hold values for this test case.
176
+
177
+ ### Add an output
178
+
179
+ 1. Select the **Outputs** tab, and then select **Add output**.
180
+ 1. In **Output name**, enter a name. A suite refers to the output by this name.
181
+ 1. Select an **Output kind**:
182
+ - **OData** reads the value from a data entity. Enter the **Entity set**, a **Filter** that finds
183
+ the record, and the **Field** to read. Use this kind when the record has a reliable business
184
+ key to filter on.
185
+ - **Form field** reads the value from the form after a step. Select the step in **After step**.
186
+ Then select the control in **Control name**, or, if you don't know the control name, describe
187
+ the value in **Hint**, for example "The new purchase order number shown after clicking New".
188
+ During a run, the agent finds the control and fills in its name for later runs.
189
+ 1. Select **Save**.
190
+
191
+ ![The Outputs tab of a test case, with a Form field output that reads the purchase order number.](media/studio/testcases-outputs.png)
192
+
193
+ ### Add an expectation
194
+
195
+ 1. Select the **Expectations** tab, and then select **Add expectation**.
196
+ 1. In **Description**, describe the check. The description appears in the test results.
197
+ 1. Select the step in **Check after step**.
198
+ 1. Select an **Expectation kind**:
199
+ - **OData** queries a data entity and compares a field with a value. Enter the **Entity set**,
200
+ **Filter**, **Expected field**, **Comparator** and **Expected value**.
201
+ - **Message** checks the messages that Dynamics 365 showed. Select a **Severity**, and
202
+ optionally enter text in **Message contains**. Leave it empty to match any message of that
203
+ severity.
204
+ 1. Select **Save**.
205
+
206
+ ![The Expectations tab of a test case, with a check that no error message appeared.](media/studio/testcases-expectations.png)
207
+
208
+ > [!TIP]
209
+ > To check that *no* error appeared, select **Severity** > **Error** and clear **Message should
210
+ > appear**. If you select **Message should appear**, the test passes only when an error *does*
211
+ > appear.
212
+
213
+ To delete a test case, select **Delete**. Studio deletes only the test case. Its test script stays,
214
+ because other test cases can share it, and any suite or fixture that refers to the test case keeps
215
+ the reference until you remove it.
216
+
217
+ ## Manage test data with fixtures
218
+
219
+ A fixture is a named set of test data, such as one company or one scenario. It holds the input
220
+ values for every test case it covers. To run the same tests with different data, create another
221
+ fixture and choose it in the suite.
222
+
223
+ ### Create a fixture
224
+
225
+ 1. Select **Fixtures**, and then select **New fixture** (the plus icon above the list).
226
+ 1. Enter a **Name** and, optionally, a **Description**. Suites refer to the fixture by its name.
227
+ 1. Select **Create**. The new fixture opens.
228
+
229
+ ### Add test cases and values
230
+
231
+ 1. Select a fixture. The **Test cases** table lists each test case that the fixture holds values
232
+ for, with how many values are set and a status, such as **Complete**.
233
+ 1. To add test cases, select **Add test cases**, select one or more test cases, and then select
234
+ **Add**.
235
+ 1. To edit a test case's values, expand its row, and then enter the values. Required values are
236
+ checked when you leave each field.
237
+ 1. Select **Save**. Studio opens any row that still has a required value missing.
238
+
239
+ ![A fixture with two test cases, one expanded to show its input values.](media/studio/fixtures-test-cases.png)
240
+
241
+ Other actions:
242
+
243
+ - **Remove from fixture** (the minus icon on a row) removes that test case's values. Studio names
244
+ any suite that uses them.
245
+ - **Duplicate** copies the saved fixture, with all its test cases and values, under a new name.
246
+ - Renaming a fixture in **General** also updates every suite that uses it.
247
+ - **Delete** deletes the fixture. Studio names the suites that use it before you confirm.
248
+
249
+ > [!NOTE]
250
+ > Fixtures created by TRXA-TOOL 0.1 stored one test case per file. Studio shows those files as
251
+ > **(old format)** and opens them read-only. Select **Merge fixture files** to convert them. Studio
252
+ > makes no changes if two old files hold values for the same test case under the same fixture name.
253
+
254
+ ## Build a suite
255
+
256
+ A suite runs test cases in order and passes data between them. For example, a purchase order suite
257
+ can create an order, then confirm, receive and invoice that same order.
258
+
259
+ ### Create a suite
260
+
261
+ 1. Select **Suites**, and then select **New suite** (the plus icon above the list).
262
+ 1. Enter these values, and then select **Create**:
263
+
264
+ | Field | Description |
265
+ |---|---|
266
+ | **File name** | The suite's file name, such as `purchase-order-lifecycle`. |
267
+ | **Suite ID** | The ID that plans use to refer to the suite. |
268
+ | **Name** | A readable name. |
269
+ | **Description** | Optional. |
270
+
271
+ ### Arrange test cases
272
+
273
+ The suite's canvas shows each test case as a box. Arrows show the order: a test case runs only
274
+ after every test case it depends on has passed.
275
+
276
+ 1. In **Add a test case**, select a test case to add it to the canvas.
277
+ 1. To make one test case depend on another, drag from the bottom handle of the first box to the top
278
+ handle of the box that should run after it.
279
+ 1. To remove a dependency, select its arrow, and then press **Delete** or **Backspace**.
280
+ 1. Select **Save**.
281
+
282
+ You can drag the boxes to arrange them. The layout is saved only in your browser. To return to the
283
+ automatic layout, select **Reset layout**.
284
+
285
+ ![A suite canvas with four test cases connected in order.](media/studio/suites-canvas.png)
286
+
287
+ ### Set up a test case in the suite
288
+
289
+ Point to a box and select its **Edit** icon to open its settings. To remove the test case from the
290
+ suite, select the **×** icon or **Remove from suite**.
291
+
292
+ | Setting | Description |
293
+ |---|---|
294
+ | **Legal entity** | Optional. The company to open the test case in. Leave it empty to stay in the current company. |
295
+ | **Depends on** | The test cases that must pass first. Select **×** on one to remove that dependency. |
296
+ | **Run condition** | Optional. In **Only run if**, select a dependency, one of its outputs, a comparator and a value. If the condition isn't met, the test case is *skipped*, which is neither a pass nor a failure. Test cases that depend on a skipped test case are *blocked*. |
297
+ | **Input bindings** | Where each input gets its value. See the following table. |
298
+
299
+ For each input, select a **Source**:
300
+
301
+ | Source | The value comes from |
302
+ |---|---|
303
+ | **Unbound** | Nowhere yet. A required input must be bound before the suite can run. |
304
+ | **Fixture value** | A fixture that holds values for this test case. |
305
+ | **Output of an upstream test case** | An output of a test case that runs earlier in the suite, such as the new purchase order number. |
306
+ | **Literal value** | A value that you type into the suite. Use it for constants, not for business data. |
307
+ | **Autogenerated** | The agent, which invents a value during the run. Use it for values that only need to be unique, such as a new vendor account. |
308
+
309
+ ![The settings panel for a test case in a suite, with its dependency, run condition and input bindings.](media/studio/suites-inspector.png)
310
+
311
+ Select **Done** to close the panel, and then select **Save**. Studio checks the suite when you
312
+ save, for example for test cases that depend on each other in a loop.
313
+
314
+ ## Group suites into a plan
315
+
316
+ A plan groups suites that you run and report on together. The suites in a plan don't share data
317
+ or depend on each other. If two suites need to share data, combine them into one suite.
318
+
319
+ 1. Select **Plans**, and then select **New plan** (the plus icon above the list).
320
+ 1. Enter a **File name**, **Plan ID**, **Name** and, optionally, a **Description**, and then select
321
+ **Create**.
322
+ 1. Select **Add suites**, select the suites to include, and then select **Add**.
323
+ 1. Use the up and down arrows to set the run order, and the minus icon to remove a suite.
324
+ 1. Select **Save**.
325
+
326
+ Under **Rollup status**, select a run in **Run** to see each suite's status and an **Overall**
327
+ result. A suite is *missing* if it hasn't run yet in that plan run, *incomplete* while it's
328
+ running, *failed* if any test case failed or was blocked, and *passed* otherwise. The **Overall**
329
+ result shows the worst status of any suite.
330
+
331
+ ![A plan with two suites in run order, and the rollup status of a completed run.](media/studio/plans-editor.png)
332
+
333
+ ## Run a test with an AI agent
334
+
335
+ Test cases, suites and plans each have a **Run** button. It hands the item to an AI agent, which
336
+ replays it in Dynamics 365 through the MCP connection you set up.
337
+
338
+ Before you begin, complete [Set up TRXA Studio for agent runs](SETUP.md). If an item isn't ready,
339
+ the **Run** button is unavailable and an **Agent not ready** link opens **Settings**.
340
+
341
+ 1. Open the test case, suite or plan, and then select **Run**.
342
+ 1. In the dialog, enter these values:
343
+
344
+ | Field | Description |
345
+ |---|---|
346
+ | **Run name** | How you find the run later. Studio starts with the item's name. Add what makes this run different, such as the update you're testing. |
347
+ | **Agent** | **GitHub Copilot** or **Claude Code**. Only agents that are ready can run. |
348
+ | **Expected environment** | The environment you expect the agent to be connected to, such as `contoso-sandbox`. |
349
+ | **Expected company** | The company you expect, such as `USMF`. |
350
+ | **How far to run** | **Navigation only** opens forms and reads values. Nothing is saved, posted, created or deleted. **Full run** performs every step, including saving and posting. |
351
+
352
+ ![The Run suite with AI agent dialog.](media/studio/run-dialog.png)
353
+
354
+ 1. Select **Start navigation run** or **Start full run**.
355
+
356
+ Before it does anything else, the agent checks that it's connected to the environment and company
357
+ you entered. If they don't match, it stops and reports why. The agent runs unattended and never
358
+ asks questions.
359
+
360
+ > [!WARNING]
361
+ > A full run creates, saves and posts real records in the connected environment. Selecting
362
+ > **Start full run** is your confirmation. Use a test or sandbox environment.
363
+
364
+ While the run is in progress:
365
+
366
+ - Studio opens the run's page in **Test Runs**, which updates as the agent works. To cancel the run,
367
+ select **Stop run**.
368
+ - You can go to other pages. **Run activity**, in the upper-right corner, shows a progress bar for
369
+ the run.
370
+ - When the run ends, a notification shows the result, with a **View run** link.
371
+
372
+ Only one agent run can be in progress at a time. During a run, the agent can't write files outside
373
+ the working folder or run shell commands other than `trxa`. Studio saves each run's transcript in
374
+ the working folder's `agent-runs` folder. It isn't sent anywhere except to the agent you chose.
375
+
376
+ ## Review test runs
377
+
378
+ Select **Test Runs** to see every run: the test cases, suites and plans run from Studio, by an agent
379
+ in a terminal, or with the `trxa` command line.
380
+
381
+ ![The Test Runs table, with a passed and a failed plan run.](media/studio/testruns-list.png)
382
+
383
+ Each run is one row. A plan run includes its suites, and a suite run includes its test cases. Runs
384
+ in progress stay at the top. Use the search box and the **Type**, **Status**, **Started** and **Run
385
+ by** filters to find a run (an **Environment** filter appears once runs record one), and select a column heading to sort by it.
386
+
387
+ Select a run's name to open its page. The page shows the run's status, who ran it and how long it
388
+ took, a progress bar, and a summary of the results.
389
+
390
+ - For a plan run, expand a suite to see its test cases.
391
+ - Expand a test case to see its **Inputs**, captured **Outputs**, the **Assertions** that were
392
+ checked (each with the value the agent observed), and the **Infolog** messages Dynamics 365 showed.
393
+ - Turn on **Show failures only** to hide the test cases that passed.
394
+
395
+ ![A plan run's page, with one suite expanded to show its four test cases.](media/studio/testrun-details.png)
396
+
397
+ For a run started by an agent from Studio, the **Agent activity** tab shows what the agent did,
398
+ the reason if the run failed or was interrupted, and how many actions the run's permissions
399
+ blocked. Select **Full log** to download the complete transcript.
400
+
401
+ To share results:
402
+
403
+ - **Copy run summary** copies the results as formatted text that you can paste into an email, a
404
+ chat or a work item.
405
+ - **Export run** downloads the results as **Markdown (.md)**, or prints them to **PDF**.
406
+ - **Rename** changes the run's name.
407
+
408
+ A run's results are a record of what happened, so you can't edit them. To get new results, run the
409
+ test again. **Delete run** deletes a plan run's record. To delete one suite's results, use
410
+ **Delete** on that suite.
411
+
412
+ ## Troubleshoot
413
+
414
+ | Problem | Solution |
415
+ |---|---|
416
+ | `trxa studio` reports that the port is in use | Run `trxa studio stop`, and then start Studio again. If the port is still in use, another program is using port 4321. To use a different port, set the `STUDIO_SERVER_PORT` environment variable before you start Studio. |
417
+ | A record you added outside Studio doesn't appear | Lists load when you open a page. Refresh the page. **Test Runs** refreshes on its own every few seconds. |
418
+ | A list shows **Invalid** for a file | The file doesn't match the expected format, and Studio can't open it. The message describes the problem. Fix the file in a text editor, or check it with the matching `trxa validate-...` command. |
419
+ | The **Run** button is unavailable | An item in **Settings** > **Agent runs** isn't ready. See [Troubleshoot](SETUP.md#troubleshoot) in the setup article. |
420
+ | A run fails right away with a sign-in or connection message | Sign in to the agent again (`copilot login`, or run `claude`), and check the MCP connection. See [Step 5](SETUP.md#step-5-connect-the-agent-to-dynamics-365). |
421
+ | A run shows as failed with the reason **interrupted** | Studio was closed or restarted while the run was in progress. Run the test again. |
422
+
423
+ ## Related content
424
+
425
+ - [Set up TRXA Studio for agent runs](SETUP.md)
426
+ - [TRXA-TOOL manual](MANUAL.md): file formats and the `trxa` command line
427
+ - [Use Model Context Protocol for finance and operations apps](https://learn.microsoft.com/dynamics365/fin-ops-core/dev-itpro/copilot/copilot-mcp)
@@ -0,0 +1,58 @@
1
+ import { type ScrubbedValue } from "./scrub.js";
2
+ import { type Confidence } from "./mapping.js";
3
+ export interface StepSummary {
4
+ sequence: number;
5
+ kind: string;
6
+ formName: string | null;
7
+ /** Scrubbed -- literal values are `<<REDACTED:...>>` tokens, never real data. */
8
+ description: string;
9
+ hintTool: string | null;
10
+ hintConfidence: Confidence | null;
11
+ hintNote: string | null;
12
+ }
13
+ export interface RecordingSummary {
14
+ file: string;
15
+ name: string;
16
+ canonicalId: string;
17
+ stepCount: number;
18
+ steps: StepSummary[];
19
+ requiredPrivileges: string[];
20
+ }
21
+ export interface ParseError {
22
+ file: string;
23
+ error: string;
24
+ }
25
+ export interface GapBucket {
26
+ /** e.g. "CommandUserAction:ChangeSelectedIndexInCache:Grid" or "Unknown:ValidationUserAction" */
27
+ key: string;
28
+ count: number;
29
+ sampleRecordings: string[];
30
+ }
31
+ export interface LibraryReport {
32
+ generatedAt: string;
33
+ recordingsAnalyzed: number;
34
+ parseErrors: ParseError[];
35
+ recordings: RecordingSummary[];
36
+ aggregate: {
37
+ nodeKindCounts: Record<string, number>;
38
+ hintConfidenceCounts: {
39
+ high: number;
40
+ medium: number;
41
+ low: number;
42
+ unmapped: number;
43
+ };
44
+ unknownXsiTypes: GapBucket[];
45
+ lowConfidenceGaps: GapBucket[];
46
+ distinctSecurityPrivileges: string[];
47
+ };
48
+ }
49
+ export interface LibraryAnalysis {
50
+ report: LibraryReport;
51
+ scrubbedValues: ScrubbedValue[];
52
+ }
53
+ /**
54
+ * Locally parses and scrubs every `.axtr` file in `recordingsDir`, returning a structural-only
55
+ * report (safe to hand to an LLM) plus the full list of stripped literal values (never safe to
56
+ * hand to an LLM -- keep local, see the `analyze-library` CLI command's CSV output).
57
+ */
58
+ export declare function analyzeLibrary(recordingsDir: string): Promise<LibraryAnalysis>;
@@ -0,0 +1,122 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { extractAxtr } from "./extract.js";
4
+ import { parseRecordingXml } from "./parse.js";
5
+ import { scrubRecording } from "./scrub.js";
6
+ import { normalizeRecording } from "./normalize.js";
7
+ import { mapStep } from "./mapping.js";
8
+ import { parseBpmPackage, distinctPrivileges } from "./bpm.js";
9
+ /** Cosmetic cap on how many example recording filenames a gap bucket lists in the report --
10
+ * `count` above already tracks the true total, this just keeps `sampleRecordings` from growing
11
+ * unboundedly for a gap that recurs across most of a large library. No significance to the value
12
+ * 5 beyond "a handful is enough to spot-check"; raise it freely if a reviewer wants more examples. */
13
+ const MAX_SAMPLE_RECORDINGS = 5;
14
+ function bumpBucket(map, key, recordingFile) {
15
+ const existing = map.get(key);
16
+ if (existing) {
17
+ existing.count += 1;
18
+ if (!existing.sampleRecordings.includes(recordingFile) && existing.sampleRecordings.length < MAX_SAMPLE_RECORDINGS) {
19
+ existing.sampleRecordings.push(recordingFile);
20
+ }
21
+ }
22
+ else {
23
+ map.set(key, { key, count: 1, sampleRecordings: [recordingFile] });
24
+ }
25
+ }
26
+ function gapKeyFor(step) {
27
+ const node = step.node;
28
+ if (node.kind === "Unknown")
29
+ return `Unknown:${node.xsiType}`;
30
+ if (node.kind === "CommandUserAction")
31
+ return `CommandUserAction:${node.CommandName}:${node.ControlType}`;
32
+ if (node.kind === "PropertyUserAction")
33
+ return `PropertyUserAction:${node.UserActionType}`;
34
+ return `${node.kind}`;
35
+ }
36
+ async function analyzeOneRecording(axtrPath, fileName) {
37
+ const bytes = await readFile(axtrPath);
38
+ const extracted = await extractAxtr(bytes);
39
+ const recording = parseRecordingXml(extracted.recordingXml);
40
+ const scrubbed = scrubRecording(recording, fileName);
41
+ const script = normalizeRecording(scrubbed.recording);
42
+ const bpm = await parseBpmPackage(extracted.bpmPackageBytes);
43
+ const summary = {
44
+ file: fileName,
45
+ name: script.name,
46
+ canonicalId: script.canonicalId,
47
+ stepCount: script.steps.length,
48
+ steps: script.steps.map((step) => {
49
+ const hint = mapStep(step);
50
+ return {
51
+ sequence: step.sequence,
52
+ kind: step.node.kind,
53
+ formName: step.formName,
54
+ description: step.description,
55
+ hintTool: hint?.tool ?? null,
56
+ hintConfidence: hint?.confidence ?? null,
57
+ hintNote: hint?.note ?? null,
58
+ };
59
+ }),
60
+ requiredPrivileges: bpm ? distinctPrivileges(bpm) : [],
61
+ };
62
+ return { summary, scrubbedValues: scrubbed.values, steps: script.steps };
63
+ }
64
+ /**
65
+ * Locally parses and scrubs every `.axtr` file in `recordingsDir`, returning a structural-only
66
+ * report (safe to hand to an LLM) plus the full list of stripped literal values (never safe to
67
+ * hand to an LLM -- keep local, see the `analyze-library` CLI command's CSV output).
68
+ */
69
+ export async function analyzeLibrary(recordingsDir) {
70
+ const entries = (await readdir(recordingsDir)).filter((f) => f.toLowerCase().endsWith(".axtr")).sort();
71
+ const recordings = [];
72
+ const parseErrors = [];
73
+ const scrubbedValues = [];
74
+ const nodeKindCounts = {};
75
+ const hintConfidenceCounts = { high: 0, medium: 0, low: 0, unmapped: 0 };
76
+ const unknownXsiTypes = new Map();
77
+ const lowConfidenceGaps = new Map();
78
+ const privilegeSet = new Set();
79
+ for (const fileName of entries) {
80
+ const axtrPath = path.join(recordingsDir, fileName);
81
+ try {
82
+ const { summary, scrubbedValues: values, steps } = await analyzeOneRecording(axtrPath, fileName);
83
+ recordings.push(summary);
84
+ scrubbedValues.push(...values);
85
+ for (const p of summary.requiredPrivileges)
86
+ privilegeSet.add(p);
87
+ for (const step of steps) {
88
+ nodeKindCounts[step.node.kind] = (nodeKindCounts[step.node.kind] ?? 0) + 1;
89
+ const hint = mapStep(step);
90
+ if (!hint) {
91
+ hintConfidenceCounts.unmapped += 1;
92
+ bumpBucket(lowConfidenceGaps, gapKeyFor(step), fileName);
93
+ }
94
+ else {
95
+ hintConfidenceCounts[hint.confidence] += 1;
96
+ if (hint.confidence === "low")
97
+ bumpBucket(lowConfidenceGaps, gapKeyFor(step), fileName);
98
+ }
99
+ if (step.node.kind === "Unknown") {
100
+ bumpBucket(unknownXsiTypes, step.node.xsiType, fileName);
101
+ }
102
+ }
103
+ }
104
+ catch (err) {
105
+ parseErrors.push({ file: fileName, error: err instanceof Error ? err.message : String(err) });
106
+ }
107
+ }
108
+ const report = {
109
+ generatedAt: new Date().toISOString(),
110
+ recordingsAnalyzed: recordings.length,
111
+ parseErrors,
112
+ recordings,
113
+ aggregate: {
114
+ nodeKindCounts,
115
+ hintConfidenceCounts,
116
+ unknownXsiTypes: [...unknownXsiTypes.values()].sort((a, b) => b.count - a.count),
117
+ lowConfidenceGaps: [...lowConfidenceGaps.values()].sort((a, b) => b.count - a.count),
118
+ distinctSecurityPrivileges: [...privilegeSet].sort(),
119
+ },
120
+ };
121
+ return { report, scrubbedValues };
122
+ }
package/dist/bpm.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Parses the nested BPMPackage.ax7bpm zip (itself a ZIP, confirmed via inspection) embedded in
3
+ * an .axtr package. Its `Recording/ProcessStep.xml` lists one <Task> per *form* visited (not
4
+ * per user action) with the full security role/duty/privilege matrix required to reach it, plus
5
+ * a Connections graph of form-to-form flow. `Recording/TaskStep.xml` was inspected too and found
6
+ * to duplicate the same generic ordinal descriptions already present in Recording.xml /
7
+ * recording_resource.xml -- it carries no additional information, so it is intentionally not parsed.
8
+ *
9
+ * No authored "expected outcome" / assertion data exists anywhere in this package.
10
+ */
11
+ export interface BpmSecurityRequirement {
12
+ roleName: string;
13
+ roleDisplayName: string;
14
+ dutyName: string;
15
+ dutyDisplayName: string;
16
+ privilegeName: string;
17
+ privilegeDisplayName: string;
18
+ accessLevel: string;
19
+ isValidRole: boolean;
20
+ }
21
+ export interface BpmTask {
22
+ id: string;
23
+ aotName: string;
24
+ objectType: string;
25
+ configurationKey: string;
26
+ layer: string;
27
+ model: string;
28
+ sequence: number;
29
+ /** Every role/duty/privilege combination known to grant access to this form. */
30
+ security: BpmSecurityRequirement[];
31
+ /** Task Ids this task flows into. */
32
+ connectsTo: string[];
33
+ }
34
+ export interface BpmPackage {
35
+ tasks: BpmTask[];
36
+ }
37
+ /** Parses the nested BPMPackage.ax7bpm bytes extracted from a .axtr file. Returns null if absent. */
38
+ export declare function parseBpmPackage(bytes: Buffer | null): Promise<BpmPackage | null>;
39
+ /** Every distinct privilege that appears across all tasks -- a starting point for a security preflight check. */
40
+ export declare function distinctPrivileges(pkg: BpmPackage): string[];