@qontinui/shared-types 0.2.1 → 0.2.3
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/dist/TaskRunFindingActionType.d-B4VmmbB6.d.cts +62 -0
- package/dist/TaskRunFindingActionType.d-B4VmmbB6.d.ts +62 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/task-run.d.cts +3 -62
- package/dist/task-run.d.ts +3 -62
- package/dist/tauri-events.cjs +19 -0
- package/dist/tauri-events.cjs.map +1 -0
- package/dist/tauri-events.d.cts +1160 -0
- package/dist/tauri-events.d.ts +1160 -0
- package/dist/tauri-events.js +1 -0
- package/dist/tauri-events.js.map +1 -0
- package/dist/ui-bridge-ir.d.cts +59 -1
- package/dist/ui-bridge-ir.d.ts +59 -1
- package/package.json +7 -2
- package/src/generated/AppEvent.d.ts +587 -586
- package/src/generated/CanvasPanel.d.ts +35 -0
- package/src/generated/DevSeedFindingPayload.d.ts +30 -0
- package/src/generated/FlowEvent.d.ts +86 -66
- package/src/generated/RecommendationReviewDecisionPayload.d.ts +24 -0
- package/src/generated/RunnerConnectedConnection.d.ts +34 -0
- package/src/generated/RunnerFinding.d.ts +48 -0
- package/src/generated/RunnerFindingCodeContext.d.ts +20 -0
- package/src/generated/RunnerFindingUserInput.d.ts +19 -0
- package/src/generated/RunnerRelayMessage.d.ts +75 -0
- package/src/generated/RunnerStatusEvent.d.ts +67 -0
- package/src/generated/index.ts +430 -421
- package/src/tauri-events/_api.ts +76 -0
- package/src/tauri-events/index.ts +1 -0
- package/src/ui-bridge-ir/document.ts +62 -0
- package/src/ui-bridge-ir/ir.schema.json +25 -0
- package/src/ui-bridge-ir/__tests__/legacy-roster.test.ts +0 -182
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tauri Event Payload Types
|
|
3
|
+
*
|
|
4
|
+
* Wire-format types for payloads emitted on Tauri event channels by the
|
|
5
|
+
* runner. Use these to type the generic parameter of `listen<T>(...)` so
|
|
6
|
+
* future Rust serde renames (e.g. snake_case → camelCase) and shape
|
|
7
|
+
* changes break the TypeScript build instead of silently dropping events
|
|
8
|
+
* at the listener guard.
|
|
9
|
+
*
|
|
10
|
+
* ## Adjacently-tagged envelope (`AppEvent`)
|
|
11
|
+
*
|
|
12
|
+
* Most runner events are variants of the `AppEvent` enum, which is
|
|
13
|
+
* serialized via `#[serde(tag = "event_type", content = "data")]`. The
|
|
14
|
+
* wire payload is therefore `{ event_type: "<VariantName>", data: { ... } }`
|
|
15
|
+
* and consumers MUST read `event.payload.data.<field>` (NOT
|
|
16
|
+
* `event.payload.<field>`). The `AppEvent` discriminated union below
|
|
17
|
+
* encodes that envelope; reaching `data.<field>` after narrowing by
|
|
18
|
+
* `event_type` is type-safe.
|
|
19
|
+
*
|
|
20
|
+
* ## Direct-payload events
|
|
21
|
+
*
|
|
22
|
+
* A handful of events bypass `AppEvent` and emit a payload struct
|
|
23
|
+
* directly:
|
|
24
|
+
* - `terminal-output` → `TerminalOutputEvent`
|
|
25
|
+
* - `terminal-exit` → `TerminalExitEvent`
|
|
26
|
+
* - `terminal-created` → `TerminalInfo`
|
|
27
|
+
* - `finding_detected` / `finding_resolved` → wrap a `RunnerFinding`
|
|
28
|
+
* inside the `AppEvent` envelope's `data.finding` field as untyped
|
|
29
|
+
* JSON; consumers should cast `data.finding as RunnerFinding`.
|
|
30
|
+
*
|
|
31
|
+
* Source of truth:
|
|
32
|
+
* - `AppEvent`: `qontinui-runner/src-tauri/src/event_system/types.rs`
|
|
33
|
+
* - Terminal events: `qontinui-schemas/rust/src/terminal.rs`
|
|
34
|
+
* - `RunnerFinding`: `qontinui-runner/src-tauri/src/tauri_event_payloads.rs`
|
|
35
|
+
*
|
|
36
|
+
* Regenerate via `cd qontinui-runner && npm run gen-events`.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
export type { AppEvent } from "../generated/AppEvent";
|
|
40
|
+
|
|
41
|
+
// ── Terminal events (direct payloads, not wrapped in AppEvent) ──
|
|
42
|
+
export type { TerminalInfo } from "../generated/TerminalInfo";
|
|
43
|
+
export type { TerminalOutputEvent } from "../generated/TerminalOutputEvent";
|
|
44
|
+
export type { TerminalExitEvent } from "../generated/TerminalExitEvent";
|
|
45
|
+
|
|
46
|
+
// ── Runner-local finding payload (carried inside AppEvent::FindingDetected /
|
|
47
|
+
// AppEvent::FindingResolved as `data.finding`). Distinct from
|
|
48
|
+
// `qontinui_types::verification::Finding`. ──
|
|
49
|
+
export type { RunnerFinding } from "../generated/RunnerFinding";
|
|
50
|
+
export type { RunnerFindingCodeContext } from "../generated/RunnerFindingCodeContext";
|
|
51
|
+
export type { RunnerFindingUserInput } from "../generated/RunnerFindingUserInput";
|
|
52
|
+
|
|
53
|
+
// ── WS relay envelopes (runner→backend) and backend→client status events.
|
|
54
|
+
// Discriminated unions tagged on `type`. Source of truth lives in
|
|
55
|
+
// `qontinui-runner/src-tauri/src/relay_envelopes.rs`; backend-originated
|
|
56
|
+
// events mirror `qontinui-web/backend/app/services/runner/event_publisher.py`. ──
|
|
57
|
+
export type { RunnerRelayMessage } from "../generated/RunnerRelayMessage";
|
|
58
|
+
export type { RunnerStatusEvent } from "../generated/RunnerStatusEvent";
|
|
59
|
+
export type { RunnerConnectedConnection } from "../generated/RunnerConnectedConnection";
|
|
60
|
+
|
|
61
|
+
// ── Dev-only seed-finding payload (`dev:seed-finding` Tauri event from
|
|
62
|
+
// `commands/dev_findings.rs`). Sibling to the camelCase RunnerFinding
|
|
63
|
+
// fields so the TS listener can spread them straight into a Finding. ──
|
|
64
|
+
export type { DevSeedFindingPayload } from "../generated/DevSeedFindingPayload";
|
|
65
|
+
|
|
66
|
+
// ── Recommendation review decision payloads (`review-approved` /
|
|
67
|
+
// `review-rejected` from `commands/productivity.rs`). One struct
|
|
68
|
+
// covers both channels — only `userDecision` differs. ──
|
|
69
|
+
export type { RecommendationReviewDecisionPayload } from "../generated/RecommendationReviewDecisionPayload";
|
|
70
|
+
|
|
71
|
+
// ── Canvas panel payload carried inside AppEvent::CanvasUpdate's
|
|
72
|
+
// `data.panel` field. Wire-format mirror of the runner's `StoredPanel`
|
|
73
|
+
// (`mcp/canvas.rs`). The hand-written `CanvasPanel` in `../canvas/index.ts`
|
|
74
|
+
// pre-dates this generated re-export; both shapes are intentionally
|
|
75
|
+
// identical. New consumers should prefer this generated import. ──
|
|
76
|
+
export type { CanvasPanel as CanvasUpdatePanel } from "../generated/CanvasPanel";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./_api";
|
|
@@ -16,6 +16,61 @@ import type { IRTransition } from "./transition";
|
|
|
16
16
|
*/
|
|
17
17
|
export type IRVersion = "1.0";
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Axes that the spec <-> IR pairing gate compares. A document's
|
|
21
|
+
* `pairingPolicy.acceptDrift.axes` lists which of these are
|
|
22
|
+
* intentionally allowed to diverge between the legacy spec and the
|
|
23
|
+
* IR's forward projection.
|
|
24
|
+
*/
|
|
25
|
+
export type IRPairingAxis = "groupCount" | "groupIds" | "assertionCount";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Per-document opt-out for specific spec<->IR pairing axes.
|
|
29
|
+
*
|
|
30
|
+
* The canonical use case is a hand-authored IR that uses a different
|
|
31
|
+
* grouping convention than the legacy spec. Without an explicit policy
|
|
32
|
+
* the pairing gate flags it as a mismatch on every PR, which trains
|
|
33
|
+
* reviewers to ignore the gate. The policy makes the divergence
|
|
34
|
+
* explicit and audit-friendly:
|
|
35
|
+
*
|
|
36
|
+
* - `axes` — which fingerprint comparisons to skip
|
|
37
|
+
* - `reason` — human-readable justification (required, surfaced in
|
|
38
|
+
* `check-spec-pairing` summary output)
|
|
39
|
+
* - `since` — ISO date (YYYY-MM-DD) the exemption was added
|
|
40
|
+
* - `expiresAt` — optional ISO date (YYYY-MM-DD) after which the
|
|
41
|
+
* exemption stops applying. Forces periodic
|
|
42
|
+
* re-justification: the pairing gate downgrades
|
|
43
|
+
* expired exemptions back to ordinary mismatches.
|
|
44
|
+
*
|
|
45
|
+
* Mismatches NOT covered by the listed `axes` still fail the gate as
|
|
46
|
+
* usual — you can accept drift on `groupCount` while still requiring
|
|
47
|
+
* the assertion total to match.
|
|
48
|
+
*/
|
|
49
|
+
export interface IRAcceptDriftPolicy {
|
|
50
|
+
/** Fingerprint axes whose mismatches are tolerated. */
|
|
51
|
+
axes: IRPairingAxis[];
|
|
52
|
+
/** Human-readable justification for the exemption. */
|
|
53
|
+
reason: string;
|
|
54
|
+
/** ISO date (YYYY-MM-DD) the exemption was added. */
|
|
55
|
+
since: string;
|
|
56
|
+
/**
|
|
57
|
+
* Optional ISO date (YYYY-MM-DD) after which the exemption stops
|
|
58
|
+
* applying. The pairing gate then surfaces the document as an
|
|
59
|
+
* ordinary mismatch.
|
|
60
|
+
*/
|
|
61
|
+
expiresAt?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Per-document pairing policy block. Currently only carries
|
|
66
|
+
* `acceptDrift`; future policies (e.g. assertion-shape exemptions)
|
|
67
|
+
* can extend this without breaking the IR schema.
|
|
68
|
+
*/
|
|
69
|
+
export interface IRPairingPolicy {
|
|
70
|
+
/** Opt-out from specific spec<->IR pairing mismatch axes. */
|
|
71
|
+
acceptDrift?: IRAcceptDriftPolicy;
|
|
72
|
+
}
|
|
73
|
+
|
|
19
74
|
/**
|
|
20
75
|
* Top-level IR document.
|
|
21
76
|
*/
|
|
@@ -33,6 +88,13 @@ export interface IRDocument {
|
|
|
33
88
|
/** Where this document came from. */
|
|
34
89
|
provenance?: IRProvenance;
|
|
35
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Per-document policy block — opt-out from specific spec<->IR
|
|
93
|
+
* pairing mismatches. Used by hand-authored IRs that intentionally
|
|
94
|
+
* diverge from the legacy spec shape.
|
|
95
|
+
*/
|
|
96
|
+
pairingPolicy?: IRPairingPolicy;
|
|
97
|
+
|
|
36
98
|
/** State declarations within this document. */
|
|
37
99
|
states: IRState[];
|
|
38
100
|
/** Transition declarations within this document. */
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"description": { "type": "string" },
|
|
16
16
|
"metadata": { "$ref": "#/$defs/IRMetadata" },
|
|
17
17
|
"provenance": { "$ref": "#/$defs/IRProvenance" },
|
|
18
|
+
"pairingPolicy": { "$ref": "#/$defs/IRPairingPolicy" },
|
|
18
19
|
"initialState": { "type": "string" },
|
|
19
20
|
"states": {
|
|
20
21
|
"type": "array",
|
|
@@ -26,6 +27,30 @@
|
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
29
|
"$defs": {
|
|
30
|
+
"IRPairingPolicy": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"properties": {
|
|
34
|
+
"acceptDrift": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"required": ["axes", "reason", "since"],
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"properties": {
|
|
39
|
+
"axes": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["groupCount", "groupIds", "assertionCount"]
|
|
44
|
+
},
|
|
45
|
+
"minItems": 1
|
|
46
|
+
},
|
|
47
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
48
|
+
"since": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
|
|
49
|
+
"expiresAt": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
29
54
|
"IRElementCriteria": {
|
|
30
55
|
"type": "object",
|
|
31
56
|
"additionalProperties": false,
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Legacy spec roster test (Phase A2 fixture gate).
|
|
3
|
-
*
|
|
4
|
-
* Walks the entire `qontinui_parent` monorepo, collects every shipping
|
|
5
|
-
* `*.spec.uibridge.json`, and round-trips each through:
|
|
6
|
-
*
|
|
7
|
-
* legacy --projectLegacyToIR--> IR --projectIRToBundledPage--> legacy'
|
|
8
|
-
*
|
|
9
|
-
* For each fixture we assert (semantically — not byte-strict):
|
|
10
|
-
* - same number of groups
|
|
11
|
-
* - same group ids in input order
|
|
12
|
-
* - same total assertion count across all groups
|
|
13
|
-
*
|
|
14
|
-
* This is coarse but effective: it catches any data loss in the inverse
|
|
15
|
-
* projection without forcing the inverse to preserve hand-edited assertion
|
|
16
|
-
* ids / severity / category / etc. (which the forward direction regenerates
|
|
17
|
-
* from defaults). Phase A4 will tighten the contract for hand-authored
|
|
18
|
-
* fields.
|
|
19
|
-
*
|
|
20
|
-
* The walker fails fast if it finds < 100 fixtures (canary against a
|
|
21
|
-
* misconfigured directory walk) — current expected count is 121 (96 runner +
|
|
22
|
-
* 19 web + 5 mobile + 2 supervisor) as of 2026-04. The number is allowed to
|
|
23
|
-
* grow; the lower bound just guards against accidentally testing nothing.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
27
|
-
import path from "node:path";
|
|
28
|
-
|
|
29
|
-
import { describe, expect, it } from "vitest";
|
|
30
|
-
|
|
31
|
-
import {
|
|
32
|
-
projectIRToBundledPage,
|
|
33
|
-
projectLegacyToIR,
|
|
34
|
-
type LegacySpec,
|
|
35
|
-
} from "../projection";
|
|
36
|
-
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// Discovery
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Resolve the monorepo root from the test file's location. The test file
|
|
43
|
-
* lives at `qontinui-schemas/ts/src/ui-bridge-ir/__tests__/`, so five
|
|
44
|
-
* `..` segments take us to `qontinui_parent/`.
|
|
45
|
-
*
|
|
46
|
-
* Use `process.cwd()` as a fallback only when the test runs from outside the
|
|
47
|
-
* usual layout (e.g. CI checkout with renamed parent) — vitest sets cwd to
|
|
48
|
-
* the package root by default.
|
|
49
|
-
*/
|
|
50
|
-
const MONOREPO_ROOT = path.resolve(
|
|
51
|
-
__dirname,
|
|
52
|
-
"..",
|
|
53
|
-
"..",
|
|
54
|
-
"..",
|
|
55
|
-
"..",
|
|
56
|
-
"..",
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// Directories to skip. Note: do NOT include "build" — Next.js apps have route
|
|
60
|
-
// folders named `build/` (e.g., qontinui-web/frontend/src/app/(app)/build/)
|
|
61
|
-
// that legitimately contain spec fixtures.
|
|
62
|
-
const SKIP_DIRS = new Set([
|
|
63
|
-
"node_modules",
|
|
64
|
-
"target",
|
|
65
|
-
"target-agent",
|
|
66
|
-
"dist",
|
|
67
|
-
".git",
|
|
68
|
-
".next",
|
|
69
|
-
".venv",
|
|
70
|
-
"__pycache__",
|
|
71
|
-
"out",
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Recursively walk `root`, collecting every `*.spec.uibridge.json` file.
|
|
76
|
-
* Skips the directory names in `SKIP_DIRS`. Synchronous; the fixture set is
|
|
77
|
-
* small (low hundreds) so we don't bother with worker threads.
|
|
78
|
-
*/
|
|
79
|
-
function collectSpecs(root: string): string[] {
|
|
80
|
-
const out: string[] = [];
|
|
81
|
-
const stack: string[] = [root];
|
|
82
|
-
while (stack.length > 0) {
|
|
83
|
-
const current = stack.pop()!;
|
|
84
|
-
let entries: import("node:fs").Dirent[];
|
|
85
|
-
try {
|
|
86
|
-
entries = readdirSync(current, { withFileTypes: true });
|
|
87
|
-
} catch {
|
|
88
|
-
// Permission errors / vanished dirs — skip silently.
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
for (const entry of entries) {
|
|
92
|
-
if (SKIP_DIRS.has(entry.name)) continue;
|
|
93
|
-
const full = path.join(current, entry.name);
|
|
94
|
-
if (entry.isDirectory()) {
|
|
95
|
-
stack.push(full);
|
|
96
|
-
} else if (entry.isFile() && entry.name.endsWith(".spec.uibridge.json")) {
|
|
97
|
-
out.push(full);
|
|
98
|
-
} else if (entry.isSymbolicLink()) {
|
|
99
|
-
// Resolve symlinks defensively — some plugin checkouts use them.
|
|
100
|
-
try {
|
|
101
|
-
const stat = statSync(full);
|
|
102
|
-
if (stat.isDirectory()) {
|
|
103
|
-
stack.push(full);
|
|
104
|
-
} else if (stat.isFile() && entry.name.endsWith(".spec.uibridge.json")) {
|
|
105
|
-
out.push(full);
|
|
106
|
-
}
|
|
107
|
-
} catch {
|
|
108
|
-
// ignore
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Sort for deterministic test ordering.
|
|
114
|
-
out.sort();
|
|
115
|
-
return out;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const SPECS = collectSpecs(MONOREPO_ROOT);
|
|
119
|
-
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
// Tests
|
|
122
|
-
// ---------------------------------------------------------------------------
|
|
123
|
-
|
|
124
|
-
describe("legacy spec roster — discovery sanity", () => {
|
|
125
|
-
it("finds at least 100 *.spec.uibridge.json files in the monorepo", () => {
|
|
126
|
-
expect(SPECS.length).toBeGreaterThanOrEqual(100);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("does not include node_modules / dist / target paths", () => {
|
|
130
|
-
for (const f of SPECS) {
|
|
131
|
-
expect(f).not.toMatch(/[\\/](?:node_modules|dist|target|\.git)[\\/]/);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe("legacy spec roster — round-trip via IR", () => {
|
|
137
|
-
// Use it.each so every fixture gets its own test name and failures pinpoint
|
|
138
|
-
// the exact file. Pre-load each fixture as a tuple for the iterator.
|
|
139
|
-
const cases: ReadonlyArray<readonly [string, string]> = SPECS.map((p) => [
|
|
140
|
-
path.relative(MONOREPO_ROOT, p),
|
|
141
|
-
p,
|
|
142
|
-
] as const);
|
|
143
|
-
|
|
144
|
-
it.each(cases)("%s round-trips through IR without data loss", (_relpath, full) => {
|
|
145
|
-
let raw = readFileSync(full, "utf-8");
|
|
146
|
-
// Strip UTF-8 BOM if present — at least one shipping spec
|
|
147
|
-
// (settings-security.spec.uibridge.json) was saved with a BOM.
|
|
148
|
-
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
|
|
149
|
-
const legacy = JSON.parse(raw) as LegacySpec;
|
|
150
|
-
|
|
151
|
-
const ir = projectLegacyToIR(legacy);
|
|
152
|
-
const projected = projectIRToBundledPage(ir);
|
|
153
|
-
|
|
154
|
-
// 1. Same number of groups.
|
|
155
|
-
expect(projected.groups.length).toBe(legacy.groups.length);
|
|
156
|
-
|
|
157
|
-
// 2. Same group ids in input order.
|
|
158
|
-
expect(projected.groups.map((g) => g.id)).toEqual(legacy.groups.map((g) => g.id));
|
|
159
|
-
|
|
160
|
-
// 3. Same total assertion count across all groups (coarse data-loss check).
|
|
161
|
-
const projectedAssertionCount = projected.groups.reduce(
|
|
162
|
-
(sum, g) => sum + g.assertions.length,
|
|
163
|
-
0,
|
|
164
|
-
);
|
|
165
|
-
const legacyAssertionCount = legacy.groups.reduce(
|
|
166
|
-
(sum, g) => sum + (Array.isArray(g.assertions) ? g.assertions.length : 0),
|
|
167
|
-
0,
|
|
168
|
-
);
|
|
169
|
-
// The forward projection emits a placeholder assertion for any state with
|
|
170
|
-
// zero requiredElements. Real legacy fixtures never have zero-assertion
|
|
171
|
-
// groups, but be defensive: allow projected >= legacy when legacy had
|
|
172
|
-
// groups with zero assertions.
|
|
173
|
-
const legacyHasEmptyGroup = legacy.groups.some(
|
|
174
|
-
(g) => !Array.isArray(g.assertions) || g.assertions.length === 0,
|
|
175
|
-
);
|
|
176
|
-
if (legacyHasEmptyGroup) {
|
|
177
|
-
expect(projectedAssertionCount).toBeGreaterThanOrEqual(legacyAssertionCount);
|
|
178
|
-
} else {
|
|
179
|
-
expect(projectedAssertionCount).toBe(legacyAssertionCount);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
});
|