diff --git a/packages/react-widgets/__tests__/hooks/useWidgetFetch.test.js b/packages/react-widgets/__tests__/hooks/useWidgetFetch.test.js index 3412f3e06..9e4618231 100644 --- a/packages/react-widgets/__tests__/hooks/useWidgetFetch.test.js +++ b/packages/react-widgets/__tests__/hooks/useWidgetFetch.test.js @@ -3,7 +3,9 @@ import { DEFAULT_INVALID_COLUMN_ERR } from '../../src/widgets/utils/constants'; import { act, render, screen } from '@testing-library/react'; import React from 'react'; import useWidgetFetch from '../../src/hooks/useWidgetFetch'; -import { mockReduxHooks } from '../mockReduxHooks'; +import { mockClear, mockReduxHooks, mockSetup } from '../mockReduxHooks'; +import { selectViewport } from '@carto/react-redux'; +import bboxPolygon from '@turf/bbox-polygon'; const PARAMS_MOCK = { column: '__test__' @@ -11,15 +13,37 @@ const PARAMS_MOCK = { const SOURCE_MOCK = { id: 'test', - data: 'testTable' + data: 'testTable', + type: 'table', + credentials: { + apiVersion: 'v3' + } }; +const viewport = [-10, -5, 8, 9]; +const spatialFilter = bboxPolygon([-10, -5, 8, 9]).geometry; + jest.mock('../../src/hooks/useWidgetSource', () => () => SOURCE_MOCK); describe('useWidgetFetch', () => { - mockReduxHooks(); + beforeAll(() => { + const { useDispatch, useSelector } = mockSetup(); + const defaultSelector = jest.fn(); + + useDispatch.mockReturnValue(jest.fn()); + useSelector.mockImplementation((selector) => { + if (selector === selectViewport) { + return viewport; + } + return defaultSelector; + }); + }); - test('should work correctly', async () => { + afterAll(() => { + mockClear(); + }); + + it('should work correctly (no remote attempt)', async () => { const onError = jest.fn(); const modelFn = jest .fn() @@ -35,6 +59,7 @@ describe('useWidgetFetch', () => { dataSource: 'test', params: PARAMS_MOCK, global: false, + attemptRemoteCalculation: false, onError }} /> @@ -44,7 +69,9 @@ describe('useWidgetFetch', () => { expect(modelFn).toBeCalledWith({ source: SOURCE_MOCK, ...PARAMS_MOCK, - global: false + global: false, + remoteCalculation: false, + spatialFilter: spatialFilter }); expect(screen.getByText('loading')).toBeInTheDocument(); @@ -65,6 +92,7 @@ describe('useWidgetFetch', () => { dataSource: 'test', params: PARAMS_MOCK, global: true, + attemptRemoteCalculation: false, onError }} /> @@ -74,7 +102,9 @@ describe('useWidgetFetch', () => { expect(modelFn).toBeCalledWith({ source: SOURCE_MOCK, ...PARAMS_MOCK, - global: true + global: true, + remoteCalculation: false, + spatialFilter: null // never in global mode }); expect(screen.getByText('loading')).toBeInTheDocument(); @@ -93,6 +123,7 @@ describe('useWidgetFetch', () => { dataSource: 'test', params: PARAMS_MOCK, global: false, + remoteCalculation: false, onError }} /> @@ -104,6 +135,70 @@ describe('useWidgetFetch', () => { expect(onError).toBeCalledTimes(1); expect(screen.queryByText(DEFAULT_INVALID_COLUMN_ERR)).toBeInTheDocument(); }); + + it('should work correctly (non-global, remote attempt)', async () => { + const onError = jest.fn(); + const modelFn = jest + .fn() + .mockImplementation( + () => new Promise((resolve) => setTimeout(() => resolve('data'), 100)) + ); + + const { rerender } = render( + + ); + + // Test modelFn is called with the right params + expect(modelFn).toBeCalledWith({ + source: SOURCE_MOCK, + ...PARAMS_MOCK, + global: false, + remoteCalculation: true, + spatialFilter: spatialFilter + }); + }); + + it('should work correctly (global, remote attempt)', async () => { + const onError = jest.fn(); + const modelFn = jest + .fn() + .mockImplementation( + () => new Promise((resolve) => setTimeout(() => resolve('data'), 100)) + ); + + const { rerender } = render( + + ); + + // Test modelFn is called with the right params + expect(modelFn).toBeCalledWith({ + source: SOURCE_MOCK, + ...PARAMS_MOCK, + global: true, + remoteCalculation: true, + spatialFilter: null // no spatial filter for glboal case + }); + }); }); // Aux diff --git a/packages/react-widgets/__tests__/mockReduxHooks.js b/packages/react-widgets/__tests__/mockReduxHooks.js index 726f5c4e7..65e4397ef 100644 --- a/packages/react-widgets/__tests__/mockReduxHooks.js +++ b/packages/react-widgets/__tests__/mockReduxHooks.js @@ -11,6 +11,10 @@ export function mockReduxHooks(dispatchValue, selectorValue) { useSelectorSpy.mockReturnValue(mockSelectorFn); } +export function mockSetup() { + return { useDispatch: useDispatchSpy, useSelector: useSelectorSpy }; +} + export function mockClear() { useDispatchSpy.mockClear(); useSelectorSpy.mockClear(); diff --git a/packages/react-widgets/__tests__/models/utils.test.js b/packages/react-widgets/__tests__/models/utils.test.js index 8a3775f78..91d5fed9a 100644 --- a/packages/react-widgets/__tests__/models/utils.test.js +++ b/packages/react-widgets/__tests__/models/utils.test.js @@ -41,19 +41,34 @@ const fromRemote = jest.fn(); describe('utils', () => { describe('wrapModelCall', () => { - test('should work correctly', () => { - const props = { source: V2_SOURCE, global: false }; - wrapModelCall(props, fromLocal, fromRemote); - expect(fromLocal).toHaveBeenCalledWith(props); + const cases = [ + // source, global, remoteCalculation, expectedFn + [V2_SOURCE, false, false, fromLocal], + [V3_SOURCE, false, false, fromLocal], + [V3_SOURCE, true, false, fromRemote], + [V2_SOURCE, false, true, fromLocal], + [V3_SOURCE, false, true, fromRemote], + [V3_SOURCE, true, true, fromRemote] + ]; + + test.each(cases)( + 'should work correctly', + (source, global, remoteCalculation, expectedFn) => { + const props = { source, global, remoteCalculation }; + wrapModelCall(props, fromLocal, fromRemote); + expect(expectedFn).toHaveBeenCalledWith(props); + } + ); - const props2 = { source: V3_SOURCE, global: true }; - wrapModelCall(props2, fromLocal, fromRemote); - expect(fromRemote).toHaveBeenCalledWith(props2); + test('should throw error if global is true but fromRemote is missing', () => { + expect(() => + wrapModelCall({ source: V3_SOURCE, global: true }, fromLocal) + ).toThrowError(); }); - test('should throw error if global is true but fromRemote is missing', () => { + test('should throw error if remoteCalculation is true but fromRemote is missing', () => { expect(() => - wrapModelCall({ source: V2_SOURCE, global: true }, fromLocal) + wrapModelCall({ source: V3_SOURCE, remoteCalculation: true }, fromLocal) ).toThrowError(); }); diff --git a/packages/react-widgets/src/models/utils.js b/packages/react-widgets/src/models/utils.js index d05c3f0ad..732bf2e52 100644 --- a/packages/react-widgets/src/models/utils.js +++ b/packages/react-widgets/src/models/utils.js @@ -30,6 +30,10 @@ export function wrapModelCall(props, fromLocal, fromRemote) { return fromRemote(props); } else if (remoteCalculation && isRemoteCalculationSupported(props)) { + if (!fromRemote) { + throw new Error(`Remote calculation isn't supported for this widget`); + } + // The widget supports remote calculation, preferred whenever possible return fromRemote(props); } else {