137 lines
5.1 KiB
TypeScript
137 lines
5.1 KiB
TypeScript
import { execFileSync } from 'node:child_process'
|
||
import * as path from 'node:path'
|
||
|
||
import { type MockBackendFixture, setupMockBackend, waitForAppReady } from './fixtures'
|
||
import { expect, test } from './test'
|
||
|
||
let fixture: MockBackendFixture | null = null
|
||
type DesktopTestWindow = Window & {
|
||
hermesDesktop: {
|
||
api<T = unknown>(request: { path: string }): Promise<T>
|
||
setUiLanguage(locale: string): void
|
||
selectSavePath(options?: { title?: string; defaultPath?: string }): Promise<string | null>
|
||
}
|
||
}
|
||
|
||
test.beforeAll(async () => {
|
||
test.setTimeout(150_000)
|
||
fixture = await setupMockBackend({ extraDisplayConfig: ' language: tr' })
|
||
await waitForAppReady(fixture, 120_000)
|
||
await expect
|
||
.poll(
|
||
async () => {
|
||
const status = await fixture!.page.evaluate(() =>
|
||
(window as unknown as DesktopTestWindow).hermesDesktop.api({ path: '/api/status' })
|
||
)
|
||
|
||
return Boolean(status)
|
||
},
|
||
{ timeout: 30_000 }
|
||
)
|
||
.toBe(true)
|
||
})
|
||
|
||
test.afterAll(async () => {
|
||
await fixture?.cleanup()
|
||
fixture = null
|
||
})
|
||
|
||
test('Turkish safety settings persist the backend enum through the real desktop bridge', async () => {
|
||
const { page, sandbox } = fixture!
|
||
await page.evaluate(() => {
|
||
window.location.hash = '/settings?tab=config%3Asafety'
|
||
})
|
||
await expect(page.getByText('Gizli bilgileri maskele', { exact: true })).toBeVisible()
|
||
await expect(
|
||
page.getByText('Algılanan gizli bilgileri mümkün olduğunda modelin görebildiği içerikten gizler.', { exact: true })
|
||
).toBeVisible()
|
||
const approvals = page.locator('[data-tour="field-approvals.mode"]')
|
||
await approvals.getByRole('combobox').click()
|
||
await page.getByRole('option', { name: 'Elle onayla', exact: true }).click()
|
||
await expect
|
||
.poll(
|
||
async () => {
|
||
const config = await page.evaluate(() =>
|
||
(window as unknown as DesktopTestWindow).hermesDesktop.api<Record<string, unknown>>({ path: '/api/config' })
|
||
)
|
||
|
||
return (config.approvals as { mode?: string } | undefined)?.mode
|
||
},
|
||
{ timeout: 30_000 }
|
||
)
|
||
.toBe('manual')
|
||
await expect
|
||
.poll(
|
||
() => {
|
||
return execFileSync(
|
||
process.env.HERMES_DESKTOP_PYTHON || 'python',
|
||
[
|
||
'-c',
|
||
'import pathlib, sys, yaml; print(yaml.safe_load(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8")).get("approvals", {}).get("mode", ""))',
|
||
path.join(sandbox.hermesHome, 'config.yaml')
|
||
],
|
||
{ encoding: 'utf8' }
|
||
).trim()
|
||
},
|
||
{ timeout: 15_000 }
|
||
)
|
||
.toBe('manual')
|
||
await expect(approvals.getByRole('combobox')).toContainText('Elle onayla')
|
||
await page.screenshot({ path: test.info().outputPath('turkish-safety-settings.png'), fullPage: true })
|
||
})
|
||
|
||
test('native language bridge updates the Mac menu and preserves menu-free Windows', async () => {
|
||
const { app, page } = fixture!
|
||
const platform = await app.evaluate(() => process.platform)
|
||
for (const locale of ['en', 'tr']) {
|
||
await page.evaluate(language => {
|
||
;(window as unknown as DesktopTestWindow).hermesDesktop.setUiLanguage(language)
|
||
}, locale)
|
||
await expect
|
||
.poll(() => app.evaluate(({ Menu }) => Menu.getApplicationMenu()?.items.map((item: { label: string }) => item.label) ?? null))
|
||
.toEqual(
|
||
platform === 'darwin'
|
||
? expect.arrayContaining([locale === 'tr' ? 'Dosya' : 'File', locale === 'tr' ? 'Düzen' : 'Edit'])
|
||
: null
|
||
)
|
||
}
|
||
})
|
||
|
||
test('native save dialog follows language while preserving caller title and path', async () => {
|
||
const { app, page } = fixture!
|
||
await app.evaluate(({ dialog }) => {
|
||
const state = globalThis as typeof globalThis & { originalSave?: unknown; saveOptions?: unknown }
|
||
state.originalSave = dialog.showSaveDialog
|
||
dialog.showSaveDialog = async (_parent: unknown, options: unknown) => {
|
||
state.saveOptions = options
|
||
return { canceled: true, filePath: '' }
|
||
}
|
||
})
|
||
try {
|
||
for (const locale of ['tr', 'en']) {
|
||
const result = await page.evaluate(async language => {
|
||
const bridge = (window as unknown as DesktopTestWindow).hermesDesktop
|
||
bridge.setUiLanguage(language)
|
||
return bridge.selectSavePath({ defaultPath: 'My custom archive.zip' })
|
||
}, locale)
|
||
expect(result).toBeNull()
|
||
const options = await app.evaluate(() => (globalThis as typeof globalThis & { saveOptions?: unknown }).saveOptions)
|
||
expect(options).toMatchObject({ title: locale === 'tr' ? 'Kaydet' : 'Save', defaultPath: 'My custom archive.zip' })
|
||
}
|
||
await page.evaluate(async () => {
|
||
const bridge = (window as unknown as DesktopTestWindow).hermesDesktop
|
||
bridge.setUiLanguage('tr')
|
||
await bridge.selectSavePath({ title: 'My custom title' })
|
||
})
|
||
expect(await app.evaluate(() => (globalThis as typeof globalThis & { saveOptions?: unknown }).saveOptions))
|
||
.toMatchObject({ title: 'My custom title' })
|
||
} finally {
|
||
await app.evaluate(({ dialog }) => {
|
||
const state = globalThis as typeof globalThis & { originalSave?: typeof dialog.showSaveDialog; saveOptions?: unknown }
|
||
dialog.showSaveDialog = state.originalSave!
|
||
delete state.originalSave
|
||
delete state.saveOptions
|
||
})
|
||
}
|
||
})
|