using System.Text.Json.Nodes; using decePubClient.E2E.Support; using Microsoft.Playwright; namespace decePubClient.E2E.Actions { // The home feed hears of a new post and of its deletion through PrivaPub's streaming API, in seconds, where polling // would take up to 45 public sealed class StreamingTests(Browser browser) { [Fact] public async Task A_post_made_elsewhere_waits_behind_the_new_button_and_its_deletion_takes_it_away() { var persona = Pasture.Personas().First(); var text = $"streamed {Guid.NewGuid():N}"[..21]; await using var session = await SignIn.As(browser, persona); var page = session.Page; // the stream counts once the app has subscribed to the home feed on it (it opens after the first page is read, // later on a busy page) var subscribed = new TaskCompletionSource(); page.WebSocket += (_, socket) => { if (socket.Url.Contains("/api/v1/streaming")) socket.FrameSent += (_, frame) => { if (frame.Text?.Contains("\"subscribe\"") == true && frame.Text.Contains("\"user\"")) subscribed.TrySetResult(); }; }; JsonNode post = null; try { await Results.Cell("client.render.stream", "privapub", persona.UserName, async () => { await page.GotoAsync($"{Pasture.Site}/"); await page.EvaluateAsync("() => localStorage.removeItem('FeedFilter')"); subscribed = new(); await page.GotoAsync($"{Pasture.Site}/"); await page.Locator("article[data-uri], [data-feed-empty]").First.WaitForAsync(new() { Timeout = 30000 }); await subscribed.Task.WaitAsync(TimeSpan.FromSeconds(30)); // (and the server has taken the subscription in) await Task.Delay(500); post = await PersonaApi.Call(persona, "/api/v1/statuses", HttpMethod.Post, new JsonObject { ["status"] = text, ["visibility"] = "unlisted" }); var fresh = page.GetByRole(AriaRole.Button, new() { Name = "1 new" }); await fresh.WaitForAsync(new() { Timeout = 10000 }); await fresh.ClickAsync(); var card = page.Locator($"article[data-uri='{post!["uri"]!.GetValue()}']"); await Assertions.Expect(card).ToContainTextAsync(text, new() { Timeout = 5000 }); await PersonaApi.Call(persona, $"/api/v1/statuses/{post["id"]!.GetValue()}", HttpMethod.Delete); post = null; await Assertions.Expect(card).ToHaveCountAsync(0, new() { Timeout = 10000 }); await session.AssertGuards(); }, page); } finally { if (post is not null) await PersonaApi.Call(persona, $"/api/v1/statuses/{post["id"]!.GetValue()}", HttpMethod.Delete); } } } }