Skip to content
File

Blob: tests/shared/editor-metrics.test.ts

typescript62 lines
1import { getSchema } from "@tiptap/core";
2import type { JSONContent } from "@tiptap/core";
3import { describe, expect, it } from "vitest";
4 
5import { collectEditorTextMetrics, createHeadlessEditorExtensions } from "@/shared/editor/schema";
6 
7const schema = getSchema(createHeadlessEditorExtensions());
8 
9function metricsFor(content: JSONContent) {
10 return collectEditorTextMetrics(schema.nodeFromJSON(content));
11}
12 
13describe("shared editor metrics", () => {
14 it("matches Tiptap CharacterCount text extraction for block text", () => {
15 expect(
16 metricsFor({
17 type: "doc",
18 content: [
19 { type: "paragraph", content: [{ type: "text", text: "Hello world" }] },
20 { type: "paragraph", content: [{ type: "text", text: "Second line" }] },
21 { type: "codeBlock", content: [{ type: "text", text: "const answer = 42" }] },
22 ],
23 }),
24 ).toEqual({ words: 8, characters: 39 });
25 });
26 
27 it("counts atom nodes as character placeholders without adding words", () => {
28 expect(
29 metricsFor({
30 type: "doc",
31 content: [
32 {
33 type: "paragraph",
34 content: [
35 { type: "text", text: "See " },
36 { type: "pageMention", attrs: { pageId: "page-roadmap" } },
37 { type: "text", text: " soon" },
38 ],
39 },
40 {
41 type: "image",
42 attrs: {
43 src: "/uploads/example.png",
44 alt: "Example",
45 title: null,
46 align: "center",
47 width: 480,
48 naturalWidth: null,
49 naturalHeight: null,
50 pendingInsertId: null,
51 },
52 },
53 ],
54 }),
55 ).toEqual({ words: 2, characters: 11 });
56 });
57 
58 it("returns zero metrics for empty documents", () => {
59 expect(metricsFor({ type: "doc", content: [{ type: "paragraph" }] })).toEqual({ words: 0, characters: 0 });
60 });
61});