@@ -226,4 +226,116 @@ describe('ComponentTree', () => {
226226 expect ( tree . getNode ( 6 ) ! . type ) . toBe ( 'profiler' ) ;
227227 expect ( tree . getNode ( 7 ) ! . type ) . toBe ( 'suspense' ) ;
228228 } ) ;
229+
230+ describe ( 'UPDATE_ERRORS_OR_WARNINGS' , ( ) => {
231+ it ( 'should store error and warning counts on nodes' , ( ) => {
232+ const ops = buildOps ( 1 , 100 , [ 'App' , 'Form' ] , ( s ) => [
233+ ...addOp ( 1 , 5 , 0 , s ( 'App' ) ) ,
234+ ...addOp ( 2 , 5 , 1 , s ( 'Form' ) ) ,
235+ ] ) ;
236+ tree . applyOperations ( ops ) ;
237+
238+ // Operation type 5 = UPDATE_ERRORS_OR_WARNINGS: opcode, id, numErrors, numWarnings
239+ const errorOps = [ 1 , 100 , 0 , 5 , 2 , 1 , 3 ] ;
240+ tree . applyOperations ( errorOps ) ;
241+
242+ const node = tree . getNode ( 2 ) ;
243+ expect ( node ) . toBeDefined ( ) ;
244+ expect ( node ! . errors ) . toBe ( 1 ) ;
245+ expect ( node ! . warnings ) . toBe ( 3 ) ;
246+
247+ // App should still have zero counts
248+ expect ( tree . getNode ( 1 ) ! . errors ) . toBe ( 0 ) ;
249+ expect ( tree . getNode ( 1 ) ! . warnings ) . toBe ( 0 ) ;
250+ } ) ;
251+
252+ it ( 'should update counts when a second errors operation arrives' , ( ) => {
253+ const ops = buildOps ( 1 , 100 , [ 'App' ] , ( s ) => [
254+ ...addOp ( 1 , 5 , 0 , s ( 'App' ) ) ,
255+ ] ) ;
256+ tree . applyOperations ( ops ) ;
257+
258+ tree . applyOperations ( [ 1 , 100 , 0 , 5 , 1 , 2 , 0 ] ) ;
259+ expect ( tree . getNode ( 1 ) ! . errors ) . toBe ( 2 ) ;
260+ expect ( tree . getNode ( 1 ) ! . warnings ) . toBe ( 0 ) ;
261+
262+ // Counts are replaced, not accumulated
263+ tree . applyOperations ( [ 1 , 100 , 0 , 5 , 1 , 0 , 5 ] ) ;
264+ expect ( tree . getNode ( 1 ) ! . errors ) . toBe ( 0 ) ;
265+ expect ( tree . getNode ( 1 ) ! . warnings ) . toBe ( 5 ) ;
266+ } ) ;
267+
268+ it ( 'should initialize errors and warnings to zero' , ( ) => {
269+ const ops = buildOps ( 1 , 100 , [ 'App' ] , ( s ) => [
270+ ...addOp ( 1 , 5 , 0 , s ( 'App' ) ) ,
271+ ] ) ;
272+ tree . applyOperations ( ops ) ;
273+
274+ expect ( tree . getNode ( 1 ) ! . errors ) . toBe ( 0 ) ;
275+ expect ( tree . getNode ( 1 ) ! . warnings ) . toBe ( 0 ) ;
276+ } ) ;
277+ } ) ;
278+
279+ describe ( 'getComponentsWithErrorsOrWarnings' , ( ) => {
280+ it ( 'should return only components with non-zero errors or warnings' , ( ) => {
281+ const ops = buildOps ( 1 , 100 , [ 'App' , 'Form' , 'Button' ] , ( s ) => [
282+ ...addOp ( 1 , 5 , 0 , s ( 'App' ) ) ,
283+ ...addOp ( 2 , 5 , 1 , s ( 'Form' ) ) ,
284+ ...addOp ( 3 , 5 , 1 , s ( 'Button' ) ) ,
285+ ] ) ;
286+ tree . applyOperations ( ops ) ;
287+
288+ // Give Form errors and Button warnings
289+ tree . applyOperations ( [ 1 , 100 , 0 , 5 , 2 , 3 , 0 ] ) ; // Form: 3 errors
290+ tree . applyOperations ( [ 1 , 100 , 0 , 5 , 3 , 0 , 2 ] ) ; // Button: 2 warnings
291+
292+ // Need to call getTree first to assign labels
293+ tree . getTree ( ) ;
294+
295+ const results = tree . getComponentsWithErrorsOrWarnings ( ) ;
296+ expect ( results ) . toHaveLength ( 2 ) ;
297+ expect ( results . map ( ( r ) => r . displayName ) . sort ( ) ) . toEqual ( [ 'Button' , 'Form' ] ) ;
298+
299+ const form = results . find ( ( r ) => r . displayName === 'Form' ) ! ;
300+ expect ( form . errors ) . toBe ( 3 ) ;
301+ expect ( form . warnings ) . toBeUndefined ( ) ;
302+
303+ const button = results . find ( ( r ) => r . displayName === 'Button' ) ! ;
304+ expect ( button . errors ) . toBeUndefined ( ) ;
305+ expect ( button . warnings ) . toBe ( 2 ) ;
306+ } ) ;
307+
308+ it ( 'should return empty array when no components have errors' , ( ) => {
309+ const ops = buildOps ( 1 , 100 , [ 'App' ] , ( s ) => [
310+ ...addOp ( 1 , 5 , 0 , s ( 'App' ) ) ,
311+ ] ) ;
312+ tree . applyOperations ( ops ) ;
313+
314+ tree . getTree ( ) ;
315+ const results = tree . getComponentsWithErrorsOrWarnings ( ) ;
316+ expect ( results ) . toHaveLength ( 0 ) ;
317+ } ) ;
318+ } ) ;
319+
320+ describe ( 'tree output includes error annotations' , ( ) => {
321+ it ( 'should include errors/warnings on tree nodes when non-zero' , ( ) => {
322+ const ops = buildOps ( 1 , 100 , [ 'App' , 'Form' ] , ( s ) => [
323+ ...addOp ( 1 , 5 , 0 , s ( 'App' ) ) ,
324+ ...addOp ( 2 , 5 , 1 , s ( 'Form' ) ) ,
325+ ] ) ;
326+ tree . applyOperations ( ops ) ;
327+
328+ tree . applyOperations ( [ 1 , 100 , 0 , 5 , 2 , 1 , 2 ] ) ;
329+
330+ const treeNodes = tree . getTree ( ) ;
331+ const formNode = treeNodes . find ( ( n ) => n . displayName === 'Form' ) ! ;
332+ expect ( formNode . errors ) . toBe ( 1 ) ;
333+ expect ( formNode . warnings ) . toBe ( 2 ) ;
334+
335+ // App has no errors/warnings, so they should be omitted
336+ const appNode = treeNodes . find ( ( n ) => n . displayName === 'App' ) ! ;
337+ expect ( appNode . errors ) . toBeUndefined ( ) ;
338+ expect ( appNode . warnings ) . toBeUndefined ( ) ;
339+ } ) ;
340+ } ) ;
229341} ) ;
0 commit comments