81 lines
3.6 KiB
TypeScript
81 lines
3.6 KiB
TypeScript
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<FeedViewProps>, 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}</span>`);
|
|
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;/);
|
|
});
|
|
});
|