@@ -449,3 +449,173 @@ describe("basic", () => {
449
449
vi . useRealTimers ( )
450
450
} )
451
451
} )
452
+
453
+ describe ( "edge cases" , ( ) => {
454
+ test ( "reactive props updates" , async ( ) => {
455
+ const actionSpy = vi . fn ( )
456
+
457
+ const machine = createMachine < any > ( {
458
+ props ( { props } ) {
459
+ return { max : props . max || 0 }
460
+ } ,
461
+ initialState ( ) {
462
+ return "test"
463
+ } ,
464
+ context ( { bindable } ) {
465
+ return {
466
+ count : bindable ( ( ) => ( { defaultValue : 0 } ) ) ,
467
+ }
468
+ } ,
469
+ states : {
470
+ test : {
471
+ on : {
472
+ INCREMENT : {
473
+ actions : [ "increment" ] ,
474
+ } ,
475
+ CHECK : {
476
+ guard : ( { prop, context } : any ) => context . get ( "count" ) < prop ( "max" ) ,
477
+ actions : [ "allowAction" ] ,
478
+ } ,
479
+ } ,
480
+ } ,
481
+ } ,
482
+ implementations : {
483
+ actions : {
484
+ allowAction : actionSpy ,
485
+ increment : ( { context } ) => context . set ( "count" , context . get ( "count" ) + 1 ) ,
486
+ } ,
487
+ } ,
488
+ } )
489
+
490
+ const { result } = renderHook ( ( ) => useMachine ( machine , { max : 5 } ) )
491
+
492
+ // Increment count to 3
493
+ await act ( async ( ) => result . current . send ( { type : "INCREMENT" } ) )
494
+ await act ( async ( ) => result . current . send ( { type : "INCREMENT" } ) )
495
+ await act ( async ( ) => result . current . send ( { type : "INCREMENT" } ) )
496
+
497
+ // max is 5, count is 3, should allow action
498
+ await act ( async ( ) => result . current . send ( { type : "CHECK" } ) )
499
+ expect ( actionSpy ) . toHaveBeenCalledTimes ( 1 )
500
+ } )
501
+
502
+ test ( "state.matches() helper" , async ( ) => {
503
+ const machine = createMachine < any > ( {
504
+ initialState ( ) {
505
+ return "idle"
506
+ } ,
507
+ states : {
508
+ idle : {
509
+ on : {
510
+ START : { target : "loading" } ,
511
+ } ,
512
+ } ,
513
+ loading : {
514
+ on : {
515
+ SUCCESS : { target : "success" } ,
516
+ ERROR : { target : "error" } ,
517
+ } ,
518
+ } ,
519
+ success : { } ,
520
+ error : { } ,
521
+ } ,
522
+ } )
523
+
524
+ const { result, send } = renderMachine ( machine )
525
+
526
+ expect ( result . current . state . matches ( "idle" ) ) . toBe ( true )
527
+ expect ( result . current . state . matches ( "idle" , "loading" ) ) . toBe ( true )
528
+ expect ( result . current . state . matches ( "loading" , "success" ) ) . toBe ( false )
529
+
530
+ await send ( { type : "START" } )
531
+ expect ( result . current . state . matches ( "loading" ) ) . toBe ( true )
532
+ expect ( result . current . state . matches ( "idle" , "loading" , "error" ) ) . toBe ( true )
533
+
534
+ await send ( { type : "SUCCESS" } )
535
+ expect ( result . current . state . matches ( "success" , "error" ) ) . toBe ( true )
536
+ expect ( result . current . state . matches ( "idle" , "loading" ) ) . toBe ( false )
537
+ } )
538
+
539
+ test ( "same-state transitions with actions" , async ( ) => {
540
+ const actionSpy = vi . fn ( )
541
+
542
+ const machine = createMachine < any > ( {
543
+ initialState ( ) {
544
+ return "active"
545
+ } ,
546
+ states : {
547
+ active : {
548
+ on : {
549
+ PING : {
550
+ target : "active" ,
551
+ actions : [ "onPing" ] ,
552
+ } ,
553
+ } ,
554
+ } ,
555
+ } ,
556
+ implementations : {
557
+ actions : {
558
+ onPing : actionSpy ,
559
+ } ,
560
+ } ,
561
+ } )
562
+
563
+ const { result, send } = renderMachine ( machine )
564
+
565
+ expect ( result . current . state . get ( ) ) . toBe ( "active" )
566
+
567
+ await send ( { type : "PING" } )
568
+ expect ( result . current . state . get ( ) ) . toBe ( "active" )
569
+ expect ( actionSpy ) . toHaveBeenCalledTimes ( 1 )
570
+
571
+ await send ( { type : "PING" } )
572
+ expect ( result . current . state . get ( ) ) . toBe ( "active" )
573
+ expect ( actionSpy ) . toHaveBeenCalledTimes ( 2 )
574
+ } )
575
+
576
+ test ( "event previous/current tracking" , async ( ) => {
577
+ let capturedPrevious : any = null
578
+ let capturedCurrent : any = null
579
+
580
+ const machine = createMachine < any > ( {
581
+ initialState ( ) {
582
+ return "test"
583
+ } ,
584
+ states : {
585
+ test : {
586
+ on : {
587
+ FIRST : {
588
+ target : "second" ,
589
+ } ,
590
+ SECOND : {
591
+ actions : [ "captureEvents" ] ,
592
+ } ,
593
+ } ,
594
+ } ,
595
+ second : {
596
+ on : {
597
+ THIRD : {
598
+ actions : [ "captureEvents" ] ,
599
+ } ,
600
+ } ,
601
+ } ,
602
+ } ,
603
+ implementations : {
604
+ actions : {
605
+ captureEvents ( { event } ) {
606
+ capturedPrevious = event . previous ( )
607
+ capturedCurrent = event . current ( )
608
+ } ,
609
+ } ,
610
+ } ,
611
+ } )
612
+
613
+ const { send } = renderMachine ( machine )
614
+
615
+ await send ( { type : "FIRST" , data : "first-data" } )
616
+ await send ( { type : "THIRD" , data : "third-data" } )
617
+
618
+ expect ( capturedCurrent ) . toMatchObject ( { type : "THIRD" , data : "third-data" } )
619
+ expect ( capturedPrevious ) . toMatchObject ( { type : "FIRST" , data : "first-data" } )
620
+ } )
621
+ } )
0 commit comments