From 35390027a0a38ec994014f19fe3217f13c384c7c Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Wed, 21 Apr 2021 15:47:15 -0700 Subject: [PATCH 01/10] Add unit tests for VirtualizedList render quirks This change adds a series of snapshot tests to validate the render output of VirtualizedList in mixed scenarios. Jest timer mocks are used to measure rendering at different ticks. These test cases mostly center around realization logic, to help prevent regressions when chaning internal state representation. --- .../Lists/__tests__/VirtualizedList-test.js | 938 ++++- .../VirtualizedList-test.js.snap | 3519 ++++++++++++++++- 2 files changed, 4166 insertions(+), 291 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index 260a592fe4f008..c8cba907cb5649 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -104,6 +104,28 @@ describe('VirtualizedList', () => { expect(component).toMatchSnapshot(); }); + it('renders empty list after batch', () => { + const component = ReactTestRenderer.create( + } + getItem={(data, index) => data[index]} + getItemCount={data => data.length} + />, + ); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); + }); + it('renders null list', () => { const component = ReactTestRenderer.create( { // onLayout, which can cause https://github.com/facebook/react-native/issues/16067 instance._onContentSizeChange(300, initialContentHeight); instance._onContentSizeChange(300, data.length * ITEM_HEIGHT); - jest.runAllTimers(); + performAllBatches(); expect(onEndReached).not.toHaveBeenCalled(); @@ -384,7 +406,7 @@ describe('VirtualizedList', () => { contentInset: {right: 0, top: 0, left: 0, bottom: 0}, }, }); - jest.runAllTimers(); + performAllBatches(); expect(onEndReached).toHaveBeenCalled(); }); @@ -506,28 +528,30 @@ describe('VirtualizedList', () => { }); it('forwards correct stickyHeaderIndices when all in initial render window', () => { - const items = Array(10) - .fill() - .map((_, i) => (i % 3 === 0 ? {key: i, sticky: true} : {key: i})); - const stickyIndices = items - .filter(item => item.sticky) - .map(item => item.key); + const items = generateItemsStickyEveryN(10, 3); + const ITEM_HEIGHT = 10; + const component = ReactTestRenderer.create( + , + ); + + expect(component).toMatchSnapshot(); + }); + + it('forwards correct stickyHeaderIndices when ListHeaderComponent present', () => { + const items = generateItemsStickyEveryN(10, 3); const ITEM_HEIGHT = 10; const component = ReactTestRenderer.create( React.createElement('Header')} initialNumToRender={10} - renderItem={({item}) => } - getItem={(data, index) => data[index]} - getItemCount={data => data.length} - getItemLayout={(_, index) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index, - })} + {...baseItemProps(items)} + {...fixedHeightItemLayoutProps(ITEM_HEIGHT)} />, ); @@ -535,132 +559,838 @@ describe('VirtualizedList', () => { }); it('forwards correct stickyHeaderIndices when partially in initial render window', () => { - const items = Array(10) - .fill() - .map((_, i) => (i % 3 === 0 ? {key: i, sticky: true} : {key: i})); - const stickyIndices = items - .filter(item => item.sticky) - .map(item => item.key); + const items = generateItemsStickyEveryN(10, 3); const ITEM_HEIGHT = 10; const component = ReactTestRenderer.create( } - getItem={(data, index) => data[index]} - getItemCount={data => data.length} - getItemLayout={(_, index) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index, - })} + {...baseItemProps(items)} + {...fixedHeightItemLayoutProps(ITEM_HEIGHT)} />, ); expect(component).toMatchSnapshot(); }); - it('realizes sticky headers in viewport on batched render', () => { - const items = Array(10) - .fill() - .map((_, i) => (i % 3 === 0 ? {key: i, sticky: true} : {key: i})); - const stickyIndices = items - .filter(item => item.sticky) - .map(item => item.key); - + it('renders sticky headers in viewport on batched render', () => { + const items = generateItemsStickyEveryN(10, 3); const ITEM_HEIGHT = 10; - const virtualizedListProps = { - data: items, - stickyHeaderIndices: stickyIndices, - initialNumToRender: 1, - windowSize: 1, - renderItem: ({item}) => , - getItem: (data, index) => data[index], - getItemCount: data => data.length, - getItemLayout: (_, index) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index, - }), - }; - let component; - ReactTestRenderer.act(() => { component = ReactTestRenderer.create( - , + , ); }); ReactTestRenderer.act(() => { - component - .getInstance() - ._onLayout({nativeEvent: {layout: {width: 10, height: 50}}}); - component.getInstance()._onContentSizeChange(10, 100); - jest.runAllTimers(); + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performAllBatches(); }); expect(component).toMatchSnapshot(); }); - it('keeps sticky headers realized after scrolled out of viewport', () => { - const items = Array(20) - .fill() - .map((_, i) => - i % 3 === 0 ? {key: i, sticky: true} : {key: i, sticky: false}, - ); - const stickyIndices = items - .filter(item => item.sticky) - .map(item => item.key); - + it('keeps sticky headers above viewport visualized', () => { + const items = generateItemsStickyEveryN(20, 3); const ITEM_HEIGHT = 10; - const virtualizedListProps = { - data: items, - stickyHeaderIndices: stickyIndices, - initialNumToRender: 1, - windowSize: 1, - renderItem: ({item}) => , - getItem: (data, index) => data[index], - getItemCount: data => data.length, - getItemLayout: (_, index) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index, - }), - }; - let component; - ReactTestRenderer.act(() => { component = ReactTestRenderer.create( - , + , ); }); ReactTestRenderer.act(() => { - component - .getInstance() - ._onLayout({nativeEvent: {layout: {width: 10, height: 50}}}); - component.getInstance()._onContentSizeChange(10, 200); - jest.runAllTimers(); + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performAllBatches(); }); ReactTestRenderer.act(() => { - component.getInstance()._onScroll({ - nativeEvent: { - contentOffset: {x: 0, y: 150}, - contentSize: {width: 10, height: 200}, - layoutMeasurement: {width: 10, height: 50}, - }, - }); - jest.runAllTimers(); + simulateScroll(component, {x: 0, y: 150}); + performAllBatches(); }); expect(component).toMatchSnapshot(); }); }); + +it('unmounts sticky headers moved below viewport', () => { + const items = generateItemsStickyEveryN(20, 3); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + simulateScroll(component, {x: 0, y: 150}); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + simulateScroll(component, {x: 0, y: 0}); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders offset cells in initial render when initialScrollIndex set', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + const component = ReactTestRenderer.create( + , + ); + + expect(component).toMatchSnapshot(); +}); + +it('does not over-render when there is less than initialNumToRender cells', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + const component = ReactTestRenderer.create( + , + ); + + expect(component).toMatchSnapshot(); +}); + +it('retains intitial render if initialScrollIndex == 0', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + simulateScroll(component, {x: 0, y: 150}); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('discards intitial render if initialScrollIndex != 0', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + component.getInstance()._onScroll({ + nativeEvent: { + contentOffset: {x: 0, y: 150}, + contentSize: {width: 10, height: 200}, + layoutMeasurement: {width: 10, height: 50}, + }, + }); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('expands render area by maxToRenderPerBatch on tick', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + const props = { + initialNumToRender: 5, + maxToRenderPerBatch: 2, + }; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performNextBatch(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('does not adjust render area until content area layed out', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + let component; + + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateViewportLayout(component, {width: 10, height: 50}); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('does not adjust render area with non-zero initialScrollIndex until scrolled', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('adjusts render area with non-zero initialScrollIndex after scrolled', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + + simulateScroll(component, {x: 0, y: 150}); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders initialNumToRender cells when virtualization disabled', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + const component = ReactTestRenderer.create( + , + ); + + expect(component).toMatchSnapshot(); +}); + +it('renders items before initialScrollIndex on first batch tick when virtualization disabled', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 100}, + }); + performNextBatch(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('eventually renders all items when virtualization disabled', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 100}, + }); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('retains initial render region when an item is appended', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + component.update( + , + ); + }); + + expect(component).toMatchSnapshot(); +}); + +it('retains batch render region when an item is appended', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 100}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + component.update( + , + ); + }); + + expect(component).toMatchSnapshot(); +}); + +it('constrains batch render region when an item is removed', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 100}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + component.update( + , + ); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders a zero-height tail spacer on initial render if getItemLayout not defined', () => { + const items = generateItems(10); + + const component = ReactTestRenderer.create( + , + ); + + expect(component).toMatchSnapshot(); +}); + +it('renders zero-height tail spacer on batch render if cells not yet measured and getItemLayout not defined', () => { + const items = generateItems(10); + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 200}, + }); + performNextBatch(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders tail spacer up to last measured index if getItemLayout not defined', () => { + const items = generateItems(10); + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + const LAST_MEASURED_CELL = 6; + for (let i = 0; i <= LAST_MEASURED_CELL; ++i) { + simulateCellLayout(component, items, i, { + width: 10, + height: 10, + x: 0, + y: 10 * i, + }); + } + + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 30}, + }); + performNextBatch(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders full tail spacer if all cells measured', () => { + const items = generateItems(10); + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + const LAST_MEASURED_CELL = 9; + for (let i = 0; i <= LAST_MEASURED_CELL; ++i) { + simulateCellLayout(component, items, i, { + width: 10, + height: 10, + x: 0, + y: 10 * i, + }); + } + + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 30}, + }); + performNextBatch(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders tail spacer using frame average when getItemLayout undefined', () => { + const items = generateItems(10); + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + const LAST_MEASURED_CELL = 6; + for (let i = 0; i <= LAST_MEASURED_CELL; ++i) { + simulateCellLayout(component, items, i, { + width: 10, + height: i, + x: 0, + y: 10 * i, + }); + } + + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: 30}, + }); + performNextBatch(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders windowSize derived region at top', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 20}, + content: {width: 10, height: 100}, + }); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders windowSize derived region in middle', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 20}, + content: {width: 10, height: 100}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + simulateScroll(component, {x: 0, y: 50}); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +it('renders windowSize derived region at bottom', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 20}, + content: {width: 10, height: 100}, + }); + performAllBatches(); + }); + + ReactTestRenderer.act(() => { + simulateScroll(component, {x: 0, y: 80}); + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); +}); + +function generateItems(count) { + return Array(count) + .fill() + .map((_, i) => ({key: i})); +} + +function generateItemsStickyEveryN(count, n) { + return Array(count) + .fill() + .map((_, i) => (i % n === 0 ? {key: i, sticky: true} : {key: i})); +} + +function baseItemProps(items) { + return { + data: items, + renderItem: ({item}) => + React.createElement('MockCellItem', {value: item.key, ...item}), + getItem: (data, index) => data[index], + getItemCount: data => data.length, + stickyHeaderIndices: stickyHeaderIndices(items), + }; +} + +function stickyHeaderIndices(items) { + return items.filter(item => item.sticky).map(item => item.key); +} + +function fixedHeightItemLayoutProps(height) { + return { + getItemLayout: (_, index) => ({ + length: height, + offset: height * index, + index, + }), + }; +} + +let lastViewportLayout; +let lastContentLayout; + +function simulateLayout(component, args) { + simulateViewportLayout(component, args.viewport); + simulateContentLayout(component, args.content); +} + +function simulateViewportLayout(component, dimensions) { + lastViewportLayout = dimensions; + component.getInstance()._onLayout({nativeEvent: {layout: dimensions}}); +} + +function simulateContentLayout(component, dimensions) { + lastContentLayout = dimensions; + component + .getInstance() + ._onContentSizeChange(dimensions.width, dimensions.height); +} + +function simulateCellLayout(component, items, itemIndex, dimensions) { + const instance = component.getInstance(); + const cellKey = instance._keyExtractor(items[itemIndex], itemIndex); + instance._onCellLayout( + {nativeEvent: {layout: dimensions}}, + cellKey, + itemIndex, + ); +} + +function simulateScroll(component, position) { + component.getInstance()._onScroll({ + nativeEvent: { + contentOffset: position, + contentSize: lastContentLayout, + layoutMeasurement: lastViewportLayout, + }, + }); +} + +function performAllBatches() { + jest.runAllTimers(); +} + +function performNextBatch() { + jest.runOnlyPendingTimers(); +} diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index 6bd99586b38897..05727566a8733a 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -1,5 +1,152 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`VirtualizedList forwards correct stickyHeaderIndices when ListHeaderComponent present 1`] = ` + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + exports[`VirtualizedList forwards correct stickyHeaderIndices when all in initial render window 1`] = ` - @@ -74,21 +221,21 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when all in initia - - - @@ -96,21 +243,21 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when all in initia - - - @@ -118,21 +265,21 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when all in initia - - - @@ -205,7 +352,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when partially in - @@ -213,21 +360,21 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when partially in - - - @@ -235,7 +382,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when partially in - @@ -613,7 +760,7 @@ exports[`VirtualizedList handles separators correctly 3`] = ` `; -exports[`VirtualizedList keeps sticky headers realized after scrolled out of viewport 1`] = ` +exports[`VirtualizedList keeps sticky headers above viewport visualized 1`] = ` - + style={null} + > + + - + + + + + + + + + + + + + style={null} + > + + - + + + @@ -766,23 +937,21 @@ exports[`VirtualizedList keeps sticky headers realized after scrolled out of vie - - - @@ -790,23 +959,21 @@ exports[`VirtualizedList keeps sticky headers realized after scrolled out of vie - - - @@ -814,23 +981,21 @@ exports[`VirtualizedList keeps sticky headers realized after scrolled out of vie - - - @@ -838,8 +1003,7 @@ exports[`VirtualizedList keeps sticky headers realized after scrolled out of vie - @@ -847,138 +1011,28 @@ exports[`VirtualizedList keeps sticky headers realized after scrolled out of vie `; -exports[`VirtualizedList realizes sticky headers in viewport on batched render 1`] = ` +exports[`VirtualizedList renders all the bells and whistles 1`] = ` - - - - - - - - - - - - - - - - - - - -`; - -exports[`VirtualizedList renders all the bells and whistles 1`] = ` - `; +exports[`VirtualizedList renders empty list after batch 1`] = ` + + + +`; + exports[`VirtualizedList renders empty list with empty component 1`] = ` `; +exports[`VirtualizedList renders sticky headers in viewport on batched render 1`] = ` + + + + + + + + + + + + + + + + + + + + +`; + exports[`VirtualizedList test getItem functionality where data is not an Array 1`] = ` `; + +exports[`adjusts render area with non-zero initialScrollIndex after scrolled 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`constrains batch render region when an item is removed 1`] = ` + + + + + + + + + + + + + + + + + + + +`; + +exports[`discards intitial render if initialScrollIndex != 0 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`does not adjust render area until content area layed out 1`] = ` + + + + + + + + + + + + + + + + + + + + +`; + +exports[`does not adjust render area with non-zero initialScrollIndex until scrolled 1`] = ` + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`does not over-render when there is less than initialNumToRender cells 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`eventually renders all items when virtualization disabled 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`expands render area by maxToRenderPerBatch on tick 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders a zero-height tail spacer on initial render if getItemLayout not defined 1`] = ` + + + + + + + + + + + + + + +`; + +exports[`renders full tail spacer if all cells measured 1`] = ` + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders initialNumToRender cells when virtualization disabled 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders items before initialScrollIndex on first batch tick when virtualization disabled 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders offset cells in initial render when initialScrollIndex set 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders tail spacer up to last measured index if getItemLayout not defined 1`] = ` + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders tail spacer using frame average when getItemLayout undefined 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders windowSize derived region at bottom 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders windowSize derived region at top 1`] = ` + + + + + + + + + + + + + + + + + +`; + +exports[`renders windowSize derived region in middle 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders zero-height tail spacer on batch render if cells not yet measured and getItemLayout not defined 1`] = ` + + + + + + + + + + + + + + +`; + +exports[`retains batch render region when an item is appended 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`retains initial render region when an item is appended 1`] = ` + + + + + + + + + + + + + + +`; + +exports[`retains intitial render if initialScrollIndex == 0 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`unmounts sticky headers moved below viewport 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; From 23fe28dcd44706115390d872efdd6a11449da07a Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 23 Apr 2021 15:14:15 -0700 Subject: [PATCH 02/10] Fix a couple of tests --- .../Lists/__tests__/VirtualizedList-test.js | 7 +- .../VirtualizedList-test.js.snap | 80 ++++++++++++++----- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index c8cba907cb5649..35ee1ef0df5085 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -899,6 +899,7 @@ it('renders initialNumToRender cells when virtualization disabled', () => { , @@ -915,10 +916,11 @@ it('renders items before initialScrollIndex on first batch tick when virtualizat ReactTestRenderer.act(() => { component = ReactTestRenderer.create( , @@ -948,6 +950,7 @@ it('eventually renders all items when virtualization disabled', () => { initialScrollIndex={1} windowSize={1} maxToRenderPerBatch={10} + disableVirtualization {...baseItemProps(items)} {...fixedHeightItemLayoutProps(ITEM_HEIGHT)} />, diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index 05727566a8733a..1e36c701d2632a 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -2545,6 +2545,7 @@ exports[`eventually renders all items when virtualization disabled 1`] = ` }, ] } + disableVirtualization={true} getItem={[Function]} getItemCount={[Function]} getItemLayout={[Function]} @@ -2607,12 +2608,33 @@ exports[`eventually renders all items when virtualization disabled 1`] = ` /> + style={null} + > + + + + + + + + + + + `; @@ -2986,6 +3008,7 @@ exports[`renders initialNumToRender cells when virtualization disabled 1`] = ` }, ] } + disableVirtualization={true} getItem={[Function]} getItemCount={[Function]} getItemLayout={[Function]} @@ -3045,13 +3068,6 @@ exports[`renders initialNumToRender cells when virtualization disabled 1`] = ` value={5} /> - `; @@ -3092,11 +3108,12 @@ exports[`renders items before initialScrollIndex on first batch tick when virtua }, ] } + disableVirtualization={true} getItem={[Function]} getItemCount={[Function]} getItemLayout={[Function]} - initialNumToRender={5} - initialScrollIndex={1} + initialNumToRender={1} + initialScrollIndex={4} maxToRenderPerBatch={10} onContentSizeChange={[Function]} onLayout={[Function]} @@ -3154,12 +3171,33 @@ exports[`renders items before initialScrollIndex on first batch tick when virtua /> + style={null} + > + + + + + + + + + + + `; From 7482c26d81d25b409fdd4d2a8bc70f4175e9aabe Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 23 Apr 2021 15:48:38 -0700 Subject: [PATCH 03/10] Add some more tests --- .../Lists/__tests__/VirtualizedList-test.js | 43 +++++ .../VirtualizedList-test.js.snap | 182 ++++++++++++++++++ 2 files changed, 225 insertions(+) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index 35ee1ef0df5085..d94425c20143f9 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -908,6 +908,49 @@ it('renders initialNumToRender cells when virtualization disabled', () => { expect(component).toMatchSnapshot(); }); +it('renders no spacers up to initialScrollIndex on first render when virtualization disabled', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + expect(component).toMatchSnapshot(); +}); + +it('expands first in viewport to render up to maxToRenderPerBatch on initial render', () => { + const items = generateItems(10); + const ITEM_HEIGHT = 10; + + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + expect(component).toMatchSnapshot(); +}); + it('renders items before initialScrollIndex on first batch tick when virtualization disabled', () => { const items = generateItems(10); const ITEM_HEIGHT = 10; diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index 1e36c701d2632a..b839118ad30c60 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -2639,6 +2639,114 @@ exports[`eventually renders all items when virtualization disabled 1`] = ` `; +exports[`expands first in viewport to render up to maxToRenderPerBatch on initial render 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + +`; + exports[`expands render area by maxToRenderPerBatch on tick 1`] = ` `; +exports[`renders no spacers up to initialScrollIndex on first render when virtualization disabled 1`] = ` + + + + + + + + + + +`; + exports[`renders offset cells in initial render when initialScrollIndex set 1`] = ` Date: Fri, 23 Apr 2021 16:55:28 -0700 Subject: [PATCH 04/10] Fix faulty layout mocking --- .../Lists/__tests__/VirtualizedList-test.js | 5 ++++- .../__snapshots__/VirtualizedList-test.js.snap | 18 +----------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index d94425c20143f9..e28b84f84075e3 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -1243,13 +1243,16 @@ it('renders tail spacer using frame average when getItemLayout undefined', () => ReactTestRenderer.act(() => { const LAST_MEASURED_CELL = 6; + + let currentY = 0; for (let i = 0; i <= LAST_MEASURED_CELL; ++i) { simulateCellLayout(component, items, i, { width: 10, height: i, x: 0, - y: 10 * i, + y: currentY + i, }); + currentY += i; } simulateLayout(component, { diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index b839118ad30c60..ead975be037f8e 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -3693,26 +3693,10 @@ exports[`renders tail spacer using frame average when getItemLayout undefined 1` value={3} /> - - - - - - From 9a8c55a3e1d56eab162be627bc97ded2eb4316b3 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 23 Apr 2021 17:33:58 -0700 Subject: [PATCH 05/10] renaming --- .../Lists/__tests__/VirtualizedList-test.js | 22 +++++++++---------- .../VirtualizedList-test.js.snap | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index e28b84f84075e3..c34aa31fa243e4 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -1190,7 +1190,7 @@ it('renders tail spacer up to last measured index if getItemLayout not defined', expect(component).toMatchSnapshot(); }); -it('renders full tail spacer if all cells measured', () => { +it('renders tail spacer up to last measured with irregular layout when getItemLayout undefined', () => { const items = generateItems(10); let component; @@ -1206,14 +1206,17 @@ it('renders full tail spacer if all cells measured', () => { }); ReactTestRenderer.act(() => { - const LAST_MEASURED_CELL = 9; + const LAST_MEASURED_CELL = 6; + + let currentY = 0; for (let i = 0; i <= LAST_MEASURED_CELL; ++i) { simulateCellLayout(component, items, i, { width: 10, - height: 10, + height: i, x: 0, - y: 10 * i, + y: currentY + i, }); + currentY += i; } simulateLayout(component, { @@ -1226,7 +1229,7 @@ it('renders full tail spacer if all cells measured', () => { expect(component).toMatchSnapshot(); }); -it('renders tail spacer using frame average when getItemLayout undefined', () => { +it('renders full tail spacer if all cells measured', () => { const items = generateItems(10); let component; @@ -1242,17 +1245,14 @@ it('renders tail spacer using frame average when getItemLayout undefined', () => }); ReactTestRenderer.act(() => { - const LAST_MEASURED_CELL = 6; - - let currentY = 0; + const LAST_MEASURED_CELL = 9; for (let i = 0; i <= LAST_MEASURED_CELL; ++i) { simulateCellLayout(component, items, i, { width: 10, - height: i, + height: 10, x: 0, - y: currentY + i, + y: 10 * i, }); - currentY += i; } simulateLayout(component, { diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index ead975be037f8e..9ccf61b8fb3d6a 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -3608,7 +3608,7 @@ exports[`renders tail spacer up to last measured index if getItemLayout not defi `; -exports[`renders tail spacer using frame average when getItemLayout undefined 1`] = ` +exports[`renders tail spacer up to last measured with irregular layout when getItemLayout undefined 1`] = ` Date: Tue, 8 Jun 2021 22:40:07 -0700 Subject: [PATCH 06/10] window->windowSize --- Libraries/Lists/__tests__/VirtualizedList-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index c34aa31fa243e4..8b4518f0a92c8b 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -610,7 +610,7 @@ describe('VirtualizedList', () => { component = ReactTestRenderer.create( , @@ -643,7 +643,7 @@ it('unmounts sticky headers moved below viewport', () => { component = ReactTestRenderer.create( , From d5fc5441581e88ff5e9070ab0347596350b28f0c Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 1 Jul 2021 06:09:28 -0700 Subject: [PATCH 07/10] Update snapshots for window->windowSize --- .../VirtualizedList-test.js.snap | 195 +++--------------- 1 file changed, 25 insertions(+), 170 deletions(-) diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index 9ccf61b8fb3d6a..186223702cd844 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -849,15 +849,14 @@ exports[`VirtualizedList keeps sticky headers above viewport visualized 1`] = ` stickyHeaderIndices={ Array [ 0, - 3, - 6, - 9, - 12, - 15, - 18, + 2, + 4, + 7, + 10, + 13, ] } - window={1} + windowSize={1} > - - - - - - - - - - - - - - + style={ + Object { + "height": 50, + } + } + /> @@ -913,19 +883,12 @@ exports[`VirtualizedList keeps sticky headers above viewport visualized 1`] = ` /> - - - - - + style={ + Object { + "height": 20, + } + } + /> @@ -4641,14 +4604,9 @@ exports[`unmounts sticky headers moved below viewport 1`] = ` Array [ 0, 3, - 6, - 9, - 12, - 15, - 18, ] } - window={1} + windowSize={1} > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style={ + Object { + "height": 150, + } + } + /> `; From 9499dca9422059e2e5a6cfc50947f932a00888c4 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 1 Jul 2021 07:57:02 -0700 Subject: [PATCH 08/10] Add descriptions --- .../Lists/__tests__/VirtualizedList-test.js | 116 +++++++++++++++--- .../VirtualizedList-test.js.snap | 28 +---- 2 files changed, 104 insertions(+), 40 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index 8b4518f0a92c8b..d9ee704f02c017 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -539,6 +539,9 @@ describe('VirtualizedList', () => { />, ); + // The initial render is specified to be the length of items provided. + // Expect that all sticky items (1 every 3) are passed to the underlying + // scrollview. expect(component).toMatchSnapshot(); }); @@ -555,6 +558,9 @@ describe('VirtualizedList', () => { />, ); + // The initial render is specified to be the length of items provided. + // Expect that all sticky items (1 every 3) are passed to the underlying + // scrollview, indices offset by 1 to account for the the header component. expect(component).toMatchSnapshot(); }); @@ -571,6 +577,9 @@ describe('VirtualizedList', () => { />, ); + // The initial render is specified to be half the length of items provided. + // Expect that all sticky items of index < 5 are passed to the underlying + // scrollview. expect(component).toMatchSnapshot(); }); @@ -593,11 +602,14 @@ describe('VirtualizedList', () => { ReactTestRenderer.act(() => { simulateLayout(component, { viewport: {width: 10, height: 50}, - content: {width: 10, height: 200}, + content: {width: 10, height: 100}, }); performAllBatches(); }); + // A windowSize of 1 means we will render just the viewport height (50dip). + // Expect 5 10dip items to eventually be rendered, with sticky headers in + // the first 5 propagated. expect(component).toMatchSnapshot(); }); @@ -630,6 +642,11 @@ describe('VirtualizedList', () => { performAllBatches(); }); + // Scroll to the bottom 50 dip (last five items) of the content. Expect the + // last five items to be rendered, along with every sticky header above, + // even though they are out of the viewport window in layout coordinates. + // This is because they will remain rendered even once scrolled-past in + // layout space. expect(component).toMatchSnapshot(); }); }); @@ -668,6 +685,9 @@ it('unmounts sticky headers moved below viewport', () => { performAllBatches(); }); + // Scroll to the bottom 50 dip (last five items) of the content, then back up + // to the first 5. Ensure that sticky items are unmounted once they are below + // the render area. expect(component).toMatchSnapshot(); }); @@ -684,6 +704,7 @@ it('renders offset cells in initial render when initialScrollIndex set', () => { />, ); + // Check that the first render respects initialScrollIndex expect(component).toMatchSnapshot(); }); @@ -700,6 +721,8 @@ it('does not over-render when there is less than initialNumToRender cells', () = />, ); + // Check that the first render clamps to the last item when intialNumToRender + // goes over it. expect(component).toMatchSnapshot(); }); @@ -733,6 +756,9 @@ it('retains intitial render if initialScrollIndex == 0', () => { performAllBatches(); }); + // If initialScrollIndex is 0 (the default), we should never unmount the top + // initialNumToRender as part of the "scroll to top optimization", even after + // scrolling to the bottom five items. expect(component).toMatchSnapshot(); }); @@ -762,16 +788,12 @@ it('discards intitial render if initialScrollIndex != 0', () => { }); ReactTestRenderer.act(() => { - component.getInstance()._onScroll({ - nativeEvent: { - contentOffset: {x: 0, y: 150}, - contentSize: {width: 10, height: 200}, - layoutMeasurement: {width: 10, height: 50}, - }, - }); + simulateScroll(component, {x: 0, y: 150}); performAllBatches(); }); + // If initialScrollIndex is not 0, we do not enable retaining initial render + // as part of "scroll to top" optimization. expect(component).toMatchSnapshot(); }); @@ -803,6 +825,11 @@ it('expands render area by maxToRenderPerBatch on tick', () => { performNextBatch(); }); + // We start by rendering 5 items in the initial render, but have default + // windowSize, enabling eventual rendering up to 20 viewports worth of + // content. We limit this to rendering 2 items per-batch via + // maxToRenderPerBatch, so we should only have 7 items rendered after the + // initial timer tick. expect(component).toMatchSnapshot(); }); @@ -828,6 +855,9 @@ it('does not adjust render area until content area layed out', () => { performAllBatches(); }); + // We should not start layout-based logic to expand rendered area until + // content is layed out. Expect only the 5 initial items to be rendered after + // processing all batch work, even though the windowSize allows for more. expect(component).toMatchSnapshot(); }); @@ -857,6 +887,10 @@ it('does not adjust render area with non-zero initialScrollIndex until scrolled' performAllBatches(); }); + // Layout information from before the time we scroll to initial index may not + // correspond to the area "initialScrollIndex" points to. Expect only the 5 + // initial items (starting at initialScrollIndex) to be rendered after + // processing all batch work, even though the windowSize allows for more. expect(component).toMatchSnapshot(); }); @@ -884,10 +918,12 @@ it('adjusts render area with non-zero initialScrollIndex after scrolled', () => content: {width: 10, height: 200}, }); - simulateScroll(component, {x: 0, y: 150}); + simulateScroll(component, {x: 0, y: 10}); performAllBatches(); }); + // We should expand the render area after receiving a message indcating we + // arrived at initialScrollIndex. expect(component).toMatchSnapshot(); }); @@ -905,6 +941,8 @@ it('renders initialNumToRender cells when virtualization disabled', () => { />, ); + // We should render initialNumToRender items with no spacers on initial render + // when virtualization is disabled expect(component).toMatchSnapshot(); }); @@ -918,7 +956,6 @@ it('renders no spacers up to initialScrollIndex on first render when virtualizat { performAllBatches(); }); + // After all batch ticks, all items should eventually be rendered when\ + // virtualization is disabled. expect(component).toMatchSnapshot(); }); @@ -1037,6 +1085,9 @@ it('retains initial render region when an item is appended', () => { ); }); + // Adding an item to the list before batch render should keep the existing + // rendered items rendered. Expect the first 3 items rendered, and a spacer + // for 8 items (including the 11th, added item). expect(component).toMatchSnapshot(); }); @@ -1076,6 +1127,10 @@ it('retains batch render region when an item is appended', () => { ); }); + // Adding an item to the list after batch render should keep the existing + // rendered items rendered. We batch render 10 items, then add an 11th. Expect + // the first ten items to be present, with a spacer for the 11th until the + // next batch render. expect(component).toMatchSnapshot(); }); @@ -1115,6 +1170,9 @@ it('constrains batch render region when an item is removed', () => { ); }); + // If the number of items is reduced, we should remove the corresponding + // already rendered items. Expect there to be 5 items present. New items in a + // previously occupied index may also be immediately rendered. expect(component).toMatchSnapshot(); }); @@ -1125,6 +1183,9 @@ it('renders a zero-height tail spacer on initial render if getItemLayout not def , ); + // Do not add space for out-of-viewport content on initial render when we do + // not yet know how large it should be (no getItemLayout and cell onLayout not + // yet called). Expect the tail spacer not to occupy space. expect(component).toMatchSnapshot(); }); @@ -1151,6 +1212,9 @@ it('renders zero-height tail spacer on batch render if cells not yet measured an performNextBatch(); }); + // Do not add space for out-of-viewport content unless the cell has previously + // been layed out and measurements cached. Expect the tail spacer not to + // occupy space. expect(component).toMatchSnapshot(); }); @@ -1187,6 +1251,10 @@ it('renders tail spacer up to last measured index if getItemLayout not defined', performNextBatch(); }); + // If cells in the out-of-viewport area have been measured, their space can be + // incorporated into the tail spacer, without space for the cells we can not + // measure until layout. Expect there to be a tail spacer occupying the space + // for measured, but not yet rendered items (up to and including item 6). expect(component).toMatchSnapshot(); }); @@ -1226,6 +1294,10 @@ it('renders tail spacer up to last measured with irregular layout when getItemLa performNextBatch(); }); + // If cells in the out-of-viewport area have been measured, their space can be + // incorporated into the tail spacer, without space for the cells we can not + // measure until layout. Expect there to be a tail spacer occupying the space + // for measured, but not yet rendered items (up to and including item 6). expect(component).toMatchSnapshot(); }); @@ -1262,6 +1334,8 @@ it('renders full tail spacer if all cells measured', () => { performNextBatch(); }); + // The tail-spacer should occupy the space of all non-rendered items if all + // items have been measured. expect(component).toMatchSnapshot(); }); @@ -1290,6 +1364,10 @@ it('renders windowSize derived region at top', () => { performAllBatches(); }); + // A windowSize of 3 means that we should render a viewport's worth of content + // above and below the current. A 20 dip viewport at the top of the list means + // we should render the top 4 10-dip items (for the current viewport, and + // 20dip below). expect(component).toMatchSnapshot(); }); @@ -1323,6 +1401,11 @@ it('renders windowSize derived region in middle', () => { performAllBatches(); }); + // A windowSize of 3 means that we should render a viewport's worth of content + // above and below the current. A 20 dip viewport in the top of the list means + // we should render the 6 10-dip items (for the current viewport, 20 dip above + // and below), along with retaining the top initialNumToRenderItems. We seem + // to actually render 7 in the middle due to rounding at the moment. expect(component).toMatchSnapshot(); }); @@ -1356,6 +1439,11 @@ it('renders windowSize derived region at bottom', () => { performAllBatches(); }); + // A windowSize of 3 means that we should render a viewport's worth of content + // above and below the current. A 20 dip viewport at the bottom of the list + // means we should render the bottom 4 10-dip items (for the current viewport, + // and 20dip above), along with retaining the top initialNumToRenderItems. We + // seem to actually render 4 at the bottom due to rounding at the moment. expect(component).toMatchSnapshot(); }); diff --git a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index 186223702cd844..ad93703101b2ad 100644 --- a/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -2654,7 +2654,6 @@ exports[`expands first in viewport to render up to maxToRenderPerBatch on initia renderItem={[Function]} scrollEventThrottle={50} stickyHeaderIndices={Array []} - windowSize={1} > - - - - - - - - - `; @@ -3326,7 +3303,6 @@ exports[`renders no spacers up to initialScrollIndex on first render when virtua renderItem={[Function]} scrollEventThrottle={50} stickyHeaderIndices={Array []} - windowSize={1} > Date: Thu, 1 Jul 2021 08:15:28 -0700 Subject: [PATCH 09/10] run prettier --- Libraries/Lists/__tests__/VirtualizedList-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index d9ee704f02c017..ae5d9169c1e862 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -609,7 +609,7 @@ describe('VirtualizedList', () => { // A windowSize of 1 means we will render just the viewport height (50dip). // Expect 5 10dip items to eventually be rendered, with sticky headers in - // the first 5 propagated. + // the first 5 propagated. expect(component).toMatchSnapshot(); }); @@ -966,7 +966,7 @@ it('renders no spacers up to initialScrollIndex on first render when virtualizat // There should be no spacers present in an offset initial render with // virtualiztion disabled. Only initialNumToRender items starting at - // initialScrollIndex. + // initialScrollIndex. expect(component).toMatchSnapshot(); }); @@ -989,7 +989,7 @@ it('expands first in viewport to render up to maxToRenderPerBatch on initial ren // When virtualization is disabled we may render items before initialItemIndex // if initialItemIndex + initialNumToRender < maToRenderPerBatch. Expect cells - // 0-3 to be rendered in this example, even though initialScrollIndex is 4. + // 0-3 to be rendered in this example, even though initialScrollIndex is 4. expect(component).toMatchSnapshot(); }); From 68ef8e8e6a9a8695bf6a7e7f9c43485bea9a3418 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Tue, 6 Jul 2021 15:36:05 -0700 Subject: [PATCH 10/10] update description --- Libraries/Lists/__tests__/VirtualizedList-test.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index ae5d9169c1e862..70f2022f4cbf07 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -643,10 +643,11 @@ describe('VirtualizedList', () => { }); // Scroll to the bottom 50 dip (last five items) of the content. Expect the - // last five items to be rendered, along with every sticky header above, - // even though they are out of the viewport window in layout coordinates. - // This is because they will remain rendered even once scrolled-past in - // layout space. + // last five items to be rendered (possibly more if realization window is + // larger), along with the most recent sticky header above the realization + // region, even though they are out of the viewport window in layout + // coordinates. This is because they will remain rendered even once + // scrolled-past in layout space. expect(component).toMatchSnapshot(); }); });