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(); }); });