feat: implement new feature for enhanced user experience

This commit is contained in:
2026-02-11 16:36:56 +00:00
parent 62f1906276
commit dc00d24899
75 changed files with 2456 additions and 35 deletions

View File

@@ -0,0 +1,76 @@
import { APIRequestContext, request } from "@playwright/test"
import { ADMIN_TOKEN, API_BASE } from "../../fixtures/test-constants"
let adminContext: APIRequestContext | null = null
/**
* Get or create a singleton admin API context with the ADMIN_TOKEN.
*/
async function getAdminContext(baseURL: string): Promise<APIRequestContext> {
if (!adminContext) {
adminContext = await request.newContext({
baseURL,
ignoreHTTPSErrors: true,
extraHTTPHeaders: {
Token: ADMIN_TOKEN,
},
})
}
return adminContext
}
/**
* Delete a user by ID via admin API.
*/
export async function deleteUser(baseURL: string, userId: string): Promise<boolean> {
const ctx = await getAdminContext(baseURL)
const res = await ctx.delete(`${API_BASE}/user/${userId}`)
return res.ok()
}
/**
* Cleanup test users matching a pattern in their email.
*/
export async function cleanupTestUsers(baseURL: string, emailPattern: RegExp | string): Promise<number> {
const ctx = await getAdminContext(baseURL)
const res = await ctx.get(`${API_BASE}/user`)
if (!res.ok()) return 0
const users: { id?: string; _id?: string; email?: string }[] = await res.json()
if (!Array.isArray(users)) return 0
let deleted = 0
for (const user of users) {
const email = user.email || ""
const matches = typeof emailPattern === "string" ? email.includes(emailPattern) : emailPattern.test(email)
if (matches) {
const userId = user.id || user._id
if (userId) {
const ok = await deleteUser(baseURL, userId)
if (ok) deleted++
}
}
}
return deleted
}
/**
* Cleanup all test data (users and tokens matching @test.example.com).
*/
export async function cleanupAllTestData(baseURL: string): Promise<{ users: number }> {
const testPattern = "@test.example.com"
const users = await cleanupTestUsers(baseURL, testPattern)
return { users }
}
/**
* Dispose the admin API context. Call in globalTeardown.
*/
export async function disposeAdminApi(): Promise<void> {
if (adminContext) {
await adminContext.dispose()
adminContext = null
}
}