99 PerformanceResourceTiming ,
1010 createHistogram ,
1111 monitorEventLoopDelay ,
12+ eventLoopUtilization ,
13+ timerify ,
1214 constants ,
1315} from 'node:perf_hooks' ;
1416import {
@@ -32,6 +34,8 @@ export const testPerformanceExports = {
3234 ok ( PerformanceResourceTiming ) ;
3335 ok ( createHistogram ) ;
3436 ok ( monitorEventLoopDelay ) ;
37+ ok ( eventLoopUtilization ) ;
38+ ok ( timerify ) ;
3539 ok ( constants ) ;
3640
3741 // Test that performance is an instance of Performance
@@ -42,17 +46,37 @@ export const testPerformanceExports = {
4246export const testPerformanceConstants = {
4347 test ( ) {
4448 deepStrictEqual ( constants , {
45- NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE : 16 ,
46- NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY : 32 ,
47- NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED : 2 ,
48- NODE_PERFORMANCE_GC_FLAGS_FORCED : 4 ,
49- NODE_PERFORMANCE_GC_FLAGS_NO : 0 ,
50- NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE : 64 ,
51- NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING : 8 ,
52- NODE_PERFORMANCE_GC_INCREMENTAL : 8 ,
49+ // GC type constants
5350 NODE_PERFORMANCE_GC_MAJOR : 4 ,
5451 NODE_PERFORMANCE_GC_MINOR : 1 ,
52+ NODE_PERFORMANCE_GC_INCREMENTAL : 8 ,
5553 NODE_PERFORMANCE_GC_WEAKCB : 16 ,
54+
55+ // GC flags constants
56+ NODE_PERFORMANCE_GC_FLAGS_NO : 0 ,
57+ NODE_PERFORMANCE_GC_FLAGS_CONSTRUCT_RETAINED : 2 ,
58+ NODE_PERFORMANCE_GC_FLAGS_FORCED : 4 ,
59+ NODE_PERFORMANCE_GC_FLAGS_SYNCHRONOUS_PHANTOM_PROCESSING : 8 ,
60+ NODE_PERFORMANCE_GC_FLAGS_ALL_AVAILABLE_GARBAGE : 16 ,
61+ NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY : 32 ,
62+ NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE : 64 ,
63+
64+ // Entry type constants
65+ NODE_PERFORMANCE_ENTRY_TYPE_GC : 0 ,
66+ NODE_PERFORMANCE_ENTRY_TYPE_HTTP : 1 ,
67+ NODE_PERFORMANCE_ENTRY_TYPE_HTTP2 : 2 ,
68+ NODE_PERFORMANCE_ENTRY_TYPE_NET : 3 ,
69+ NODE_PERFORMANCE_ENTRY_TYPE_DNS : 4 ,
70+
71+ // Milestone constants
72+ NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN_TIMESTAMP : 0 ,
73+ NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN : 1 ,
74+ NODE_PERFORMANCE_MILESTONE_ENVIRONMENT : 2 ,
75+ NODE_PERFORMANCE_MILESTONE_NODE_START : 3 ,
76+ NODE_PERFORMANCE_MILESTONE_V8_START : 4 ,
77+ NODE_PERFORMANCE_MILESTONE_LOOP_START : 5 ,
78+ NODE_PERFORMANCE_MILESTONE_LOOP_EXIT : 6 ,
79+ NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE : 7 ,
5680 } ) ;
5781 ok ( Object . isFrozen ( constants ) ) ;
5882 } ,
@@ -80,13 +104,205 @@ export const testPerformanceBasicFunctionality = {
80104
81105export const testUnimplementedMethods = {
82106 test ( ) {
107+ // These methods throw because they cannot be meaningfully stubbed
83108 throws ( ( ) => createHistogram ( ) , { message : / i s n o t i m p l e m e n t e d / } ) ;
84109 throws ( ( ) => monitorEventLoopDelay ( ) , {
85110 message : / i s n o t i m p l e m e n t e d / ,
86111 } ) ;
87112 } ,
88113} ;
89114
115+ export const testStandaloneEventLoopUtilization = {
116+ test ( ) {
117+ // eventLoopUtilization returns stub values for compatibility
118+ const result = eventLoopUtilization ( ) ;
119+ ok ( typeof result === 'object' , 'should return an object' ) ;
120+ strictEqual ( result . idle , 0 , 'idle should be 0' ) ;
121+ strictEqual ( result . active , 0 , 'active should be 0' ) ;
122+ strictEqual ( result . utilization , 0 , 'utilization should be 0' ) ;
123+
124+ // Should accept optional parameters without error
125+ const result2 = eventLoopUtilization ( result ) ;
126+ ok (
127+ typeof result2 === 'object' ,
128+ 'should return an object with one argument'
129+ ) ;
130+
131+ const result3 = eventLoopUtilization ( result , result2 ) ;
132+ ok (
133+ typeof result3 === 'object' ,
134+ 'should return an object with two arguments'
135+ ) ;
136+ } ,
137+ } ;
138+
139+ export const testStandaloneTimerify = {
140+ test ( ) {
141+ // timerify returns the function as-is (stub behavior)
142+ const testFn = ( ) => 42 ;
143+ const timerified = timerify ( testFn ) ;
144+
145+ strictEqual ( timerified , testFn , 'timerify should return the same function' ) ;
146+ strictEqual ( timerified ( ) , 42 , 'timerified function should work correctly' ) ;
147+ } ,
148+ } ;
149+
150+ export const testPerformanceNodeTiming = {
151+ test ( ) {
152+ // nodeTiming is a Node.js-specific property available on both
153+ // the perf_hooks performance export and globalThis.performance
154+ ok (
155+ perfHooksPerformance . nodeTiming ,
156+ 'nodeTiming should exist on perf_hooks'
157+ ) ;
158+ ok (
159+ globalThis . performance . nodeTiming ,
160+ 'nodeTiming should exist on globalThis'
161+ ) ;
162+
163+ const nodeTiming = perfHooksPerformance . nodeTiming ;
164+ strictEqual ( nodeTiming . name , 'node' , 'name should be "node"' ) ;
165+ strictEqual ( nodeTiming . entryType , 'node' , 'entryType should be "node"' ) ;
166+ strictEqual (
167+ typeof nodeTiming . startTime ,
168+ 'number' ,
169+ 'startTime should be a number'
170+ ) ;
171+ strictEqual (
172+ typeof nodeTiming . duration ,
173+ 'number' ,
174+ 'duration should be a number'
175+ ) ;
176+ strictEqual (
177+ typeof nodeTiming . nodeStart ,
178+ 'number' ,
179+ 'nodeStart should be a number'
180+ ) ;
181+ strictEqual (
182+ typeof nodeTiming . v8Start ,
183+ 'number' ,
184+ 'v8Start should be a number'
185+ ) ;
186+ strictEqual (
187+ typeof nodeTiming . bootstrapComplete ,
188+ 'number' ,
189+ 'bootstrapComplete should be a number'
190+ ) ;
191+ strictEqual (
192+ typeof nodeTiming . environment ,
193+ 'number' ,
194+ 'environment should be a number'
195+ ) ;
196+ strictEqual (
197+ typeof nodeTiming . loopStart ,
198+ 'number' ,
199+ 'loopStart should be a number'
200+ ) ;
201+ strictEqual (
202+ typeof nodeTiming . loopExit ,
203+ 'number' ,
204+ 'loopExit should be a number'
205+ ) ;
206+ strictEqual (
207+ typeof nodeTiming . idleTime ,
208+ 'number' ,
209+ 'idleTime should be a number'
210+ ) ;
211+
212+ // uvMetricsInfo should return an object with libuv metrics (stub values)
213+ ok (
214+ typeof nodeTiming . uvMetricsInfo === 'object' ,
215+ 'uvMetricsInfo should be an object'
216+ ) ;
217+ strictEqual (
218+ nodeTiming . uvMetricsInfo . loopCount ,
219+ 0 ,
220+ 'uvMetricsInfo.loopCount should be 0'
221+ ) ;
222+ strictEqual (
223+ nodeTiming . uvMetricsInfo . events ,
224+ 0 ,
225+ 'uvMetricsInfo.events should be 0'
226+ ) ;
227+ strictEqual (
228+ nodeTiming . uvMetricsInfo . eventsWaiting ,
229+ 0 ,
230+ 'uvMetricsInfo.eventsWaiting should be 0'
231+ ) ;
232+
233+ // Verify that nodeTiming properties are instance (own) properties, not prototype properties
234+ // This matches Node.js behavior where Reflect.ownKeys(performance.nodeTiming) includes
235+ // all properties like nodeStart, v8Start, etc.
236+ const ownKeys = Reflect . ownKeys ( nodeTiming ) ;
237+ ok ( ownKeys . includes ( 'nodeStart' ) , 'nodeStart should be an own property' ) ;
238+ ok ( ownKeys . includes ( 'v8Start' ) , 'v8Start should be an own property' ) ;
239+ ok (
240+ ownKeys . includes ( 'bootstrapComplete' ) ,
241+ 'bootstrapComplete should be an own property'
242+ ) ;
243+ ok (
244+ ownKeys . includes ( 'environment' ) ,
245+ 'environment should be an own property'
246+ ) ;
247+ ok ( ownKeys . includes ( 'loopStart' ) , 'loopStart should be an own property' ) ;
248+ ok ( ownKeys . includes ( 'loopExit' ) , 'loopExit should be an own property' ) ;
249+ ok ( ownKeys . includes ( 'idleTime' ) , 'idleTime should be an own property' ) ;
250+ ok (
251+ ownKeys . includes ( 'uvMetricsInfo' ) ,
252+ 'uvMetricsInfo should be an own property'
253+ ) ;
254+
255+ // Verify prototype only has constructor and toJSON (matching Node.js behavior)
256+ const protoKeys = Reflect . ownKeys ( Object . getPrototypeOf ( nodeTiming ) ) ;
257+ ok ( protoKeys . includes ( 'constructor' ) , 'prototype should have constructor' ) ;
258+ ok ( protoKeys . includes ( 'toJSON' ) , 'prototype should have toJSON' ) ;
259+
260+ // toJSON should work and include uvMetricsInfo
261+ ok ( typeof nodeTiming . toJSON === 'function' , 'toJSON should be a function' ) ;
262+ const json = nodeTiming . toJSON ( ) ;
263+ ok ( typeof json === 'object' , 'toJSON should return an object' ) ;
264+ ok (
265+ typeof json . uvMetricsInfo === 'object' ,
266+ 'toJSON should include uvMetricsInfo'
267+ ) ;
268+ } ,
269+ } ;
270+
271+ export const testPerformanceEventLoopUtilization = {
272+ test ( ) {
273+ // performance.eventLoopUtilization() should return stub values (not throw)
274+ const result = perfHooksPerformance . eventLoopUtilization ( ) ;
275+ ok ( typeof result === 'object' , 'should return an object' ) ;
276+ strictEqual ( result . idle , 0 , 'idle should be 0' ) ;
277+ strictEqual ( result . active , 0 , 'active should be 0' ) ;
278+ strictEqual ( result . utilization , 0 , 'utilization should be 0' ) ;
279+ } ,
280+ } ;
281+
282+ export const testPerformanceTimerify = {
283+ test ( ) {
284+ // performance.timerify() should return the function as-is (stub behavior)
285+ const testFn = ( ) => 'test' ;
286+ const timerified = perfHooksPerformance . timerify ( testFn ) ;
287+
288+ strictEqual ( timerified , testFn , 'timerify should return the same function' ) ;
289+ strictEqual (
290+ timerified ( ) ,
291+ 'test' ,
292+ 'timerified function should work correctly'
293+ ) ;
294+ } ,
295+ } ;
296+
297+ export const testPerformanceMarkResourceTiming = {
298+ test ( ) {
299+ // markResourceTiming should not throw (no-op stub)
300+ perfHooksPerformance . markResourceTiming ( ) ;
301+ // If we get here without throwing, the test passes
302+ ok ( true , 'markResourceTiming should not throw' ) ;
303+ } ,
304+ } ;
305+
90306export const testPerformanceMark = {
91307 test ( ) {
92308 perfHooksPerformance . clearMarks ( ) ;
0 commit comments