import React from 'react';
import {
Dimensions,
EmitterSubscription,
FlatList,
ListRenderItem,
Text,
ViewToken,
} from 'react-native';
import { act, render } from '@testing-library/react-native';
import Carousel from '../Carousel';
import { ReactTestInstance } from 'react-test-renderer';
jest.mock('../CarouselPageIndicator', () => 'CarouselPageIndicator');
type ItemProps = { str?: string };
const Item = ({ str }: ItemProps) => {str};
const renderItem: ListRenderItem = ({ item }) => (
);
describe('Carousel', () => {
beforeEach(() => {
jest.clearAllMocks();
});
/* eslint-disable no-console */
// turn off console errors during tests, can be safely removed once React Native v0.64 is no longer supported
const consoleWarn = console.warn;
console.warn = jest.fn();
afterAll(() => {
console.warn = consoleWarn;
/* eslint-enable no-console */
});
it('renders with multiple items in the data', () => {
const data = [
{ key: 1, str: 'foo' },
{ key: 2, str: 'bar' },
];
const { container, getByText, toJSON } = render(
);
expect(toJSON()).toMatchSnapshot();
expect(getByText(data[0].str)).toBeDefined();
expect(getByText(data[1].str)).toBeDefined();
const carouselPageIndicator = container.children[1] as ReactTestInstance;
expect(carouselPageIndicator.type).toBe('CarouselPageIndicator');
expect(carouselPageIndicator.props.numberOfItems).toBe(2);
});
it('renders with just one item in the data', () => {
const data = [{ key: 1, str: 'qux' }];
const { container, toJSON, getByText } = render(
);
expect(toJSON()).toMatchSnapshot();
expect(getByText(data[0].str)).toBeDefined();
const flatList = container.children[0] as ReactTestInstance;
expect(flatList.props.data).toStrictEqual(data);
const carouselPageIndicator = container.children[1] as ReactTestInstance;
expect(carouselPageIndicator.type).toBe('CarouselPageIndicator');
expect(carouselPageIndicator.props.numberOfItems).toBe(1);
});
it('returns null if data is null', () => {
// Ideally, this should not happen but, if it does, we should be able to handle gracefully
const { toJSON } = render(
);
expect(toJSON()).toBeNull();
});
it('returns null if there are no items in the data', () => {
// Ideally, this should not happen but, if it does, we should be able to handle gracefully
const { toJSON } = render();
expect(toJSON()).toBeNull();
});
it('calls the orientation handler on orientation change', () => {
let orientationHandler: Function;
const data = [{ key: 1, str: 'qux' }];
const window = { width: 350 };
const addEventListenerSpy = jest.spyOn(Dimensions, 'addEventListener');
// Get a reference to the orientation handler by spying on the `addEventListener` call
addEventListenerSpy.mockImplementation(((_, handler) => {
orientationHandler = handler;
}) as Dimensions['addEventListener']);
const { container, toJSON } = render(
);
const flatList = container.children[0] as ReactTestInstance;
expect(flatList.props.snapToInterval).not.toBe(window.width);
// Call the orientation handler with an arbitrary `ScaledSize`
act(() => {
orientationHandler({ window });
});
expect(flatList.props.snapToInterval).toBe(window.width);
expect(toJSON()).toMatchSnapshot();
});
it('cleans up the orientation change listener (React Native < v0.65)', () => {
// ensure that `addEventListener` returns `undefined`
jest
.spyOn(Dimensions, 'addEventListener')
.mockReturnValue(undefined as unknown as EmitterSubscription);
const removeEventListener = jest.spyOn(Dimensions, 'removeEventListener');
const { unmount } = render();
act(() => {
unmount();
});
expect(removeEventListener).toBeCalled();
});
it('cleans up the orientation change listener (React Native v0.65+)', () => {
const mockSubscription = { remove: jest.fn() };
const addEventListenerSpy = jest.spyOn(Dimensions, 'addEventListener');
(addEventListenerSpy as jest.Mock).mockReturnValue(mockSubscription);
const { unmount } = render();
act(() => {
unmount();
});
expect(mockSubscription.remove).toBeCalled();
});
it('sets the index when viewable items change', () => {
const data = [
{ key: 1, str: 'foo' },
{ key: 2, str: 'bar' },
];
const { container } = render(
);
const flatList = container.children[0] as ReactTestInstance;
const carouselPageIndicator = container.children[1] as ReactTestInstance;
const { onViewableItemsChanged } = flatList.props as FlatList['props'];
expect(carouselPageIndicator.props.currentIndex).toBe(0);
act(() => {
const info = { viewableItems: [{ index: 1 } as ViewToken], changed: [] };
onViewableItemsChanged!(info);
});
expect(carouselPageIndicator.props.currentIndex).toBe(1);
// should not update the index if `viewableItems` is empty
act(() => {
const info = { viewableItems: [], changed: [] };
onViewableItemsChanged!(info);
});
expect(carouselPageIndicator.props.currentIndex).toBe(1);
});
});