- Add Vitest unit tests for AiChatWidget component - Add Playwright E2E tests: ai-chat-widget, api, security, guardrails - Add AI guardrails tests validating no phone/email leaks from Ollama - Add security tests: auth, rate limiting, input validation, token handling - Add API tests for AI endpoints and authentication - Fix playwright.config.ts reporter path conflict - Update CompanyJobsPage with AI generate buttons (orange icon, loading state) - Fix AiChatWidget accessibility (role=dialog, aria-label, aria-modal) - Add app.css spin animation for AI loading spinner - Add Gitea Actions workflow for nightly CI tests - Add TESTING.md documentation
118 lines
No EOL
4.4 KiB
TypeScript
118 lines
No EOL
4.4 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import AxeBuilder from "@axe-core/playwright";
|
|
|
|
async function setupAuth(page: any): Promise<boolean> {
|
|
const res = await page.request.post("http://localhost:3000/api/auth/login", {
|
|
data: {
|
|
email: "testcompany@example.com",
|
|
password: "TestPassword123!",
|
|
},
|
|
});
|
|
if (!res.ok()) return false;
|
|
const data = await res.json();
|
|
const token = data.access_token;
|
|
await page.goto("http://localhost:3000/dashboard");
|
|
await page.evaluate((t: string) => {
|
|
window.sessionStorage.setItem("nxtgauge_access_token", t);
|
|
window.sessionStorage.setItem("nxtgauge_frontend_access_token", t);
|
|
}, token);
|
|
await page.reload();
|
|
await page.waitForLoadState("networkidle");
|
|
return !page.url().includes("/login");
|
|
}
|
|
|
|
test.describe("Company Jobs Page - Authenticated", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
const loggedIn = await setupAuth(page);
|
|
if (!loggedIn) test.skip();
|
|
});
|
|
|
|
test("page loads and shows jobs section", async ({ page }) => {
|
|
const jobsHeader = page.locator("text=Jobs").first();
|
|
await expect(jobsHeader).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test("create job form opens and has AI buttons", async ({ page }) => {
|
|
const createBtn = page.locator("text=+ Create Job").first();
|
|
await expect(createBtn).toBeVisible({ timeout: 10000 });
|
|
await createBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const titleInput = page.locator('input[placeholder="Frontend Developer"]').first();
|
|
await expect(titleInput).toBeVisible();
|
|
|
|
const aiButtons = page.locator('button[title="Generate with AI"]');
|
|
const count = await aiButtons.count();
|
|
expect(count).toBeGreaterThanOrEqual(3);
|
|
});
|
|
|
|
test("create job shows error when title is empty", async ({ page }) => {
|
|
const createBtn = page.locator("text=+ Create Job").first();
|
|
await createBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.fill('input[placeholder="Bengaluru (Hybrid)"]', "Some location");
|
|
await page.fill('textarea[placeholder*="Role overview"]', "Some description");
|
|
|
|
await page.locator("text=Create Draft").first().click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const error = page.locator('text="Title, description, and location are required."');
|
|
await expect(error).toBeVisible({ timeout: 3000 });
|
|
});
|
|
|
|
test("create job shows error when description is empty", async ({ page }) => {
|
|
const createBtn = page.locator("text=+ Create Job").first();
|
|
await createBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.fill('input[placeholder="Frontend Developer"]', "Test Title");
|
|
await page.fill('input[placeholder="Bengaluru (Hybrid)"]', "Some location");
|
|
|
|
await page.locator("text=Create Draft").first().click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const error = page.locator('text="Title, description, and location are required."');
|
|
await expect(error).toBeVisible({ timeout: 3000 });
|
|
});
|
|
|
|
test("create job shows error when location is empty", async ({ page }) => {
|
|
const createBtn = page.locator("text=+ Create Job").first();
|
|
await createBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.fill('input[placeholder="Frontend Developer"]', "Test Title");
|
|
await page.fill('textarea[placeholder*="Role overview"]', "Some description");
|
|
|
|
await page.locator("text=Create Draft").first().click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const error = page.locator('text="Title, description, and location are required."');
|
|
await expect(error).toBeVisible({ timeout: 3000 });
|
|
});
|
|
|
|
test("form clears error when user starts typing required field", async ({ page }) => {
|
|
const createBtn = page.locator("text=+ Create Job").first();
|
|
await createBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.locator("text=Create Draft").first().click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const error = page.locator('text="Title, description, and location are required."');
|
|
await expect(error).toBeVisible({ timeout: 3000 });
|
|
|
|
await page.fill('input[placeholder="Frontend Developer"]', "T");
|
|
await page.waitForTimeout(200);
|
|
await expect(error).not.toBeVisible();
|
|
});
|
|
|
|
test("create job form has no accessibility violations", async ({ page }) => {
|
|
const createBtn = page.locator("text=+ Create Job").first();
|
|
await createBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
const results = await new AxeBuilder({ page }).analyze();
|
|
expect(results.violations).toEqual([]);
|
|
});
|
|
}); |