Files
mimic/e2e/tests/us28-templates-nav.spec.ts

137 lines
4.4 KiB
TypeScript
Raw Normal View History

/**
* US-28 Admin/redteam access templates from the nav.
* Covers AC-28.1 (Templates link in topbar), AC-28.2 (ProtectedRoute SOC redirect),
* AC-28.3 (page is always edit-capable, no read-only mode).
*/
import { test, expect } from '@playwright/test';
import {
adminToken,
deleteUserByUsername,
ensureUser,
login,
} from '../fixtures/api';
import { seedTokenInStorage } from '../fixtures/auth';
const REDTEAM_USER = 'us28-redteam';
const SOC_USER = 'us28-soc';
const PASS = 'us28-pass-strong';
test.describe('US-28 — templates nav', () => {
let redteamToken: string;
let socToken: string;
let adminTok: string;
test.beforeAll(async () => {
adminTok = await adminToken();
await ensureUser(REDTEAM_USER, PASS, 'redteam');
await ensureUser(SOC_USER, PASS, 'soc');
redteamToken = (await login(REDTEAM_USER, PASS)).token;
socToken = (await login(SOC_USER, PASS)).token;
});
test.afterAll(async () => {
try {
const tok = await adminToken();
for (const u of [REDTEAM_USER, SOC_USER]) await deleteUserByUsername(tok, u);
} catch { /* noop */ }
});
// AC-28.1 — Templates nav link visible to admin + redteam
test('AC-28.1 — redteam sees "Templates" link in topbar nav', async ({
page,
context,
}) => {
await seedTokenInStorage(context, redteamToken);
await page.goto('/engagements');
const link = page.getByRole('link', { name: /^templates$/i });
await expect(link).toBeVisible();
await expect(link).toHaveAttribute('href', '/admin/templates');
});
test('AC-28.1 — admin sees "Templates" link in topbar nav', async ({
page,
context,
}) => {
await seedTokenInStorage(context, adminTok);
await page.goto('/engagements');
await expect(page.getByRole('link', { name: /^templates$/i })).toBeVisible();
});
test('AC-28.1 — SOC does NOT see "Templates" link in topbar nav', async ({
page,
context,
}) => {
await seedTokenInStorage(context, socToken);
await page.goto('/engagements');
// Wait for page to fully load before asserting absence
await page.waitForLoadState('networkidle');
await expect(page.getByRole('link', { name: /^templates$/i })).not.toBeVisible();
});
test('AC-28.1 — clicking Templates link navigates to /admin/templates', async ({
page,
context,
}) => {
await seedTokenInStorage(context, redteamToken);
await page.goto('/engagements');
await page.getByRole('link', { name: /^templates$/i }).click();
await expect(page).toHaveURL(/\/admin\/templates/);
});
// AC-28.2 — SOC typing /admin/templates URL directly → redirected
test('AC-28.2 — SOC direct URL /admin/templates → redirected to /engagements', async ({
page,
context,
}) => {
await seedTokenInStorage(context, socToken);
await page.goto('/admin/templates');
await expect(page).toHaveURL(/\/engagements/, { timeout: 5_000 });
});
test('AC-28.2 — SOC direct URL /admin/templates/new → redirected to /engagements', async ({
page,
context,
}) => {
await seedTokenInStorage(context, socToken);
await page.goto('/admin/templates/new');
await expect(page).toHaveURL(/\/engagements/, { timeout: 5_000 });
});
// AC-28.3 — Page assumes canEdit=true (form inputs are never disabled for admin/redteam)
test('AC-28.3 — TemplateFormPage for redteam: name input is editable (no read-only mode)', async ({
page,
context,
}) => {
await seedTokenInStorage(context, redteamToken);
await page.goto('/admin/templates/new');
const nameInput = page.getByLabel(/name/i).first();
await expect(nameInput).toBeVisible();
await expect(nameInput).toBeEnabled();
// Should be able to type
await nameInput.fill('AC-28.3 editability test');
await expect(nameInput).toHaveValue('AC-28.3 editability test');
});
test('AC-28.3 — admin has same edit access on /admin/templates', async ({
page,
context,
}) => {
await seedTokenInStorage(context, adminTok);
await page.goto('/admin/templates');
// Page loads (not redirected)
await expect(page).toHaveURL(/\/admin\/templates/);
await expect(page.getByRole('heading', { name: 'Templates', exact: true })).toBeVisible({ timeout: 5_000 });
// "New" link in header when templates exist, "New template" in empty state — either is fine
await expect(page.getByRole('link', { name: /new( template)?/i }).first()).toBeVisible();
});
});