121 lines
4.4 KiB
TypeScript
121 lines
4.4 KiB
TypeScript
import { expect, type APIRequestContext, type Page } from '@playwright/test';
|
|
import { Wait, waitForDashboardReady, waitForAppReady } from './wait';
|
|
|
|
export type InquiryListItem = {
|
|
id: number;
|
|
name: string;
|
|
phone: string;
|
|
message: string;
|
|
};
|
|
|
|
export async function getAdminToken(
|
|
request: APIRequestContext,
|
|
baseUrl: string,
|
|
username: string,
|
|
password: string,
|
|
) {
|
|
const response = await request.post(`${baseUrl}/api/auth/login`, {
|
|
data: { username, password },
|
|
});
|
|
|
|
expect(response.status(), 'login API should accept the configured admin credentials').toBe(200);
|
|
const body = await response.json();
|
|
expect(body?.token, 'login API should return a token').toBeTruthy();
|
|
return body.token as string;
|
|
}
|
|
|
|
export async function installAdminToken(page: Page, token: string) {
|
|
await page.addInitScript(value => {
|
|
localStorage.setItem('accessToken', value);
|
|
localStorage.setItem('refreshToken', 'ci-test-refresh-token');
|
|
// Calculate C# Ticks for 1 hour from now: (JS_ms * 10000) + 621355968000000000
|
|
const expiryMs = Date.now() + 3600 * 1000;
|
|
const ticks = (expiryMs * 10000) + 621355968000000000;
|
|
localStorage.setItem('tokenExpiry', ticks.toString());
|
|
}, token);
|
|
}
|
|
|
|
export async function loginThroughAdminUi(
|
|
page: Page,
|
|
baseUrl: string,
|
|
username: string,
|
|
password: string,
|
|
) {
|
|
await page.goto(`${baseUrl}/admin/login`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.locator('form#admin-login-form').waitFor({ state: 'visible', timeout: 30_000 });
|
|
await page.locator('#Username').fill(username);
|
|
await page.locator('#Password').fill(password);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForTimeout(2000);
|
|
|
|
if (page.url().includes('/admin/login')) {
|
|
const token = await page.evaluate(async ({ loginUrl, username, password }) => {
|
|
const response = await fetch(loginUrl, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`login failed: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
localStorage.setItem('accessToken', data.accessToken || data.token || '');
|
|
localStorage.setItem('refreshToken', data.refreshToken || '');
|
|
const expiryMs = Date.now() + ((data.expiresIn || 3600) * 1000);
|
|
localStorage.setItem('tokenExpiry', ((expiryMs * 10000) + 621355968000000000).toString());
|
|
return data.accessToken || data.token || '';
|
|
}, { loginUrl: `${baseUrl}/api/auth/login`, username, password });
|
|
|
|
if (!token) {
|
|
throw new Error('login token missing');
|
|
}
|
|
|
|
await page.goto(`${baseUrl}/admin/dashboard`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
}
|
|
}
|
|
|
|
export async function navigateInBlazor(page: Page, targetUrl: string) {
|
|
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
|
|
await waitForAppReady(page).catch(() => {});
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
const spinner = page.locator('.mud-progress-circular, .mud-progress-linear-bar');
|
|
try {
|
|
if (await spinner.count() > 0) {
|
|
await spinner.first().waitFor({ state: 'hidden', timeout: Wait.medium });
|
|
}
|
|
} catch (e) {
|
|
// Suppress timeout if the spinner was already gone or never showed up
|
|
}
|
|
}
|
|
|
|
export async function waitForAdminSection(page: Page, headingText: string) {
|
|
const hero = page.locator('.admin-page-hero');
|
|
await expect(page.locator('body')).toContainText(headingText, { timeout: Wait.page });
|
|
await expect(hero).toBeVisible({ timeout: Wait.page });
|
|
await expect(hero).toContainText(headingText, { timeout: Wait.page });
|
|
await expect(page.getByRole('heading', { name: headingText, exact: true })).toBeVisible({ timeout: Wait.page });
|
|
}
|
|
|
|
export async function findInquiryByName(
|
|
request: APIRequestContext,
|
|
baseUrl: string,
|
|
token: string,
|
|
name: string,
|
|
) {
|
|
const response = await request.get(`${baseUrl}/api/inquiry?page=1&pageSize=100`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
|
|
expect(response.status(), 'admin inquiry list API should be accessible with the token').toBe(200);
|
|
const body = await response.json();
|
|
const items = (body?.data ?? []) as InquiryListItem[];
|
|
const inquiry = items.find(item => item.name === name);
|
|
expect(inquiry, `created inquiry ${name} should appear in the admin inquiry API`).toBeTruthy();
|
|
return inquiry!;
|
|
}
|