feat: tests & bootstrap
This commit is contained in:
parent
4bd927bb4e
commit
79e20ca37c
19 changed files with 706 additions and 23 deletions
74
tests/e2e/fixtures.ts
Normal file
74
tests/e2e/fixtures.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import {
|
||||
test as base,
|
||||
expect,
|
||||
type Page,
|
||||
type BrowserContext,
|
||||
} from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Shared fixtures and helpers.
|
||||
*
|
||||
* Works against either the mock plugin (VITE_USE_MOCK_API=true) or the real
|
||||
* Django backend (stratoflights). Mock accepts any credentials; Django
|
||||
* expects demo/demo and enforces CSRF.
|
||||
*
|
||||
* The `TEST_USERNAME` / `TEST_PASSWORD` envs let CI override the default.
|
||||
*
|
||||
* Critical detail: login/logout must go through `page.context().request` so
|
||||
* cookies are shared between API calls and the page. Using the plain
|
||||
* `request` fixture gives you a separate cookie jar.
|
||||
*/
|
||||
|
||||
const USERNAME = process.env.TEST_USERNAME ?? 'demo';
|
||||
const PASSWORD = process.env.TEST_PASSWORD ?? 'demo';
|
||||
|
||||
export async function login(context: BrowserContext): Promise<void> {
|
||||
const request = context.request;
|
||||
await request.get('/api/csrf/');
|
||||
const state = await context.storageState();
|
||||
const csrf = state.cookies.find((c) => c.name === 'csrftoken')?.value ?? '';
|
||||
const res = await request.post('/api/login/', {
|
||||
data: { username: USERNAME, password: PASSWORD },
|
||||
headers: { 'X-CSRFToken': csrf, 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!res.ok()) {
|
||||
throw new Error(
|
||||
`Login failed: ${res.status()} ${await res.text()}. Is stratoflights running on :8000 with a user "${USERNAME}" / "${PASSWORD}"?`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function logout(context: BrowserContext): Promise<void> {
|
||||
const state = await context.storageState();
|
||||
const csrf = state.cookies.find((c) => c.name === 'csrftoken')?.value ?? '';
|
||||
await context.request.post('/api/logout/', {
|
||||
headers: { 'X-CSRFToken': csrf, 'Content-Type': 'application/json' },
|
||||
});
|
||||
// Clear cookies so the page navigator is fully anonymous next goto.
|
||||
await context.clearCookies();
|
||||
}
|
||||
|
||||
export const test = base.extend({
|
||||
page: async ({ page }, use) => {
|
||||
const hardErrors: string[] = [];
|
||||
page.on('pageerror', (e) => hardErrors.push(`[pageerror] ${e.message}`));
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'error') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(' [page console.error]', msg.text());
|
||||
}
|
||||
});
|
||||
await use(page);
|
||||
if (hardErrors.length > 0) throw new Error('Page errors:\n' + hardErrors.join('\n'));
|
||||
},
|
||||
});
|
||||
|
||||
export { expect };
|
||||
|
||||
export async function openPredict(page: Page) {
|
||||
await page.goto('/predict');
|
||||
await page
|
||||
.locator('.map-container canvas')
|
||||
.first()
|
||||
.waitFor({ state: 'attached', timeout: 15_000 });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue