2024-11-16 21:26:26 -07:00
|
|
|
import 'jasmine';
|
2024-11-16 09:45:13 -07:00
|
|
|
import importSingle from '../../../src/services/import/single.js';
|
|
|
|
import importUtils from '../../../src/services/import/utils.js';
|
|
|
|
import BNote from '../../../src/becca/entities/bnote.js';
|
|
|
|
import TaskContext from '../../../src/services/task_context.js';
|
2024-11-16 21:36:07 -07:00
|
|
|
import sql from '../../../src/services/sql.js';
|
|
|
|
import cls from '../../../src/services/cls.js';
|
2024-11-16 09:45:13 -07:00
|
|
|
|
|
|
|
describe('HTML Import', () => {
|
|
|
|
let parentNote: BNote;
|
|
|
|
let taskContext: TaskContext;
|
|
|
|
|
2024-11-16 21:36:07 -07:00
|
|
|
beforeAll(() => {
|
|
|
|
// Set up in-memory database for testing
|
|
|
|
process.env.TRILIUM_INTEGRATION_TEST = 'memory';
|
|
|
|
});
|
|
|
|
|
2024-11-16 09:45:13 -07:00
|
|
|
beforeEach(() => {
|
2024-11-16 21:36:07 -07:00
|
|
|
return cls.init(() => {
|
2024-11-16 21:39:08 -07:00
|
|
|
return sql.transactional(() => {
|
|
|
|
// Create required tables
|
|
|
|
sql.execute(`
|
|
|
|
CREATE TABLE IF NOT EXISTS notes (
|
|
|
|
noteId TEXT PRIMARY KEY,
|
|
|
|
title TEXT,
|
|
|
|
type TEXT,
|
|
|
|
mime TEXT,
|
|
|
|
isProtected INTEGER DEFAULT 0,
|
|
|
|
isDeleted INTEGER DEFAULT 0
|
|
|
|
)
|
|
|
|
`);
|
2024-11-16 09:45:13 -07:00
|
|
|
|
2024-11-16 21:39:08 -07:00
|
|
|
sql.execute(`
|
|
|
|
CREATE TABLE IF NOT EXISTS note_contents (
|
|
|
|
noteId TEXT PRIMARY KEY,
|
|
|
|
content TEXT,
|
|
|
|
hash TEXT,
|
|
|
|
utcDateModified TEXT
|
|
|
|
)
|
|
|
|
`);
|
2024-11-16 21:36:07 -07:00
|
|
|
|
2024-11-16 21:39:08 -07:00
|
|
|
sql.execute(`
|
|
|
|
CREATE TABLE IF NOT EXISTS entity_changes (
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
entityName TEXT NOT NULL,
|
|
|
|
entityId TEXT NOT NULL,
|
|
|
|
hash TEXT NOT NULL,
|
|
|
|
isErased INTEGER NOT NULL,
|
|
|
|
isSynced INTEGER NOT NULL,
|
|
|
|
utcDateChanged TEXT NOT NULL,
|
|
|
|
changeId TEXT NOT NULL,
|
|
|
|
componentId TEXT,
|
|
|
|
instanceId TEXT
|
|
|
|
)
|
|
|
|
`);
|
|
|
|
|
|
|
|
// Create a mock parent note
|
|
|
|
parentNote = new BNote({
|
|
|
|
noteId: 'testParent',
|
|
|
|
title: 'Test Parent',
|
|
|
|
type: 'text',
|
|
|
|
mime: 'text/html'
|
|
|
|
});
|
|
|
|
|
|
|
|
// Create a mock task context
|
|
|
|
taskContext = new TaskContext('test', 'test');
|
|
|
|
// Set textImportedAsText to true to ensure HTML imports are processed
|
|
|
|
taskContext.data = { textImportedAsText: true };
|
2024-11-16 21:36:07 -07:00
|
|
|
|
|
|
|
// Insert parent note
|
|
|
|
sql.insert('notes', {
|
|
|
|
noteId: parentNote.noteId,
|
|
|
|
title: parentNote.title,
|
|
|
|
type: parentNote.type,
|
2024-11-16 21:39:08 -07:00
|
|
|
mime: parentNote.mime,
|
|
|
|
isProtected: 0,
|
|
|
|
isDeleted: 0
|
2024-11-16 21:36:07 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('extractHtmlTitle', () => {
|
|
|
|
it('should extract title from HTML content', () => {
|
|
|
|
const html = `
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Test Title</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<p>Content</p>
|
|
|
|
</body>
|
2024-11-16 21:17:50 -07:00
|
|
|
</html>
|
|
|
|
`;
|
2024-11-16 09:45:13 -07:00
|
|
|
|
|
|
|
const title = importUtils.extractHtmlTitle(html);
|
2024-11-16 21:17:50 -07:00
|
|
|
expect(title).toBe('Test Title');
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
|
2024-11-16 21:17:50 -07:00
|
|
|
it('should return null if no title tag is present', () => {
|
2024-11-16 09:45:13 -07:00
|
|
|
const html = `
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<p>Content</p>
|
|
|
|
</body>
|
2024-11-16 21:17:50 -07:00
|
|
|
</html>
|
|
|
|
`;
|
|
|
|
|
2024-11-16 09:45:13 -07:00
|
|
|
const title = importUtils.extractHtmlTitle(html);
|
2024-11-16 21:17:50 -07:00
|
|
|
expect(title).toBeNull();
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-11-16 21:17:50 -07:00
|
|
|
describe('importSingleFile with HTML', () => {
|
|
|
|
it('should import HTML file with title from title tag', () => {
|
2024-11-16 21:36:07 -07:00
|
|
|
return cls.init(() => {
|
|
|
|
return sql.transactional(() => {
|
|
|
|
const file = {
|
|
|
|
originalname: 'test.html',
|
|
|
|
mimetype: 'text/html',
|
|
|
|
buffer: Buffer.from(`
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>HTML Title</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<p>Test content</p>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`)
|
|
|
|
};
|
|
|
|
|
|
|
|
const note = importSingle.importSingleFile(taskContext, file, parentNote);
|
|
|
|
expect(note.title).toBe('HTML Title');
|
|
|
|
expect(note.mime).toBe('text/html');
|
|
|
|
});
|
|
|
|
});
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
|
2024-11-16 21:17:50 -07:00
|
|
|
it('should import HTML file with title from h1 when no title tag', () => {
|
2024-11-16 21:36:07 -07:00
|
|
|
return cls.init(() => {
|
|
|
|
return sql.transactional(() => {
|
|
|
|
const file = {
|
|
|
|
originalname: 'test.html',
|
|
|
|
mimetype: 'text/html',
|
|
|
|
buffer: Buffer.from(`
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>Heading Title</h1>
|
|
|
|
<p>Test content</p>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`)
|
|
|
|
};
|
|
|
|
|
|
|
|
const note = importSingle.importSingleFile(taskContext, file, parentNote);
|
|
|
|
expect(note.title).toBe('Heading Title');
|
|
|
|
expect(note.mime).toBe('text/html');
|
|
|
|
});
|
|
|
|
});
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
|
2024-11-16 21:17:50 -07:00
|
|
|
it('should import HTML file with filename as title when no title or h1', () => {
|
2024-11-16 21:36:07 -07:00
|
|
|
return cls.init(() => {
|
|
|
|
return sql.transactional(() => {
|
|
|
|
const file = {
|
|
|
|
originalname: 'test-document.html',
|
|
|
|
mimetype: 'text/html',
|
|
|
|
buffer: Buffer.from(`
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<p>Test content without title</p>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`)
|
|
|
|
};
|
|
|
|
|
|
|
|
const note = importSingle.importSingleFile(taskContext, file, parentNote);
|
|
|
|
expect(note.title).toBe('test-document');
|
|
|
|
expect(note.mime).toBe('text/html');
|
|
|
|
});
|
|
|
|
});
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
|
2024-11-16 21:17:50 -07:00
|
|
|
it('should sanitize HTML content during import', () => {
|
2024-11-16 21:36:07 -07:00
|
|
|
return cls.init(() => {
|
|
|
|
return sql.transactional(() => {
|
|
|
|
const file = {
|
|
|
|
originalname: 'test.html',
|
|
|
|
mimetype: 'text/html',
|
|
|
|
buffer: Buffer.from(`
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Test Title</title>
|
|
|
|
<script>alert('xss');</script>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<p>Safe content</p>
|
|
|
|
<script>alert('xss');</script>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`)
|
|
|
|
};
|
|
|
|
|
|
|
|
const note = importSingle.importSingleFile(taskContext, file, parentNote);
|
|
|
|
expect(note.title).toBe('Test Title');
|
|
|
|
const content = note.getContent();
|
|
|
|
expect(content).not.toContain('<script>');
|
|
|
|
expect(content).toContain('<p>Safe content</p>');
|
|
|
|
});
|
|
|
|
});
|
2024-11-16 09:45:13 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|