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.
- package/.claude/skills/replay-trxa-testplan/SKILL.md +627 -0
- package/LICENSE +120 -0
- package/MANUAL.md +556 -0
- package/README.md +91 -3
- package/SETUP.md +401 -0
- package/STUDIO.md +427 -0
- package/dist/analyze.d.ts +58 -0
- package/dist/analyze.js +122 -0
- package/dist/bpm.d.ts +40 -0
- package/dist/bpm.js +60 -0
- package/dist/build-test-script.d.ts +24 -0
- package/dist/build-test-script.js +40 -0
- package/dist/csv.d.ts +1 -0
- package/dist/csv.js +11 -0
- package/dist/extract.d.ts +14 -0
- package/dist/extract.js +34 -0
- package/dist/fixture-schema.d.ts +52 -0
- package/dist/fixture-schema.js +112 -0
- package/dist/fixture-store.d.ts +37 -0
- package/dist/fixture-store.js +75 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +850 -0
- package/dist/library-archive.d.ts +37 -0
- package/dist/library-archive.js +106 -0
- package/dist/mapping.d.ts +25 -0
- package/dist/mapping.js +393 -0
- package/dist/mcp-bridge.d.ts +52 -0
- package/dist/mcp-bridge.js +290 -0
- package/dist/normalize.d.ts +21 -0
- package/dist/normalize.js +43 -0
- package/dist/parse.d.ts +3 -0
- package/dist/parse.js +233 -0
- package/dist/plan-runner.d.ts +27 -0
- package/dist/plan-runner.js +36 -0
- package/dist/plan-schema.d.ts +27 -0
- package/dist/plan-schema.js +45 -0
- package/dist/planrun-schema.d.ts +53 -0
- package/dist/planrun-schema.js +111 -0
- package/dist/planrun.d.ts +20 -0
- package/dist/planrun.js +46 -0
- package/dist/report-schema.d.ts +92 -0
- package/dist/report-schema.js +115 -0
- package/dist/report.d.ts +45 -0
- package/dist/report.js +77 -0
- package/dist/schema.d.ts +96 -0
- package/dist/schema.js +10 -0
- package/dist/scrub.d.ts +44 -0
- package/dist/scrub.js +166 -0
- package/dist/suggest-inputs.d.ts +19 -0
- package/dist/suggest-inputs.js +92 -0
- package/dist/suite-runner.d.ts +59 -0
- package/dist/suite-runner.js +204 -0
- package/dist/suite-schema.d.ts +59 -0
- package/dist/suite-schema.js +95 -0
- package/dist/testcase-schema.d.ts +102 -0
- package/dist/testcase-schema.js +132 -0
- package/dist/token-usage.d.ts +38 -0
- package/dist/token-usage.js +95 -0
- package/media/studio/fixtures-test-cases.png +0 -0
- package/media/studio/plans-editor.png +0 -0
- package/media/studio/recordings-create-test-case.png +0 -0
- package/media/studio/run-dialog.png +0 -0
- package/media/studio/settings-agent-runs.png +0 -0
- package/media/studio/settings-working-folder.png +0 -0
- package/media/studio/studio-recordings.png +0 -0
- package/media/studio/suites-canvas.png +0 -0
- package/media/studio/suites-inspector.png +0 -0
- package/media/studio/testcases-expectations.png +0 -0
- package/media/studio/testcases-outputs.png +0 -0
- package/media/studio/testrun-details.png +0 -0
- package/media/studio/testruns-list.png +0 -0
- package/package.json +62 -6
- package/studio/client-dist/assets/CreatePanel-C5bb5OIs.js +6 -0
- package/studio/client-dist/assets/FactTile-G1pURLys.js +1 -0
- package/studio/client-dist/assets/FieldLabel-DPmIEQ08.js +1 -0
- package/studio/client-dist/assets/FixtureNamePicker-137AAgwZ.js +1 -0
- package/studio/client-dist/assets/Fixtures-D0t-soph.js +1 -0
- package/studio/client-dist/assets/GeneralSection-CWkTeSX2.js +1 -0
- package/studio/client-dist/assets/ListPaneHeader-DXKqX36N.js +1 -0
- package/studio/client-dist/assets/Plans-C70evkGc.js +1 -0
- package/studio/client-dist/assets/Recordings-BT0dyVLs.js +1 -0
- package/studio/client-dist/assets/Settings-k-J3u5lH.js +1 -0
- package/studio/client-dist/assets/Suites-DLioOiRN.css +1 -0
- package/studio/client-dist/assets/Suites-DQhhMxjT.js +7 -0
- package/studio/client-dist/assets/TestCases-BdiopVGI.js +1 -0
- package/studio/client-dist/assets/TestRuns-CylAe-3t.js +9 -0
- package/studio/client-dist/assets/api-DJZ703_F.js +24 -0
- package/studio/client-dist/assets/bucket-18-viuwGcaj.js +1 -0
- package/studio/client-dist/assets/fixtureName-CTZT_jKu.js +1 -0
- package/studio/client-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-300-normal-LoaTnByX.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-300-normal-RhT0Ayi3.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-italic-Bf1n82Tb.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-italic-D9J6q4_O.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-300-normal-Bbd5v1xK.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-300-normal-DqmgDkIN.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-italic-BELOEI8R.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-italic-CfSXsIbO.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-300-normal-J-CBqZoJ.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-300-normal-STc8XiDr.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-400-italic-BEZwmy7D.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-400-italic-CTYLy0lX.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-300-normal-C9Of02-5.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-300-normal-b4i7YzLG.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-400-italic-CZTNEAuW.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-400-italic-CsGl1sm0.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-300-normal-B8Zgwjh3.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-300-normal-BLFuR0P1.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-italic-Du9zzoL2.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-italic-V_9i1P_m.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-300-normal-CkYRZD2a.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-300-normal-SDfX6ktj.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-italic-78jqtfZH.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-italic-BbRnwouv.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- package/studio/client-dist/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- package/studio/client-dist/assets/index-cxBc2iRv.js +10 -0
- package/studio/client-dist/assets/index-wjWuVHXN.css +1 -0
- package/studio/client-dist/assets/status-DUGxC7G7.js +1 -0
- package/studio/client-dist/assets/testCaseTree-Cl1-bc5O.js +1 -0
- package/studio/client-dist/axtr-icon.svg +2 -0
- package/studio/client-dist/favicon.svg +2 -0
- package/studio/client-dist/index.html +15 -0
- 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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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>;
|
package/dist/analyze.js
ADDED
|
@@ -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[];
|