using System.Text.Json.Nodes; using decePubClient.E2E.Support; using Microsoft.Playwright; namespace decePubClient.E2E.Actions { // A persona reports a remote account from its profile, with a category, its words and a copy to the account's server: // PrivaPub's moderators see it, forwarded (/clientapi/moderator/reports). And who liked or boosted a post is listed // from its card public sealed class ReportTests(Browser browser) { [Fact] public async Task A_remote_account_is_reported_from_its_profile() { var persona = Pasture.Personas().First(); var target = (await PersonaApi.Call(persona, "/api/v2/search?q=mastouser%40mastodon.test&resolve=true&type=accounts"))?["accounts"]?.AsArray().FirstOrDefault(); Assert.SkipWhen(target is null, "no Mastodon: run tools/pasture/run.sh add mastodon"); var targetId = target!["id"]!.GetValue(); var words = $"reported by the e2e tests {Guid.NewGuid():N}"[..40]; await using var session = await SignIn.As(browser, persona); var page = session.Page; await Results.Cell("client.action.report", "mastodon", persona.UserName, async () => { await page.GotoAsync($"{Pasture.Site}/account/{Uri.EscapeDataString(targetId)}"); // (the profile's own button: each of its posts' cards has one too) await page.Locator($"article[data-account-id='{targetId}'] button[data-report]").ClickAsync(new() { Timeout = 30000 }); var dialog = page.Locator("[data-report-dialog]"); await dialog.Locator("[data-report-category]").SelectOptionAsync("Spam"); await dialog.Locator("[data-report-comment]").FillAsync(words); await Assertions.Expect(dialog.Locator("[data-report-forward]")).ToBeCheckedAsync(); await dialog.Locator("[data-report-send]").ClickAsync(); await Assertions.Expect(dialog).ToHaveCountAsync(0, new() { Timeout = 15000 }); var report = (await PastureAdmin.Get("/clientapi/moderator/reports?resolved=false"))!.AsArray() .FirstOrDefault(r => r!["comment"]?.GetValue() == words); Assert.NotNull(report); Assert.Equal((targetId, "spam", true), (report!["targetAccountId"]!.GetValue(), report["category"]!.GetValue(), report["forwarded"]!.GetValue())); await session.AssertGuards(); }, page); } [Fact] public async Task Who_liked_and_boosted_a_post_is_listed_from_its_card() { var personas = Pasture.Personas(); Assert.SkipWhen(personas.Count < 2, "the town has one persona"); var (author, fan) = (personas[0], personas[1]); var post = (await PersonaApi.Call(author, "/api/v1/statuses", HttpMethod.Post, new JsonObject { ["status"] = $"like and boost me {Guid.NewGuid():N}"[..28], ["visibility"] = "public" }))!; var id = post["id"]!.GetValue(); var fanAcct = (await PersonaApi.Call(fan, "/api/v1/accounts/verify_credentials"))!["acct"]!.GetValue(); await PersonaApi.Call(fan, $"/api/v1/statuses/{id}/favourite", HttpMethod.Post); await PersonaApi.Call(fan, $"/api/v1/statuses/{id}/reblog", HttpMethod.Post); await using var session = await SignIn.As(browser, author); var page = session.Page; try { await Results.Cell("client.render.interacted-by", "privapub", author.UserName, async () => { await page.GotoAsync($"{Pasture.Site}/expand/{Uri.EscapeDataString(id)}"); var card = page.Locator($"article[data-uri='{post["uri"]!.GetValue()}']").First; await card.WaitForAsync(new() { Timeout = 30000 }); foreach (var (button, list) in new[] { ("data-liked-by", "likes"), ("data-boosted-by", "boosts") }) { await card.Locator("footer [title='Other'] > [role=button]").ClickAsync(); await card.Locator($"button[{button}]").ClickAsync(); var dialog = card.Locator($"[data-interacted-by='{list}']"); await Assertions.Expect(dialog.Locator($"[data-account='{fanAcct}']")).ToHaveCountAsync(1, new() { Timeout = 15000 }); await page.Keyboard.PressAsync("Escape"); await Assertions.Expect(dialog).ToHaveCountAsync(0); } await session.AssertGuards(); }, page); } finally { await PersonaApi.Call(author, $"/api/v1/statuses/{id}", HttpMethod.Delete); } } } }