using Microsoft.Playwright; namespace ScadaLink.CentralUI.PlaywrightTests; /// /// Verifies that navigation sections and links are shown/hidden based on the user's role. /// /// LDAP test users (all passwords: "password"): /// admin → Admin only /// designer → Design only /// deployer → Deployment only /// multi-role → Admin + Design + Deployment /// /// Nav structure (from NavMenu.razor): /// All authenticated: Dashboard, Health Dashboard /// Admin: LDAP Mappings, Sites, API Keys, SMTP Configuration, Audit Log /// Design: Templates, Shared Scripts, Connections, External Systems /// Deployment: Topology, Deployments, Debug View, Event Logs, Parked Messages /// [Collection("Playwright")] public class RoleNavigationTests { private readonly PlaywrightFixture _fixture; public RoleNavigationTests(PlaywrightFixture fixture) { _fixture = fixture; } // ── Admin-only user ───────────────────────────────────────────── [Fact] public async Task AdminUser_SeesAdminSection() { var page = await _fixture.NewAuthenticatedPageAsync("admin", "password"); await AssertNavLinkVisible(page, "LDAP Mappings"); await AssertNavLinkVisible(page, "Sites"); await AssertNavLinkVisible(page, "API Keys"); await AssertNavLinkVisible(page, "SMTP Configuration"); await AssertNavLinkVisible(page, "Audit Log"); } [Fact] public async Task AdminUser_DoesNotSeeDesignSection() { var page = await _fixture.NewAuthenticatedPageAsync("admin", "password"); await AssertNavLinkHidden(page, "Templates"); await AssertNavLinkHidden(page, "Shared Scripts"); await AssertNavLinkHidden(page, "Connections"); await AssertNavLinkHidden(page, "External Systems"); } [Fact] public async Task AdminUser_DoesNotSeeDeploymentSection() { var page = await _fixture.NewAuthenticatedPageAsync("admin", "password"); await AssertNavLinkHidden(page, "Topology"); await AssertNavLinkHidden(page, "Deployments"); await AssertNavLinkHidden(page, "Debug View"); } [Fact] public async Task AdminUser_SeesHealthDashboard_NotDeploymentMonitoring() { var page = await _fixture.NewAuthenticatedPageAsync("admin", "password"); // Health Dashboard is all-roles; Event Logs and Parked Messages are // Deployment-role only (NavMenu.razor / Component-CentralUI). await AssertNavLinkVisible(page, "Health Dashboard"); await AssertNavLinkHidden(page, "Event Logs"); await AssertNavLinkHidden(page, "Parked Messages"); } // ── Design-only user ──────────────────────────────────────────── [Fact] public async Task DesignUser_SeesDesignSection() { var page = await _fixture.NewAuthenticatedPageAsync("designer", "password"); await AssertNavLinkVisible(page, "Templates"); await AssertNavLinkVisible(page, "Shared Scripts"); await AssertNavLinkVisible(page, "Connections"); await AssertNavLinkVisible(page, "External Systems"); } [Fact] public async Task DesignUser_DoesNotSeeAdminSection() { var page = await _fixture.NewAuthenticatedPageAsync("designer", "password"); await AssertNavLinkHidden(page, "LDAP Mappings"); await AssertNavLinkHidden(page, "Sites"); await AssertNavLinkHidden(page, "API Keys"); await AssertNavLinkHidden(page, "SMTP Configuration"); await AssertNavLinkHidden(page, "Audit Log"); } [Fact] public async Task DesignUser_DoesNotSeeDeploymentSection() { var page = await _fixture.NewAuthenticatedPageAsync("designer", "password"); await AssertNavLinkHidden(page, "Topology"); await AssertNavLinkHidden(page, "Deployments"); await AssertNavLinkHidden(page, "Debug View"); } [Fact] public async Task DesignUser_SeesHealthDashboard_NotDeploymentMonitoringOrAudit() { var page = await _fixture.NewAuthenticatedPageAsync("designer", "password"); // A Design-only user sees the all-roles Health Dashboard but not the // Deployment-gated Event Logs / Parked Messages, nor the Admin Audit Log. await AssertNavLinkVisible(page, "Health Dashboard"); await AssertNavLinkHidden(page, "Event Logs"); await AssertNavLinkHidden(page, "Parked Messages"); await AssertNavLinkHidden(page, "Audit Log"); } // ── Deployment-only user ──────────────────────────────────────── [Fact] public async Task DeploymentUser_SeesDeploymentSection() { var page = await _fixture.NewAuthenticatedPageAsync("deployer", "password"); await AssertNavLinkVisible(page, "Topology"); await AssertNavLinkVisible(page, "Deployments"); await AssertNavLinkVisible(page, "Debug View"); } [Fact] public async Task DeploymentUser_DoesNotSeeAdminSection() { var page = await _fixture.NewAuthenticatedPageAsync("deployer", "password"); await AssertNavLinkHidden(page, "LDAP Mappings"); await AssertNavLinkHidden(page, "Sites"); await AssertNavLinkHidden(page, "API Keys"); await AssertNavLinkHidden(page, "SMTP Configuration"); await AssertNavLinkHidden(page, "Audit Log"); } [Fact] public async Task DeploymentUser_DoesNotSeeDesignSection() { var page = await _fixture.NewAuthenticatedPageAsync("deployer", "password"); await AssertNavLinkHidden(page, "Templates"); await AssertNavLinkHidden(page, "Shared Scripts"); await AssertNavLinkHidden(page, "Connections"); await AssertNavLinkHidden(page, "External Systems"); } [Fact] public async Task DeploymentUser_SeesMonitoringButNotConfigurationAuditLog() { var page = await _fixture.NewAuthenticatedPageAsync("deployer", "password"); // Event Logs and Parked Messages are Deployment-role gated, so a // Deployment user sees them; Configuration Audit Log is Admin-only. await AssertNavLinkVisible(page, "Health Dashboard"); await AssertNavLinkVisible(page, "Event Logs"); await AssertNavLinkVisible(page, "Parked Messages"); await AssertNavLinkHidden(page, "Configuration Audit Log"); } // ── Multi-role user (Admin + Design + Deployment) ─────────────── [Fact] public async Task MultiRoleUser_SeesAllSections() { var page = await _fixture.NewAuthenticatedPageAsync("multi-role", "password"); // Admin await AssertNavLinkVisible(page, "LDAP Mappings"); await AssertNavLinkVisible(page, "Sites"); await AssertNavLinkVisible(page, "API Keys"); await AssertNavLinkVisible(page, "SMTP Configuration"); await AssertNavLinkVisible(page, "Audit Log"); // Design await AssertNavLinkVisible(page, "Templates"); await AssertNavLinkVisible(page, "Shared Scripts"); await AssertNavLinkVisible(page, "Connections"); await AssertNavLinkVisible(page, "External Systems"); // Deployment await AssertNavLinkVisible(page, "Topology"); await AssertNavLinkVisible(page, "Deployments"); await AssertNavLinkVisible(page, "Debug View"); // Monitoring await AssertNavLinkVisible(page, "Health Dashboard"); await AssertNavLinkVisible(page, "Event Logs"); await AssertNavLinkVisible(page, "Parked Messages"); } // ── All users see Dashboard ───────────────────────────────────── [Theory] [InlineData("admin")] [InlineData("designer")] [InlineData("deployer")] [InlineData("multi-role")] public async Task AllUsers_SeeDashboardLink(string username) { var page = await _fixture.NewAuthenticatedPageAsync(username, "password"); var dashboardLink = page.GetByRole(AriaRole.Link, new() { Name = "Dashboard", Exact = true }); await Assertions.Expect(dashboardLink).ToBeVisibleAsync(); } // ── Helpers ───────────────────────────────────────────────────── private static async Task AssertNavLinkVisible(IPage page, string linkText) { // Sections are collapsed by default — expand them so a present link is // in the DOM. Idempotent: already-expanded sections are skipped. await PlaywrightFixture.ExpandAllNavSectionsAsync(page); var locator = page.Locator($"nav a:has-text('{linkText}')"); var count = await locator.CountAsync(); Assert.True(count > 0, $"Expected nav link '{linkText}' to be visible, but it was not found"); } private static async Task AssertNavLinkHidden(IPage page, string linkText) { var locator = page.Locator($"nav a:has-text('{linkText}')"); var count = await locator.CountAsync(); Assert.True(count == 0, $"Expected nav link '{linkText}' to be hidden, but it was found"); } }