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();
+});

Reply via email to