110 lines
5.5 KiB
TypeScript
110 lines
5.5 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
// v0.71.0 — Admin panel e2e: top-bar button → modal with login form; wrong credentials show an
|
|
// error; a session minted by the /api/test/admin-session hook (FLIGHT_TEST_HOOKS=1) authenticates
|
|
// WITHOUT the real admin password ever appearing in test code; then the generic DB grid is
|
|
// exercised end-to-end: table list → edit + save a cell (verified by an independent re-fetch) →
|
|
// add a row → delete it → logout back to the login form.
|
|
|
|
const SESSION_KEY = 'flight.session'; // must match components/AdminPanel.tsx
|
|
|
|
async function mintAdminSession(page: import('@playwright/test').Page): Promise<string> {
|
|
const res = await page.request.post('/api/test/admin-session');
|
|
expect(res.status(), 'admin-session hook should be enabled in the CI stack (FLIGHT_TEST_HOOKS=1)').toBe(200);
|
|
return ((await res.json()) as { sessionId: string }).sessionId;
|
|
}
|
|
|
|
test('admin panel: login form, wrong-creds error, session auth, DB grid CRUD, logout', async ({ page }) => {
|
|
await page.goto('/');
|
|
const adminBtn = page.getByTestId('admin-btn');
|
|
await expect(adminBtn).toBeVisible({ timeout: 30_000 });
|
|
|
|
// --- Open the modal: no stored session → login form.
|
|
await adminBtn.click();
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toBeVisible();
|
|
await expect(page.getByTestId('admin-username')).toBeVisible();
|
|
await expect(page.getByTestId('admin-password')).toBeVisible();
|
|
|
|
// --- Wrong credentials → error line (the seeded admin's plaintext is never used here).
|
|
await page.getByTestId('admin-username').fill('kamilkrzywda');
|
|
await page.getByTestId('admin-password').fill('wrong-password-on-purpose');
|
|
await page.getByTestId('admin-login-submit').click();
|
|
await expect(page.getByTestId('admin-error')).toBeVisible({ timeout: 10_000 });
|
|
|
|
// --- Authenticate via the test hook + localStorage, then reopen the panel.
|
|
const sessionId = await mintAdminSession(page);
|
|
await page.evaluate((key) => window.localStorage.setItem(key, sessionId), SESSION_KEY);
|
|
await page.reload();
|
|
await expect(adminBtn).toBeVisible({ timeout: 30_000 });
|
|
await adminBtn.click();
|
|
|
|
// --- Authenticated admin → table list including packages and users.
|
|
const tables = page.getByTestId('admin-tables');
|
|
await expect(tables).toBeVisible({ timeout: 30_000 });
|
|
await expect(page.getByTestId('admin-table-packages')).toBeVisible();
|
|
await expect(page.getByTestId('admin-table-users')).toBeVisible();
|
|
|
|
// --- Open packages, edit one cell, Save; verify via an INDEPENDENT re-fetch of the same route.
|
|
await page.getByTestId('admin-table-packages').click();
|
|
const grid = page.getByTestId('admin-rows');
|
|
await expect(grid).toBeVisible({ timeout: 30_000 });
|
|
|
|
const rowidCell = page.getByTestId('admin-cell-0-__rowid');
|
|
const originalRowid = Number(await rowidCell.inputValue());
|
|
expect(Number.isInteger(originalRowid)).toBeTruthy();
|
|
|
|
const rewardCell = page.getByTestId('admin-cell-0-reward');
|
|
await rewardCell.fill('987654');
|
|
await page.getByTestId('admin-save-0').click();
|
|
|
|
// The grid re-fetches after save — the edited cell shows the new value…
|
|
await expect(rewardCell).toHaveValue('987654', { timeout: 15_000 });
|
|
// …and an independent request (not the grid's) confirms it persisted server-side.
|
|
const check = await page.request.get('/api/admin/tables/packages/rows?limit=500', {
|
|
headers: { 'X-Flight-Session': sessionId },
|
|
});
|
|
expect(check.status()).toBe(200);
|
|
const fetched = (await check.json()) as { rows: Array<Record<string, unknown>> };
|
|
const row = fetched.rows.find((r) => Number(r.__rowid) === originalRowid);
|
|
expect(row, 'edited packages row must still exist').toBeTruthy();
|
|
expect(Number(row?.reward)).toBe(987654);
|
|
|
|
// --- Add a row (users table — fewest required columns), then delete it again.
|
|
await page.getByTestId('admin-back').click();
|
|
await expect(tables).toBeVisible();
|
|
await page.getByTestId('admin-table-users').click();
|
|
const usersGrid = page.getByTestId('admin-rows');
|
|
await expect(usersGrid).toBeVisible({ timeout: 30_000 });
|
|
|
|
const existingRows = await page.getByTestId(/admin-save-\d+/).count();
|
|
await page.getByTestId('admin-add-row').click();
|
|
const n = existingRows; // the new empty row is appended last
|
|
await page.getByTestId(`admin-cell-${n}-id`).fill('user-e2e-admin-test');
|
|
await page.getByTestId(`admin-cell-${n}-username`).fill('e2e-admin-user');
|
|
await page.getByTestId(`admin-cell-${n}-password_hash`).fill('aa$bb'); // any string — dev-tool insert, not a real credential
|
|
await page.getByTestId(`admin-cell-${n}-created_at`).fill(String(Date.now()));
|
|
await page.getByTestId(`admin-save-${n}`).click();
|
|
|
|
// The inserted row is visible after the grid's post-save re-fetch.
|
|
const bodyRows = usersGrid.locator('tbody tr');
|
|
const findUserRow = async (): Promise<number> => {
|
|
const count = await bodyRows.count();
|
|
for (let i = 0; i < count; i++) {
|
|
if ((await page.getByTestId(`admin-cell-${i}-username`).inputValue()) === 'e2e-admin-user') return i;
|
|
}
|
|
return -1;
|
|
};
|
|
await expect.poll(findUserRow, { timeout: 15_000 }).toBeGreaterThanOrEqual(0);
|
|
const addedIdx = await findUserRow();
|
|
|
|
// Delete it again — gone after the post-delete re-fetch.
|
|
await page.getByTestId(`admin-delete-${addedIdx}`).click();
|
|
await expect.poll(findUserRow, { timeout: 15_000 }).toBe(-1);
|
|
|
|
// --- Logout returns to the login form.
|
|
await page.getByTestId('admin-logout').click();
|
|
await expect(page.getByTestId('admin-username')).toBeVisible({ timeout: 10_000 });
|
|
await expect(tables).toBeHidden();
|
|
});
|