179 lines
6.7 KiB
JavaScript
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');
|
|
});
|
|
});
|