Files
prefab-page-detail/Prefab.Tests/Web.Client/Components/Catalog/ProductCardShould.cs
2025-10-27 21:39:50 -04:00

129 lines
4.1 KiB
C#

using System.Globalization;
using Bunit;
using Prefab.Web.Client.Components.Catalog;
using Prefab.Web.Client.Models.Shared;
using Prefab.Web.Client.ViewModels.Catalog;
using Shouldly;
namespace Prefab.Tests.Web.Client.Components.Catalog;
[Trait(TraitName.Category, TraitCategory.Unit)]
public sealed class ProductCardShould : BunitContext
{
[Fact]
public void RenderSaleBadgeOldPriceAndRating()
{
var model = CreateModel(m =>
{
m.IsOnSale = true;
m.OldPrice = 419m;
m.Rating = 4;
m.ReviewCount = 15;
m.Badges.Add("New");
});
var cut = Render<ProductCard>(parameters =>
{
parameters.Add(p => p.Product, model);
parameters.Add(p => p.ShowPrice, true);
});
var root = cut.Find($".{TemplateCss.ProductCardRoot}");
root.ClassList.ShouldContain(TemplateCss.ProductCardGridModifier);
cut.FindAll($".{TemplateCss.ProductCardBadgeSale}").Count.ShouldBe(1);
cut.FindAll($".{TemplateCss.ProductCardBadge}").Count.ShouldBe(2);
var newPrice = cut.Find($".{TemplateCss.ProductCardPriceNew}");
var expectedNewPrice = model.FromPrice?.Amount ?? throw new InvalidOperationException("Expected from price to be set.");
newPrice.TextContent.Trim().ShouldBe(expectedNewPrice.ToString("C", CultureInfo.CurrentCulture));
var oldPrice = cut.Find($".{TemplateCss.ProductCardPriceOld}");
var expectedOldPrice = model.OldPrice ?? throw new InvalidOperationException("Expected old price to be set for sale items.");
oldPrice.TextContent.Trim().ShouldBe(expectedOldPrice.ToString("C", CultureInfo.CurrentCulture));
var rating = cut.Find($".{TemplateCss.ProductCardRating}");
var ratingLabel = rating.QuerySelector($".{TemplateCss.Rating}")!;
ratingLabel.GetAttribute("aria-label").ShouldBe("4 out of 5");
}
[Fact]
public void NotRenderSaleBadgeOrOldPriceWhenAbsent()
{
var model = CreateModel(m =>
{
m.IsOnSale = false;
m.OldPrice = null;
m.Rating = 0;
m.ReviewCount = 0;
});
var cut = Render<ProductCard>(parameters =>
{
parameters.Add(p => p.Product, model);
parameters.Add(p => p.ShowPrice, true);
});
cut.FindAll($".{TemplateCss.ProductCardBadgeSale}").ShouldBeEmpty();
cut.Markup.ShouldNotContain(TemplateCss.ProductCardPriceOld);
cut.Markup.ShouldNotContain(TemplateCss.ProductCardRating);
}
[Fact]
public void NotRenderPricesByDefault()
{
var model = CreateModel();
var cut = Render<ProductCard>(parameters =>
{
parameters.Add(p => p.Product, model);
// ShowPrice is false by default
});
cut.Markup.ShouldNotContain(TemplateCss.ProductCardPrice);
}
[Fact]
public void NotRenderActionButtons()
{
var model = CreateModel();
var cut = Render<ProductCard>(parameters =>
{
parameters.Add(p => p.Product, model);
});
cut.Markup.ShouldNotContain("product-card__actions");
cut.Markup.ShouldNotContain("product-card__buttons");
}
private static ProductCardModel CreateModel(Action<ProductCardModel>? configure = null)
{
var model = new ProductCardModel
{
Id = Guid.NewGuid(),
Title = "Aluminum Chandelier",
Url = "/browse/product?slug=aluminum-chandelier",
Slug = "aluminum-chandelier",
CategoryName = "Chandeliers",
CategoryUrl = "/catalog/chandeliers",
PrimaryImageUrl = "/images/products/product1.jpg",
FromPrice = new MoneyModel
{
Amount = 321.54m,
Currency = "USD"
},
IsPriced = true,
Rating = 3,
ReviewCount = 2,
Sku = "SKU-1001",
IsOnSale = true,
OldPrice = 399.99m,
LastModifiedOn = DateTimeOffset.UtcNow
};
configure?.Invoke(model);
return model;
}
}