|
| 1 | +import { SolutionTestsTemplate } from '@/templates/SolutionTestsTemplate'; |
| 2 | +import { PreviewFullScreen } from '@/app/_components/preview-full-screen'; |
| 3 | + |
| 4 | +# Solution tests |
| 5 | + |
| 6 | +A full-page composition for managing UiPath Solution Tests — test cases, |
| 7 | +batch runs, a KPI score trend, run-result baselines, and adopt / update / |
| 8 | +remove actions. The view is domain-neutral: it reads the generic `UiPathST*` |
| 9 | +Solution Test entity collections from your vs-core solution and is |
| 10 | +parameterized for the subject entity (loans, claims, invoices, …) through |
| 11 | +`SolutionTestsConfig`. Intended to render inside an |
| 12 | +[`ApolloShell`](/patterns/shell). |
| 13 | + |
| 14 | +<PreviewFullScreen title="Solution tests preview"> |
| 15 | + <SolutionTestsTemplate /> |
| 16 | +</PreviewFullScreen> |
| 17 | + |
| 18 | +## Composition |
| 19 | + |
| 20 | +In production, render the smart `SolutionTests` container inside a |
| 21 | +`SolutionTestsProvider`; it reads the `UiPathST*` collections from your vs-core |
| 22 | +solution through the collection-backed hooks. The preview above is instead a |
| 23 | +presentational demo — it renders the dumb `SolutionTestsView` (and the dumb |
| 24 | +expanded views) with in-memory mock data and no-op handlers, so it never |
| 25 | +touches the hooks or vs-core. From top to bottom the view stacks: |
| 26 | + |
| 27 | +- **`PageHeader`** — title and the "Run all" action. |
| 28 | +- **KPI bar** — cards plus a `recharts` score-trend chart across recent runs. |
| 29 | +- **Tabs** — `Test cases` (list, enable/disable, run, delete) and |
| 30 | + `Test runs` (batch runs backed by live queries, per-run results, force-stop). |
| 31 | +- **Dialogs** — run details, JSON viewer, and delete confirmation, plus the |
| 32 | + expected-vs-actual baseline adopt / update / remove flows. |
| 33 | + |
| 34 | +Source: [`templates/SolutionTestsTemplate.tsx`](https://github.com/UiPath/apollo-ui/blob/main/apps/apollo-vertex/templates/SolutionTestsTemplate.tsx) |
| 35 | + |
| 36 | +## Consumer interface |
| 37 | + |
| 38 | +The smart container has four inputs, all on `SolutionTestsProvider` (three |
| 39 | +required, plus an optional `config`). Reads are implicit (the `UiPathST*` |
| 40 | +collections on your vs-core solution); writes and presentation are explicit |
| 41 | +props: |
| 42 | + |
| 43 | +```tsx |
| 44 | +interface SolutionTestsProviderProps { |
| 45 | + /** |
| 46 | + * Required. Base URL that write-action slugs are appended to (no trailing |
| 47 | + * slash): `{base}/{org}/{tenant}/orchestrator_/t/{folderKey}`. |
| 48 | + */ |
| 49 | + triggerBaseUrl: string; |
| 50 | + /** Required. Resolves the caller's current bearer token (refreshed near expiry). */ |
| 51 | + getToken: () => Promise<string | null> | string | null; |
| 52 | + /** |
| 53 | + * Required. Resolves a Solution Test entity name to its DataFabric id (GUID), |
| 54 | + * for attachment reads. e.g. `(name) => entities[name]?.id` from vss codegen. |
| 55 | + */ |
| 56 | + getEntityId: (name: string) => string | undefined; |
| 57 | + /** Optional per-vertical presentation config. */ |
| 58 | + config?: SolutionTestsConfig; |
| 59 | + children: ReactNode; |
| 60 | +} |
| 61 | + |
| 62 | +interface SolutionTestsConfig { |
| 63 | + /** Columns inserted between Test Name and Version. Usually the only customization. */ |
| 64 | + subjectColumns?: ColumnDef<SolutionTest>[]; |
| 65 | + /** Turns the test name into a link to its subject. */ |
| 66 | + getSubjectHref?: (test: SolutionTest) => string | undefined; |
| 67 | + /** Subject noun for labels, e.g. `{ singular: "Loan", plural: "Loans" }`. */ |
| 68 | + subjectNoun?: { singular: string; plural: string }; |
| 69 | + /** Score at/above which a result passes (drives pass color + KPI trend line). Defaults to 0.9. */ |
| 70 | + passThreshold?: number; |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +So a consumer is responsible for exactly three things: |
| 75 | + |
| 76 | +1. **Reads** — ensure the name-keyed `UiPathST*` collections exist on your |
| 77 | + vs-core solution (`solution.api.collections.solutionTests`); the list reads |
| 78 | + query them reactively with no wiring. Attachment reads (expected/actual |
| 79 | + output, evaluator results) additionally need `getEntityId` to resolve each |
| 80 | + entity's DataFabric id, since vs-core doesn't expose entity ids on the |
| 81 | + solution. Pass `(name) => entities[name]?.id` from your vss codegen. |
| 82 | +2. **Writes** — pass `triggerBaseUrl` + `getToken`. The provider builds the |
| 83 | + whole write surface (run, delete, force-stop, adopt / update / remove |
| 84 | + baseline) from them. Toggle-active isn't a trigger. It goes through the |
| 85 | + collection's optimistic update and needs no extra wiring. |
| 86 | +3. **Presentation** — pass `config.subjectColumns` (and optionally |
| 87 | + `getSubjectHref` / `subjectNoun`) to flavor the table for your vertical. |
| 88 | + |
| 89 | +Everything else (evaluator labels, status labels, the poll interval) is |
| 90 | +hard-coded in `constants.ts`. Edit there to retarget a deployment. The pass |
| 91 | +threshold defaults to `0.9` in `constants.ts` but is overridable per vertical |
| 92 | +via `config.passThreshold`. Consumers who want to supply their own data plumbing can instead |
| 93 | +render the dumb `SolutionTestsView`, which takes all data and callbacks via |
| 94 | +props (this is what the preview above does). |
| 95 | + |
| 96 | +## Installation |
| 97 | + |
| 98 | +```bash |
| 99 | +npx shadcn@latest add @uipath/solution-tests |
| 100 | +``` |
| 101 | + |
| 102 | +> **Requires [`@uipath/vs-core`](https://www.npmjs.com/package/@uipath/vs-core) |
| 103 | +> `^2.0.4`.** The container reads the Solution Test collections from the |
| 104 | +> dedicated, name-keyed `solution.api.collections.solutionTests` namespace |
| 105 | +> introduced in vs-core 2.x. Install vs-core per its README — it brings its own |
| 106 | +> peers (`@tanstack/db`, `@tanstack/query-db-collection`, |
| 107 | +> `@tanstack/react-query`, `@uipath/uipath-typescript`). The component itself |
| 108 | +> pulls `@tanstack/react-db` (for `useLiveQuery`), `@tanstack/react-table`, |
| 109 | +> `@tanstack/react-router`, `recharts`, `sonner`, and `react-i18next`. |
| 110 | +
|
| 111 | +The Solution Test entities (`UiPathST*`) are generic across verticals, so the |
| 112 | +only per-vertical customization most consumers need is their vertical's |
| 113 | +**`subjectColumns`**. The smart `SolutionTests` container reads its data from |
| 114 | +your vs-core Solution Test collections — the name-keyed |
| 115 | +`solution.api.collections.solutionTests` set (`UiPathSTTests`, |
| 116 | +`UiPathSTBatchRuns`, `UiPathSTRuns`, `UiPathSTJobs`, `UiPathSTRunResults`) — via |
| 117 | +the collection-backed hooks, so your only data responsibility is to make sure |
| 118 | +those collections exist on your solution. Wrap `<SolutionTests />` in a |
| 119 | +`<SolutionTestsProvider>` with your presentation `config`. |
| 120 | + |
| 121 | +Reads are live the moment the collections exist. **Writes** (run, delete, |
| 122 | +force-stop, adopt/update/remove baseline) POST an action slug to your Solution |
| 123 | +Test trigger base URL — pass `triggerBaseUrl` and `getToken` to the provider and |
| 124 | +it builds the whole write surface for you, so you don't reimplement the |
| 125 | +trigger/auth plumbing. (Toggle-active and attachment reads aren't triggers — they |
| 126 | +go through the vs-core collections/sdk.) |
| 127 | + |
| 128 | +```tsx |
| 129 | +"use client"; |
| 130 | + |
| 131 | +import { |
| 132 | + SolutionTests, |
| 133 | + SolutionTestsProvider, |
| 134 | + type SolutionTestsConfig, |
| 135 | +} from "@/components/ui/solution-tests"; |
| 136 | +import { entities } from "@/vss.gen"; // vss codegen: entity name -> { id } |
| 137 | + |
| 138 | +const config: SolutionTestsConfig = { |
| 139 | + subjectColumns, // the only per-vertical customization |
| 140 | + subjectNoun: { singular: "Loan", plural: "Loans" }, |
| 141 | +}; |
| 142 | + |
| 143 | +export default function SolutionTestsPage() { |
| 144 | + // List reads come from the vs-core solution's `collections.solutionTests` in |
| 145 | + // context (provided higher up by your shell). No read wiring required. |
| 146 | + return ( |
| 147 | + <SolutionTestsProvider |
| 148 | + config={config} |
| 149 | + // `{base}/{org}/{tenant}/orchestrator_/t/{folderKey}` (no trailing slash) |
| 150 | + triggerBaseUrl={`/${env.UIPATH_ORG_NAME}/${env.UIPATH_TENANT_NAME}/orchestrator_/t/${env.UIPATH_SOLUTION_FOLDER_KEY}`} |
| 151 | + getToken={() => tokenService.getCurrentToken()} |
| 152 | + getEntityId={(name) => entities[name]?.id} |
| 153 | + > |
| 154 | + <SolutionTests /> |
| 155 | + </SolutionTestsProvider> |
| 156 | + ); |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +The exported dumb `SolutionTestsView` takes all data + callbacks via props if |
| 161 | +you prefer to supply your own data plumbing or render the view against a mock |
| 162 | +(as the preview does). |
| 163 | + |
| 164 | +## Customizing |
| 165 | + |
| 166 | +- **Subject columns** — `config.subjectColumns` injects your vertical's columns |
| 167 | + between Test Name and Version; `config.getSubjectHref` turns the test name |
| 168 | + into a link to the subject. This is normally the only required config. |
| 169 | +- **Data source** — the smart `SolutionTests` container reads the `UiPathST*` |
| 170 | + collections from `solution.api.collections.solutionTests` (vs-core 2.x) via |
| 171 | + the collection-backed hooks in `hooks.ts`; just ensure those collections exist |
| 172 | + on your solution. Reads are reactive (live queries); writes POST to the |
| 173 | + provider's `triggerBaseUrl` (see Installation). |
| 174 | +- **Subject noun** — `config.subjectNoun` (a `singular` / `plural` pair) drives |
| 175 | + the subject-flavored KPI card (e.g. "Loans passing"), the runs "passed" |
| 176 | + column, and the run-results dialog title; omit it for generic "Tests" |
| 177 | + wording. |
| 178 | +- **Fixed setup** — evaluator labels, status labels, and the poll interval are |
| 179 | + hard-coded in `constants.ts`; edit there to retarget a deployment. The pass |
| 180 | + threshold defaults to `0.9` but is overridable via `config.passThreshold`. |
| 181 | +- **i18n** — framework strings use `react-i18next`; wrap your app in |
| 182 | + `ApolloShell` (which initializes i18n via `LocaleProvider`) or provide your |
| 183 | + own `I18nextProvider`. |
0 commit comments