openpencil/tests/engine/tauri/file-actions.test.ts
Victor Wads 4e48420ac1
feat(app): open multiple files from the Open dialog (#552)
- Open multiple selected design files in separate tabs.\n- Support desktop, File System Access, and fallback pickers.\n- Continue opening later selections when one file fails.
2026-08-18 11:30:36 +03:00

124 lines
3.7 KiB
TypeScript

import { afterEach, describe, expect, test } from 'bun:test'
import { saveExportedFile } from '@/app/document/export/files'
import { watchTauriFile } from '@/app/document/io/watch-targets'
import { chooseTauriOpenPaths, readTauriDesignFile } from '@/app/shell/menu/files'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri/mocks'
afterEach(async () => {
await clearTauriMocks()
Reflect.deleteProperty(globalThis, 'window')
})
describe('Tauri file actions', () => {
test('chooses multiple design files through plugin-dialog', async () => {
await mockTauriIPC((cmd, args) => {
expect(cmd).toBe('plugin:dialog|open')
expect(args).toEqual({
options: {
filters: [{ name: 'Design file', extensions: ['fig', 'pen', 'html', 'htm', 'xhtml'] }],
multiple: true
}
})
return ['/tmp/design.fig', '/tmp/design.pen']
})
await expect(chooseTauriOpenPaths()).resolves.toEqual(['/tmp/design.fig', '/tmp/design.pen'])
})
test('reads a Tauri design file into a File object', async () => {
await mockTauriIPC((cmd, args) => {
expect(cmd).toBe('plugin:fs|read_file')
expect(args).toEqual({ path: '/tmp/design.pen', options: undefined })
return [123, 34, 97, 34, 58, 49, 125]
})
const file = await readTauriDesignFile('/tmp/design.pen')
expect(file.name).toBe('design.pen')
await expect(file.text()).resolves.toBe('{"a":1}')
})
test('saves exports through Tauri dialog and fs APIs', async () => {
const calls: Array<{ cmd: string; args: unknown; options: unknown }> = []
await mockTauriIPC((cmd, args, options) => {
calls.push({ cmd, args, options })
if (cmd === 'plugin:dialog|save') return '/tmp/export.png'
return null
})
await saveExportedFile(
new Uint8Array([1, 2, 3]),
'Export@1x.png',
'PNG',
'.png',
'image/png',
() => {
throw new Error('browser download should not run in Tauri')
}
)
expect(calls[0]).toEqual({
cmd: 'plugin:dialog|save',
args: {
options: {
defaultPath: 'Export@1x.png',
filters: [{ name: 'PNG', extensions: ['png'] }]
}
},
options: undefined
})
expect(calls[1]?.cmd).toBe('plugin:fs|write_file')
expect([...new Uint8Array(calls[1]?.args as ArrayBuffer)]).toEqual([1, 2, 3])
expect(calls[1]?.options).toEqual({
headers: { path: '%2Ftmp%2Fexport.png', options: undefined }
})
})
test('watches Tauri files and ignores recent local writes', async () => {
let onEvent: ((event: unknown) => void) | null = null
const calls: string[] = []
await mockTauriIPC((cmd, args) => {
if (cmd === 'plugin:fs|watch') {
expect(args).toMatchObject({
paths: ['/tmp/design.fig'],
options: { delayMs: 500 }
})
onEvent = (args as { onEvent: { onmessage: (event: unknown) => void } }).onEvent.onmessage
return 7
}
calls.push(cmd)
return null
})
const unwatch = await watchTauriFile(
'/tmp/design.fig',
() => 0,
() => calls.push('reload')
)
onEvent?.({ type: { modify: { kind: 'data' } } })
expect(calls).toEqual(['reload'])
await clearTauriMocks()
await mockTauriIPC((cmd, args) => {
if (cmd === 'plugin:fs|watch') {
onEvent = (args as { onEvent: { onmessage: (event: unknown) => void } }).onEvent.onmessage
return 8
}
calls.push(cmd)
return null
})
calls.length = 0
await watchTauriFile(
'/tmp/design.fig',
() => Date.now(),
() => calls.push('reload')
)
onEvent?.({ type: { modify: { kind: 'data' } } })
expect(calls).toEqual([])
unwatch()
})
})