import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import ArtistMultiValue, { Props } from '../artistMultiValue';

describe('<ArtistMultiValue>', () => {
    describe('when it renders', () => {
        it('displays correct layout', () => {
            const component = shallow(<ArtistMultiValue data={{}} selectProps={{}} />);
            expect(component).toMatchSnapshot();
        });
    });

    describe('with handlers', () => {
        const artist = { artistInfoId: 1 };
        const artistProfile = { name: 'Lost Highway' };
        const renderComponent = (props: Props) => shallow(<ArtistMultiValue {...props} />);
        const getInstance = (component: ShallowWrapper) => component.instance() as ArtistMultiValue;

        const defaultProps = {
            data: artist,
            selectProps: {
                onProfilesFetch: undefined,
                onEditIncorrectArtist: undefined,
            },
        };

        it('calls handleOpen and fetches the data', async () => {
            const promise = Promise.resolve([artistProfile]);
            const testProps = {
                ...defaultProps,
                selectProps: {
                    onProfilesFetch: jest.fn().mockReturnValue(promise),
                },
            };
            const component = renderComponent(testProps);
            return await getInstance(component)
                .handleOpen()
                ?.then(() => {
                    expect(component.state()).toEqual({
                        profiles: [artistProfile],
                        isLoading: false,
                        isFetchExecuted: true,
                    });
                });
        });
        it('calls handleOpen and fetches the data only once', () => {
            const onProfilesFetch = jest.fn().mockReturnValue(Promise.resolve([]));
            const testProps = {
                ...defaultProps,
                selectProps: { onProfilesFetch },
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);
            void instance.handleOpen();
            void instance.handleOpen();
            void instance.handleOpen();
            expect(onProfilesFetch).toHaveBeenCalledTimes(1);
        });
        it('calls handleOpen without onProfilesFetch', async () => {
            const component = renderComponent(defaultProps);
            expect(await getInstance(component).handleOpen()).toBeUndefined();
        });
        it('calls handleEditIncorrectArtist and calls onEditIncorrectArtist with data', () => {
            const testProps = {
                ...defaultProps,
                selectProps: {
                    ...defaultProps.selectProps,
                    onEditIncorrectArtist: jest.fn(),
                },
            };
            getInstance(renderComponent(testProps)).handleEditIncorrectArtist();
            expect(testProps.selectProps.onEditIncorrectArtist).toHaveBeenCalledWith(
                defaultProps.data
            );
        });
        it('calls handleEditIncorrectArtist without onEditIncorrectArtist call', () => {
            const component = renderComponent(defaultProps);
            expect(getInstance(component).handleEditIncorrectArtist()).toBeUndefined();
        });
        it('calls componentWillUnmount without cancelablePromise defined', () => {
            const component = renderComponent(defaultProps);
            const { cancelablePromise } = getInstance(component);
            component.unmount();
            expect(cancelablePromise).toBeUndefined();
        });
        it('calls componentWillUnmount with cancelablePromise defined', () => {
            const cancelFn = jest.fn();
            const onProfilesFetch = jest.fn().mockReturnValue(Promise.reject(new Error('fail')));
            const testProps = {
                ...defaultProps,
                selectProps: { onProfilesFetch },
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);
            void instance.handleOpen();
            expect(instance.cancelablePromise).toBeDefined();

            if (!instance.cancelablePromise)
                throw new Error('instance.cancelablePromise is undefined');
            instance.cancelablePromise.cancel = cancelFn;
            component.unmount();
            expect(cancelFn).toHaveBeenCalled();
        });
    });
});
