# 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: auth/profile.spec.ts >> Auth: Profile & User Menu >> profile dialog: auth tab visible
- Location: src/tests/auth/profile.spec.ts:73:7

# Error details

```
Error: Login failed for e2e-admin-auth-profile-spec-ts-1779467507121@tum-ed-as (404): Not Found
```

# Test source

```ts
  1   | import { test as base, type Page, type BrowserContext } from '@playwright/test';
  2   | import * as crypto from 'node:crypto';
  3   | import type { FeatureFlags } from '@masterev/config-shared';
  4   | import { convexRun } from './helpers/convex.js';
  5   | import { customers, type CustomerConfig } from './config.js';
  6   | import type { AuthMode, CustomerRuntime } from './framework/runtime.js';
  7   | 
  8   | type UserRole = 'ADMIN' | 'POWERUSER' | 'USER';
  9   | 
  10  | interface CreatedUser {
  11  |   userId: string;
  12  |   username: string;
  13  |   password: string;
  14  | }
  15  | 
  16  | const SESSION_COOKIE_NAME = 'session';
  17  | 
  18  | function base64UrlEncode(buffer: Uint8Array | Buffer): string {
  19  |   return Buffer.from(buffer)
  20  |     .toString('base64')
  21  |     .replace(/\+/g, '-')
  22  |     .replace(/\//g, '_')
  23  |     .replace(/=+$/, '');
  24  | }
  25  | 
  26  | function newSessionId(): string {
  27  |   return base64UrlEncode(crypto.randomBytes(32));
  28  | }
  29  | 
  30  | async function hashSessionId(sessionId: string): Promise<string> {
  31  |   const data = new TextEncoder().encode(sessionId);
  32  |   const hashBuffer = await crypto.webcrypto.subtle.digest('SHA-256', data);
  33  |   return base64UrlEncode(new Uint8Array(hashBuffer));
  34  | }
  35  | 
  36  | async function passwordLogin(
  37  |   customerUrl: string,
  38  |   customerName: string,
  39  |   username: string,
  40  |   password: string
  41  | ): Promise<string> {
  42  |   const loginUrl = new URL('/api/actions/auth/password/login', customerUrl);
  43  |   loginUrl.searchParams.set('redirect_uri', customerUrl);
  44  |   loginUrl.searchParams.set('customer', customerName);
  45  | 
  46  |   const response = await fetch(loginUrl.toString(), {
  47  |     method: 'POST',
  48  |     headers: { 'Content-Type': 'application/json' },
  49  |     body: JSON.stringify({ username, password }),
  50  |     redirect: 'manual',
  51  |   });
  52  | 
  53  |   if (!response.ok) {
  54  |     const body = await response.text();
> 55  |     throw new Error(
      |           ^ Error: Login failed for e2e-admin-auth-profile-spec-ts-1779467507121@tum-ed-as (404): Not Found
  56  |       `Login failed for ${username}@${customerName} (${response.status}): ${body}`
  57  |     );
  58  |   }
  59  | 
  60  |   const setCookie = response.headers.get('set-cookie');
  61  |   if (!setCookie) {
  62  |     throw new Error(`No Set-Cookie header for ${username}@${customerName}`);
  63  |   }
  64  |   const sessionMatch = /session=([^;]+)/.exec(setCookie);
  65  |   if (!sessionMatch || sessionMatch[1] === undefined) {
  66  |     throw new Error(`No session cookie for ${username}@${customerName}`);
  67  |   }
  68  |   return sessionMatch[1];
  69  | }
  70  | 
  71  | async function localBypassLogin(
  72  |   customerName: string,
  73  |   userId: string
  74  | ): Promise<string> {
  75  |   const sessionId = newSessionId();
  76  |   const hash = await hashSessionId(sessionId);
  77  |   await convexRun('sessions:createSession', {
  78  |     hash,
  79  |     userId,
  80  |     customer: customerName,
  81  |     userAgent: 'e2e-local-bypass',
  82  |     ip: '127.0.0.1',
  83  |   });
  84  |   return sessionId;
  85  | }
  86  | 
  87  | async function loginUser(
  88  |   browser: { newContext: (opts: object) => Promise<BrowserContext> },
  89  |   runtime: CustomerRuntime,
  90  |   customerName: string,
  91  |   user: CreatedUser
  92  | ): Promise<BrowserContext> {
  93  |   const sessionValue =
  94  |     runtime.authMode === 'local-bypass'
  95  |       ? await localBypassLogin(customerName, user.userId)
  96  |       : await passwordLogin(
  97  |           runtime.baseURL,
  98  |           customerName,
  99  |           user.username,
  100 |           user.password
  101 |         );
  102 | 
  103 |   const url = new URL(runtime.baseURL);
  104 |   const isHttps = url.protocol === 'https:';
  105 | 
  106 |   return browser.newContext({
  107 |     baseURL: runtime.baseURL,
  108 |     ignoreHTTPSErrors: true,
  109 |     storageState: {
  110 |       cookies: [
  111 |         {
  112 |           name: SESSION_COOKIE_NAME,
  113 |           value: sessionValue,
  114 |           domain: url.hostname,
  115 |           path: '/',
  116 |           httpOnly: true,
  117 |           secure: isHttps,
  118 |           sameSite: 'Lax' as const,
  119 |           expires: -1,
  120 |         },
  121 |       ],
  122 |       origins: [],
  123 |     },
  124 |   });
  125 | }
  126 | 
  127 | /**
  128 |  * Create a unique test user via Convex HTTP API and return credentials.
  129 |  */
  130 | async function createTestUser(
  131 |   customer: string,
  132 |   role: UserRole,
  133 |   testInfo: { titlePath: string[] },
  134 |   teamId?: string
  135 | ): Promise<CreatedUser> {
  136 |   const specName = testInfo.titlePath[0]
  137 |     ?.replace(/[^a-zA-Z0-9]/g, '-')
  138 |     .toLowerCase()
  139 |     .slice(0, 30) ?? 'test';
  140 |   const timestamp = Date.now();
  141 |   const username = `e2e-${role.toLowerCase()}-${specName}-${timestamp}`;
  142 |   const password = `e2e-pass-${timestamp}`;
  143 | 
  144 |   const userId = await convexRun<string>('e2e:createUser', {
  145 |     customer,
  146 |     name: `E2E ${role} ${specName}`,
  147 |     username,
  148 |     role,
  149 |     password,
  150 |     teamIds: teamId ? [teamId] : [],
  151 |   });
  152 | 
  153 |   return { userId, username, password };
  154 | }
  155 | 
```