@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
@@ -12,6 +12,7 @@ import React, { useState, useCallback } from "react";
12
12
  import { View, Text, TouchableOpacity, TextInput, StyleSheet, ScrollView, Modal, Pressable, Switch } from "react-native";
13
13
  import { Eye, Filter, Plus, X, Box, Settings, Hash, SectionHeader, night } from "@buoy-gg/shared-ui";
14
14
  import { IdentifierBadge, IDENTIFIER_CONFIG } from "./IdentifierBadge";
15
+ import { detectedItemTestID, patternChipTestID, settingTestID } from "../utils/testIDs";
15
16
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
16
17
  // Use shared identifier config (FilterType is a subset of IdentifierType)
17
18
  const getFilterConfig = type => IDENTIFIER_CONFIG[type];
@@ -21,11 +22,13 @@ const FILTER_TYPES = ["any", "viewType", "testID", "nativeID", "component", "acc
21
22
 
22
23
  // Type picker component
23
24
  function TypePicker({
25
+ mode,
24
26
  onSelect,
25
27
  onCancel
26
28
  }) {
27
29
  return /*#__PURE__*/_jsxs(View, {
28
30
  nativeID: "__rn_buoy__type-picker",
31
+ testID: `buoy-highlight-${mode}-types`,
29
32
  style: styles.typePicker,
30
33
  children: [/*#__PURE__*/_jsx(View, {
31
34
  style: styles.typePickerRow,
@@ -33,6 +36,9 @@ function TypePicker({
33
36
  const config = getFilterConfig(type);
34
37
  const IconComponent = config.icon;
35
38
  return /*#__PURE__*/_jsxs(TouchableOpacity, {
39
+ testID: `buoy-highlight-${mode}-type-${type.toLowerCase()}`,
40
+ accessibilityRole: "button",
41
+ accessibilityLabel: config.label,
36
42
  style: [styles.typeOption, {
37
43
  backgroundColor: config.color + "15",
38
44
  borderColor: config.color + "40"
@@ -50,6 +56,9 @@ function TypePicker({
50
56
  }, type);
51
57
  })
52
58
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
59
+ testID: `buoy-highlight-${mode}-types-cancel`,
60
+ accessibilityRole: "button",
61
+ accessibilityLabel: "Cancel",
53
62
  onPress: onCancel,
54
63
  style: styles.typePickerCancel,
55
64
  children: /*#__PURE__*/_jsx(X, {
@@ -62,6 +71,7 @@ function TypePicker({
62
71
 
63
72
  // Pattern input with badge prefix
64
73
  function PatternInput({
74
+ mode,
65
75
  type,
66
76
  onSubmit,
67
77
  onCancel
@@ -75,6 +85,7 @@ function PatternInput({
75
85
  };
76
86
  return /*#__PURE__*/_jsxs(View, {
77
87
  nativeID: "__rn_buoy__pattern-input",
88
+ testID: `buoy-highlight-${mode}-input-row`,
78
89
  style: styles.patternInputContainer,
79
90
  children: [/*#__PURE__*/_jsx(IdentifierBadge, {
80
91
  type: type,
@@ -82,6 +93,7 @@ function PatternInput({
82
93
  badgeOnly: true,
83
94
  compact: true
84
95
  }), /*#__PURE__*/_jsx(TextInput, {
96
+ testID: `buoy-highlight-${mode}-input`,
85
97
  value: value,
86
98
  onChangeText: setValue,
87
99
  onSubmitEditing: handleSubmit,
@@ -93,6 +105,9 @@ function PatternInput({
93
105
  autoCorrect: false,
94
106
  autoCapitalize: "none"
95
107
  }), value.trim() && /*#__PURE__*/_jsx(TouchableOpacity, {
108
+ testID: `buoy-highlight-${mode}-submit`,
109
+ accessibilityRole: "button",
110
+ accessibilityLabel: "Add pattern",
96
111
  onPress: handleSubmit,
97
112
  style: [styles.addPatternButton, {
98
113
  backgroundColor: config.color + "20"
@@ -104,6 +119,9 @@ function PatternInput({
104
119
  children: "Add"
105
120
  })
106
121
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
122
+ testID: `buoy-highlight-${mode}-input-cancel`,
123
+ accessibilityRole: "button",
124
+ accessibilityLabel: "Cancel",
107
125
  onPress: onCancel,
108
126
  style: styles.cancelButton,
109
127
  children: /*#__PURE__*/_jsx(X, {
@@ -116,11 +134,15 @@ function PatternInput({
116
134
 
117
135
  // Pattern chip (removable badge) - tap to remove
118
136
  function PatternChip({
137
+ mode,
119
138
  pattern,
120
139
  onRemove
121
140
  }) {
122
141
  const config = getFilterConfig(pattern.type);
123
142
  return /*#__PURE__*/_jsxs(TouchableOpacity, {
143
+ testID: patternChipTestID(mode, pattern.type, pattern.value),
144
+ accessibilityRole: "button",
145
+ accessibilityLabel: `${config.label} ${pattern.value}, tap to remove`,
124
146
  style: [styles.patternChip, {
125
147
  borderColor: config.color + "40"
126
148
  }],
@@ -167,7 +189,11 @@ function DetectedCategoryBadge({
167
189
  }) {
168
190
  // "all" has its own config
169
191
  const config = filterType === "all" ? ALL_CATEGORY_CONFIG : getFilterConfig(filterType);
192
+ const handle = `buoy-highlight-detected-cat-${String(filterType).toLowerCase()}`;
170
193
  return /*#__PURE__*/_jsxs(TouchableOpacity, {
194
+ testID: handle,
195
+ accessibilityRole: "button",
196
+ accessibilityLabel: `${config.label}, ${count}`,
171
197
  style: [styles.categoryBadge, {
172
198
  backgroundColor: config.color + "15",
173
199
  borderColor: isSelected ? config.color : config.color + "40",
@@ -187,6 +213,7 @@ function DetectedCategoryBadge({
187
213
  backgroundColor: config.color + "25"
188
214
  }],
189
215
  children: /*#__PURE__*/_jsx(Text, {
216
+ testID: `${handle}-count`,
190
217
  style: [styles.categoryBadgeCount, {
191
218
  color: config.color
192
219
  }],
@@ -374,6 +401,7 @@ export function HighlightFilterView({
374
401
  style: styles.container,
375
402
  children: /*#__PURE__*/_jsx(ScrollView, {
376
403
  nativeID: "__rn_buoy__filter-view",
404
+ testID: "buoy-highlight-filter-scroll",
377
405
  style: styles.scrollView,
378
406
  contentContainerStyle: styles.scrollContent,
379
407
  showsVerticalScrollIndicator: false,
@@ -388,6 +416,7 @@ export function HighlightFilterView({
388
416
  }), /*#__PURE__*/_jsx(SectionHeader.Title, {
389
417
  children: "INCLUDE ONLY"
390
418
  }), filters.includePatterns.length > 0 && /*#__PURE__*/_jsx(SectionHeader.Badge, {
419
+ testID: "buoy-highlight-include-count",
391
420
  count: filters.includePatterns.length,
392
421
  color: night.color.accent
393
422
  })]
@@ -397,22 +426,28 @@ export function HighlightFilterView({
397
426
  }), filters.includePatterns.length > 0 && /*#__PURE__*/_jsx(View, {
398
427
  style: styles.patternChips,
399
428
  children: filters.includePatterns.map((pattern, index) => /*#__PURE__*/_jsx(PatternChip, {
429
+ mode: "include",
400
430
  pattern: pattern,
401
431
  onRemove: () => handleRemoveIncludePattern(index)
402
432
  }, `${pattern.type}-${pattern.value}-${index}`))
403
433
  }), /*#__PURE__*/_jsx(View, {
404
434
  style: styles.addPatternRow,
405
435
  children: showIncludeTypePicker ? /*#__PURE__*/_jsx(TypePicker, {
436
+ mode: "include",
406
437
  onSelect: type => {
407
438
  setShowIncludeTypePicker(false);
408
439
  setIncludeInputType(type);
409
440
  },
410
441
  onCancel: () => setShowIncludeTypePicker(false)
411
442
  }) : includeInputType ? /*#__PURE__*/_jsx(PatternInput, {
443
+ mode: "include",
412
444
  type: includeInputType,
413
445
  onSubmit: value => handleAddIncludePattern(includeInputType, value),
414
446
  onCancel: () => setIncludeInputType(null)
415
447
  }) : /*#__PURE__*/_jsxs(TouchableOpacity, {
448
+ testID: "buoy-highlight-include-add",
449
+ accessibilityRole: "button",
450
+ accessibilityLabel: "Add include pattern",
416
451
  style: [styles.addButton, {
417
452
  borderColor: night.color.accentBorderStrong
418
453
  }],
@@ -438,6 +473,7 @@ export function HighlightFilterView({
438
473
  }), /*#__PURE__*/_jsx(SectionHeader.Title, {
439
474
  children: "EXCLUDE"
440
475
  }), filters.excludePatterns.length > 0 && /*#__PURE__*/_jsx(SectionHeader.Badge, {
476
+ testID: "buoy-highlight-exclude-count",
441
477
  count: filters.excludePatterns.length,
442
478
  color: night.color.accent
443
479
  })]
@@ -447,22 +483,28 @@ export function HighlightFilterView({
447
483
  }), filters.excludePatterns.length > 0 && /*#__PURE__*/_jsx(View, {
448
484
  style: styles.patternChips,
449
485
  children: filters.excludePatterns.map((pattern, index) => /*#__PURE__*/_jsx(PatternChip, {
486
+ mode: "exclude",
450
487
  pattern: pattern,
451
488
  onRemove: () => handleRemoveExcludePattern(index)
452
489
  }, `${pattern.type}-${pattern.value}-${index}`))
453
490
  }), /*#__PURE__*/_jsx(View, {
454
491
  style: styles.addPatternRow,
455
492
  children: showExcludeTypePicker ? /*#__PURE__*/_jsx(TypePicker, {
493
+ mode: "exclude",
456
494
  onSelect: type => {
457
495
  setShowExcludeTypePicker(false);
458
496
  setExcludeInputType(type);
459
497
  },
460
498
  onCancel: () => setShowExcludeTypePicker(false)
461
499
  }) : excludeInputType ? /*#__PURE__*/_jsx(PatternInput, {
500
+ mode: "exclude",
462
501
  type: excludeInputType,
463
502
  onSubmit: value => handleAddExcludePattern(excludeInputType, value),
464
503
  onCancel: () => setExcludeInputType(null)
465
504
  }) : /*#__PURE__*/_jsxs(TouchableOpacity, {
505
+ testID: "buoy-highlight-exclude-add",
506
+ accessibilityRole: "button",
507
+ accessibilityLabel: "Add exclude pattern",
466
508
  style: [styles.addButton, {
467
509
  borderColor: night.color.accentBorderStrong
468
510
  }],
@@ -496,6 +538,7 @@ export function HighlightFilterView({
496
538
  children: "Show only components with render count in a specific range."
497
539
  }), /*#__PURE__*/_jsxs(View, {
498
540
  nativeID: "__rn_buoy__render-count-range",
541
+ testID: "buoy-highlight-range",
499
542
  style: styles.renderCountRangeContainer,
500
543
  children: [/*#__PURE__*/_jsxs(View, {
501
544
  style: styles.renderCountInputRow,
@@ -518,6 +561,7 @@ export function HighlightFilterView({
518
561
  });
519
562
  }
520
563
  },
564
+ testID: "buoy-highlight-range-min",
521
565
  placeholder: "0",
522
566
  placeholderTextColor: night.color.placeholder,
523
567
  style: styles.renderCountInput,
@@ -546,6 +590,7 @@ export function HighlightFilterView({
546
590
  });
547
591
  }
548
592
  },
593
+ testID: "buoy-highlight-range-max",
549
594
  placeholder: "\u221E",
550
595
  placeholderTextColor: night.color.placeholder,
551
596
  style: styles.renderCountInput,
@@ -554,6 +599,9 @@ export function HighlightFilterView({
554
599
  })]
555
600
  })]
556
601
  }), (filters.minRenderCount !== undefined || filters.maxRenderCount !== undefined) && /*#__PURE__*/_jsxs(TouchableOpacity, {
602
+ testID: "buoy-highlight-range-clear",
603
+ accessibilityRole: "button",
604
+ accessibilityLabel: "Clear range",
557
605
  style: styles.clearRangeButton,
558
606
  onPress: () => onFilterChange({
559
607
  minRenderCount: undefined,
@@ -619,8 +667,10 @@ export function HighlightFilterView({
619
667
  })]
620
668
  }), /*#__PURE__*/_jsx(View, {
621
669
  nativeID: "__rn_buoy__detected-items",
670
+ testID: "buoy-highlight-detected",
622
671
  style: styles.detectedItemsContainer,
623
672
  children: selectedItems.length > 0 ? /*#__PURE__*/_jsx(ScrollView, {
673
+ testID: "buoy-highlight-detected-scroll",
624
674
  style: styles.detectedItemsScroll,
625
675
  contentContainerStyle: styles.detectedItems,
626
676
  nestedScrollEnabled: true,
@@ -628,6 +678,9 @@ export function HighlightFilterView({
628
678
  children: selectedItems.map(item => {
629
679
  const itemConfig = getFilterConfig(item.type);
630
680
  return /*#__PURE__*/_jsx(TouchableOpacity, {
681
+ testID: detectedItemTestID(item.type, item.value),
682
+ accessibilityRole: "button",
683
+ accessibilityLabel: `${itemConfig.label} ${item.value}`,
631
684
  style: [styles.detectedItem, {
632
685
  borderColor: itemConfig.color + "40"
633
686
  }],
@@ -645,6 +698,7 @@ export function HighlightFilterView({
645
698
  }, `${item.type}-${item.value}`);
646
699
  })
647
700
  }) : /*#__PURE__*/_jsx(Text, {
701
+ testID: "buoy-highlight-detected-empty",
648
702
  style: styles.emptyText,
649
703
  children: "No items detected yet. Start tracking to see components here."
650
704
  })
@@ -655,9 +709,13 @@ export function HighlightFilterView({
655
709
  animationType: "fade",
656
710
  onRequestClose: () => setActionPopupItem(null),
657
711
  children: /*#__PURE__*/_jsx(Pressable, {
712
+ accessible: false,
713
+ testID: "buoy-highlight-detected-popup-backdrop",
658
714
  style: styles.modalOverlay,
659
715
  onPress: () => setActionPopupItem(null),
660
716
  children: /*#__PURE__*/_jsx(View, {
717
+ accessible: false,
718
+ testID: "buoy-highlight-detected-popup",
661
719
  style: styles.actionPopup,
662
720
  children: actionPopupItem && /*#__PURE__*/_jsxs(_Fragment, {
663
721
  children: [/*#__PURE__*/_jsx(View, {
@@ -671,6 +729,9 @@ export function HighlightFilterView({
671
729
  }), /*#__PURE__*/_jsxs(View, {
672
730
  style: styles.actionPopupButtons,
673
731
  children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
732
+ testID: "buoy-highlight-detected-popup-include",
733
+ accessibilityRole: "button",
734
+ accessibilityLabel: "Include only",
674
735
  style: [styles.actionPopupButton, styles.actionPopupInclude],
675
736
  onPress: handlePopupInclude,
676
737
  children: [/*#__PURE__*/_jsx(Eye, {
@@ -683,6 +744,9 @@ export function HighlightFilterView({
683
744
  children: "Include Only"
684
745
  })]
685
746
  }), /*#__PURE__*/_jsxs(TouchableOpacity, {
747
+ testID: "buoy-highlight-detected-popup-exclude",
748
+ accessibilityRole: "button",
749
+ accessibilityLabel: "Exclude",
686
750
  style: [styles.actionPopupButton, styles.actionPopupExclude],
687
751
  onPress: handlePopupExclude,
688
752
  children: [/*#__PURE__*/_jsx(Filter, {
@@ -696,6 +760,9 @@ export function HighlightFilterView({
696
760
  })]
697
761
  })]
698
762
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
763
+ testID: "buoy-highlight-detected-popup-cancel",
764
+ accessibilityRole: "button",
765
+ accessibilityLabel: "Cancel",
699
766
  style: styles.actionPopupCancel,
700
767
  onPress: () => setActionPopupItem(null),
701
768
  children: /*#__PURE__*/_jsx(Text, {
@@ -757,6 +824,7 @@ export function HighlightFilterView({
757
824
  style: styles.settingLabel,
758
825
  children: "Show Render Count"
759
826
  }), /*#__PURE__*/_jsx(Switch, {
827
+ testID: settingTestID("showRenderCount"),
760
828
  value: settings.showRenderCount,
761
829
  onValueChange: value => {
762
830
  onSettingsChange({
@@ -787,6 +855,7 @@ export function HighlightFilterView({
787
855
  style: [styles.settingLabel, !settings.showRenderCount && styles.settingLabelDisabled],
788
856
  children: "Track Render Causes"
789
857
  }), /*#__PURE__*/_jsx(Switch, {
858
+ testID: settingTestID("trackRenderCauses"),
790
859
  value: settings.trackRenderCauses,
791
860
  onValueChange: value => onSettingsChange({
792
861
  trackRenderCauses: value
@@ -813,6 +882,7 @@ export function HighlightFilterView({
813
882
  style: [styles.settingLabel, !settings.trackRenderCauses && styles.settingLabelDisabled],
814
883
  children: "Enable Render History"
815
884
  }), /*#__PURE__*/_jsx(Switch, {
885
+ testID: settingTestID("enableRenderHistory"),
816
886
  value: settings.enableRenderHistory,
817
887
  onValueChange: value => onSettingsChange({
818
888
  enableRenderHistory: value
@@ -841,6 +911,7 @@ export function HighlightFilterView({
841
911
  }), /*#__PURE__*/_jsx(View, {
842
912
  style: styles.settingValue,
843
913
  children: /*#__PURE__*/_jsx(Text, {
914
+ testID: "buoy-highlight-setting-debugLogLevel-value",
844
915
  style: [styles.settingValueText, settings.debugLogLevel !== "off" && {
845
916
  color: night.color.warning
846
917
  }],
@@ -860,6 +931,9 @@ export function HighlightFilterView({
860
931
  onPress: () => onSettingsChange({
861
932
  debugLogLevel: preset.value
862
933
  }),
934
+ testID: `buoy-highlight-preset-log-${preset.value}`,
935
+ accessibilityRole: "button",
936
+ accessibilityLabel: `Debug log level ${preset.label}`,
863
937
  children: /*#__PURE__*/_jsx(Text, {
864
938
  style: [styles.batchSizePresetText, settings.debugLogLevel === preset.value && styles.batchSizePresetTextActive, preset.value !== "off" && settings.debugLogLevel === preset.value && {
865
939
  color: night.color.warning
@@ -881,6 +955,7 @@ export function HighlightFilterView({
881
955
  }), /*#__PURE__*/_jsx(View, {
882
956
  style: styles.settingValue,
883
957
  children: /*#__PURE__*/_jsx(Text, {
958
+ testID: "buoy-highlight-setting-batchSize-value",
884
959
  style: styles.settingValueText,
885
960
  children: settings.batchSize
886
961
  })
@@ -895,12 +970,38 @@ export function HighlightFilterView({
895
970
  onPress: () => onSettingsChange({
896
971
  batchSize: preset.value
897
972
  }),
973
+ testID: `buoy-highlight-preset-batch-${preset.value}`,
974
+ accessibilityRole: "button",
975
+ accessibilityLabel: `Batch size ${preset.label}`,
898
976
  children: /*#__PURE__*/_jsx(Text, {
899
977
  style: [styles.batchSizePresetText, settings.batchSize === preset.value && styles.batchSizePresetTextActive],
900
978
  children: preset.label
901
979
  })
902
980
  }, preset.value))
903
981
  })]
982
+ }), /*#__PURE__*/_jsxs(View, {
983
+ style: [styles.settingItem, styles.settingItemSpaced],
984
+ children: [/*#__PURE__*/_jsxs(View, {
985
+ style: styles.settingHeader,
986
+ children: [/*#__PURE__*/_jsx(Text, {
987
+ style: styles.settingLabel,
988
+ children: "Show host views"
989
+ }), /*#__PURE__*/_jsx(Switch, {
990
+ testID: settingTestID("showHostViews"),
991
+ value: !!settings.showHostViews,
992
+ onValueChange: value => onSettingsChange({
993
+ showHostViews: value
994
+ }),
995
+ trackColor: {
996
+ false: night.color.surfaceElevated,
997
+ true: night.color.accent + "80"
998
+ },
999
+ thumbColor: settings.showHostViews ? night.color.accent : night.color.knob
1000
+ })]
1001
+ }), /*#__PURE__*/_jsx(Text, {
1002
+ style: styles.settingDescription,
1003
+ children: "Include anonymous View / RCTView / Text hosts. Off by default so the list is named composites (what actually re-rendered)."
1004
+ })]
904
1005
  }), /*#__PURE__*/_jsxs(View, {
905
1006
  style: [styles.settingItem, styles.settingItemSpaced],
906
1007
  children: [/*#__PURE__*/_jsxs(View, {
@@ -909,6 +1010,7 @@ export function HighlightFilterView({
909
1010
  style: styles.settingLabel,
910
1011
  children: "Highlight Dev Tools"
911
1012
  }), /*#__PURE__*/_jsx(Switch, {
1013
+ testID: settingTestID("excludeDevTools"),
912
1014
  value: !settings.excludeDevTools,
913
1015
  onValueChange: value => onSettingsChange({
914
1016
  excludeDevTools: !value
@@ -32,11 +32,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
32
32
  // Disabled banner - memoized since props rarely change
33
33
  const DisabledBanner = /*#__PURE__*/React.memo(function DisabledBanner() {
34
34
  return /*#__PURE__*/_jsxs(View, {
35
+ testID: "buoy-highlight-disabled-banner",
35
36
  style: styles.disabledBanner,
36
37
  children: [/*#__PURE__*/_jsx(Power, {
37
38
  size: 14,
38
39
  color: night.color.warning
39
40
  }), /*#__PURE__*/_jsx(Text, {
41
+ testID: "buoy-highlight-disabled-text",
40
42
  style: styles.disabledText,
41
43
  children: "Render tracking is disabled"
42
44
  })]
@@ -505,6 +507,7 @@ export function HighlightUpdatesModal({
505
507
  }) : null;
506
508
  const body = /*#__PURE__*/_jsx(View, {
507
509
  nativeID: "__rn_buoy__highlight-modal",
510
+ testID: "buoy-highlight-body",
508
511
  style: styles.container,
509
512
  children: showCopySettingsView ? /*#__PURE__*/_jsx(RendersCopySettingsView, {
510
513
  renders: copyRendersSnapshot
@@ -600,7 +603,8 @@ const styles = StyleSheet.create({
600
603
  paddingVertical: 8,
601
604
  borderBottomWidth: 1,
602
605
  borderBottomColor: night.color.border,
603
- backgroundColor: night.color.surface
606
+ // Clear, like Bench — the host's hoisted sky shows through the header.
607
+ backgroundColor: "transparent"
604
608
  },
605
609
  container: {
606
610
  flex: 1,
@@ -68,13 +68,15 @@ export function IdentifierBadge({
68
68
  badgeOnly = false,
69
69
  valueOnly = false,
70
70
  shortLabel = false,
71
- showIcon = false
71
+ showIcon = false,
72
+ testID
72
73
  }) {
73
74
  const config = IDENTIFIER_CONFIG[type];
74
75
  const IconComponent = config.icon;
75
76
  const label = shortLabel ? config.shortLabel : config.label;
76
77
  if (valueOnly) {
77
78
  return /*#__PURE__*/_jsx(Text, {
79
+ testID: testID ? `${testID}-value` : undefined,
78
80
  style: [styles.valueOnly, compact && styles.valueOnlyCompact, {
79
81
  color: config.color
80
82
  }],
@@ -84,6 +86,7 @@ export function IdentifierBadge({
84
86
  }
85
87
  if (badgeOnly) {
86
88
  return /*#__PURE__*/_jsxs(View, {
89
+ testID: testID,
87
90
  style: [styles.badgeOnly, compact && styles.badgeOnlyCompact, {
88
91
  backgroundColor: config.color + "20",
89
92
  borderColor: config.color + "40"
@@ -100,6 +103,8 @@ export function IdentifierBadge({
100
103
  });
101
104
  }
102
105
  return /*#__PURE__*/_jsxs(View, {
106
+ testID: testID,
107
+ accessibilityLabel: `${config.label} ${value}`,
103
108
  style: [styles.container, compact && styles.containerCompact],
104
109
  children: [/*#__PURE__*/_jsxs(View, {
105
110
  style: [styles.badge, compact && styles.badgeCompact, {
@@ -115,6 +120,7 @@ export function IdentifierBadge({
115
120
  children: label
116
121
  })]
117
122
  }), /*#__PURE__*/_jsx(Text, {
123
+ testID: testID ? `${testID}-value` : undefined,
118
124
  style: [styles.value, compact && styles.valueCompact],
119
125
  numberOfLines: 1,
120
126
  children: value
@@ -87,11 +87,13 @@ function IsolatedRenderListInner({
87
87
  const renderFooter = useCallback(() => {
88
88
  if (lockedComponentCount <= 0) return null;
89
89
  return /*#__PURE__*/_jsxs(View, {
90
+ testID: "buoy-highlight-locked",
90
91
  style: styles.lockedBanner,
91
92
  children: [/*#__PURE__*/_jsx(Lock, {
92
93
  size: 14,
93
94
  color: night.color.warning
94
95
  }), /*#__PURE__*/_jsxs(Text, {
96
+ testID: "buoy-highlight-locked-text",
95
97
  style: styles.lockedBannerText,
96
98
  children: [lockedComponentCount, " more ", lockedComponentCount === 1 ? 'component' : 'components', " locked"]
97
99
  }), /*#__PURE__*/_jsx(Text, {
@@ -104,20 +106,24 @@ function IsolatedRenderListInner({
104
106
  // Show empty state when no renders
105
107
  if (renders.length === 0) {
106
108
  return /*#__PURE__*/_jsxs(View, {
109
+ testID: "buoy-highlight-empty",
107
110
  style: styles.emptyState,
108
111
  children: [/*#__PURE__*/_jsx(Activity, {
109
112
  size: 32,
110
113
  color: night.color.textTertiary
111
114
  }), /*#__PURE__*/_jsx(Text, {
115
+ testID: "buoy-highlight-empty-title",
112
116
  style: styles.emptyTitle,
113
117
  children: "No renders tracked"
114
118
  }), /*#__PURE__*/_jsx(Text, {
119
+ testID: "buoy-highlight-empty-text",
115
120
  style: styles.emptyText,
116
121
  children: isTracking ? "Component renders will appear here" : "Enable tracking to start capturing"
117
122
  })]
118
123
  });
119
124
  }
120
125
  return /*#__PURE__*/_jsx(FlatList, {
126
+ testID: "buoy-highlight-list",
121
127
  ref: flatListRef,
122
128
  data: visibleRenders,
123
129
  renderItem: renderItem,
@@ -27,11 +27,13 @@ const SearchSectionInner = /*#__PURE__*/memo(function SearchSection({
27
27
  if (!isActive) return null;
28
28
  return /*#__PURE__*/_jsxs(View, {
29
29
  nativeID: "__rn_buoy__search-container",
30
+ testID: "buoy-highlight-search-container",
30
31
  style: styles.headerSearchContainer,
31
32
  children: [/*#__PURE__*/_jsx(Search, {
32
33
  size: 14,
33
34
  color: night.color.textSecondary
34
35
  }), /*#__PURE__*/_jsx(TextInput, {
36
+ testID: "buoy-highlight-search-field",
35
37
  style: styles.headerSearchInput,
36
38
  placeholder: "Search testID, nativeID, component...",
37
39
  placeholderTextColor: night.color.placeholder,
@@ -45,6 +47,9 @@ const SearchSectionInner = /*#__PURE__*/memo(function SearchSection({
45
47
  returnKeyType: "search",
46
48
  autoFocus: true
47
49
  }), searchText.length > 0 ? /*#__PURE__*/_jsx(TouchableOpacity, {
50
+ testID: "buoy-highlight-search-clear",
51
+ accessibilityRole: "button",
52
+ accessibilityLabel: "Clear search",
48
53
  onPress: () => {
49
54
  onSearchChange("");
50
55
  onSearchClose();
@@ -77,6 +82,9 @@ const HeaderActionsInner = /*#__PURE__*/memo(function HeaderActions({
77
82
  }) {
78
83
  return /*#__PURE__*/_jsxs(ModalHeader.Actions, {
79
84
  children: [/*#__PURE__*/_jsx(TouchableOpacity, {
85
+ testID: "buoy-highlight-search",
86
+ accessibilityRole: "button",
87
+ accessibilityLabel: "Search renders",
80
88
  onPress: onSearchToggle,
81
89
  style: styles.headerActionButton,
82
90
  children: /*#__PURE__*/_jsx(Search, {
@@ -84,6 +92,9 @@ const HeaderActionsInner = /*#__PURE__*/memo(function HeaderActions({
84
92
  color: night.color.textSecondary
85
93
  })
86
94
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
95
+ testID: "buoy-highlight-filters",
96
+ accessibilityRole: "button",
97
+ accessibilityLabel: activeFilterCount > 0 ? `Filters, ${activeFilterCount} active` : "Filters",
87
98
  onPress: onFilterToggle,
88
99
  style: [styles.headerActionButton, activeFilterCount > 0 && styles.activeFilterButton],
89
100
  children: /*#__PURE__*/_jsx(Filter, {
@@ -91,6 +102,8 @@ const HeaderActionsInner = /*#__PURE__*/memo(function HeaderActions({
91
102
  color: activeFilterCount > 0 ? night.color.accent : night.color.textTertiary
92
103
  })
93
104
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
105
+ testID: "buoy-highlight-copy",
106
+ accessibilityRole: "button",
94
107
  onPress: onOpenCopySettings,
95
108
  style: [styles.headerActionButton, !hasRenders && styles.headerActionButtonDisabled],
96
109
  disabled: !hasRenders,
@@ -100,6 +113,9 @@ const HeaderActionsInner = /*#__PURE__*/memo(function HeaderActions({
100
113
  color: hasRenders ? night.color.textSecondary : night.color.textTertiary
101
114
  })
102
115
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
116
+ testID: "buoy-highlight-freeze",
117
+ accessibilityRole: "button",
118
+ accessibilityLabel: isFrozen ? "Unfreeze frame" : "Freeze frame",
103
119
  onPress: onToggleFreeze,
104
120
  style: [styles.headerActionButton, isFrozen && styles.freezeButton, !isTracking && styles.headerActionButtonDisabled],
105
121
  disabled: !isTracking,
@@ -108,10 +124,14 @@ const HeaderActionsInner = /*#__PURE__*/memo(function HeaderActions({
108
124
  color: !isTracking ? night.color.textTertiary : isFrozen ? night.color.accent : night.color.textTertiary
109
125
  })
110
126
  }), /*#__PURE__*/_jsx(PowerToggleButton, {
127
+ testID: "buoy-highlight-power",
111
128
  isEnabled: isTracking,
112
129
  onToggle: onToggleTracking,
113
130
  accessibilityLabel: "Toggle render tracking"
114
131
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
132
+ testID: "buoy-highlight-clear",
133
+ accessibilityRole: "button",
134
+ accessibilityLabel: "Clear tracked renders",
115
135
  onPress: onClear,
116
136
  style: [styles.headerActionButton, !hasRenders && styles.headerActionButtonDisabled],
117
137
  disabled: !hasRenders,
@@ -199,7 +219,8 @@ export const FilterViewHeader = /*#__PURE__*/memo(function FilterViewHeader({
199
219
  children: /*#__PURE__*/_jsx(TabSelector, {
200
220
  tabs: tabs,
201
221
  activeTab: activeTab,
202
- onTabChange: tab => onTabChange(tab)
222
+ onTabChange: tab => onTabChange(tab),
223
+ testIDPrefix: "buoy-highlight-filter-tab"
203
224
  })
204
225
  }), /*#__PURE__*/_jsx(ModalHeader.Actions, {})]
205
226
  });
@@ -232,7 +253,8 @@ export const DetailViewHeader = /*#__PURE__*/memo(function DetailViewHeader({
232
253
  children: /*#__PURE__*/_jsx(TabSelector, {
233
254
  tabs: tabs,
234
255
  activeTab: activeTab,
235
- onTabChange: tab => onTabChange(tab)
256
+ onTabChange: tab => onTabChange(tab),
257
+ testIDPrefix: "buoy-highlight-detail-tab"
236
258
  })
237
259
  }), /*#__PURE__*/_jsx(ModalHeader.Actions, {})]
238
260
  });
@@ -249,7 +271,8 @@ export const CopySettingsViewHeader = /*#__PURE__*/memo(function CopySettingsVie
249
271
  children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
250
272
  onBack: onBack
251
273
  }), /*#__PURE__*/_jsx(ModalHeader.Content, {
252
- title: "Copy Settings"
274
+ title: "Copy Settings",
275
+ testID: "buoy-highlight-copy-title"
253
276
  })]
254
277
  });
255
278
  });
@@ -91,7 +91,8 @@ export function RenderCauseBadge({
91
91
  cause,
92
92
  compact = false,
93
93
  showKeys = false,
94
- showTwoLevel = false
94
+ showTwoLevel = false,
95
+ testID
95
96
  }) {
96
97
  const config = CAUSE_CONFIG[cause.type];
97
98
  const componentConfig = cause.componentCause ? COMPONENT_CAUSE_CONFIG[cause.componentCause] : null;
@@ -100,6 +101,7 @@ export function RenderCauseBadge({
100
101
  const showComponentCause = showTwoLevel && componentConfig && cause.componentCause !== cause.type;
101
102
  return /*#__PURE__*/_jsxs(View, {
102
103
  nativeID: "__rn_buoy__cause-badge",
104
+ testID: testID,
103
105
  style: [styles.container, compact && styles.containerCompact],
104
106
  children: [showComponentCause && /*#__PURE__*/_jsxs(_Fragment, {
105
107
  children: [/*#__PURE__*/_jsx(View, {
@@ -107,6 +109,7 @@ export function RenderCauseBadge({
107
109
  backgroundColor: componentConfig.color + "15"
108
110
  }],
109
111
  children: /*#__PURE__*/_jsx(Text, {
112
+ testID: testID ? `${testID}-component` : undefined,
110
113
  style: [styles.badgeText, compact && styles.badgeTextCompact, styles.componentBadgeText, {
111
114
  color: componentConfig.color
112
115
  }],
@@ -121,16 +124,19 @@ export function RenderCauseBadge({
121
124
  backgroundColor: config.color + "20"
122
125
  }],
123
126
  children: /*#__PURE__*/_jsx(Text, {
127
+ testID: testID ? `${testID}-native` : undefined,
124
128
  style: [styles.badgeText, compact && styles.badgeTextCompact, {
125
129
  color: config.color
126
130
  }],
127
131
  children: config.label
128
132
  })
129
133
  }), showKeys && cause.changedKeys && cause.changedKeys.length > 0 && /*#__PURE__*/_jsx(Text, {
134
+ testID: testID ? `${testID}-keys` : undefined,
130
135
  style: [styles.keysText, compact && styles.keysTextCompact],
131
136
  numberOfLines: 1,
132
137
  children: cause.changedKeys.join(", ")
133
138
  }), showKeys && cause.hookIndices && cause.hookIndices.length > 0 && /*#__PURE__*/_jsxs(Text, {
139
+ testID: testID ? `${testID}-hooks` : undefined,
134
140
  style: [styles.keysText, compact && styles.keysTextCompact],
135
141
  numberOfLines: 1,
136
142
  children: ["Hook ", cause.hookIndices.join(", ")]