What can't be undone asks first
Deleting a post, taking it off the persona's wall, muting or blocking an account (from a card or a profile), leaving a group, dropping a scheduled post and signing out acted at the first tap, a few pixels from their neighbours. They now ask first: new ConfirmService (Ask returns whether the reader confirmed, as PublicNoticeService does) and ConfirmHost in MainLayout, a sheet from the bottom on a phone and a dialog from md, with Cancel and the action named, in red when it deletes or cuts off. Logout is a button that asks, then goes to /logout as before. The reversible ones (unmute, unblock, removing a conversation from the list, a picture from the composer) don't ask; the inline confirms of lists, filters and the login's deletion stay. The desktop tests that delete a reply, take a post off a wall or drop a scheduled post confirm; a phone flow deletes a post after cancelling once. 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
931b7fcff1
commit
1c9773bab0
15 files changed
+211
-3
No files matched your search
@@ -148,6 +148,7 @@ namespace decePubClient.E2E.Actions
|
||||
await reply.WaitForAsync(new() { Timeout = 15000 });
|
||||
await reply.Locator("footer [title='Other']").First.ClickAsync();
|
||||
await reply.GetByRole(AriaRole.Button).Filter(new() { HasText = "Delete" }).First.ClickAsync();
|
||||
await session.Page.Locator("[data-confirm-yes]").ClickAsync();
|
||||
Assert.True(await Pasture.Eventually(async () =>
|
||||
{
|
||||
var row = (await Pasture.Stored(origin, replyUri))[replyUri]!;
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace decePubClient.E2E.Actions
|
||||
(await PersonaApi.Call(persona, $"/api/v1/scheduled_statuses/{id}"))?["scheduled_at"]?.GetValue<DateTime>().ToUniversalTime()
|
||||
is { } at && Math.Abs((at - later.ToUniversalTime()).TotalMinutes) < 2, 15), "PrivaPub never moved the post");
|
||||
await item.GetByRole(AriaRole.Button, new() { Name = "Never publish it" }).ClickAsync();
|
||||
await page.Locator("[data-confirm-yes]").ClickAsync();
|
||||
await Assertions.Expect(item).ToHaveCountAsync(0, new() { Timeout = 15000 });
|
||||
Assert.Null(await PersonaApi.Call(persona, $"/api/v1/scheduled_statuses/{id}"));
|
||||
await session.AssertGuards();
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace decePubClient.E2E.Actions
|
||||
await Assertions.Expect(card.Locator("[data-wall]")).ToContainTextAsync(me["display_name"]!.GetValue<string>() is { Length: > 0 } name ? name : me["username"]!.GetValue<string>());
|
||||
await card.Locator("footer [title='Other'] > [role=button]").ClickAsync();
|
||||
await card.Locator("button[data-take-off-wall]").ClickAsync();
|
||||
await session.Page.Locator("[data-confirm-yes]").ClickAsync();
|
||||
Assert.True(await Pasture.Eventually(async () => await PersonaApi.Call(persona, $"/api/v1/statuses/{status["id"]!.GetValue<string>()}") is null, 15),
|
||||
"the post is still on PrivaPub");
|
||||
Assert.True(await Pasture.Eventually(async () =>
|
||||
|
||||
@@ -225,6 +225,43 @@ namespace decePubClient.E2E.Mobile
|
||||
}, page);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Deleting_a_post_asks_first()
|
||||
{
|
||||
var persona = Persona();
|
||||
var text = $"to be deleted {Guid.NewGuid():N}"[..26];
|
||||
var made = (await PersonaApi.Call(persona, "/api/v1/statuses", HttpMethod.Post, new JsonObject { ["status"] = text, ["visibility"] = "public" }))!;
|
||||
var id = made["id"]!.GetValue<string>();
|
||||
await using var session = await SignIn.As(browser, persona, Phone);
|
||||
try
|
||||
{
|
||||
await Flow(session, "delete", async page =>
|
||||
{
|
||||
var card = page.Locator($"#feed-scroll article[data-uri='{made["uri"]}']").First;
|
||||
await card.WaitForAsync(new() { Timeout = 30000 });
|
||||
var more = card.Locator("footer [title='Other'] > [role=button]");
|
||||
await more.TapAsync();
|
||||
await card.Locator("[data-sheet] button", new() { HasText = "Delete" }).TapAsync();
|
||||
var confirm = page.Locator("[data-confirm].modal-open");
|
||||
await Assertions.Expect(confirm).ToBeVisibleAsync();
|
||||
await AssertWhole(page, "[data-confirm] .modal-box");
|
||||
await page.Locator("[data-confirm-no]").TapAsync();
|
||||
await Assertions.Expect(confirm).ToHaveCountAsync(0);
|
||||
Assert.NotNull(await PersonaApi.Call(persona, $"/api/v1/statuses/{id}"));
|
||||
|
||||
await more.TapAsync();
|
||||
await card.Locator("[data-sheet] button", new() { HasText = "Delete" }).TapAsync();
|
||||
await page.Locator("[data-confirm-yes]").TapAsync();
|
||||
await Assertions.Expect(card).ToHaveCountAsync(0, new() { Timeout = 15000 });
|
||||
Assert.True(await Pasture.Eventually(async () => await PersonaApi.Call(persona, $"/api/v1/statuses/{id}") is null, 15), "the post is still there");
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
await PersonaApi.Call(persona, $"/api/v1/statuses/{id}", HttpMethod.Delete);
|
||||
}
|
||||
}
|
||||
|
||||
static async Task<JsonNode> Latest(Pasture.Persona persona) =>
|
||||
(await PersonaApi.Call(persona, $"/api/v1/accounts/{persona.LocalId}/statuses?limit=1"))!.AsArray().FirstOrDefault();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user