Skip to content

Conversation

@sadpandajoe
Copy link
Member

@sadpandajoe sadpandajoe commented Dec 16, 2025

SUMMARY

Adds comprehensive Playwright E2E test coverage for the Dataset List page, covering all major CRUD operations accessible from the list view.

Tests Added (12 total):

Test Description
Navigate to Explore Click dataset name → redirects to Explore page
Delete single Row delete action → confirmation modal → API delete
Delete bulk Multi-select → bulk delete → API verification (404)
Duplicate Virtual dataset → duplicate modal → new dataset created
Export single Row export action → validates zip contents
Export bulk Multi-select → bulk export → validates zip with multiple datasets
Edit name Edit modal → unlock → change name → confirm save
Edit description Edit modal → Settings tab → update description
Edit column date format Edit modal → Columns tab → set python_date_format
Create via wizard Google Sheets database → select table → create + explore
Create without exploring Google Sheets database → "Create dataset" option → list redirect
Import Upload zip fixture → handle overwrite flow → verify import

Infrastructure Added:

  • testAssets fixture for automatic cleanup (datasets, databases)
  • createTestDataset() helper using virtual datasets (duplicate API only works with virtual)
  • Inline datasetListPage fixture for navigation (Playwright-idiomatic beforeEach)
  • Generic API intercept helpers (waitForGet, waitForPost, etc.)
  • Status assertion utilities (expectStatus, expectStatusOneOf)
  • ENDPOINTS constants for API path discoverability
  • ImportDatasetModal page object

Components Added:

  • Select - Ant Design combobox with type-to-filter
  • Checkbox - Checkbox interactions for bulk select
  • ConfirmDialog - OK/Cancel confirmation dialogs
  • EditDatasetModal - Dataset edit modal with lock/unlock, tabs, description editor
  • CreateDatasetPage - Create dataset wizard page object
  • ChartCreationPage - Chart creation wizard (post-dataset creation)

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A - Test-only changes

TESTING INSTRUCTIONS

# Run all dataset tests
INCLUDE_EXPERIMENTAL=true npx playwright test tests/experimental/dataset/

# Run specific test
INCLUDE_EXPERIMENTAL=true npx playwright test tests/experimental/dataset/dataset-list.spec.ts

Note: Tests require Google Sheets connector (shillelagh[gsheetsapi]) for create/import tests. Tests will skip gracefully if unavailable.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

Related PRs:

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive Playwright end-to-end tests for the dataset list page, expanding test coverage for export, bulk operations, dataset creation wizard navigation, and dataset editing functionality. The changes introduce new page object models and core component wrappers to support these additional test scenarios.

  • Adds 4 new test cases covering export (single and bulk), create wizard navigation, and dataset editing
  • Introduces new page objects (CreateDatasetPage, EditDatasetModal, ConfirmDialog) for better test organization
  • Adds reusable core components (Select, Checkbox, Tabs, Textarea) to the component library
  • Refactors test file to remove unused file-scoped variables

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts Adds 4 new tests for export, bulk export, create wizard, and edit functionality; removes unused explorePage variable
superset-frontend/playwright/pages/DatasetListPage.ts Adds methods for bulk selection, edit action, and checkbox interactions
superset-frontend/playwright/pages/CreateDatasetPage.ts New page object for dataset creation wizard with database/schema/table selection
superset-frontend/playwright/components/modals/EditDatasetModal.ts New modal component for dataset editing with description and tab navigation support
superset-frontend/playwright/components/modals/ConfirmDialog.ts New component for Ant Design confirmation dialogs with OK/Cancel actions
superset-frontend/playwright/components/core/Checkbox.ts New core component wrapper for checkbox interactions
superset-frontend/playwright/components/core/Select.ts New core component wrapper for Ant Design Select/Combobox with filtering support
superset-frontend/playwright/components/core/Tabs.ts New core component wrapper for Ant Design tab navigation
superset-frontend/playwright/components/core/Textarea.ts New core component wrapper for multi-line text input
superset-frontend/playwright/components/core/index.ts Exports new core components for use throughout the test suite

@sadpandajoe sadpandajoe force-pushed the feat-additional-dataset-playwright-tests branch from 2b82b04 to a94eaff Compare December 18, 2025 03:30
@sadpandajoe sadpandajoe force-pushed the feat-additional-dataset-playwright-tests branch from 314176b to 017c171 Compare December 18, 2025 05:50
@sadpandajoe sadpandajoe force-pushed the feat-additional-dataset-playwright-tests branch from e257bdf to 8775c2a Compare January 12, 2026 19:00
@netlify
Copy link

netlify bot commented Jan 12, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit d11046e
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/698131ca8dab7400086f9a0e
😎 Deploy Preview https://deploy-preview-36684--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@sadpandajoe sadpandajoe force-pushed the feat-additional-dataset-playwright-tests branch from 8775c2a to 1d21092 Compare January 12, 2026 22:27
@sadpandajoe sadpandajoe moved this from In progress to In review in Superset Playwright Migration Jan 21, 2026
@sadpandajoe sadpandajoe marked this pull request as ready for review January 21, 2026 00:54
@dosubot dosubot bot added the listview Namespace | Anything related to lists, such as Dashboards, Charts, Datasets, etc. label Jan 21, 2026
@sadpandajoe sadpandajoe requested a review from Copilot January 21, 2026 00:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 26 changed files in this pull request and generated 10 comments.

Files not reviewed (1)
  • superset-frontend/package-lock.json: Language not supported

Copy link
Contributor

@bito-code-review bito-code-review bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #0ee456

Actionable Suggestions - 2
  • superset-frontend/playwright/components/core/AceEditor.ts - 1
  • superset-frontend/playwright/helpers/api/database.ts - 1
    • Interface Mismatch with API Response · Line 31-45
Additional Suggestions - 6
  • superset-frontend/playwright/components/modals/ConfirmDialog.ts - 1
    • Discouraged waitForTimeout usage · Line 51-51
      The waitForTimeout here is discouraged in Playwright as it introduces fixed delays that can make tests unreliable. Playwright's click() method already waits for elements to be stable, including during animations, so this explicit wait is unnecessary and potentially harmful.
      Code suggestion
       @@ -49,4 +49,2 @@
      -  async clickOk(): Promise<void> {
      -    // Wait for modal animation to complete before clicking
      -    await this.page.waitForTimeout(200);
      -    await this.clickFooterButton('OK');
      +  async clickOk(): Promise<void> {
      +    await this.clickFooterButton('OK');
          }
  • superset-frontend/playwright/components/core/Select.ts - 1
    • Flaky test timing · Line 87-87
      The use of page.waitForTimeout here can make tests unreliable in slower environments or CI. It appears better to wait for the actual dropdown state change instead of a fixed delay.
  • superset-frontend/playwright/components/core/Checkbox.ts - 1
    • Inconsistent constructor API · Line 29-32
      The constructor only accepts Locator, unlike other Playwright components (e.g., `Button` and `Input`) that accept string | Locator for flexibility. This creates inconsistency in the API.
      Code suggestion
       @@ -28,5 +28,5 @@
      - 
      - constructor(page: Page, locator: Locator) {
      -     this.page = page;
      -     this.locator = locator;
      -   }
      + constructor(page: Page, locator: string);
      + constructor(page: Page, locator: string | Locator) {
      +     this.page = page;
      +     this.locator = typeof locator === 'string' ? page.locator(locator) : locator;
      +   }
  • superset-frontend/playwright/components/core/AceEditor.ts - 2
    • Type Safety Violation · Line 69-69
      The code uses (window as any).ace.edit(id) in multiple methods, which violates the project's strict rule against 'any' types. This undermines TypeScript type safety efforts. A global declaration can provide proper typing without 'any'.
      Code suggestion
       @@ -20,2 +20,6 @@
      - import { Locator, Page } from '@playwright/test';
      - 
      + import { Locator, Page } from '@playwright/test';
      - 
      + 
      + declare global {
      +   interface Window {
      +     ace: any;
      +   }
      + }
    • Extra Newlines in Append · Line 108-108
      The appendText logic adds a newline even if the current text already ends with one, leading to extra blank lines. This can cause inconsistent editor content.
      Code suggestion
       @@ -108,1 +108,1 @@
      -       const newText = currentText + (currentText ? '\\n' : '') + value;
      +       const newText = currentText + (currentText && !currentText.endsWith('\n') ? '\n' : '') + value;
  • superset-frontend/playwright/pages/ChartCreationPage.ts - 1
    • Unused selector dead code · Line 32-32
      The VIZ_TYPE_ITEM selector is defined but never used in the class methods. This creates dead code that should be removed to keep the codebase clean.
Review Details
  • Files reviewed - 23 · Commit Range: 2dcebac..a5303ba
    • superset-frontend/playwright.config.ts
    • superset-frontend/playwright/components/core/AceEditor.ts
    • superset-frontend/playwright/components/core/Checkbox.ts
    • superset-frontend/playwright/components/core/Select.ts
    • superset-frontend/playwright/components/core/Tabs.ts
    • superset-frontend/playwright/components/core/Textarea.ts
    • superset-frontend/playwright/components/core/index.ts
    • superset-frontend/playwright/components/modals/ConfirmDialog.ts
    • superset-frontend/playwright/components/modals/DuplicateDatasetModal.ts
    • superset-frontend/playwright/components/modals/EditDatasetModal.ts
    • superset-frontend/playwright/components/modals/ImportDatasetModal.ts
    • superset-frontend/playwright/components/modals/index.ts
    • superset-frontend/playwright/helpers/api/assertions.ts
    • superset-frontend/playwright/helpers/api/database.ts
    • superset-frontend/playwright/helpers/api/dataset.ts
    • superset-frontend/playwright/helpers/api/intercepts.ts
    • superset-frontend/playwright/helpers/fixtures/index.ts
    • superset-frontend/playwright/helpers/fixtures/testAssets.ts
    • superset-frontend/playwright/pages/ChartCreationPage.ts
    • superset-frontend/playwright/pages/CreateDatasetPage.ts
    • superset-frontend/playwright/pages/DatasetListPage.ts
    • superset-frontend/playwright/tests/experimental/dataset/create-dataset.spec.ts
    • superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts
  • Files skipped - 2
    • superset-frontend/package-lock.json - Reason: Filter setting
    • superset-frontend/package.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

// Extract origin (scheme + host + port) for health check
const healthUrl = new URL('health', baseUrl).href;
return {
command: `curl -f ${healthUrl}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The shell command string interpolates healthUrl directly into curl -f ${healthUrl}; because healthUrl is ultimately derived from the PLAYWRIGHT_BASE_URL environment variable, a value containing shell metacharacters like ; can break out of the command and execute arbitrary shell instructions when Playwright starts the webServer, creating a command injection risk even though it is primarily a test configuration. [security]

Severity Level: Critical 🚨
- ❌ Arbitrary shell command execution possible via env var.
- ⚠️ Local development environment security risk.
- ⚠️ CI secrets misuse could escalate risk.
Suggested change
command: `curl -f ${healthUrl}`,
command: `curl -f "${healthUrl}"`,
Steps of Reproduction ✅
1. Start tests locally where Playwright uses the webServer config in
superset-frontend/playwright.config.ts (webServer closure around lines 120-136).

2. Export a malicious PLAYWRIGHT_BASE_URL such as:
PLAYWRIGHT_BASE_URL='http://localhost:8088/health; echo hacked > /tmp/pwn' and run
Playwright. The code constructs healthUrl from the env var and then places it unquoted
into the shell command at line 130: `command: \`curl -f ${healthUrl}\``.

3. When Playwright executes the webServer command, the unquoted interpolation allows the
shell to interpret the semicolon and run the appended command, performing arbitrary
actions (e.g., creating /tmp/pwn). Observe the side-effect on the filesystem or process
list to confirm command execution.

4. Note: In typical CI the env var is controlled and this is unlikely, but locally or in a
compromised environment this creates a real command-injection vector originating from this
config line (superset-frontend/playwright.config.ts:130).
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/playwright.config.ts
**Line:** 130:130
**Comment:**
	*Security: The shell command string interpolates `healthUrl` directly into `curl -f ${healthUrl}`; because `healthUrl` is ultimately derived from the `PLAYWRIGHT_BASE_URL` environment variable, a value containing shell metacharacters like `;` can break out of the command and execute arbitrary shell instructions when Playwright starts the webServer, creating a command injection risk even though it is primarily a test configuration.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@bito-code-review
Copy link
Contributor

bito-code-review bot commented Jan 23, 2026

Code Review Agent Run #7c3fe6

Actionable Suggestions - 0
Additional Suggestions - 4
  • superset-frontend/playwright/tests/experimental/dataset/create-dataset.spec.ts - 4
    • Unreachable code after test.skip() · Line 88-88
      The 'return null;' statements after test.skip() are unreachable because test.skip() throws a TestSkippedError, causing the function to exit early. This also makes the Promise type incorrect (null is never returned) and the 'if (!setup) return;' checks in tests dead code. It looks like this could confuse maintainers if test.skip() behavior changes.
    • Unreachable code after test.skip() · Line 121-121
      The 'return null;' statements after test.skip() are unreachable because test.skip() throws a TestSkippedError, causing the function to exit early. This also makes the Promise type incorrect (null is never returned) and the 'if (!setup) return;' checks in tests dead code. It looks like this could confuse maintainers if test.skip() behavior changes.
    • Unreachable code after test.skip() · Line 130-130
      The 'return null;' statements after test.skip() are unreachable because test.skip() throws a TestSkippedError, causing the function to exit early. This also makes the Promise type incorrect (null is never returned) and the 'if (!setup) return;' checks in tests dead code. It looks like this could confuse maintainers if test.skip() behavior changes.
    • Unreachable code after test.skip() · Line 180-180
      The 'return null;' statements after test.skip() are unreachable because test.skip() throws a TestSkippedError, causing the function to exit early. This also makes the Promise type incorrect (null is never returned) and the 'if (!setup) return;' checks in tests dead code. It looks like this could confuse maintainers if test.skip() behavior changes.
Review Details
  • Files reviewed - 24 · Commit Range: a5303ba..57052f3
    • superset-frontend/playwright.config.ts
    • superset-frontend/playwright/components/core/AceEditor.ts
    • superset-frontend/playwright/components/core/Checkbox.ts
    • superset-frontend/playwright/components/core/Select.ts
    • superset-frontend/playwright/components/core/Tabs.ts
    • superset-frontend/playwright/components/core/Textarea.ts
    • superset-frontend/playwright/components/core/index.ts
    • superset-frontend/playwright/components/modals/ConfirmDialog.ts
    • superset-frontend/playwright/components/modals/DuplicateDatasetModal.ts
    • superset-frontend/playwright/components/modals/EditDatasetModal.ts
    • superset-frontend/playwright/components/modals/ImportDatasetModal.ts
    • superset-frontend/playwright/components/modals/index.ts
    • superset-frontend/playwright/helpers/api/assertions.ts
    • superset-frontend/playwright/helpers/api/database.ts
    • superset-frontend/playwright/helpers/api/dataset.ts
    • superset-frontend/playwright/helpers/api/intercepts.ts
    • superset-frontend/playwright/helpers/fixtures/index.ts
    • superset-frontend/playwright/helpers/fixtures/testAssets.ts
    • superset-frontend/playwright/pages/ChartCreationPage.ts
    • superset-frontend/playwright/pages/CreateDatasetPage.ts
    • superset-frontend/playwright/pages/DatasetListPage.ts
    • superset-frontend/playwright/tests/experimental/dataset/create-dataset.spec.ts
    • superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts
    • superset-frontend/playwright/tests/experimental/dataset/dataset-test-helpers.ts
  • Files skipped - 2
    • superset-frontend/package-lock.json - Reason: Filter setting
    • superset-frontend/package.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

This comment was marked as outdated.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 27 changed files in this pull request and generated 17 comments.

Files not reviewed (1)
  • superset-frontend/package-lock.json: Language not supported

Comment on lines +154 to +150
} catch {
// Fallback: locator might be the input itself (e.g., from getByRole('combobox'))
await this.locator.fill(text);
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type method has a try-catch block that falls back to filling the locator directly if the searchInput is not found. However, the catch block is too broad and will catch any error (including network errors, JavaScript errors, etc.). Consider catching more specific errors or at least checking the error type before falling back to the alternative approach.

Suggested change
} catch {
// Fallback: locator might be the input itself (e.g., from getByRole('combobox'))
await this.locator.fill(text);
} catch (error) {
// Only fall back when we fail to find/attach the search input (e.g. timeout)
if (error instanceof Error && /timeout/i.test(error.message)) {
// Fallback: locator might be the input itself (e.g., from getByRole('combobox'))
await this.locator.fill(text);
} else {
// Unexpected error - rethrow so tests fail visibly
throw error;
}

Copilot uses AI. Check for mistakes.
async expandColumn(columnName: string): Promise<Locator> {
// Find cell with exact column name text, then derive row from that cell
const cell = this.body.getByRole('cell', { name: columnName, exact: true });
const row = cell.locator('xpath=ancestor::tr[1]');
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EditDatasetModal.expandColumn method uses an XPath expression 'xpath=ancestor::tr[1]' to find the row element from a cell (line 157). XPath selectors are generally more fragile than modern Playwright locators and can break if the DOM structure changes. Consider using a more robust selector strategy, such as cell.locator('..').locator('xpath=ancestor::tr[1]') or refactoring to use getByRole with table row relationships.

Suggested change
const row = cell.locator('xpath=ancestor::tr[1]');
const row = this.body.getByRole('row', { has: cell });

Copilot uses AI. Check for mistakes.
Comment on lines 72 to 75
} catch (error) {
if (!(error instanceof Error) || error.name !== 'TimeoutError') {
throw error;
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling pattern here only catches TimeoutError but re-throws all other errors. However, the catch block checks if the error is an instance of Error before accessing the name property. If a non-Error object is thrown (e.g., a string or null), this check will succeed but error.name will be undefined, potentially causing unexpected behavior. Consider adding a more defensive check like: if (error?.name !== 'TimeoutError') throw error;

Copilot uses AI. Check for mistakes.
Comment on lines 40 to 41
trackDataset: id => datasetIds.add(id),
trackDatabase: id => databaseIds.add(id),
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testAssets fixture uses Set to de-duplicate IDs (line 36-37), which is good. However, if a test tracks the same ID multiple times (e.g., due to a bug or retry logic), the cleanup will only attempt deletion once. While this is generally fine, consider logging when duplicate IDs are tracked to help identify potential test issues during development.

Suggested change
trackDataset: id => datasetIds.add(id),
trackDatabase: id => databaseIds.add(id),
trackDataset: id => {
if (datasetIds.has(id)) {
console.warn(
`[testAssets] Dataset ID ${id} is being tracked multiple times in the same test.`,
);
}
datasetIds.add(id);
},
trackDatabase: id => {
if (databaseIds.has(id)) {
console.warn(
`[testAssets] Database ID ${id} is being tracked multiple times in the same test.`,
);
}
databaseIds.add(id);
},

Copilot uses AI. Check for mistakes.
await this.locator.waitFor({ state: 'attached' });
await this.locator
.locator(ACE_EDITOR_SELECTORS.CONTENT)
.waitFor({ state: 'attached' });
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AceEditor methods (setText, getText, appendText, focus) all use page.evaluate to directly access the ace global object. If the ace library is not loaded or takes time to load, these methods will fail with 'Ace editor library not loaded' error. While waitForReady() checks for DOM elements, it doesn't verify that the ace JavaScript library is actually loaded. Consider adding a check in waitForReady() to poll for window.ace availability before proceeding with editor operations.

Suggested change
.waitFor({ state: 'attached' });
.waitFor({ state: 'attached' });
// Additionally ensure the Ace library is loaded in the page before proceeding.
await this.page.waitForFunction(() => {
const win = window as unknown as {
ace?: { edit?: unknown };
};
return !!(win.ace && typeof win.ace.edit === 'function');
});

Copilot uses AI. Check for mistakes.
Comment on lines 58 to 72
function matchUrl(
url: string,
pattern: string | RegExp,
pathMatch?: boolean,
): boolean {
if (typeof pattern === 'string') {
if (pathMatch) {
const pathname = normalizePath(new URL(url).pathname);
const normalizedPattern = normalizePath(pattern);
return pathname.endsWith(normalizedPattern);
}
return url.includes(pattern);
}
return pattern.test(url);
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The matchUrl function uses new URL(url) to parse URLs when pathMatch is enabled (line 65). If the url parameter is not a valid URL (e.g., a relative path), this will throw an error. While this is likely intentional to catch malformed URLs, consider adding error handling or validation to provide a more helpful error message when an invalid URL is encountered.

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +118
// The dropdown trigger is in the same button group, find it relative to main button
const dropdownTrigger = mainButton
.locator('xpath=following-sibling::button')
.first();
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateDatasetPage.clickCreateDataset method uses an XPath expression 'xpath=following-sibling::button' to find the dropdown trigger button (line 117). XPath selectors are generally more fragile than modern Playwright locators and can break if the DOM structure changes. Consider using a more robust selector strategy, such as data-test attributes or getByRole with more specific criteria, to make this locator more maintainable.

Suggested change
// The dropdown trigger is in the same button group, find it relative to main button
const dropdownTrigger = mainButton
.locator('xpath=following-sibling::button')
.first();
// The dropdown trigger is in the same button group: go to the parent container
// and select the second button in that group (index 1).
const dropdownTrigger = mainButton
.locator('..')
.getByRole('button')
.nth(1);

Copilot uses AI. Check for mistakes.

const createResponse = await apiPostVirtualDataset(page, {
database: baseDataset!.database.id,
schema: baseDataset!.schema ?? '',
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema field in VirtualDatasetCreatePayload is defined as 'string | null', but in the apiPostVirtualDataset call on line 583, it uses 'baseDataset!.schema ?? '''. This means if schema is null, it will be converted to an empty string. However, the type definition allows null. Consider either: (1) passing null directly when schema is null to match the type signature, or (2) documenting why an empty string is preferred over null for the schema field.

Suggested change
schema: baseDataset!.schema ?? '',
schema: baseDataset!.schema ?? null,

Copilot uses AI. Check for mistakes.
@sadpandajoe sadpandajoe added the 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR label Jan 23, 2026
@bito-code-review
Copy link
Contributor

bito-code-review bot commented Jan 29, 2026

Code Review Agent Run #777dde

Actionable Suggestions - 0
Review Details
  • Files reviewed - 27 · Commit Range: 82b8f2e..b1b16ac
    • superset-frontend/playwright.config.ts
    • superset-frontend/playwright/components/ListView/BulkSelect.ts
    • superset-frontend/playwright/components/ListView/index.ts
    • superset-frontend/playwright/components/core/AceEditor.ts
    • superset-frontend/playwright/components/core/Checkbox.ts
    • superset-frontend/playwright/components/core/Select.ts
    • superset-frontend/playwright/components/core/Tabs.ts
    • superset-frontend/playwright/components/core/Textarea.ts
    • superset-frontend/playwright/components/core/index.ts
    • superset-frontend/playwright/components/modals/ConfirmDialog.ts
    • superset-frontend/playwright/components/modals/DuplicateDatasetModal.ts
    • superset-frontend/playwright/components/modals/EditDatasetModal.ts
    • superset-frontend/playwright/components/modals/ImportDatasetModal.ts
    • superset-frontend/playwright/components/modals/index.ts
    • superset-frontend/playwright/helpers/api/assertions.ts
    • superset-frontend/playwright/helpers/api/database.ts
    • superset-frontend/playwright/helpers/api/dataset.ts
    • superset-frontend/playwright/helpers/api/intercepts.ts
    • superset-frontend/playwright/helpers/fixtures/index.ts
    • superset-frontend/playwright/helpers/fixtures/testAssets.ts
    • superset-frontend/playwright/pages/ChartCreationPage.ts
    • superset-frontend/playwright/pages/CreateDatasetPage.ts
    • superset-frontend/playwright/pages/DatasetListPage.ts
    • superset-frontend/playwright/tests/experimental/dataset/create-dataset.spec.ts
    • superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts
    • superset-frontend/playwright/tests/experimental/dataset/dataset-test-helpers.ts
    • superset-frontend/playwright/utils/constants.ts
  • Files skipped - 2
    • superset-frontend/package-lock.json - Reason: Filter setting
    • superset-frontend/package.json - Reason: Filter setting
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

sadpandajoe and others added 27 commits February 2, 2026 13:18
Add test for exporting a single dataset via the action button.
The test verifies:
- Download is triggered when clicking export action
- Downloaded file is a zip file
- Download completes without failure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
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]>
Add E2E test for the create dataset wizard flow:
- Navigate to create dataset page via "+ Dataset" button
- Verify cascading select dropdowns (database → schema → table)
- Verify create and explore button availability

Add supporting components:
- Select component for Ant Design combobox interactions (with type-to-filter)
- CreateDatasetPage page object with component-based selectors

Refactor test file to initialize page objects only in tests that use them.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add E2E test for editing a dataset description:
- Click edit action to open DatasourceModal
- Update description field
- Save with confirmation dialog
- Verify toast and persisted change

Add supporting components:
- EditDatasetModal using Modal base class and component abstractions
- ConfirmDialog for reusable OK/Cancel confirmation dialogs
- Textarea component for multi-line text input
- Tabs component for tab navigation
- clickEditAction method in DatasetListPage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Address Copilot review comments:
- Fix EditDatasetModal passing wrong modalSelector parameter to Modal
- Add constructor overloads to Textarea and Select for consistency with Button/Input
- Add Page parameter to Checkbox for consistency with other components
- Clarify ConfirmDialog constructor comment
- Refactor edit test to use throwaway dataset for test isolation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The create wizard test was failing with strict mode violation because
getByRole('button', { name: 'Dataset' }) matched both the "Datasets"
nav link and the "+ Dataset" button. Added clickAddDataset() method
to DatasetListPage with a regex pattern that specifically targets the
"+ Dataset" button.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
1. Select.clickOption: Changed from getByTitle() to Ant Design's
   .ant-select-item-option class selector since dropdown options
   don't have title attributes.

2. Export tests: Changed from waitForEvent('download') to API
   response interception. The export uses blob download via fetch
   (downloadBlob with createObjectURL), which doesn't trigger
   Playwright's download event consistently. Now verifies the
   export API response headers (content-type and content-disposition).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
1. Renamed "bulk export multiple datasets" to "export dataset via bulk
   select action" - more accurately describes test scope (tests bulk
   select mechanism, not multi-item export).

2. Added virtual dataset guard in first test - fails fast with clear
   message if example dataset is missing or not virtual, which is
   required for duplicate functionality used in other tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add SELECT_SELECTORS constants matching Cypress patterns
- Use .getByText() with exact match for option selection
- Wait for dropdown visibility before finding options
- Remove invalid dataset_type guard (field not in list API response)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add acceptDownloads: true to Playwright config for export tests
- Create AceEditor component for Ace Editor interactions
- Fix Select.clickOption to handle multiple dropdowns using :not(.ant-select-dropdown-hidden)
- Update CreateDatasetPage to use data-test selectors instead of fromRole
- Update EditDatasetModal to use AceEditor instead of Textarea for description
- Update test to use getDescription() for AceEditor value verification

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Select: Add waitForDropdownClose after selection to avoid stale dropdowns
- Select: Support partial text matching for filtered dropdown options
- Select: Fix type() to target actual search input inside component
- ConfirmDialog: Use specific dialog name to avoid strict mode violations
- ConfirmDialog: Add animation wait before clicking OK button
- EditDatasetModal: Use specific dialog name selector for reliability
- DuplicateDatasetModal: Triple-click to select existing text before fill
- DatasetListPage: Use role-based selectors for action buttons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add duplicateDataset() helper for API-based dataset duplication
- Add unzipper dependency for export file verification in tests
- Restore test.describe wrapper in dataset-list tests
- Simplify test resource tracking and cleanup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Change sql/schema types from `string?` to `string | null` in DatasetResult
  (API can return null for these fields)
- Flatten test structure: remove test.describe wrapper per project convention
  (matches "avoid nesting when testing" guidance)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Normalize duplicateDataset() return: handle id at top level or in result
- Accept both 200/201 status in response intercept (varies by environment)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add guard in duplicateDataset() to throw if resolvedId is falsy
- Handle both response shapes in test (result.id vs top-level id)
- Assert duplicateId is truthy before using for cleanup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…efix support

- Change duplicateDataset() return type from DatasetResult to { id: number }
  to accurately reflect API response (may only return id without full details)
- Use download.saveAs(tempPath) instead of download.path() which can return
  null in certain Playwright runners
- Wrap saveAs + unzip in try/finally for reliable temp directory cleanup
- Fix webServer health check to respect PLAYWRIGHT_BASE_URL for app prefix

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Replace page.waitForEvent('download') with API response interception
for the export dataset test. Superset's export uses blob downloads
(createObjectURL + anchor click) which don't trigger Playwright's
download event consistently in app-prefix configurations.

The new approach:
- Intercepts /api/v1/dataset/export/ response directly
- Verifies content-type and content-disposition headers
- Parses zip contents from response body using unzipper.Open.buffer()
- Eliminates temp file handling (simpler, faster)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Refactor EditDatasetModal to use data-test selectors and Input component
- Add SELECTORS for inline-name, lock, and unlock icons
- Replace AceEditor description methods with name input methods
- Add enableEditMode() to click lock icon before editing
- Update edit test to modify dataset name instead of description
- Add ConfirmDialog handling for save confirmation
- Simplify dataset wizard test to verify UI states without creating

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add E2E test for dataset creation via wizard using Google Sheets database
- Create ChartCreationPage page object for the "Create a new chart" wizard
- Add database API helpers (apiPostDatabase, apiDeleteDatabase, getDatabaseByName)
- Use public Netflix dataset Google Sheet (no auth required)
- Test flow: Dataset List → Create Dataset → Chart Creation → Explore

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…dation

- Fix Select.type() to handle both container and input locators by waiting
  for nested search input with timeout, falling back to direct fill
- Extract expectValidExportZip() helper to reduce duplication in export tests
- Add Response type to Playwright imports

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Use sibling relationship to main button instead of generic class selector
for more reliable dropdown trigger identification.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…ucture

- Fix command injection risk: quote URL in curl health check
- Fix health check URL: use origin instead of preserving app prefix path
- Select.ts: only catch TimeoutError in waitForDropdownClose() and type()
- dataset.ts: handle both response shapes in createTestVirtualDataset()
- dataset-list.spec.ts: add explicit timeout to expect.poll()
- dataset-list.spec.ts: use null instead of empty string for schema

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Create BulkSelect component for ListView bulk operations
- Encapsulates enable(), selectRow(), clickAction() pattern
- Update DatasetListPage to use BulkSelect via composition
- Reusable for future ChartListPage, DashboardListPage, etc.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Move BulkSelect from core/ to ListView/ (not an Ant Design wrapper)
- Create ListView/index.ts for ListView-specific components
- Update imports in DatasetListPage

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Consolidate clickOk() and clickOkIfVisible() into single method with
  optional timeout parameter (no timeout = strict, with timeout = conditional)
- Look up 'examples' database by name instead of hardcoding databaseId=1
- Add API 404 verification to single-delete test (matches bulk-delete)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add TIMEOUT.CONFIRM_DIALOG (2s) for confirmation dialogs
- Add TIMEOUT.FILE_IMPORT (30s) for import operations
- Replace hardcoded timeout values in dataset-list.spec.ts

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@sadpandajoe sadpandajoe force-pushed the feat-additional-dataset-playwright-tests branch from b1b16ac to d11046e Compare February 2, 2026 23:22
Copy link
Contributor

@bito-code-review bito-code-review bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #819970

Actionable Suggestions - 3
  • superset-frontend/playwright/helpers/api/dataset.ts - 1
  • superset-frontend/playwright.config.ts - 1
  • superset-frontend/playwright/components/core/Textarea.ts - 1
Additional Suggestions - 3
  • superset-frontend/playwright/pages/CreateDatasetPage.ts - 2
    • Property visibility inconsistency · Line 27-27
      The page property should be private to match the pattern in other page objects like AuthPage, improving encapsulation.
      Code suggestion
       @@ -26,1 +26,1 @@
      -  readonly page: Page;
      +  private readonly page: Page;
    • Hardcoded URL instead of constant · Line 78-78
      Consider adding a URL constant for 'dataset/add/' to match the pattern in other page objects like AuthPage, which improves maintainability.
  • superset-frontend/playwright/components/core/Checkbox.ts - 1
    • Constructor pattern mismatch · Line 39-45
      The constructor should be overloaded to accept either a string selector or Locator, like other core components (Button, Input), and the unused page field removed for consistency.
Review Details
  • Files reviewed - 19 · Commit Range: 0c95457..d11046e
    • superset-frontend/playwright.config.ts
    • superset-frontend/playwright/components/ListView/BulkSelect.ts
    • superset-frontend/playwright/components/ListView/index.ts
    • superset-frontend/playwright/components/core/AceEditor.ts
    • superset-frontend/playwright/components/core/Checkbox.ts
    • superset-frontend/playwright/components/core/Select.ts
    • superset-frontend/playwright/components/core/Tabs.ts
    • superset-frontend/playwright/components/core/Textarea.ts
    • superset-frontend/playwright/components/core/index.ts
    • superset-frontend/playwright/components/modals/ConfirmDialog.ts
    • superset-frontend/playwright/components/modals/DuplicateDatasetModal.ts
    • superset-frontend/playwright/components/modals/EditDatasetModal.ts
    • superset-frontend/playwright/helpers/api/database.ts
    • superset-frontend/playwright/helpers/api/dataset.ts
    • superset-frontend/playwright/pages/ChartCreationPage.ts
    • superset-frontend/playwright/pages/CreateDatasetPage.ts
    • superset-frontend/playwright/pages/DatasetListPage.ts
    • superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts
    • superset-frontend/playwright/utils/constants.ts
  • Files skipped - 2
    • superset-frontend/package-lock.json - Reason: Filter setting
    • superset-frontend/package.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +218 to +219
});
const body = await response.json();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing API response validation

The duplicateDataset function lacks a check for response.ok() before parsing JSON, which could cause exceptions if the API fails. Other functions in this file, like createTestVirtualDataset, include this check for consistency.

Code suggestion
Check the AI-generated fix before applying
Suggested change
});
const body = await response.json();
});
if (!response.ok()) {
throw new Error(`Duplicate dataset API failed: ${response.status()} ${response.statusText()}`);
}
const body = await response.json();

Code Review Run #819970


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Comment on lines +130 to +134
return {
// Quote URL to prevent shell injection via PLAYWRIGHT_BASE_URL
command: `curl -f '${healthUrl}'`,
url: healthUrl,
reuseExistingServer: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shell injection risk in webServer command

The curl command uses single quotes around the healthUrl, but if PLAYWRIGHT_BASE_URL contains single quotes, it could lead to shell injection. Switching to double quotes prevents this since URLs typically don't contain double quotes.

Code suggestion
Check the AI-generated fix before applying
Suggested change
return {
// Quote URL to prevent shell injection via PLAYWRIGHT_BASE_URL
command: `curl -f '${healthUrl}'`,
url: healthUrl,
reuseExistingServer: true,
return {
// Quote URL to prevent shell injection via PLAYWRIGHT_BASE_URL
command: `curl -f "${healthUrl}"`,
url: healthUrl,
reuseExistingServer: true,

Code Review Run #819970


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

* @param name - The name attribute value
*/
static fromName(page: Page, name: string): Textarea {
const locator = page.locator(`textarea[name="${name}"]`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selector Escaping Issue

The selector construction in fromName could fail if the name parameter includes double quotes, as it directly interpolates without escaping. This might cause locator errors in tests with such names.

Code suggestion
Check the AI-generated fix before applying
 -    const locator = page.locator(`textarea[name=\"${name}\"]`);
 +    const locator = page.locator(`textarea[name=${JSON.stringify(name)}]`);

Code Review Run #819970


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies:npm listview Namespace | Anything related to lists, such as Dashboards, Charts, Datasets, etc. size/XXL 🎪 ⌛ 48h Environment expires after 48 hours (default)

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

3 participants