An Article or a Page with its title, or a titled post longer than a few paragraphs (WriteFreely sends its posts as Notes with a name), shows its cover, its excerpt and a Read button in a feed, and its whole text on demand; the post opened in its thread shows whole. A feed of Ghost, WordPress and WriteFreely long reads stays a feed. tests/e2e ArticleTests checks it with a WriteFreely blog; the render matrix still passes (70). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
64 lines
3.4 KiB
C#
64 lines
3.4 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Text;
|
|
using System.Text.Json.Nodes;
|
|
|
|
using decePubClient.E2E.Support;
|
|
|
|
using Microsoft.Playwright;
|
|
|
|
namespace decePubClient.E2E.Actions
|
|
{
|
|
// An article in a feed shows its title, its excerpt and a Read button, and its whole text once asked: a WriteFreely
|
|
// blog the persona follows publishes one (SocialPub's peers/writefreely.sh keeps the blog's token).
|
|
public sealed class ArticleTests(Browser browser)
|
|
{
|
|
[Fact]
|
|
public async Task An_article_is_folded_in_the_feed_and_read_on_demand()
|
|
{
|
|
var tokenFile = Path.Combine(Pasture.Root, ".state", "writefreely", "wfuser.token");
|
|
Assert.SkipUnless(File.Exists(tokenFile), "no WriteFreely blog: run tools/pasture/run.sh add writefreely");
|
|
var persona = Pasture.Personas().First();
|
|
var blog = (await PersonaApi.Call(persona, "/api/v2/search?q=wfuser%40writefreely.test&resolve=true&type=accounts"))?["accounts"]?.AsArray().FirstOrDefault();
|
|
Assert.SkipWhen(blog is null, "PrivaPub cannot resolve the WriteFreely blog");
|
|
var blogId = blog!["id"]!.GetValue<string>();
|
|
await PersonaApi.Call(persona, $"/api/v1/accounts/{blogId}/follow", HttpMethod.Post);
|
|
Assert.True(await Pasture.Eventually(async () =>
|
|
(await PersonaApi.Call(persona, $"/api/v1/accounts/relationships?id[]={blogId}"))![0]!["following"]!.GetValue<bool>(), 45), "the blog never accepted");
|
|
|
|
var title = $"An article to fold {Guid.NewGuid():N}"[..30];
|
|
var ending = $"the last words {Guid.NewGuid():N}"[..24];
|
|
var body = string.Join(" ", Enumerable.Repeat("A long paragraph that a feed should not show whole.", 12)) + " " + ending;
|
|
using var http = Pasture.Http();
|
|
using var publish = new HttpRequestMessage(HttpMethod.Post, "https://writefreely.test/api/collections/wfuser/posts")
|
|
{
|
|
Content = new StringContent(new JsonObject { ["title"] = title, ["body"] = body }.ToJsonString(), Encoding.UTF8, "application/json")
|
|
};
|
|
publish.Headers.Authorization = new AuthenticationHeaderValue("Token", File.ReadAllText(tokenFile).Trim());
|
|
(await http.SendAsync(publish)).EnsureSuccessStatusCode();
|
|
JsonNode article = null;
|
|
Assert.True(await Pasture.Eventually(async () =>
|
|
(article = (await PersonaApi.Call(persona, "/api/v1/timelines/home?limit=40"))!.AsArray()
|
|
.FirstOrDefault(s => s!["privapub"]?["title"]?.GetValue<string>() == title)) is not null, 60), "the article never reached the persona");
|
|
|
|
await using var session = await SignIn.As(browser, persona);
|
|
var page = session.Page;
|
|
await Results.Cell("client.render.article", "writefreely", persona.UserName, async () =>
|
|
{
|
|
await page.GotoAsync($"{Pasture.Site}/");
|
|
await page.EvaluateAsync("() => localStorage.removeItem('FeedFilter')");
|
|
await page.GotoAsync($"{Pasture.Site}/");
|
|
var card = page.Locator($"article[data-uri='{article!["uri"]!.GetValue<string>()}']").First;
|
|
await card.WaitForAsync(new() { Timeout = 30000 });
|
|
|
|
await Assertions.Expect(card).ToContainTextAsync(title);
|
|
await Assertions.Expect(card.Locator("[data-article]")).ToBeVisibleAsync();
|
|
await Assertions.Expect(card).Not.ToContainTextAsync(ending);
|
|
await card.Locator("[data-article-open]").ClickAsync();
|
|
await Assertions.Expect(card).ToContainTextAsync(ending, new() { Timeout = 5000 });
|
|
await session.AssertGuards();
|
|
}, page);
|
|
await PersonaApi.Call(persona, $"/api/v1/accounts/{blogId}/unfollow", HttpMethod.Post);
|
|
}
|
|
}
|
|
}
|