2023-03-29 09:01:51 -04:00
|
|
|
/**
|
|
|
|
* 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 React from 'react';
|
|
|
|
import userEvent from '@testing-library/user-event';
|
|
|
|
import {
|
|
|
|
Behavior,
|
|
|
|
ChartMetadata,
|
|
|
|
getChartMetadataRegistry,
|
|
|
|
} from '@superset-ui/core';
|
|
|
|
import fetchMock from 'fetch-mock';
|
|
|
|
import { render, screen, within, waitFor } from 'spec/helpers/testing-library';
|
|
|
|
import chartQueries, { sliceId } from 'spec/fixtures/mockChartQueries';
|
|
|
|
import { Menu } from 'src/components/Menu';
|
|
|
|
import { supersetGetCache } from 'src/utils/cachedSupersetGet';
|
|
|
|
import { DrillByMenuItems, DrillByMenuItemsProps } from './DrillByMenuItems';
|
|
|
|
|
|
|
|
/* eslint jest/expect-expect: ["warn", { "assertFunctionNames": ["expect*"] }] */
|
|
|
|
|
2023-04-12 07:43:09 -04:00
|
|
|
const DATASET_ENDPOINT = 'glob:*/api/v1/dataset/7';
|
|
|
|
const CHART_DATA_ENDPOINT = 'glob:*/api/v1/chart/data*';
|
|
|
|
const FORM_DATA_KEY_ENDPOINT = 'glob:*/api/v1/explore/form_data';
|
2023-03-29 09:01:51 -04:00
|
|
|
const { form_data: defaultFormData } = chartQueries[sliceId];
|
|
|
|
|
|
|
|
const defaultColumns = [
|
|
|
|
{ column_name: 'col1', groupby: true },
|
|
|
|
{ column_name: 'col2', groupby: true },
|
|
|
|
{ column_name: 'col3', groupby: true },
|
|
|
|
{ column_name: 'col4', groupby: true },
|
|
|
|
{ column_name: 'col5', groupby: true },
|
|
|
|
{ column_name: 'col6', groupby: true },
|
|
|
|
{ column_name: 'col7', groupby: true },
|
|
|
|
{ column_name: 'col8', groupby: true },
|
|
|
|
{ column_name: 'col9', groupby: true },
|
|
|
|
{ column_name: 'col10', groupby: true },
|
|
|
|
{ column_name: 'col11', groupby: true },
|
|
|
|
];
|
|
|
|
|
|
|
|
const defaultFilters = [
|
|
|
|
{
|
|
|
|
col: 'filter_col',
|
|
|
|
op: '==' as const,
|
|
|
|
val: 'val',
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const renderMenu = ({
|
|
|
|
formData = defaultFormData,
|
2023-04-21 02:50:44 -04:00
|
|
|
drillByConfig = { filters: defaultFilters, groupbyFieldName: 'groupby' },
|
2023-04-12 07:43:09 -04:00
|
|
|
...rest
|
2023-03-29 09:01:51 -04:00
|
|
|
}: Partial<DrillByMenuItemsProps>) =>
|
|
|
|
render(
|
|
|
|
<Menu>
|
|
|
|
<DrillByMenuItems
|
|
|
|
formData={formData ?? defaultFormData}
|
2023-04-21 02:50:44 -04:00
|
|
|
drillByConfig={drillByConfig}
|
2023-04-12 07:43:09 -04:00
|
|
|
{...rest}
|
2023-03-29 09:01:51 -04:00
|
|
|
/>
|
|
|
|
</Menu>,
|
|
|
|
{ useRouter: true, useRedux: true },
|
|
|
|
);
|
|
|
|
|
|
|
|
const expectDrillByDisabled = async (tooltipContent: string) => {
|
|
|
|
const drillByMenuItem = screen.getByRole('menuitem', {
|
|
|
|
name: 'Drill by',
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(drillByMenuItem).toBeVisible();
|
|
|
|
expect(drillByMenuItem).toHaveAttribute('aria-disabled', 'true');
|
|
|
|
const tooltipTrigger = within(drillByMenuItem).getByTestId('tooltip-trigger');
|
|
|
|
userEvent.hover(tooltipTrigger as HTMLElement);
|
|
|
|
const tooltip = await screen.findByRole('tooltip', { name: tooltipContent });
|
|
|
|
|
|
|
|
expect(tooltip).toBeInTheDocument();
|
|
|
|
};
|
|
|
|
|
|
|
|
const expectDrillByEnabled = async () => {
|
|
|
|
const drillByMenuItem = screen.getByRole('menuitem', {
|
|
|
|
name: 'Drill by',
|
|
|
|
});
|
|
|
|
expect(drillByMenuItem).toBeInTheDocument();
|
|
|
|
await waitFor(() =>
|
|
|
|
expect(drillByMenuItem).not.toHaveAttribute('aria-disabled'),
|
|
|
|
);
|
|
|
|
const tooltipTrigger =
|
|
|
|
within(drillByMenuItem).queryByTestId('tooltip-trigger');
|
|
|
|
expect(tooltipTrigger).not.toBeInTheDocument();
|
|
|
|
|
|
|
|
userEvent.hover(
|
|
|
|
within(drillByMenuItem).getByRole('button', { name: 'Drill by' }),
|
|
|
|
);
|
|
|
|
expect(await screen.findByTestId('drill-by-submenu')).toBeInTheDocument();
|
|
|
|
};
|
|
|
|
|
|
|
|
getChartMetadataRegistry().registerValue(
|
|
|
|
'pie',
|
|
|
|
new ChartMetadata({
|
|
|
|
name: 'fake pie',
|
|
|
|
thumbnail: '.png',
|
|
|
|
useLegacyApi: false,
|
|
|
|
behaviors: [Behavior.DRILL_BY],
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
afterEach(() => {
|
|
|
|
supersetGetCache.clear();
|
|
|
|
fetchMock.restore();
|
|
|
|
});
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
test('render disabled menu item for unsupported chart', async () => {
|
|
|
|
renderMenu({
|
|
|
|
formData: { ...defaultFormData, viz_type: 'unsupported_viz' },
|
2023-03-29 09:01:51 -04:00
|
|
|
});
|
2023-04-04 09:50:35 -04:00
|
|
|
await expectDrillByDisabled(
|
|
|
|
'Drill by is not yet supported for this chart type',
|
|
|
|
);
|
|
|
|
});
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
test('render disabled menu item for supported chart, no filters', async () => {
|
2023-04-21 02:50:44 -04:00
|
|
|
renderMenu({ drillByConfig: { filters: [], groupbyFieldName: 'groupby' } });
|
2023-04-04 09:50:35 -04:00
|
|
|
await expectDrillByDisabled('Drill by is not available for this data point');
|
|
|
|
});
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
test('render disabled menu item for supported chart, no columns', async () => {
|
2023-04-12 07:43:09 -04:00
|
|
|
fetchMock.get(DATASET_ENDPOINT, { result: { columns: [] } });
|
2023-04-04 09:50:35 -04:00
|
|
|
renderMenu({});
|
2023-04-12 07:43:09 -04:00
|
|
|
await waitFor(() => fetchMock.called(DATASET_ENDPOINT));
|
2023-04-26 13:36:21 -04:00
|
|
|
await expectDrillByEnabled();
|
|
|
|
screen.getByText('No columns found');
|
2023-04-04 09:50:35 -04:00
|
|
|
});
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
test('render menu item with submenu without searchbox', async () => {
|
|
|
|
const slicedColumns = defaultColumns.slice(0, 9);
|
2023-04-12 07:43:09 -04:00
|
|
|
fetchMock.get(DATASET_ENDPOINT, {
|
2023-04-04 09:50:35 -04:00
|
|
|
result: { columns: slicedColumns },
|
2023-03-29 09:01:51 -04:00
|
|
|
});
|
2023-04-04 09:50:35 -04:00
|
|
|
renderMenu({});
|
2023-04-12 07:43:09 -04:00
|
|
|
await waitFor(() => fetchMock.called(DATASET_ENDPOINT));
|
2023-04-04 09:50:35 -04:00
|
|
|
await expectDrillByEnabled();
|
|
|
|
slicedColumns.forEach(column => {
|
|
|
|
expect(screen.getByText(column.column_name)).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
|
|
|
|
});
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
test('render menu item with submenu and searchbox', async () => {
|
2023-04-12 07:43:09 -04:00
|
|
|
fetchMock.get(DATASET_ENDPOINT, {
|
2023-04-04 09:50:35 -04:00
|
|
|
result: { columns: defaultColumns },
|
|
|
|
});
|
|
|
|
renderMenu({});
|
2023-04-12 07:43:09 -04:00
|
|
|
await waitFor(() => fetchMock.called(DATASET_ENDPOINT));
|
2023-04-04 09:50:35 -04:00
|
|
|
await expectDrillByEnabled();
|
|
|
|
defaultColumns.forEach(column => {
|
|
|
|
expect(screen.getByText(column.column_name)).toBeInTheDocument();
|
|
|
|
});
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
const searchbox = screen.getByRole('textbox');
|
|
|
|
expect(searchbox).toBeInTheDocument();
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
userEvent.type(searchbox, 'col1');
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
await screen.findByText('col1');
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
const expectedFilteredColumnNames = ['col1', 'col10', 'col11'];
|
2023-03-29 09:01:51 -04:00
|
|
|
|
2023-04-04 09:50:35 -04:00
|
|
|
defaultColumns
|
|
|
|
.filter(col => !expectedFilteredColumnNames.includes(col.column_name))
|
|
|
|
.forEach(col => {
|
|
|
|
expect(screen.queryByText(col.column_name)).not.toBeInTheDocument();
|
2023-03-29 09:01:51 -04:00
|
|
|
});
|
2023-04-04 09:50:35 -04:00
|
|
|
|
|
|
|
expectedFilteredColumnNames.forEach(colName => {
|
|
|
|
expect(screen.getByText(colName)).toBeInTheDocument();
|
2023-03-29 09:01:51 -04:00
|
|
|
});
|
|
|
|
});
|
2023-04-12 07:43:09 -04:00
|
|
|
|
|
|
|
test('Do not display excluded column in the menu', async () => {
|
|
|
|
fetchMock.get(DATASET_ENDPOINT, {
|
|
|
|
result: { columns: defaultColumns },
|
|
|
|
});
|
|
|
|
|
|
|
|
const excludedColNames = ['col3', 'col5'];
|
|
|
|
renderMenu({
|
|
|
|
excludedColumns: excludedColNames.map(colName => ({
|
|
|
|
column_name: colName,
|
|
|
|
})),
|
|
|
|
});
|
|
|
|
|
|
|
|
await waitFor(() => fetchMock.called(DATASET_ENDPOINT));
|
|
|
|
await expectDrillByEnabled();
|
|
|
|
|
|
|
|
excludedColNames.forEach(colName => {
|
|
|
|
expect(screen.queryByText(colName)).not.toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
|
|
|
defaultColumns
|
|
|
|
.filter(column => !excludedColNames.includes(column.column_name))
|
|
|
|
.forEach(column => {
|
|
|
|
expect(screen.getByText(column.column_name)).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test('When menu item is clicked, call onSelection with clicked column and drill by filters', async () => {
|
|
|
|
fetchMock
|
|
|
|
.get(DATASET_ENDPOINT, {
|
|
|
|
result: { columns: defaultColumns },
|
|
|
|
})
|
|
|
|
.post(FORM_DATA_KEY_ENDPOINT, {})
|
|
|
|
.post(CHART_DATA_ENDPOINT, {});
|
|
|
|
|
|
|
|
const onSelectionMock = jest.fn();
|
|
|
|
renderMenu({
|
|
|
|
onSelection: onSelectionMock,
|
|
|
|
});
|
|
|
|
|
|
|
|
await waitFor(() => fetchMock.called(DATASET_ENDPOINT));
|
|
|
|
await expectDrillByEnabled();
|
|
|
|
|
|
|
|
userEvent.click(screen.getByText('col1'));
|
|
|
|
expect(onSelectionMock).toHaveBeenCalledWith(
|
|
|
|
{
|
|
|
|
column_name: 'col1',
|
|
|
|
groupby: true,
|
|
|
|
},
|
2023-04-21 02:50:44 -04:00
|
|
|
{ filters: defaultFilters, groupbyFieldName: 'groupby' },
|
2023-04-12 07:43:09 -04:00
|
|
|
);
|
|
|
|
});
|