import { describe, expect, it } from "vitest"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { readFileSync } from "node:fs"; import { FeedView, type FeedViewProps } from "@/components/FeedView"; import { MobileDock, dockState } from "@/components/MobileDock"; import { CompactLayoutContext } from "@/components/layout-context"; import { toCardPosts } from "@/lib/feed-model"; const item = (id: string, likes: number, comments: number, liked = false) => ({ type: "post", publishedAt: "2026-10-19T11:00:00Z", likesCount: likes, commentsCount: comments, isLiked: liked, post: { id, content: `text ${id}`, author: { id: "a", displayName: `Author ${id}` }, mediaItems: [] }, }); const POSTS = toCardPosts([item("p1", 12, 3), item("p2", 40, 1, true)]); function feed(over: Partial, compact = true) { const props: FeedViewProps = { posts: POSTS, status: "ready", loadingMore: false, loadMoreFailed: false, newCount: 0, onRetry: () => {}, onShowNew: () => {}, onToggleLike: () => {}, ...over, }; return renderToStaticMarkup(createElement(CompactLayoutContext.Provider, { value: compact }, createElement(FeedView, props))); } describe("dock state", () => { it("shows the counts and liked state of the post on screen", () => { const s = dockState({ index: 1, kind: "post", post: POSTS[1] }, { canLike: true }); expect(s.likes).toBe(POSTS[1].likesCount); expect(s.comments).toBe(POSTS[1].commentsCount); expect(s.liked).toBe(POSTS[1].isLiked); expect(s.likeDisabled).toBe(false); expect(s.commentsDisabled).toBe(false); }); it("disables like and comments on the featured and loading slides", () => { for (const kind of ["featured", "load"] as const) { const s = dockState({ index: 2, kind, post: null }); expect(s.likeDisabled).toBe(true); expect(s.commentsDisabled).toBe(true); expect(s.likes).toBeNull(); } }); it("disables like while the like call is in flight", () => { expect(dockState({ index: 0, kind: "post", post: POSTS[0] }, { canLike: true, likeBusy: true }).likeDisabled).toBe(true); }); }); describe("dock markup", () => { it("renders like with aria-pressed, comments, the sparkle new-post button and More", () => { const out = renderToStaticMarkup(createElement(MobileDock, { slide: { index: 1, kind: "post", post: POSTS[1] }, onToggleLike: () => {} })); expect(out).toContain('aria-label="Post actions"'); expect(out).toMatch(/class="dk dk-like dk-like-on"[^>]*aria-pressed="true"/); expect(out).toContain(`>${POSTS[1].likesCount}`); expect(out).toContain(`Open comments, ${POSTS[1].commentsCount} comment`); expect(out).toContain("btn-spark btn-spark-fab dk-plus"); expect(out).not.toContain("btn-spark-fold"); expect(out).toContain('aria-label="View more"'); }); it("is part of the phone feed and follows the first slide", () => { const out = feed({}); expect(out).toContain('data-testid="feed-dock"'); expect(out).toContain(`data-testid="dock-likes">${POSTS[0].likesCount}<`); }); it("is absent on desktop", () => { expect(feed({}, false)).not.toContain("feed-dock"); }); }); describe("dock stylesheet", () => { const css = readFileSync("src/styles/mobile.css", "utf8"); it("floats 16px above the bottom on frosted glass", () => { expect(css).toMatch(/\.dock \{[^}]*bottom: calc\(16px[^}]*backdrop-filter: blur/); }); it("keeps touch targets at least 44px and disabled buttons fully opaque", () => { expect(css).toMatch(/\.dk \{[^}]*min-width: 44px;[^}]*height: 52px/); expect(css).toMatch(/\.dk:disabled \{ opacity: 1;/); }); });