/
githubmirror
/
obsidian-importer
Обзор
Документация
Войти
/
githubmirror
/
obsidian-importer
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/write/plan-note.test.ts
189 строк
8 KB
Steph Ango
Add "Update" to Notion API and Airtable API importers (#619)
11 авг 2026, 02:34
Не верифицирован
11 авг 2026, 02:34
de0c5bf
Код
Авторство
О чём код?
/** * Planning a note before its markdown exists. * * writeNote decides everything at the moment it writes, which is fine for an * importer holding a converted note already. An importer reading an API needs * the answer earlier: where the note goes settles where its attachments and * links point, and whether the source has moved on settles whether converting * it is worth doing at all. * * write-note.test.ts covers the rules themselves. This covers reaching them in * two steps rather than one. */ import '../shims/runtime'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { DuplicateHandling, FormatImporter, NoteDisposition, PlannedNote } from '../../src/format-importer'; import { ImportContext } from '../../src/import-context'; import { MemoryVault, memoryApp } from '../shims/vault'; class PlanningImporter extends FormatImporter { init(): void {} async import(_ctx: ImportContext): Promise<void> {} preflight(ctx: ImportContext, planned: PlannedNote, sourceMtime?: number): NoteDisposition { return this.preflightNote(ctx, planned, sourceMtime); } } function importer(duplicateHandling: DuplicateHandling, idProperty?: string) { const vault = new MemoryVault(); const subject = new PlanningImporter(memoryApp(vault), { sourceEl: null, optionsEl: null } as never); subject.duplicateHandling = duplicateHandling; subject.idProperty = idProperty ?? null; subject.indexImportedNotes(); return { vault, subject, ctx: new ImportContext() }; } test('a name nothing holds is planned as itself', () => { const { vault, subject } = importer(DuplicateHandling.Update); const planned = subject.planNote(vault.root, 'Note'); assert.equal(planned.desiredPath, 'Note.md'); assert.equal(planned.targetPath, 'Note.md'); assert.equal(planned.file, null); }); test('two notes planned before either is written are given different names', () => { const { vault, subject } = importer(DuplicateHandling.Update); const first = subject.planNote(vault.root, 'Note'); const second = subject.planNote(vault.root, 'Note'); assert.equal(first.targetPath, 'Note.md'); assert.equal(second.targetPath, 'Note 1.md', 'the second should not be handed a name the first is holding'); }); test('a note the user moved is planned where it now is, not where it would have gone', async () => { const { vault, subject } = importer(DuplicateHandling.Update, 'notion-id'); await vault.createFolder('Archive'); await vault.create('Archive/Note.md', '---\nnotion-id: abc\n---\nmoved\n'); subject.indexImportedNotes(); const planned = subject.planNote(vault.root, 'Note', 'abc'); assert.equal(planned.desiredPath, 'Note.md'); assert.equal(planned.targetPath, 'Archive/Note.md'); assert.equal(planned.file?.path, 'Archive/Note.md'); }); test('a note this run has already planned onto is not planned onto twice', async () => { const { vault, subject } = importer(DuplicateHandling.Update, 'airtable-id'); await vault.create('Note.md', 'a note from before ids were recorded\n'); subject.indexImportedNotes(); const first = subject.planNote(vault.root, 'Note', 'rec1'); const second = subject.planNote(vault.root, 'Note', 'rec2'); assert.equal(first.file?.path, 'Note.md'); assert.equal(second.file, null, 'the second record cannot have the note the first took'); assert.equal(second.targetPath, 'Note 1.md'); }); // A note is planned before it is written, so the name it is holding is not // free even though the vault has nothing at it yet. test('an attachment is not given a name a note is waiting to be written under', async () => { const { vault, subject } = importer(DuplicateHandling.Update); const planned = subject.planNote(vault.root, 'notes'); const attachment = await subject.getAvailablePathForAttachment('notes.md', [], 'Somewhere.md'); assert.equal(planned.targetPath, 'notes.md'); assert.notEqual(attachment, 'notes.md'); }); test('what preflight makes of a note nothing matches', () => { const { vault, subject, ctx } = importer(DuplicateHandling.Update); assert.equal(subject.preflight(ctx, subject.planNote(vault.root, 'Note')), 'create'); assert.deepEqual(ctx.skipped, []); }); test('"Create a copy" over a name already taken plans a copy', async () => { const { vault, subject, ctx } = importer(DuplicateHandling.CreateCopy); await vault.create('Note.md', 'already here'); const planned = subject.planNote(vault.root, 'Note'); assert.equal(planned.targetPath, 'Note 1.md'); assert.equal(subject.preflight(ctx, planned), 'copy'); }); test('the source time decides, and says why', async () => { const cases: [number, NoteDisposition, number][] = [ [2_000, 'unchanged', 1], [1_000, 'preserve', 1], [3_000, 'update', 0], ]; for (const [sourceMtime, expected, skipped] of cases) { const { vault, subject, ctx } = importer(DuplicateHandling.Update); await vault.create('Note.md', 'here', { mtime: 2_000 }); const disposition = subject.preflight(ctx, subject.planNote(vault.root, 'Note'), sourceMtime); assert.equal(disposition, expected, `mtime ${sourceMtime}`); assert.equal(ctx.skipped.length, skipped, `mtime ${sourceMtime} should ${skipped ? '' : 'not '}report`); } }); test('with no source time there is nothing to decide on yet', async () => { const { vault, subject, ctx } = importer(DuplicateHandling.Update); await vault.create('Note.md', 'here'); assert.equal(subject.preflight(ctx, subject.planNote(vault.root, 'Note')), 'compare-content'); assert.deepEqual(ctx.skipped, [], 'nothing has been decided, so nothing should be reported'); }); test('"Skip" settles it at preflight, so the markdown need never be made', async () => { const { vault, subject, ctx } = importer(DuplicateHandling.Skip); await vault.create('Note.md', 'here'); assert.equal(subject.preflight(ctx, subject.planNote(vault.root, 'Note')), 'skip'); assert.deepEqual(ctx.skipped, ['Note']); }); test('an answer preflight already gave is acted on rather than asked again', async () => { const { vault, subject, ctx } = importer(DuplicateHandling.Update); await vault.create('Note.md', 'here', { mtime: 2_000 }); const planned = subject.planNote(vault.root, 'Note'); const disposition = subject.preflight(ctx, planned, 2_000); const { written, outcome } = await subject.writePlannedNote(ctx, planned, 'converted anyway', { disposition }); assert.equal(written, false); assert.equal(outcome, 'unchanged'); assert.equal(vault.contents.get('Note.md'), 'here'); assert.equal(ctx.skipped.length, 1, 'preflight reported it, so the write should not report it again'); }); test('every outcome a write can end in', async () => { const created = importer(DuplicateHandling.Update); assert.equal((await created.subject.writePlannedNote( created.ctx, created.subject.planNote(created.vault.root, 'Note'), 'body')).outcome, 'created'); const updated = importer(DuplicateHandling.Update); await updated.vault.create('Note.md', 'old', { mtime: 1_000 }); assert.equal((await updated.subject.writePlannedNote( updated.ctx, updated.subject.planNote(updated.vault.root, 'Note'), 'new', { mtime: 2_000 })).outcome, 'updated'); const skipped = importer(DuplicateHandling.Skip); await skipped.vault.create('Note.md', 'old'); assert.equal((await skipped.subject.writePlannedNote( skipped.ctx, skipped.subject.planNote(skipped.vault.root, 'Note'), 'new')).outcome, 'skipped'); const unchanged = importer(DuplicateHandling.Update); await unchanged.vault.create('Note.md', 'old', { mtime: 2_000 }); assert.equal((await unchanged.subject.writePlannedNote( unchanged.ctx, unchanged.subject.planNote(unchanged.vault.root, 'Note'), 'new', { mtime: 2_000 })).outcome, 'unchanged'); const preserved = importer(DuplicateHandling.Update); await preserved.vault.create('Note.md', 'edited by hand', { mtime: 3_000 }); assert.equal((await preserved.subject.writePlannedNote( preserved.ctx, preserved.subject.planNote(preserved.vault.root, 'Note'), 'new', { mtime: 2_000 })).outcome, 'preserved'); assert.equal(preserved.vault.contents.get('Note.md'), 'edited by hand'); });