11import { describe , expect , it } from 'vitest' ;
22
3- import { guardHolds , initialState , reduce , type WizardConfig , type WizardState } from '../reducer' ;
3+ import { isStepReachable , initialState , reduce , type WizardConfig , type WizardState } from '../reducer' ;
44import type { WizardStepDescriptor } from '../types' ;
55
66const cfg = ( descriptors : WizardStepDescriptor [ ] ) : WizardConfig => ( { descriptors } ) ;
77
88/**
9- * A representative monotonic guard set over 4 steps:
10- * a — entry (no guard , always enterable)
11- * b — guard : g1
12- * c — guard : g2
13- * d — guard : g3
9+ * A representative monotonic reachability set over 4 steps:
10+ * a — entry (no isReachable , always enterable)
11+ * b — isReachable : g1
12+ * c — isReachable : g2
13+ * d — isReachable : g3
1414 * Toggling g1..g3 from all-false to all-true walks the furthest-reachable
1515 * boundary one step at a time. Monotonic by construction (g3 ⇒ g2 ⇒ g1).
1616 */
1717const monotonic = ( g1 : boolean , g2 : boolean , g3 : boolean ) : WizardStepDescriptor [ ] => [
1818 { id : 'a' } ,
19- { id : 'b' , guard : ( ) => g1 } ,
20- { id : 'c' , guard : ( ) => g2 } ,
21- { id : 'd' , guard : ( ) => g3 } ,
19+ { id : 'b' , isReachable : ( ) => g1 } ,
20+ { id : 'c' , isReachable : ( ) => g2 } ,
21+ { id : 'd' , isReachable : ( ) => g3 } ,
2222] ;
2323
2424const at = ( current : string ) : WizardState => ( { current, direction : 0 , hasNavigated : false } ) ;
2525
26- describe ( 'guardHolds ' , ( ) => {
27- it ( 'resolves TRUE when no guard (entry step default flip )' , ( ) => {
28- expect ( guardHolds ( { id : 'a' } ) ) . toBe ( true ) ;
26+ describe ( 'isStepReachable ' , ( ) => {
27+ it ( 'resolves TRUE when no isReachable (entry step default)' , ( ) => {
28+ expect ( isStepReachable ( { id : 'a' } ) ) . toBe ( true ) ;
2929 } ) ;
3030
3131 it ( 'delegates to the inline predicate' , ( ) => {
32- expect ( guardHolds ( { id : 'b' , guard : ( ) => true } ) ) . toBe ( true ) ;
33- expect ( guardHolds ( { id : 'b' , guard : ( ) => false } ) ) . toBe ( false ) ;
32+ expect ( isStepReachable ( { id : 'b' , isReachable : ( ) => true } ) ) . toBe ( true ) ;
33+ expect ( isStepReachable ( { id : 'b' , isReachable : ( ) => false } ) ) . toBe ( false ) ;
3434 } ) ;
3535} ) ;
3636
3737describe ( 'initialState — furthest contiguously-reachable step' , ( ) => {
38- it ( 'all guards false but entry → step 0' , ( ) => {
38+ it ( 'all isReachable false but entry → step 0' , ( ) => {
3939 expect ( initialState ( cfg ( monotonic ( false , false , false ) ) ) . current ) . toBe ( 'a' ) ;
4040 } ) ;
4141
@@ -51,7 +51,7 @@ describe('initialState — furthest contiguously-reachable step', () => {
5151 expect ( initialState ( cfg ( monotonic ( true , true , true ) ) ) . current ) . toBe ( 'd' ) ;
5252 } ) ;
5353
54- it ( 'stops at the first gate (does not jump a closed guard )' , ( ) => {
54+ it ( 'stops at the first gate (does not jump a closed isReachable )' , ( ) => {
5555 // b open, c closed, d open: contiguous run stops at b.
5656 expect ( initialState ( cfg ( monotonic ( true , false , true ) ) ) . current ) . toBe ( 'b' ) ;
5757 } ) ;
@@ -80,9 +80,9 @@ describe('reduce — referential identity on every no-op path', () => {
8080 expect ( reduce ( s , { type : 'NEXT' } , cfg ( monotonic ( true , true , true ) ) ) ) . toBe ( s ) ;
8181 } ) ;
8282
83- it ( 'NEXT blocked by next guard → same ref' , ( ) => {
83+ it ( 'NEXT blocked by next isReachable → same ref' , ( ) => {
8484 const s = at ( 'a' ) ;
85- // b's guard is false → cannot advance.
85+ // b's isReachable is false → cannot advance.
8686 expect ( reduce ( s , { type : 'NEXT' } , cfg ( monotonic ( false , false , false ) ) ) ) . toBe ( s ) ;
8787 } ) ;
8888
@@ -96,12 +96,12 @@ describe('reduce — referential identity on every no-op path', () => {
9696 expect ( reduce ( s , { type : 'PREV' } , cfg ( monotonic ( true , true , true ) ) ) ) . toBe ( s ) ;
9797 } ) ;
9898
99- it ( 'PREV blocked by predecessor guard → same ref' , ( ) => {
100- // Non-monotonic on purpose: at c, b's guard is false.
99+ it ( 'PREV blocked by predecessor isReachable → same ref' , ( ) => {
100+ // Non-monotonic on purpose: at c, b's isReachable is false.
101101 const steps : WizardStepDescriptor [ ] = [
102102 { id : 'a' } ,
103- { id : 'b' , guard : ( ) => false } ,
104- { id : 'c' , guard : ( ) => true } ,
103+ { id : 'b' , isReachable : ( ) => false } ,
104+ { id : 'c' , isReachable : ( ) => true } ,
105105 ] ;
106106 const s = at ( 'c' ) ;
107107 expect ( reduce ( s , { type : 'PREV' } , cfg ( steps ) ) ) . toBe ( s ) ;
@@ -117,9 +117,9 @@ describe('reduce — referential identity on every no-op path', () => {
117117 expect ( reduce ( s , { type : 'GOTO' , step : 'a' } , cfg ( monotonic ( true , true , true ) ) ) ) . toBe ( s ) ;
118118 } ) ;
119119
120- it ( 'GOTO blocked by target guard → same ref' , ( ) => {
120+ it ( 'GOTO blocked by target isReachable → same ref' , ( ) => {
121121 const s = at ( 'a' ) ;
122- // d's guard is false → cannot jump.
122+ // d's isReachable is false → cannot jump.
123123 expect ( reduce ( s , { type : 'GOTO' , step : 'd' } , cfg ( monotonic ( true , true , false ) ) ) ) . toBe ( s ) ;
124124 } ) ;
125125
@@ -130,8 +130,8 @@ describe('reduce — referential identity on every no-op path', () => {
130130 } ) ;
131131} ) ;
132132
133- describe ( 'reduce — NEXT sequential + guard -gated' , ( ) => {
134- it ( 'advances exactly one slot when the next guard holds' , ( ) => {
133+ describe ( 'reduce — NEXT sequential + isReachable -gated' , ( ) => {
134+ it ( 'advances exactly one slot when the next isReachable holds' , ( ) => {
135135 const next = reduce ( at ( 'a' ) , { type : 'NEXT' } , cfg ( monotonic ( true , false , false ) ) ) ;
136136 expect ( next . current ) . toBe ( 'b' ) ;
137137 expect ( next . direction ) . toBe ( 1 ) ;
@@ -144,14 +144,14 @@ describe('reduce — NEXT sequential + guard-gated', () => {
144144 expect ( next . current ) . toBe ( 'b' ) ;
145145 } ) ;
146146
147- it ( 'a hard stop mid-flow does not skip ahead to a later open guard ' , ( ) => {
147+ it ( 'a hard stop mid-flow does not skip ahead to a later open isReachable ' , ( ) => {
148148 // b open, c closed, d open. From b, NEXT targets c (closed) → no-op.
149149 const s = at ( 'b' ) ;
150150 expect ( reduce ( s , { type : 'NEXT' } , cfg ( monotonic ( true , false , true ) ) ) ) . toBe ( s ) ;
151151 } ) ;
152152} ) ;
153153
154- describe ( 'reduce — PREV positional + guard -gated' , ( ) => {
154+ describe ( 'reduce — PREV positional + isReachable -gated' , ( ) => {
155155 it ( 'walks exactly one declaration slot back' , ( ) => {
156156 const prev = reduce ( at ( 'c' ) , { type : 'PREV' } , cfg ( monotonic ( true , true , true ) ) ) ;
157157 expect ( prev . current ) . toBe ( 'b' ) ;
@@ -165,7 +165,7 @@ describe('reduce — PREV positional + guard-gated', () => {
165165 } ) ;
166166} ) ;
167167
168- describe ( 'reduce — GOTO guard -gated' , ( ) => {
168+ describe ( 'reduce — GOTO isReachable -gated' , ( ) => {
169169 it ( 'jumps to a reachable target' , ( ) => {
170170 const goto = reduce ( at ( 'a' ) , { type : 'GOTO' , step : 'c' } , cfg ( monotonic ( true , true , true ) ) ) ;
171171 expect ( goto . current ) . toBe ( 'c' ) ;
0 commit comments