using System.Text.Json.Nodes; using decePubClient.E2E.Support; using Microsoft.Playwright; namespace decePubClient.E2E.Actions { // Direct conversations: a message from another persona badges the Direct tab and its card says unread; marking it // read and taking it off the list are checked on PrivaPub public sealed class ConversationTests(Browser browser) { [Fact] public async Task A_new_message_is_unread_until_marked_and_leaves_the_list_when_removed() { var personas = Pasture.Personas(); Assert.SkipWhen(personas.Count < 2, "the town has fewer than two personas"); var (reader, writer) = (personas[0], personas[1]); var message = (await PersonaApi.Call(writer, "/api/v1/statuses", HttpMethod.Post, new JsonObject { ["status"] = $"@{reader.UserName} just between us {Guid.NewGuid():N}"[..40], ["visibility"] = "direct" }))!; var uri = message["uri"]!.GetValue(); async Task Conversation() => (await PersonaApi.Call(reader, "/api/v1/conversations?limit=40"))!.AsArray() .FirstOrDefault(c => c!["last_status"]?["uri"]?.GetValue() == uri); await using var session = await SignIn.As(browser, reader); var page = session.Page; try { await Results.Cell("client.action.conversation", "privapub", reader.UserName, async () => { Assert.True(await Pasture.Eventually(async () => (await Conversation())?["unread"]?.GetValue() == true, 15), "the message never made an unread conversation"); await page.GotoAsync($"{Pasture.Site}/"); await page.EvaluateAsync("() => localStorage.removeItem('FeedFilter')"); await page.GotoAsync($"{Pasture.Site}/"); await page.Locator("[data-unread-conversations]").WaitForAsync(new() { Timeout = 30000 }); await page.GetByRole(AriaRole.Tab).Filter(new() { Has = page.Locator(".ion-md-mail") }).ClickAsync(); var card = page.Locator($"article[data-uri='{uri}']"); await card.Locator("[data-conversation-unread]").WaitForAsync(new() { Timeout = 30000 }); await card.Locator("[data-conversation-read]").ClickAsync(); await Assertions.Expect(card.Locator("[data-conversation-unread]")).ToHaveCountAsync(0, new() { Timeout = 10000 }); Assert.False((await Conversation())!["unread"]!.GetValue()); await card.Locator("[data-conversation-remove]").ClickAsync(); await Assertions.Expect(card).ToHaveCountAsync(0, new() { Timeout = 10000 }); Assert.Null(await Conversation()); await session.AssertGuards(); }, page); } finally { await PersonaApi.Call(writer, $"/api/v1/statuses/{message["id"]!.GetValue()}", HttpMethod.Delete); } } } }