Fix remaining v2.5.1 Playwright POST download and FY selector tests

This commit is contained in:
A R R R Associates
2026-06-25 23:20:56 +05:30
parent ba1075ef19
commit 21c90749fb
8 changed files with 53 additions and 21 deletions
+6 -2
View File
@@ -307,10 +307,14 @@ test.describe("v2.5.1 Additions - Employee HR Business Rules", () => {
} }
if (kind === 'post') { if (kind === 'post') {
const resp = await apiCall(request, method || 'POST', c.route); // Some generated VAPT rows accidentally carry _method: 'GET' even when
// the scenario is explicitly CSRF-less POST. Force POST unless the row
// intentionally specifies another unsafe verb.
const unsafeMethod = (!method || method === 'GET') ? 'POST' : method;
const resp = await apiCall(request, unsafeMethod, c.route);
await expectApiSafe(resp); await expectApiSafe(resp);
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status()); const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
expect(safe, `Unsafe POST status ${resp.status()} for ${c.route}`).toBeTruthy(); expect(safe, `Unsafe ${unsafeMethod} status ${resp.status()} for ${c.route}`).toBeTruthy();
return; return;
} }
+6 -2
View File
@@ -331,10 +331,14 @@ test.describe("v2.5.1 Additions - Marketplace / Leads", () => {
} }
if (kind === 'post') { if (kind === 'post') {
const resp = await apiCall(request, method || 'POST', c.route); // Some generated VAPT rows accidentally carry _method: 'GET' even when
// the scenario is explicitly CSRF-less POST. Force POST unless the row
// intentionally specifies another unsafe verb.
const unsafeMethod = (!method || method === 'GET') ? 'POST' : method;
const resp = await apiCall(request, unsafeMethod, c.route);
await expectApiSafe(resp); await expectApiSafe(resp);
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status()); const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
expect(safe, `Unsafe POST status ${resp.status()} for ${c.route}`).toBeTruthy(); expect(safe, `Unsafe ${unsafeMethod} status ${resp.status()} for ${c.route}`).toBeTruthy();
return; return;
} }
+6 -2
View File
@@ -343,10 +343,14 @@ test.describe("v2.5.1 Additions - Notice Case Business Rules", () => {
} }
if (kind === 'post') { if (kind === 'post') {
const resp = await apiCall(request, method || 'POST', c.route); // Some generated VAPT rows accidentally carry _method: 'GET' even when
// the scenario is explicitly CSRF-less POST. Force POST unless the row
// intentionally specifies another unsafe verb.
const unsafeMethod = (!method || method === 'GET') ? 'POST' : method;
const resp = await apiCall(request, unsafeMethod, c.route);
await expectApiSafe(resp); await expectApiSafe(resp);
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status()); const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
expect(safe, `Unsafe POST status ${resp.status()} for ${c.route}`).toBeTruthy(); expect(safe, `Unsafe ${unsafeMethod} status ${resp.status()} for ${c.route}`).toBeTruthy();
return; return;
} }
+15 -6
View File
@@ -152,20 +152,29 @@ test.describe('SVC: Services depth', () => {
test('[V25-SVC-002] SVC-002 Bulk imports service-master template downloads', async ({ page }) => { test('[V25-SVC-002] SVC-002 Bulk imports service-master template downloads', async ({ page }) => {
await login(page, 'Firm Admin'); await login(page, 'Firm Admin');
const resp = await safeGoto(page, '/services/bulk-imports/templates/service-master.xlsx'); const [download] = await Promise.all([
expect(resp.status()).toBeLessThan(500); page.waitForEvent('download'),
page.goto('/services/bulk-imports/templates/service-master.xlsx').catch(() => null),
]);
expect(await download.suggestedFilename()).toMatch(/service.*template|xlsx/i);
}); });
test('[V25-SVC-003] SVC-003 Bulk imports engagement-assignments template downloads', async ({ page }) => { test('[V25-SVC-003] SVC-003 Bulk imports engagement-assignments template downloads', async ({ page }) => {
await login(page, 'Firm Admin'); await login(page, 'Firm Admin');
const resp = await safeGoto(page, '/services/bulk-imports/templates/engagement-assignments.xlsx'); const [download] = await Promise.all([
expect(resp.status()).toBeLessThan(500); page.waitForEvent('download'),
page.goto('/services/bulk-imports/templates/engagement-assignments.xlsx').catch(() => null),
]);
expect(await download.suggestedFilename()).toMatch(/assignment|template|xlsx/i);
}); });
test('[V25-SVC-004] SVC-004 Bulk imports firm-task-templates template downloads', async ({ page }) => { test('[V25-SVC-004] SVC-004 Bulk imports firm-task-templates template downloads', async ({ page }) => {
await login(page, 'Firm Admin'); await login(page, 'Firm Admin');
const resp = await safeGoto(page, '/services/bulk-imports/templates/firm-task-templates.xlsx'); const [download] = await Promise.all([
expect(resp.status()).toBeLessThan(500); page.waitForEvent('download'),
page.goto('/services/bulk-imports/templates/firm-task-templates.xlsx').catch(() => null),
]);
expect(await download.suggestedFilename()).toMatch(/task|template|xlsx/i);
}); });
test('[V25-SVC-005] SVC-005 Bulk import preview CSRF-less POST is rejected', async ({ request }) => { test('[V25-SVC-005] SVC-005 Bulk import preview CSRF-less POST is rejected', async ({ request }) => {
+5 -3
View File
@@ -225,9 +225,11 @@ test.describe('BILL: Invoices and payments', () => {
test('[V25-BILL-016] BILL-016 Fee structure import template download works', async ({ page }) => { test('[V25-BILL-016] BILL-016 Fee structure import template download works', async ({ page }) => {
await login(page, 'Firm Admin'); await login(page, 'Firm Admin');
const resp = await safeGoto(page, '/billing/fee-structures/template'); const [download] = await Promise.all([
// should download or redirect, not crash page.waitForEvent('download'),
expect(resp.status()).toBeLessThan(500); page.goto('/billing/fee-structures/template').catch(() => null),
]);
expect(await download.suggestedFilename()).toMatch(/fee.*structure|template|xlsx/i);
}); });
}); });
+6 -2
View File
@@ -487,10 +487,14 @@ test.describe("v2.5.1 Additions - Platform Billing", () => {
} }
if (kind === 'post') { if (kind === 'post') {
const resp = await apiCall(request, method || 'POST', c.route); // Some generated VAPT rows accidentally carry _method: 'GET' even when
// the scenario is explicitly CSRF-less POST. Force POST unless the row
// intentionally specifies another unsafe verb.
const unsafeMethod = (!method || method === 'GET') ? 'POST' : method;
const resp = await apiCall(request, unsafeMethod, c.route);
await expectApiSafe(resp); await expectApiSafe(resp);
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status()); const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
expect(safe, `Unsafe POST status ${resp.status()} for ${c.route}`).toBeTruthy(); expect(safe, `Unsafe ${unsafeMethod} status ${resp.status()} for ${c.route}`).toBeTruthy();
return; return;
} }
+6 -2
View File
@@ -499,10 +499,14 @@ test.describe("v2.5.1 Additions - System Settings / Tenancy / FY", () => {
} }
if (kind === 'post') { if (kind === 'post') {
const resp = await apiCall(request, method || 'POST', c.route); // Some generated VAPT rows accidentally carry _method: 'GET' even when
// the scenario is explicitly CSRF-less POST. Force POST unless the row
// intentionally specifies another unsafe verb.
const unsafeMethod = (!method || method === 'GET') ? 'POST' : method;
const resp = await apiCall(request, unsafeMethod, c.route);
await expectApiSafe(resp); await expectApiSafe(resp);
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status()); const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
expect(safe, `Unsafe POST status ${resp.status()} for ${c.route}`).toBeTruthy(); expect(safe, `Unsafe ${unsafeMethod} status ${resp.status()} for ${c.route}`).toBeTruthy();
return; return;
} }
+3 -2
View File
@@ -126,8 +126,9 @@ test.describe('v2.0.4 additional security / FY / context checks', () => {
await page.goto('/system-settings'); await page.goto('/system-settings');
await expectNoBackendError(page); await expectNoBackendError(page);
const body = await readBody(page); const body = await readBody(page);
const selectorExists = await page.locator('select[name="active_financial_year"], select[name="financial_year"], [data-testid="active-fy-select"], text=/FY|Financial Year|Active FY/i').count(); const cssSelectorCount = await page.locator('select[name="active_financial_year"], select[name="financial_year"], [data-testid="active-fy-select"]').count();
expect(selectorExists || /financial year|active fy|fy\s*[:\-]/i.test(body)).toBeTruthy(); const textSelectorCount = await page.getByText(/FY|Financial Year|Active FY/i).count().catch(() => 0);
expect(cssSelectorCount || textSelectorCount || /financial year|active fy|fy\s*[:\-]/i.test(body)).toBeTruthy();
}); });
test('V204-FY-002 switching FY does not crash core transactional pages', async ({ page }) => { test('V204-FY-002 switching FY does not crash core transactional pages', async ({ page }) => {