From bc274903b4a938862fa736564098ee08bb962153 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 13 Aug 2026 04:15:26 -0700 Subject: [PATCH 1/3] Stabilize sampling-profiler tracing test (#57924) Summary: Changelog: [Internal] Exercise JavaScript briefly before ending each trace. This gives the sampling profiler a bounded opportunity to record a stack while preserving the test comparison between disabled and enabled categories. Reviewed By: Abbondanzo Differential Revision: D115740464 --- .../ReactCommon/jsinspector-modern/tests/TracingTest.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/TracingTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/TracingTest.cpp index c371e76349e6..af45b463e1a4 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/TracingTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/TracingTest.cpp @@ -37,7 +37,15 @@ class TracingTest : public TracingTestBase< TEST_F(TracingTest, EnablesSamplingProfilerOnlyCategoryIsSpecified) { InSequence s; + const auto runSamplingWorkload = [this]() { + eval(R"( + const start = Date.now(); + while (Date.now() - start < 10) {} + )"); + }; + startTracing({}); + runSamplingWorkload(); auto allTraceEvents = endTracingAndCollectEvents(); EXPECT_THAT( @@ -47,6 +55,7 @@ TEST_F(TracingTest, EnablesSamplingProfilerOnlyCategoryIsSpecified) { AtJsonPtr("/cat", "disabled-by-default-v8.cpu_profiler"))))); startTracing({tracing::Category::JavaScriptSampling}); + runSamplingWorkload(); allTraceEvents = endTracingAndCollectEvents(); EXPECT_THAT( From 7b708e5409d363db669f1267666061da53aab6d1 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 13 Aug 2026 04:15:26 -0700 Subject: [PATCH 2/3] Ignore stale minimum-view-time viewability updates Summary: Changelog: [General][Fixed] - Ignore stale viewability updates while enforcing minimum view time Minimum view-time callbacks can outlive a newer viewport update. During initial layout, callbacks for intermediate viewport snapshots can fire after the current snapshot and replace the correct visible-item set. Discard callbacks whose captured visible-index set is no longer current, and cover the transition with a unit test. Reviewed By: Abbondanzo Differential Revision: D115740467 --- .../Lists/__tests__/FlatList-itest.js | 6 +++ .../Lists/ViewabilityHelper.js | 4 ++ .../Lists/__tests__/ViewabilityHelper-test.js | 52 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/packages/react-native/Libraries/Lists/__tests__/FlatList-itest.js b/packages/react-native/Libraries/Lists/__tests__/FlatList-itest.js index 15afb533d503..c290b5d3da74 100644 --- a/packages/react-native/Libraries/Lists/__tests__/FlatList-itest.js +++ b/packages/react-native/Libraries/Lists/__tests__/FlatList-itest.js @@ -680,6 +680,12 @@ describe('', () => { timers.advanceTimersByTime(200); + // The first timer represents an intermediate viewport snapshot and + // must not publish after the final visible set supersedes it. + expect(onViewableItemsChanged).not.toHaveBeenCalled(); + + timers.advanceTimersByTime(200); + expect(onViewableItemsChanged).toHaveBeenCalled(); } finally { timers.uninstall(); diff --git a/packages/virtualized-lists/Lists/ViewabilityHelper.js b/packages/virtualized-lists/Lists/ViewabilityHelper.js index 08c7ef8c80c4..8157efa4dfa5 100644 --- a/packages/virtualized-lists/Lists/ViewabilityHelper.js +++ b/packages/virtualized-lists/Lists/ViewabilityHelper.js @@ -231,6 +231,10 @@ class ViewabilityHelper { * comment suppresses an error found when Flow v0.63 was deployed. To * see the error delete this comment and run Flow. */ this._timers.delete(handle); + // `onUpdate` replaces the array whenever the visible set changes. + if (this._viewableIndices !== viewableIndices) { + return; + } this._onUpdateSync( props, viewableIndices, diff --git a/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js b/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js index 3fc9f79cc47e..257757048d6e 100644 --- a/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js +++ b/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js @@ -342,6 +342,58 @@ describe('onUpdate', function () { }); }); + it('minimumViewTime ignores stale viewability updates', function () { + const helper = new ViewabilityHelper({ + minimumViewTime: 350, + viewAreaCoveragePercentThreshold: 0, + }); + rowFrames = { + a: {y: 0, height: 200}, + b: {y: 200, height: 200}, + }; + data = [{key: 'a'}, {key: 'b'}]; + const onViewableItemsChanged = jest.fn(); + helper.onUpdate( + props, + 0, + 200, + // $FlowFixMe[incompatible-type] - Invalid `ListMetricsAggregator`. + {getCellMetrics}, + createViewToken, + onViewableItemsChanged, + ); + helper.onUpdate( + props, + 0, + 400, + // $FlowFixMe[incompatible-type] - Invalid `ListMetricsAggregator`. + {getCellMetrics}, + createViewToken, + onViewableItemsChanged, + ); + + jest.runAllTimers(); + + expect(onViewableItemsChanged.mock.calls).toEqual([ + [ + { + changed: [ + {isViewable: true, key: 'a'}, + {isViewable: true, key: 'b'}, + ], + viewabilityConfig: { + minimumViewTime: 350, + viewAreaCoveragePercentThreshold: 0, + }, + viewableItems: [ + {isViewable: true, key: 'a'}, + {isViewable: true, key: 'b'}, + ], + }, + ], + ]); + }); + it('minimumViewTime skips briefly visible items', function () { const helper = new ViewabilityHelper({ minimumViewTime: 350, From 0737f63e899e3a2429a3d8dd3d09258a827ae0df Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 13 Aug 2026 04:15:26 -0700 Subject: [PATCH 3/3] Wait for images before box-shadow screenshots (#57922) Summary: Changelog: [Internal] Expose the box-shadow example test identifier only after all three images have loaded. The E2E navigation helper now waits for stable image content before capturing the screenshot, and the Nougat golden reflects the complete example. Reviewed By: Abbondanzo Differential Revision: D115740465 --- .../js/examples/Image/ImageExample.js | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 2e75d03c5e48..7b8450989ed5 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -1258,6 +1258,41 @@ exports.category = 'Basic'; exports.description = 'Base component for displaying different types of images.'; +component BoxShadowExample() { + const [loadedImageCount, setLoadedImageCount] = useState(0); + const onLoad = () => setLoadedImageCount(count => count + 1); + + return ( + = 3 ? 'box-shadow-example' : undefined}> + + + + + ); +} + exports.examples = [ { title: 'Plain Network Image with `source` prop.', @@ -1488,34 +1523,7 @@ exports.examples = [ title: 'Box Shadow', name: 'box-shadow', render: function (): React.Node { - return ( - - - - - - ); + return ; }, }, {