rusackas commented on a change in pull request #15017:
URL: https://github.com/apache/superset/pull/15017#discussion_r647799211
##########
File path:
superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
##########
@@ -17,198 +17,289 @@
* under the License.
*/
import { Preset } from '@superset-ui/core';
-import { SelectFilterPlugin, TimeFilterPlugin } from 'src/filters/components';
-import { render, cleanup, screen } from 'spec/helpers/testing-library';
-import { Provider } from 'react-redux';
import {
- getMockStore,
- mockStore,
- stateWithoutNativeFilters,
-} from 'spec/fixtures/mockStore';
+ SelectFilterPlugin,
+ RangeFilterPlugin,
+ TimeFilterPlugin,
+ TimeColumnFilterPlugin,
+ TimeGrainFilterPlugin,
+} from 'src/filters/components';
+import { render, screen, waitFor } from 'spec/helpers/testing-library';
import fetchMock from 'fetch-mock';
import React from 'react';
-import userEvent from '@testing-library/user-event';
-import { testWithId } from 'src/utils/testUtils';
-import { waitFor } from '@testing-library/react';
+import userEvent, { specialChars } from '@testing-library/user-event';
import {
- FILTERS_CONFIG_MODAL_TEST_ID,
FiltersConfigModal,
+ FiltersConfigModalProps,
} from './FiltersConfigModal';
-jest.useFakeTimers();
-
class MainPreset extends Preset {
constructor() {
super({
name: 'Legacy charts',
plugins: [
- new TimeFilterPlugin().configure({ key: 'filter_time' }),
new SelectFilterPlugin().configure({ key: 'filter_select' }),
+ new RangeFilterPlugin().configure({ key: 'filter_range' }),
+ new TimeFilterPlugin().configure({ key: 'filter_time' }),
+ new TimeColumnFilterPlugin().configure({ key: 'filter_timecolumn' }),
+ new TimeGrainFilterPlugin().configure({ key: 'filter_timegrain' }),
],
});
}
}
-const getTestId = testWithId<string>(FILTERS_CONFIG_MODAL_TEST_ID, true);
+const initialStoreState = {
+ datasources: [{ id: 1, table_name: 'Datasource 1' }],
+};
-describe('FilterConfigModal', () => {
- new MainPreset().register();
- const onSave = jest.fn();
- const newFilterProps = {
- isOpen: true,
- initialFilterId: undefined,
- createNewOnOpen: true,
- onSave,
- onCancel: jest.fn(),
- };
- fetchMock.get(
- 'glob:*/api/v1/dataset/?q=*',
- {
- count: 1,
- ids: [11],
- label_columns: {
- id: 'Id',
- table_name: 'Table Name',
- },
- list_columns: ['id', 'table_name'],
- order_columns: ['table_name'],
- result: [
- {
- id: 11,
- owners: [],
- table_name: 'birth_names',
- },
- ],
- },
- { overwriteRoutes: true },
- );
- fetchMock.get(
- 'glob:*/api/v1/dataset/11',
- {
- description_columns: {},
- id: 3,
- label_columns: {
- columns: 'Columns',
- table_name: 'Table Name',
- },
- result: {
- columns: [
- {
- column_name: 'name',
- groupby: true,
- id: 334,
- },
- ],
- table_name: 'birth_names',
+fetchMock.get('glob:*/api/v1/dataset/1', {
+ description_columns: {},
+ id: 1,
+ label_columns: {
+ columns: 'Columns',
+ table_name: 'Table Name',
+ },
+ result: {
+ metrics: [],
+ columns: [
+ {
+ column_name: 'Column A',
+ id: 1,
},
- show_columns: ['id', 'table_name'],
- },
- { overwriteRoutes: true },
- );
- fetchMock.post(
- 'glob:*/api/v1/chart/data',
+ ],
+ table_name: 'birth_names',
+ id: 1,
+ },
+ show_columns: ['id', 'table_name'],
+});
+
+fetchMock.post('glob:*/api/v1/chart/data', {
+ result: [
{
- result: [
- {
- status: 'success',
- data: [
- { name: 'Aaron', count: 453 },
- { name: 'Abigail', count: 228 },
- { name: 'Adam', count: 454 },
- ],
- applied_filters: [{ column: 'name' }],
- },
+ status: 'success',
+ data: [
+ { name: 'Aaron', count: 453 },
+ { name: 'Abigail', count: 228 },
+ { name: 'Adam', count: 454 },
],
+ applied_filters: [{ column: 'name' }],
},
- { overwriteRoutes: true },
- );
-
- const renderWrapper = (
- props = newFilterProps,
- state: object = stateWithoutNativeFilters,
- ) =>
- render(
- <Provider
- store={state ? getMockStore(stateWithoutNativeFilters) : mockStore}
- >
- <FiltersConfigModal {...props} />
- </Provider>,
- );
-
- afterEach(() => {
- cleanup();
- jest.clearAllMocks();
+ ],
+});
+
+const FILTER_TYPE_REGEX = /^filter type$/i;
+const FILTER_NAME_REGEX = /^filter name$/i;
+const DATASET_REGEX = /^dataset$/i;
+const COLUMN_REGEX = /^column$/i;
+const VALUE_REGEX = /^value$/i;
+const NUMERICAL_RANGE_REGEX = /^numerical range$/i;
+const TIME_RANGE_REGEX = /^time range$/i;
+const TIME_COLUMN_REGEX = /^time column$/i;
+const TIME_GRAIN_REGEX = /^time grain$/i;
+const ADVANCED_REGEX = /^advanced$/i;
+const DEFAULT_VALUE_REGEX = /^filter has default value$/i;
+const MULTIPLE_REGEX = /^multiple select$/i;
+const REQUIRED_REGEX = /^required$/i;
+const APPLY_INSTANTLY_REGEX = /^apply changes instantly$/i;
+const HIERARCHICAL_REGEX = /^filter is hierarchical$/i;
+const FIRST_ITEM_REGEX = /^default to first item$/i;
+const INVERSE_SELECTION_REGEX = /^inverse selection$/i;
+const SEARCH_ALL_REGEX = /^search all filter options$/i;
+const PRE_FILTER_REGEX = /^pre-filter available values$/i;
+const SORT_REGEX = /^sort filter values$/i;
+const SAVE_REGEX = /^save$/i;
+const NAME_REQUIRED_REGEX = /^name is required$/i;
+const COLUMN_REQUIRED_REGEX = /^column is required$/i;
+const DEFAULT_VALUE_REQUIRED_REGEX = /^default value is required$/i;
+const PARENT_REQUIRED_REGEX = /^parent filter is required$/i;
+const PRE_FILTER_REQUIRED_REGEX = /^pre-filter is required$/i;
+const FILL_REQUIRED_FIELDS_REGEX = /fill all required fields to enable/;
+
+const props: FiltersConfigModalProps = {
+ isOpen: true,
+ createNewOnOpen: true,
+ onSave: jest.fn(),
+ onCancel: jest.fn(),
+};
+
+beforeAll(() => {
+ new MainPreset().register();
+});
+
+function defaultRender(
+ overrides?: Partial<FiltersConfigModalProps>,
+ initialState?: {},
+) {
+ return render(<FiltersConfigModal {...props} {...overrides} />, {
+ useRedux: true,
+ initialState,
});
+}
+
+function getCheckbox(name: RegExp) {
+ return screen.getByRole('checkbox', { name });
+}
+
+function queryCheckbox(name: RegExp) {
+ return screen.queryByRole('checkbox', { name });
+}
+
+test('renders a value filter type', () => {
+ defaultRender();
+
+ userEvent.click(screen.getByText(ADVANCED_REGEX));
+
+ expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(COLUMN_REGEX)).toBeInTheDocument();
+
+ expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked();
+ expect(getCheckbox(REQUIRED_REGEX)).not.toBeChecked();
+ expect(getCheckbox(APPLY_INSTANTLY_REGEX)).not.toBeChecked();
+ expect(getCheckbox(HIERARCHICAL_REGEX)).not.toBeChecked();
+ expect(getCheckbox(FIRST_ITEM_REGEX)).not.toBeChecked();
+ expect(getCheckbox(INVERSE_SELECTION_REGEX)).not.toBeChecked();
+ expect(getCheckbox(SEARCH_ALL_REGEX)).not.toBeChecked();
+ expect(getCheckbox(PRE_FILTER_REGEX)).not.toBeChecked();
+ expect(getCheckbox(SORT_REGEX)).not.toBeChecked();
+
+ expect(getCheckbox(MULTIPLE_REGEX)).toBeChecked();
+});
+
+test('renders a numerical range filter type', () => {
+ defaultRender();
- // TODO: fix and unskip
- it.skip('Create Select Filter (with datasource and columns) with specific
filter scope', async () => {
- renderWrapper();
-
- const FILTER_NAME = 'Select Filter 1';
-
- // fill name
- userEvent.type(screen.getByTestId(getTestId('name-input')), FILTER_NAME);
-
- // fill dataset
- await waitFor(() =>
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument(),
- );
- userEvent.click(
- screen
- .getByTestId(getTestId('datasource-input'))
- .querySelector('.Select__indicators')!,
- );
- userEvent.click(screen.getByText('birth_names'));
-
- // fill column
- userEvent.click(screen.getByText('Select...'));
- await waitFor(() =>
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument(),
- );
- userEvent.click(screen.getByText('name'));
-
- // fill controls
- expect(screen.getByText('Multiple select').parentElement!).toHaveAttribute(
- 'class',
- 'ant-checkbox-wrapper ant-checkbox-wrapper-checked',
- );
- userEvent.click(screen.getByText('Multiple select'));
+ userEvent.click(screen.getByText(VALUE_REGEX));
+ userEvent.click(screen.getByText(NUMERICAL_RANGE_REGEX));
+ userEvent.click(screen.getByText(ADVANCED_REGEX));
+
+ expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(COLUMN_REGEX)).toBeInTheDocument();
+
+ expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked();
+ expect(getCheckbox(APPLY_INSTANTLY_REGEX)).not.toBeChecked();
+ expect(getCheckbox(PRE_FILTER_REGEX)).not.toBeChecked();
+
+ expect(queryCheckbox(MULTIPLE_REGEX)).not.toBeInTheDocument();
+ expect(queryCheckbox(REQUIRED_REGEX)).not.toBeInTheDocument();
+ expect(queryCheckbox(HIERARCHICAL_REGEX)).not.toBeInTheDocument();
+ expect(queryCheckbox(FIRST_ITEM_REGEX)).not.toBeInTheDocument();
+ expect(queryCheckbox(INVERSE_SELECTION_REGEX)).not.toBeInTheDocument();
+ expect(queryCheckbox(SEARCH_ALL_REGEX)).not.toBeInTheDocument();
+ expect(queryCheckbox(SORT_REGEX)).not.toBeInTheDocument();
+});
+
+test('renders a time range filter type', () => {
+ defaultRender();
+
+ userEvent.click(screen.getByText(VALUE_REGEX));
+ userEvent.click(screen.getByText(TIME_RANGE_REGEX));
+
+ expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument();
+ expect(screen.queryByText(DATASET_REGEX)).not.toBeInTheDocument();
+ expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument();
+
+ expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked();
+ expect(getCheckbox(APPLY_INSTANTLY_REGEX)).not.toBeChecked();
+
+ expect(screen.queryByText(ADVANCED_REGEX)).not.toBeInTheDocument();
+});
+
+test('renders a time column filter type', () => {
+ defaultRender();
+
+ userEvent.click(screen.getByText(VALUE_REGEX));
+ userEvent.click(screen.getByText(TIME_COLUMN_REGEX));
+
+ expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument();
+ expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument();
+
+ expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked();
+ expect(getCheckbox(APPLY_INSTANTLY_REGEX)).not.toBeChecked();
+
+ expect(screen.queryByText(ADVANCED_REGEX)).not.toBeInTheDocument();
+});
+
+test('renders a time grain filter type', () => {
+ defaultRender();
+
+ userEvent.click(screen.getByText(VALUE_REGEX));
+ userEvent.click(screen.getByText(TIME_GRAIN_REGEX));
+
+ expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(FILTER_NAME_REGEX)).toBeInTheDocument();
+ expect(screen.getByText(DATASET_REGEX)).toBeInTheDocument();
+ expect(screen.queryByText(COLUMN_REGEX)).not.toBeInTheDocument();
+
+ expect(getCheckbox(DEFAULT_VALUE_REGEX)).not.toBeChecked();
+ expect(getCheckbox(APPLY_INSTANTLY_REGEX)).not.toBeChecked();
+
+ expect(screen.queryByText(ADVANCED_REGEX)).not.toBeInTheDocument();
+});
+
+test('validates the name', async () => {
+ defaultRender();
+ userEvent.click(screen.getByRole('button', { name: SAVE_REGEX }));
+ expect(await screen.findByText(NAME_REQUIRED_REGEX)).toBeInTheDocument();
+});
+
+test('validates the column', async () => {
+ defaultRender();
+ userEvent.click(screen.getByRole('button', { name: SAVE_REGEX }));
+ expect(await screen.findByText(COLUMN_REQUIRED_REGEX)).toBeInTheDocument();
+});
+
+// eslint-disable-next-line jest/no-disabled-tests
+test.skip('validates the default value', async () => {
+ defaultRender(undefined, initialStoreState);
+ expect(await screen.findByText('birth_names')).toBeInTheDocument();
+ userEvent.type(screen.getByRole('combobox'), `Column
A${specialChars.enter}`);
+ userEvent.click(getCheckbox(DEFAULT_VALUE_REGEX));
+ await waitFor(() => {
expect(
- screen.getByText('Multiple select').parentElement!,
- ).not.toHaveAttribute(
- 'class',
- 'ant-checkbox-wrapper ant-checkbox-wrapper-checked',
- );
-
- // choose default value
- userEvent.click(await screen.findByText('3 options'));
- userEvent.click(screen.getByTitle('Abigail'));
-
- // fill scoping
- userEvent.click(screen.getByText('Apply to specific panels'));
- userEvent.click(screen.getByText('CHART_ID'));
-
- // saving
- userEvent.click(screen.getByText('Save'));
- await waitFor(() =>
- expect(onSave.mock.calls[0][0][0]).toEqual(
- expect.objectContaining({
- cascadeParentIds: [],
- controlValues: {
- defaultToFirstItem: false,
- enableEmptyFilter: false,
- inverseSelection: false,
- multiSelect: false,
- sortAscending: true,
- },
- defaultValue: ['Abigail'],
- filterType: 'filter_select',
- isInstant: false,
- name: 'Select Filter 1',
- scope: { excluded: [], rootPath: [] },
- targets: [{ column: { name: 'name' }, datasetId: 11 }],
- }),
- ),
- );
+ screen.queryByText(FILL_REQUIRED_FIELDS_REGEX),
+ ).not.toBeInTheDocument();
});
+ expect(
+ await screen.findByText(DEFAULT_VALUE_REQUIRED_REGEX),
+ ).toBeInTheDocument();
+});
+
+test('validates the hierarchical value', async () => {
+ defaultRender();
+ userEvent.click(screen.getByText(ADVANCED_REGEX));
+ userEvent.click(getCheckbox(HIERARCHICAL_REGEX));
+ expect(await screen.findByText(PARENT_REQUIRED_REGEX)).toBeInTheDocument();
+});
+
+test('validates the pre-filter value', async () => {
+ defaultRender();
+ userEvent.click(screen.getByText(ADVANCED_REGEX));
+ userEvent.click(getCheckbox(PRE_FILTER_REGEX));
+ expect(
+ await screen.findByText(PRE_FILTER_REQUIRED_REGEX),
+ ).toBeInTheDocument();
});
+
+/*
+ TODO
+ adds a new value filter type with all fields filled
+ adds a new numerical range filter type with all fields filled
+ adds a new time range filter type with all fields filled
+ adds a new time column filter type with all fields filled
+ adds a new time grain filter type with all fields filled
+ collapsible controls opens by default when it is checked
+ advanced section opens by default when it has an option checked
+ deletes a filter
+ disables the default value when default to first item is checked
+ changes the default value options when the column changes
+ switches to configuration tab when validation fails
+ displays cancel message when there are pending operations
+ do not displays cancel message when there are no pending operations
Review comment:
```suggestion
does not display cancel message when there are no pending operations
```
obviously just a nit... really appreciate this TODO comment!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]