test(app): cover remaining Tauri integrations
This commit is contained in:
parent
057b3a02b7
commit
3bd01095eb
|
|
@ -1,6 +1,6 @@
|
|||
import { openFileFromPath } from '@/app/shell/menu/use'
|
||||
import { isTauri } from '@/app/tauri/env'
|
||||
import { createTab, openFileInNewTab } from '@/app/tabs'
|
||||
import { IS_TAURI } from '@open-pencil/core/constants'
|
||||
|
||||
import type { EditorStore } from '@/app/editor/active-store'
|
||||
|
||||
|
|
@ -9,16 +9,19 @@ export async function handleSaveFile(store: EditorStore): Promise<unknown> {
|
|||
return { ok: true }
|
||||
}
|
||||
|
||||
export async function ensureTauriParentDirectory(path: string): Promise<void> {
|
||||
if (!isTauri()) return
|
||||
const { mkdir } = await import('@tauri-apps/plugin-fs')
|
||||
const dir = path.replace(/[\\/][^\\/]+$/, '')
|
||||
await mkdir(dir, { recursive: true })
|
||||
}
|
||||
|
||||
export async function handleNewDocument(_store: EditorStore, args: unknown): Promise<unknown> {
|
||||
const path = (args as { path?: string }).path
|
||||
const tab = createTab()
|
||||
if (path) {
|
||||
tab.store.setPlannedFilePath(path)
|
||||
if (IS_TAURI) {
|
||||
const { mkdir } = await import('@tauri-apps/plugin-fs')
|
||||
const dir = path.replace(/[\\/][^\\/]+$/, '')
|
||||
await mkdir(dir, { recursive: true })
|
||||
}
|
||||
await ensureTauriParentDirectory(path)
|
||||
await tab.store.saveFigFile()
|
||||
tab.store.startWatchingCurrentFile()
|
||||
}
|
||||
|
|
@ -28,7 +31,7 @@ export async function handleNewDocument(_store: EditorStore, args: unknown): Pro
|
|||
export async function handleOpenFile(_store: EditorStore, args: unknown): Promise<unknown> {
|
||||
const path = (args as { path?: string }).path
|
||||
if (!path) throw new Error('Missing "path" in args')
|
||||
if (IS_TAURI) {
|
||||
if (isTauri()) {
|
||||
await openFileFromPath(path)
|
||||
} else {
|
||||
const response = await fetch(path)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ fileDialog.onChange((files) => {
|
|||
if (file) void openFileInNewTab(file)
|
||||
})
|
||||
|
||||
if (IS_BROWSER) {
|
||||
if (IS_BROWSER && 'window' in globalThis) {
|
||||
;(
|
||||
window as Window & { __OPEN_PENCIL_OPEN_FILE__?: (path: string) => Promise<void> }
|
||||
globalThis.window as Window & { __OPEN_PENCIL_OPEN_FILE__?: (path: string) => Promise<void> }
|
||||
).__OPEN_PENCIL_OPEN_FILE__ = async (path: string) => {
|
||||
const response = await fetch(path)
|
||||
const blob = await response.blob()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ function cssColorToRgba(value: string) {
|
|||
}
|
||||
|
||||
function readRulerTheme(): RulerTheme | null {
|
||||
if (!IS_BROWSER) return null
|
||||
if (!IS_BROWSER || !('document' in globalThis)) return null
|
||||
const style = getComputedStyle(document.documentElement)
|
||||
return {
|
||||
background: cssColorToRgba(style.getPropertyValue('--color-ruler-bg')),
|
||||
|
|
@ -53,7 +53,7 @@ function updateCanvasTheme(): void {
|
|||
}
|
||||
|
||||
function applyTheme(value: 'dark' | 'light', setting: AppTheme): void {
|
||||
if (!IS_BROWSER) return
|
||||
if (!IS_BROWSER || !('document' in globalThis)) return
|
||||
document.documentElement.dataset.theme = value
|
||||
document.documentElement.dataset.themeSetting = setting
|
||||
document.documentElement.style.colorScheme = value
|
||||
|
|
|
|||
17
tests/engine/tauri-fig-export.test.ts
Normal file
17
tests/engine/tauri-fig-export.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
describe('Tauri fig export', () => {
|
||||
test('delegates fig archive construction to the Tauri Rust command', async () => {
|
||||
const proc = Bun.spawn(['bun', 'tests/helpers/tauri-fig-export-fixture.ts'], {
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe'
|
||||
})
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited
|
||||
])
|
||||
|
||||
expect({ exitCode, stdout, stderr }).toEqual({ exitCode: 0, stdout: '', stderr: '' })
|
||||
})
|
||||
})
|
||||
89
tests/engine/tauri-remaining-integrations.test.ts
Normal file
89
tests/engine/tauri-remaining-integrations.test.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { afterEach, describe, expect, test, vi } from 'bun:test'
|
||||
|
||||
import { createACPTransport } from '@/app/ai/chat/transports'
|
||||
import { ensureTauriParentDirectory } from '@/app/automation/bridge/file-handlers'
|
||||
import { spawnMCPIfNeeded } from '@/app/automation/mcp/spawn'
|
||||
import { clearTauriMocks, installTauriMockWindow, mockTauriIPC } from '../helpers/tauri-mocks'
|
||||
|
||||
import type { ACPChatTransport } from '@/app/ai/acp/transport'
|
||||
|
||||
afterEach(async () => {
|
||||
await clearTauriMocks()
|
||||
vi.restoreAllMocks()
|
||||
// @ts-expect-error test cleanup
|
||||
delete globalThis.window
|
||||
// @ts-expect-error test cleanup
|
||||
delete globalThis.navigator
|
||||
// @ts-expect-error test cleanup
|
||||
delete globalThis.location
|
||||
})
|
||||
|
||||
describe('remaining Tauri integrations', () => {
|
||||
test('uses Tauri home directory for ACP transport cwd', async () => {
|
||||
await mockTauriIPC((cmd, args) => {
|
||||
expect(cmd).toBe('plugin:path|resolve_directory')
|
||||
expect(args).toEqual({ directory: 21 })
|
||||
return '/Users/tester'
|
||||
})
|
||||
|
||||
const transport = (await createACPTransport('acp:claude-code')) as ACPChatTransport & { cwd: string }
|
||||
|
||||
expect(transport.cwd).toBe('/Users/tester')
|
||||
})
|
||||
|
||||
test('creates parent directory for automation new-document paths in Tauri', async () => {
|
||||
await mockTauriIPC((cmd, args) => {
|
||||
expect(cmd).toBe('plugin:fs|mkdir')
|
||||
expect(args).toEqual({ path: '/tmp/open-pencil/nested', options: { recursive: true } })
|
||||
return null
|
||||
})
|
||||
|
||||
await ensureTauriParentDirectory('/tmp/open-pencil/nested/file.fig')
|
||||
})
|
||||
|
||||
test('spawns MCP server with shell plugin when health check is missing', async () => {
|
||||
installTauriMockWindow()
|
||||
Object.assign(globalThis.window, { location: { origin: 'tauri://localhost' } })
|
||||
Object.defineProperty(globalThis, 'navigator', {
|
||||
configurable: true,
|
||||
value: { platform: 'MacIntel' }
|
||||
})
|
||||
|
||||
let healthChecks = 0
|
||||
vi.spyOn(globalThis, 'fetch').mockImplementation(async () => {
|
||||
healthChecks += 1
|
||||
if (healthChecks === 1) return new Response('', { status: 404 })
|
||||
return new Response(JSON.stringify({ status: 'ok', token: 'server-token' }), { status: 200 })
|
||||
})
|
||||
|
||||
let onEvent: ((event: unknown) => void) | null = null
|
||||
const calls: Array<{ cmd: string; args: unknown }> = []
|
||||
await mockTauriIPC((cmd, args) => {
|
||||
calls.push({ cmd, args })
|
||||
if (cmd === 'plugin:shell|spawn') {
|
||||
onEvent = (args as { onEvent: { onmessage: (event: unknown) => void } }).onEvent.onmessage
|
||||
return 77
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
const handle = await spawnMCPIfNeeded()
|
||||
onEvent?.({ event: 'Stderr', payload: [119, 97, 114, 110] })
|
||||
handle?.disconnect()
|
||||
await Promise.resolve()
|
||||
|
||||
expect(handle?.authToken).toBe('server-token')
|
||||
expect(calls[0]?.cmd).toBe('plugin:shell|spawn')
|
||||
expect(calls[0]?.args).toMatchObject({
|
||||
program: 'openpencil-mcp-http',
|
||||
args: [],
|
||||
options: {
|
||||
env: {
|
||||
OPENPENCIL_MCP_AUTH_TOKEN: expect.any(String),
|
||||
OPENPENCIL_MCP_CORS_ORIGIN: 'tauri://localhost'
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(calls.at(-1)).toEqual({ cmd: 'plugin:shell|kill', args: { cmd: 'killChild', pid: 77 } })
|
||||
})
|
||||
})
|
||||
33
tests/helpers/tauri-fig-export-fixture.ts
Normal file
33
tests/helpers/tauri-fig-export-fixture.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { mockIPC } from '@tauri-apps/api/mocks'
|
||||
|
||||
const windowLike = globalThis as typeof globalThis & {
|
||||
__TAURI_INTERNALS__?: unknown
|
||||
__TAURI_EVENT_PLUGIN_INTERNALS__?: unknown
|
||||
}
|
||||
Object.assign(globalThis, { window: windowLike })
|
||||
|
||||
mockIPC((cmd, args) => {
|
||||
if (cmd !== 'build_fig_file') throw new Error(`Unexpected command: ${cmd}`)
|
||||
const payload = args as {
|
||||
schemaDeflated: number[]
|
||||
kiwiData: number[]
|
||||
thumbnailPng: number[]
|
||||
metaJson: string
|
||||
images: Array<{ name: string; data: number[] }>
|
||||
}
|
||||
if (payload.schemaDeflated.length === 0) throw new Error('schemaDeflated is empty')
|
||||
if (payload.kiwiData.length === 0) throw new Error('kiwiData is empty')
|
||||
if (payload.thumbnailPng.length === 0) throw new Error('thumbnailPng is empty')
|
||||
if (payload.images.length !== 0) throw new Error('images should be empty')
|
||||
JSON.parse(payload.metaJson)
|
||||
return [7, 8, 9]
|
||||
})
|
||||
|
||||
const [{ exportFigFile }, { SceneGraph }] = await Promise.all([
|
||||
import('@open-pencil/core/io/formats/fig/export'),
|
||||
import('@open-pencil/core/scene-graph')
|
||||
])
|
||||
const bytes = await exportFigFile(new SceneGraph())
|
||||
if (bytes.length !== 3 || bytes[0] !== 7 || bytes[1] !== 8 || bytes[2] !== 9) {
|
||||
throw new Error(`Unexpected export bytes: ${Array.from(bytes).join(',')}`)
|
||||
}
|
||||
Loading…
Reference in a new issue