Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 62 additions & 5 deletions EstateManagementUI.BlazorServer.Tests/Pages/ErrorPageTests.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
using Bunit;
using EstateManagementUI.BlazorServer.Components.Pages;
using EstateManagementUI.BlazorServer.Tests.Pages.FileProcessing;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Moq;
using Shouldly;

namespace EstateManagementUI.BlazorServer.Tests.Pages;

public class ErrorPageTests : TestContext
public class ErrorPageTests : BaseTest
{
public ErrorPageTests() :base() {
Mock<IWebHostEnvironment> _mockWebHostEnvironment = new();
_mockWebHostEnvironment.Setup(x => x.EnvironmentName).Returns(Environments.Development);

Services.AddSingleton(_mockWebHostEnvironment.Object);

// Use a real IConfiguration with an in-memory value so GetSection/GetValue work as expected
var inMemorySettings = new Dictionary<string, string?>
{
{ "AppSettings:SupportEmail", "[email protected]" }
};
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(inMemorySettings)
.Build();

Services.AddSingleton<IConfiguration>(configuration);

}

[Fact]
public void Error_RendersCorrectly()
{
// Act
var cut = RenderComponent<Error>();

// Assert
cut.Find("h1").TextContent.ShouldBe("Error.");
cut.Find("h2").TextContent.ShouldBe("An error occurred while processing your request.");
cut.Find("h1").TextContent.ShouldContain("Oops! Something went wrong");
cut.Markup.ShouldContain("We encountered an unexpected error");
}

[Fact]
Expand All @@ -24,7 +49,7 @@ public void Error_WithoutRequestId_DoesNotShowRequestId()
var cut = RenderComponent<Error>();

// Assert
cut.FindAll("strong").ShouldNotContain(e => e.TextContent.Contains("Request ID:"));
cut.Markup.ShouldNotContain("Reference ID for support:");
}

[Fact]
Expand All @@ -34,7 +59,7 @@ public void Error_ShowsDevelopmentModeInformation()
var cut = RenderComponent<Error>();

// Assert
cut.Markup.ShouldContain("Development Mode");
cut.Markup.ShouldContain("Development Mode Information");
cut.Markup.ShouldContain("ASPNETCORE_ENVIRONMENT");
}

Expand All @@ -48,4 +73,36 @@ public void Error_HasCorrectPageTitle()
var pageTitle = cut.FindComponent<Microsoft.AspNetCore.Components.Web.PageTitle>();
pageTitle.Instance.ChildContent.ShouldNotBeNull();
}

[Fact]
public void Error_HasHomePageLink()
{
// Act
var cut = RenderComponent<Error>();

// Assert
var homeLink = cut.Find("a[href='/']");
homeLink.TextContent.ShouldContain("Go to Home");
}

[Fact]
public void Error_HasBackButton()
{
// Act
var cut = RenderComponent<Error>();

// Assert
var backButton = cut.Find("button[onclick='window.history.back()']");
backButton.TextContent.ShouldContain("Go Back");
}

[Fact]
public void Error_HasSupportEmail()
{
// Act
var cut = RenderComponent<Error>();

// Assert
cut.Markup.ShouldContain("[email protected]");
}
}
119 changes: 100 additions & 19 deletions EstateManagementUI.BlazorServer/Components/Pages/Error.razor
Original file line number Diff line number Diff line change
@@ -1,36 +1,117 @@
@page "/Error"
@using System.Diagnostics
@inject IConfiguration Configuration
@inject IWebHostEnvironment Environment

<PageTitle>Error</PageTitle>

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<div class="flex items-center justify-center min-h-[60vh]">
<div class="max-w-2xl w-full mx-auto px-4">
<div class="card shadow-admin-lg">
<div class="card-body text-center py-12">
<!-- Error Icon -->
<div class="mb-6">
<div class="inline-flex items-center justify-center w-24 h-24 rounded-full bg-red-100">
<svg class="w-16 h-16 text-admin-danger" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
</div>

@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<!-- Error Title -->
<h1 class="text-3xl font-bold text-gray-900 mb-3">Oops! Something went wrong</h1>

<!-- Error Message -->
<p class="text-lg text-gray-600 mb-6">
We encountered an unexpected error while processing your request. Please try again or contact support if the problem persists.
</p>

@if (ShowRequestId)
{
<div class="mb-6 p-4 bg-gray-50 rounded-lg border border-gray-200">
<p class="text-sm text-gray-600 mb-1">Reference ID for support:</p>
<code class="text-sm font-mono text-gray-800 bg-white px-3 py-1 rounded border border-gray-300">@RequestId</code>
</div>
}

<!-- Action Buttons -->
<div class="flex flex-col sm:flex-row gap-3 justify-center mt-8">
<a href="/" class="btn btn-primary">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
</svg>
Go to Home
</a>
<button onclick="window.history.back()" class="btn btn-secondary">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
Go Back
</button>
</div>

<!-- Help Text -->
<div class="mt-8 pt-6 border-t border-gray-200">
<p class="text-sm text-gray-500">
Need help? Contact our support team at
<a href="mailto:@SupportEmail" class="text-admin-primary hover:underline">@SupportEmail</a>
</p>
</div>
</div>
</div>

<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
<!-- Development Mode Info (collapsed by default) -->
@if (ShowDevelopmentInfo)
{
<div class="mt-6 card">
<div class="card-header">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-gray-700 flex items-center">
<svg class="w-5 h-5 mr-2 text-admin-warning" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
</svg>
Development Mode Information
</h3>
</div>
</div>
<div class="card-body">
<div class="text-sm text-gray-600 space-y-3">
<p>
<strong>Note:</strong> This information is only visible in development mode.
</p>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about errors.
</p>
<p class="text-admin-danger">
<strong>Warning:</strong> The Development environment shouldn't be enabled for deployed applications.
It can result in displaying sensitive information from exceptions to end users.
</p>
<p>
For local debugging, enable the <strong>Development</strong> environment by setting the
<code class="bg-gray-100 px-2 py-1 rounded text-xs">ASPNETCORE_ENVIRONMENT</code>
environment variable to <code class="bg-gray-100 px-2 py-1 rounded text-xs">Development</code>
and restarting the app.
</p>
</div>
</div>
</div>
}
</div>
</div>

@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }

private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private bool ShowDevelopmentInfo { get; set; }
private string SupportEmail { get; set; } = "[email protected]";

protected override void OnInitialized() =>
protected override void OnInitialized()
{
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
ShowDevelopmentInfo = Environment.IsDevelopment();
SupportEmail = Configuration.GetValue<string>("AppSettings:SupportEmail", "[email protected]") ?? "[email protected]";
}
}
Loading