@@ -41,7 +41,26 @@ describe('PerformanceObserverSink', () => {
4141 expect ( ( ) => new PerformanceObserverSink ( options ) ) . not . toThrow ( ) ;
4242 } ) ;
4343
44- it ( 'internal PerformanceObserver should process observed entries' , async ( ) => {
44+ it ( 'unsubscribe stops observing performance entries' , async ( ) => {
45+ const observer = new PerformanceObserverSink ( {
46+ ...options ,
47+ flushThreshold : 1 ,
48+ } ) ;
49+
50+ observer . subscribe ( ) ;
51+ performance . mark ( 'subscribed-mark1' ) ;
52+ performance . mark ( 'subscribed-mark2' ) ;
53+ await awaitObserverCallback ( ) ;
54+ expect ( encode ) . toHaveBeenCalledTimes ( 2 ) ;
55+
56+ observer . unsubscribe ( ) ;
57+ performance . mark ( 'unsubscribed-mark1' ) ;
58+ performance . mark ( 'unsubscribed-mark2' ) ;
59+ await awaitObserverCallback ( ) ;
60+ expect ( encode ) . toHaveBeenCalledTimes ( 2 ) ;
61+ } ) ;
62+
63+ it ( 'observes and encodes performance entries' , async ( ) => {
4564 const observer = new PerformanceObserverSink ( options ) ;
4665 observer . subscribe ( ) ;
4766
@@ -64,23 +83,26 @@ describe('PerformanceObserverSink', () => {
6483 ) ;
6584 } ) ;
6685
67- it ( 'internal PerformanceObserver calls flush if flushThreshold exceeded' , async ( ) => {
86+ it ( 'handles multiple items per performance entry' , async ( ) => {
87+ const multiEncodeFn = vi . fn ( e => [
88+ `${ e . entryType } -item1` ,
89+ `${ e . entryType } item2` ,
90+ ] ) ;
6891 const observer = new PerformanceObserverSink ( {
6992 ...options ,
70- flushThreshold : 3 ,
93+ encodePerfEntry : multiEncodeFn ,
7194 } ) ;
72- observer . subscribe ( ) ;
7395
74- performance . mark ( 'test-mark1' ) ;
75- performance . mark ( 'test-mark2' ) ;
76- performance . mark ( 'test-mark3' ) ;
96+ observer . subscribe ( ) ;
7797
98+ performance . mark ( 'test-mark' ) ;
7899 await awaitObserverCallback ( ) ;
100+ observer . flush ( ) ;
79101
80- expect ( encode ) . toHaveBeenCalledTimes ( 3 ) ;
102+ expect ( sink . getWrittenItems ( ) ) . toHaveLength ( 2 ) ;
81103 } ) ;
82104
83- it ( 'flush flushes observed entries when subscribed ' , async ( ) => {
105+ it ( 'successfully writes queued items to sink ' , async ( ) => {
84106 const observer = new PerformanceObserverSink ( options ) ;
85107 observer . subscribe ( ) ;
86108
@@ -96,86 +118,89 @@ describe('PerformanceObserverSink', () => {
96118 ] ) ;
97119 } ) ;
98120
99- it ( 'flush calls encode for each entry ' , async ( ) => {
121+ it ( 'observes performance entries when subscribed ' , async ( ) => {
100122 const observer = new PerformanceObserverSink ( options ) ;
123+
124+ observer . subscribe ( ) ;
125+ performance . mark ( 'test-mark-1' ) ;
126+ performance . mark ( 'test-mark-2' ) ;
127+ await awaitObserverCallback ( ) ;
128+ observer . flush ( ) ;
129+ expect ( sink . getWrittenItems ( ) ) . toHaveLength ( 2 ) ;
130+ } ) ;
131+
132+ it ( 'triggers proactive flush when threshold exceeded' , async ( ) => {
133+ const observer = new PerformanceObserverSink ( {
134+ ...options ,
135+ flushThreshold : 3 ,
136+ } ) ;
101137 observer . subscribe ( ) ;
102138
103139 performance . mark ( 'test-mark1' ) ;
104140 performance . mark ( 'test-mark2' ) ;
141+ performance . mark ( 'test-mark3' ) ;
105142
106143 await awaitObserverCallback ( ) ;
107- observer . flush ( ) ;
108144
109- expect ( encode ) . toHaveBeenCalledWith (
110- expect . objectContaining ( {
111- name : 'test-mark1' ,
112- entryType : 'mark' ,
113- } ) ,
114- ) ;
115- expect ( encode ) . toHaveBeenCalledWith (
116- expect . objectContaining ( {
117- name : 'test-mark2' ,
118- entryType : 'mark' ,
119- } ) ,
120- ) ;
145+ expect ( encode ) . toHaveBeenCalledTimes ( 3 ) ;
121146 } ) ;
122147
123- it ( 'unsubscribe stops observing performance entries' , async ( ) => {
148+ it ( 'keeps items in queue when sink write fails' , async ( ) => {
149+ const failingSink = new MockSink ( ) ;
150+ failingSink . open ( ) ;
151+ failingSink . write . mockImplementation ( ( ) => {
152+ throw new Error ( 'Sink write failed' ) ;
153+ } ) ;
154+
124155 const observer = new PerformanceObserverSink ( {
125- ...options ,
156+ sink : failingSink ,
157+ encodePerfEntry : encode ,
126158 flushThreshold : 1 ,
159+ maxQueueSize : 10 ,
127160 } ) ;
128161
129162 observer . subscribe ( ) ;
130- performance . mark ( 'subscribed-mark1' ) ;
131- performance . mark ( 'subscribed-mark2' ) ;
132- await awaitObserverCallback ( ) ;
133- expect ( encode ) . toHaveBeenCalledTimes ( 2 ) ;
134163
135- observer . unsubscribe ( ) ;
136- performance . mark ( 'unsubscribed-mark1' ) ;
137- performance . mark ( 'unsubscribed-mark2' ) ;
164+ performance . mark ( 'test-mark' ) ;
138165 await awaitObserverCallback ( ) ;
139- expect ( encode ) . toHaveBeenCalledTimes ( 2 ) ;
166+
167+ const stats = observer . getStats ( ) ;
168+ expect ( stats . dropped ) . toBe ( 0 ) ;
169+ expect ( stats . queued ) . toBe ( 1 ) ;
140170 } ) ;
141171
142- it ( 'should observe performance entries and write them to the sink on flush' , async ( ) => {
143- const observer = new PerformanceObserverSink ( options ) ;
172+ it ( 'keeps items in queue when sink is closed during flush' , async ( ) => {
173+ const closedSink = new MockSink ( ) ;
174+ closedSink . open ( ) ;
175+ closedSink . close ( ) ;
176+
177+ const observer = new PerformanceObserverSink ( {
178+ sink : closedSink ,
179+ encodePerfEntry : encode ,
180+ } ) ;
144181
145182 observer . subscribe ( ) ;
183+
146184 performance . mark ( 'test-mark' ) ;
147185 await awaitObserverCallback ( ) ;
148- observer . flush ( ) ;
149- expect ( sink . getWrittenItems ( ) ) . toHaveLength ( 1 ) ;
150- } ) ;
151186
152- it ( 'should observe performance entries when subscribed' , async ( ) => {
153- const observer = new PerformanceObserverSink ( options ) ;
154-
155- observer . subscribe ( ) ;
156- performance . mark ( 'test-mark-1' ) ;
157- performance . mark ( 'test-mark-2' ) ;
158- await awaitObserverCallback ( ) ;
159- observer . flush ( ) ;
160- expect ( sink . getWrittenItems ( ) ) . toHaveLength ( 2 ) ;
187+ const stats = observer . getStats ( ) ;
188+ expect ( stats . queued ) . toBe ( 1 ) ;
189+ expect ( stats . dropped ) . toBe ( 0 ) ;
161190 } ) ;
162191
163- it ( 'handles multiple encoded items per performance entry' , async ( ) => {
164- const multiEncodeFn = vi . fn ( e => [
165- `${ e . entryType } -item1` ,
166- `${ e . entryType } item2` ,
167- ] ) ;
192+ it ( 'handles flush errors gracefully without losing items' , async ( ) => {
168193 const observer = new PerformanceObserverSink ( {
169- ... options ,
170- encodePerfEntry : multiEncodeFn ,
194+ sink ,
195+ encodePerfEntry : encode ,
171196 } ) ;
172197
173198 observer . subscribe ( ) ;
174199
175200 performance . mark ( 'test-mark' ) ;
176201 await awaitObserverCallback ( ) ;
177- observer . flush ( ) ;
178202
179- expect ( sink . getWrittenItems ( ) ) . toHaveLength ( 2 ) ;
203+ const stats = observer . getStats ( ) ;
204+ expect ( stats . queued ) . toBeGreaterThanOrEqual ( 0 ) ;
180205 } ) ;
181206} ) ;
0 commit comments