mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 01:08:59 +00:00
128 lines
4.0 KiB
TypeScript
128 lines
4.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import type { ExternalPhoneAppDefinition } from '@/types/apps'
|
|
import {
|
|
createLbPhoneFrameDocument,
|
|
createLbPhoneHostSettings,
|
|
getLbPhoneCallbackResource,
|
|
usesLbPhoneHostRuntime,
|
|
} from '@/utils/lbPhoneAppBridge'
|
|
import { DEFAULT_PHONE_PREFERENCES } from '@/utils/preferences'
|
|
|
|
function externalApp(
|
|
overrides: Partial<ExternalPhoneAppDefinition> = {},
|
|
): ExternalPhoneAppDefinition {
|
|
return {
|
|
bridgeMode: 'legacy',
|
|
bundled: false,
|
|
capabilities: [],
|
|
category: 'games',
|
|
compatibility: { provider: 'lb_phone', resourceName: 'snake_app' },
|
|
component: null,
|
|
defaultInstalled: true,
|
|
description: 'Snake',
|
|
developer: 'Example',
|
|
dockOrder: null,
|
|
gridOrder: 100,
|
|
icon: {} as ExternalPhoneAppDefinition['icon'],
|
|
iconClass: 'app-icon--custom',
|
|
iconImage: 'https://cfx-nui-snake_app/ui/icon.png',
|
|
id: 'snake-game' as ExternalPhoneAppDefinition['id'],
|
|
kind: 'external',
|
|
name: 'Snake',
|
|
orientation: 'portrait',
|
|
ownerResource: 'phone_adapter',
|
|
readyTimeoutMs: 8000,
|
|
removable: true,
|
|
route: '/apps/snake-game' as ExternalPhoneAppDefinition['route'],
|
|
ui: 'https://cfx-nui-snake_app/ui/dist/index.html',
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
describe('LB Phone app bridge', () => {
|
|
it('selects local LB documents without taking over query-driven apps', () => {
|
|
expect(usesLbPhoneHostRuntime(externalApp())).toBe(true)
|
|
expect(
|
|
usesLbPhoneHostRuntime(
|
|
externalApp({
|
|
ui: 'https://cfx-nui-snake_app/ui/index.html?route=/dispatch',
|
|
}),
|
|
),
|
|
).toBe(false)
|
|
expect(
|
|
usesLbPhoneHostRuntime(
|
|
externalApp({ compatibility: { provider: '17mov' } }),
|
|
),
|
|
).toBe(false)
|
|
})
|
|
|
|
it('uses the declared callback resource and rejects malformed overrides', () => {
|
|
expect(getLbPhoneCallbackResource(externalApp())).toBe('snake_app')
|
|
expect(
|
|
getLbPhoneCallbackResource(
|
|
externalApp({
|
|
compatibility: {
|
|
provider: 'lb_phone',
|
|
resourceName: '../wrong',
|
|
},
|
|
}),
|
|
),
|
|
).toBe('phone_adapter')
|
|
})
|
|
|
|
it('maps phone preferences into the LB settings contract', () => {
|
|
const settings = createLbPhoneHostSettings({
|
|
deviceName: 'Main phone',
|
|
isDarkMode: true,
|
|
language: 'de',
|
|
preferences: DEFAULT_PHONE_PREFERENCES,
|
|
securityEnabled: true,
|
|
})
|
|
|
|
expect(settings).toMatchObject({
|
|
display: { brightness: 1, size: 1, theme: 'dark' },
|
|
locale: 'de',
|
|
name: 'Main phone',
|
|
security: { pinCode: true },
|
|
})
|
|
})
|
|
|
|
it('injects the LB runtime and asset base before the vendor bundle', () => {
|
|
const html =
|
|
'<!doctype html><html><head><script type="module" src="/ui/dist/assets/index.js"></script></head><body></body></html>'
|
|
const document = createLbPhoneFrameDocument(html, {
|
|
appName: 'snake-game',
|
|
resourceName: 'snake_app',
|
|
settings: createLbPhoneHostSettings({
|
|
deviceName: '</script><script>window.injected=true</script>',
|
|
isDarkMode: false,
|
|
language: 'en',
|
|
preferences: DEFAULT_PHONE_PREFERENCES,
|
|
securityEnabled: false,
|
|
}),
|
|
ui: 'https://cfx-nui-snake_app/ui/dist/index.html',
|
|
})
|
|
|
|
expect(document.indexOf('<base href=')).toBeLessThan(
|
|
document.indexOf('src="/ui/dist/assets/index.js"'),
|
|
)
|
|
expect(document).toContain('globalThis.fetchNui = async')
|
|
expect(document).toContain('globalThis.onNuiEvent = globalThis.useNuiEvent')
|
|
expect(document).toContain('https://cfx-nui-snake_app/ui/dist/')
|
|
expect(document).not.toContain('</script><script>window.injected=true')
|
|
|
|
const openingTag = '<script>'
|
|
const runtimeStart = document.indexOf(openingTag)
|
|
const runtimeEnd = document.indexOf(
|
|
'</script>',
|
|
runtimeStart + openingTag.length,
|
|
)
|
|
expect(runtimeStart).toBeGreaterThanOrEqual(0)
|
|
expect(runtimeEnd).toBeGreaterThan(runtimeStart)
|
|
|
|
const runtime = document.slice(runtimeStart + openingTag.length, runtimeEnd)
|
|
expect(() => new Function(runtime)).not.toThrow()
|
|
})
|
|
})
|