@am_shork/attest 0.6.0 → 0.7.1

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/runtime.d.ts CHANGED
@@ -1,12 +1,32 @@
1
1
  /**
2
2
  * Group scenarios that verify one requirement. The describe block is named
3
3
  * `[id]` so runtime coverage can be reconstructed from the task tree (§5.4).
4
+ *
5
+ * The body is handed to `describe` untouched. It used to be wrapped in a
6
+ * set-and-restore of a module-level `currentReqId` that `scenario()` read; that
7
+ * variable is deleted rather than repaired — see `scenario`.
4
8
  */
5
9
  export declare function requirement(id: string, body: () => void): void;
6
10
  /**
7
11
  * Declare a single verifiable scenario. Must be nested inside requirement().
8
12
  * The assertion itself is delegated to Vitest `it` (design §3).
9
13
  *
14
+ * **The nesting check happens when the scenario runs, not when it is
15
+ * collected**, and that is what makes grouping work. Vitest defers a nested
16
+ * `describe`'s callback until after the parent's has returned — measured: at
17
+ * the end of the outer callback the inner one has not run — so a module-level
18
+ * `currentReqId` set and restored around `body()` was already back to `null` by
19
+ * the time a grouped `scenario()` ran. It threw, and a throw during collection
20
+ * is a *file* error: nothing in that file was collected, so every scenario in
21
+ * it came back `declared-not-run` — including correct ungrouped ones under
22
+ * other requirements — each blamed on "skipped, or excluded by an .only?".
23
+ * Meanwhile `check` and `cover` read the same file statically and reported full
24
+ * coverage, so the static half called a file green that could not execute at
25
+ * all.
26
+ *
27
+ * Checking here costs nothing and fails better: a scenario genuinely outside
28
+ * any `requirement()` now fails as one test instead of taking its file down.
29
+ *
10
30
  * `timeoutMs` is passed straight through to `it`. It exists because `verify`
11
31
  * runs the suite **isolated** (§5.4) — no `vitest.config.ts` unless
12
32
  * `--vitest-config` names one — so Vitest's 5-second default is what every
package/dist/runtime.js CHANGED
@@ -2,27 +2,53 @@
2
2
  // Pure delegation to Vitest; no in-process coverage singleton — coverage is
3
3
  // reconstructed from the Vitest task tree instead (design §5.4).
4
4
  import { describe, it } from 'vitest';
5
- let currentReqId = null;
5
+ import { requirementIdOf, requirementSuiteName } from './core/req-suite.js';
6
6
  /**
7
7
  * Group scenarios that verify one requirement. The describe block is named
8
8
  * `[id]` so runtime coverage can be reconstructed from the task tree (§5.4).
9
+ *
10
+ * The body is handed to `describe` untouched. It used to be wrapped in a
11
+ * set-and-restore of a module-level `currentReqId` that `scenario()` read; that
12
+ * variable is deleted rather than repaired — see `scenario`.
9
13
  */
10
14
  export function requirement(id, body) {
11
- describe(`[${id}]`, () => {
12
- const prev = currentReqId;
13
- currentReqId = id;
14
- try {
15
- body();
16
- }
17
- finally {
18
- currentReqId = prev;
19
- }
20
- });
15
+ describe(requirementSuiteName(id), body);
16
+ }
17
+ /**
18
+ * The requirement a scenario actually ended up inside, read from the task tree.
19
+ *
20
+ * The same source `runner.ts` attributes coverage from, which is the point: a
21
+ * module-level "current requirement" was a *second* answer to a question the
22
+ * suite name already answers, and the two disagreed exactly where it mattered.
23
+ */
24
+ function owningRequirement(task) {
25
+ for (let s = task?.suite; s; s = s.suite) {
26
+ const id = requirementIdOf(s.name ?? '');
27
+ if (id !== undefined)
28
+ return id;
29
+ }
30
+ return undefined;
21
31
  }
22
32
  /**
23
33
  * Declare a single verifiable scenario. Must be nested inside requirement().
24
34
  * The assertion itself is delegated to Vitest `it` (design §3).
25
35
  *
36
+ * **The nesting check happens when the scenario runs, not when it is
37
+ * collected**, and that is what makes grouping work. Vitest defers a nested
38
+ * `describe`'s callback until after the parent's has returned — measured: at
39
+ * the end of the outer callback the inner one has not run — so a module-level
40
+ * `currentReqId` set and restored around `body()` was already back to `null` by
41
+ * the time a grouped `scenario()` ran. It threw, and a throw during collection
42
+ * is a *file* error: nothing in that file was collected, so every scenario in
43
+ * it came back `declared-not-run` — including correct ungrouped ones under
44
+ * other requirements — each blamed on "skipped, or excluded by an .only?".
45
+ * Meanwhile `check` and `cover` read the same file statically and reported full
46
+ * coverage, so the static half called a file green that could not execute at
47
+ * all.
48
+ *
49
+ * Checking here costs nothing and fails better: a scenario genuinely outside
50
+ * any `requirement()` now fails as one test instead of taking its file down.
51
+ *
26
52
  * `timeoutMs` is passed straight through to `it`. It exists because `verify`
27
53
  * runs the suite **isolated** (§5.4) — no `vitest.config.ts` unless
28
54
  * `--vitest-config` names one — so Vitest's 5-second default is what every
@@ -32,9 +58,11 @@ export function requirement(id, body) {
32
58
  * for the whole suite, sets one number for what is a property of one scenario.
33
59
  */
34
60
  export function scenario(name, fn, timeoutMs) {
35
- if (!currentReqId) {
36
- throw new Error(`scenario("${name}") must be nested inside requirement(...)`);
37
- }
38
- it(name, fn, timeoutMs);
61
+ it(name, async (ctx) => {
62
+ if (owningRequirement(ctx.task) === undefined) {
63
+ throw new Error(`scenario("${name}") must be nested inside requirement(...)`);
64
+ }
65
+ return fn();
66
+ }, timeoutMs);
39
67
  }
40
68
  //# sourceMappingURL=runtime.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@am_shork/attest",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "TDD-native spec framework: tests are the source of truth for verification, ID-bound requirements the source of truth for intent.",
5
5
  "type": "module",
6
6
  "engines": {