锁定移动通知权限不可用边界
移动通知桥接把权限查询失败转为稳定 host_error 移动通知桥接把权限请求失败转为稳定 host_error 移动通知单测覆盖权限不可用时不调度系统通知 移动壳配置检查反查通知权限不可用边界
This commit is contained in:
@@ -104,6 +104,11 @@ const hostBridgeNetworkTestSource = fs.readFileSync(
|
||||
);
|
||||
const notificationsPath = new URL('../src/host-bridge/notifications.ts', import.meta.url);
|
||||
const notificationsSource = fs.readFileSync(notificationsPath, 'utf8');
|
||||
const notificationsTestPath = new URL(
|
||||
'../src/host-bridge/notifications.test.ts',
|
||||
import.meta.url,
|
||||
);
|
||||
const notificationsTestSource = fs.readFileSync(notificationsTestPath, 'utf8');
|
||||
const protocolPath = new URL('../src/host-bridge/protocol.ts', import.meta.url);
|
||||
const protocolSource = fs.readFileSync(protocolPath, 'utf8');
|
||||
const protocolTestPath = new URL(
|
||||
@@ -1936,6 +1941,7 @@ for (const notificationSnippet of [
|
||||
'request: HostBridgeRequest',
|
||||
'normalizeHostBridgeLocalNotification(request.payload)',
|
||||
"invalidRequest('title is required')",
|
||||
"message: 'notification permission unavailable'",
|
||||
'showMobileLocalNotification(notification)',
|
||||
'ok(request, await showMobileLocalNotification(notification))',
|
||||
'HOST_BRIDGE_LOCAL_NOTIFICATION_DELIVERED_TO_SYSTEM_RESULT',
|
||||
@@ -1945,6 +1951,18 @@ for (const notificationSnippet of [
|
||||
}
|
||||
}
|
||||
|
||||
for (const snippet of [
|
||||
'rejects delivery when current permission lookup is unavailable',
|
||||
'rejects delivery when permission request is unavailable',
|
||||
"message: 'notification permission unavailable'",
|
||||
'expect(Notifications.setNotificationChannelAsync).not.toHaveBeenCalled()',
|
||||
'expect(Notifications.scheduleNotificationAsync).not.toHaveBeenCalled()',
|
||||
]) {
|
||||
if (!notificationsTestSource.includes(snippet)) {
|
||||
throw new Error(`mobile shell notification tests missing ${snippet}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const snippet of [
|
||||
'file.exportText',
|
||||
'file.importText',
|
||||
|
||||
@@ -152,6 +152,42 @@ describe('mobile local notification helpers', () => {
|
||||
expect(Notifications.scheduleNotificationAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('rejects delivery when current permission lookup is unavailable', async () => {
|
||||
vi.mocked(Notifications.getPermissionsAsync).mockRejectedValueOnce(
|
||||
new Error('native permission failed'),
|
||||
);
|
||||
|
||||
await expect(
|
||||
showMobileLocalNotification({ title: '生成完成' }),
|
||||
).rejects.toMatchObject({
|
||||
code: 'host_error',
|
||||
message: 'notification permission unavailable',
|
||||
});
|
||||
|
||||
expect(Notifications.requestPermissionsAsync).not.toHaveBeenCalled();
|
||||
expect(Notifications.setNotificationChannelAsync).not.toHaveBeenCalled();
|
||||
expect(Notifications.scheduleNotificationAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('rejects delivery when permission request is unavailable', async () => {
|
||||
vi.mocked(Notifications.getPermissionsAsync).mockResolvedValue(
|
||||
DENIED_NOTIFICATION_PERMISSION,
|
||||
);
|
||||
vi.mocked(Notifications.requestPermissionsAsync).mockRejectedValueOnce(
|
||||
new Error('native permission failed'),
|
||||
);
|
||||
|
||||
await expect(
|
||||
showMobileLocalNotification({ title: '生成完成' }),
|
||||
).rejects.toMatchObject({
|
||||
code: 'host_error',
|
||||
message: 'notification permission unavailable',
|
||||
});
|
||||
|
||||
expect(Notifications.setNotificationChannelAsync).not.toHaveBeenCalled();
|
||||
expect(Notifications.scheduleNotificationAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('schedules iOS notification without channel trigger', async () => {
|
||||
await showMobileLocalNotification({
|
||||
title: '生成完成',
|
||||
|
||||
@@ -30,18 +30,38 @@ function hasNotificationPermission(
|
||||
}
|
||||
|
||||
async function ensureNotificationPermission() {
|
||||
const currentPermission = await Notifications.getPermissionsAsync();
|
||||
let currentPermission: Awaited<
|
||||
ReturnType<typeof Notifications.getPermissionsAsync>
|
||||
>;
|
||||
try {
|
||||
currentPermission = await Notifications.getPermissionsAsync();
|
||||
} catch {
|
||||
throw {
|
||||
code: 'host_error',
|
||||
message: 'notification permission unavailable',
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
if (hasNotificationPermission(currentPermission)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestedPermission = await Notifications.requestPermissionsAsync({
|
||||
ios: {
|
||||
allowAlert: true,
|
||||
allowBadge: false,
|
||||
allowSound: false,
|
||||
},
|
||||
});
|
||||
let requestedPermission: Awaited<
|
||||
ReturnType<typeof Notifications.requestPermissionsAsync>
|
||||
>;
|
||||
try {
|
||||
requestedPermission = await Notifications.requestPermissionsAsync({
|
||||
ios: {
|
||||
allowAlert: true,
|
||||
allowBadge: false,
|
||||
allowSound: false,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
throw {
|
||||
code: 'host_error',
|
||||
message: 'notification permission unavailable',
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
if (!hasNotificationPermission(requestedPermission)) {
|
||||
throw {
|
||||
code: 'host_error',
|
||||
|
||||
Reference in New Issue
Block a user