Files
onenote-tools/src/onenote/onepkg/joplin-fixture.test.ts

165 lines
5.4 KiB
TypeScript

import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, it } from 'vitest';
import { parseOneNoteSection } from '../parser/parse-section.js';
import { parseOneNoteTableOfContents } from '../parser/parse-toc.js';
import { enumerateCabinet, extractCabinet } from './cabinet.js';
import { openOneNotePackage } from './package.js';
/*
* Joplin CLI test fixture, AGPL-3.0-or-later. See tests/fixtures/README.md.
* Pinned upstream revision: 1e73aad7eb08fde5d9e4cb533df40052a5cd32d7
*/
const encodedFixture = readFileSync(
resolve(process.cwd(), 'tests/fixtures/joplin-test.onepkg.base64'),
'utf8'
);
const JOPLIN_ONEPKG = Uint8Array.from(
Buffer.from(encodedFixture.replaceAll(/\s/gu, ''), 'base64')
);
const ONE_MAGIC = Uint8Array.from([
0xe4, 0x52, 0x5c, 0x7b, 0x8c, 0xd8, 0xa7, 0x4d, 0xae, 0xb1, 0x53, 0x78, 0xd0,
0x29, 0x96, 0xd3,
]);
const EXPECTED_PATHS = [
'Another section.one',
'Open Notebook.onetoc2',
'Section group/A.one',
'Section group/B.one',
'Section group/Open Notebook.onetoc2',
'Tést!.one',
'⅀⸨ Unicode ⸩.one',
];
describe('pinned Joplin .onepkg fixture', () => {
it('retains the reviewed upstream bytes', () => {
expect(JOPLIN_ONEPKG).toHaveLength(11_066);
expect(createHash('sha256').update(JOPLIN_ONEPKG).digest('hex')).toBe(
'83cb622d40f0ab127038b629553b6926be3e2a6ce642f169df0e2614c13e8469'
);
});
it('enumerates all seven UTF-8/nested entries using LZX 0x1203', () => {
const listing = enumerateCabinet(JOPLIN_ONEPKG);
expect(listing.entries.map((entry) => entry.normalizedPath)).toEqual(
EXPECTED_PATHS
);
expect(listing.folders).toHaveLength(1);
expect(listing.folders[0]).toMatchObject({
dataBlockCount: 3,
compression: { kind: 'lzx', raw: 0x1203, windowBits: 18 },
});
expect(
listing.diagnostics.filter(
(diagnostic) => diagnostic.code === 'cab-unmarked-utf8-name'
)
).toHaveLength(2);
});
it('extracts every entry and preserves native .one headers', async () => {
const extraction = await extractCabinet(JOPLIN_ONEPKG);
expect(
extraction.extractedEntries.map((entry) => entry.normalizedPath)
).toEqual(EXPECTED_PATHS);
const sections = extraction.extractedEntries.filter((entry) =>
entry.normalizedPath.toLowerCase().endsWith('.one')
);
expect(sections).toHaveLength(5);
for (const section of sections) {
expect(section.bytes.subarray(0, ONE_MAGIC.length)).toEqual(ONE_MAGIC);
}
const opened = await openOneNotePackage(JOPLIN_ONEPKG, {
sourceName: 'test.onepkg',
});
expect(opened.package.entries).toHaveLength(7);
expect(opened.package.sections).toHaveLength(5);
expect(
opened.package.sections.every(
(section) => section.parseStatus === 'not-requested'
)
).toBe(true);
expect(opened.package.tree).toMatchObject({
interpreted: true,
tocPath: 'Open Notebook.onetoc2',
nodes: [
{ kind: 'section', displayName: 'Tést!' },
{ kind: 'section', displayName: 'Another section' },
{
kind: 'section-group',
displayName: 'Section group',
children: [
{ kind: 'section', displayName: 'A' },
{ kind: 'section', displayName: 'B' },
],
},
{ kind: 'section', displayName: '⅀⸨ Unicode ⸩' },
],
});
});
it('opens and parses all five packaged sections end to end', async () => {
const opened = await openOneNotePackage(JOPLIN_ONEPKG, {
sourceName: 'test.onepkg',
parseSection: ({ bytes, normalizedPath }) => ({
status: 'parsed',
value: parseOneNoteSection(bytes, { sourceName: normalizedPath }),
}),
});
expect(opened.package.sections).toHaveLength(5);
expect(
opened.package.sections.map((section) => section.parseStatus)
).toEqual(['parsed', 'parsed', 'parsed', 'parsed', 'parsed']);
expect(
opened.package.entries.some((entry) => entry.parseStatus === 'failed')
).toBe(false);
const testSection = opened.package.sections.find(
(section) => section.path === 'Tést!.one'
);
expect(testSection?.section?.sectionName).toBe('Tést!');
expect(testSection?.section?.pages[0]).toMatchObject({
title: 'Testing…',
text: 'Link to page: Page 2',
});
});
it('interprets both desktop TOCs with their authored ordering', async () => {
const extraction = await extractCabinet(JOPLIN_ONEPKG);
const root = extraction.extractedEntries.find(
(entry) => entry.normalizedPath === 'Open Notebook.onetoc2'
);
const group = extraction.extractedEntries.find(
(entry) => entry.normalizedPath === 'Section group/Open Notebook.onetoc2'
);
expect(root).toBeDefined();
expect(group).toBeDefined();
expect(
parseOneNoteTableOfContents(root!.bytes, {
sourceName: root!.normalizedPath,
}).entries.map((entry) => [entry.orderingId, entry.filename])
).toEqual([
[1, 'Tést!.one'],
[2, 'OneNote_RecycleBin'],
[3, 'Another section.one'],
[4, 'Section group'],
[5, '⅀⸨ Unicode ⸩.one'],
]);
expect(
parseOneNoteTableOfContents(group!.bytes, {
sourceName: group!.normalizedPath,
}).entries.map((entry) => [entry.orderingId, entry.filename])
).toEqual([
[1, 'A.one'],
[2, 'B.one'],
]);
});
});