11import { act , renderHook } from '@testing-library/react' ;
22import { vi } from 'vitest' ;
33
4- import { renderHookServer } from '@/tests' ;
4+ import { createTrigger , renderHookServer } from '@/tests' ;
55
66import { usePerformanceObserver } from './usePerformanceObserver' ;
77
8- const mockObserve = vi . fn ( ) ;
9- const mockDisconnect = vi . fn ( ) ;
8+ const PERFORMANCE_OBSERVER_KEY = 'performance-observer' ;
9+ const trigger = createTrigger < string , PerformanceObserverCallback > ( ) ;
1010
11- let observerCallback : PerformanceObserverCallback ;
11+ const mockPerformanceObserverObserve = vi . fn ( ) ;
12+ const mockPerformanceObserverDisconnect = vi . fn ( ) ;
1213
1314const createMockPerformanceEntry = (
1415 name = 'test-entry' ,
@@ -25,17 +26,26 @@ const createMockPerformanceEntry = (
2526
2627class MockPerformanceObserver {
2728 constructor ( callback : PerformanceObserverCallback ) {
28- observerCallback = callback ;
29+ this . callback = callback ;
2930 }
3031
31- observe = ( options ?: PerformanceObserverInit ) => mockObserve ( options ) ;
32- disconnect = ( ) => mockDisconnect ( ) ;
32+ callback : PerformanceObserverCallback ;
33+
34+ observe = ( options ?: PerformanceObserverInit ) => {
35+ trigger . add ( PERFORMANCE_OBSERVER_KEY , this . callback ) ;
36+ mockPerformanceObserverObserve ( options ) ;
37+ } ;
38+
39+ disconnect = ( ) => {
40+ trigger . delete ( PERFORMANCE_OBSERVER_KEY ) ;
41+ mockPerformanceObserverDisconnect ( ) ;
42+ } ;
3343}
3444
3545globalThis . PerformanceObserver = MockPerformanceObserver as any ;
3646
37- afterEach ( ( ) => {
38- vi . clearAllMocks ( ) ;
47+ beforeEach ( ( ) => {
48+ trigger . clear ( ) ;
3949} ) ;
4050
4151it ( 'Should use performance observer' , ( ) => {
@@ -45,6 +55,7 @@ it('Should use performance observer', () => {
4555 expect ( result . current . entries ) . toEqual ( [ ] ) ;
4656 expect ( result . current . start ) . toBeTypeOf ( 'function' ) ;
4757 expect ( result . current . stop ) . toBeTypeOf ( 'function' ) ;
58+ expect ( result . current . observer ) . toBeUndefined ( ) ;
4859} ) ;
4960
5061it ( 'Should use performance observer on server side' , ( ) => {
@@ -54,113 +65,77 @@ it('Should use performance observer on server side', () => {
5465 expect ( result . current . entries ) . toEqual ( [ ] ) ;
5566 expect ( result . current . start ) . toBeTypeOf ( 'function' ) ;
5667 expect ( result . current . stop ) . toBeTypeOf ( 'function' ) ;
68+ expect ( result . current . observer ) . toBeUndefined ( ) ;
5769} ) ;
5870
59- it ( 'Should start observing when immediate is true ' , ( ) => {
71+ it ( 'Should start observing when immediate' , ( ) => {
6072 renderHook ( ( ) => usePerformanceObserver ( { entryTypes : [ 'measure' ] , immediate : true } ) ) ;
6173
62- expect ( mockObserve ) . toHaveBeenCalledOnce ( ) ;
63- expect ( mockObserve ) . toHaveBeenCalledWith ( expect . objectContaining ( { entryTypes : [ 'measure' ] } ) ) ;
64- } ) ;
65-
66- it ( 'Should not start observing when immediate is not set' , ( ) => {
67- renderHook ( ( ) => usePerformanceObserver ( { entryTypes : [ 'measure' ] } ) ) ;
68-
69- expect ( mockObserve ) . not . toHaveBeenCalled ( ) ;
74+ expect ( mockPerformanceObserverObserve ) . toHaveBeenCalledOnce ( ) ;
75+ expect ( mockPerformanceObserverObserve ) . toHaveBeenCalledWith (
76+ expect . objectContaining ( { entryTypes : [ 'measure' ] } )
77+ ) ;
7078} ) ;
7179
72- it ( 'Should start, stop and restart observer manually' , ( ) => {
80+ it ( 'Should restart observer manually' , ( ) => {
7381 const { result } = renderHook ( ( ) => usePerformanceObserver ( { entryTypes : [ 'measure' ] } ) ) ;
7482
75- expect ( mockObserve ) . not . toHaveBeenCalled ( ) ;
76-
77- act ( ( ) => result . current . start ( ) ) ;
83+ const entry = createMockPerformanceEntry ( ) ;
84+ const entryList = {
85+ getEntries : ( ) => [ entry ]
86+ } as PerformanceObserverEntryList ;
7887
79- expect ( mockObserve ) . toHaveBeenCalledOnce ( ) ;
88+ act ( ( ) => {
89+ result . current . start ( ) ;
90+ trigger . callback ( PERFORMANCE_OBSERVER_KEY , entryList , result . current . observer ! ) ;
91+ } ) ;
8092
81- act ( ( ) => result . current . stop ( ) ) ;
93+ expect ( result . current . entries ) . toEqual ( [ entry ] ) ;
8294
83- expect ( mockDisconnect ) . toHaveBeenCalledOnce ( ) ;
95+ expect ( mockPerformanceObserverObserve ) . toHaveBeenCalledOnce ( ) ;
8496
85- act ( ( ) => result . current . start ( ) ) ;
97+ act ( result . current . stop ) ;
8698
87- expect ( mockObserve ) . toHaveBeenCalledTimes ( 2 ) ;
99+ expect ( mockPerformanceObserverDisconnect ) . toHaveBeenCalledOnce ( ) ;
88100} ) ;
89101
90102it ( 'Should call callback when performance entries are observed' , ( ) => {
91103 const callback = vi . fn ( ) ;
92104
93- renderHook ( ( ) => usePerformanceObserver ( { entryTypes : [ 'measure' ] , immediate : true } , callback ) ) ;
94-
95- const entry = createMockPerformanceEntry ( ) ;
96- const entryList = { getEntries : ( ) => [ entry ] } as PerformanceObserverEntryList ;
97-
98- act ( ( ) => observerCallback ( entryList , { } as PerformanceObserver ) ) ;
99-
100- expect ( callback ) . toHaveBeenCalledOnce ( ) ;
101- expect ( callback ) . toHaveBeenCalledWith ( entryList , expect . any ( Object ) ) ;
102- } ) ;
103-
104- it ( 'Should update entries state when performance entries are observed' , ( ) => {
105105 const { result } = renderHook ( ( ) =>
106- usePerformanceObserver ( { entryTypes : [ 'measure' ] , immediate : true } )
106+ usePerformanceObserver ( { entryTypes : [ 'measure' ] } , callback )
107107 ) ;
108108
109- expect ( result . current . entries ) . toEqual ( [ ] ) ;
109+ const entry = createMockPerformanceEntry ( ) ;
110+ const entryList = {
111+ getEntries : ( ) => [ entry ]
112+ } as PerformanceObserverEntryList ;
110113
111- const entry = createMockPerformanceEntry ( 'paint' , 'measure' , 0 , 50 ) ;
112- const entryList = { getEntries : ( ) => [ entry ] } as PerformanceObserverEntryList ;
114+ act ( ( ) => {
115+ result . current . start ( ) ;
116+ trigger . callback ( PERFORMANCE_OBSERVER_KEY , entryList , result . current . observer ) ;
117+ } ) ;
113118
114- act ( ( ) => observerCallback ( entryList , { } as PerformanceObserver ) ) ;
119+ expect ( result . current . observer ) . toBeTruthy ( ) ;
115120
116121 expect ( result . current . entries ) . toEqual ( [ entry ] ) ;
117- } ) ;
118-
119- it ( 'Should disconnect observer on unmount' , ( ) => {
120- const { unmount } = renderHook ( ( ) =>
121- usePerformanceObserver ( { entryTypes : [ 'measure' ] , immediate : true } )
122- ) ;
123-
124- unmount ( ) ;
125-
126- expect ( mockDisconnect ) . toHaveBeenCalledOnce ( ) ;
122+ expect ( callback ) . toHaveBeenCalledOnce ( ) ;
127123} ) ;
128124
129125it ( 'Should not disconnect observer on unmount when not started' , ( ) => {
130126 const { unmount } = renderHook ( ( ) => usePerformanceObserver ( { entryTypes : [ 'measure' ] } ) ) ;
131127
132128 unmount ( ) ;
133129
134- expect ( mockDisconnect ) . not . toHaveBeenCalled ( ) ;
135- } ) ;
136-
137- it ( 'Should work without callback' , ( ) => {
138- const { result } = renderHook ( ( ) =>
139- usePerformanceObserver ( { entryTypes : [ 'measure' ] , immediate : true } )
140- ) ;
141-
142- const entry = createMockPerformanceEntry ( ) ;
143- const entryList = { getEntries : ( ) => [ entry ] } as PerformanceObserverEntryList ;
144-
145- act ( ( ) => observerCallback ( entryList , { } as PerformanceObserver ) ) ;
146-
147- expect ( result . current . entries ) . toEqual ( [ entry ] ) ;
130+ expect ( mockPerformanceObserverDisconnect ) . not . toHaveBeenCalled ( ) ;
148131} ) ;
149132
150- it ( 'Should update entries state with multiple entries ' , ( ) => {
151- const { result } = renderHook ( ( ) =>
133+ it ( 'Should disconnect observer on unmount ' , ( ) => {
134+ const { unmount } = renderHook ( ( ) =>
152135 usePerformanceObserver ( { entryTypes : [ 'measure' ] , immediate : true } )
153136 ) ;
154137
155- const entries = [
156- createMockPerformanceEntry ( 'first-paint' , 'paint' , 0 , 10 ) ,
157- createMockPerformanceEntry ( 'api-call' , 'measure' , 100 , 200 ) ,
158- createMockPerformanceEntry ( 'lcp' , 'largest-contentful-paint' , 50 , 150 )
159- ] ;
160- const entryList = { getEntries : ( ) => entries } as PerformanceObserverEntryList ;
161-
162- act ( ( ) => observerCallback ( entryList , { } as PerformanceObserver ) ) ;
138+ unmount ( ) ;
163139
164- expect ( result . current . entries ) . toEqual ( entries ) ;
165- expect ( result . current . entries ) . toHaveLength ( 3 ) ;
140+ expect ( mockPerformanceObserverDisconnect ) . toHaveBeenCalledOnce ( ) ;
166141} ) ;
0 commit comments