korbit-ai[bot] commented on code in PR #35110:
URL: https://github.com/apache/superset/pull/35110#discussion_r2345334965


##########
superset-frontend/playwright.config.ts:
##########
@@ -0,0 +1,90 @@
+/**
+ * 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.
+ */
+
+/// <reference types="node" />
+
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { defineConfig } from '@playwright/test';
+
+export default defineConfig({
+  // Test directory
+  testDir: './playwright/tests',
+
+  // Timeout settings
+  timeout: 30000,
+  expect: { timeout: 8000 },
+
+  // Parallel execution
+  fullyParallel: true,
+  workers: process.env.CI ? 2 : 1,
+
+  // Retry logic - 2 retries in CI, 0 locally
+  retries: process.env.CI ? 2 : 0,
+
+  // Reporter configuration - multiple reporters for better visibility
+  reporter: process.env.CI
+    ? [
+        ['github'], // GitHub Actions annotations
+        ['list'], // Detailed output with summary table
+        ['html', { outputFolder: 'playwright-report', open: 'never' }], // 
Interactive report
+        ['json', { outputFile: 'test-results/results.json' }], // 
Machine-readable
+      ]
+    : [
+        ['list'], // Shows summary table locally
+        ['html', { outputFolder: 'playwright-report', open: 'on-failure' }], 
// Auto-open on failure
+      ],
+
+  // Global test setup
+  use: {
+    // Use environment variable for base URL in CI, default to localhost:8088 
for local
+    baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8088',
+
+    // Browser settings
+    headless: !!process.env.CI,
+
+    viewport: { width: 1280, height: 1024 },
+
+    // Screenshots and videos on failure
+    screenshot: 'only-on-failure',
+    video: 'retain-on-failure',
+
+    // Trace collection for debugging
+    trace: 'retain-on-failure',
+  },
+
+  projects: [
+    {
+      name: 'chromium',
+      use: {
+        browserName: 'chromium',
+        testIdAttribute: 'data-test',
+      },
+    },
+  ],
+
+  // Web server setup - disabled in CI (Flask started separately in workflow)
+  webServer: process.env.CI
+    ? undefined
+    : {
+        command: 'curl -f http://localhost:8088/health',

Review Comment:
   ### Incorrect webServer Command <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The webServer command only checks if the server is reachable but doesn't 
actually start the development server.
   
   
   ###### Why this matters
   Tests will fail locally as there's no server running to test against. The 
health check alone doesn't initialize the required test environment.
   
   ###### Suggested change ∙ *Feature Preview*
   Replace the command with the actual command to start the development server:
   ```typescript
   command: 'npm run dev',
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/00c80044-840b-4c4a-b4e0-5c4ab5d159f3/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/00c80044-840b-4c4a-b4e0-5c4ab5d159f3?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/00c80044-840b-4c4a-b4e0-5c4ab5d159f3?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/00c80044-840b-4c4a-b4e0-5c4ab5d159f3?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/00c80044-840b-4c4a-b4e0-5c4ab5d159f3)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:785e2bed-1034-493c-9cfa-76e6921c6614 -->
   
   
   [](785e2bed-1034-493c-9cfa-76e6921c6614)



##########
superset-frontend/playwright/pages/AuthPage.ts:
##########
@@ -0,0 +1,122 @@
+/**
+ * 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 { Page, Response } from '@playwright/test';
+import { Form } from '../components/core';
+import { URL } from '../utils/urls';
+
+export class AuthPage {
+  private readonly page: Page;
+
+  private readonly loginForm: Form;
+
+  // Selectors specific to the auth/login page
+  private static readonly SELECTORS = {
+    LOGIN_FORM: '[data-test="login-form"]',
+    USERNAME_INPUT: '[data-test="username-input"]',
+    PASSWORD_INPUT: '[data-test="password-input"]',
+    LOGIN_BUTTON: '[data-test="login-button"]',
+    ERROR_SELECTORS: [
+      '[role="alert"]',
+      '.ant-form-item-explain-error',
+      '.ant-form-item-explain.ant-form-item-explain-error',
+      '.alert-danger',
+    ],
+  } as const;
+
+  constructor(page: Page) {
+    this.page = page;
+    this.loginForm = new Form(page, AuthPage.SELECTORS.LOGIN_FORM);
+  }
+
+  /**
+   * Navigate to the login page
+   */
+  async goto(): Promise<void> {
+    await this.page.goto(URL.LOGIN);
+  }
+
+  /**
+   * Wait for login form to be visible
+   */
+  async waitForLoginForm(): Promise<void> {
+    await this.loginForm.waitForVisible({ timeout: 5000 });
+  }
+
+  /**
+   * Login with provided credentials
+   * @param username - Username to enter
+   * @param password - Password to enter
+   */
+  async loginWithCredentials(
+    username: string,
+    password: string,
+  ): Promise<void> {
+    const usernameInput = this.loginForm.getInput(
+      AuthPage.SELECTORS.USERNAME_INPUT,
+    );
+    const passwordInput = this.loginForm.getInput(
+      AuthPage.SELECTORS.PASSWORD_INPUT,
+    );
+    const loginButton = this.loginForm.getButton(
+      AuthPage.SELECTORS.LOGIN_BUTTON,
+    );
+
+    await usernameInput.fill(username);
+    await passwordInput.fill(password);
+    await loginButton.click();
+  }
+
+  /**
+   * Get current page URL
+   */
+  async getCurrentUrl(): Promise<string> {
+    return this.page.url();
+  }
+
+  /**
+   * Get the session cookie specifically
+   */
+  async getSessionCookie(): Promise<{ name: string; value: string } | null> {
+    const cookies = await this.page.context().cookies();
+    return cookies.find((c: any) => c.name === 'session') || null;

Review Comment:
   ### Unsafe Cookie Type Checking <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The cookie type is explicitly set to 'any', which bypasses type checking for 
cookie properties and could lead to runtime errors.
   
   
   ###### Why this matters
   If the cookie structure changes in future Playwright versions, the code 
won't catch potential incompatibilities at compile time, potentially causing 
test failures.
   
   ###### Suggested change ∙ *Feature Preview*
   Use the proper Playwright cookie type:
   ```typescript
   import { Cookie } from '@playwright/test';
   
   async getSessionCookie(): Promise<Cookie | null> {
       const cookies = await this.page.context().cookies();
       return cookies.find((c: Cookie) => c.name === 'session') || null;
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fece8598-341d-40f5-8af7-09489944767d/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fece8598-341d-40f5-8af7-09489944767d?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fece8598-341d-40f5-8af7-09489944767d?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fece8598-341d-40f5-8af7-09489944767d?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/fece8598-341d-40f5-8af7-09489944767d)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:e1c1387b-8143-4fdb-923f-8ddcd4b5bd79 -->
   
   
   [](e1c1387b-8143-4fdb-923f-8ddcd4b5bd79)



##########
superset-frontend/playwright/pages/AuthPage.ts:
##########
@@ -0,0 +1,122 @@
+/**
+ * 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 { Page, Response } from '@playwright/test';
+import { Form } from '../components/core';
+import { URL } from '../utils/urls';
+
+export class AuthPage {
+  private readonly page: Page;
+
+  private readonly loginForm: Form;
+
+  // Selectors specific to the auth/login page
+  private static readonly SELECTORS = {
+    LOGIN_FORM: '[data-test="login-form"]',
+    USERNAME_INPUT: '[data-test="username-input"]',
+    PASSWORD_INPUT: '[data-test="password-input"]',
+    LOGIN_BUTTON: '[data-test="login-button"]',
+    ERROR_SELECTORS: [
+      '[role="alert"]',
+      '.ant-form-item-explain-error',
+      '.ant-form-item-explain.ant-form-item-explain-error',
+      '.alert-danger',
+    ],
+  } as const;
+
+  constructor(page: Page) {
+    this.page = page;
+    this.loginForm = new Form(page, AuthPage.SELECTORS.LOGIN_FORM);
+  }
+
+  /**
+   * Navigate to the login page
+   */
+  async goto(): Promise<void> {
+    await this.page.goto(URL.LOGIN);
+  }
+
+  /**
+   * Wait for login form to be visible
+   */
+  async waitForLoginForm(): Promise<void> {
+    await this.loginForm.waitForVisible({ timeout: 5000 });
+  }

Review Comment:
   ### Insufficient Login Form Timeout <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The hardcoded timeout of 5000ms for the login form visibility check may be 
insufficient in slower environments or under heavy load.
   
   
   ###### Why this matters
   Under slow network conditions or system load, the login form might take 
longer than 5 seconds to appear, causing false test failures.
   
   ###### Suggested change ∙ *Feature Preview*
   Make the timeout configurable with a default value that's more accommodating:
   ```typescript
   async waitForLoginForm(timeout = 30000): Promise<void> {
       await this.loginForm.waitForVisible({ timeout });
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ba830c92-153c-4269-b16e-4535563e8520/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ba830c92-153c-4269-b16e-4535563e8520?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ba830c92-153c-4269-b16e-4535563e8520?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ba830c92-153c-4269-b16e-4535563e8520?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/ba830c92-153c-4269-b16e-4535563e8520)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:07d4a8fa-2bfc-4b7b-ad61-dff1a4585060 -->
   
   
   [](07d4a8fa-2bfc-4b7b-ad61-dff1a4585060)



##########
superset-frontend/playwright/components/core/Form.ts:
##########
@@ -0,0 +1,110 @@
+/**
+ * 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, Page } from '@playwright/test';
+import { Input } from './Input';
+import { Button } from './Button';
+
+export class Form {
+  private readonly page: Page;
+
+  private readonly locator: Locator;
+
+  constructor(page: Page, selector: string);
+
+  constructor(page: Page, locator: Locator);
+
+  constructor(page: Page, selectorOrLocator: string | Locator) {
+    this.page = page;
+    if (typeof selectorOrLocator === 'string') {
+      this.locator = page.locator(selectorOrLocator);
+    } else {
+      this.locator = selectorOrLocator;
+    }
+  }
+
+  /**
+   * Gets the form element locator
+   */
+  get element(): Locator {
+    return this.locator;
+  }
+
+  /**
+   * Gets an input field within the form (properly scoped)
+   * @param inputSelector - Selector for the input field
+   */
+  getInput(inputSelector: string): Input {
+    const scopedLocator = this.locator.locator(inputSelector);
+    return new Input(this.page, scopedLocator);
+  }
+
+  /**
+   * Gets a button within the form (properly scoped)
+   * @param buttonSelector - Selector for the button
+   */
+  getButton(buttonSelector: string): Button {
+    const scopedLocator = this.locator.locator(buttonSelector);
+    return new Button(this.page, scopedLocator);
+  }
+
+  /**
+   * Checks if the form is visible
+   */
+  async isVisible(): Promise<boolean> {
+    return this.locator.isVisible();
+  }
+
+  /**
+   * Submits the form (triggers submit event)
+   */
+  async submit(): Promise<void> {
+    await this.locator.evaluate((form: HTMLElement) => {
+      if (form instanceof HTMLFormElement) {
+        form.submit();
+      }
+    });

Review Comment:
   ### Form Submit Method Bypasses Validation <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The form.submit() method bypasses form validation and doesn't trigger submit 
event handlers, which could lead to missed test scenarios that depend on form 
validation or submit event handlers.
   
   
   ###### Why this matters
   When testing forms in a real application, bypassing form validation and 
event handlers means the tests won't catch validation errors or execute crucial 
submit event logic that would occur in actual user interactions.
   
   ###### Suggested change ∙ *Feature Preview*
   Replace the form.submit() with a more realistic form submission approach 
that triggers validation and events:
   ```typescript
   async submit(): Promise<void> {
     await this.locator.evaluate((form: HTMLElement) => {
       if (form instanceof HTMLFormElement) {
         const submitEvent = new Event('submit', { bubbles: true, cancelable: 
true });
         const eventResult = form.dispatchEvent(submitEvent);
         if (eventResult) {
           form.submit();
         }
       }
     });
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d1a2696e-76af-4bb1-a063-966b88e9d800/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d1a2696e-76af-4bb1-a063-966b88e9d800?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d1a2696e-76af-4bb1-a063-966b88e9d800?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d1a2696e-76af-4bb1-a063-966b88e9d800?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/d1a2696e-76af-4bb1-a063-966b88e9d800)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:a42f812e-5fcf-4d0d-9d52-b1d3ba68e5ed -->
   
   
   [](a42f812e-5fcf-4d0d-9d52-b1d3ba68e5ed)



##########
.github/workflows/bashlib.sh:
##########
@@ -182,6 +182,76 @@
   kill $flaskProcessId
 }
 
+playwright-install() {
+  cd "$GITHUB_WORKSPACE/superset-frontend"
+
+  say "::group::Install Playwright browsers"
+  npx playwright install --with-deps chromium
+  # Create output directories for test results and debugging
+  mkdir -p playwright-results
+  mkdir -p test-results
+  say "::endgroup::"
+}
+
+playwright-run() {
+  local APP_ROOT=$1
+
+  # Start Flask from the project root (same as Cypress)
+  cd "$GITHUB_WORKSPACE"
+  local flasklog="${HOME}/flask-playwright.log"
+  local port=8081
+  PLAYWRIGHT_BASE_URL="http://localhost:${port}";
+  if [ -n "$APP_ROOT" ]; then
+    export SUPERSET_APP_ROOT=$APP_ROOT
+    PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL}${APP_ROOT}/
+  fi
+  export PLAYWRIGHT_BASE_URL
+
+  nohup flask run --no-debugger -p $port >"$flasklog" 2>&1 </dev/null &
+  local flaskProcessId=$!
+
+  # Ensure cleanup on exit
+  trap "kill $flaskProcessId 2>/dev/null || true" EXIT

Review Comment:
   ### Late Process Cleanup Registration <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The Flask server cleanup trap is set after the server process is started, 
creating a small window where the process might not be cleaned up if the script 
fails between these lines.
   
   
   ###### Why this matters
   If a failure occurs between starting the Flask server and setting the trap, 
the Flask process could remain running, causing port conflicts in subsequent 
test runs.
   
   ###### Suggested change ∙ *Feature Preview*
   Move the trap before starting the Flask server:
   ```bash
   trap "kill $flaskProcessId 2>/dev/null || true" EXIT
   nohup flask run --no-debugger -p $port >"$flasklog" 2>&1 </dev/null &
   local flaskProcessId=$!
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/6f24d658-e62b-47fe-80fc-94df66b95961/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/6f24d658-e62b-47fe-80fc-94df66b95961?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/6f24d658-e62b-47fe-80fc-94df66b95961?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/6f24d658-e62b-47fe-80fc-94df66b95961?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/6f24d658-e62b-47fe-80fc-94df66b95961)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:bbab22da-a699-4c4f-9474-f1ffbd35ac9d -->
   
   
   [](bbab22da-a699-4c4f-9474-f1ffbd35ac9d)



##########
.github/workflows/bashlib.sh:
##########
@@ -182,6 +182,76 @@ cypress-run-all() {
   kill $flaskProcessId
 }
 
+playwright-install() {
+  cd "$GITHUB_WORKSPACE/superset-frontend"
+
+  say "::group::Install Playwright browsers"
+  npx playwright install --with-deps chromium
+  # Create output directories for test results and debugging
+  mkdir -p playwright-results
+  mkdir -p test-results
+  say "::endgroup::"
+}
+
+playwright-run() {
+  local APP_ROOT=$1
+
+  # Start Flask from the project root (same as Cypress)
+  cd "$GITHUB_WORKSPACE"
+  local flasklog="${HOME}/flask-playwright.log"
+  local port=8081
+  PLAYWRIGHT_BASE_URL="http://localhost:${port}";
+  if [ -n "$APP_ROOT" ]; then
+    export SUPERSET_APP_ROOT=$APP_ROOT
+    PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL}${APP_ROOT}/
+  fi
+  export PLAYWRIGHT_BASE_URL
+
+  nohup flask run --no-debugger -p $port >"$flasklog" 2>&1 </dev/null &
+  local flaskProcessId=$!
+
+  # Ensure cleanup on exit
+  trap "kill $flaskProcessId 2>/dev/null || true" EXIT
+
+  # Wait for server to be ready with health check
+  local timeout=60
+  say "Waiting for Flask server to start on port $port..."
+  while [ $timeout -gt 0 ]; do
+    if curl -f ${PLAYWRIGHT_BASE_URL}/health >/dev/null 2>&1; then
+      say "Flask server is ready"
+      break
+    fi
+    sleep 1
+    timeout=$((timeout - 1))
+  done

Review Comment:
   ### Inefficient Health Check Polling <sub>![category 
Performance](https://img.shields.io/badge/Performance-4f46e5)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The health check polling uses a fixed 1-second sleep interval, which can 
lead to unnecessary waits.
   
   
   ###### Why this matters
   In scenarios where the server starts quickly, the code still waits a full 
second before the next check, introducing latency into the test startup.
   
   ###### Suggested change ∙ *Feature Preview*
   Implement an exponential backoff strategy starting with a shorter interval 
(e.g., 0.1s) that increases up to 1s. Example:
   ```bash
   interval=0.1
   while [ $timeout -gt 0 ]; do
     if curl -f ${PLAYWRIGHT_BASE_URL}/health >/dev/null 2>&1; then
       say "Flask server is ready"
       break
     fi
     sleep $interval
     interval=$(echo "if($interval < 1.0) $interval * 2 else 1.0" | bc)
     timeout=$((timeout - 1))
   done
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/c34cae11-6542-4bd8-a4f1-797da893cf36/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/c34cae11-6542-4bd8-a4f1-797da893cf36?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/c34cae11-6542-4bd8-a4f1-797da893cf36?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/c34cae11-6542-4bd8-a4f1-797da893cf36?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/c34cae11-6542-4bd8-a4f1-797da893cf36)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:24daea13-4ced-4d03-a74f-a6eeeb3d456e -->
   
   
   [](24daea13-4ced-4d03-a74f-a6eeeb3d456e)



##########
.github/workflows/bashlib.sh:
##########
@@ -182,6 +182,76 @@
   kill $flaskProcessId
 }
 
+playwright-install() {
+  cd "$GITHUB_WORKSPACE/superset-frontend"
+
+  say "::group::Install Playwright browsers"
+  npx playwright install --with-deps chromium
+  # Create output directories for test results and debugging
+  mkdir -p playwright-results
+  mkdir -p test-results
+  say "::endgroup::"
+}
+
+playwright-run() {
+  local APP_ROOT=$1
+
+  # Start Flask from the project root (same as Cypress)
+  cd "$GITHUB_WORKSPACE"
+  local flasklog="${HOME}/flask-playwright.log"
+  local port=8081
+  PLAYWRIGHT_BASE_URL="http://localhost:${port}";
+  if [ -n "$APP_ROOT" ]; then
+    export SUPERSET_APP_ROOT=$APP_ROOT
+    PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL}${APP_ROOT}/
+  fi
+  export PLAYWRIGHT_BASE_URL
+
+  nohup flask run --no-debugger -p $port >"$flasklog" 2>&1 </dev/null &
+  local flaskProcessId=$!
+
+  # Ensure cleanup on exit
+  trap "kill $flaskProcessId 2>/dev/null || true" EXIT
+
+  # Wait for server to be ready with health check
+  local timeout=60
+  say "Waiting for Flask server to start on port $port..."
+  while [ $timeout -gt 0 ]; do
+    if curl -f ${PLAYWRIGHT_BASE_URL}/health >/dev/null 2>&1; then
+      say "Flask server is ready"
+      break
+    fi
+    sleep 1
+    timeout=$((timeout - 1))
+  done
+
+  if [ $timeout -eq 0 ]; then
+    echo "::error::Flask server failed to start within 60 seconds"
+    echo "::group::Flask startup log"
+    cat "$flasklog"
+    echo "::endgroup::"
+    return 1
+  fi
+
+  # Change to frontend directory for Playwright execution
+  cd "$GITHUB_WORKSPACE/superset-frontend"
+
+  say "::group::Run Playwright tests"
+  echo "Running Playwright with baseURL: ${PLAYWRIGHT_BASE_URL}"
+  npx playwright test auth/login --reporter=github --output=playwright-results

Review Comment:
   ### Hardcoded Test Suite Path <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The Playwright test command is hardcoded to only run the 'auth/login' test 
suite, which doesn't align with the developer's intent to migrate existing 
Cypress tests.
   
   
   ###### Why this matters
   This limitation prevents running other test suites and effectively blocks 
the migration of other Cypress tests to Playwright.
   
   ###### Suggested change ∙ *Feature Preview*
   Modify the command to accept a test path parameter:
   ```bash
   local test_path=${1:-"auth/login"}
   npx playwright test $test_path --reporter=github --output=playwright-results
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/30fba1b2-42f5-4497-aecd-eca30314144d/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/30fba1b2-42f5-4497-aecd-eca30314144d?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/30fba1b2-42f5-4497-aecd-eca30314144d?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/30fba1b2-42f5-4497-aecd-eca30314144d?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/30fba1b2-42f5-4497-aecd-eca30314144d)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:303bdef1-bbb1-45bd-ab29-c8f43d8fb2fc -->
   
   
   [](303bdef1-bbb1-45bd-ab29-c8f43d8fb2fc)



##########
.github/workflows/bashlib.sh:
##########
@@ -182,6 +182,76 @@
   kill $flaskProcessId
 }
 
+playwright-install() {
+  cd "$GITHUB_WORKSPACE/superset-frontend"
+
+  say "::group::Install Playwright browsers"
+  npx playwright install --with-deps chromium
+  # Create output directories for test results and debugging
+  mkdir -p playwright-results
+  mkdir -p test-results
+  say "::endgroup::"
+}
+
+playwright-run() {
+  local APP_ROOT=$1
+
+  # Start Flask from the project root (same as Cypress)
+  cd "$GITHUB_WORKSPACE"
+  local flasklog="${HOME}/flask-playwright.log"
+  local port=8081
+  PLAYWRIGHT_BASE_URL="http://localhost:${port}";
+  if [ -n "$APP_ROOT" ]; then
+    export SUPERSET_APP_ROOT=$APP_ROOT
+    PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL}${APP_ROOT}/
+  fi
+  export PLAYWRIGHT_BASE_URL
+
+  nohup flask run --no-debugger -p $port >"$flasklog" 2>&1 </dev/null &
+  local flaskProcessId=$!
+
+  # Ensure cleanup on exit
+  trap "kill $flaskProcessId 2>/dev/null || true" EXIT
+
+  # Wait for server to be ready with health check
+  local timeout=60
+  say "Waiting for Flask server to start on port $port..."
+  while [ $timeout -gt 0 ]; do
+    if curl -f ${PLAYWRIGHT_BASE_URL}/health >/dev/null 2>&1; then
+      say "Flask server is ready"
+      break
+    fi
+    sleep 1
+    timeout=$((timeout - 1))
+  done
+
+  if [ $timeout -eq 0 ]; then
+    echo "::error::Flask server failed to start within 60 seconds"
+    echo "::group::Flask startup log"
+    cat "$flasklog"
+    echo "::endgroup::"
+    return 1
+  fi
+
+  # Change to frontend directory for Playwright execution
+  cd "$GITHUB_WORKSPACE/superset-frontend"
+
+  say "::group::Run Playwright tests"
+  echo "Running Playwright with baseURL: ${PLAYWRIGHT_BASE_URL}"
+  npx playwright test auth/login --reporter=github --output=playwright-results
+  local status=$?
+  say "::endgroup::"
+
+  # After job is done, print out Flask log for debugging
+  echo "::group::Flask log for Playwright run"
+  cat "$flasklog"
+  echo "::endgroup::"
+  # make sure the program exits
+  kill $flaskProcessId
+
+  return $status
+}

Review Comment:
   ### Inflexible Playwright Test Configuration <sub>![category 
Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The Playwright test configuration is hardcoded in the function, limiting 
flexibility and reusability.
   
   
   ###### Why this matters
   Hardcoded test parameters make it difficult to run different test suites or 
configure test runs differently without modifying the script.
   
   ###### Suggested change ∙ *Feature Preview*
   Parameterize the test configuration:
   ```bash
   playwright-run() {
     local APP_ROOT=$1
     local TEST_SPEC=${2:-"auth/login"}
     local REPORTER=${3:-"github"}
     local OUTPUT_DIR=${4:-"playwright-results"}
     
     npx playwright test "$TEST_SPEC" --reporter="$REPORTER" 
--output="$OUTPUT_DIR"
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/7cec4873-84c6-4e38-8fb9-c8bd9f6283bd/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/7cec4873-84c6-4e38-8fb9-c8bd9f6283bd?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/7cec4873-84c6-4e38-8fb9-c8bd9f6283bd?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/7cec4873-84c6-4e38-8fb9-c8bd9f6283bd?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/7cec4873-84c6-4e38-8fb9-c8bd9f6283bd)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:5658fda7-64bd-4921-b028-a5e28a82c74d -->
   
   
   [](5658fda7-64bd-4921-b028-a5e28a82c74d)



##########
superset-frontend/playwright/pages/AuthPage.ts:
##########
@@ -0,0 +1,122 @@
+/**
+ * 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 { Page, Response } from '@playwright/test';
+import { Form } from '../components/core';
+import { URL } from '../utils/urls';
+
+export class AuthPage {
+  private readonly page: Page;
+
+  private readonly loginForm: Form;
+
+  // Selectors specific to the auth/login page
+  private static readonly SELECTORS = {
+    LOGIN_FORM: '[data-test="login-form"]',
+    USERNAME_INPUT: '[data-test="username-input"]',
+    PASSWORD_INPUT: '[data-test="password-input"]',
+    LOGIN_BUTTON: '[data-test="login-button"]',
+    ERROR_SELECTORS: [
+      '[role="alert"]',
+      '.ant-form-item-explain-error',
+      '.ant-form-item-explain.ant-form-item-explain-error',
+      '.alert-danger',
+    ],
+  } as const;
+
+  constructor(page: Page) {
+    this.page = page;
+    this.loginForm = new Form(page, AuthPage.SELECTORS.LOGIN_FORM);
+  }
+
+  /**
+   * Navigate to the login page
+   */
+  async goto(): Promise<void> {
+    await this.page.goto(URL.LOGIN);
+  }
+
+  /**
+   * Wait for login form to be visible
+   */
+  async waitForLoginForm(): Promise<void> {
+    await this.loginForm.waitForVisible({ timeout: 5000 });
+  }
+
+  /**
+   * Login with provided credentials
+   * @param username - Username to enter
+   * @param password - Password to enter
+   */
+  async loginWithCredentials(
+    username: string,
+    password: string,
+  ): Promise<void> {
+    const usernameInput = this.loginForm.getInput(
+      AuthPage.SELECTORS.USERNAME_INPUT,
+    );
+    const passwordInput = this.loginForm.getInput(
+      AuthPage.SELECTORS.PASSWORD_INPUT,
+    );
+    const loginButton = this.loginForm.getButton(
+      AuthPage.SELECTORS.LOGIN_BUTTON,
+    );
+
+    await usernameInput.fill(username);
+    await passwordInput.fill(password);
+    await loginButton.click();
+  }
+
+  /**
+   * Get current page URL
+   */
+  async getCurrentUrl(): Promise<string> {
+    return this.page.url();
+  }
+
+  /**
+   * Get the session cookie specifically
+   */
+  async getSessionCookie(): Promise<{ name: string; value: string } | null> {
+    const cookies = await this.page.context().cookies();
+    return cookies.find((c: any) => c.name === 'session') || null;
+  }
+
+  /**
+   * Check if login form has validation errors
+   */
+  async hasLoginError(): Promise<boolean> {
+    const visibilityPromises = AuthPage.SELECTORS.ERROR_SELECTORS.map(
+      selector => this.page.locator(selector).isVisible(),
+    );
+    const visibilityResults = await Promise.all(visibilityPromises);
+    return visibilityResults.some((isVisible: any) => isVisible);
+  }

Review Comment:
   ### Error detection logic should be encapsulated in Form component 
<sub>![category Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The method contains inline implementation of error detection logic that 
could be abstracted into the Form class for better reusability and separation 
of concerns.
   
   
   ###### Why this matters
   This creates tight coupling between AuthPage and error detection 
implementation details. If error detection logic needs to change, it would 
require modifications in multiple page objects instead of a single Form class.
   
   ###### Suggested change ∙ *Feature Preview*
   Move error detection logic to Form class:
   ```typescript
   // In Form class
   async hasErrors(errorSelectors: string[]): Promise<boolean> {
     const visibilityPromises = errorSelectors.map(
       selector => this.page.locator(selector).isVisible()
     );
     const visibilityResults = await Promise.all(visibilityPromises);
     return visibilityResults.some(isVisible => isVisible);
   }
   
   // In AuthPage
   async hasLoginError(): Promise<boolean> {
     return this.loginForm.hasErrors(AuthPage.SELECTORS.ERROR_SELECTORS);
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0250a9f5-7989-4a29-8c34-a28c94dcaa62/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0250a9f5-7989-4a29-8c34-a28c94dcaa62?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0250a9f5-7989-4a29-8c34-a28c94dcaa62?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0250a9f5-7989-4a29-8c34-a28c94dcaa62?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/0250a9f5-7989-4a29-8c34-a28c94dcaa62)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:cd85c157-543b-4e23-963c-507788730eeb -->
   
   
   [](cd85c157-543b-4e23-963c-507788730eeb)



##########
superset-frontend/playwright/pages/AuthPage.ts:
##########
@@ -0,0 +1,122 @@
+/**
+ * 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 { Page, Response } from '@playwright/test';
+import { Form } from '../components/core';
+import { URL } from '../utils/urls';
+
+export class AuthPage {
+  private readonly page: Page;
+
+  private readonly loginForm: Form;
+
+  // Selectors specific to the auth/login page
+  private static readonly SELECTORS = {
+    LOGIN_FORM: '[data-test="login-form"]',
+    USERNAME_INPUT: '[data-test="username-input"]',
+    PASSWORD_INPUT: '[data-test="password-input"]',
+    LOGIN_BUTTON: '[data-test="login-button"]',
+    ERROR_SELECTORS: [
+      '[role="alert"]',
+      '.ant-form-item-explain-error',
+      '.ant-form-item-explain.ant-form-item-explain-error',
+      '.alert-danger',
+    ],
+  } as const;
+
+  constructor(page: Page) {
+    this.page = page;
+    this.loginForm = new Form(page, AuthPage.SELECTORS.LOGIN_FORM);
+  }
+
+  /**
+   * Navigate to the login page
+   */
+  async goto(): Promise<void> {
+    await this.page.goto(URL.LOGIN);
+  }
+
+  /**
+   * Wait for login form to be visible
+   */
+  async waitForLoginForm(): Promise<void> {
+    await this.loginForm.waitForVisible({ timeout: 5000 });
+  }
+
+  /**
+   * Login with provided credentials
+   * @param username - Username to enter
+   * @param password - Password to enter
+   */
+  async loginWithCredentials(
+    username: string,
+    password: string,
+  ): Promise<void> {
+    const usernameInput = this.loginForm.getInput(
+      AuthPage.SELECTORS.USERNAME_INPUT,
+    );
+    const passwordInput = this.loginForm.getInput(
+      AuthPage.SELECTORS.PASSWORD_INPUT,
+    );
+    const loginButton = this.loginForm.getButton(
+      AuthPage.SELECTORS.LOGIN_BUTTON,
+    );
+
+    await usernameInput.fill(username);
+    await passwordInput.fill(password);
+    await loginButton.click();
+  }
+
+  /**
+   * Get current page URL
+   */
+  async getCurrentUrl(): Promise<string> {
+    return this.page.url();
+  }
+
+  /**
+   * Get the session cookie specifically
+   */
+  async getSessionCookie(): Promise<{ name: string; value: string } | null> {
+    const cookies = await this.page.context().cookies();
+    return cookies.find((c: any) => c.name === 'session') || null;
+  }
+
+  /**
+   * Check if login form has validation errors
+   */
+  async hasLoginError(): Promise<boolean> {
+    const visibilityPromises = AuthPage.SELECTORS.ERROR_SELECTORS.map(
+      selector => this.page.locator(selector).isVisible(),
+    );
+    const visibilityResults = await Promise.all(visibilityPromises);
+    return visibilityResults.some((isVisible: any) => isVisible);
+  }
+
+  /**
+   * Wait for a login request to be made and return the response
+   */
+  async waitForLoginRequest(): Promise<Response> {
+    return this.page.waitForResponse(
+      (response: any) =>
+        response.url().includes('/login/') &&
+        response.request().method() === 'POST',

Review Comment:
   ### Untyped Response Handling <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The response parameter is typed as 'any' in the waitForLoginRequest method, 
which could mask potential API response handling issues.
   
   
   ###### Why this matters
   Without proper typing, changes in the Playwright Response interface might 
not be caught during development, leading to potential runtime failures.
   
   ###### Suggested change ∙ *Feature Preview*
   Use the proper Playwright Response type:
   ```typescript
   (response: Response) =>
       response.url().includes('/login/') &&
       response.request().method() === 'POST'
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/510c4d83-e147-4c7e-97b2-0f1cedf3875a/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/510c4d83-e147-4c7e-97b2-0f1cedf3875a?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/510c4d83-e147-4c7e-97b2-0f1cedf3875a?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/510c4d83-e147-4c7e-97b2-0f1cedf3875a?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/510c4d83-e147-4c7e-97b2-0f1cedf3875a)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:c8a39d84-a27e-4826-9d2b-06b038224568 -->
   
   
   [](c8a39d84-a27e-4826-9d2b-06b038224568)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@superset.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@superset.apache.org
For additional commands, e-mail: notifications-h...@superset.apache.org


Reply via email to