Files
docmost-sync/test/filters.test.ts
vvzvlad cc13c94f53 test(docmost-client): add unit tests for pure lib modules
Add 230 Vitest unit tests covering the dependency-light, pure modules of
packages/docmost-client/src/lib, imported directly from source:

- node-ops: tree addressing, immutability/clone guarantees, table ops,
  throw-vs-noop contracts (87)
- transforms: commentsToFootnotes reading-order renumbering, insertMarkerAfter
  mark-preserving split, setCalloutRange regex statefulness (43)
- json-edit: applyTextEdits literal $&/$1, error distinction, immutability (17)
- page-lock: async per-page mutex ordering and error isolation (6)
- filters: filterPage/filterComment truthiness traps, filterSearchResult (19)
- markdown-converter: per-node golden matrix + edge cases (41)
- markdown-document envelope: round-trip, CRLF, malformed-JSON throws (17)

No source files changed. The pre-existing test/markdown-document.test.ts is
left intact; new envelope coverage lives in markdown-document-envelope.test.ts.
Full suite: 16 files / 279 tests green.
2026-06-16 22:10:06 +03:00

235 lines
6.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
filterComment,
filterGroup,
filterPage,
filterSearchResult,
filterSpace,
filterWorkspace,
} from '../packages/docmost-client/src/lib/filters.js';
describe('filterPage', () => {
const basePage = {
id: 'pg1',
slugId: 'slug-1',
title: 'Title',
parentPageId: 'parent-1',
spaceId: 'space-1',
isLocked: false,
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
// Extra fields that must be dropped by the filter.
secret: 'should-not-leak',
};
it('omits the content key when no content arg is given', () => {
const result = filterPage(basePage);
expect(result).not.toHaveProperty('content');
expect(result).not.toHaveProperty('secret');
expect(result.id).toBe('pg1');
});
it('includes an empty-string content (truthiness trap: empty string is kept)', () => {
const result = filterPage(basePage, '');
expect(result).toHaveProperty('content');
expect(result.content).toBe('');
});
it('includes a non-empty content string', () => {
const result = filterPage(basePage, '# Markdown');
expect(result.content).toBe('# Markdown');
});
it('omits content when it is not a string', () => {
// A non-string (e.g. an object passed by mistake) must be dropped.
const result = filterPage(basePage, { junk: true } as unknown as string);
expect(result).not.toHaveProperty('content');
});
it('omits subpages when undefined', () => {
const result = filterPage(basePage);
expect(result).not.toHaveProperty('subpages');
});
it('omits subpages when empty array', () => {
const result = filterPage(basePage, undefined, []);
expect(result).not.toHaveProperty('subpages');
});
it('maps non-empty subpages to { id, title } only', () => {
const result = filterPage(basePage, undefined, [
{ id: 's1', title: 'Sub 1', extra: 'drop-me' },
{ id: 's2', title: 'Sub 2' },
]);
expect(result.subpages).toEqual([
{ id: 's1', title: 'Sub 1' },
{ id: 's2', title: 'Sub 2' },
]);
});
});
describe('filterComment', () => {
const baseComment = {
id: 'c1',
pageId: 'pg1',
content: 'original content',
creatorId: 'u1',
createdAt: '2024-01-01',
};
it('overrides comment.content with markdownContent when provided', () => {
const result = filterComment(baseComment, 'markdown version');
expect(result.content).toBe('markdown version');
});
it('falls back to comment.content when markdownContent is undefined', () => {
const result = filterComment(baseComment, undefined);
expect(result.content).toBe('original content');
});
it('keeps an empty-string markdownContent (uses ?? not ||)', () => {
// `??` only falls back on null/undefined, so "" must be preserved.
const result = filterComment(baseComment, '');
expect(result.content).toBe('');
});
it('applies defaults for selection/type/parentCommentId/editedAt/resolvedAt/resolvedById', () => {
const result = filterComment(baseComment);
expect(result.selection).toBeNull();
expect(result.type).toBe('page');
expect(result.parentCommentId).toBeNull();
expect(result.editedAt).toBeNull();
expect(result.resolvedAt).toBeNull();
expect(result.resolvedById).toBeNull();
});
it('passes through provided optional values', () => {
const result = filterComment({
...baseComment,
selection: 'some text',
type: 'inline',
parentCommentId: 'c0',
editedAt: '2024-02-01',
resolvedAt: '2024-03-01',
resolvedById: 'u9',
});
expect(result.selection).toBe('some text');
expect(result.type).toBe('inline');
expect(result.parentCommentId).toBe('c0');
expect(result.editedAt).toBe('2024-02-01');
expect(result.resolvedAt).toBe('2024-03-01');
expect(result.resolvedById).toBe('u9');
});
it('returns null for creatorName when creator is absent', () => {
const result = filterComment(baseComment);
expect(result.creatorName).toBeNull();
});
it('reads the nested creator?.name when present', () => {
const result = filterComment({
...baseComment,
creator: { name: 'Alice' },
});
expect(result.creatorName).toBe('Alice');
});
});
describe('filterSearchResult', () => {
const baseResult = {
id: 'r1',
title: 'Result',
parentPageId: 'p0',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
rank: 0.9,
highlight: '<em>Result</em>',
};
it('reads nested space?.id and space?.name when space is present', () => {
const result = filterSearchResult({
...baseResult,
space: { id: 'sp1', name: 'Space One' },
});
expect(result.spaceId).toBe('sp1');
expect(result.spaceName).toBe('Space One');
});
it('returns undefined for space fields when space is absent (no throw)', () => {
const result = filterSearchResult(baseResult);
expect(result.spaceId).toBeUndefined();
expect(result.spaceName).toBeUndefined();
});
});
describe('flat pluckers (no branching)', () => {
it('filterWorkspace plucks the expected shape', () => {
const result = filterWorkspace({
id: 'w1',
name: 'WS',
description: 'desc',
defaultSpaceId: 'sp1',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
extra: 'drop',
});
expect(result).toEqual({
id: 'w1',
name: 'WS',
description: 'desc',
defaultSpaceId: 'sp1',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
});
});
it('filterSpace plucks the expected shape', () => {
const result = filterSpace({
id: 'sp1',
name: 'Space',
description: 'desc',
slug: 'space',
visibility: 'open',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
extra: 'drop',
});
expect(result).toEqual({
id: 'sp1',
name: 'Space',
description: 'desc',
slug: 'space',
visibility: 'open',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
});
});
it('filterGroup plucks the expected shape', () => {
const result = filterGroup({
id: 'g1',
name: 'Group',
description: 'desc',
workspaceId: 'w1',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
extra: 'drop',
});
expect(result).toEqual({
id: 'g1',
name: 'Group',
description: 'desc',
workspaceId: 'w1',
createdAt: '2024-01-01',
updatedAt: '2024-01-02',
deletedAt: null,
});
});
});