guanghulab/tests/smoke/notion-page-reader.test.js
2026-05-10 13:12:44 +08:00

179 lines
6.7 KiB
JavaScript

/**
* Unit test · Notion 页面阅读器 URL 解析
*
* 测试 extractPageId 函数对各种 Notion URL 格式的解析
*
* 运行: node tests/smoke/notion-page-reader.test.js
*/
const { extractPageId, extractBlockText, blocksToMarkdown } = require('../../scripts/notion-page-reader');
// ══════════════════════════════════════════════════════════
// extractPageId 测试
// ══════════════════════════════════════════════════════════
describe('extractPageId', () => {
test('extracts from 32-char hex string', () => {
expect(extractPageId('abc123def4561234567890abcdef1234'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('extracts from UUID with dashes', () => {
expect(extractPageId('abc123de-f456-1234-5678-90abcdef1234'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('extracts from notion.so URL with title', () => {
expect(extractPageId('https://www.notion.so/workspace/My-Page-Title-abc123def4561234567890abcdef1234'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('extracts from notion.so URL without title', () => {
expect(extractPageId('https://www.notion.so/abc123def4561234567890abcdef1234'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('extracts from notion.site URL', () => {
expect(extractPageId('https://myworkspace.notion.site/Page-Title-abc123def4561234567890abcdef1234'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('handles URL with query parameters', () => {
expect(extractPageId('https://www.notion.so/abc123def4561234567890abcdef1234?v=xxx&p=123'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('handles URL with anchor', () => {
expect(extractPageId('https://www.notion.so/abc123def4561234567890abcdef1234#section'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('handles uppercase hex', () => {
expect(extractPageId('ABC123DEF4561234567890ABCDEF1234'))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
test('returns null for empty input', () => {
expect(extractPageId('')).toBeNull();
expect(extractPageId(null)).toBeNull();
expect(extractPageId(undefined)).toBeNull();
});
test('returns null for invalid input', () => {
expect(extractPageId('not-a-valid-id')).toBeNull();
expect(extractPageId('https://google.com')).toBeNull();
});
test('handles whitespace around input', () => {
expect(extractPageId(' abc123def4561234567890abcdef1234 '))
.toBe('abc123de-f456-1234-5678-90abcdef1234');
});
});
// ══════════════════════════════════════════════════════════
// extractBlockText 测试
// ══════════════════════════════════════════════════════════
describe('extractBlockText', () => {
test('extracts text from paragraph block', () => {
const block = {
type: 'paragraph',
paragraph: {
rich_text: [{ plain_text: 'Hello ' }, { plain_text: 'World' }]
}
};
expect(extractBlockText(block)).toBe('Hello World');
});
test('returns empty string for block without content', () => {
const block = { type: 'divider', divider: {} };
expect(extractBlockText(block)).toBe('');
});
test('handles block with text property', () => {
const block = {
type: 'paragraph',
paragraph: {
text: [{ plain_text: 'Legacy text' }]
}
};
expect(extractBlockText(block)).toBe('Legacy text');
});
});
// ══════════════════════════════════════════════════════════
// blocksToMarkdown 测试
// ══════════════════════════════════════════════════════════
describe('blocksToMarkdown', () => {
test('converts heading blocks', () => {
const blocks = [
{ type: 'heading_1', heading_1: { rich_text: [{ plain_text: 'Title' }] } },
{ type: 'heading_2', heading_2: { rich_text: [{ plain_text: 'Subtitle' }] } },
{ type: 'heading_3', heading_3: { rich_text: [{ plain_text: 'Section' }] } },
];
const md = blocksToMarkdown(blocks);
expect(md).toContain('# Title');
expect(md).toContain('## Subtitle');
expect(md).toContain('### Section');
});
test('converts list items', () => {
const blocks = [
{ type: 'bulleted_list_item', bulleted_list_item: { rich_text: [{ plain_text: 'Bullet' }] } },
{ type: 'numbered_list_item', numbered_list_item: { rich_text: [{ plain_text: 'Number' }] } },
];
const md = blocksToMarkdown(blocks);
expect(md).toContain('- Bullet');
expect(md).toContain('1. Number');
});
test('converts code blocks', () => {
const blocks = [
{ type: 'code', code: { rich_text: [{ plain_text: 'console.log("hi")' }], language: 'javascript' } },
];
const md = blocksToMarkdown(blocks);
expect(md).toContain('```javascript');
expect(md).toContain('console.log("hi")');
});
test('converts to_do blocks', () => {
const blocks = [
{ type: 'to_do', to_do: { rich_text: [{ plain_text: 'Done' }], checked: true } },
{ type: 'to_do', to_do: { rich_text: [{ plain_text: 'Todo' }], checked: false } },
];
const md = blocksToMarkdown(blocks);
expect(md).toContain('☑ Done');
expect(md).toContain('☐ Todo');
});
test('converts divider', () => {
const blocks = [
{ type: 'paragraph', paragraph: { rich_text: [{ plain_text: 'Before' }] } },
{ type: 'divider', divider: {} },
{ type: 'paragraph', paragraph: { rich_text: [{ plain_text: 'After' }] } },
];
const md = blocksToMarkdown(blocks);
expect(md).toContain('---');
});
test('converts quote and callout', () => {
const blocks = [
{ type: 'quote', quote: { rich_text: [{ plain_text: 'A quote' }] } },
{ type: 'callout', callout: { rich_text: [{ plain_text: 'A callout' }] } },
];
const md = blocksToMarkdown(blocks);
expect(md).toContain('> A quote');
expect(md).toContain('> A callout');
});
test('filters empty blocks', () => {
const blocks = [
{ type: 'paragraph', paragraph: { rich_text: [] } },
{ type: 'paragraph', paragraph: { rich_text: [{ plain_text: 'Content' }] } },
];
const md = blocksToMarkdown(blocks);
expect(md).toBe('Content');
});
});