littleshop/LittleShop.Tests/UI/AdminPanelTests.cs
2025-09-29 08:03:36 +01:00

322 lines
9.8 KiB
C#

using Microsoft.Playwright;
using Xunit;
using FluentAssertions;
using LittleShop.Tests.Infrastructure;
namespace LittleShop.Tests.UI;
[Collection("Playwright")]
public class AdminPanelTests : IClassFixture<TestWebApplicationFactory>, 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 = _factory.Server.BaseAddress.ToString().TrimEnd('/'); // Use the test server's URL
}
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<string>();
_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<string>();
_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");
}
}