import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import {
bridgeAvailable,
nativeBridge,
nativePushFlag,
notifyNativeTurn,
requestPermissionViaBridge,
} from '@/composables/nativeBridge.js'
function makeBridge(overrides = {}) {
return {
notify: vi.fn(),
enableBackgroundMode: vi.fn(),
disableBackgroundMode: vi.fn(),
hasNotificationPermission: vi.fn(() => true),
requestNotificationPermission: vi.fn(),
isIgnoringBatteryOptimizations: vi.fn(() => true),
requestIgnoreBatteryOptimizations: vi.fn(),
...overrides,
}
}
describe('nativeBridge', () => {
// node env here may have no localStorage global — provide a minimal shim
// so the flag path is testable.
let mem
beforeEach(() => {
mem = {}
if (!globalThis.localStorage) {
vi.stubGlobal('localStorage', {
getItem: (k) => (k in mem ? mem[k] : null),
setItem: (k, v) => { mem[k] = String(v) },
removeItem: (k) => { delete mem[k] },
clear: () => { for (const key of Object.keys(mem)) delete mem[key] },
})
} else {
localStorage.clear()
}
if (window.NaviAndroid) delete window.NaviAndroid
})
afterEach(() => {
if (window.NaviAndroid) delete window.NaviAndroid
})
it('without the bridge everything is a safe no-op', () => {
expect(nativeBridge()).toBe(null)
expect(bridgeAvailable()).toBe(false)
expect(nativePushFlag()).toBe(false)
expect(() => notifyNativeTurn('t', 'b', '/x')).not.toThrow()
})
it('detects the bridge surface', () => {
window.NaviAndroid = makeBridge()
expect(bridgeAvailable()).toBe(true)
})
it('notifyNativeTurn skips unless the user enabled native push', () => {
window.NaviAndroid = makeBridge()
Object.defineProperty(document, 'hidden', { value: true, configurable: true })
notifyNativeTurn('Navi', 'body', '/#s1')
expect(window.NaviAndroid.notify).not.toHaveBeenCalled()
localStorage?.setItem?.("navi_push_android", "1")
notifyNativeTurn('Navi', 'body', '/#s1')
expect(window.NaviAndroid.notify).toHaveBeenCalledWith('Navi', 'body', '/#s1')
})
it('notifyNativeTurn skips when the window is visible', () => {
Object.defineProperty(document, 'hidden', { value: false, configurable: true })
window.NaviAndroid = makeBridge()
localStorage?.setItem?.("navi_push_android", "1")
notifyNativeTurn('Navi', 'body', '/#s1')
expect(window.NaviAndroid.notify).not.toHaveBeenCalled()
})
it('requestPermissionViaBridge resolves from the navi-perm-result event', async () => {
const bridge = makeBridge({ hasNotificationPermission: vi.fn(() => false) })
window.NaviAndroid = bridge
const p = requestPermissionViaBridge()
expect(bridge.requestNotificationPermission).toHaveBeenCalled()
window.dispatchEvent(new CustomEvent('navi-perm-result', { detail: true }))
await expect(p).resolves.toBe(true)
})
it('requestPermissionViaBridge fails closed when already-declined is echoed', async () => {
const bridge = makeBridge({ hasNotificationPermission: vi.fn(() => false) })
window.NaviAndroid = bridge
const p = requestPermissionViaBridge()
window.dispatchEvent(new CustomEvent('navi-perm-result', { detail: false }))
await expect(p).resolves.toBe(false)
})
})