251 lines
8.6 KiB
C#
251 lines
8.6 KiB
C#
using Microsoft.Playwright;
|
||
|
||
namespace Enciphered.Blazor.UIComponents.Tests;
|
||
|
||
[TestFixture]
|
||
public class CardTests : PlaywrightTestBase
|
||
{
|
||
// ── Helpers ──────────────────────────────────────────────────────────────
|
||
|
||
private async Task GoToCardsAsync()
|
||
{
|
||
await Page.GotoAsync($"{BaseUrl}/cards", new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });
|
||
await Page.WaitForSelectorAsync("[data-testid='card-basic']", new PageWaitForSelectorOptions { Timeout = 10_000 });
|
||
}
|
||
|
||
private ILocator Card(string testId) => Page.Locator($"[data-testid='{testId}']");
|
||
|
||
// ────────────────────────────────────────────
|
||
// Basic card – structure
|
||
// ────────────────────────────────────────────
|
||
|
||
[Test]
|
||
public async Task BasicCard_Is_Visible()
|
||
{
|
||
await GoToCardsAsync();
|
||
await Expect(Card("card-basic")).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task BasicCard_Has_Rounded_Border_Classes()
|
||
{
|
||
await GoToCardsAsync();
|
||
var card = Card("card-basic");
|
||
await Expect(card).ToHaveClassAsync(new System.Text.RegularExpressions.Regex("rounded-xl"));
|
||
await Expect(card).ToHaveClassAsync(new System.Text.RegularExpressions.Regex("border"));
|
||
}
|
||
|
||
[Test]
|
||
public async Task BasicCard_Renders_Title()
|
||
{
|
||
await GoToCardsAsync();
|
||
var title = Card("card-basic").Locator("h3");
|
||
await Expect(title).ToBeVisibleAsync();
|
||
await Expect(title).ToHaveTextAsync("Card Title");
|
||
}
|
||
|
||
[Test]
|
||
public async Task BasicCard_Renders_Description()
|
||
{
|
||
await GoToCardsAsync();
|
||
var desc = Card("card-basic").Locator("[data-slot='card-description']");
|
||
await Expect(desc).ToBeVisibleAsync();
|
||
await Expect(desc).ToHaveTextAsync("Card Description");
|
||
}
|
||
|
||
[Test]
|
||
public async Task BasicCard_Renders_Content()
|
||
{
|
||
await GoToCardsAsync();
|
||
var content = Card("card-basic").Locator("p:has-text('Card Content')");
|
||
await Expect(content).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task BasicCard_Renders_Footer()
|
||
{
|
||
await GoToCardsAsync();
|
||
var footer = Card("card-basic").Locator("p:has-text('Card Footer')");
|
||
await Expect(footer).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task BasicCard_Description_Appears_Below_Title()
|
||
{
|
||
await GoToCardsAsync();
|
||
var title = Card("card-basic").Locator("h3");
|
||
var desc = Card("card-basic").Locator("[data-slot='card-description']");
|
||
|
||
var titleBox = await title.BoundingBoxAsync();
|
||
var descBox = await desc.BoundingBoxAsync();
|
||
|
||
Assert.That(titleBox, Is.Not.Null, "Title bounding box should exist");
|
||
Assert.That(descBox, Is.Not.Null, "Description bounding box should exist");
|
||
Assert.That(descBox!.Y, Is.GreaterThan(titleBox!.Y), "Description should be positioned below the title");
|
||
}
|
||
|
||
// ────────────────────────────────────────────
|
||
// Login card – header with action
|
||
// ────────────────────────────────────────────
|
||
|
||
[Test]
|
||
public async Task LoginCard_Is_Visible()
|
||
{
|
||
await GoToCardsAsync();
|
||
await Expect(Card("card-login")).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Renders_Title()
|
||
{
|
||
await GoToCardsAsync();
|
||
var title = Card("card-login").Locator("h3");
|
||
await Expect(title).ToHaveTextAsync("Login to your account");
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Renders_Action()
|
||
{
|
||
await GoToCardsAsync();
|
||
var action = Card("card-login").Locator("[data-slot='card-action']");
|
||
await Expect(action).ToBeVisibleAsync();
|
||
await Expect(action).ToContainTextAsync("Sign Up");
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Action_Is_Right_Of_Title()
|
||
{
|
||
await GoToCardsAsync();
|
||
var title = Card("card-login").Locator("h3");
|
||
var action = Card("card-login").Locator("[data-slot='card-action']");
|
||
|
||
var titleBox = await title.BoundingBoxAsync();
|
||
var actionBox = await action.BoundingBoxAsync();
|
||
|
||
Assert.That(titleBox, Is.Not.Null);
|
||
Assert.That(actionBox, Is.Not.Null);
|
||
Assert.That(actionBox!.X, Is.GreaterThan(titleBox!.X), "Action should be to the right of the title");
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Renders_Description()
|
||
{
|
||
await GoToCardsAsync();
|
||
var desc = Card("card-login").Locator("[data-slot='card-description']");
|
||
await Expect(desc).ToContainTextAsync("Enter your email below");
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Has_Email_Input()
|
||
{
|
||
await GoToCardsAsync();
|
||
var email = Card("card-login").Locator("input#email");
|
||
await Expect(email).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Has_Password_Input()
|
||
{
|
||
await GoToCardsAsync();
|
||
var password = Card("card-login").Locator("input#password");
|
||
await Expect(password).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Has_Login_Button()
|
||
{
|
||
await GoToCardsAsync();
|
||
var btn = Card("card-login").Locator("button:has-text('Login')").First;
|
||
await Expect(btn).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task LoginCard_Has_Google_Button()
|
||
{
|
||
await GoToCardsAsync();
|
||
var btn = Card("card-login").Locator("button:has-text('Login with Google')");
|
||
await Expect(btn).ToBeVisibleAsync();
|
||
}
|
||
|
||
// ────────────────────────────────────────────
|
||
// Image card
|
||
// ────────────────────────────────────────────
|
||
|
||
[Test]
|
||
public async Task ImageCard_Is_Visible()
|
||
{
|
||
await GoToCardsAsync();
|
||
await Expect(Card("card-image")).ToBeVisibleAsync();
|
||
}
|
||
|
||
[Test]
|
||
public async Task ImageCard_Renders_Image()
|
||
{
|
||
await GoToCardsAsync();
|
||
var img = Card("card-image").Locator("img");
|
||
await Expect(img).ToBeVisibleAsync();
|
||
await Expect(img).ToHaveAttributeAsync("alt", "Event cover");
|
||
}
|
||
|
||
[Test]
|
||
public async Task ImageCard_Image_Appears_Above_Title()
|
||
{
|
||
await GoToCardsAsync();
|
||
var img = Card("card-image").Locator("img");
|
||
var title = Card("card-image").Locator("h3");
|
||
|
||
var imgBox = await img.BoundingBoxAsync();
|
||
var titleBox = await title.BoundingBoxAsync();
|
||
|
||
Assert.That(imgBox, Is.Not.Null);
|
||
Assert.That(titleBox, Is.Not.Null);
|
||
Assert.That(titleBox!.Y, Is.GreaterThan(imgBox!.Y), "Title should be below the image");
|
||
}
|
||
|
||
[Test]
|
||
public async Task ImageCard_Renders_Title()
|
||
{
|
||
await GoToCardsAsync();
|
||
var title = Card("card-image").Locator("h3");
|
||
await Expect(title).ToHaveTextAsync("Design systems meetup");
|
||
}
|
||
|
||
[Test]
|
||
public async Task ImageCard_Renders_Featured_Badge()
|
||
{
|
||
await GoToCardsAsync();
|
||
var badge = Card("card-image").Locator("[data-slot='card-action']");
|
||
await Expect(badge).ToContainTextAsync("Featured");
|
||
}
|
||
|
||
[Test]
|
||
public async Task ImageCard_Renders_Description()
|
||
{
|
||
await GoToCardsAsync();
|
||
var desc = Card("card-image").Locator("[data-slot='card-description']");
|
||
await Expect(desc).ToContainTextAsync("A practical talk on component APIs");
|
||
}
|
||
|
||
[Test]
|
||
public async Task ImageCard_Has_View_Event_Button()
|
||
{
|
||
await GoToCardsAsync();
|
||
var btn = Card("card-image").Locator("button:has-text('View Event')");
|
||
await Expect(btn).ToBeVisibleAsync();
|
||
}
|
||
|
||
// ────────────────────────────────────────────
|
||
// All three cards render on the page
|
||
// ────────────────────────────────────────────
|
||
|
||
[Test]
|
||
public async Task CardsPage_Renders_Three_Cards()
|
||
{
|
||
await GoToCardsAsync();
|
||
|
||
await Expect(Card("card-basic")).ToBeVisibleAsync();
|
||
await Expect(Card("card-login")).ToBeVisibleAsync();
|
||
await Expect(Card("card-image")).ToBeVisibleAsync();
|
||
}
|
||
}
|