import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createCardRender } from "./card"; import countdown, { tick as countdownTick } from "../count-down/count-down"; import type { ICountdownContext, ICountdownFormatter, TCountdownParser, TCountdownValue } from "../count-down/types"; const parser: TCountdownParser = () => [0, 0, 0, 0, 0]; const fmt: ICountdownFormatter = (ms) => String(ms); // 新渲染契约:render(el, remaining, value, ctx),formatter/parser 收敛进 ctx const val: TCountdownValue = [0, 0, 0, 0, 0]; const ctx = { el: document.createElement("div"), id: 0, deadline: 0, remaining: 0, value: val, active: true, paused: false, fmt, parser } as ICountdownContext; // DOM: host > ul.cd-root.cd-flip-root > li.cd-cell.cd-flip-cell // > span.cd-num.cd-next.cd-flip-num.cd-flip-next + span.cd-num.cd-now.cd-flip-num.cd-flip-now // 翻页态 .cd-flipping 加在两个面(span)上,不在 li 上。 function root(host: Element) { return host.querySelector(".cd-root") as HTMLElement; } function items(host: Element) { return Array.from(root(host).children) as HTMLElement[]; } function now(item: HTMLElement) { return item.querySelector(".cd-now") as HTMLElement; } function next(item: HTMLElement) { return item.querySelector(".cd-next") as HTMLElement; } function nowDigit(item: HTMLElement) { return now(item).dataset.digit; } function nextDigit(item: HTMLElement) { return next(item).dataset.digit; } /** 该格是否处于翻页态(状态类在 now 面上;分隔符恒为 false) */ function flipping(item: HTMLElement) { const n = now(item); return !!n && n.classList.contains("cd-flipping"); } /** 读出整块时钟当前展示的字符串(数字取 now 面 data-digit,分隔符取文本) */ function readClock(host: Element) { return items(host) .map((it) => (it.classList.contains("cd-sep") ? it.textContent : nowDigit(it))) .join(""); } describe("createCardRender DOM & structure", () => { it("builds ul.cd-root > li.cd-cell per char, no animation on first render", () => { const host = document.createElement("div"); createCardRender()(host, 123, val, ctx); const ul = root(host); expect(ul.tagName).toBe("UL"); expect(ul.classList.contains("cd-root")).toBe(true); expect(ul.classList.contains("cd-flip-root")).toBe(true); const its = items(host); expect(its.length).toBe(3); // 每格带公共类 + 效果类 expect(its.every((it) => it.tagName === "LI" && it.classList.contains("cd-cell") && it.classList.contains("cd-flip-cell"))).toBe(true); expect(its.map((it) => nowDigit(it)).join("")).toBe("123"); // 每格内:next 在前、now 在后,均带 cd-num + 效果类 const n0 = its[0].children[0]; const n1 = its[0].children[1]; expect(n0.classList.contains("cd-next") && n0.classList.contains("cd-flip-next")).toBe(true); expect(n1.classList.contains("cd-now") && n1.classList.contains("cd-flip-num")).toBe(true); expect(its.some((it) => flipping(it))).toBe(false); }); it("does NOT inject any