blob: f6594539a08919a300bf49e16bcedc14ff1c5910 [file] [log] [blame]
import React from 'react';
import { shallow } from 'enzyme';
import ChartClient from '@superset-ui/core/src/chart/clients/ChartClient';
import ChartDataProvider, { Props } from '@superset-ui/core/src/chart/components/ChartDataProvider';
import { bigNumberFormData } from '../fixtures/formData';
// Note: the mock implementation of these function directly affects the expected results below
const defaultMockLoadFormData = jest.fn(({ formData }: { formData: unknown }) =>
Promise.resolve(formData),
);
type MockLoadFormData = typeof defaultMockLoadFormData | jest.Mock<Promise<unknown>, unknown[]>;
let mockLoadFormData: MockLoadFormData = defaultMockLoadFormData;
function createPromise<T>(input: T) {
return Promise.resolve(input);
}
function createArrayPromise<T>(input: T) {
return Promise.resolve([input]);
}
const mockLoadDatasource = jest.fn<Promise<unknown>, unknown[]>(createPromise);
const mockLoadQueryData = jest.fn<Promise<unknown>, unknown[]>(createArrayPromise);
// ChartClient is now a mock
jest.mock('@superset-ui/core/src/chart/clients/ChartClient', () =>
jest.fn().mockImplementation(() => ({
loadDatasource: mockLoadDatasource,
loadFormData: mockLoadFormData,
loadQueryData: mockLoadQueryData,
})),
);
const ChartClientMock = ChartClient as jest.Mock<ChartClient>;
describe('ChartDataProvider', () => {
beforeEach(() => {
ChartClientMock.mockClear();
mockLoadFormData = defaultMockLoadFormData;
mockLoadFormData.mockClear();
mockLoadDatasource.mockClear();
mockLoadQueryData.mockClear();
});
const props: Props = {
formData: { ...bigNumberFormData },
children: () => <div />,
};
function setup(overrideProps?: Partial<Props>) {
return shallow(<ChartDataProvider {...props} {...overrideProps} />);
}
it('instantiates a new ChartClient()', () => {
setup();
expect(ChartClientMock).toHaveBeenCalledTimes(1);
});
describe('ChartClient.loadFormData', () => {
it('calls method on mount', () => {
setup();
expect(mockLoadFormData.mock.calls).toHaveLength(1);
expect(mockLoadFormData.mock.calls[0][0]).toEqual({
sliceId: props.sliceId,
formData: props.formData,
});
});
it('should pass formDataRequestOptions to ChartClient.loadFormData', () => {
const options = { host: 'override' };
setup({ formDataRequestOptions: options });
expect(mockLoadFormData.mock.calls).toHaveLength(1);
expect(mockLoadFormData.mock.calls[0][1]).toEqual(options);
});
it('calls ChartClient.loadFormData when formData or sliceId change', () => {
const wrapper = setup();
const newProps = { sliceId: 123, formData: undefined };
expect(mockLoadFormData.mock.calls).toHaveLength(1);
wrapper.setProps(newProps);
expect(mockLoadFormData.mock.calls).toHaveLength(2);
expect(mockLoadFormData.mock.calls[1][0]).toEqual(newProps);
});
});
describe('ChartClient.loadDatasource', () => {
it('does not method if loadDatasource is false', () =>
new Promise(done => {
expect.assertions(1);
setup({ loadDatasource: false });
setTimeout(() => {
expect(mockLoadDatasource.mock.calls).toHaveLength(0);
done();
}, 0);
}));
it('calls method on mount if loadDatasource is true', () =>
new Promise(done => {
expect.assertions(2);
setup({ loadDatasource: true });
setTimeout(() => {
expect(mockLoadDatasource.mock.calls).toHaveLength(1);
expect(mockLoadDatasource.mock.calls[0][0]).toEqual(props.formData.datasource);
done();
}, 0);
}));
it('should pass datasourceRequestOptions to ChartClient.loadDatasource', () =>
new Promise(done => {
expect.assertions(2);
const options = { host: 'override' };
setup({ loadDatasource: true, datasourceRequestOptions: options });
setTimeout(() => {
expect(mockLoadDatasource.mock.calls).toHaveLength(1);
expect(mockLoadDatasource.mock.calls[0][1]).toEqual(options);
done();
}, 0);
}));
it('calls ChartClient.loadDatasource if loadDatasource is true and formData or sliceId change', () =>
new Promise(done => {
expect.assertions(3);
const newDatasource = 'test';
const wrapper = setup({ loadDatasource: true });
wrapper.setProps({ formData: { datasource: newDatasource }, sliceId: undefined });
setTimeout(() => {
expect(mockLoadDatasource.mock.calls).toHaveLength(2);
expect(mockLoadDatasource.mock.calls[0][0]).toEqual(props.formData.datasource);
expect(mockLoadDatasource.mock.calls[1][0]).toEqual(newDatasource);
done();
}, 0);
}));
});
describe('ChartClient.loadQueryData', () => {
it('calls method on mount', () =>
new Promise(done => {
expect.assertions(2);
setup();
setTimeout(() => {
expect(mockLoadQueryData.mock.calls).toHaveLength(1);
expect(mockLoadQueryData.mock.calls[0][0]).toEqual(props.formData);
done();
}, 0);
}));
it('should pass queryDataRequestOptions to ChartClient.loadQueryData', () =>
new Promise(done => {
expect.assertions(2);
const options = { host: 'override' };
setup({ queryRequestOptions: options });
setTimeout(() => {
expect(mockLoadQueryData.mock.calls).toHaveLength(1);
expect(mockLoadQueryData.mock.calls[0][1]).toEqual(options);
done();
}, 0);
}));
it('calls ChartClient.loadQueryData when formData or sliceId change', () =>
new Promise(done => {
expect.assertions(3);
const newFormData = { key: 'test' };
const wrapper = setup();
wrapper.setProps({ formData: newFormData, sliceId: undefined });
setTimeout(() => {
expect(mockLoadQueryData.mock.calls).toHaveLength(2);
expect(mockLoadQueryData.mock.calls[0][0]).toEqual(props.formData);
expect(mockLoadQueryData.mock.calls[1][0]).toEqual(newFormData);
done();
}, 0);
}));
});
describe('children', () => {
it('calls children({ loading: true }) when loading', () => {
const children = jest.fn<React.ReactNode, unknown[]>();
setup({ children });
// during the first tick (before more promises resolve) loading is true
expect(children.mock.calls).toHaveLength(1);
expect(children.mock.calls[0][0]).toEqual({ loading: true });
});
it('calls children({ payload }) when loaded', () =>
new Promise(done => {
expect.assertions(2);
const children = jest.fn<React.ReactNode, unknown[]>();
setup({ children, loadDatasource: true });
setTimeout(() => {
expect(children.mock.calls).toHaveLength(2);
expect(children.mock.calls[1][0]).toEqual({
payload: {
formData: props.formData,
datasource: props.formData.datasource,
queriesData: [props.formData],
},
});
done();
}, 0);
}));
it('calls children({ error }) upon request error', () =>
new Promise(done => {
expect.assertions(2);
const children = jest.fn<React.ReactNode, unknown[]>();
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
setup({ children });
setTimeout(() => {
expect(children.mock.calls).toHaveLength(2); // loading + error
expect(children.mock.calls[1][0]).toEqual({ error: new Error('error') });
done();
}, 0);
}));
it('calls children({ error }) upon JS error', () =>
new Promise(done => {
expect.assertions(2);
const children = jest.fn<React.ReactNode, unknown[]>();
mockLoadFormData = jest.fn(() => {
throw new Error('non-async error');
});
setup({ children });
setTimeout(() => {
expect(children.mock.calls).toHaveLength(2); // loading + error
expect(children.mock.calls[1][0]).toEqual({ error: new Error('non-async error') });
done();
}, 0);
}));
});
describe('callbacks', () => {
it('calls onLoad(payload) when loaded', () =>
new Promise(done => {
expect.assertions(2);
const onLoaded = jest.fn<void, unknown[]>();
setup({ onLoaded, loadDatasource: true });
setTimeout(() => {
expect(onLoaded.mock.calls).toHaveLength(1);
expect(onLoaded.mock.calls[0][0]).toEqual({
formData: props.formData,
datasource: props.formData.datasource,
queriesData: [props.formData],
});
done();
}, 0);
}));
it('calls onError(error) upon request error', () =>
new Promise(done => {
expect.assertions(2);
const onError = jest.fn<void, unknown[]>();
mockLoadFormData = jest.fn(() => Promise.reject(new Error('error')));
setup({ onError });
setTimeout(() => {
expect(onError.mock.calls).toHaveLength(1);
expect(onError.mock.calls[0][0]).toEqual(new Error('error'));
done();
}, 0);
}));
it('calls onError(error) upon JS error', () =>
new Promise(done => {
expect.assertions(2);
const onError = jest.fn<void, unknown[]>();
mockLoadFormData = jest.fn(() => {
throw new Error('non-async error');
});
setup({ onError });
setTimeout(() => {
expect(onError.mock.calls).toHaveLength(1);
expect(onError.mock.calls[0][0]).toEqual(new Error('non-async error'));
done();
}, 0);
}));
});
});