You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit da5d401
Browse filesBrowse the repository at this point in the historyBrowse files
`os test` reports the suite and scenario names an author writes, and selects scenarios with `--tags` (#20289)
8
+
9
+
Clause-②: no
10
+
11
+
A Quality Protocol suite's `name`, each scenario's `name` and `description`, and scenario `tags` were parsed at load and then read by nothing: the report headed each suite with its file's basename, printed every scenario by its `id`, and `os test --tags critical` failed with `Nonexistent flag: --tags`.
12
+
13
+
-**Names in the report.** The suite heading is now the suite's `name` followed by its file — `📄 Running suite: Accounts smoke (accounts.test.json)` — and each scenario line is its `name` with the `id` in brackets — `✅ Scenario: An account can be created [acct-create] (12ms)` (the id alone when the two are equal). A failed scenario's `description` is printed under its line, before the error. A suite whose file fails to load is still headed by the file alone, since no name was parsed.
14
+
-**`--tags TAG[,TAG...]`** runs only the scenarios carrying AT LEAST ONE of the listed tags (any-of, exact, case-sensitive) — the comma-list reading of Odoo's `--test-tags` and the everyday use of Playwright's `--grep @a|@b`. With the flag, an untagged scenario is left out. Left-out scenarios are **deselected**: not run, counted on the summary (`--tags smoke selected 1 of 4 scenarios; 3 deselected (not run, not counted as passed).`), never counted as passed. A requested tag that no loaded scenario carries is named on the summary. An empty entry (`--tags smoke,`) is refused before anything runs. Without the flag nothing changes: every scenario runs.
15
+
-**Exit status.** A selection that matches no scenario takes the posture an empty pattern already has: exit `0` with `No scenario matched --tags …`, and exit `1` under `--fail-on-empty`, whose description now covers both cases. The `Found N test suites.` line and the `SUCCESS: All N scenarios passed.` / `FAILED: …` summary lines keep their spelling.
16
+
-**`@objectstack/core`:**`QA.TestResult` gains `scenarioName` and `description` on every result, and `suiteName` on every result `runSuite` produces (absent only from a lone `runScenario` call, which has no suite).
17
+
-**`@objectstack/spec`:**`TestScenario.requires` (`params`, `plugins`) is still checked by nothing — its describe() now says **NOT CHECKED** instead of reading as a guard, so a scenario that declares a plugin the target lacks still runs, and the unmet requirement surfaces only as whatever failure it causes, if any. The liveness ledger (`liveness/qa.json`) moves the four keys above to `live`, citing their readers.
Expression refusals now carry a stable `code` and typed `params` beside their English `message`, so a localized author surface can render its own words: `validateExpression` (every entry in `errors[]` and `warnings[]`), `collectCelRootIdentifiers` (its `ok: false` arm), `predicateSlotRefusal` and `structuralConditionRefusal` (#20291)
7
+
8
+
Clause-②: yes
9
+
10
+
Until now the only thing a refusal said was an English sentence, and a designer running in another locale could only show it verbatim, beside its own translated headings. Each refusal now also names its code — one of a closed, kebab-case set — and the values its sentence interpolates, so a consumer keys a catalogue row to the code and fills it from the params. The `message` is the same sentence, byte for byte; nothing is removed or renamed, so no existing reader changes.
11
+
12
+
-`@objectstack/formula` exports `EXPRESSION_REFUSAL_CODES` (the closed set as a frozen list) and the types `ExpressionRefusalCode`, `ExpressionRefusalParams` (code → params), `ExpressionRefusal`, `ExprValidationCode`, `CelRootsRefusalCode`, `CelFieldRole`, `ExpressionSourceKind` and `CelRootIdentifiersResult`. `ExprValidationError` gains `code` and `params`.
13
+
-`@objectstack/spec/automation` exports `FLOW_SLOT_REFUSAL_CODES` (the closed set of both flow-slot refusal producers, as a frozen list) and the types `FlowSlotRefusalCode`, `FlowSlotRefusalParams` (code → params), `PredicateSlotRefusalCode`, `PredicateSlotRefusal`, `PredicateSlotValueKind`, `StructuralConditionRefusalCode`, `StructuralConditionRefusal` and `StructuralConditionValueKind`. `predicateSlotRefusal` returns `PredicateSlotRefusal` and `structuralConditionRefusal` returns `StructuralConditionRefusal`; each is its previous `{ message, source }` plus `code` and `params`.
14
+
- Narrowing on `code` narrows `params`. A code never changes once published: a reworded message keeps its code, and a new refusal gets a new one.
15
+
- A `detail` param is the CEL or template engine's own diagnostic, in English, passed through as the message carries it.
16
+
- These are authoring diagnostics returned as values, not ADR-0112 request error codes, which is why they are kebab-case.
17
+
- The `validate_expression` MCP tool forwards `validateExpression`'s `errors` and `warnings` as they are, so each entry in its answer now also carries `code` and `params`.
Copy file name to clipboardExpand all lines: content/docs/references/qa/testing.mdx
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -115,7 +115,7 @@ A complete test scenario with setup, execution steps, and teardown
115
115
|**setup**|`{ name: string; description?: string; action: object; assertions?: object[]; … }[]`| optional | Steps to run before main test (preconditions) |
116
116
|**steps**|`{ name: string; description?: string; action: object; assertions?: object[]; … }[]`| ✅ | Main test sequence to execute |
117
117
|**teardown**|`{ name: string; description?: string; action: object; assertions?: object[]; … }[]`| optional | Steps to cleanup after test execution |
118
-
|**requires**|`{ params?: string[]; plugins?: string[] }`| optional | Environment requirements for this scenario |
118
+
|**requires**|`{ params?: string[]; plugins?: string[] }`| optional | Environment requirements for this scenario. NOT CHECKED by `os test` or the core TestRunner: the scenario runs whether or not they hold, and an unmet requirement surfaces only as the failure it causes|
119
119
120
120
### Nested Shape: `TestScenario.setup[number]`
121
121
@@ -157,8 +157,8 @@ A single step in a test scenario, consisting of an action and optional assertion
157
157
158
158
| Property | Type | Required | Description |
159
159
| :--- | :--- | :--- | :--- |
160
-
|**params**|`string[]`| optional |Required environment variables or parameters |
161
-
|**plugins**|`string[]`| optional |Required plugins that must be loaded |
160
+
|**params**|`string[]`| optional |Environment variables or parameters the scenario needs. Declared only: nothing checks them before the scenario runs|
161
+
|**plugins**|`string[]`| optional |Plugins the scenario needs loaded on the target. Declared only: nothing checks them before the scenario runs|
162
162
163
163
164
164
---
@@ -223,7 +223,7 @@ A complete test scenario with setup, execution steps, and teardown
223
223
|**setup**|`{ name: string; description?: string; action: object; assertions?: object[]; … }[]`| optional | Steps to run before main test (preconditions) |
224
224
|**steps**|`{ name: string; description?: string; action: object; assertions?: object[]; … }[]`| ✅ | Main test sequence to execute |
225
225
|**teardown**|`{ name: string; description?: string; action: object; assertions?: object[]; … }[]`| optional | Steps to cleanup after test execution |
226
-
|**requires**|`{ params?: string[]; plugins?: string[] }`| optional | Environment requirements for this scenario |
226
+
|**requires**|`{ params?: string[]; plugins?: string[] }`| optional | Environment requirements for this scenario. NOT CHECKED by `os test` or the core TestRunner: the scenario runs whether or not they hold, and an unmet requirement surfaces only as the failure it causes|
0 commit comments