262 lines
9.5 KiB
TypeScript
262 lines
9.5 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest'
|
|
import { resolveImageUrls, portableImageUrls } from './vaultImages'
|
|
|
|
let tauriMode = false
|
|
|
|
vi.mock('@tauri-apps/api/core', () => ({
|
|
convertFileSrc: vi.fn((path: string) => `asset://localhost/${encodeURIComponent(path)}`),
|
|
}))
|
|
|
|
vi.mock('../mock-tauri', () => ({
|
|
isTauri: () => tauriMode,
|
|
}))
|
|
|
|
function assetUrl(path: string): string {
|
|
return `asset://localhost/${encodeURIComponent(path)}`
|
|
}
|
|
|
|
function httpAssetUrl(path: string): string {
|
|
return `http://asset.localhost/${encodeURIComponent(path)}`
|
|
}
|
|
|
|
describe('resolveImageUrls', () => {
|
|
it('is a no-op outside Tauri', () => {
|
|
tauriMode = false
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, '/vault')).toBe(markdown)
|
|
})
|
|
|
|
it('is a no-op when vaultPath is empty', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, '')).toBe(markdown)
|
|
})
|
|
|
|
it('converts relative attachment paths to asset URLs', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, '/vault')).toBe(
|
|
`})`,
|
|
)
|
|
})
|
|
|
|
it('converts Windows relative attachment paths without mixed separators', () => {
|
|
tauriMode = true
|
|
const vaultPath = 'C:\\Users\\lnq12\\Documents\\tolaria-test\\Getting Started'
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, vaultPath)).toBe(
|
|
`})`,
|
|
)
|
|
})
|
|
|
|
it('leaves already-correct asset URLs unchanged', () => {
|
|
tauriMode = true
|
|
const url = assetUrl('/vault/attachments/file.png')
|
|
const markdown = ``
|
|
|
|
expect(resolveImageUrls(markdown, '/vault')).toBe(markdown)
|
|
})
|
|
|
|
it('rewrites legacy asset URLs from a different vault', () => {
|
|
tauriMode = true
|
|
const legacyUrl = assetUrl('/Users/luca/Workspace/tolaria-getting-started/attachments/CleanShot.png')
|
|
const markdown = ``
|
|
|
|
expect(resolveImageUrls(markdown, '/Users/john/Documents/Getting Started')).toBe(
|
|
`})`,
|
|
)
|
|
})
|
|
|
|
it('rewrites Windows legacy asset URLs from a different vault', () => {
|
|
tauriMode = true
|
|
const legacyUrl = httpAssetUrl('C:\\Users\\old\\Workspace\\tolaria-getting-started\\attachments\\CleanShot.png')
|
|
const markdown = ``
|
|
|
|
expect(resolveImageUrls(markdown, 'C:\\Users\\john\\Documents\\Getting Started')).toBe(
|
|
`})`,
|
|
)
|
|
})
|
|
|
|
it('leaves already-correct http asset URLs unchanged', () => {
|
|
tauriMode = true
|
|
const url = httpAssetUrl('/vault/attachments/file.png')
|
|
const markdown = ``
|
|
|
|
expect(resolveImageUrls(markdown, '/vault')).toBe(markdown)
|
|
})
|
|
|
|
it('leaves external URLs unchanged', () => {
|
|
tauriMode = true
|
|
const httpImage = ''
|
|
const dataImage = ''
|
|
|
|
expect(resolveImageUrls(httpImage, '/vault')).toBe(httpImage)
|
|
expect(resolveImageUrls(dataImage, '/vault')).toBe(dataImage)
|
|
})
|
|
|
|
it('handles multiple images in one document', () => {
|
|
tauriMode = true
|
|
const markdown = `})\n\n`
|
|
|
|
const result = resolveImageUrls(markdown, '/vault')
|
|
|
|
expect(result).toContain(`})`)
|
|
expect(result).toContain(`})`)
|
|
})
|
|
|
|
it('preserves alt text and title attributes', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, '/vault')).toBe(
|
|
`} "starter vault")`,
|
|
)
|
|
})
|
|
|
|
it('resolves note-relative image paths against the active note directory', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, '/vault', '/vault/projects/notes/plan.md')).toBe(
|
|
`})`,
|
|
)
|
|
})
|
|
|
|
it('resolves parent traversal from the active note directory', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
|
|
expect(resolveImageUrls(markdown, '/vault', '/vault/projects/notes/plan.md')).toBe(
|
|
`})`,
|
|
)
|
|
})
|
|
|
|
it('keeps remote and data image URLs unchanged when notePath is present', () => {
|
|
tauriMode = true
|
|
const httpImage = ''
|
|
const dataImage = ''
|
|
|
|
expect(resolveImageUrls(httpImage, '/vault', '/vault/projects/plan.md')).toBe(httpImage)
|
|
expect(resolveImageUrls(dataImage, '/vault', '/vault/projects/plan.md')).toBe(dataImage)
|
|
})
|
|
|
|
it('skips unknown asset URLs without an attachments segment', () => {
|
|
tauriMode = true
|
|
const url = httpAssetUrl('/some/other/path/file.png')
|
|
const markdown = ``
|
|
|
|
expect(resolveImageUrls(markdown, '/vault')).toBe(markdown)
|
|
})
|
|
})
|
|
|
|
describe('portableImageUrls', () => {
|
|
it('converts vault attachment asset URLs to relative paths', () => {
|
|
const url = assetUrl('/vault/attachments/1776369786040-CleanShot.png')
|
|
const markdown = ``
|
|
|
|
expect(portableImageUrls(markdown, '/vault')).toBe(
|
|
'',
|
|
)
|
|
})
|
|
|
|
it('converts legacy asset protocol attachment URLs to relative paths', () => {
|
|
const url = httpAssetUrl('/vault/attachments/legacy.png')
|
|
const markdown = ``
|
|
|
|
expect(portableImageUrls(markdown, '/vault')).toBe(
|
|
'',
|
|
)
|
|
})
|
|
|
|
it('converts Windows extended-length asset URLs to relative paths', () => {
|
|
const url = httpAssetUrl('\\\\?\\C:\\Users\\lnq12\\Documents\\tolaria-test\\Getting Started\\attachments\\1777388840027-shot.png')
|
|
const markdown = ``
|
|
|
|
expect(portableImageUrls(markdown, 'C:\\Users\\lnq12\\Documents\\tolaria-test\\Getting Started')).toBe(
|
|
'',
|
|
)
|
|
})
|
|
|
|
it('is a no-op when vaultPath is empty', () => {
|
|
const url = assetUrl('/vault/attachments/file.png')
|
|
const markdown = ``
|
|
|
|
expect(portableImageUrls(markdown, '')).toBe(markdown)
|
|
})
|
|
|
|
it('unwraps asset URLs from other vaults to absolute filesystem paths', () => {
|
|
const url = assetUrl('/other-vault/attachments/file.png')
|
|
const markdown = ``
|
|
|
|
expect(portableImageUrls(markdown, '/vault')).toBe('')
|
|
})
|
|
|
|
it('leaves relative and external paths unchanged', () => {
|
|
const relativeImage = ''
|
|
const httpImage = ''
|
|
|
|
expect(portableImageUrls(relativeImage, '/vault')).toBe(relativeImage)
|
|
expect(portableImageUrls(httpImage, '/vault')).toBe(httpImage)
|
|
})
|
|
|
|
it('handles multiple images', () => {
|
|
const markdown = `})\n\n})`
|
|
|
|
const result = portableImageUrls(markdown, '/vault')
|
|
|
|
expect(result).toContain('')
|
|
expect(result).toContain('')
|
|
})
|
|
|
|
it('preserves title attributes when converting to portable paths', () => {
|
|
const markdown = `} "starter vault")`
|
|
|
|
expect(portableImageUrls(markdown, '/vault')).toBe('')
|
|
})
|
|
|
|
it('serializes vault-local asset URLs relative to the active note directory', () => {
|
|
const markdown = `})`
|
|
|
|
expect(portableImageUrls(markdown, '/vault', '/vault/projects/notes/plan.md')).toBe(
|
|
'',
|
|
)
|
|
})
|
|
|
|
it('serializes vault-local asset URLs with parent traversal when needed', () => {
|
|
const markdown = `})`
|
|
|
|
expect(portableImageUrls(markdown, '/vault', '/vault/projects/notes/plan.md')).toBe(
|
|
'',
|
|
)
|
|
})
|
|
|
|
it('unwraps external asset URLs to filesystem paths instead of saving asset scheme URLs', () => {
|
|
const markdown = `})`
|
|
|
|
expect(portableImageUrls(markdown, '/vault')).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('resolveImageUrls / portableImageUrls round-trip', () => {
|
|
it('keeps relative attachment markdown stable', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
|
|
expect(portableImageUrls(resolveImageUrls(markdown, '/vault'), '/vault')).toBe(markdown)
|
|
})
|
|
|
|
it('keeps note-relative markdown stable', () => {
|
|
tauriMode = true
|
|
const markdown = ''
|
|
const notePath = '/vault/projects/notes/plan.md'
|
|
|
|
expect(
|
|
portableImageUrls(resolveImageUrls(markdown, '/vault', notePath), '/vault', notePath),
|
|
).toBe(markdown)
|
|
})
|
|
})
|