102 lines
4.8 KiB
TypeScript
102 lines
4.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { createElement } from "react";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { FeaturedCarousel } from "@/components/FeaturedCarousel";
|
|
import { TrendingMinnectsRail } from "@/components/TrendingMinnectsRail";
|
|
import { DiscoverRailsView } from "@/components/DiscoverRails";
|
|
import { fetchDiscover } from "@/lib/discover-client";
|
|
import type { DiscoverExpert, DiscoverMinnect } from "@/lib/discover";
|
|
|
|
const expert = (o: Partial<DiscoverExpert> = {}): DiscoverExpert => ({
|
|
id: 1, slug: "ann", displayName: "Ann Lee", byline: "Growth coach", pictureUrl: "https://x/a.jpg",
|
|
rating: 4.8, reviewsCount: 12, available: true, location: { city: null, country: null }, ...o,
|
|
});
|
|
const minnect = (o: Partial<DiscoverMinnect> = {}): DiscoverMinnect => ({
|
|
id: 9, question: "How do I price?", views: 1234, thumbnailUrl: "https://x/t.jpg",
|
|
media: { kind: "audio", url: "https://x/a.mp3", duration: 30, waveform: null },
|
|
expert: { slug: "ann", displayName: "Ann Lee", pictureUrl: null, byline: null, rating: null }, ...o,
|
|
});
|
|
const html = (el: ReturnType<typeof createElement>) => renderToStaticMarkup(el);
|
|
|
|
describe("FeaturedCarousel (static render)", () => {
|
|
it("renders the center expert with name, byline, rating and reviews", () => {
|
|
const out = html(createElement(FeaturedCarousel, { experts: [expert()] }));
|
|
expect(out).toContain("<h2");
|
|
expect(out).toContain("Ann Lee");
|
|
expect(out).toContain("Growth coach");
|
|
expect(out).toContain("4.8");
|
|
expect(out).toContain("12 reviews");
|
|
expect(out).toContain("<img");
|
|
});
|
|
it("falls back to initials and omits missing fields", () => {
|
|
const out = html(createElement(FeaturedCarousel, {
|
|
experts: [expert({ pictureUrl: null, byline: null, rating: null, reviewsCount: null })],
|
|
}));
|
|
expect(out).not.toContain("<img");
|
|
expect(out).toContain("AL");
|
|
expect(out).not.toContain("review");
|
|
});
|
|
it("renders nothing when empty", () => {
|
|
expect(html(createElement(FeaturedCarousel, { experts: [] }))).toBe("");
|
|
});
|
|
});
|
|
|
|
describe("TrendingMinnectsRail", () => {
|
|
it("renders question, views, thumbnail and an audio control", () => {
|
|
const out = html(createElement(TrendingMinnectsRail, { minnects: [minnect()] }));
|
|
expect(out).toContain("How do I price?");
|
|
expect(out).toContain("1,234 views");
|
|
expect(out).toContain("<audio");
|
|
expect(out).toContain("controls");
|
|
expect(out).toContain('src="https://x/a.mp3"');
|
|
expect(out).toContain("t.jpg");
|
|
});
|
|
it("renders a video with poster for video answers, no audio element", () => {
|
|
const url = "https://valuentainment-quick-questions-prod.s3.amazonaws.com/a.mov?X-Amz-Signature=abc";
|
|
const out = html(createElement(TrendingMinnectsRail, {
|
|
minnects: [minnect({ media: { kind: "video", url, duration: 9, waveform: null } })],
|
|
}));
|
|
expect(out).toContain("<video");
|
|
expect(out).toContain("controls");
|
|
expect(out).toContain("preload=\"none\"");
|
|
expect(out).toContain("X-Amz-Signature=abc");
|
|
expect(out).toContain('poster="https://x/t.jpg"');
|
|
expect(out).not.toContain("<audio");
|
|
expect(out).not.toContain("autoPlay");
|
|
});
|
|
it("has no media element for a missing url but keeps the thumbnail", () => {
|
|
const out = html(createElement(TrendingMinnectsRail, { minnects: [minnect({ media: null })] }));
|
|
expect(out).not.toContain("<audio");
|
|
expect(out).not.toContain("<video");
|
|
expect(out).toContain("minnect-thumb");
|
|
});
|
|
it("has no audio element or thumbnail when those are missing", () => {
|
|
const out = html(createElement(TrendingMinnectsRail, {
|
|
minnects: [minnect({ media: null, thumbnailUrl: null, question: null, views: null })],
|
|
}));
|
|
expect(out).not.toContain("<audio");
|
|
expect(out).not.toContain("minnect-thumb");
|
|
expect(out).toContain("Ann Lee");
|
|
});
|
|
});
|
|
|
|
describe("DiscoverRailsView / fetchDiscover", () => {
|
|
it("renders both rails from data and nothing when null", () => {
|
|
const data = { featuredExperts: [expert()], trendingMinnects: [minnect()], categories: [] };
|
|
const out = html(createElement(DiscoverRailsView, { data }));
|
|
expect(out).toContain("Featured experts");
|
|
expect(out).toContain("Trending minnects");
|
|
expect(html(createElement(DiscoverRailsView, { data: null }))).toBe("");
|
|
});
|
|
it("fetches /api/discover and tolerates missing arrays", async () => {
|
|
const f = (async () => new Response(JSON.stringify({ featuredExperts: [expert()] }), { status: 200 })) as typeof fetch;
|
|
const d = await fetchDiscover(f);
|
|
expect(d.featuredExperts).toHaveLength(1);
|
|
expect(d.trendingMinnects).toEqual([]);
|
|
});
|
|
it("throws on a 502", async () => {
|
|
const f = (async () => new Response("{}", { status: 502 })) as typeof fetch;
|
|
await expect(fetchDiscover(f)).rejects.toThrow("discover_unavailable");
|
|
});
|
|
});
|