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
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using EstateManagementUI.BlazorServer.Common;
using EstateManagementUI.BlazorServer.Components;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;

namespace EstateManagementUI.BlazorServer.Tests.Common;

public sealed class BootstrapperExtensionsTests
{
[Fact]
public async Task ConfigureLiveLogin_registers_only_the_live_authentication_route()
{
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.Services.AddRazorComponents();

WebApplication app = builder.Build();
app.MapRazorComponents<App>();

app.ConfigureLiveLogin();

await app.StartAsync(TestContext.Current.CancellationToken);

var endpoints = app.Services.GetRequiredService<EndpointDataSource>().Endpoints;
var loginEndpoints = endpoints
.OfType<RouteEndpoint>()
.Where(endpoint => endpoint.RoutePattern.RawText == "/login")
.Where(endpoint => endpoint.Metadata.GetMetadata<HttpMethodMetadata>()?.HttpMethods.Contains("GET") == true)
.ToList();
var authenticationLoginEndpoints = endpoints
.OfType<RouteEndpoint>()
.Where(endpoint => endpoint.RoutePattern.RawText == "/authentication/login")
.Where(endpoint => endpoint.Metadata.GetMetadata<HttpMethodMetadata>()?.HttpMethods.Contains("GET") == true)
.ToList();

loginEndpoints.Count.ShouldBe(1);
authenticationLoginEndpoints.Count.ShouldBe(1);

await app.StopAsync(TestContext.Current.CancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
using Bunit;
using EstateManagementUI.BlazorServer.Components.Pages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using TestContext = Bunit.TestContext;

namespace EstateManagementUI.BlazorServer.Tests.Pages;

public class EntryScreenPageTests : TestContext
{
public EntryScreenPageTests()
{
Services.AddSingleton<IConfiguration>(new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["AppSettings:TestMode"] = "Disabled"
})
.Build());
}

[Fact]
public void EntryScreen_RendersCorrectly()
{
Expand Down Expand Up @@ -75,4 +87,27 @@ public void EntryScreen_HasCorrectPageTitle()
var pageTitle = cut.FindComponent<Microsoft.AspNetCore.Components.Web.PageTitle>();
pageTitle.Instance.ChildContent.ShouldNotBeNull();
}

[Fact]
public void EntryScreen_live_mode_sign_in_link_goes_directly_to_authentication()
{
var cut = RenderComponent<EntryScreen>();

cut.Find("#loginButton").GetAttribute("href").ShouldBe("/authentication/login");
}

[Fact]
public void EntryScreen_test_mode_sign_in_link_goes_to_login_page()
{
Services.AddSingleton<IConfiguration>(new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["AppSettings:TestMode"] = "BackedByTestDataStore"
})
.Build());

var cut = RenderComponent<EntryScreen>();

cut.Find("#loginButton").GetAttribute("href").ShouldBe("/login");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace EstateManagementUI.BlazorServer.Common;

public static class BoostrapperExtensions {
public static WebApplication ConfigureLiveLogin(this WebApplication app) {
app.MapGet("/login", (HttpContext context) =>
app.MapGet("/authentication/login", (HttpContext context) =>
{
return Results.Challenge(
properties: new Microsoft.AspNetCore.Authentication.AuthenticationProperties
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@page "/entry"
@using Microsoft.AspNetCore.Components.Authorization
@layout Components.Layout.EntryLayout
@inject IConfiguration Configuration

<PageTitle>Welcome - Estate Management</PageTitle>

Expand Down Expand Up @@ -131,7 +132,7 @@
<!-- Sign In Section -->
<div class="mt-12 text-center">
<p class="text-gray-600 mb-4">Sign in to access all features</p>
<a id="loginButton" href="/login" target="_top" rel="external" data-enhance-nav="false" onclick="window.location.href=this.href; return false;" class="inline-flex items-center px-8 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-md transition-colors duration-150">
<a id="loginButton" href="@SignInHref" target="_top" rel="external" data-enhance-nav="false" onclick="window.location.href=this.href; return false;" class="inline-flex items-center px-8 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-md transition-colors duration-150">
<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="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
</svg>
Expand All @@ -141,3 +142,12 @@
</div>
</div>
</div>

@code {
private string SignInHref => string.Equals(
Configuration["AppSettings:TestMode"],
"Disabled",
StringComparison.OrdinalIgnoreCase)
? "/authentication/login"
: "/login";
}
79 changes: 48 additions & 31 deletions EstateManagementUI.BlazorServer/Components/Pages/Login.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@page "/login"
@layout Components.Layout.EntryLayout
@inject IConfiguration Configuration

<PageTitle>Sign in - Estate Management</PageTitle>

Expand All @@ -11,42 +12,58 @@
<p class="mt-2 text-gray-600">Use your estate credentials to continue.</p>
</div>

<form method="post" action="/test-support/login" class="space-y-6">
<input type="hidden" name="returnUrl" value="@ReturnUrl" />

<div>
<label for="loginUsername" class="block text-sm font-medium text-gray-700 mb-2">Email address</label>
<input id="loginUsername"
name="username"
type="email"
autocomplete="username"
class="w-full rounded-md border border-gray-300 px-4 py-3 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="name@example.com"
required />
</div>

<div>
<label for="loginPassword" class="block text-sm font-medium text-gray-700 mb-2">Password</label>
<input id="loginPassword"
name="password"
type="password"
autocomplete="current-password"
class="w-full rounded-md border border-gray-300 px-4 py-3 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Password"
required />
</div>

<button id="submitLoginButton"
type="submit"
class="w-full inline-flex justify-center items-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-md transition-colors duration-150">
Sign In
</button>
</form>
@if (IsTestMode)
{
<form method="post" action="/test-support/login" class="space-y-6">
<input type="hidden" name="returnUrl" value="@ReturnUrl" />

<div>
<label for="loginUsername" class="block text-sm font-medium text-gray-700 mb-2">Email address</label>
<input id="loginUsername"
name="username"
type="email"
autocomplete="username"
class="w-full rounded-md border border-gray-300 px-4 py-3 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="name@example.com"
required />
</div>

<div>
<label for="loginPassword" class="block text-sm font-medium text-gray-700 mb-2">Password</label>
<input id="loginPassword"
name="password"
type="password"
autocomplete="current-password"
class="w-full rounded-md border border-gray-300 px-4 py-3 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Password"
required />
</div>

<button id="submitLoginButton"
type="submit"
class="w-full inline-flex justify-center items-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-md transition-colors duration-150">
Sign In
</button>
</form>
}
else
{
<a href="/authentication/login"
data-enhance-nav="false"
class="w-full inline-flex justify-center items-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-md transition-colors duration-150">
Continue to sign in
Comment thread
StuartFerguson marked this conversation as resolved.
Resolved
</a>
}
</div>
</div>
</div>

@code {
private bool IsTestMode => !string.Equals(
Configuration["AppSettings:TestMode"],
"Disabled",
StringComparison.OrdinalIgnoreCase);

[SupplyParameterFromQuery]
public string? ReturnUrl { get; set; } = "/";
}
Loading