This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 7e5440a  test(native-filters): Filter config modal test (#14245)
7e5440a is described below

commit 7e5440a359e90b1d36f1caa7fc2fb31c816ede49
Author: simcha90 <[email protected]>
AuthorDate: Tue Apr 20 14:07:08 2021 +0300

    test(native-filters): Filter config modal test (#14245)
    
    * test: add tests for filter bar
    
    * test: merge filter bar tests with master
    
    * test: add test for filter set
    
    * test: filter set tests
    
    * test: merge with master
    
    * test: fix tests for filter bar
    
    * test: filter config modal
    
    * test: add select filter
    
    * feat: filter config modal
    
    * lint: fix lint
    
    * lint: fix TS
    
    * test: test testWithId
---
 .../integration/dashboard/nativeFilters.test.ts    |  18 +-
 superset-frontend/spec/fixtures/mockStore.js       |  31 +++
 .../nativeFilters/FilterBar/FilterBar.test.tsx     |  39 ++--
 .../FilterBar/FilterConfigurationLink/index.tsx    |   1 +
 .../components/nativeFilters/FilterBar/index.tsx   |   2 +-
 .../FiltersConfigForm/FiltersConfigForm.tsx        |   2 +-
 .../FiltersConfigModal/FiltersConfigModal.test.tsx | 213 +++++++++++++++++++++
 .../FiltersConfigModal/FiltersConfigModal.tsx      |   2 +-
 .../nativeFilters/FiltersConfigModal/utils.ts      |   2 +-
 .../controls/DateFilterControl/DateFilterLabel.tsx |   2 +-
 superset-frontend/src/utils/common.js              |  12 --
 superset-frontend/src/utils/testUtils.test.ts      |  54 ++++++
 superset-frontend/src/utils/testUtils.ts           |  40 ++++
 13 files changed, 374 insertions(+), 44 deletions(-)

diff --git 
a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
 
b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
index 84e6f4e..ce73e4f 100644
--- 
a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
+++ 
b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
@@ -62,11 +62,13 @@ describe('Nativefilters', () => {
       .type('Country name');
 
     cy.get('.ant-modal')
-      .find('[data-test="datasource-input"]')
+      .find('[data-test="filters-config-modal__datasource-input"]')
       .click()
       .type('wb_health_population');
 
-    cy.get('.ant-modal [data-test="datasource-input"] .Select__menu')
+    cy.get(
+      '.ant-modal [data-test="filters-config-modal__datasource-input"] 
.Select__menu',
+    )
       .contains('wb_health_population')
       .click();
 
@@ -159,11 +161,13 @@ describe('Nativefilters', () => {
         .type('Country name');
 
       cy.get('.ant-modal')
-        .find('[data-test="datasource-input"]')
+        .find('[data-test="filters-config-modal__datasource-input"]')
         .click()
         .type('wb_health_population');
 
-      cy.get('.ant-modal [data-test="datasource-input"] .Select__menu')
+      cy.get(
+        '.ant-modal [data-test="filters-config-modal__datasource-input"] 
.Select__menu',
+      )
         .contains('wb_health_population')
         .click();
 
@@ -191,12 +195,14 @@ describe('Nativefilters', () => {
         .type('Region Name');
 
       cy.get('.ant-modal')
-        .find('[data-test="datasource-input"]')
+        .find('[data-test="filters-config-modal__datasource-input"]')
         .last()
         .click()
         .type('wb_health_population');
 
-      cy.get('.ant-modal [data-test="datasource-input"] .Select__menu')
+      cy.get(
+        '.ant-modal [data-test="filters-config-modal__datasource-input"] 
.Select__menu',
+      )
         .last()
         .contains('wb_health_population')
         .click();
diff --git a/superset-frontend/spec/fixtures/mockStore.js 
b/superset-frontend/spec/fixtures/mockStore.js
index 8bf1a07..66434c5 100644
--- a/superset-frontend/spec/fixtures/mockStore.js
+++ b/superset-frontend/spec/fixtures/mockStore.js
@@ -124,3 +124,34 @@ export const getMockStoreWithNativeFilters = () =>
       },
     },
   });
+
+export const stateWithoutNativeFilters = {
+  ...mockState,
+  charts: {
+    ...mockState.charts,
+    [sliceIdWithAppliedFilter]: {
+      ...mockState.charts[sliceId],
+      queryResponse: {
+        status: 'success',
+        applied_filters: [{ column: 'region' }],
+        rejected_filters: [],
+      },
+    },
+    [sliceIdWithRejectedFilter]: {
+      ...mockState.charts[sliceId],
+      queryResponse: {
+        status: 'success',
+        applied_filters: [],
+        rejected_filters: [{ column: 'region', reason: 'not_in_datasource' }],
+      },
+    },
+  },
+  dashboardInfo: {
+    dash_edit_perm: true,
+    metadata: {
+      native_filter_configuration: [],
+    },
+  },
+  dataMask: {},
+  nativeFilters: { filters: {}, filterSets: {} },
+};
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
index 032133c..4b1cee2 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
@@ -21,9 +21,13 @@ import React from 'react';
 import { render, screen, cleanup } from 'spec/helpers/testing-library';
 import { Provider } from 'react-redux';
 import userEvent from '@testing-library/user-event';
-import { getMockStore, mockStore } from 'spec/fixtures/mockStore';
+import {
+  getMockStore,
+  mockStore,
+  stateWithoutNativeFilters,
+} from 'spec/fixtures/mockStore';
 import * as mockCore from '@superset-ui/core';
-import { testWithId } from 'src/utils/common';
+import { testWithId } from 'src/utils/testUtils';
 import { FeatureFlag } from 'src/featureFlags';
 import { Preset } from '@superset-ui/core';
 import { TimeFilterPlugin, SelectFilterPlugin } from 'src/filters/components';
@@ -48,9 +52,12 @@ class MainPreset extends Preset {
   }
 }
 
-const getTestId = testWithId(FILTER_BAR_TEST_ID, true);
-const getModalTestId = testWithId(FILTERS_CONFIG_MODAL_TEST_ID, true);
-const getDateControlTestId = testWithId(DATE_FILTER_CONTROL_TEST_ID, true);
+const getTestId = testWithId<string>(FILTER_BAR_TEST_ID, true);
+const getModalTestId = testWithId<string>(FILTERS_CONFIG_MODAL_TEST_ID, true);
+const getDateControlTestId = testWithId<string>(
+  DATE_FILTER_CONTROL_TEST_ID,
+  true,
+);
 
 const FILTER_NAME = 'Time filter 1';
 const FILTER_SET_NAME = 'New filter set';
@@ -100,6 +107,7 @@ const changeFilterValue = async () => {
 };
 
 describe('FilterBar', () => {
+  new MainPreset().register();
   const toggleFiltersBar = jest.fn();
   const closedBarProps = {
     filtersOpen: false,
@@ -109,16 +117,6 @@ describe('FilterBar', () => {
     filtersOpen: true,
     toggleFiltersBar,
   };
-  const noFiltersState = {
-    dashboardInfo: {
-      dash_edit_perm: true,
-      metadata: {
-        native_filter_configuration: [],
-      },
-    },
-    dataMask: {},
-    nativeFilters: { filters: {}, filterSets: {} },
-  };
 
   const mockApi = jest.fn(async data => {
     const json = JSON.parse(data.json_metadata);
@@ -169,7 +167,6 @@ describe('FilterBar', () => {
     };
   });
 
-  new MainPreset().register();
   beforeEach(() => {
     toggleFiltersBar.mockClear();
     fetchMock.get(
@@ -265,7 +262,7 @@ describe('FilterBar', () => {
 
   it('no edit filter button by disabled permissions', () => {
     renderWrapper(openedBarProps, {
-      ...noFiltersState,
+      ...stateWithoutNativeFilters,
       dashboardInfo: { metadata: {} },
     });
 
@@ -285,7 +282,7 @@ describe('FilterBar', () => {
   });
 
   it('no filters', () => {
-    renderWrapper(openedBarProps, noFiltersState);
+    renderWrapper(openedBarProps, stateWithoutNativeFilters);
 
     expect(screen.getByTestId(getTestId('clear-button'))).toBeDisabled();
     expect(screen.getByTestId(getTestId('apply-button'))).toBeDisabled();
@@ -297,7 +294,7 @@ describe('FilterBar', () => {
       [FeatureFlag.DASHBOARD_NATIVE_FILTERS_SET]: true,
       [FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
     };
-    renderWrapper(openedBarProps, noFiltersState);
+    renderWrapper(openedBarProps, stateWithoutNativeFilters);
     expect(screen.getByTestId(getTestId('apply-button'))).toBeDisabled();
 
     addFilterFlow();
@@ -315,7 +312,7 @@ describe('FilterBar', () => {
     global.featureFlags = {
       [FeatureFlag.DASHBOARD_NATIVE_FILTERS_SET]: true,
     };
-    renderWrapper(openedBarProps, noFiltersState);
+    renderWrapper(openedBarProps, stateWithoutNativeFilters);
 
     addFilterFlow();
 
@@ -357,7 +354,7 @@ describe('FilterBar', () => {
       [FeatureFlag.DASHBOARD_NATIVE_FILTERS_SET]: true,
       [FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
     };
-    renderWrapper(openedBarProps, noFiltersState);
+    renderWrapper(openedBarProps, stateWithoutNativeFilters);
 
     addFilterFlow();
 
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
index 71717aa..49dea83 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
@@ -51,6 +51,7 @@ export const FilterConfigurationLink: React.FC<FCBProps> = ({
 
   return (
     <>
+      {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
       <HeaderButton
         {...getFilterBarTestId('create-filter')}
         buttonStyle="link"
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
index eea6aab..4771f03 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
@@ -29,7 +29,7 @@ import { updateDataMask } from 'src/dataMask/actions';
 import { DataMaskState } from 'src/dataMask/types';
 import { useImmer } from 'use-immer';
 import { areObjectsEqual } from 'src/reduxUtils';
-import { testWithId } from 'src/utils/common';
+import { testWithId } from 'src/utils/testUtils';
 import { Filter } from 'src/dashboard/components/nativeFilters/types';
 import { mapParentFiltersToChildren, TabIds } from './utils';
 import FilterSets from './FilterSets';
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index c6ebf9a..0a9ba42 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -187,7 +187,7 @@ export const FiltersConfigForm: 
React.FC<FiltersConfigFormProps> = ({
             initialValue={{ value: initDatasetId }}
             label={<StyledLabel>{t('Dataset')}</StyledLabel>}
             rules={[{ required: !removed, message: t('Dataset is required') }]}
-            data-test="datasource-input"
+            {...getFiltersConfigModalTestId('datasource-input')}
           >
             <SupersetResourceSelect
               initialId={initDatasetId}
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
new file mode 100644
index 0000000..234970b
--- /dev/null
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
@@ -0,0 +1,213 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * 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';
+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 {
+  FILTERS_CONFIG_MODAL_TEST_ID,
+  FiltersConfigModal,
+} 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' }),
+      ],
+    });
+  }
+}
+
+const getTestId = testWithId<string>(FILTERS_CONFIG_MODAL_TEST_ID, true);
+
+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',
+      },
+      show_columns: ['id', 'table_name'],
+    },
+    { overwriteRoutes: true },
+  );
+  fetchMock.post(
+    'glob:*/api/v1/chart/data',
+    {
+      result: [
+        {
+          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();
+  });
+
+  it('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'));
+    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 }],
+        }),
+      ),
+    );
+  });
+});
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
index 17733b3..2100ef6 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
@@ -22,7 +22,7 @@ import { t, styled } from '@superset-ui/core';
 import { Form } from 'src/common/components';
 import { StyledModal } from 'src/components/Modal';
 import ErrorBoundary from 'src/components/ErrorBoundary';
-import { testWithId } from 'src/utils/common';
+import { testWithId } from 'src/utils/testUtils';
 import { useFilterConfigMap, useFilterConfiguration } from '../state';
 import { FilterRemoval, NativeFiltersForm } from './types';
 import { FilterConfiguration } from '../types';
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
index 1e07071..9c71a8c 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
@@ -123,7 +123,7 @@ export const createHandleSave = (
     removedFilters,
     setCurrentFilterId,
   );
-  if (values == null) return;
+  if (values === null) return;
 
   const newFilterConfig: FilterConfiguration = filterIds
     .filter(id => !removedFilters[id])
diff --git 
a/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
 
b/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
index 5540246..3009949 100644
--- 
a/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
+++ 
b/superset-frontend/src/explore/components/controls/DateFilterControl/DateFilterLabel.tsx
@@ -41,7 +41,7 @@ import { Tooltip } from 'src/common/components/Tooltip';
 import { DEFAULT_TIME_RANGE } from 'src/explore/constants';
 import { useDebouncedEffect } from 'src/explore/exploreUtils';
 import { SLOW_DEBOUNCE } from 'src/constants';
-import { testWithId } from 'src/utils/common';
+import { testWithId } from 'src/utils/testUtils';
 import { SelectOptionType, FrameType } from './types';
 import {
   COMMON_RANGE_VALUES_SET,
diff --git a/superset-frontend/src/utils/common.js 
b/superset-frontend/src/utils/common.js
index 5939668..974268c 100644
--- a/superset-frontend/src/utils/common.js
+++ b/superset-frontend/src/utils/common.js
@@ -125,15 +125,3 @@ export const detectOS = () => {
 
   return 'Unknown OS';
 };
-
-// Using bem standard
-export const testWithId = (prefix, idOnly = false) => id => {
-  if (!id) {
-    return idOnly ? prefix : { 'data-test': prefix };
-  }
-  if (!prefix) {
-    return idOnly ? id : { 'data-test': id };
-  }
-  const newId = `${prefix}__${id}`;
-  return idOnly ? newId : { 'data-test': newId };
-};
diff --git a/superset-frontend/src/utils/testUtils.test.ts 
b/superset-frontend/src/utils/testUtils.test.ts
new file mode 100644
index 0000000..2411db7
--- /dev/null
+++ b/superset-frontend/src/utils/testUtils.test.ts
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { testWithId } from './testUtils';
+
+describe('testUtils', () => {
+  it('testWithId with prefix only', () => {
+    expect(testWithId('prefix')()).toEqual({ 'data-test': 'prefix' });
+  });
+
+  it('testWithId with prefix only and idOnly', () => {
+    expect(testWithId('prefix', true)()).toEqual('prefix');
+  });
+
+  it('testWithId with id only', () => {
+    expect(testWithId()('id')).toEqual({ 'data-test': 'id' });
+  });
+
+  it('testWithId with id only and idOnly', () => {
+    expect(testWithId(undefined, true)('id')).toEqual('id');
+  });
+
+  it('testWithId with prefix and id', () => {
+    expect(testWithId('prefix')('id')).toEqual({ 'data-test': 'prefix__id' });
+  });
+
+  it('testWithId with prefix and id and idOnly', () => {
+    expect(testWithId('prefix', true)('id')).toEqual('prefix__id');
+  });
+
+  it('testWithId without prefix and id', () => {
+    expect(testWithId()()).toEqual({ 'data-test': '' });
+  });
+
+  it('testWithId without prefix and id and idOnly', () => {
+    expect(testWithId(undefined, true)()).toEqual('');
+  });
+});
diff --git a/superset-frontend/src/utils/testUtils.ts 
b/superset-frontend/src/utils/testUtils.ts
new file mode 100644
index 0000000..de3dee8
--- /dev/null
+++ b/superset-frontend/src/utils/testUtils.ts
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { JsonObject } from '@superset-ui/core';
+
+type TestWithIdType<T> = T extends string ? string : { 'data-test': string };
+
+// Using bem standard
+export const testWithId = <T extends string | JsonObject = JsonObject>(
+  prefix?: string,
+  idOnly = false,
+) => (id?: string): TestWithIdType<T> => {
+  if (!id && prefix) {
+    return (idOnly ? prefix : { 'data-test': prefix }) as TestWithIdType<T>;
+  }
+  if (id && !prefix) {
+    return (idOnly ? id : { 'data-test': id }) as TestWithIdType<T>;
+  }
+  if (!id && !prefix) {
+    console.warn('testWithId function has missed "prefix" and "id" params');
+    return (idOnly ? '' : { 'data-test': '' }) as TestWithIdType<T>;
+  }
+  const newId = `${prefix}__${id}`;
+  return (idOnly ? newId : { 'data-test': newId }) as TestWithIdType<T>;
+};

Reply via email to