using Microsoft.Playwright; using Xunit; using FluentAssertions; using LittleShop.Tests.Infrastructure; namespace LittleShop.Tests.UI; [Collection("Playwright")] public class AdminPanelTests : IClassFixture, IAsyncLifetime { private readonly TestWebApplicationFactory _factory; private IPlaywright? _playwright; private IBrowser? _browser; private IBrowserContext? _context; private IPage? _page; private readonly string _baseUrl; public AdminPanelTests(TestWebApplicationFactory factory) { _factory = factory; _baseUrl = "https://localhost:5001"; // Adjust based on your test configuration } public async Task InitializeAsync() { _playwright = await Playwright.CreateAsync(); _browser = await _playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true // Set to false for debugging }); _context = await _browser.NewContextAsync(new BrowserNewContextOptions { IgnoreHTTPSErrors = true // For self-signed certificates in test }); _page = await _context.NewPageAsync(); } public async Task DisposeAsync() { if (_page != null) await _page.CloseAsync(); if (_context != null) await _context.CloseAsync(); if (_browser != null) await _browser.CloseAsync(); _playwright?.Dispose(); } [Fact] public async Task LoginPage_ShouldLoadWithoutErrors() { // Arrange & Act var response = await _page!.GotoAsync($"{_baseUrl}/Admin/Account/Login"); // Assert response.Should().NotBeNull(); response!.Status.Should().Be(200); // Check for login form elements var usernameInput = await _page.QuerySelectorAsync("input[name='Username']"); var passwordInput = await _page.QuerySelectorAsync("input[name='Password']"); var submitButton = await _page.QuerySelectorAsync("button[type='submit']"); usernameInput.Should().NotBeNull(); passwordInput.Should().NotBeNull(); submitButton.Should().NotBeNull(); } [Fact] public async Task Login_WithValidCredentials_ShouldRedirectToDashboard() { // Arrange await _page!.GotoAsync($"{_baseUrl}/Admin/Account/Login"); // Act await _page.FillAsync("input[name='Username']", "admin"); await _page.FillAsync("input[name='Password']", "admin"); await _page.ClickAsync("button[type='submit']"); // Wait for navigation await _page.WaitForURLAsync($"{_baseUrl}/Admin/Dashboard"); // Assert _page.Url.Should().Contain("/Admin/Dashboard"); // Check dashboard loaded var dashboardTitle = await _page.TextContentAsync("h1"); dashboardTitle.Should().Contain("Dashboard"); } [Fact] public async Task Login_WithInvalidCredentials_ShouldShowError() { // Arrange await _page!.GotoAsync($"{_baseUrl}/Admin/Account/Login"); // Act await _page.FillAsync("input[name='Username']", "wronguser"); await _page.FillAsync("input[name='Password']", "wrongpass"); await _page.ClickAsync("button[type='submit']"); // Assert // Should stay on login page _page.Url.Should().Contain("/Admin/Account/Login"); // Check for error message var errorMessage = await _page.QuerySelectorAsync(".text-danger, .alert-danger"); errorMessage.Should().NotBeNull(); } [Fact] public async Task AdminPages_WithoutLogin_ShouldRedirectToLogin() { // Arrange & Act var pagesToTest = new[] { "/Admin/Dashboard", "/Admin/Categories", "/Admin/Products", "/Admin/Orders", "/Admin/Users" }; foreach (var pageUrl in pagesToTest) { await _page!.GotoAsync($"{_baseUrl}{pageUrl}"); // Assert - Should redirect to login _page.Url.Should().Contain("/Admin/Account/Login"); } } [Fact] public async Task Dashboard_AfterLogin_ShouldLoadAllSections() { // Arrange - Login first await LoginAsAdmin(); // Act await _page!.GotoAsync($"{_baseUrl}/Admin/Dashboard"); // Assert var response = await _page.GotoAsync($"{_baseUrl}/Admin/Dashboard"); response!.Status.Should().Be(200); // Check for dashboard sections var statsCards = await _page.QuerySelectorAllAsync(".card"); statsCards.Count.Should().BeGreaterThan(0); // Check navigation menu is present var navMenu = await _page.QuerySelectorAsync(".navbar"); navMenu.Should().NotBeNull(); } [Fact] public async Task Categories_CRUD_ShouldWorkWithoutErrors() { // Arrange - Login first await LoginAsAdmin(); // Navigate to Categories await _page!.GotoAsync($"{_baseUrl}/Admin/Categories"); var response = await _page.GotoAsync($"{_baseUrl}/Admin/Categories"); response!.Status.Should().Be(200); // Create Category await _page.ClickAsync("a[href*='Categories/Create']"); await _page.WaitForURLAsync("**/Admin/Categories/Create"); await _page.FillAsync("input[name='Name']", "Test Category"); await _page.FillAsync("textarea[name='Description']", "Test Description"); await _page.ClickAsync("button[type='submit']"); // Should redirect back to index await _page.WaitForURLAsync("**/Admin/Categories"); // Verify category appears in list var categoryName = await _page.TextContentAsync("td:has-text('Test Category')"); categoryName.Should().NotBeNull(); } [Fact] public async Task Products_Index_ShouldLoadWithoutErrors() { // Arrange - Login first await LoginAsAdmin(); // Act var response = await _page!.GotoAsync($"{_baseUrl}/Admin/Products"); // Assert response!.Status.Should().Be(200); // Check for products table or empty message var productsTable = await _page.QuerySelectorAsync("table"); var emptyMessage = await _page.QuerySelectorAsync("text=No products found"); (productsTable != null || emptyMessage != null).Should().BeTrue(); } [Fact] public async Task Orders_Index_ShouldLoadWithoutErrors() { // Arrange - Login first await LoginAsAdmin(); // Act var response = await _page!.GotoAsync($"{_baseUrl}/Admin/Orders"); // Assert response!.Status.Should().Be(200); // Check for orders table or empty message var ordersTable = await _page.QuerySelectorAsync("table"); var emptyMessage = await _page.QuerySelectorAsync("text=No orders found"); (ordersTable != null || emptyMessage != null).Should().BeTrue(); } [Fact] public async Task Users_Index_ShouldLoadWithoutErrors() { // Arrange - Login first await LoginAsAdmin(); // Act var response = await _page!.GotoAsync($"{_baseUrl}/Admin/Users"); // Assert response!.Status.Should().Be(200); // Should at least show the admin user var usersTable = await _page.QuerySelectorAsync("table"); usersTable.Should().NotBeNull(); var adminUser = await _page.TextContentAsync("td:has-text('admin')"); adminUser.Should().NotBeNull(); } [Fact] public async Task Logout_ShouldRedirectToLogin() { // Arrange - Login first await LoginAsAdmin(); // Act - Find and click logout await _page!.ClickAsync("a[href*='Account/Logout']"); // Assert await _page.WaitForURLAsync("**/Admin/Account/Login"); _page.Url.Should().Contain("/Admin/Account/Login"); } [Fact] public async Task NetworkErrors_ShouldBeCaptured() { // Arrange var failedRequests = new List(); _page!.RequestFailed += (_, request) => { failedRequests.Add($"{request.Method} {request.Url} - {request.Failure}"); }; await LoginAsAdmin(); // Act - Navigate through admin pages var pagesToCheck = new[] { "/Admin/Dashboard", "/Admin/Categories", "/Admin/Products", "/Admin/Orders", "/Admin/Users" }; foreach (var pageUrl in pagesToCheck) { await _page.GotoAsync($"{_baseUrl}{pageUrl}"); } // Assert - No failed requests failedRequests.Should().BeEmpty("No network requests should fail"); } [Fact] public async Task ConsoleErrors_ShouldBeCaptured() { // Arrange var consoleErrors = new List(); _page!.Console += (_, msg) => { if (msg.Type == "error") { consoleErrors.Add(msg.Text); } }; await LoginAsAdmin(); // Act - Navigate through admin pages var pagesToCheck = new[] { "/Admin/Dashboard", "/Admin/Categories", "/Admin/Products" }; foreach (var pageUrl in pagesToCheck) { await _page.GotoAsync($"{_baseUrl}{pageUrl}"); } // Assert - No console errors consoleErrors.Should().BeEmpty("No JavaScript errors should occur"); } private async Task LoginAsAdmin() { await _page!.GotoAsync($"{_baseUrl}/Admin/Account/Login"); await _page.FillAsync("input[name='Username']", "admin"); await _page.FillAsync("input[name='Password']", "admin"); await _page.ClickAsync("button[type='submit']"); await _page.WaitForURLAsync($"{_baseUrl}/Admin/Dashboard"); } }