This is an automated email from the ASF dual-hosted git repository. jli pushed a commit to branch feat-additional-dataset-playwright-tests in repository https://gitbox.apache.org/repos/asf/superset.git
commit d90ec33556d6584b0a166130be75bbe650e7577c Author: Joe Li <[email protected]> AuthorDate: Tue Dec 16 13:13:29 2025 -0800 test(playwright): add bulk export datasets E2E test Add test for bulk exporting datasets via bulk select mode. The test verifies: - Bulk select mode can be enabled - Datasets can be selected via checkboxes - Export bulk action triggers download - Downloaded file is a zip file Add Checkbox component to components/core for checkbox interactions. Add bulk select methods to DatasetListPage: - getBulkSelectButton() / clickBulkSelectButton() - getDatasetCheckbox() / selectDatasetCheckbox() - getBulkActionButton() / clickBulkAction() - getBulkSelectControls() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --- .../playwright/components/core/Checkbox.ts | 80 ++++++++++++++++++++++ .../playwright/components/core/index.ts | 1 + .../playwright/pages/DatasetListPage.ts | 69 ++++++++++++++++++- .../experimental/dataset/dataset-list.spec.ts | 35 ++++++++++ 4 files changed, 184 insertions(+), 1 deletion(-) diff --git a/superset-frontend/playwright/components/core/Checkbox.ts b/superset-frontend/playwright/components/core/Checkbox.ts new file mode 100644 index 0000000000..612b8867b1 --- /dev/null +++ b/superset-frontend/playwright/components/core/Checkbox.ts @@ -0,0 +1,80 @@ +/** + * 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 { Locator } from '@playwright/test'; + +/** + * Checkbox component for checkbox interactions. + */ +export class Checkbox { + private readonly locator: Locator; + + constructor(locator: Locator) { + this.locator = locator; + } + + /** + * Gets the checkbox element locator + */ + get element(): Locator { + return this.locator; + } + + /** + * Checks the checkbox (ensures it's checked) + */ + async check(): Promise<void> { + await this.locator.check(); + } + + /** + * Unchecks the checkbox (ensures it's unchecked) + */ + async uncheck(): Promise<void> { + await this.locator.uncheck(); + } + + /** + * Toggles the checkbox state + */ + async toggle(): Promise<void> { + await this.locator.click(); + } + + /** + * Checks if the checkbox is checked + */ + async isChecked(): Promise<boolean> { + return this.locator.isChecked(); + } + + /** + * Checks if the checkbox is visible + */ + async isVisible(): Promise<boolean> { + return this.locator.isVisible(); + } + + /** + * Checks if the checkbox is enabled + */ + async isEnabled(): Promise<boolean> { + return this.locator.isEnabled(); + } +} diff --git a/superset-frontend/playwright/components/core/index.ts b/superset-frontend/playwright/components/core/index.ts index 82a26c2b69..c0bec9f231 100644 --- a/superset-frontend/playwright/components/core/index.ts +++ b/superset-frontend/playwright/components/core/index.ts @@ -19,6 +19,7 @@ // Core Playwright Components for Superset export { Button } from './Button'; +export { Checkbox } from './Checkbox'; export { Form } from './Form'; export { Input } from './Input'; export { Modal } from './Modal'; diff --git a/superset-frontend/playwright/pages/DatasetListPage.ts b/superset-frontend/playwright/pages/DatasetListPage.ts index a7b6af75a1..89662021be 100644 --- a/superset-frontend/playwright/pages/DatasetListPage.ts +++ b/superset-frontend/playwright/pages/DatasetListPage.ts @@ -18,7 +18,7 @@ */ import { Page, Locator } from '@playwright/test'; -import { Table } from '../components/core'; +import { Button, Checkbox, Table } from '../components/core'; import { URL } from '../utils/urls'; /** @@ -33,6 +33,8 @@ export class DatasetListPage { DELETE_ACTION: '.action-button svg[data-icon="delete"]', EXPORT_ACTION: '.action-button svg[data-icon="upload"]', DUPLICATE_ACTION: '.action-button svg[data-icon="copy"]', + BULK_SELECT_CONTROLS: '[data-test="bulk-select-controls"]', + BULK_SELECT_ACTION: '[data-test="bulk-select-action"]', } as const; constructor(page: Page) { @@ -112,4 +114,69 @@ export class DatasetListPage { DatasetListPage.SELECTORS.DUPLICATE_ACTION, ); } + + /** + * Gets the "Bulk select" button + */ + getBulkSelectButton(): Button { + return new Button( + this.page, + this.page.getByRole('button', { name: 'Bulk select' }), + ); + } + + /** + * Clicks the "Bulk select" button to enable bulk selection mode + */ + async clickBulkSelectButton(): Promise<void> { + await this.getBulkSelectButton().click(); + } + + /** + * Gets the checkbox for a dataset row + * @param datasetName - The name of the dataset + */ + getDatasetCheckbox(datasetName: string): Checkbox { + const row = this.table.getRow(datasetName); + return new Checkbox(row.getByRole('checkbox')); + } + + /** + * Selects a dataset's checkbox in bulk select mode + * @param datasetName - The name of the dataset to select + */ + async selectDatasetCheckbox(datasetName: string): Promise<void> { + await this.getDatasetCheckbox(datasetName).check(); + } + + /** + * Gets a bulk action button by name + * @param actionName - The name of the bulk action (e.g., "Export", "Delete") + */ + getBulkActionButton(actionName: string): Button { + const bulkControls = this.page.locator( + DatasetListPage.SELECTORS.BULK_SELECT_CONTROLS, + ); + return new Button( + this.page, + bulkControls.locator(DatasetListPage.SELECTORS.BULK_SELECT_ACTION, { + hasText: actionName, + }), + ); + } + + /** + * Clicks a bulk action button by name (e.g., "Export", "Delete") + * @param actionName - The name of the bulk action to click + */ + async clickBulkAction(actionName: string): Promise<void> { + await this.getBulkActionButton(actionName).click(); + } + + /** + * Gets the bulk select controls locator (for assertions) + */ + getBulkSelectControls(): Locator { + return this.page.locator(DatasetListPage.SELECTORS.BULK_SELECT_CONTROLS); + } } diff --git a/superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts b/superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts index 2bc8eeb389..b4d9fb7e7e 100644 --- a/superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts +++ b/superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts @@ -278,3 +278,38 @@ test('should export a single dataset', async ({ page }) => { const failure = await download.failure(); expect(failure).toBeNull(); }); + +test('should bulk export multiple datasets', async ({ page }) => { + // Use existing example dataset for bulk export + const datasetName = TEST_DATASETS.EXAMPLE_DATASET; + + // Verify dataset is visible in list + await expect(datasetListPage.getDatasetRow(datasetName)).toBeVisible(); + + // Enable bulk select mode + await datasetListPage.clickBulkSelectButton(); + + // Verify bulk select controls appear + await expect(datasetListPage.getBulkSelectControls()).toBeVisible(); + + // Select the dataset checkbox + await datasetListPage.selectDatasetCheckbox(datasetName); + + // Set up download handler before triggering bulk export + const downloadPromise = page.waitForEvent('download'); + + // Click Export bulk action + await datasetListPage.clickBulkAction('Export'); + + // Wait for download to complete + const download = await downloadPromise; + + // Verify downloaded file is a zip + const fileName = download.suggestedFilename(); + expect(fileName).toMatch(/\.zip$/); + expect(fileName).toContain('dataset'); + + // Verify download completed successfully (no failure) + const failure = await download.failure(); + expect(failure).toBeNull(); +});
