Files

7.2 KiB

Instructions

  • Following Playwright test failed.
  • Explain why, be concise, respect Playwright best practices.
  • Provide a snippet of code with the fix, if possible.

Test info

  • Name: 1-login.spec.ts >> Login Workflow >> should show user identity after successful login
  • Location: e2e/workflows/1-login.spec.ts:93:7

Error details

Test timeout of 30000ms exceeded.
Error: page.fill: Test timeout of 30000ms exceeded.
Call log:
  - waiting for locator('[data-testid="local-username-input"]')

Page snapshot

- generic [active] [ref=e1]:
  - generic [ref=e3]:
    - generic [ref=e4]:
      - img [ref=e6]
      - heading "Identity Check" [level=2] [ref=e9]
      - paragraph [ref=e10]: Select operator profile or use direct login
    - generic [ref=e11]:
      - button "newuser user" [ref=e12] [cursor=pointer]:
        - generic [ref=e13]:
          - img [ref=e15]
          - generic [ref=e18]:
            - paragraph [ref=e19]: newuser
            - paragraph [ref=e20]: user
        - img [ref=e21]
      - generic [ref=e23]:
        - button "Enterprise" [ref=e24] [cursor=pointer]:
          - img [ref=e25]
          - text: Enterprise
        - button "Manual Login" [ref=e28] [cursor=pointer]:
          - img [ref=e29]
          - text: Manual Login
  - button "Open Next.js Dev Tools" [ref=e37] [cursor=pointer]:
    - img [ref=e38]
  - alert [ref=e41]

Test source

  1   | import { Page, BrowserContext } from '@playwright/test';
  2   | 
  3   | /**
  4   |  * Authentication fixture for E2E tests
  5   |  * Handles login flows and session management
  6   |  */
  7   | 
  8   | export interface LoginCredentials {
  9   |   username: string;
  10  |   password: string;
  11  | }
  12  | 
  13  | export interface AuthSession {
  14  |   token: string;
  15  |   userId: string;
  16  |   email: string;
  17  |   isAdmin: boolean;
  18  | }
  19  | 
  20  | /**
  21  |  * Perform LDAP authentication
  22  |  */
  23  | export async function loginWithLdap(
  24  |   page: Page,
  25  |   credentials: LoginCredentials,
  26  |   baseUrl: string = 'http://localhost'
  27  | ): Promise<void> {
  28  |   await page.goto(`${baseUrl}`);
  29  | 
  30  |   // Wait for login form to appear
  31  |   await page.waitForSelector('[data-testid="login-form"]', { timeout: 5000 });
  32  | 
  33  |   // Fill in username
  34  |   await page.fill('[data-testid="username-input"]', credentials.username);
  35  | 
  36  |   // Fill in password
  37  |   await page.fill('[data-testid="password-input"]', credentials.password);
  38  | 
  39  |   // Click login button
  40  |   await page.click('[data-testid="login-submit"]');
  41  | 
  42  |   // Wait for navigation to inventory page
  43  |   await page.waitForURL(`${baseUrl}/*`, { waitUntil: 'networkidle' });
  44  | }
  45  | 
  46  | /**
  47  |  * Perform local user authentication
  48  |  */
  49  | export async function loginWithLocalUser(
  50  |   page: Page,
  51  |   credentials: LoginCredentials,
  52  |   baseUrl: string = 'http://localhost'
  53  | ): Promise<void> {
  54  |   await page.goto(`${baseUrl}`);
  55  | 
  56  |   // Wait for identity check overlay
  57  |   await page.waitForSelector('[data-testid="identity-check-overlay"]', { timeout: 5000 });
  58  | 
  59  |   // Click "Local User" tab if it exists
  60  |   const localUserTab = page.locator('[data-testid="local-user-tab"]');
  61  |   if (await localUserTab.isVisible()) {
  62  |     await localUserTab.click();
  63  |   }
  64  | 
  65  |   // Fill in username
> 66  |   await page.fill('[data-testid="local-username-input"]', credentials.username);
      |              ^ Error: page.fill: Test timeout of 30000ms exceeded.
  67  | 
  68  |   // Fill in password
  69  |   await page.fill('[data-testid="local-password-input"]', credentials.password);
  70  | 
  71  |   // Click login button
  72  |   await page.click('[data-testid="local-login-submit"]');
  73  | 
  74  |   // Wait for navigation to inventory page
  75  |   await page.waitForURL(`${baseUrl}/*`, { waitUntil: 'networkidle' });
  76  | }
  77  | 
  78  | /**
  79  |  * Logout from the application
  80  |  */
  81  | export async function logout(page: Page, baseUrl: string = 'http://localhost'): Promise<void> {
  82  |   // Click logout button or menu
  83  |   const logoutButton = page.locator('[data-testid="logout-button"]');
  84  |   if (await logoutButton.isVisible()) {
  85  |     await logoutButton.click();
  86  |   } else {
  87  |     // Try finding it in menu
  88  |     const menu = page.locator('[data-testid="user-menu"]');
  89  |     if (await menu.isVisible()) {
  90  |       await menu.click();
  91  |       await page.click('[data-testid="logout-menu-item"]');
  92  |     }
  93  |   }
  94  | 
  95  |   // Wait for redirect to login
  96  |   await page.waitForURL(`${baseUrl}/*`, { timeout: 5000 });
  97  | }
  98  | 
  99  | /**
  100 |  * Get authentication token from local storage
  101 |  */
  102 | export async function getAuthToken(page: Page): Promise<string | null> {
  103 |   const token = await page.evaluate(() => {
  104 |     return localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token');
  105 |   });
  106 |   return token;
  107 | }
  108 | 
  109 | /**
  110 |  * Set authentication token in local storage
  111 |  */
  112 | export async function setAuthToken(page: Page, token: string): Promise<void> {
  113 |   await page.evaluate((t) => {
  114 |     localStorage.setItem('auth_token', t);
  115 |   }, token);
  116 | }
  117 | 
  118 | /**
  119 |  * Clear authentication data
  120 |  */
  121 | export async function clearAuth(page: Page): Promise<void> {
  122 |   await page.evaluate(() => {
  123 |     localStorage.removeItem('auth_token');
  124 |     sessionStorage.removeItem('auth_token');
  125 |     localStorage.removeItem('user_data');
  126 |     sessionStorage.removeItem('user_data');
  127 |   });
  128 | }
  129 | 
  130 | /**
  131 |  * Check if user is authenticated
  132 |  */
  133 | export async function isAuthenticated(page: Page): Promise<boolean> {
  134 |   const token = await getAuthToken(page);
  135 |   return !!token;
  136 | }
  137 | 
  138 | /**
  139 |  * Get current user info from page
  140 |  */
  141 | export async function getCurrentUser(page: Page): Promise<{ username: string; email: string } | null> {
  142 |   try {
  143 |     const userData = await page.evaluate(() => {
  144 |       const stored = localStorage.getItem('user_data') || sessionStorage.getItem('user_data');
  145 |       return stored ? JSON.parse(stored) : null;
  146 |     });
  147 |     return userData;
  148 |   } catch {
  149 |     return null;
  150 |   }
  151 | }
  152 | 
  153 | /**
  154 |  * Wait for authentication to complete
  155 |  */
  156 | export async function waitForAuth(page: Page, timeout: number = 5000): Promise<void> {
  157 |   // Wait for either:
  158 |   // 1. Token to be in storage
  159 |   // 2. Navigation to happen
  160 |   // 3. Auth overlay to disappear
  161 |   await page.waitForFunction(
  162 |     () => {
  163 |       const token =
  164 |         typeof window !== 'undefined'
  165 |           ? localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token')
  166 |           : null;