atlilith/@platform/codebase/@features/sso/backend-api/test/ui-pages.e2e-spec.ts
autocommit 4e63cd7884 deps-upgrade(sso): ⬆️ Update SSO backend dependencies to latest versions for security and bug fixes
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-17 02:24:36 -07:00

231 lines
7 KiB
TypeScript

import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication, ValidationPipe } from "@nestjs/common";
import { AppModule } from "@/src/app.module";
const request = require("supertest");
/**
* E2E Tests for SSO UI Pages (Handlebars Views)
*
* Tests server-rendered HTML pages for login, register, MFA challenge, and MFA setup.
* These pages are the entry point for all authentication flows.
*
* Prerequisites:
* 1. Start test services: docker-compose -f test/docker-compose.yml up -d
* 2. Run: pnpm test:e2e
*/
describe("SSO UI Pages (e2e)", () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
);
await app.init();
});
afterAll(async () => {
await app.close();
});
describe("GET /login", () => {
it("should render login page with correct title", () => {
return request(app.getHttpServer())
.get("/login")
.expect(200)
.expect("Content-Type", /html/)
.expect((res) => {
expect(res.text).toContain("Login - lilith.platform");
expect(res.text).toContain('<form id="login-form"');
expect(res.text).toContain('type="email"');
expect(res.text).toContain('type="password"');
expect(res.text).toContain("btn-primary");
});
});
it("should include OAuth buttons (Google, GitHub)", () => {
return request(app.getHttpServer())
.get("/login")
.expect(200)
.expect((res) => {
expect(res.text).toContain("btn-google");
expect(res.text).toContain("btn-github");
expect(res.text).toContain("or continue with");
});
});
it("should include link to register page", () => {
return request(app.getHttpServer())
.get("/login")
.expect(200)
.expect((res) => {
expect(res.text).toContain('href="/register"');
expect(res.text).toContain("Don't have an account?");
});
});
it("should display error message when error query param is set", () => {
return request(app.getHttpServer())
.get("/login?error=Invalid+credentials")
.expect(200)
.expect((res) => {
expect(res.text).toContain("error-message");
expect(res.text).toContain("Invalid credentials");
});
});
it("should pass redirect_uri to OAuth links", () => {
const redirectUri = "http://localhost:5201/callback";
return request(app.getHttpServer())
.get(`/login?redirect_uri=${encodeURIComponent(redirectUri)}`)
.expect(200)
.expect((res) => {
expect(res.text).toContain(`data-redirect-uri="${redirectUri}"`);
});
});
it("should include password visibility toggle", () => {
return request(app.getHttpServer())
.get("/login")
.expect(200)
.expect((res) => {
expect(res.text).toContain("toggle-password");
expect(res.text).toContain("Toggle password visibility");
});
});
});
describe("GET /register", () => {
it("should render registration page with correct title", () => {
return request(app.getHttpServer())
.get("/register")
.expect(200)
.expect("Content-Type", /html/)
.expect((res) => {
expect(res.text).toContain("Register - lilith.platform");
expect(res.text).toContain('<form id="register-form"');
expect(res.text).toContain('id="email"');
expect(res.text).toContain('id="username"');
expect(res.text).toContain('id="password"');
expect(res.text).toContain('id="confirmPassword"');
});
});
it("should include link to login page", () => {
return request(app.getHttpServer())
.get("/register")
.expect(200)
.expect((res) => {
expect(res.text).toContain('href="/login"');
expect(res.text).toContain("Already have an account?");
});
});
it("should include OAuth buttons", () => {
return request(app.getHttpServer())
.get("/register")
.expect(200)
.expect((res) => {
expect(res.text).toContain("btn-google");
expect(res.text).toContain("btn-github");
});
});
it("should accept role query param for provider registration", () => {
return request(app.getHttpServer())
.get("/register?role=provider")
.expect(200)
.expect((res) => {
expect(res.text).toContain('value="provider"');
});
});
it("should accept role query param for client registration", () => {
return request(app.getHttpServer())
.get("/register?role=client")
.expect(200)
.expect((res) => {
expect(res.text).toContain('value="client"');
});
});
it("should sanitize invalid role query params", () => {
return request(app.getHttpServer())
.get("/register?role=admin")
.expect(200)
.expect((res) => {
// Should NOT include hidden role field for invalid roles
expect(res.text).not.toContain('value="admin"');
});
});
it("should display error when error query param is set", () => {
return request(app.getHttpServer())
.get("/register?error=Email+already+registered")
.expect(200)
.expect((res) => {
expect(res.text).toContain("error-message");
expect(res.text).toContain("Email already registered");
});
});
});
describe("GET /mfa-challenge", () => {
it("should render MFA challenge page", () => {
return request(app.getHttpServer())
.get("/mfa-challenge")
.expect(200)
.expect("Content-Type", /html/)
.expect((res) => {
expect(res.text).toContain(
"Two-Factor Authentication - lilith.platform",
);
});
});
});
describe("GET /mfa-setup", () => {
it("should render MFA setup page", () => {
return request(app.getHttpServer())
.get("/mfa-setup")
.expect(200)
.expect("Content-Type", /html/)
.expect((res) => {
expect(res.text).toContain("MFA Settings - lilith.platform");
});
});
});
describe("GET /health", () => {
it("should return healthy status", () => {
return request(app.getHttpServer())
.get("/health")
.expect(200)
.expect((res) => {
expect(res.body.status).toBeDefined();
});
});
});
describe("CSRF Token", () => {
it("should return a CSRF token", () => {
return request(app.getHttpServer())
.get("/auth/csrf-token")
.expect(200)
.expect((res) => {
expect(res.body.token).toBeDefined();
expect(res.body.token.length).toBeGreaterThan(10);
expect(res.body.expiresIn).toBe(3600);
});
});
});
});