@buoy-gg/highlight-updates 7.0.27 → 7.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +102 -0
  2. package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +5 -1
  3. package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +7 -1
  4. package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +6 -0
  5. package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +26 -3
  6. package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +7 -1
  7. package/lib/commonjs/highlight-updates/components/RenderDetailView.js +40 -1
  8. package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +29 -6
  9. package/lib/commonjs/highlight-updates/components/RenderListItem.js +15 -0
  10. package/lib/commonjs/highlight-updates/components/RendersCopySettingsView.js +11 -0
  11. package/lib/commonjs/highlight-updates/components/StatsDisplay.js +3 -0
  12. package/lib/commonjs/highlight-updates/utils/CommitProfiler.js +6 -1
  13. package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +65 -3
  14. package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +15 -3
  15. package/lib/commonjs/highlight-updates/utils/RenderTracker.js +6 -1
  16. package/lib/commonjs/highlight-updates/utils/namedComposite.js +41 -0
  17. package/lib/commonjs/highlight-updates/utils/ownStateChanged.js +103 -0
  18. package/lib/commonjs/highlight-updates/utils/screenElements.js +50 -25
  19. package/lib/commonjs/highlight-updates/utils/tapMatch.js +58 -0
  20. package/lib/commonjs/highlight-updates/utils/testIDs.js +68 -0
  21. package/lib/commonjs/index.js +34 -0
  22. package/lib/module/highlight-updates/components/HighlightFilterView.js +102 -0
  23. package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +5 -1
  24. package/lib/module/highlight-updates/components/IdentifierBadge.js +7 -1
  25. package/lib/module/highlight-updates/components/IsolatedRenderList.js +6 -0
  26. package/lib/module/highlight-updates/components/ModalHeaderContent.js +26 -3
  27. package/lib/module/highlight-updates/components/RenderCauseBadge.js +7 -1
  28. package/lib/module/highlight-updates/components/RenderDetailView.js +40 -1
  29. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +29 -6
  30. package/lib/module/highlight-updates/components/RenderListItem.js +15 -0
  31. package/lib/module/highlight-updates/components/RendersCopySettingsView.js +11 -0
  32. package/lib/module/highlight-updates/components/StatsDisplay.js +3 -0
  33. package/lib/module/highlight-updates/utils/CommitProfiler.js +5 -1
  34. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +65 -3
  35. package/lib/module/highlight-updates/utils/RenderCauseDetector.js +15 -3
  36. package/lib/module/highlight-updates/utils/RenderTracker.js +6 -1
  37. package/lib/module/highlight-updates/utils/namedComposite.js +36 -0
  38. package/lib/module/highlight-updates/utils/ownStateChanged.js +98 -0
  39. package/lib/module/highlight-updates/utils/screenElements.js +50 -25
  40. package/lib/module/highlight-updates/utils/tapMatch.js +52 -0
  41. package/lib/module/highlight-updates/utils/testIDs.js +60 -0
  42. package/lib/module/index.js +5 -1
  43. package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +1 -1
  44. package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +1 -1
  45. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts +7 -1
  46. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +1 -1
  47. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +1 -1
  48. package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +1 -1
  49. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts +8 -1
  50. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +1 -1
  51. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +1 -1
  52. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +1 -1
  53. package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +1 -1
  54. package/lib/typescript/highlight-updates/components/RendersCopySettingsView.d.ts.map +1 -1
  55. package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +1 -1
  56. package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts.map +1 -1
  57. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -1
  58. package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +1 -1
  59. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts +6 -0
  60. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +1 -1
  61. package/lib/typescript/highlight-updates/utils/namedComposite.d.ts +17 -0
  62. package/lib/typescript/highlight-updates/utils/namedComposite.d.ts.map +1 -0
  63. package/lib/typescript/highlight-updates/utils/ownStateChanged.d.ts +66 -0
  64. package/lib/typescript/highlight-updates/utils/ownStateChanged.d.ts.map +1 -0
  65. package/lib/typescript/highlight-updates/utils/screenElements.d.ts.map +1 -1
  66. package/lib/typescript/highlight-updates/utils/tapMatch.d.ts +42 -0
  67. package/lib/typescript/highlight-updates/utils/tapMatch.d.ts.map +1 -0
  68. package/lib/typescript/highlight-updates/utils/testIDs.d.ts +44 -0
  69. package/lib/typescript/highlight-updates/utils/testIDs.d.ts.map +1 -0
  70. package/lib/typescript/index.d.ts +1 -0
  71. package/lib/typescript/index.d.ts.map +1 -1
  72. package/package.json +7 -7
@@ -9,6 +9,7 @@ var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactNative = require("react-native");
10
10
  var _sharedUi = require("@buoy-gg/shared-ui");
11
11
  var _IdentifierBadge = require("./IdentifierBadge");
12
+ var _testIDs = require("../utils/testIDs");
12
13
  var _jsxRuntime = require("react/jsx-runtime");
13
14
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
15
  /**
@@ -27,11 +28,13 @@ const FILTER_TYPES = ["any", "viewType", "testID", "nativeID", "component", "acc
27
28
 
28
29
  // Type picker component
29
30
  function TypePicker({
31
+ mode,
30
32
  onSelect,
31
33
  onCancel
32
34
  }) {
33
35
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
34
36
  nativeID: "__rn_buoy__type-picker",
37
+ testID: `buoy-highlight-${mode}-types`,
35
38
  style: styles.typePicker,
36
39
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
37
40
  style: styles.typePickerRow,
@@ -39,6 +42,9 @@ function TypePicker({
39
42
  const config = getFilterConfig(type);
40
43
  const IconComponent = config.icon;
41
44
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
45
+ testID: `buoy-highlight-${mode}-type-${type.toLowerCase()}`,
46
+ accessibilityRole: "button",
47
+ accessibilityLabel: config.label,
42
48
  style: [styles.typeOption, {
43
49
  backgroundColor: config.color + "15",
44
50
  borderColor: config.color + "40"
@@ -56,6 +62,9 @@ function TypePicker({
56
62
  }, type);
57
63
  })
58
64
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
65
+ testID: `buoy-highlight-${mode}-types-cancel`,
66
+ accessibilityRole: "button",
67
+ accessibilityLabel: "Cancel",
59
68
  onPress: onCancel,
60
69
  style: styles.typePickerCancel,
61
70
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
@@ -68,6 +77,7 @@ function TypePicker({
68
77
 
69
78
  // Pattern input with badge prefix
70
79
  function PatternInput({
80
+ mode,
71
81
  type,
72
82
  onSubmit,
73
83
  onCancel
@@ -81,6 +91,7 @@ function PatternInput({
81
91
  };
82
92
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
83
93
  nativeID: "__rn_buoy__pattern-input",
94
+ testID: `buoy-highlight-${mode}-input-row`,
84
95
  style: styles.patternInputContainer,
85
96
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_IdentifierBadge.IdentifierBadge, {
86
97
  type: type,
@@ -88,6 +99,7 @@ function PatternInput({
88
99
  badgeOnly: true,
89
100
  compact: true
90
101
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
102
+ testID: `buoy-highlight-${mode}-input`,
91
103
  value: value,
92
104
  onChangeText: setValue,
93
105
  onSubmitEditing: handleSubmit,
@@ -99,6 +111,9 @@ function PatternInput({
99
111
  autoCorrect: false,
100
112
  autoCapitalize: "none"
101
113
  }), value.trim() && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
114
+ testID: `buoy-highlight-${mode}-submit`,
115
+ accessibilityRole: "button",
116
+ accessibilityLabel: "Add pattern",
102
117
  onPress: handleSubmit,
103
118
  style: [styles.addPatternButton, {
104
119
  backgroundColor: config.color + "20"
@@ -110,6 +125,9 @@ function PatternInput({
110
125
  children: "Add"
111
126
  })
112
127
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
128
+ testID: `buoy-highlight-${mode}-input-cancel`,
129
+ accessibilityRole: "button",
130
+ accessibilityLabel: "Cancel",
113
131
  onPress: onCancel,
114
132
  style: styles.cancelButton,
115
133
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
@@ -122,11 +140,15 @@ function PatternInput({
122
140
 
123
141
  // Pattern chip (removable badge) - tap to remove
124
142
  function PatternChip({
143
+ mode,
125
144
  pattern,
126
145
  onRemove
127
146
  }) {
128
147
  const config = getFilterConfig(pattern.type);
129
148
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
149
+ testID: (0, _testIDs.patternChipTestID)(mode, pattern.type, pattern.value),
150
+ accessibilityRole: "button",
151
+ accessibilityLabel: `${config.label} ${pattern.value}, tap to remove`,
130
152
  style: [styles.patternChip, {
131
153
  borderColor: config.color + "40"
132
154
  }],
@@ -173,7 +195,11 @@ function DetectedCategoryBadge({
173
195
  }) {
174
196
  // "all" has its own config
175
197
  const config = filterType === "all" ? ALL_CATEGORY_CONFIG : getFilterConfig(filterType);
198
+ const handle = `buoy-highlight-detected-cat-${String(filterType).toLowerCase()}`;
176
199
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
200
+ testID: handle,
201
+ accessibilityRole: "button",
202
+ accessibilityLabel: `${config.label}, ${count}`,
177
203
  style: [styles.categoryBadge, {
178
204
  backgroundColor: config.color + "15",
179
205
  borderColor: isSelected ? config.color : config.color + "40",
@@ -193,6 +219,7 @@ function DetectedCategoryBadge({
193
219
  backgroundColor: config.color + "25"
194
220
  }],
195
221
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
222
+ testID: `${handle}-count`,
196
223
  style: [styles.categoryBadgeCount, {
197
224
  color: config.color
198
225
  }],
@@ -380,6 +407,7 @@ function HighlightFilterView({
380
407
  style: styles.container,
381
408
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
382
409
  nativeID: "__rn_buoy__filter-view",
410
+ testID: "buoy-highlight-filter-scroll",
383
411
  style: styles.scrollView,
384
412
  contentContainerStyle: styles.scrollContent,
385
413
  showsVerticalScrollIndicator: false,
@@ -394,6 +422,7 @@ function HighlightFilterView({
394
422
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
395
423
  children: "INCLUDE ONLY"
396
424
  }), filters.includePatterns.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
425
+ testID: "buoy-highlight-include-count",
397
426
  count: filters.includePatterns.length,
398
427
  color: _sharedUi.night.color.accent
399
428
  })]
@@ -403,22 +432,28 @@ function HighlightFilterView({
403
432
  }), filters.includePatterns.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
404
433
  style: styles.patternChips,
405
434
  children: filters.includePatterns.map((pattern, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(PatternChip, {
435
+ mode: "include",
406
436
  pattern: pattern,
407
437
  onRemove: () => handleRemoveIncludePattern(index)
408
438
  }, `${pattern.type}-${pattern.value}-${index}`))
409
439
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
410
440
  style: styles.addPatternRow,
411
441
  children: showIncludeTypePicker ? /*#__PURE__*/(0, _jsxRuntime.jsx)(TypePicker, {
442
+ mode: "include",
412
443
  onSelect: type => {
413
444
  setShowIncludeTypePicker(false);
414
445
  setIncludeInputType(type);
415
446
  },
416
447
  onCancel: () => setShowIncludeTypePicker(false)
417
448
  }) : includeInputType ? /*#__PURE__*/(0, _jsxRuntime.jsx)(PatternInput, {
449
+ mode: "include",
418
450
  type: includeInputType,
419
451
  onSubmit: value => handleAddIncludePattern(includeInputType, value),
420
452
  onCancel: () => setIncludeInputType(null)
421
453
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
454
+ testID: "buoy-highlight-include-add",
455
+ accessibilityRole: "button",
456
+ accessibilityLabel: "Add include pattern",
422
457
  style: [styles.addButton, {
423
458
  borderColor: _sharedUi.night.color.accentBorderStrong
424
459
  }],
@@ -444,6 +479,7 @@ function HighlightFilterView({
444
479
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
445
480
  children: "EXCLUDE"
446
481
  }), filters.excludePatterns.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
482
+ testID: "buoy-highlight-exclude-count",
447
483
  count: filters.excludePatterns.length,
448
484
  color: _sharedUi.night.color.accent
449
485
  })]
@@ -453,22 +489,28 @@ function HighlightFilterView({
453
489
  }), filters.excludePatterns.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
454
490
  style: styles.patternChips,
455
491
  children: filters.excludePatterns.map((pattern, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(PatternChip, {
492
+ mode: "exclude",
456
493
  pattern: pattern,
457
494
  onRemove: () => handleRemoveExcludePattern(index)
458
495
  }, `${pattern.type}-${pattern.value}-${index}`))
459
496
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
460
497
  style: styles.addPatternRow,
461
498
  children: showExcludeTypePicker ? /*#__PURE__*/(0, _jsxRuntime.jsx)(TypePicker, {
499
+ mode: "exclude",
462
500
  onSelect: type => {
463
501
  setShowExcludeTypePicker(false);
464
502
  setExcludeInputType(type);
465
503
  },
466
504
  onCancel: () => setShowExcludeTypePicker(false)
467
505
  }) : excludeInputType ? /*#__PURE__*/(0, _jsxRuntime.jsx)(PatternInput, {
506
+ mode: "exclude",
468
507
  type: excludeInputType,
469
508
  onSubmit: value => handleAddExcludePattern(excludeInputType, value),
470
509
  onCancel: () => setExcludeInputType(null)
471
510
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
511
+ testID: "buoy-highlight-exclude-add",
512
+ accessibilityRole: "button",
513
+ accessibilityLabel: "Add exclude pattern",
472
514
  style: [styles.addButton, {
473
515
  borderColor: _sharedUi.night.color.accentBorderStrong
474
516
  }],
@@ -502,6 +544,7 @@ function HighlightFilterView({
502
544
  children: "Show only components with render count in a specific range."
503
545
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
504
546
  nativeID: "__rn_buoy__render-count-range",
547
+ testID: "buoy-highlight-range",
505
548
  style: styles.renderCountRangeContainer,
506
549
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
507
550
  style: styles.renderCountInputRow,
@@ -524,6 +567,7 @@ function HighlightFilterView({
524
567
  });
525
568
  }
526
569
  },
570
+ testID: "buoy-highlight-range-min",
527
571
  placeholder: "0",
528
572
  placeholderTextColor: _sharedUi.night.color.placeholder,
529
573
  style: styles.renderCountInput,
@@ -552,6 +596,7 @@ function HighlightFilterView({
552
596
  });
553
597
  }
554
598
  },
599
+ testID: "buoy-highlight-range-max",
555
600
  placeholder: "\u221E",
556
601
  placeholderTextColor: _sharedUi.night.color.placeholder,
557
602
  style: styles.renderCountInput,
@@ -560,6 +605,9 @@ function HighlightFilterView({
560
605
  })]
561
606
  })]
562
607
  }), (filters.minRenderCount !== undefined || filters.maxRenderCount !== undefined) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
608
+ testID: "buoy-highlight-range-clear",
609
+ accessibilityRole: "button",
610
+ accessibilityLabel: "Clear range",
563
611
  style: styles.clearRangeButton,
564
612
  onPress: () => onFilterChange({
565
613
  minRenderCount: undefined,
@@ -625,8 +673,10 @@ function HighlightFilterView({
625
673
  })]
626
674
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
627
675
  nativeID: "__rn_buoy__detected-items",
676
+ testID: "buoy-highlight-detected",
628
677
  style: styles.detectedItemsContainer,
629
678
  children: selectedItems.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
679
+ testID: "buoy-highlight-detected-scroll",
630
680
  style: styles.detectedItemsScroll,
631
681
  contentContainerStyle: styles.detectedItems,
632
682
  nestedScrollEnabled: true,
@@ -634,6 +684,9 @@ function HighlightFilterView({
634
684
  children: selectedItems.map(item => {
635
685
  const itemConfig = getFilterConfig(item.type);
636
686
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
687
+ testID: (0, _testIDs.detectedItemTestID)(item.type, item.value),
688
+ accessibilityRole: "button",
689
+ accessibilityLabel: `${itemConfig.label} ${item.value}`,
637
690
  style: [styles.detectedItem, {
638
691
  borderColor: itemConfig.color + "40"
639
692
  }],
@@ -651,6 +704,7 @@ function HighlightFilterView({
651
704
  }, `${item.type}-${item.value}`);
652
705
  })
653
706
  }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
707
+ testID: "buoy-highlight-detected-empty",
654
708
  style: styles.emptyText,
655
709
  children: "No items detected yet. Start tracking to see components here."
656
710
  })
@@ -661,9 +715,13 @@ function HighlightFilterView({
661
715
  animationType: "fade",
662
716
  onRequestClose: () => setActionPopupItem(null),
663
717
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
718
+ accessible: false,
719
+ testID: "buoy-highlight-detected-popup-backdrop",
664
720
  style: styles.modalOverlay,
665
721
  onPress: () => setActionPopupItem(null),
666
722
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
723
+ accessible: false,
724
+ testID: "buoy-highlight-detected-popup",
667
725
  style: styles.actionPopup,
668
726
  children: actionPopupItem && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
669
727
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -677,6 +735,9 @@ function HighlightFilterView({
677
735
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
678
736
  style: styles.actionPopupButtons,
679
737
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
738
+ testID: "buoy-highlight-detected-popup-include",
739
+ accessibilityRole: "button",
740
+ accessibilityLabel: "Include only",
680
741
  style: [styles.actionPopupButton, styles.actionPopupInclude],
681
742
  onPress: handlePopupInclude,
682
743
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Eye, {
@@ -689,6 +750,9 @@ function HighlightFilterView({
689
750
  children: "Include Only"
690
751
  })]
691
752
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
753
+ testID: "buoy-highlight-detected-popup-exclude",
754
+ accessibilityRole: "button",
755
+ accessibilityLabel: "Exclude",
692
756
  style: [styles.actionPopupButton, styles.actionPopupExclude],
693
757
  onPress: handlePopupExclude,
694
758
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Filter, {
@@ -702,6 +766,9 @@ function HighlightFilterView({
702
766
  })]
703
767
  })]
704
768
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
769
+ testID: "buoy-highlight-detected-popup-cancel",
770
+ accessibilityRole: "button",
771
+ accessibilityLabel: "Cancel",
705
772
  style: styles.actionPopupCancel,
706
773
  onPress: () => setActionPopupItem(null),
707
774
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
@@ -763,6 +830,7 @@ function HighlightFilterView({
763
830
  style: styles.settingLabel,
764
831
  children: "Show Render Count"
765
832
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
833
+ testID: (0, _testIDs.settingTestID)("showRenderCount"),
766
834
  value: settings.showRenderCount,
767
835
  onValueChange: value => {
768
836
  onSettingsChange({
@@ -793,6 +861,7 @@ function HighlightFilterView({
793
861
  style: [styles.settingLabel, !settings.showRenderCount && styles.settingLabelDisabled],
794
862
  children: "Track Render Causes"
795
863
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
864
+ testID: (0, _testIDs.settingTestID)("trackRenderCauses"),
796
865
  value: settings.trackRenderCauses,
797
866
  onValueChange: value => onSettingsChange({
798
867
  trackRenderCauses: value
@@ -819,6 +888,7 @@ function HighlightFilterView({
819
888
  style: [styles.settingLabel, !settings.trackRenderCauses && styles.settingLabelDisabled],
820
889
  children: "Enable Render History"
821
890
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
891
+ testID: (0, _testIDs.settingTestID)("enableRenderHistory"),
822
892
  value: settings.enableRenderHistory,
823
893
  onValueChange: value => onSettingsChange({
824
894
  enableRenderHistory: value
@@ -847,6 +917,7 @@ function HighlightFilterView({
847
917
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
848
918
  style: styles.settingValue,
849
919
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
920
+ testID: "buoy-highlight-setting-debugLogLevel-value",
850
921
  style: [styles.settingValueText, settings.debugLogLevel !== "off" && {
851
922
  color: _sharedUi.night.color.warning
852
923
  }],
@@ -866,6 +937,9 @@ function HighlightFilterView({
866
937
  onPress: () => onSettingsChange({
867
938
  debugLogLevel: preset.value
868
939
  }),
940
+ testID: `buoy-highlight-preset-log-${preset.value}`,
941
+ accessibilityRole: "button",
942
+ accessibilityLabel: `Debug log level ${preset.label}`,
869
943
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
870
944
  style: [styles.batchSizePresetText, settings.debugLogLevel === preset.value && styles.batchSizePresetTextActive, preset.value !== "off" && settings.debugLogLevel === preset.value && {
871
945
  color: _sharedUi.night.color.warning
@@ -887,6 +961,7 @@ function HighlightFilterView({
887
961
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
888
962
  style: styles.settingValue,
889
963
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
964
+ testID: "buoy-highlight-setting-batchSize-value",
890
965
  style: styles.settingValueText,
891
966
  children: settings.batchSize
892
967
  })
@@ -901,12 +976,38 @@ function HighlightFilterView({
901
976
  onPress: () => onSettingsChange({
902
977
  batchSize: preset.value
903
978
  }),
979
+ testID: `buoy-highlight-preset-batch-${preset.value}`,
980
+ accessibilityRole: "button",
981
+ accessibilityLabel: `Batch size ${preset.label}`,
904
982
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
905
983
  style: [styles.batchSizePresetText, settings.batchSize === preset.value && styles.batchSizePresetTextActive],
906
984
  children: preset.label
907
985
  })
908
986
  }, preset.value))
909
987
  })]
988
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
989
+ style: [styles.settingItem, styles.settingItemSpaced],
990
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
991
+ style: styles.settingHeader,
992
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
993
+ style: styles.settingLabel,
994
+ children: "Show host views"
995
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
996
+ testID: (0, _testIDs.settingTestID)("showHostViews"),
997
+ value: !!settings.showHostViews,
998
+ onValueChange: value => onSettingsChange({
999
+ showHostViews: value
1000
+ }),
1001
+ trackColor: {
1002
+ false: _sharedUi.night.color.surfaceElevated,
1003
+ true: _sharedUi.night.color.accent + "80"
1004
+ },
1005
+ thumbColor: settings.showHostViews ? _sharedUi.night.color.accent : _sharedUi.night.color.knob
1006
+ })]
1007
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1008
+ style: styles.settingDescription,
1009
+ children: "Include anonymous View / RCTView / Text hosts. Off by default so the list is named composites (what actually re-rendered)."
1010
+ })]
910
1011
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
911
1012
  style: [styles.settingItem, styles.settingItemSpaced],
912
1013
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
@@ -915,6 +1016,7 @@ function HighlightFilterView({
915
1016
  style: styles.settingLabel,
916
1017
  children: "Highlight Dev Tools"
917
1018
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
1019
+ testID: (0, _testIDs.settingTestID)("excludeDevTools"),
918
1020
  value: !settings.excludeDevTools,
919
1021
  onValueChange: value => onSettingsChange({
920
1022
  excludeDevTools: !value
@@ -39,11 +39,13 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
39
39
  // Disabled banner - memoized since props rarely change
40
40
  const DisabledBanner = /*#__PURE__*/_react.default.memo(function DisabledBanner() {
41
41
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
42
+ testID: "buoy-highlight-disabled-banner",
42
43
  style: styles.disabledBanner,
43
44
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Power, {
44
45
  size: 14,
45
46
  color: _sharedUi.night.color.warning
46
47
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
48
+ testID: "buoy-highlight-disabled-text",
47
49
  style: styles.disabledText,
48
50
  children: "Render tracking is disabled"
49
51
  })]
@@ -512,6 +514,7 @@ function HighlightUpdatesModal({
512
514
  }) : null;
513
515
  const body = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
514
516
  nativeID: "__rn_buoy__highlight-modal",
517
+ testID: "buoy-highlight-body",
515
518
  style: styles.container,
516
519
  children: showCopySettingsView ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_RendersCopySettingsView.RendersCopySettingsView, {
517
520
  renders: copyRendersSnapshot
@@ -607,7 +610,8 @@ const styles = _reactNative.StyleSheet.create({
607
610
  paddingVertical: 8,
608
611
  borderBottomWidth: 1,
609
612
  borderBottomColor: _sharedUi.night.color.border,
610
- backgroundColor: _sharedUi.night.color.surface
613
+ // Clear, like Bench — the host's hoisted sky shows through the header.
614
+ backgroundColor: "transparent"
611
615
  },
612
616
  container: {
613
617
  flex: 1,
@@ -76,13 +76,15 @@ function IdentifierBadge({
76
76
  badgeOnly = false,
77
77
  valueOnly = false,
78
78
  shortLabel = false,
79
- showIcon = false
79
+ showIcon = false,
80
+ testID
80
81
  }) {
81
82
  const config = IDENTIFIER_CONFIG[type];
82
83
  const IconComponent = config.icon;
83
84
  const label = shortLabel ? config.shortLabel : config.label;
84
85
  if (valueOnly) {
85
86
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
87
+ testID: testID ? `${testID}-value` : undefined,
86
88
  style: [styles.valueOnly, compact && styles.valueOnlyCompact, {
87
89
  color: config.color
88
90
  }],
@@ -92,6 +94,7 @@ function IdentifierBadge({
92
94
  }
93
95
  if (badgeOnly) {
94
96
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
97
+ testID: testID,
95
98
  style: [styles.badgeOnly, compact && styles.badgeOnlyCompact, {
96
99
  backgroundColor: config.color + "20",
97
100
  borderColor: config.color + "40"
@@ -108,6 +111,8 @@ function IdentifierBadge({
108
111
  });
109
112
  }
110
113
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
114
+ testID: testID,
115
+ accessibilityLabel: `${config.label} ${value}`,
111
116
  style: [styles.container, compact && styles.containerCompact],
112
117
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
113
118
  style: [styles.badge, compact && styles.badgeCompact, {
@@ -123,6 +128,7 @@ function IdentifierBadge({
123
128
  children: label
124
129
  })]
125
130
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
131
+ testID: testID ? `${testID}-value` : undefined,
126
132
  style: [styles.value, compact && styles.valueCompact],
127
133
  numberOfLines: 1,
128
134
  children: value
@@ -92,11 +92,13 @@ function IsolatedRenderListInner({
92
92
  const renderFooter = (0, _react.useCallback)(() => {
93
93
  if (lockedComponentCount <= 0) return null;
94
94
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
95
+ testID: "buoy-highlight-locked",
95
96
  style: styles.lockedBanner,
96
97
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Lock, {
97
98
  size: 14,
98
99
  color: _sharedUi.night.color.warning
99
100
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
101
+ testID: "buoy-highlight-locked-text",
100
102
  style: styles.lockedBannerText,
101
103
  children: [lockedComponentCount, " more ", lockedComponentCount === 1 ? 'component' : 'components', " locked"]
102
104
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
@@ -109,20 +111,24 @@ function IsolatedRenderListInner({
109
111
  // Show empty state when no renders
110
112
  if (renders.length === 0) {
111
113
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
114
+ testID: "buoy-highlight-empty",
112
115
  style: styles.emptyState,
113
116
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Activity, {
114
117
  size: 32,
115
118
  color: _sharedUi.night.color.textTertiary
116
119
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
120
+ testID: "buoy-highlight-empty-title",
117
121
  style: styles.emptyTitle,
118
122
  children: "No renders tracked"
119
123
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
124
+ testID: "buoy-highlight-empty-text",
120
125
  style: styles.emptyText,
121
126
  children: isTracking ? "Component renders will appear here" : "Enable tracking to start capturing"
122
127
  })]
123
128
  });
124
129
  }
125
130
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
131
+ testID: "buoy-highlight-list",
126
132
  ref: flatListRef,
127
133
  data: visibleRenders,
128
134
  renderItem: renderItem,
@@ -32,11 +32,13 @@ const SearchSectionInner = /*#__PURE__*/(0, _react.memo)(function SearchSection(
32
32
  if (!isActive) return null;
33
33
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
34
34
  nativeID: "__rn_buoy__search-container",
35
+ testID: "buoy-highlight-search-container",
35
36
  style: styles.headerSearchContainer,
36
37
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
37
38
  size: 14,
38
39
  color: _sharedUi.night.color.textSecondary
39
40
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
41
+ testID: "buoy-highlight-search-field",
40
42
  style: styles.headerSearchInput,
41
43
  placeholder: "Search testID, nativeID, component...",
42
44
  placeholderTextColor: _sharedUi.night.color.placeholder,
@@ -50,6 +52,9 @@ const SearchSectionInner = /*#__PURE__*/(0, _react.memo)(function SearchSection(
50
52
  returnKeyType: "search",
51
53
  autoFocus: true
52
54
  }), searchText.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
55
+ testID: "buoy-highlight-search-clear",
56
+ accessibilityRole: "button",
57
+ accessibilityLabel: "Clear search",
53
58
  onPress: () => {
54
59
  onSearchChange("");
55
60
  onSearchClose();
@@ -82,6 +87,9 @@ const HeaderActionsInner = /*#__PURE__*/(0, _react.memo)(function HeaderActions(
82
87
  }) {
83
88
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
84
89
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
90
+ testID: "buoy-highlight-search",
91
+ accessibilityRole: "button",
92
+ accessibilityLabel: "Search renders",
85
93
  onPress: onSearchToggle,
86
94
  style: styles.headerActionButton,
87
95
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
@@ -89,6 +97,9 @@ const HeaderActionsInner = /*#__PURE__*/(0, _react.memo)(function HeaderActions(
89
97
  color: _sharedUi.night.color.textSecondary
90
98
  })
91
99
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
100
+ testID: "buoy-highlight-filters",
101
+ accessibilityRole: "button",
102
+ accessibilityLabel: activeFilterCount > 0 ? `Filters, ${activeFilterCount} active` : "Filters",
92
103
  onPress: onFilterToggle,
93
104
  style: [styles.headerActionButton, activeFilterCount > 0 && styles.activeFilterButton],
94
105
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Filter, {
@@ -96,6 +107,8 @@ const HeaderActionsInner = /*#__PURE__*/(0, _react.memo)(function HeaderActions(
96
107
  color: activeFilterCount > 0 ? _sharedUi.night.color.accent : _sharedUi.night.color.textTertiary
97
108
  })
98
109
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
110
+ testID: "buoy-highlight-copy",
111
+ accessibilityRole: "button",
99
112
  onPress: onOpenCopySettings,
100
113
  style: [styles.headerActionButton, !hasRenders && styles.headerActionButtonDisabled],
101
114
  disabled: !hasRenders,
@@ -105,6 +118,9 @@ const HeaderActionsInner = /*#__PURE__*/(0, _react.memo)(function HeaderActions(
105
118
  color: hasRenders ? _sharedUi.night.color.textSecondary : _sharedUi.night.color.textTertiary
106
119
  })
107
120
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
121
+ testID: "buoy-highlight-freeze",
122
+ accessibilityRole: "button",
123
+ accessibilityLabel: isFrozen ? "Unfreeze frame" : "Freeze frame",
108
124
  onPress: onToggleFreeze,
109
125
  style: [styles.headerActionButton, isFrozen && styles.freezeButton, !isTracking && styles.headerActionButtonDisabled],
110
126
  disabled: !isTracking,
@@ -113,10 +129,14 @@ const HeaderActionsInner = /*#__PURE__*/(0, _react.memo)(function HeaderActions(
113
129
  color: !isTracking ? _sharedUi.night.color.textTertiary : isFrozen ? _sharedUi.night.color.accent : _sharedUi.night.color.textTertiary
114
130
  })
115
131
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PowerToggleButton, {
132
+ testID: "buoy-highlight-power",
116
133
  isEnabled: isTracking,
117
134
  onToggle: onToggleTracking,
118
135
  accessibilityLabel: "Toggle render tracking"
119
136
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
137
+ testID: "buoy-highlight-clear",
138
+ accessibilityRole: "button",
139
+ accessibilityLabel: "Clear tracked renders",
120
140
  onPress: onClear,
121
141
  style: [styles.headerActionButton, !hasRenders && styles.headerActionButtonDisabled],
122
142
  disabled: !hasRenders,
@@ -204,7 +224,8 @@ const FilterViewHeader = exports.FilterViewHeader = /*#__PURE__*/(0, _react.memo
204
224
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TabSelector, {
205
225
  tabs: tabs,
206
226
  activeTab: activeTab,
207
- onTabChange: tab => onTabChange(tab)
227
+ onTabChange: tab => onTabChange(tab),
228
+ testIDPrefix: "buoy-highlight-filter-tab"
208
229
  })
209
230
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Actions, {})]
210
231
  });
@@ -237,7 +258,8 @@ const DetailViewHeader = exports.DetailViewHeader = /*#__PURE__*/(0, _react.memo
237
258
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TabSelector, {
238
259
  tabs: tabs,
239
260
  activeTab: activeTab,
240
- onTabChange: tab => onTabChange(tab)
261
+ onTabChange: tab => onTabChange(tab),
262
+ testIDPrefix: "buoy-highlight-detail-tab"
241
263
  })
242
264
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Actions, {})]
243
265
  });
@@ -254,7 +276,8 @@ const CopySettingsViewHeader = exports.CopySettingsViewHeader = /*#__PURE__*/(0,
254
276
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
255
277
  onBack: onBack
256
278
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
257
- title: "Copy Settings"
279
+ title: "Copy Settings",
280
+ testID: "buoy-highlight-copy-title"
258
281
  })]
259
282
  });
260
283
  });
@@ -100,7 +100,8 @@ function RenderCauseBadge({
100
100
  cause,
101
101
  compact = false,
102
102
  showKeys = false,
103
- showTwoLevel = false
103
+ showTwoLevel = false,
104
+ testID
104
105
  }) {
105
106
  const config = CAUSE_CONFIG[cause.type];
106
107
  const componentConfig = cause.componentCause ? COMPONENT_CAUSE_CONFIG[cause.componentCause] : null;
@@ -109,6 +110,7 @@ function RenderCauseBadge({
109
110
  const showComponentCause = showTwoLevel && componentConfig && cause.componentCause !== cause.type;
110
111
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
111
112
  nativeID: "__rn_buoy__cause-badge",
113
+ testID: testID,
112
114
  style: [styles.container, compact && styles.containerCompact],
113
115
  children: [showComponentCause && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
114
116
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -116,6 +118,7 @@ function RenderCauseBadge({
116
118
  backgroundColor: componentConfig.color + "15"
117
119
  }],
118
120
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
121
+ testID: testID ? `${testID}-component` : undefined,
119
122
  style: [styles.badgeText, compact && styles.badgeTextCompact, styles.componentBadgeText, {
120
123
  color: componentConfig.color
121
124
  }],
@@ -130,16 +133,19 @@ function RenderCauseBadge({
130
133
  backgroundColor: config.color + "20"
131
134
  }],
132
135
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
136
+ testID: testID ? `${testID}-native` : undefined,
133
137
  style: [styles.badgeText, compact && styles.badgeTextCompact, {
134
138
  color: config.color
135
139
  }],
136
140
  children: config.label
137
141
  })
138
142
  }), showKeys && cause.changedKeys && cause.changedKeys.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
143
+ testID: testID ? `${testID}-keys` : undefined,
139
144
  style: [styles.keysText, compact && styles.keysTextCompact],
140
145
  numberOfLines: 1,
141
146
  children: cause.changedKeys.join(", ")
142
147
  }), showKeys && cause.hookIndices && cause.hookIndices.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
148
+ testID: testID ? `${testID}-hooks` : undefined,
143
149
  style: [styles.keysText, compact && styles.keysTextCompact],
144
150
  numberOfLines: 1,
145
151
  children: ["Hook ", cause.hookIndices.join(", ")]