Expand e2e auth coverage, Vitest CI, and shared URL defaults
Wire viewer-frontend Vitest into Gitea Actions, centralize DEV base URLs in env-defaults.json, expand playkit API catalog specs, and land auth/mail flows plus verify-email public-host redirect and idle-logout e2e hooks.
This commit is contained in:
@@ -15,4 +15,14 @@ export class AccountMenu extends BasePage {
|
||||
await this.openMenu();
|
||||
await this.click(this.page.getByRole('button', { name: 'Sign out' }));
|
||||
}
|
||||
|
||||
async openManageUsers(): Promise<void> {
|
||||
await this.openMenu();
|
||||
await this.click(this.page.getByRole('button', { name: 'Manage Users' }));
|
||||
await waitForVisible(this.page.getByRole('heading', { name: /Manage Users/i }));
|
||||
}
|
||||
|
||||
async closeManageUsers(): Promise<void> {
|
||||
await this.click(this.page.getByLabel('Close manage users'));
|
||||
}
|
||||
}
|
||||
|
||||
+23
-3
@@ -8,11 +8,31 @@ export class LoginPage extends BasePage {
|
||||
|
||||
async openLogin(): Promise<void> {
|
||||
await this.open('/login');
|
||||
await this.page.waitForLoadState('domcontentloaded');
|
||||
await this.page.locator('#email').waitFor({ state: 'visible' });
|
||||
// Wait for client bundle / React hydration (native GET submit otherwise)
|
||||
await this.page.waitForFunction(() => {
|
||||
const form = document.querySelector('form');
|
||||
const btn = document.querySelector('button[type="submit"]');
|
||||
return !!form && !!btn && !!(window as unknown as { next?: unknown }).next;
|
||||
}).catch(() => undefined);
|
||||
await this.page.waitForTimeout(600);
|
||||
}
|
||||
|
||||
async signIn(email: string, password: string): Promise<void> {
|
||||
await this.fill(this.page.locator('#email'), email);
|
||||
await this.fill(this.page.locator('#password'), password);
|
||||
await this.click(this.page.locator('button[type="submit"]'));
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
await this.fill(this.page.locator('#email'), email);
|
||||
await this.fill(this.page.locator('#password'), password);
|
||||
await this.page.waitForTimeout(250 * (attempt + 1));
|
||||
await this.click(this.page.getByRole('button', { name: /Sign in/i }));
|
||||
await this.page.waitForTimeout(400);
|
||||
// Native GET fallback leaks credentials into the query string — retry.
|
||||
if (!this.page.url().includes('password=')) {
|
||||
return;
|
||||
}
|
||||
this.log.warn('login GET fallback detected; retrying after re-open', { attempt });
|
||||
await this.openLogin();
|
||||
}
|
||||
throw new Error('Login form submitted as GET (page not hydrated)');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { type Page } from '@playwright/test';
|
||||
import { BasePage } from '@levkin/playkit';
|
||||
|
||||
export class RegisterPage extends BasePage {
|
||||
constructor(page: Page, baseUrl: string) {
|
||||
super(page, baseUrl);
|
||||
}
|
||||
|
||||
async openRegister(): Promise<void> {
|
||||
await this.open('/register');
|
||||
await this.page.waitForLoadState('domcontentloaded');
|
||||
await this.page.locator('#email').waitFor({ state: 'visible' });
|
||||
await this.page.waitForTimeout(300);
|
||||
}
|
||||
|
||||
async register(input: {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}): Promise<void> {
|
||||
await this.fill(this.page.locator('#name'), input.name);
|
||||
await this.fill(this.page.locator('#email'), input.email);
|
||||
await this.fill(this.page.locator('#password'), input.password);
|
||||
await this.fill(this.page.locator('#confirmPassword'), input.password);
|
||||
await this.click(this.page.locator('button[type="submit"]'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user