接入原生壳应用角标能力
新增 HostBridge app.setBadgeCount 契约和 H5 能力门控 Expo 壳按平台声明能力并在 iOS 调用系统角标 API Tauri 壳通过主窗口设置任务栏角标并校验 payload 补齐角标能力测试、漂移检查和架构文档
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import * as Linking from 'expo-linking';
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import { Share } from 'react-native';
|
||||
import { Platform, PushNotificationIOS, Share } from 'react-native';
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
@@ -73,6 +73,9 @@ vi.mock('react-native', () => ({
|
||||
Platform: {
|
||||
OS: 'ios',
|
||||
},
|
||||
PushNotificationIOS: {
|
||||
setApplicationIconBadgeNumber: vi.fn(),
|
||||
},
|
||||
Share: {
|
||||
share: vi.fn(),
|
||||
},
|
||||
@@ -122,9 +125,15 @@ function expectFailed(response: HostBridgeResponse) {
|
||||
return response;
|
||||
}
|
||||
|
||||
function setPlatformOS(os: 'ios' | 'android') {
|
||||
(Platform as { OS: 'ios' | 'android' }).OS = os;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.mocked(Haptics.impactAsync).mockReset();
|
||||
vi.mocked(Linking.openURL).mockReset();
|
||||
vi.mocked(PushNotificationIOS.setApplicationIconBadgeNumber).mockReset();
|
||||
setPlatformOS('ios');
|
||||
vi.mocked(Sharing.isAvailableAsync).mockReset();
|
||||
vi.mocked(Sharing.isAvailableAsync).mockResolvedValue(true);
|
||||
vi.mocked(Sharing.shareAsync).mockReset();
|
||||
@@ -152,10 +161,29 @@ describe('handleMobileHostBridgeMessage', () => {
|
||||
expect(
|
||||
(okResponse.result as { capabilities: string[] }).capabilities,
|
||||
).toEqual(
|
||||
expect.arrayContaining(['host.events', 'navigation.canGoBack']),
|
||||
expect.arrayContaining([
|
||||
'host.events',
|
||||
'navigation.canGoBack',
|
||||
'app.setBadgeCount',
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('Android runtime 不声明 iOS 角标能力', async () => {
|
||||
setPlatformOS('android');
|
||||
|
||||
const response = await send(request('host.getRuntime'));
|
||||
|
||||
const okResponse = expectOk(response);
|
||||
expect(okResponse.result).toMatchObject({
|
||||
shell: 'expo_mobile',
|
||||
platform: 'android',
|
||||
});
|
||||
expect(
|
||||
(okResponse.result as { capabilities: string[] }).capabilities,
|
||||
).not.toContain('app.setBadgeCount');
|
||||
});
|
||||
|
||||
test('navigation.openNativePage 把同源路径切到移动壳 WebView', async () => {
|
||||
const openWebViewUrl = vi.fn();
|
||||
configureMobileHostBridgeNavigation({
|
||||
@@ -241,6 +269,40 @@ describe('handleMobileHostBridgeMessage', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('app.setBadgeCount 在 iOS 调起系统角标能力', async () => {
|
||||
const response = await send(
|
||||
request('app.setBadgeCount', {
|
||||
count: 12,
|
||||
}),
|
||||
);
|
||||
|
||||
expectOk(response);
|
||||
expect(
|
||||
PushNotificationIOS.setApplicationIconBadgeNumber,
|
||||
).toHaveBeenCalledWith(12);
|
||||
});
|
||||
|
||||
test('app.setBadgeCount 拒绝非法数量并在 Android 返回 unsupported', async () => {
|
||||
const invalid = await send(
|
||||
request('app.setBadgeCount', {
|
||||
count: 1.5,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(expectFailed(invalid).error.code).toBe('invalid_request');
|
||||
expect(PushNotificationIOS.setApplicationIconBadgeNumber).not.toHaveBeenCalled();
|
||||
|
||||
setPlatformOS('android');
|
||||
const unsupported = await send(
|
||||
request('app.setBadgeCount', {
|
||||
count: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(expectFailed(unsupported).error.code).toBe('unsupported_capability');
|
||||
expect(PushNotificationIOS.setApplicationIconBadgeNumber).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('share.open 使用直接分享 payload 调起系统分享', async () => {
|
||||
const response = await send(
|
||||
request('share.open', {
|
||||
|
||||
Reference in New Issue
Block a user