1
1
import { describe , expect , it , vi , beforeEach , afterEach } from 'vitest'
2
2
3
- import { mountSuspended } from '@nuxt/test-utils/runtime'
3
+ import { mockNuxtImport , mountSuspended } from '@nuxt/test-utils/runtime'
4
4
import { satisfies } from 'semver'
5
5
import { version as nuxtVersion } from 'nuxt/package.json'
6
6
@@ -25,11 +25,13 @@ import ComponentWithAttrs from '~/components/ComponentWithAttrs.vue'
25
25
import ComponentWithReservedProp from '~/components/ComponentWithReservedProp.vue'
26
26
import ComponentWithReservedState from '~/components/ComponentWithReservedState.vue'
27
27
import ComponentWithImports from '~/components/ComponentWithImports.vue'
28
+ import GenericStateComponent from '~/components/GenericStateComponent.vue'
28
29
29
30
import { BoundAttrs } from '#components'
30
31
import DirectiveComponent from '~/components/DirectiveComponent.vue'
31
32
import CustomComponent from '~/components/CustomComponent.vue'
32
33
import WrapperElement from '~/components/WrapperElement.vue'
34
+ import WatcherComponent from '~/components/WatcherComponent.vue'
33
35
34
36
const formats = {
35
37
ExportDefaultComponent,
@@ -454,3 +456,81 @@ it('element should be changed', async () => {
454
456
455
457
expect ( component . element . tagName ) . toBe ( 'SPAN' )
456
458
} )
459
+
460
+ describe ( 'composable state isolation' , ( ) => {
461
+ const { useCounterMock } = vi . hoisted ( ( ) => {
462
+ return {
463
+ useCounterMock : vi . fn ( ( ) => {
464
+ return {
465
+ isPositive : ( ) : boolean => false ,
466
+ }
467
+ } ) ,
468
+ }
469
+ } )
470
+
471
+ mockNuxtImport ( 'useCounter' , ( ) => {
472
+ return useCounterMock
473
+ } )
474
+
475
+ it ( 'shows zero or negative state by default' , async ( ) => {
476
+ const component = await mountSuspended ( GenericStateComponent )
477
+ expect ( component . text ( ) ) . toMatchInlineSnapshot ( '"Zero or negative count"' )
478
+ } )
479
+
480
+ it ( 'shows positive state when counter is positive' , async ( ) => {
481
+ useCounterMock . mockRestore ( )
482
+ useCounterMock . mockImplementation ( ( ) => ( {
483
+ isPositive : ( ) => true ,
484
+ } ) )
485
+ const component = await mountSuspended ( GenericStateComponent )
486
+ expect ( component . text ( ) ) . toMatchInlineSnapshot ( '"Positive count"' )
487
+ } )
488
+ } )
489
+
490
+ describe ( 'watcher cleanup validation' , ( ) => {
491
+ let watcherCallCount = 0
492
+ beforeEach ( ( ) => {
493
+ watcherCallCount = 0
494
+ // Mock console.log to count watcher calls
495
+ vi . spyOn ( console , 'log' ) . mockImplementation ( ( message ) => {
496
+ if ( typeof message === 'string' && message . includes ( 'Test state has changed' ) ) {
497
+ watcherCallCount ++
498
+ }
499
+ } )
500
+ } )
501
+
502
+ afterEach ( ( ) => {
503
+ vi . restoreAllMocks ( )
504
+ } )
505
+
506
+ it ( 'mounts component in test 1' , async ( ) => {
507
+ await mountSuspended ( WatcherComponent , {
508
+ props : {
509
+ title : 'Component 1' ,
510
+ } ,
511
+ } )
512
+
513
+ expect ( watcherCallCount ) . toBe ( 0 ) // No state changes yet
514
+ } )
515
+
516
+ it ( 'mounts component in test 2 and validates watcher cleanup' , async ( ) => {
517
+ await mountSuspended ( WatcherComponent , {
518
+ props : {
519
+ title : 'Component 2' ,
520
+ } ,
521
+ } )
522
+
523
+ // Reset counter after mounting
524
+ watcherCallCount = 0
525
+
526
+ // Change the state - this should only trigger Component 2's watcher
527
+ const state = useState ( 'testState' )
528
+ state . value = 'new state'
529
+
530
+ await nextTick ( )
531
+
532
+ // Before the fix: would see 2 watcher calls (Component 1 and Component 2)
533
+ // After the fix: should only see 1 watcher call (Component 2 only)
534
+ expect ( watcherCallCount ) . toBe ( 1 )
535
+ } )
536
+ } )
0 commit comments