Another's profile and posts can be reported from the client (/api/v1/reports): a category, words, and for a remote account a copy to its server, which PrivaPub sends from its own actor, never naming the persona. Who liked or boosted a post opens from its card's menu (favourited_by, reblogged_by). e2e: a report from a Mastodon account's profile reaches PrivaPub's moderators forwarded, and a post's likes and boosts list the persona who gave them; the admin helper the tests share moves to Support. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
85 lines
4.1 KiB
C#
85 lines
4.1 KiB
C#
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<string>();
|
|
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<string>() == words);
|
|
Assert.NotNull(report);
|
|
Assert.Equal((targetId, "spam", true), (report!["targetAccountId"]!.GetValue<string>(), report["category"]!.GetValue<string>(),
|
|
report["forwarded"]!.GetValue<bool>()));
|
|
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<string>();
|
|
var fanAcct = (await PersonaApi.Call(fan, "/api/v1/accounts/verify_credentials"))!["acct"]!.GetValue<string>();
|
|
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<string>()}']").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);
|
|
}
|
|
}
|
|
}
|
|
}
|