Files
TS_Proto/e2e/tests/auth.spec.ts
T
nilesh.pandClaude Opus 4.8 4d3eb18a6c Initial commit: TravelHub multi-tenant flight booking platform
Angular + Go + PostgreSQL sample with strict per-tenant isolation.

- Backend: Go API (basic auth, tenant-scoped repository layer, pgx pool)
- Frontend: Angular 22 SPA (search, checkout, bookings, dashboard)
- Database: schema, seed data and demo bookings migrations
- Tests: 311 across Go (integration), Angular (unit) and Playwright (e2e),
  plus a generated evidence report with PDF export
- Security: whitebox VAPT report under security/

Secrets (.env, tokens, DB credentials) are git-ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-13 11:38:01 +05:30

156 lines
5.3 KiB
TypeScript

import { expect, test } from '../fixtures/test';
import { GLOBETROTTER_AGENT, SKYLINE_ADMIN, SKYLINE_AGENT } from '../fixtures/accounts';
test.describe('Sign in', () => {
test('offers every active agency in the picker', async ({ login }) => {
await login.goto();
// One placeholder option plus the seeded agencies.
await expect(login.tenantSelect.locator('option')).toHaveCount(3);
await expect(login.tenantSelect.locator('option')).toContainText([
'Select an agency',
'Globetrotter Tours',
'Skyline Travel',
]);
});
test('a demo shortcut fills the whole form', async ({ login }) => {
await login.goto();
await login.useDemoChip(SKYLINE_AGENT);
await expect(login.tenantSelect).toHaveValue('skyline');
await expect(login.email).toHaveValue(SKYLINE_AGENT.email);
await expect(login.password).toHaveValue(SKYLINE_AGENT.password);
});
test('signs an agent into their own agency', async ({ login, shell, page, evidence }) => {
await login.goto();
await evidence.step('Sign-in screen with the agency picker', {
agenciesOffered: (await login.tenantSelect.locator('option').count()) - 1,
demoShortcuts: await login.demoChips.count(),
});
await login.signIn(SKYLINE_AGENT);
await expect(page).toHaveURL(/\/dashboard$/);
await expect(shell.agencyName).toHaveText(SKYLINE_AGENT.tenantName);
await expect(shell.accountName).toHaveText(SKYLINE_AGENT.fullName);
await expect(shell.accountMeta).toContainText(SKYLINE_AGENT.email);
await evidence.step('Signed in to the agency console', {
agency: SKYLINE_AGENT.tenantName,
user: SKYLINE_AGENT.fullName,
email: SKYLINE_AGENT.email,
role: SKYLINE_AGENT.role,
landedOn: '/dashboard',
});
});
test('signs an admin in and shows the admin role', async ({ login, shell }) => {
await login.signIn(SKYLINE_ADMIN);
await expect(shell.accountName).toHaveText(SKYLINE_ADMIN.fullName);
await expect(shell.accountMeta).toContainText('admin');
});
test('rejects a wrong password', async ({ login }) => {
await login.goto();
const message = await login.signInExpectingFailure(SKYLINE_AGENT, 'definitely-wrong');
expect(message).toContain('not valid for this agency');
await expect(login.submit).toBeVisible();
});
// The central multi-tenant rule, exercised through the UI.
test('rejects valid credentials aimed at the wrong agency', async ({ login }) => {
await login.goto();
await login.tenantSelect.selectOption(GLOBETROTTER_AGENT.tenant);
await login.email.fill(SKYLINE_AGENT.email);
await login.password.fill(SKYLINE_AGENT.password);
await login.submit.click();
await expect(login.error).toContainText('not valid for this agency');
await expect(login.submit).toBeVisible();
});
test('will not submit without an agency', async ({ login, page }) => {
await login.goto();
await login.email.fill(SKYLINE_AGENT.email);
await login.password.fill(SKYLINE_AGENT.password);
await login.submit.click();
await expect(page.locator('.field .error')).toContainText('choose an agency');
await expect(page).toHaveURL(/\/login/);
});
test('rejects a malformed email before contacting the server', async ({ login, page }) => {
await login.goto();
await login.tenantSelect.selectOption(SKYLINE_AGENT.tenant);
await login.email.fill('not-an-email');
await login.password.fill('whatever');
await login.submit.click();
await expect(page.locator('.field .error')).toContainText('valid email');
});
});
test.describe('Session', () => {
test('survives a page reload', async ({ login, shell, page }) => {
await login.signIn(SKYLINE_AGENT);
await page.reload();
await expect(page).toHaveURL(/\/dashboard$/);
await expect(shell.agencyName).toHaveText(SKYLINE_AGENT.tenantName);
});
test('sends an anonymous visitor to sign-in and back again', async ({ login, page }) => {
await page.goto('/bookings');
// The guard should bounce to the sign-in screen and remember the target.
await expect(page).toHaveURL(/\/login\?returnTo=%2Fbookings/);
await login.useDemoChip(SKYLINE_AGENT);
await login.submit.click();
await expect(page).toHaveURL(/\/bookings$/);
});
test('signing out clears the session for good', async ({ login, shell, page }) => {
await login.signIn(SKYLINE_AGENT);
await shell.signOutAndWait();
await expect(page).toHaveURL(/\/login$/);
// Going back must not resurrect the console.
await page.goto('/dashboard');
await expect(page).toHaveURL(/\/login/);
});
test('an already signed-in user is moved off the sign-in screen', async ({ login, page }) => {
await login.signIn(SKYLINE_AGENT);
await page.goto('/login');
await expect(page).toHaveURL(/\/dashboard$/);
});
test('stale stored credentials are discarded', async ({ login, page }) => {
await login.signIn(SKYLINE_AGENT);
// Corrupt the stored password the way a changed account would.
await page.evaluate(() => {
localStorage.setItem(
'travelhub.credentials',
JSON.stringify({ tenant: 'skyline', basic: btoa('agent@skyline.test:stale') }),
);
});
await page.goto('/dashboard');
await expect(page).toHaveURL(/\/login/);
const stored = await page.evaluate(() => localStorage.getItem('travelhub.credentials'));
expect(stored).toBeNull();
});
});