Chore: Convert ListView/index.test.tsx to RTL (#61590)

* Chore: Convert ListView/index.test.tsx to RTL

* Update .better.results

Co-authored-by: André Pereira <adrapereira@gmail.com>
This commit is contained in:
Hamas Shafiq 2023-01-18 14:10:31 +00:00 committed by GitHub
parent 18e8d1e28d
commit 58a86133af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 341 deletions

View File

@ -1,5 +1,5 @@
// BETTERER RESULTS V2.
//
//
// If this file contains merge conflicts, use `betterer merge` to automatically resolve them:
// https://phenomnomnominal.github.io/betterer/docs/results-file/#merge
//
@ -8,9 +8,6 @@ exports[`no enzyme tests`] = {
"packages/grafana-ui/src/components/QueryField/QueryField.test.tsx:2976628669": [
[0, 26, 13, "RegExp match", "2409514259"]
],
"packages/jaeger-ui-components/src/TraceTimelineViewer/ListView/index.test.tsx:3266788928": [
[14, 56, 13, "RegExp match", "2409514259"]
],
"packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.test.tsx:3891071965": [
[13, 42, 13, "RegExp match", "2409514259"]
]

View File

@ -1,98 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<ListView> shallow tests matches a snapshot 1`] = `
<div
style={
{
"position": "relative",
}
}
>
<div
style={
{
"height": 1640,
"position": "relative",
}
}
>
<div
className="SomeClassName"
style={
{
"margin": 0,
"padding": 0,
"position": "absolute",
"top": 0,
}
}
>
<Item
data-item-key="0"
key="0"
style={
{
"height": 2,
"position": "absolute",
"top": 0,
}
}
>
0
</Item>
<Item
data-item-key="1"
key="1"
style={
{
"height": 4,
"position": "absolute",
"top": 2,
}
}
>
1
</Item>
<Item
data-item-key="2"
key="2"
style={
{
"height": 6,
"position": "absolute",
"top": 6,
}
}
>
2
</Item>
<Item
data-item-key="3"
key="3"
style={
{
"height": 8,
"position": "absolute",
"top": 12,
}
}
>
3
</Item>
<Item
data-item-key="4"
key="4"
style={
{
"height": 10,
"position": "absolute",
"top": 20,
}
}
>
4
</Item>
</div>
</div>
</div>
`;

View File

@ -12,255 +12,53 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { TNil } from '../../types';
import { polyfill as polyfillAnimationFrame } from '../../utils/test/requestAnimationFrame';
import ListView, { TListViewProps } from './index';
// Util to get list of all callbacks added to an event emitter by event type.
// jest adds "error" event listeners to window, this util makes it easier to
// ignore those calls.
function getListenersByType(
mockFn: jest.MockContext<
void,
[
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions | undefined
]
>
) {
const rv: {
[eventType: string]: EventListenerOrEventListenerObject[];
} = {};
mockFn.calls.forEach(([eventType, callback]) => {
if (!rv[eventType]) {
rv[eventType] = [callback];
} else {
rv[eventType].push(callback);
}
});
return rv;
const DATA_LENGTH = 10;
function getHeight(index: number) {
return index * 2 + 2;
}
describe('<ListView>', () => {
// polyfill window.requestAnimationFrame (and cancel) into jsDom's window
polyfillAnimationFrame(window);
function Item(props: React.HTMLProps<HTMLDivElement>) {
const { children, ...rest } = props;
return <div {...rest}>{children}</div>;
}
const DATA_LENGTH = 40;
const renderItem: TListViewProps['itemRenderer'] = (itemKey, styles, itemIndex, attrs) => {
return (
<Item key={itemKey} style={styles} {...attrs} data-testid="item">
{itemIndex}
</Item>
);
};
function getHeight(index: number) {
return index * 2 + 2;
}
const props = {
dataLength: DATA_LENGTH,
getIndexFromKey: Number,
getKeyFromIndex: String,
initialDraw: 5,
itemHeightGetter: getHeight,
itemRenderer: renderItem,
itemsWrapperClassName: 'SomeClassName',
viewBuffer: 10,
viewBufferMin: 5,
windowScroller: true,
};
function Item(props: React.HTMLProps<HTMLDivElement>) {
const { children, ...rest } = props;
return <div {...rest}>{children}</div>;
}
const renderItem: TListViewProps['itemRenderer'] = (itemKey, styles, itemIndex, attrs) => {
return (
<Item key={itemKey} style={styles} {...attrs}>
{itemIndex}
</Item>
);
};
let instance: ListView;
const props = {
dataLength: DATA_LENGTH,
getIndexFromKey: Number,
getKeyFromIndex: String,
initialDraw: 5,
itemHeightGetter: getHeight,
itemRenderer: renderItem,
itemsWrapperClassName: 'SomeClassName',
viewBuffer: 10,
viewBufferMin: 5,
windowScroller: true,
};
describe('shallow tests', () => {
let wrapper: ShallowWrapper<TListViewProps, {}, ListView>;
beforeEach(() => {
wrapper = shallow(<ListView {...props} />);
});
it('renders without exploding', () => {
expect(wrapper).toBeDefined();
});
it('matches a snapshot', () => {
expect(wrapper).toMatchSnapshot();
});
it('initialDraw sets the number of items initially drawn', () => {
expect(wrapper.find(Item).length).toBe(props.initialDraw);
});
it('sets the height of the items according to the height func', () => {
const items = wrapper.find(Item);
const expectedHeights: number[] = [];
const heights = items.map((node, i) => {
expectedHeights.push(getHeight(i));
return node.prop('style')?.height;
});
expect(heights.length).toBe(props.initialDraw);
expect(heights).toEqual(expectedHeights);
});
it('saves the currently drawn indexes to _startIndexDrawn and _endIndexDrawn', () => {
const inst = wrapper.instance();
expect(inst._startIndexDrawn).toBe(0);
expect(inst._endIndexDrawn).toBe(props.initialDraw - 1);
});
describe('<ListView />', () => {
beforeEach(() => {
render(<ListView {...props} />);
});
describe('mount tests', () => {
let wrapper: ReactWrapper<TListViewProps, {}, ListView>;
describe('accessor functions', () => {
const clientHeight = 2;
const scrollTop = 3;
it('renders without exploding', () => {
expect(screen.getByTestId('ListView')).toBeInTheDocument();
});
let oldRender: () => JSX.Element;
let oldInitWrapper: (elm: HTMLElement | TNil) => void;
const initWrapperMock = jest.fn((elm) => {
if (elm != null) {
// jsDom requires `defineProperties` instead of just setting the props
Object.defineProperties(elm, {
clientHeight: {
get: () => clientHeight,
},
scrollTop: {
get: () => scrollTop,
},
});
}
oldInitWrapper.call(this, elm);
});
beforeAll(() => {
oldRender = ListView.prototype.render;
// `_initWrapper` is not on the prototype, so it needs to be mocked
// on each instance, use `render()` as a hook to do that
ListView.prototype.render = function altRender() {
if (this._initWrapper !== initWrapperMock) {
oldInitWrapper = this._initWrapper;
this._initWrapper = initWrapperMock;
}
return oldRender.call(this);
};
});
afterAll(() => {
ListView.prototype.render = oldRender;
});
beforeEach(() => {
initWrapperMock.mockClear();
wrapper = mount(<ListView {...props} />);
instance = wrapper.instance();
});
it('getBottomVisibleIndex() returns a number', () => {
const n = instance.getBottomVisibleIndex();
expect(Number.isNaN(n)).toBe(false);
expect(n).toEqual(expect.any(Number));
});
it('getTopVisibleIndex() returns a number', () => {
const n = instance.getTopVisibleIndex();
expect(Number.isNaN(n)).toBe(false);
expect(n).toEqual(expect.any(Number));
});
it('getRowPosition() returns a number', () => {
const { height, y } = instance.getRowPosition(2);
expect(height).toEqual(expect.any(Number));
expect(y).toEqual(expect.any(Number));
});
});
describe('windowScroller', () => {
let windowAddListenerSpy: jest.SpyInstance<
void,
[
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions | undefined
]
>;
let windowRmListenerSpy: jest.SpyInstance<
void,
[
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions | undefined
]
>;
beforeEach(() => {
windowAddListenerSpy = jest.spyOn(window, 'addEventListener');
windowRmListenerSpy = jest.spyOn(window, 'removeEventListener');
const wsProps = { ...props, windowScroller: true };
wrapper = mount(<ListView {...(wsProps as unknown as TListViewProps)} />);
instance = wrapper.instance();
});
afterEach(() => {
windowAddListenerSpy.mockRestore();
});
it('adds the onScroll listener to the window element after the component mounts', () => {
const eventListeners = getListenersByType(windowAddListenerSpy.mock);
expect(eventListeners.scroll).toEqual([instance._onScroll]);
});
it('removes the onScroll listener from window when unmounting', () => {
// jest adds "error" event listeners to window, ignore those calls
let eventListeners = getListenersByType(windowRmListenerSpy.mock);
expect(eventListeners.scroll).not.toBeDefined();
wrapper.unmount();
eventListeners = getListenersByType(windowRmListenerSpy.mock);
expect(eventListeners.scroll).toEqual([instance._onScroll]);
});
it('calls _positionList when the document is scrolled', (done) => {
const event = new Event('scroll');
const fn = jest.spyOn(instance, '_positionList');
expect(instance._isScrolledOrResized).toBe(false);
window.dispatchEvent(event);
expect(instance._isScrolledOrResized).toBe(true);
window.requestAnimationFrame(() => {
expect(fn).toHaveBeenCalled();
done();
});
});
it('uses the root HTML element to determine if the view has changed', () => {
const htmlElm = instance._htmlElm;
expect(htmlElm).toBeTruthy();
const spyFns = {
clientHeight: jest.fn(() => instance._viewHeight + 1),
scrollTop: jest.fn(() => instance._scrollTop + 1),
};
Object.defineProperties(htmlElm, {
clientHeight: {
get: spyFns.clientHeight,
},
scrollTop: {
get: spyFns.scrollTop,
},
});
const hasChanged = instance._isViewChanged();
expect(hasChanged).toBe(true);
expect(spyFns.clientHeight).toHaveBeenCalled();
expect(spyFns.scrollTop).toHaveBeenCalled();
});
});
it('renders the correct number of items', () => {
expect(screen.getAllByTestId('item').length).toBe(DATA_LENGTH);
});
});

View File

@ -494,7 +494,7 @@ export default class ListView extends React.Component<TListViewProps> {
height: this._yPositions.getEstimatedHeight(),
};
return (
<div {...wrapperProps}>
<div {...wrapperProps} data-testid="ListView">
<div style={scrollerStyle}>
<div
style={{