81 lines
3.6 KiB
TypeScript
81 lines
3.6 KiB
TypeScript
|
|
import assert from 'node:assert/strict'
|
||
|
|
import test from 'node:test'
|
||
|
|
|
||
|
|
import {embeddedAttachmentIds, resolveMarkdownImage, resolveMarkdownLink, stableAttachments} from '../src/markdownImages.ts'
|
||
|
|
|
||
|
|
const project={id:'project-1',name:'Demo',path:'/home/config/Demo',full_access:0}
|
||
|
|
const attachments=[
|
||
|
|
{id:'shot-1',original_name:'capture navigateur.png',content_type:'image/png',size:42,url:'/api/browser/screenshots/shot-1'},
|
||
|
|
]
|
||
|
|
|
||
|
|
test('messages without attachments reuse one stable empty collection',()=>{
|
||
|
|
assert.equal(stableAttachments(undefined),stableAttachments(undefined))
|
||
|
|
})
|
||
|
|
|
||
|
|
test('an attachment reference is resolved inside markdown text',()=>{
|
||
|
|
assert.deepEqual(resolveMarkdownImage('attachment://shot-1',attachments,project),{
|
||
|
|
url:'/api/browser/screenshots/shot-1',
|
||
|
|
name:'capture navigateur.png',
|
||
|
|
attachmentId:'shot-1',
|
||
|
|
reference:'attachment://shot-1',
|
||
|
|
})
|
||
|
|
assert.deepEqual(
|
||
|
|
embeddedAttachmentIds('Avant\n\n\n\nAprès',attachments),
|
||
|
|
new Set(['shot-1']),
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('an attachment can be referenced by its encoded file name',()=>{
|
||
|
|
assert.equal(
|
||
|
|
resolveMarkdownImage('attachment://capture%20navigateur.png',attachments,project)?.url,
|
||
|
|
'/api/browser/screenshots/shot-1',
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('an absolute project image is served through the authenticated download route',()=>{
|
||
|
|
const image=resolveMarkdownImage('/home/config/Demo/docs/schema.png',[],project)
|
||
|
|
assert.equal(image?.url,'/api/projects/project-1/files/download?path=docs%2Fschema.png&inline=true')
|
||
|
|
assert.equal(image?.name,'schema.png')
|
||
|
|
})
|
||
|
|
|
||
|
|
test('an image created in a conversation worktree uses its authorized workspace route',()=>{
|
||
|
|
const conversation={id:'conversation-1',title:'Images',status:'idle' as const,updated_at:'',worktree_path:'/app/data/worktrees/conversation-1'}
|
||
|
|
const image=resolveMarkdownImage('/app/data/worktrees/conversation-1/output/result.webp',[],project,conversation)
|
||
|
|
assert.equal(image?.url,'/api/conversations/conversation-1/files/image?path=output%2Fresult.webp')
|
||
|
|
})
|
||
|
|
|
||
|
|
test('external images remain external and unrelated absolute paths are rejected',()=>{
|
||
|
|
assert.equal(resolveMarkdownImage('https://example.com/image.png',[],project)?.url,'https://example.com/image.png')
|
||
|
|
assert.equal(resolveMarkdownImage('/etc/passwd',[],project),null)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('a project archive link becomes an authenticated download opened outside the PWA',()=>{
|
||
|
|
const link=resolveMarkdownLink('/home/config/Demo/output/boards.zip',project)
|
||
|
|
assert.deepEqual(link,{
|
||
|
|
url:'/api/projects/project-1/files/download?path=output%2Fboards.zip',
|
||
|
|
name:'boards.zip',
|
||
|
|
download:true,
|
||
|
|
external:false,
|
||
|
|
presentation:'dialog',
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
test('a relative artifact created in a conversation uses its private worktree route',()=>{
|
||
|
|
const conversation={id:'conversation-1',title:'Images',status:'idle' as const,updated_at:'',worktree_path:'/app/data/worktrees/conversation-1'}
|
||
|
|
const link=resolveMarkdownLink('output/boards.zip',project,conversation)
|
||
|
|
assert.equal(link.url,'/api/conversations/conversation-1/files/download?path=output%2Fboards.zip')
|
||
|
|
assert.equal(link.name,'boards.zip')
|
||
|
|
assert.equal(link.download,true)
|
||
|
|
assert.equal(link.presentation,'dialog')
|
||
|
|
})
|
||
|
|
|
||
|
|
test('external links are opened separately without pretending to be local downloads',()=>{
|
||
|
|
assert.deepEqual(resolveMarkdownLink('https://example.com/help',project),{
|
||
|
|
url:'https://example.com/help',name:'help',download:false,external:true,presentation:'external',
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
test('ordinary links keep navigating inside the application',()=>{
|
||
|
|
assert.equal(resolveMarkdownLink('#details',project).presentation,'navigate')
|
||
|
|
})
|