@buoy-gg/highlight-updates 7.0.28 → 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 (57) hide show
  1. package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +80 -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 +10 -0
  14. package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +15 -3
  15. package/lib/commonjs/highlight-updates/utils/ownStateChanged.js +103 -0
  16. package/lib/commonjs/highlight-updates/utils/testIDs.js +68 -0
  17. package/lib/commonjs/index.js +34 -0
  18. package/lib/module/highlight-updates/components/HighlightFilterView.js +80 -0
  19. package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +5 -1
  20. package/lib/module/highlight-updates/components/IdentifierBadge.js +7 -1
  21. package/lib/module/highlight-updates/components/IsolatedRenderList.js +6 -0
  22. package/lib/module/highlight-updates/components/ModalHeaderContent.js +26 -3
  23. package/lib/module/highlight-updates/components/RenderCauseBadge.js +7 -1
  24. package/lib/module/highlight-updates/components/RenderDetailView.js +40 -1
  25. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +29 -6
  26. package/lib/module/highlight-updates/components/RenderListItem.js +15 -0
  27. package/lib/module/highlight-updates/components/RendersCopySettingsView.js +11 -0
  28. package/lib/module/highlight-updates/components/StatsDisplay.js +3 -0
  29. package/lib/module/highlight-updates/utils/CommitProfiler.js +5 -1
  30. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +10 -0
  31. package/lib/module/highlight-updates/utils/RenderCauseDetector.js +15 -3
  32. package/lib/module/highlight-updates/utils/ownStateChanged.js +98 -0
  33. package/lib/module/highlight-updates/utils/testIDs.js +60 -0
  34. package/lib/module/index.js +5 -1
  35. package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +1 -1
  36. package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +1 -1
  37. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts +7 -1
  38. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +1 -1
  39. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +1 -1
  40. package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +1 -1
  41. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts +8 -1
  42. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +1 -1
  43. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +1 -1
  44. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +1 -1
  45. package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +1 -1
  46. package/lib/typescript/highlight-updates/components/RendersCopySettingsView.d.ts.map +1 -1
  47. package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +1 -1
  48. package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts.map +1 -1
  49. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -1
  50. package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +1 -1
  51. package/lib/typescript/highlight-updates/utils/ownStateChanged.d.ts +66 -0
  52. package/lib/typescript/highlight-updates/utils/ownStateChanged.d.ts.map +1 -0
  53. package/lib/typescript/highlight-updates/utils/testIDs.d.ts +44 -0
  54. package/lib/typescript/highlight-updates/utils/testIDs.d.ts.map +1 -0
  55. package/lib/typescript/index.d.ts +1 -0
  56. package/lib/typescript/index.d.ts.map +1 -1
  57. package/package.json +7 -7
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.detectedItemTestID = detectedItemTestID;
7
+ exports.identifierSlug = identifierSlug;
8
+ exports.patternChipTestID = patternChipTestID;
9
+ exports.renderRowTestID = renderRowTestID;
10
+ exports.settingTestID = settingTestID;
11
+ /**
12
+ * Stable e2e handles for the Render Highlighter (highlight-updates).
13
+ *
14
+ * This tool's list is the hardest thing in Buoy to address, because the thing
15
+ * it lists is a NATIVE VIEW and the only key it has for one is the fiber's
16
+ * `nativeTag` — a per-session counter that changes on every launch, on every
17
+ * remount, and (on a dynamic screen) between two runs of the same test. A
18
+ * handle built from it would name a different component every time.
19
+ *
20
+ * So rows are addressed by their IDENTIFIER, in exactly the priority the list
21
+ * item itself uses to label them: testID → nativeID → component name →
22
+ * accessibility label → nativeTag. That is the same string the developer sees
23
+ * on the row, it is stable as long as the component is, and it is what a test
24
+ * can name up front. `nativeTag` remains the last resort for an anonymous host
25
+ * view, which no test should be naming anyway.
26
+ *
27
+ * Slug rules are mirrored in `example-dev-build/utils/renderBench.js` so the
28
+ * fixture and the tool agree about what a row is called; the suite's
29
+ * `fixture.js` spot-checks that they still do.
30
+ */
31
+
32
+ /** Any identifier string → a testID-safe slug. */
33
+ function identifierSlug(value) {
34
+ return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
35
+ }
36
+
37
+ /**
38
+ * `buoy-highlight-row-<slug>` for one row of the render list.
39
+ *
40
+ * Sub-handles hang off it: `-name`, `-count`, `-type`, `-cause`, `-identifier`,
41
+ * `-time`. The count is the tool's whole claim, so it gets its own handle
42
+ * rather than living inside the row's merged label.
43
+ */
44
+ function renderRowTestID(identifier) {
45
+ return `buoy-highlight-row-${identifierSlug(identifier)}`;
46
+ }
47
+
48
+ /**
49
+ * `buoy-highlight-detected-<type>-<slug>` for one chip in DETECTED ITEMS.
50
+ *
51
+ * The type is in the handle because the same string legitimately appears under
52
+ * two categories — a component called `Pressable` and a viewType called
53
+ * `Pressable` are different filters with the same name, and a suite that added
54
+ * the wrong one would still see "a pattern was added".
55
+ */
56
+ function detectedItemTestID(type, value) {
57
+ return `buoy-highlight-detected-${identifierSlug(type)}-${identifierSlug(value)}`;
58
+ }
59
+
60
+ /** `buoy-highlight-<mode>-chip-<type>-<slug>` for an active filter pattern. */
61
+ function patternChipTestID(mode, type, value) {
62
+ return `buoy-highlight-${mode}-chip-${identifierSlug(type)}-${identifierSlug(value)}`;
63
+ }
64
+
65
+ /** `buoy-highlight-setting-<key>` for a settings row's control. */
66
+ function settingTestID(key) {
67
+ return `buoy-highlight-setting-${key}`;
68
+ }
@@ -141,6 +141,12 @@ Object.defineProperty(exports, "detectRenderCause", {
141
141
  return _RenderCauseDetector.detectRenderCause;
142
142
  }
143
143
  });
144
+ Object.defineProperty(exports, "detectedItemTestID", {
145
+ enumerable: true,
146
+ get: function () {
147
+ return _testIDs.detectedItemTestID;
148
+ }
149
+ });
144
150
  Object.defineProperty(exports, "disableProfilerLogging", {
145
151
  enumerable: true,
146
152
  get: function () {
@@ -207,6 +213,12 @@ Object.defineProperty(exports, "highlightUpdatesSyncAdapter", {
207
213
  return _highlightUpdatesSyncAdapter.highlightUpdatesSyncAdapter;
208
214
  }
209
215
  });
216
+ Object.defineProperty(exports, "identifierSlug", {
217
+ enumerable: true,
218
+ get: function () {
219
+ return _testIDs.identifierSlug;
220
+ }
221
+ });
210
222
  Object.defineProperty(exports, "installProfilerInterceptor", {
211
223
  enumerable: true,
212
224
  get: function () {
@@ -238,12 +250,24 @@ Object.defineProperty(exports, "isLoggingEnabled", {
238
250
  }
239
251
  });
240
252
  exports.onRenderEvent = void 0;
253
+ Object.defineProperty(exports, "patternChipTestID", {
254
+ enumerable: true,
255
+ get: function () {
256
+ return _testIDs.patternChipTestID;
257
+ }
258
+ });
241
259
  Object.defineProperty(exports, "removeRenderCauseState", {
242
260
  enumerable: true,
243
261
  get: function () {
244
262
  return _RenderCauseDetector.removeRenderCauseState;
245
263
  }
246
264
  });
265
+ Object.defineProperty(exports, "renderRowTestID", {
266
+ enumerable: true,
267
+ get: function () {
268
+ return _testIDs.renderRowTestID;
269
+ }
270
+ });
247
271
  Object.defineProperty(exports, "safeCloneForHistory", {
248
272
  enumerable: true,
249
273
  get: function () {
@@ -262,6 +286,12 @@ Object.defineProperty(exports, "setComparisonCallback", {
262
286
  return _ProfilerInterceptor.setComparisonCallback;
263
287
  }
264
288
  });
289
+ Object.defineProperty(exports, "settingTestID", {
290
+ enumerable: true,
291
+ get: function () {
292
+ return _testIDs.settingTestID;
293
+ }
294
+ });
265
295
  Object.defineProperty(exports, "uninstallProfilerInterceptor", {
266
296
  enumerable: true,
267
297
  get: function () {
@@ -287,6 +317,7 @@ var _HighlightFilterView = require("./highlight-updates/components/HighlightFilt
287
317
  var _RenderCauseBadge = require("./highlight-updates/components/RenderCauseBadge");
288
318
  var _RenderHistoryViewer = require("./highlight-updates/components/RenderHistoryViewer");
289
319
  var _renderExportFormatter = require("./highlight-updates/utils/renderExportFormatter");
320
+ var _testIDs = require("./highlight-updates/utils/testIDs");
290
321
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
291
322
  /**
292
323
  * @buoy-gg/highlight-updates
@@ -392,4 +423,7 @@ const onRenderEvent = callback => {
392
423
  // footer) instead of inside a JsModal — for hosts that show tools in a panel.
393
424
 
394
425
  // Simple JSON export for renders
426
+
427
+ // e2e handle builders — exported so the example app's fixture can derive the
428
+ // same row handles the tool renders instead of hardcoding a second copy.
395
429
  exports.onRenderEvent = onRenderEvent;
@@ -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,6 +970,9 @@ 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
@@ -909,6 +987,7 @@ export function HighlightFilterView({
909
987
  style: styles.settingLabel,
910
988
  children: "Show host views"
911
989
  }), /*#__PURE__*/_jsx(Switch, {
990
+ testID: settingTestID("showHostViews"),
912
991
  value: !!settings.showHostViews,
913
992
  onValueChange: value => onSettingsChange({
914
993
  showHostViews: value
@@ -931,6 +1010,7 @@ export function HighlightFilterView({
931
1010
  style: styles.settingLabel,
932
1011
  children: "Highlight Dev Tools"
933
1012
  }), /*#__PURE__*/_jsx(Switch, {
1013
+ testID: settingTestID("excludeDevTools"),
934
1014
  value: !settings.excludeDevTools,
935
1015
  onValueChange: value => onSettingsChange({
936
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,