diff --git a/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor b/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor index 104d7ec8..96363130 100644 --- a/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor +++ b/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor @@ -1,5 +1,3 @@ -@inject IJSRuntime JS -
@foreach (var message in Messages) @@ -17,25 +15,3 @@ }
- -@code { - [Parameter] - public required IEnumerable Messages { get; set; } - - [Parameter] - public ChatMessage? InProgressMessage { get; set; } - - [Parameter] - public RenderFragment? NoMessagesContent { get; set; } - - private bool IsEmpty => !Messages.Any(m => (m.Role == ChatRole.User || m.Role == ChatRole.Assistant) && !string.IsNullOrEmpty(m.Text)); - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - // Activates the auto-scrolling behavior - await JS.InvokeVoidAsync("import", "./Components/Pages/Chat/ChatMessageList.razor.js"); - } - } -} diff --git a/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor.cs b/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor.cs new file mode 100644 index 00000000..1fcf88f1 --- /dev/null +++ b/src/OpenChat.PlaygroundApp/Components/Pages/Chat/ChatMessageList.razor.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.AI; +using Microsoft.JSInterop; + +namespace OpenChat.PlaygroundApp.Components.Pages.Chat; + +public partial class ChatMessageList : ComponentBase +{ + [Inject] + public IJSRuntime? JS { get; set; } + + [Parameter] + public required IEnumerable Messages { get; set; } + + [Parameter] + public ChatMessage? InProgressMessage { get; set; } + + [Parameter] + public RenderFragment? NoMessagesContent { get; set; } + + private bool IsEmpty => !Messages.Any(m => (m.Role == ChatRole.User || m.Role == ChatRole.Assistant) && !string.IsNullOrEmpty(m.Text)); + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JS!.InvokeVoidAsync("import", "./Components/Pages/Chat/ChatMessageList.razor.js"); + } + } +} \ No newline at end of file diff --git a/test/OpenChat.PlaygroundApp.Tests/Components/Pages/Chat/ChatMessageListUITests.cs b/test/OpenChat.PlaygroundApp.Tests/Components/Pages/Chat/ChatMessageListUITests.cs new file mode 100644 index 00000000..6d4fcde8 --- /dev/null +++ b/test/OpenChat.PlaygroundApp.Tests/Components/Pages/Chat/ChatMessageListUITests.cs @@ -0,0 +1,129 @@ +using Microsoft.Playwright; +using Microsoft.Playwright.Xunit; + +namespace OpenChat.PlaygroundApp.Tests.Components.Pages.Chat; + +public class ChatMessageListUITests : PageTest +{ + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + await Page.GotoAsync(TestConstants.LocalhostUrl); + await Page.WaitForLoadStateAsync(LoadState.NetworkIdle); + } + + [Trait("Category", "IntegrationTest")] + [Theory] + [InlineData("To get started, try asking about anything.")] + public async Task Given_EmptyState_When_Page_Loads_Then_NoMessages_Content_Should_Be_Visible(string expectedText) + { + // Act + var noMessagesElement = Page.Locator(".no-messages"); + + // Assert + var isVisible = await noMessagesElement.IsVisibleAsync(); + isVisible.ShouldBeTrue(); + + var content = await noMessagesElement.InnerTextAsync(); + content.ShouldContain(expectedText); + } + + [Trait("Category", "IntegrationTest")] + [Theory] + [InlineData("Hello, how are you?")] + public async Task Given_MessagesExist_When_Rendered_Then_MessageList_Should_Display_Messages(string userMessage) + { + // Arrange + var textArea = Page.GetByRole(AriaRole.Textbox, new() { Name = "User Message Textarea" }); + await textArea.FillAsync(userMessage); + await textArea.PressAsync("Enter"); + await Page.Locator(".user-message").First.WaitForAsync(new() { State = WaitForSelectorState.Attached }); + + // Act + var messageListContainer = Page.Locator(".message-list-container"); + var messageListContent = await messageListContainer.InnerTextAsync(); + + // Assert + messageListContent.ShouldContain(userMessage); + } + + [Trait("Category", "IntegrationTest")] + [Fact] + public async Task Given_InitialState_When_NoUserInteraction_Then_InProgressMessage_Should_Be_Null() + { + // Act + var chatMessages = Page.Locator("chat-messages"); + var inProgressAttribute = await chatMessages.GetAttributeAsync("in-progress"); + + // Assert + inProgressAttribute.ShouldBeNull(); + } + + [Trait("Category", "IntegrationTest")] + [Theory] + [InlineData("Test message for container")] + [InlineData("Another message for verification")] + public async Task Given_BasicTextInput_When_NoMessageSending_Then_ContainerStructure_And_InputFunction_Should_Work(string userMessage) + { + // Arrange + var textArea = Page.GetByRole(AriaRole.Textbox, new() { Name = "User Message Textarea" }); + var messageContainer = Page.Locator(".message-list-container"); + + // Act + await textArea.FillAsync(userMessage); + var containerExists = await messageContainer.IsVisibleAsync(); + var inputValue = await textArea.InputValueAsync(); + + // Assert + containerExists.ShouldBeTrue(); + inputValue.ShouldBe(userMessage); + } + + [Trait("Category", "IntegrationTest")] + [Theory] + [InlineData(".message-list-container")] + [InlineData("chat-messages")] + public async Task Given_ChatMessageList_When_Rendered_Then_Should_Display_Container_Structure(string containerSelector) + { + // Act + var container = Page.Locator(containerSelector); + var containerExists = await container.IsVisibleAsync(); + + // Assert + containerExists.ShouldBeTrue(); + } + + [Trait("Category", "IntegrationTest")] + [Theory] + [InlineData("To get started, try asking about anything.")] + public async Task Given_EmptyState_When_NoMessages_Then_NoMessagesContent_Should_Be_Visible(string expectedText) + { + // Act + var noMessagesElement = Page.Locator(".no-messages"); + var isVisible = await noMessagesElement.IsVisibleAsync(); + var content = await noMessagesElement.InnerTextAsync(); + + // Assert + isVisible.ShouldBeTrue(); + content.ShouldContain(expectedText); + } + + [Trait("Category", "IntegrationTest")] + [Theory] + [InlineData("User Message Textarea")] + public async Task Given_EmptyState_When_NoMessages_Then_InputField_Should_Be_Visible(string inputFieldName) + { + // Act + var textArea = Page.GetByRole(AriaRole.Textbox, new() { Name = inputFieldName }); + var inputExists = await textArea.IsVisibleAsync(); + + // Assert + inputExists.ShouldBeTrue(); + } + + public override async Task DisposeAsync() + { + await Page.CloseAsync(); + await base.DisposeAsync(); + } +} \ No newline at end of file