mirror of
https://github.com/apache/superset.git
synced 2026-07-29 10:02:32 +00:00
test(drill-detail): cover download handlers for CSV/XLSX flows
Adds unit tests for DownloadDropdown menu clicks and integration tests for DrillDetailPane (asserts SupersetClient.postForm payload: result_type 'drill_detail', ROW_LIMIT, dashboardId) and DrillByModal (asserts exportChart called with correct resultFormat). Bumps patch coverage on new download logic from ~45% toward full coverage of the added code paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,19 @@ jest.mock('src/dashboard/util/isEmbedded', () => ({
|
||||
isEmbedded: jest.fn(() => false),
|
||||
}));
|
||||
|
||||
jest.mock('src/explore/exploreUtils', () => {
|
||||
const actual = jest.requireActual('src/explore/exploreUtils');
|
||||
return {
|
||||
...actual,
|
||||
exportChart: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { exportChart: exportChartMock } = jest.requireMock(
|
||||
'src/explore/exploreUtils',
|
||||
) as { exportChart: jest.Mock };
|
||||
|
||||
const CHART_DATA_ENDPOINT = 'glob:*/api/v1/chart/data*';
|
||||
const FORM_DATA_KEY_ENDPOINT = 'glob:*/api/v1/explore/form_data';
|
||||
|
||||
@@ -553,4 +566,64 @@ describe('Table view with pagination', () => {
|
||||
// Table should still be rendered without crashes
|
||||
expect(screen.getByTestId('drill-by-results-table')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('CSV download calls exportChart with drilledFormData', async () => {
|
||||
exportChartMock.mockClear();
|
||||
await renderModal({
|
||||
column: { column_name: 'state', verbose_name: null },
|
||||
drillByConfig: {
|
||||
filters: [{ col: 'gender', op: '==', val: 'boy' }],
|
||||
groupbyFieldName: 'groupby',
|
||||
},
|
||||
});
|
||||
|
||||
const tableRadio = await screen.findByRole('radio', { name: /table/i });
|
||||
userEvent.click(tableRadio);
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('drill-by-results-table')).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
await userEvent.click(
|
||||
await screen.findByRole('button', { name: 'Download' }),
|
||||
);
|
||||
await userEvent.click(await screen.findByText('Export to CSV'));
|
||||
|
||||
expect(exportChartMock).toHaveBeenCalledTimes(1);
|
||||
expect(exportChartMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
resultFormat: 'csv',
|
||||
resultType: 'full',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('XLSX download calls exportChart with xlsx format', async () => {
|
||||
exportChartMock.mockClear();
|
||||
await renderModal({
|
||||
column: { column_name: 'state', verbose_name: null },
|
||||
drillByConfig: {
|
||||
filters: [{ col: 'gender', op: '==', val: 'boy' }],
|
||||
groupbyFieldName: 'groupby',
|
||||
},
|
||||
});
|
||||
|
||||
const tableRadio = await screen.findByRole('radio', { name: /table/i });
|
||||
userEvent.click(tableRadio);
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('drill-by-results-table')).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
await userEvent.click(
|
||||
await screen.findByRole('button', { name: 'Download' }),
|
||||
);
|
||||
await userEvent.click(await screen.findByText('Export to Excel'));
|
||||
|
||||
expect(exportChartMock).toHaveBeenCalledTimes(1);
|
||||
expect(exportChartMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
resultFormat: 'xlsx',
|
||||
resultType: 'full',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 { render, screen, userEvent } from 'spec/helpers/testing-library';
|
||||
import DownloadDropdown from './DownloadDropdown';
|
||||
|
||||
const onDownloadCSV = jest.fn();
|
||||
const onDownloadXLSX = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
onDownloadCSV.mockClear();
|
||||
onDownloadXLSX.mockClear();
|
||||
});
|
||||
|
||||
const setup = () =>
|
||||
render(
|
||||
<DownloadDropdown
|
||||
onDownloadCSV={onDownloadCSV}
|
||||
onDownloadXLSX={onDownloadXLSX}
|
||||
/>,
|
||||
);
|
||||
|
||||
test('renders a download trigger with accessible label', () => {
|
||||
setup();
|
||||
expect(screen.getByRole('button', { name: 'Download' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('fires onDownloadCSV when CSV menu item is selected', async () => {
|
||||
setup();
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Download' }));
|
||||
await userEvent.click(await screen.findByText('Export to CSV'));
|
||||
expect(onDownloadCSV).toHaveBeenCalledTimes(1);
|
||||
expect(onDownloadXLSX).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('fires onDownloadXLSX when Excel menu item is selected', async () => {
|
||||
setup();
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Download' }));
|
||||
await userEvent.click(await screen.findByText('Export to Excel'));
|
||||
expect(onDownloadXLSX).toHaveBeenCalledTimes(1);
|
||||
expect(onDownloadCSV).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -18,7 +18,12 @@
|
||||
*/
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { QueryFormData, SupersetClient } from '@superset-ui/core';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
userEvent,
|
||||
waitFor,
|
||||
} from 'spec/helpers/testing-library';
|
||||
import { getMockStoreWithNativeFilters } from 'spec/fixtures/mockStore';
|
||||
import chartQueries, { sliceId } from 'spec/fixtures/mockChartQueries';
|
||||
import { supersetGetCache } from 'src/utils/cachedSupersetGet';
|
||||
@@ -189,6 +194,66 @@ test('should render the error', async () => {
|
||||
expect(screen.getByText('Error: Something went wrong')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('download actions', () => {
|
||||
const renderWithDownloadPermission = () =>
|
||||
render(
|
||||
<DrillDetailPane
|
||||
initialFilters={[]}
|
||||
formData={chart.form_data as unknown as QueryFormData}
|
||||
/>,
|
||||
{
|
||||
useRedux: true,
|
||||
initialState: {
|
||||
user: { roles: { Admin: [['can_csv', 'Superset']] } },
|
||||
common: { conf: { SAMPLES_ROW_LIMIT: 10, ROW_LIMIT: 50000 } },
|
||||
dashboardInfo: { id: 123 },
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const clickDownloadItem = async (label: string) => {
|
||||
await userEvent.click(
|
||||
await screen.findByRole('button', { name: 'Download' }),
|
||||
);
|
||||
await userEvent.click(await screen.findByText(label));
|
||||
};
|
||||
|
||||
test('CSV export posts drill_detail payload with ROW_LIMIT', async () => {
|
||||
fetchWithData();
|
||||
const postFormSpy = jest
|
||||
.spyOn(SupersetClient, 'postForm')
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
renderWithDownloadPermission();
|
||||
|
||||
await clickDownloadItem('Export to CSV');
|
||||
|
||||
expect(postFormSpy).toHaveBeenCalledTimes(1);
|
||||
const body = postFormSpy.mock.calls[0][1] as { form_data: string };
|
||||
const payload = JSON.parse(body.form_data);
|
||||
expect(payload.result_type).toBe('drill_detail');
|
||||
expect(payload.result_format).toBe('csv');
|
||||
expect(payload.queries[0].row_limit).toBe(50000);
|
||||
expect(payload.form_data.dashboardId).toBe(123);
|
||||
postFormSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('XLSX export uses xlsx result_format', async () => {
|
||||
fetchWithData();
|
||||
const postFormSpy = jest
|
||||
.spyOn(SupersetClient, 'postForm')
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
renderWithDownloadPermission();
|
||||
|
||||
await clickDownloadItem('Export to Excel');
|
||||
|
||||
expect(postFormSpy).toHaveBeenCalledTimes(1);
|
||||
const body = postFormSpy.mock.calls[0][1] as { form_data: string };
|
||||
const payload = JSON.parse(body.form_data);
|
||||
expect(payload.result_format).toBe('xlsx');
|
||||
postFormSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
test('should use verbose_map for column headers when available', async () => {
|
||||
jest.restoreAllMocks();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user