129 lines
4.6 KiB
C#
129 lines
4.6 KiB
C#
using System.Net;
|
|
using Microsoft.AspNetCore.Components;
|
|
using System.Globalization;
|
|
using Bunit;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Prefab.Web.Client.Models.Catalog;
|
|
using Prefab.Web.Client.Models.Shared;
|
|
using Prefab.Web.Client.Pages.Browse;
|
|
using Prefab.Web.Client.Services;
|
|
using Shouldly;
|
|
|
|
namespace Prefab.Tests.Web.Client.Pages;
|
|
|
|
[Trait(TraitName.Category, TraitCategory.Unit)]
|
|
public sealed class ProductPageTests : BunitContext
|
|
{
|
|
[Fact]
|
|
public void ProductPage_Renders_Product_Details_From_Service()
|
|
{
|
|
var model = CreateSampleProduct();
|
|
Services.AddScoped<IProductDisplayService>(_ =>
|
|
new FakeProductDisplayService(Result<ProductDisplayModel>.Success(model)));
|
|
|
|
var navigation = Services.GetRequiredService<NavigationManager>();
|
|
navigation.NavigateTo(navigation.GetUriWithQueryParameter("slug", model.Slug));
|
|
|
|
var component = Render<Product>();
|
|
|
|
component.Markup.ShouldContain(model.Name);
|
|
model.Description.ShouldNotBeNull();
|
|
component.Markup.ShouldContain(model.Description!);
|
|
component.FindAll("input[type=radio]").Count.ShouldBe(2);
|
|
component.Markup.ShouldContain("Quantity");
|
|
component.FindAll(".product__add-to-cart").Count.ShouldBe(1);
|
|
component.Markup.ShouldContain("Documents");
|
|
component.Markup.ShouldContain("Spec Sheet");
|
|
component.Markup.ShouldContain("SW-001");
|
|
component.Markup.ShouldContain("Lighting");
|
|
var expectedPrice = model.BasePrice!.Value.ToString("C", CultureInfo.CurrentCulture);
|
|
component.Markup.ShouldContain(expectedPrice);
|
|
}
|
|
|
|
[Fact]
|
|
public void ProductPage_Shows_Problem_When_NotFound()
|
|
{
|
|
Services.AddScoped<IProductDisplayService>(_ =>
|
|
new FakeProductDisplayService(Result<ProductDisplayModel>.Failure(new ResultProblem
|
|
{
|
|
Title = "Product not found.",
|
|
Detail = "Product 'unknown' was not found.",
|
|
StatusCode = (int)HttpStatusCode.NotFound
|
|
})));
|
|
|
|
var navigation = Services.GetRequiredService<NavigationManager>();
|
|
navigation.NavigateTo(navigation.GetUriWithQueryParameter("slug", "unknown"));
|
|
|
|
var component = Render<Product>();
|
|
|
|
component.Markup.ShouldContain("Product not found.");
|
|
component.FindAll(".alert-warning").Count.ShouldBe(1);
|
|
}
|
|
|
|
private static ProductDisplayModel CreateSampleProduct() => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Slug = "sample-widget",
|
|
Name = "Sample Widget",
|
|
Description = "<p>Sample description.</p>",
|
|
BasePrice = 42.50m,
|
|
Sku = "SW-001",
|
|
Categories =
|
|
[
|
|
new ProductDisplayModel.CategoryRef(Guid.NewGuid(), "Lighting", "lighting")
|
|
],
|
|
Options =
|
|
[
|
|
new ProductDisplayModel.OptionDefinition(
|
|
Id: Guid.NewGuid(),
|
|
Code: "color",
|
|
Name: "Color",
|
|
DataType: 0,
|
|
IsVariantAxis: true,
|
|
Unit: null,
|
|
Min: null,
|
|
Max: null,
|
|
Step: null,
|
|
PricePerUnit: null,
|
|
Tiers: Array.Empty<ProductDisplayModel.Tier>(),
|
|
Values:
|
|
[
|
|
new ProductDisplayModel.OptionValue(Guid.NewGuid(), "red", "Red", 0m, 0),
|
|
new ProductDisplayModel.OptionValue(Guid.NewGuid(), "blue", "Blue", 0m, 0)
|
|
]),
|
|
new ProductDisplayModel.OptionDefinition(
|
|
Id: Guid.NewGuid(),
|
|
Code: "length",
|
|
Name: "Length",
|
|
DataType: 1,
|
|
IsVariantAxis: false,
|
|
Unit: "ft",
|
|
Min: 1,
|
|
Max: 10,
|
|
Step: 1,
|
|
PricePerUnit: null,
|
|
Tiers:
|
|
[
|
|
new ProductDisplayModel.Tier(1, 5, 1, null)
|
|
],
|
|
Values: Array.Empty<ProductDisplayModel.OptionValue>())
|
|
],
|
|
Specs =
|
|
[
|
|
new ProductDisplayModel.Spec("Weight", "25 lb", null, null)
|
|
],
|
|
GenericAttributes = new Dictionary<string, string>
|
|
{
|
|
["catalog.product.docs"] = """[{ "title": "Spec Sheet", "url": "https://example.com/spec.pdf" }]"""
|
|
}
|
|
};
|
|
|
|
private sealed class FakeProductDisplayService(Result<ProductDisplayModel> result) : IProductDisplayService
|
|
{
|
|
private readonly Result<ProductDisplayModel> _result = result;
|
|
|
|
public Task<Result<ProductDisplayModel>> Get(string slug, CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(_result);
|
|
}
|
|
}
|