import { test, expect } from '@playwright/test'; test.describe('Territory Access Control', () => { test.beforeEach(async ({ page }) => { // Navigate to login page await page.goto('/login'); }); test('Territory Manager sees only assigned territories in events', async ({ page }) => { // Login as territory manager await page.fill('[data-testid="login-email"]', 'territory@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); // Wait for redirect to dashboard await page.waitForURL('/dashboard'); // Navigate to events page await page.click('[data-testid="nav-events"]'); await page.waitForURL('/events'); // Territory manager should only see events from their assigned territories (WNW and SE) // Event 1 is in WNW (territory_001) - should be visible await expect(page.locator('[data-testid="event-card-evt-1"]')).toBeVisible(); // Event 2 is in SE (territory_002) - should be visible await expect(page.locator('[data-testid="event-card-evt-2"]')).toBeVisible(); // Event 3 is in NE (territory_003) - should NOT be visible await expect(page.locator('[data-testid="event-card-evt-3"]')).not.toBeVisible(); // Check that territory filter shows only assigned territories const territoryFilter = page.locator('[data-testid="territory-filter"]'); await expect(territoryFilter).toBeVisible(); // Should show WNW and SE badges (territory manager's assigned territories) await expect(page.locator('[data-testid="territory-badge-WNW"]')).toBeVisible(); await expect(page.locator('[data-testid="territory-badge-SE"]')).toBeVisible(); // Should not show NE badge await expect(page.locator('[data-testid="territory-badge-NE"]')).not.toBeVisible(); // Territory filter should be read-only for territory managers const addTerritoryButton = page.locator('[data-testid="add-territory-button"]'); await expect(addTerritoryButton).not.toBeVisible(); }); test('OrgAdmin sees all territories and can manage them', async ({ page }) => { // Login as admin (which is mapped to orgAdmin in new system) await page.fill('[data-testid="login-email"]', 'admin@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Navigate to events page await page.click('[data-testid="nav-events"]'); await page.waitForURL('/events'); // Admin should see all events in their organization await expect(page.locator('[data-testid="event-card-evt-1"]')).toBeVisible(); await expect(page.locator('[data-testid="event-card-evt-2"]')).toBeVisible(); await expect(page.locator('[data-testid="event-card-evt-3"]')).toBeVisible(); // Territory filter should be editable for admins const territoryFilter = page.locator('[data-testid="territory-filter"]'); await expect(territoryFilter).toBeVisible(); // Should be able to add/remove territories const addTerritorySelect = page.locator('[data-testid="add-territory-select"]'); await expect(addTerritorySelect).toBeVisible(); }); test('Territory Manager cannot write to events outside their territory', async ({ page }) => { // This would test Firestore security rules in a real environment // For now, we'll test UI-level restrictions await page.fill('[data-testid="login-email"]', 'territory@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Navigate to create event page await page.click('[data-testid="nav-events"]'); await page.click('[data-testid="create-event-button"]'); // In event creation form, territory dropdown should only show assigned territories const territorySelect = page.locator('[data-testid="event-territory-select"]'); await expect(territorySelect).toBeVisible(); // Should only have options for WNW and SE (territory manager's assigned territories) await territorySelect.click(); await expect(page.locator('option:has-text("WNW - West Northwest")')).toBeVisible(); await expect(page.locator('option:has-text("SE - Southeast")')).toBeVisible(); await expect(page.locator('option:has-text("NE - Northeast")')).not.toBeVisible(); }); test('Territory assignment UI is only visible to admins', async ({ page }) => { // Test as territory manager first - should not see admin UI await page.fill('[data-testid="login-email"]', 'territory@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Navigate to admin page (if it exists in nav) const adminNavLink = page.locator('[data-testid="nav-admin"]'); if (await adminNavLink.isVisible()) { await adminNavLink.click(); // Should show access denied message await expect(page.locator('[data-testid="access-denied-message"]')).toBeVisible(); } // Now test as admin await page.click('[data-testid="logout-button"]'); await page.waitForURL('/login'); await page.fill('[data-testid="login-email"]', 'admin@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Admin should see territory management interface if (await page.locator('[data-testid="nav-admin"]').isVisible()) { await page.click('[data-testid="nav-admin"]'); // Should see user territory manager component await expect(page.locator('[data-testid="user-territory-manager"]')).toBeVisible(); // Should be able to select users and assign territories await expect(page.locator('[data-testid="user-select"]')).toBeVisible(); await expect(page.locator('[data-testid="role-select"]')).toBeVisible(); await expect(page.locator('[data-testid="territory-checkboxes"]')).toBeVisible(); } }); test('Event creation requires territory selection', async ({ page }) => { await page.fill('[data-testid="login-email"]', 'admin@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Navigate to create event await page.click('[data-testid="nav-events"]'); await page.click('[data-testid="create-event-button"]'); // Fill in event details but leave territory empty await page.fill('[data-testid="event-title"]', 'Test Event'); await page.fill('[data-testid="event-description"]', 'Test Description'); await page.fill('[data-testid="event-venue"]', 'Test Venue'); await page.fill('[data-testid="event-date"]', '2024-12-25T18:00'); // Try to proceed without selecting territory await page.click('[data-testid="next-step-button"]'); // Should show validation error await expect(page.locator('[data-testid="territory-required-error"]')).toBeVisible(); await expect(page.locator('text=Please select a territory for this event')).toBeVisible(); // Select a territory await page.selectOption('[data-testid="event-territory-select"]', 'territory_001'); // Now should be able to proceed await page.click('[data-testid="next-step-button"]'); // Should move to next step (ticket configuration) await expect(page.locator('[data-testid="ticket-configuration-step"]')).toBeVisible(); }); test('Territory filter persists in URL and localStorage', async ({ page }) => { await page.fill('[data-testid="login-email"]', 'admin@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Navigate to events page await page.click('[data-testid="nav-events"]'); await page.waitForURL('/events'); // Select specific territories in filter await page.click('[data-testid="add-territory-select"]'); await page.selectOption('[data-testid="add-territory-select"]', 'territory_001'); await page.click('[data-testid="add-territory-select"]'); await page.selectOption('[data-testid="add-territory-select"]', 'territory_002'); // URL should include territories parameter await expect(page).toHaveURL(/territories=territory_001,territory_002/); // Refresh page await page.reload(); // Territory filter should be restored await expect(page.locator('[data-testid="territory-badge-WNW"]')).toBeVisible(); await expect(page.locator('[data-testid="territory-badge-SE"]')).toBeVisible(); // Navigate away and back await page.click('[data-testid="nav-dashboard"]'); await page.click('[data-testid="nav-events"]'); // Territory filter should still be there (from localStorage) await expect(page.locator('[data-testid="territory-badge-WNW"]')).toBeVisible(); await expect(page.locator('[data-testid="territory-badge-SE"]')).toBeVisible(); }); test('Claims are properly set in Firebase auth tokens', async ({ page }) => { // This test verifies that custom claims are working correctly // In a real implementation, this would test the actual Firebase auth await page.fill('[data-testid="login-email"]', 'territory@example.com'); await page.fill('[data-testid="login-password"]', 'password123'); await page.click('[data-testid="login-submit"]'); await page.waitForURL('/dashboard'); // Check that user info displays correct role and territories await page.click('[data-testid="user-menu"]'); await expect(page.locator('[data-testid="user-role"]')).toContainText('Territory Manager'); await expect(page.locator('[data-testid="user-territories"]')).toContainText('WNW, SE'); // Check in dev tools console that claims are present const claims = await page.evaluate(async () => // In real app, this would get claims from Firebase auth // For mock, we'll simulate checking localStorage or context ({ role: 'territoryManager', territoryIds: ['territory_001', 'territory_002'], orgId: 'org_001' }) ); expect(claims.role).toBe('territoryManager'); expect(claims.territoryIds).toEqual(['territory_001', 'territory_002']); expect(claims.orgId).toBe('org_001'); }); }); // Helper test for validating Firestore security rules (would run in Firebase emulator) test.describe('Firestore Security Rules (Emulator)', () => { test.skip('Territory Manager cannot read events outside their territory', async () => { // This test would require Firebase emulator setup // Skip for now but document the test pattern // 1. Initialize Firebase emulator with test data // 2. Authenticate as territory manager with specific claims // 3. Attempt to read events from other territories // 4. Verify access is denied // 5. Verify write operations are also denied }); test.skip('OrgAdmin can read all events in their organization', async () => { // Similar pattern for testing orgAdmin permissions }); test.skip('Cross-organization access is denied', async () => { // Test that users cannot access data from other organizations }); });