The feed hears new posts as they come
decePub opens PrivaPub's streaming API (one WebSocket for the persona, its token given as the socket's protocol, never in a URL) and subscribes to the stream of the feed in view: home, a list, local, federated, a hashtag or notifications. New posts wait behind the "new" button as polled ones did; a deletion or an edit applies at once. Polling stays for the feeds PrivaPub does not stream and for any feed while the socket is down, which opens again after a pause growing to a minute. The e2e test sees a post made elsewhere in seconds and its deletion take the card away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
6d5cf17723
commit
963565b316
7 files changed
+312
-5
No files matched your search
@@ -0,0 +1,52 @@
|
||||
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;
|
||||
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')");
|
||||
await page.GotoAsync($"{Pasture.Site}/");
|
||||
await page.Locator("article[data-uri], [data-feed-empty]").First.WaitForAsync(new() { Timeout = 30000 });
|
||||
// the stream opens once the first page is shown
|
||||
await Task.Delay(1500);
|
||||
|
||||
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<string>()}']");
|
||||
await Assertions.Expect(card).ToContainTextAsync(text, new() { Timeout = 5000 });
|
||||
|
||||
await PersonaApi.Call(persona, $"/api/v1/statuses/{post["id"]!.GetValue<string>()}", 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<string>()}", HttpMethod.Delete);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user