This is an automated email from the ASF dual-hosted git repository.

jli pushed a commit to branch feat/dataset-playwright-tests
in repository https://gitbox.apache.org/repos/asf/superset.git

commit fa114e8693b3438bd985df7e3064c1505c0ea9cd
Author: Joe Li <[email protected]>
AuthorDate: Wed Nov 12 13:13:02 2025 -0800

    refactor(playwright): harden dataset E2E tests
    
    - Remove hardcoded birth_names dependency (create test data via API)
    - Fix JSON template injection in createGsheetsDatabase (use JSON.stringify)
    - Add TypeScript interfaces for API payloads (DatabaseCreatePayload, 
DatasetCreatePayload)
    
    Tests remain in experimental/ directory until proven stable. Keeps 
test.describe
    structure per Playwright best practices.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <[email protected]>
---
 .../playwright/helpers/api/database.factories.ts   |  9 ++++++-
 .../playwright/helpers/api/database.ts             | 31 ++++++++++++++++++++--
 .../playwright/helpers/api/dataset.ts              | 13 ++++++++-
 .../experimental/dataset/dataset-list.spec.ts      | 23 ++++++++++------
 4 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/superset-frontend/playwright/helpers/api/database.factories.ts 
b/superset-frontend/playwright/helpers/api/database.factories.ts
index 7e1ede2855..6fa5c764e0 100644
--- a/superset-frontend/playwright/helpers/api/database.factories.ts
+++ b/superset-frontend/playwright/helpers/api/database.factories.ts
@@ -49,7 +49,14 @@ export async function createGsheetsDatabase(
     },
     driver: 'apsw',
     sqlalchemy_uri_placeholder: 'gsheets://',
-    extra: 
`{"allows_virtual_table_explore":true,"engine_params":{"catalog":{"${tableName}":"${NETFLIX_TITLES_SHEET}"}}}`,
+    extra: JSON.stringify({
+      allows_virtual_table_explore: true,
+      engine_params: {
+        catalog: {
+          [tableName]: NETFLIX_TITLES_SHEET,
+        },
+      },
+    }),
     expose_in_sqllab: true,
     catalog: [
       {
diff --git a/superset-frontend/playwright/helpers/api/database.ts 
b/superset-frontend/playwright/helpers/api/database.ts
index e3fac9e5fc..31955393ca 100644
--- a/superset-frontend/playwright/helpers/api/database.ts
+++ b/superset-frontend/playwright/helpers/api/database.ts
@@ -24,15 +24,42 @@ const ENDPOINTS = {
   DATABASE: 'api/v1/database/',
 } as const;
 
+/**
+ * TypeScript interface for database creation API payload
+ * Provides compile-time safety for required fields
+ */
+export interface DatabaseCreatePayload {
+  database_name: string;
+  engine: string;
+  configuration_method?: string;
+  engine_information?: {
+    disable_ssh_tunneling?: boolean;
+    supports_dynamic_catalog?: boolean;
+    supports_file_upload?: boolean;
+    supports_oauth2?: boolean;
+  };
+  driver?: string;
+  sqlalchemy_uri_placeholder?: string;
+  extra?: string;
+  expose_in_sqllab?: boolean;
+  catalog?: Array<{ name: string; value: string }>;
+  parameters?: {
+    service_account_info?: string;
+    catalog?: Record<string, string>;
+  };
+  masked_encrypted_extra?: string;
+  impersonate_user?: boolean;
+}
+
 /**
  * POST request to create a database connection
  * @param page - Playwright page instance (provides authentication context)
- * @param requestBody - Database configuration object
+ * @param requestBody - Database configuration object with type safety
  * @returns API response from database creation
  */
 export async function apiPostDatabase(
   page: Page,
-  requestBody: object,
+  requestBody: DatabaseCreatePayload,
 ): Promise<APIResponse> {
   return apiPost(page, ENDPOINTS.DATABASE, requestBody);
 }
diff --git a/superset-frontend/playwright/helpers/api/dataset.ts 
b/superset-frontend/playwright/helpers/api/dataset.ts
index b126781311..46166ffcdc 100644
--- a/superset-frontend/playwright/helpers/api/dataset.ts
+++ b/superset-frontend/playwright/helpers/api/dataset.ts
@@ -24,6 +24,17 @@ const ENDPOINTS = {
   DATASET: 'api/v1/dataset/',
 } as const;
 
+/**
+ * TypeScript interface for dataset creation API payload
+ * Provides compile-time safety for required fields
+ */
+export interface DatasetCreatePayload {
+  database: number;
+  catalog: string | null;
+  schema: string;
+  table_name: string;
+}
+
 /**
  * POST request to create a dataset
  * @param page - Playwright page instance (provides authentication context)
@@ -32,7 +43,7 @@ const ENDPOINTS = {
  */
 export async function apiPostDataset(
   page: Page,
-  requestBody: object,
+  requestBody: DatasetCreatePayload,
 ): Promise<APIResponse> {
   return apiPost(page, ENDPOINTS.DATASET, requestBody);
 }
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 11783bf0dc..a95c4e5b8a 100644
--- 
a/superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts
+++ 
b/superset-frontend/playwright/tests/experimental/dataset/dataset-list.spec.ts
@@ -71,22 +71,29 @@ test.describe('Dataset List', () => {
     return Promise.all(promises);
   }
 
-  test('should navigate to Explore when dataset name is clicked', async () => {
-    // Test uses existing fixture dataset 'birth_names'
-    // (available in Superset examples database)
+  test('should navigate to Explore when dataset name is clicked', async ({
+    page,
+  }) => {
+    // Create test dataset (hermetic - no dependency on sample data)
+    const datasetName = `test_nav_${Date.now()}`;
+    testResources = await createTestDataset(page, datasetName);
+
+    // Refresh page to see new dataset
+    await datasetListPage.goto();
+    await datasetListPage.waitForTableLoad();
 
     // Verify dataset is visible in list (uses page object + Playwright 
auto-wait)
-    await expect(datasetListPage.getDatasetRow('birth_names')).toBeVisible();
+    await expect(datasetListPage.getDatasetRow(datasetName)).toBeVisible();
 
     // Click on dataset name to navigate to Explore
-    await datasetListPage.clickDatasetName('birth_names');
+    await datasetListPage.clickDatasetName(datasetName);
 
     // Wait for Explore page to load (validates URL + datasource control)
     await explorePage.waitForPageLoad();
 
     // Verify correct dataset is loaded in datasource control
-    const datasetName = await explorePage.getDatasetName();
-    expect(datasetName).toContain('birth_names');
+    const loadedDatasetName = await explorePage.getDatasetName();
+    expect(loadedDatasetName).toContain(datasetName);
 
     // Verify visualization switcher shows default viz type (indicates full 
page load)
     await expect(explorePage.getVizSwitcher()).toBeVisible();
@@ -94,7 +101,7 @@ test.describe('Dataset List', () => {
   });
 
   test('should delete a dataset with confirmation', async ({ page }) => {
-    // Create test dataset
+    // Create test dataset (hermetic - creates own test data)
     const datasetName = `test_delete_${Date.now()}`;
     testResources = await createTestDataset(page, datasetName);
 

Reply via email to