Upgrade Playwright suite to v2.5.1 SQLite deep testing
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* UAT_Consultants_Documents -- Consultants portal + document storage module
|
||||
* =============================================================================
|
||||
*
|
||||
* Covers:
|
||||
* CONS-* : Consultant portal — list, detail, links, conversion requests,
|
||||
* service requests
|
||||
* DOC-STOR-*: Document storage — permanent vault, storage nodes, branch
|
||||
* storage dashboard, download requests
|
||||
* SEC-* : CSRF rejection, anonymous probes, IDOR on storage endpoints
|
||||
*
|
||||
* Required .env additions:
|
||||
* CONSULTANT_A_ID= # a seeded consultant id
|
||||
* CONVERSION_REQUEST_ID= # a seeded managed-client conversion request id
|
||||
* SERVICE_REQUEST_ID= # a seeded consultant service request id
|
||||
* CLIENT_A_ID= # already used in main suite; reused here
|
||||
* STORAGE_NODE_ID= # a seeded storage node id
|
||||
* DOWNLOAD_REQUEST_ID= # a seeded download request id
|
||||
* PERM_DOCUMENT_ID= # a seeded permanent document id
|
||||
* PERM_VERSION_ID= # a seeded permanent document version id
|
||||
*
|
||||
* =============================================================================
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
require('dotenv').config();
|
||||
const { login } = require('../fixtures/auth');
|
||||
const { expectNoBackendError, readBody, blockedOrNotFound, uploadPath } = require('../fixtures/v204-helpers');
|
||||
const { expectBlockedOrSafe } = require('../fixtures/assertions');
|
||||
|
||||
async function safeGoto(page, route) {
|
||||
const resp = await page.goto(route).catch(() => null);
|
||||
await page.waitForLoadState('domcontentloaded').catch(() => {});
|
||||
return resp;
|
||||
}
|
||||
|
||||
function idOr(envKey, fallback = '1') {
|
||||
return process.env[envKey] || fallback;
|
||||
}
|
||||
|
||||
function skipIfMissing(envKey) {
|
||||
if (!process.env[envKey]) test.skip(true, `Set ${envKey} in .env after seeding`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Consultants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('CONS: Consultant portal', () => {
|
||||
|
||||
test('[V25-CONS-001] CONS-001 Consultant list loads for Firm Admin', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, '/consultants');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-002] CONS-002 Consultant detail page loads', async ({ page }) => {
|
||||
skipIfMissing('CONSULTANT_A_ID');
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, `/consultants/${idOr('CONSULTANT_A_ID')}`);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-003] CONS-003 Consultant edit form loads', async ({ page }) => {
|
||||
skipIfMissing('CONSULTANT_A_ID');
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, `/consultants/${idOr('CONSULTANT_A_ID')}/edit`);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-004] CONS-004 Consultant links page loads', async ({ page }) => {
|
||||
skipIfMissing('CONSULTANT_A_ID');
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, `/consultants/${idOr('CONSULTANT_A_ID')}/links`);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-005] CONS-005 Conversion requests list loads', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, '/consultants/conversion-requests');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-006] CONS-006 Conversion request detail loads', async ({ page }) => {
|
||||
skipIfMissing('CONVERSION_REQUEST_ID');
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, `/consultants/conversion-requests/${idOr('CONVERSION_REQUEST_ID')}`);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-007] CONS-007 Conversion request review CSRF-less POST is rejected', async ({ request }) => {
|
||||
skipIfMissing('CONVERSION_REQUEST_ID');
|
||||
const resp = await request.post(
|
||||
`${process.env.BASE_URL}/consultants/conversion-requests/${idOr('CONVERSION_REQUEST_ID')}/review`,
|
||||
{ form: { decision: 'approve', csrf_token: '' } }
|
||||
).catch(() => null);
|
||||
if (!resp || [404, 405].includes(resp.status())) test.skip(true, 'Route not available');
|
||||
expect([400, 401, 403, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-CONS-008] CONS-008 Service requests list loads', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, '/consultants/service-requests');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-009] CONS-009 Service request detail loads', async ({ page }) => {
|
||||
skipIfMissing('SERVICE_REQUEST_ID');
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, `/consultants/service-requests/${idOr('SERVICE_REQUEST_ID')}`);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-CONS-010] CONS-010 Service request status update CSRF-less POST is rejected', async ({ request }) => {
|
||||
skipIfMissing('SERVICE_REQUEST_ID');
|
||||
const resp = await request.post(
|
||||
`${process.env.BASE_URL}/consultants/service-requests/${idOr('SERVICE_REQUEST_ID')}/status`,
|
||||
{ form: { status: 'approved', csrf_token: '' } }
|
||||
).catch(() => null);
|
||||
if (!resp || [404, 405].includes(resp.status())) test.skip(true, 'Route not available');
|
||||
expect([400, 401, 403, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-CONS-011] CONS-011 Staff cannot access consultant list', async ({ page }) => {
|
||||
await login(page, 'Staff');
|
||||
const resp = await safeGoto(page, '/consultants');
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
|
||||
test('[V25-CONS-012] CONS-012 Client cannot access consultant detail', async ({ page }) => {
|
||||
skipIfMissing('CONSULTANT_A_ID');
|
||||
await login(page, 'Client');
|
||||
const resp = await safeGoto(page, `/consultants/${idOr('CONSULTANT_A_ID')}`);
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
|
||||
test('[V25-CONS-013] CONS-013 Non-existent consultant ID returns safe response', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
const resp = await safeGoto(page, '/consultants/999999999');
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
|
||||
test('[V25-CONS-014] CONS-014 Consultant self-service dashboard loads', async ({ page }) => {
|
||||
await login(page, 'Consultant');
|
||||
const resp = await safeGoto(page, '/consultant/dashboard');
|
||||
// may 404 if no consultant portal route — acceptable; must not 500
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Document storage module
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('DOC-STOR: Document storage and permanent vault', () => {
|
||||
|
||||
test('[V25-DOC-STOR-001] DOC-STOR-001 Branch storage dashboard loads for Firm Admin', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, '/documents/branch-storage-dashboard');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-002] DOC-STOR-002 Permanent document vault list loads', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, '/documents/permanent');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-003] DOC-STOR-003 Permanent vault for specific client loads', async ({ page }) => {
|
||||
skipIfMissing('CLIENT_A_ID');
|
||||
await login(page, 'Firm Admin');
|
||||
await safeGoto(page, `/documents/permanent/clients/${idOr('CLIENT_A_ID')}`);
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-004] DOC-STOR-004 Permanent vault upload CSRF-less POST is rejected', async ({ request }) => {
|
||||
skipIfMissing('CLIENT_A_ID');
|
||||
const resp = await request.post(
|
||||
`${process.env.BASE_URL}/documents/permanent/clients/${idOr('CLIENT_A_ID')}/upload`,
|
||||
{ form: { csrf_token: '' } }
|
||||
).catch(() => null);
|
||||
if (!resp || [404, 405].includes(resp.status())) test.skip(true, 'Route not available');
|
||||
expect([400, 401, 403, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-005] DOC-STOR-005 Permanent document download requires auth', async ({ page }) => {
|
||||
skipIfMissing('PERM_DOCUMENT_ID');
|
||||
// anonymous access
|
||||
const resp = await safeGoto(page, `/documents/permanent/${idOr('PERM_DOCUMENT_ID')}/download`);
|
||||
const body = await readBody(page);
|
||||
await blockedOrNotFound(resp, body);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-006] DOC-STOR-006 Storage jobs list loads for System Admin', async ({ page }) => {
|
||||
await login(page, 'System Admin');
|
||||
await safeGoto(page, '/documents/storage-jobs');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-007] DOC-STOR-007 Storage nodes list loads for System Admin', async ({ page }) => {
|
||||
await login(page, 'System Admin');
|
||||
await safeGoto(page, '/documents/storage-nodes');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-008] DOC-STOR-008 Download requests list loads', async ({ page }) => {
|
||||
await login(page, 'System Admin');
|
||||
await safeGoto(page, '/documents/download-requests');
|
||||
await expectNoBackendError(page);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-009] DOC-STOR-009 Storage agent jobs endpoint requires auth', async ({ request }) => {
|
||||
const resp = await request.get(`${process.env.BASE_URL}/documents/storage-agent/jobs/pending`).catch(() => null);
|
||||
if (!resp) test.skip(true, 'Request failed at network level');
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
expect([400, 401, 403, 404, 405, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-010] DOC-STOR-010 Storage agent download-requests endpoint requires auth', async ({ request }) => {
|
||||
const resp = await request.get(`${process.env.BASE_URL}/documents/storage-agent/download-requests/pending`).catch(() => null);
|
||||
if (!resp) test.skip(true, 'Request failed at network level');
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
expect([400, 401, 403, 404, 405, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-011] DOC-STOR-011 Storage node toggle CSRF-less POST is rejected', async ({ request }) => {
|
||||
skipIfMissing('STORAGE_NODE_ID');
|
||||
const resp = await request.post(
|
||||
`${process.env.BASE_URL}/documents/storage-nodes/${idOr('STORAGE_NODE_ID')}/toggle`,
|
||||
{ form: { csrf_token: '' } }
|
||||
).catch(() => null);
|
||||
if (!resp || [404, 405].includes(resp.status())) test.skip(true, 'Route not available');
|
||||
expect([400, 401, 403, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-012] DOC-STOR-012 Staff cannot access branch storage dashboard', async ({ page }) => {
|
||||
await login(page, 'Staff');
|
||||
const resp = await safeGoto(page, '/documents/branch-storage-dashboard');
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-013] DOC-STOR-013 Client cannot access permanent vault admin', async ({ page }) => {
|
||||
await login(page, 'Client');
|
||||
const resp = await safeGoto(page, '/documents/permanent');
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-014] DOC-STOR-014 Permanent document delete CSRF-less POST is rejected', async ({ request }) => {
|
||||
skipIfMissing('PERM_DOCUMENT_ID');
|
||||
const resp = await request.post(
|
||||
`${process.env.BASE_URL}/documents/permanent/${idOr('PERM_DOCUMENT_ID')}/delete`,
|
||||
{ form: { csrf_token: '' } }
|
||||
).catch(() => null);
|
||||
if (!resp || [404, 405].includes(resp.status())) test.skip(true, 'Route not available');
|
||||
expect([400, 401, 403, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-015] DOC-STOR-015 Non-existent permanent document download is handled safely', async ({ page }) => {
|
||||
await login(page, 'Firm Admin');
|
||||
const resp = await safeGoto(page, '/documents/permanent/999999999/download');
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
|
||||
test('[V25-DOC-STOR-016] DOC-STOR-016 Storage agent agent-package download requires node auth', async ({ request }) => {
|
||||
const resp = await request.get(`${process.env.BASE_URL}/documents/storage-nodes/download-agent-package`).catch(() => null);
|
||||
if (!resp || [404, 405].includes(resp.status())) test.skip(true, 'Route not available');
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
expect([400, 401, 403, 404, 422].includes(resp.status())).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user