import { test, expect } from "../../base.js"; import { login } from "../../helpers.js"; import { MockServer } from "../../mockServer.js"; import { createPost, createProfile } from "../../../shared/factories.js"; test.describe("Hide post flow", () => { test("should hide a post from the feed and persist after refresh", async ({ page, }) => { const mockServer = new MockServer(); const otherUser = createProfile({ did: "did:plc:otheruser1", handle: "otheruser.bsky.social", displayName: "Other User", }); const post = createPost({ uri: "at://did:plc:otheruser1/app.bsky.feed.post/post1", text: "Post I want to hide", authorHandle: otherUser.handle, authorDisplayName: otherUser.displayName, }); mockServer.addProfile(otherUser); mockServer.addTimelinePosts([post]); await mockServer.setup(page); await login(page); // Verify the post is visible on home await page.goto("/"); const homeView = page.locator("#home-view"); const feedItem = homeView.locator('[data-testid="feed-item"]'); await expect(feedItem).toHaveCount(1, { timeout: 10000 }); await expect(homeView).toContainText("Post I want to hide"); // Open the post's context menu and click "Hide post for me" await feedItem.locator(".text-button").click(); await page.locator('[data-testid="menu-action-post-hide"]').click(); // Confirm the hide action in the dialog const confirmButton = page.locator("button.confirm-button"); await expect(confirmButton).toBeVisible({ timeout: 5000 }); await confirmButton.click(); // The post should immediately be hidden from the feed await expect(page.locator('[data-testid="toast"]')).toBeVisible({ timeout: 5000, }); await expect(feedItem).toHaveCount(0, { timeout: 10000 }); // Refreshing the feed should still show the post as hidden await page.goto("/"); await expect(feedItem).toHaveCount(0, { timeout: 10000 }); }); test("should hide a reply in thread view and reveal it by expanding", async ({ page, }) => { const mockServer = new MockServer(); const mainPost = createPost({ uri: "at://did:plc:author1/app.bsky.feed.post/thread1", text: "Main thread post", authorHandle: "author1.bsky.social", authorDisplayName: "Author One", replyCount: 1, }); const reply = createPost({ uri: "at://did:plc:replier1/app.bsky.feed.post/reply1", text: "Reply to hide in thread", authorHandle: "replier1.bsky.social", authorDisplayName: "Replier One", reply: { parent: { uri: mainPost.uri, cid: mainPost.cid }, root: { uri: mainPost.uri, cid: mainPost.cid }, }, }); mockServer.addPosts([mainPost, reply]); mockServer.setPostThread(mainPost.uri, { $type: "app.bsky.feed.defs#threadViewPost", post: mainPost, parent: null, replies: [ { $type: "app.bsky.feed.defs#threadViewPost", post: reply, replies: [], }, ], }); await mockServer.setup(page); await login(page); // Navigate to the thread view await page.goto("/profile/author1.bsky.social/post/thread1"); const view = page.locator("#post-detail-view"); await expect(view).toContainText("Reply to hide in thread", { timeout: 10000, }); // Open the reply's context menu and click "Hide reply for me" const replyPost = view.locator('[data-testid="small-post"]'); await replyPost.locator(".text-button").click(); await page.locator('[data-testid="menu-action-post-hide"]').click(); // Confirm the hide action in the dialog const confirmButton = page.locator("button.confirm-button"); await expect(confirmButton).toBeVisible({ timeout: 5000 }); await confirmButton.click(); // The reply should show a "Post hidden by you" moderation warning const warning = view.locator("moderation-warning"); await expect(warning).toBeVisible({ timeout: 10000 }); await expect(warning).toContainText("Post hidden by you"); // Expanding the warning should reveal the content await warning.locator(".top-bar").click(); await expect(warning.locator(".toggle-content")).toBeVisible({ timeout: 5000, }); await expect(warning).toContainText("Reply to hide in thread"); }); });