fix: migrations, auth, providers health check, E2E tests, remove neko-sama
- Add proper Alembic initial migration (0001_initial_schema.py) - Migrate refresh tokens from JSON file to SQLite (RefreshTokenTable) - Remove Neko-Sama provider entirely (redirects to Gupy, not a host) - Fix provider health check always showing UNKNOWN - Run check_all_health() on startup - Fix POST /providers/health/check background task bug - Add HTMX refresh after manual health check trigger - Fix anime search relevance scoring with MIN_RELEVANCE_THRESHOLD=0.5 - Replace bare 'except:' with 'except Exception:' across codebase - Add Playwright E2E test suite (12 tests, auth setup, helpers) - Fix toast container blocking clicks via pointer-events: none - Remove obsolete Jest/Vite test files and config - Clean up obsolete test_watchlist scripts - Update sonarr model comment for active providers
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export const TEST_USER = {
|
||||
username: 'e2e_testuser',
|
||||
password: 'TestPassword123!',
|
||||
};
|
||||
|
||||
/**
|
||||
* Log in via the UI login form.
|
||||
*/
|
||||
export async function login(page: Page, username: string, password: string) {
|
||||
await page.goto('/login');
|
||||
await page.fill('#loginUsername', username);
|
||||
await page.fill('#loginPassword', password);
|
||||
|
||||
const [response] = await Promise.all([
|
||||
page.waitForResponse((resp) => resp.url().includes('/api/auth/login')),
|
||||
page.click('#loginSubmit'),
|
||||
]);
|
||||
|
||||
expect(response.status()).toBeLessThan(400);
|
||||
|
||||
// Wait for success message or redirect
|
||||
await Promise.race([
|
||||
page.locator('#authSuccess').waitFor({ state: 'visible', timeout: 5000 }),
|
||||
page.waitForURL('**/web**', { timeout: 5000 }),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new unique user via the UI form.
|
||||
*/
|
||||
export async function register(page: Page, username: string, password: string) {
|
||||
await page.goto('/login');
|
||||
await page.click('text=Inscription');
|
||||
|
||||
await page.fill('#registerUsername', username);
|
||||
await page.fill('#registerPassword', password);
|
||||
await page.fill('#registerPasswordConfirm', password);
|
||||
|
||||
const [response] = await Promise.all([
|
||||
page.waitForResponse((resp) => resp.url().includes('/api/auth/register')),
|
||||
page.click('#registerSubmit'),
|
||||
]);
|
||||
|
||||
expect(response.status()).toBeLessThan(400);
|
||||
|
||||
await page.locator('#authSuccess').waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to a tab by name (Accueil, Anime, Série, Watchlist, etc.)
|
||||
*/
|
||||
export async function switchTab(page: Page, tabName: string) {
|
||||
// Wait for tabs to be rendered
|
||||
await page.locator('nav#mainTabs .tab').first().waitFor({ state: 'visible', timeout: 5000 });
|
||||
|
||||
const tab = page.locator('nav#mainTabs .tab', { hasText: new RegExp(tabName, 'i') });
|
||||
await tab.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await tab.click();
|
||||
await expect(tab).toHaveClass(/active/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for HTMX content to settle (no more hx-request in flight).
|
||||
*/
|
||||
export async function waitForHtmx(page: Page, timeout = 10000) {
|
||||
await page.waitForFunction(
|
||||
() => document.querySelectorAll('.htmx-request').length === 0,
|
||||
{ timeout }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that no unhandled JS errors occurred on the page.
|
||||
*/
|
||||
export function collectJsErrors(page: Page): string[] {
|
||||
const errors: string[] = [];
|
||||
page.on('pageerror', (err) => errors.push(err.message));
|
||||
return errors;
|
||||
}
|
||||
Reference in New Issue
Block a user