@windyroad/tdd 0.4.3 → 0.4.4

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.
@@ -101,5 +101,5 @@
101
101
  }
102
102
  },
103
103
  "name": "wr-tdd",
104
- "version": "0.4.3"
104
+ "version": "0.4.4"
105
105
  }
package/README.md CHANGED
@@ -49,6 +49,20 @@ Once active, the workflow is enforced on every edit:
49
49
 
50
50
  Test files and config/doc files are always writable regardless of state.
51
51
 
52
+ ## Supported test layouts
53
+
54
+ The hook associates an implementation file with its tracked test by matching one of the following path shapes (per [P201](../../docs/problems/verifying/201-windyroad-tdd-hook-only-recognises-same-dir-or-tests-test-associations.md)):
55
+
56
+ | Shape | Impl path | Test path |
57
+ |---|---|---|
58
+ | Same-directory | `src/foo.js` | `src/foo.test.js` or `src/foo.spec.js` |
59
+ | `__tests__/`-adjacent | `src/foo.js` | `src/__tests__/foo.test.js` |
60
+ | Parent `__tests__/` | `src/components/Hero.tsx` | `src/__tests__/Hero.test.tsx` |
61
+ | `test/`-mirror (Vitest default; many Jest setups) | `src/foo.js`, `src/a/b/foo.js`, `packages/<pkg>/src/foo.js` | `test/foo.test.js`, `test/a/b/foo.test.js`, `packages/<pkg>/test/foo.test.js` |
62
+ | Cucumber step-definitions | `features/step_definitions/checkout.steps.js` | `features/checkout.feature` |
63
+
64
+ The `test/`-mirror rule replaces the **last** `src` path segment with `test`, so it works at any nesting depth and for monorepo workspace layouts.
65
+
52
66
  ## How It Works
53
67
 
54
68
  | Hook | Trigger | What it does |
@@ -60,19 +74,6 @@ Test files and config/doc files are always writable regardless of state.
60
74
  | `tdd-setup-marker.sh` | Skill completes | Marks test setup as done |
61
75
  | `tdd-reset.sh` | Session end | Resets the TDD state |
62
76
 
63
- ## Jobs to be Done
64
-
65
- This plugin serves the [Jobs to be Done](../../docs/jtbd/) below. Per [ADR-051](../../docs/decisions/051-jtbd-anchored-readme-with-drift-advisory.proposed.md), the persona-grouped JTBD anchor is the canonical source of truth for the README's value framing.
66
-
67
- ### Solo developer
68
-
69
- - **[JTBD-001 Enforce Governance Without Slowing Down](../../docs/jtbd/solo-developer/JTBD-001-enforce-governance.proposed.md)** — implementation edits are blocked until a failing test exists; the agent cannot "forget" to write the test first.
70
- - **[JTBD-002 Ship AI-Assisted Code with Confidence](../../docs/jtbd/solo-developer/JTBD-002-ship-with-confidence.proposed.md)** — Red-Green-Refactor enforcement turns the test suite into the agent's safety net, not an after-thought.
71
-
72
- ### Plugin user
73
-
74
- - **[JTBD-302 Trust That the README Describes the Plugin I Just Installed](../../docs/jtbd/plugin-user/JTBD-302-trust-readme-describes-installed-behaviour.proposed.md)** — this README is anchored on current JTBD job IDs; drift between prose and shipped behaviour is detectable at retro time per ADR-051.
75
-
76
77
  ## Updating and Uninstalling
77
78
 
78
79
  ```bash
@@ -157,6 +157,22 @@ tdd_find_test_for_impl() {
157
157
  return
158
158
  fi
159
159
 
160
+ # test/-mirror layout (P201): src/foo.js → test/foo.test.js, recursive for
161
+ # nested src/a/b/foo.js → test/a/b/foo.test.js, and workspace
162
+ # packages/<pkg>/src/foo.js → packages/<pkg>/test/foo.test.js.
163
+ # Compute the mirror once — depends only on DIR (the impl's directory).
164
+ # Replaces the LAST `src` path segment with `test`.
165
+ local MIRROR_DIR=""
166
+ case "$DIR" in
167
+ src) MIRROR_DIR="test" ;;
168
+ src/*) MIRROR_DIR="test/${DIR#src/}" ;;
169
+ */src) MIRROR_DIR="${DIR%/src}/test" ;;
170
+ */src/*)
171
+ local _mirror_prefix="${DIR%/src/*}"
172
+ MIRROR_DIR="${_mirror_prefix}/test/${DIR#"${_mirror_prefix}"/src/}"
173
+ ;;
174
+ esac
175
+
160
176
  # Check tracked test files for a match
161
177
  # Priority: exact match in tracked files (any convention)
162
178
  while IFS= read -r tracked; do
@@ -188,6 +204,13 @@ tdd_find_test_for_impl() {
188
204
  ;;
189
205
  esac
190
206
 
207
+ # test/-mirror layout (P201): match when tracked_dir is the computed mirror
208
+ if [ -n "$MIRROR_DIR" ] && [ "$tracked_dir" = "$MIRROR_DIR" ]; then
209
+ case "$tracked_base" in
210
+ "${STEM}.test."*|"${STEM}.spec."*) echo "$tracked"; return ;;
211
+ esac
212
+ fi
213
+
191
214
  # Cucumber: features/step_definitions/foo.steps.js → features/foo.feature
192
215
  # If this impl is inside a step_definitions/ directory, look in the parent for a .feature file
193
216
  case "$DIR" in
@@ -238,6 +238,61 @@ teardown() {
238
238
  [ -z "$result" ]
239
239
  }
240
240
 
241
+ # --- tdd_find_test_for_impl: test/-mirror layout (P201) ---
242
+ # Vitest default + many Jest setups mirror src/ under a sibling test/ tree.
243
+ # A new behavioural rule: replace the LAST `src` path segment with `test`
244
+ # to map an impl path to its mirrored test directory.
245
+
246
+ @test "find_test_for_impl: test/-mirror at top level (src/foo.js → test/foo.test.js)" {
247
+ tdd_add_test_file "$TEST_SESSION" "test/foo.test.js"
248
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "src/foo.js")
249
+ [ "$result" = "test/foo.test.js" ]
250
+ }
251
+
252
+ @test "find_test_for_impl: test/-mirror with .spec variant (src/foo.js → test/foo.spec.js)" {
253
+ tdd_add_test_file "$TEST_SESSION" "test/foo.spec.js"
254
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "src/foo.js")
255
+ [ "$result" = "test/foo.spec.js" ]
256
+ }
257
+
258
+ @test "find_test_for_impl: test/-mirror preserves .tsx (src/Hero.tsx → test/Hero.test.tsx)" {
259
+ tdd_add_test_file "$TEST_SESSION" "test/Hero.test.tsx"
260
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "src/Hero.tsx")
261
+ [ "$result" = "test/Hero.test.tsx" ]
262
+ }
263
+
264
+ @test "find_test_for_impl: test/-mirror recursive nested (src/a/b/foo.js → test/a/b/foo.test.js)" {
265
+ tdd_add_test_file "$TEST_SESSION" "test/a/b/foo.test.js"
266
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "src/a/b/foo.js")
267
+ [ "$result" = "test/a/b/foo.test.js" ]
268
+ }
269
+
270
+ @test "find_test_for_impl: test/-mirror workspace layout (packages/foo/src/x.ts → packages/foo/test/x.test.ts)" {
271
+ tdd_add_test_file "$TEST_SESSION" "packages/foo/test/x.test.ts"
272
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "packages/foo/src/x.ts")
273
+ [ "$result" = "packages/foo/test/x.test.ts" ]
274
+ }
275
+
276
+ @test "find_test_for_impl: test/-mirror workspace nested (packages/foo/src/a/b/x.ts → packages/foo/test/a/b/x.test.ts)" {
277
+ tdd_add_test_file "$TEST_SESSION" "packages/foo/test/a/b/x.test.ts"
278
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "packages/foo/src/a/b/x.ts")
279
+ [ "$result" = "packages/foo/test/a/b/x.test.ts" ]
280
+ }
281
+
282
+ @test "find_test_for_impl: test/-mirror does NOT match wrong stem (src/foo.js + test/bar.test.js → empty)" {
283
+ tdd_add_test_file "$TEST_SESSION" "test/bar.test.js"
284
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "src/foo.js")
285
+ [ -z "$result" ]
286
+ }
287
+
288
+ @test "find_test_for_impl: test/-mirror does NOT match without src/ anchor (lib/foo.js + test/foo.test.js → empty)" {
289
+ # Ticket scope is explicitly src/-anchored mirror; lib/foo.js → test/foo.test.js
290
+ # is NOT a recognised pairing (the ticket frames it as "src/foo.js → test/foo.test.js").
291
+ tdd_add_test_file "$TEST_SESSION" "test/foo.test.js"
292
+ result=$(tdd_find_test_for_impl "$TEST_SESSION" "lib/foo.js")
293
+ [ -z "$result" ]
294
+ }
295
+
241
296
  # --- State for impl files (via association) ---
242
297
 
243
298
  @test "read_state_for_impl: returns IDLE when no associated test" {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/tdd",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "TDD state machine enforcement (Red-Green-Refactor cycle)",
5
5
  "bin": {
6
6
  "windyroad-tdd": "./bin/install.mjs"