664 lines
28 KiB
JavaScript
664 lines
28 KiB
JavaScript
// src/components/NotificationTestTool/index.js
|
||
/**
|
||
* 金融资讯通知测试工具 - 仅在开发环境显示
|
||
* 用于手动测试4种通知类型
|
||
*/
|
||
|
||
import React, { useState, useEffect } from 'react';
|
||
import {
|
||
Box,
|
||
Button,
|
||
VStack,
|
||
HStack,
|
||
Text,
|
||
IconButton,
|
||
Collapse,
|
||
useDisclosure,
|
||
Badge,
|
||
Divider,
|
||
Alert,
|
||
AlertIcon,
|
||
AlertTitle,
|
||
AlertDescription,
|
||
Code,
|
||
UnorderedList,
|
||
ListItem,
|
||
} from '@chakra-ui/react';
|
||
import { MdNotifications, MdClose, MdVolumeOff, MdVolumeUp, MdCampaign, MdTrendingUp, MdArticle, MdAssessment, MdWarning } from 'react-icons/md';
|
||
import { useNotification } from '../../contexts/NotificationContext';
|
||
import { NOTIFICATION_TYPES, PRIORITY_LEVELS } from '../../constants/notificationTypes';
|
||
|
||
const NotificationTestTool = () => {
|
||
// 只在开发环境显示 - 必须在所有 Hooks 调用之前检查
|
||
if (process.env.NODE_ENV !== 'development') {
|
||
return null;
|
||
}
|
||
|
||
const { isOpen, onToggle } = useDisclosure();
|
||
const { addNotification, soundEnabled, toggleSound, isConnected, clearAllNotifications, notifications, browserPermission, requestBrowserPermission } = useNotification();
|
||
const [testCount, setTestCount] = useState(0);
|
||
|
||
// 测试状态
|
||
const [isTestingNotification, setIsTestingNotification] = useState(false);
|
||
const [testCountdown, setTestCountdown] = useState(0);
|
||
const [notificationShown, setNotificationShown] = useState(null); // null | true | false
|
||
|
||
// 系统环境检测
|
||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||
const [isMacOS, setIsMacOS] = useState(false);
|
||
|
||
// 故障排查面板
|
||
const { isOpen: isTroubleshootOpen, onToggle: onTroubleshootToggle } = useDisclosure();
|
||
|
||
// 检测系统环境
|
||
useEffect(() => {
|
||
// 检测是否为 macOS
|
||
const platform = navigator.platform.toLowerCase();
|
||
setIsMacOS(platform.includes('mac'));
|
||
|
||
// 检测全屏状态
|
||
const checkFullscreen = () => {
|
||
setIsFullscreen(!!document.fullscreenElement);
|
||
};
|
||
document.addEventListener('fullscreenchange', checkFullscreen);
|
||
checkFullscreen();
|
||
|
||
return () => {
|
||
document.removeEventListener('fullscreenchange', checkFullscreen);
|
||
};
|
||
}, []);
|
||
|
||
// 倒计时逻辑
|
||
useEffect(() => {
|
||
if (testCountdown > 0) {
|
||
const timer = setTimeout(() => {
|
||
setTestCountdown(testCountdown - 1);
|
||
}, 1000);
|
||
return () => clearTimeout(timer);
|
||
} else if (testCountdown === 0 && isTestingNotification) {
|
||
// 倒计时结束,询问用户
|
||
setIsTestingNotification(false);
|
||
|
||
// 延迟一下再询问,确保用户有时间看到通知
|
||
setTimeout(() => {
|
||
const sawNotification = window.confirm('您是否看到了浏览器桌面通知?\n\n点击"确定"表示看到了\n点击"取消"表示没看到');
|
||
setNotificationShown(sawNotification);
|
||
|
||
if (!sawNotification) {
|
||
// 没看到通知,展开故障排查面板
|
||
if (!isTroubleshootOpen) {
|
||
onTroubleshootToggle();
|
||
}
|
||
}
|
||
}, 500);
|
||
}
|
||
}, [testCountdown, isTestingNotification, isTroubleshootOpen, onTroubleshootToggle]);
|
||
|
||
// 浏览器权限状态标签
|
||
const getPermissionLabel = () => {
|
||
switch (browserPermission) {
|
||
case 'granted':
|
||
return '已授权';
|
||
case 'denied':
|
||
return '已拒绝';
|
||
case 'default':
|
||
return '未授权';
|
||
default:
|
||
return '不支持';
|
||
}
|
||
};
|
||
|
||
const getPermissionColor = () => {
|
||
switch (browserPermission) {
|
||
case 'granted':
|
||
return 'green';
|
||
case 'denied':
|
||
return 'red';
|
||
case 'default':
|
||
return 'gray';
|
||
default:
|
||
return 'gray';
|
||
}
|
||
};
|
||
|
||
// 请求浏览器权限
|
||
const handleRequestPermission = async () => {
|
||
await requestBrowserPermission();
|
||
};
|
||
|
||
// 公告通知测试数据
|
||
const testAnnouncement = () => {
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.ANNOUNCEMENT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '【测试】贵州茅台发布2024年度财报公告',
|
||
content: '2024年度营收同比增长15.2%,净利润创历史新高,董事会建议每10股派息180元',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: false,
|
||
clickable: true,
|
||
link: '/event-detail/test001',
|
||
extra: {
|
||
announcementType: '财报',
|
||
companyCode: '600519',
|
||
companyName: '贵州茅台',
|
||
},
|
||
autoClose: 10000,
|
||
});
|
||
setTestCount(prev => prev + 1);
|
||
};
|
||
|
||
|
||
// 事件动向测试数据
|
||
const testEventAlert = () => {
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '【测试】央行宣布降准0.5个百分点',
|
||
content: '中国人民银行宣布下调金融机构存款准备金率0.5个百分点,释放长期资金约1万亿元,利好股市',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: false,
|
||
clickable: true,
|
||
link: '/event-detail/test003',
|
||
extra: {
|
||
eventId: 'test003',
|
||
relatedStocks: 12,
|
||
impactLevel: '重大利好',
|
||
},
|
||
autoClose: 12000,
|
||
});
|
||
setTestCount(prev => prev + 1);
|
||
};
|
||
|
||
// 分析报告测试数据(非AI)
|
||
const testAnalysisReport = () => {
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.ANALYSIS_REPORT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '【测试】医药行业深度报告:创新药迎来政策拐点',
|
||
content: 'CXO板块持续受益于全球创新药研发外包需求,建议关注药明康德、凯莱英等龙头企业',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
author: {
|
||
name: '李明',
|
||
organization: '中信证券',
|
||
},
|
||
isAIGenerated: false,
|
||
clickable: true,
|
||
link: '/forecast-report?id=test004',
|
||
extra: {
|
||
reportType: '行业研报',
|
||
industry: '医药',
|
||
},
|
||
autoClose: 12000,
|
||
});
|
||
setTestCount(prev => prev + 1);
|
||
};
|
||
|
||
|
||
// 预测通知测试数据(不可跳转)
|
||
const testPrediction = () => {
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||
priority: PRIORITY_LEVELS.NORMAL,
|
||
title: '【测试】【预测】央行可能宣布降准政策',
|
||
content: '基于最新宏观数据分析,预计央行将在本周宣布降准0.5个百分点,释放长期资金',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: true,
|
||
clickable: false, // ❌ 不可点击
|
||
link: null,
|
||
extra: {
|
||
isPrediction: true,
|
||
statusHint: '详细报告生成中...',
|
||
},
|
||
autoClose: 15000,
|
||
});
|
||
setTestCount(prev => prev + 1);
|
||
};
|
||
|
||
// 预测→详情流程测试(先推预测,5秒后推详情)
|
||
const testPredictionFlow = () => {
|
||
// 阶段 1: 推送预测
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||
priority: PRIORITY_LEVELS.NORMAL,
|
||
title: '【测试】【预测】新能源汽车补贴政策将延期',
|
||
content: '根据政策趋势分析,预计财政部将宣布新能源汽车购置补贴政策延长至2025年底',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: true,
|
||
clickable: false,
|
||
link: null,
|
||
extra: {
|
||
isPrediction: true,
|
||
statusHint: '详细报告生成中...',
|
||
relatedPredictionId: 'pred_test_001',
|
||
},
|
||
autoClose: 15000,
|
||
});
|
||
setTestCount(prev => prev + 1);
|
||
|
||
// 阶段 2: 5秒后推送详情
|
||
setTimeout(() => {
|
||
addNotification({
|
||
type: NOTIFICATION_TYPES.EVENT_ALERT,
|
||
priority: PRIORITY_LEVELS.IMPORTANT,
|
||
title: '【测试】新能源汽车补贴政策延期至2025年底',
|
||
content: '财政部宣布新能源汽车购置补贴政策延长至2025年底,涉及比亚迪、理想汽车等5家龙头企业',
|
||
publishTime: Date.now(),
|
||
pushTime: Date.now(),
|
||
isAIGenerated: false,
|
||
clickable: true, // ✅ 可点击
|
||
link: '/event-detail/test_pred_001',
|
||
extra: {
|
||
isPrediction: false,
|
||
relatedPredictionId: 'pred_test_001',
|
||
eventId: 'test_pred_001',
|
||
relatedStocks: 5,
|
||
impactLevel: '重大利好',
|
||
},
|
||
autoClose: 12000,
|
||
});
|
||
setTestCount(prev => prev + 1);
|
||
}, 5000);
|
||
};
|
||
|
||
|
||
return (
|
||
<Box
|
||
position="fixed"
|
||
top="116px"
|
||
right={4}
|
||
zIndex={9998}
|
||
bg="white"
|
||
borderRadius="md"
|
||
boxShadow="lg"
|
||
overflow="hidden"
|
||
>
|
||
{/* 折叠按钮 */}
|
||
<HStack
|
||
p={2}
|
||
bg="blue.500"
|
||
color="white"
|
||
cursor="pointer"
|
||
onClick={onToggle}
|
||
spacing={2}
|
||
>
|
||
<MdNotifications size={20} />
|
||
<Text fontSize="sm" fontWeight="bold">
|
||
金融资讯测试工具
|
||
</Text>
|
||
<Badge colorScheme={isConnected ? 'green' : 'red'} ml="auto">
|
||
{isConnected ? 'Connected' : 'Disconnected'}
|
||
</Badge>
|
||
<Badge colorScheme="purple">
|
||
REAL
|
||
</Badge>
|
||
<Badge colorScheme={getPermissionColor()}>
|
||
浏览器: {getPermissionLabel()}
|
||
</Badge>
|
||
<IconButton
|
||
icon={isOpen ? <MdClose /> : <MdNotifications />}
|
||
size="xs"
|
||
variant="ghost"
|
||
colorScheme="whiteAlpha"
|
||
aria-label={isOpen ? '关闭' : '打开'}
|
||
/>
|
||
</HStack>
|
||
|
||
{/* 工具面板 */}
|
||
<Collapse in={isOpen} animateOpacity>
|
||
<VStack p={4} spacing={3} align="stretch" minW="280px">
|
||
<Text fontSize="xs" color="gray.600" fontWeight="bold">
|
||
通知类型测试
|
||
</Text>
|
||
|
||
{/* 公告通知 */}
|
||
<Button
|
||
size="sm"
|
||
colorScheme="blue"
|
||
leftIcon={<MdCampaign />}
|
||
onClick={testAnnouncement}
|
||
>
|
||
公告通知
|
||
</Button>
|
||
|
||
{/* 事件动向 */}
|
||
<Button
|
||
size="sm"
|
||
colorScheme="orange"
|
||
leftIcon={<MdArticle />}
|
||
onClick={testEventAlert}
|
||
>
|
||
事件动向
|
||
</Button>
|
||
|
||
{/* 分析报告 */}
|
||
<Button
|
||
size="sm"
|
||
colorScheme="purple"
|
||
leftIcon={<MdAssessment />}
|
||
onClick={testAnalysisReport}
|
||
>
|
||
分析报告
|
||
</Button>
|
||
|
||
{/* 预测通知 */}
|
||
<Button
|
||
size="sm"
|
||
colorScheme="gray"
|
||
leftIcon={<MdArticle />}
|
||
onClick={testPrediction}
|
||
>
|
||
预测通知(不可跳转)
|
||
</Button>
|
||
|
||
<Divider />
|
||
|
||
<Text fontSize="xs" color="gray.600" fontWeight="bold">
|
||
组合测试
|
||
</Text>
|
||
|
||
{/* 预测→详情流程测试 */}
|
||
<Button
|
||
size="sm"
|
||
colorScheme="cyan"
|
||
onClick={testPredictionFlow}
|
||
>
|
||
预测→详情流程(5秒延迟)
|
||
</Button>
|
||
|
||
<Divider />
|
||
|
||
<Text fontSize="xs" color="gray.600" fontWeight="bold">
|
||
浏览器通知
|
||
</Text>
|
||
|
||
{/* 请求权限按钮 */}
|
||
{browserPermission !== 'granted' && (
|
||
<Button
|
||
size="sm"
|
||
colorScheme={browserPermission === 'denied' ? 'red' : 'blue'}
|
||
onClick={handleRequestPermission}
|
||
isDisabled={browserPermission === 'denied'}
|
||
>
|
||
{browserPermission === 'denied' ? '权限已拒绝' : '请求浏览器权限'}
|
||
</Button>
|
||
)}
|
||
|
||
{/* 测试浏览器通知按钮 */}
|
||
{browserPermission === 'granted' && (
|
||
<Button
|
||
size="sm"
|
||
colorScheme="green"
|
||
leftIcon={<MdNotifications />}
|
||
onClick={() => {
|
||
console.log('测试浏览器通知按钮被点击');
|
||
console.log('Notification support:', 'Notification' in window);
|
||
console.log('Notification permission:', Notification?.permission);
|
||
console.log('Platform:', navigator.platform);
|
||
console.log('Fullscreen:', !!document.fullscreenElement);
|
||
|
||
// 直接使用原生 Notification API 测试
|
||
if (!('Notification' in window)) {
|
||
alert('您的浏览器不支持桌面通知');
|
||
return;
|
||
}
|
||
|
||
if (Notification.permission !== 'granted') {
|
||
alert('浏览器通知权限未授予\n当前权限状态:' + Notification.permission);
|
||
return;
|
||
}
|
||
|
||
// 重置状态
|
||
setNotificationShown(null);
|
||
setIsTestingNotification(true);
|
||
setTestCountdown(8); // 8秒倒计时
|
||
|
||
try {
|
||
console.log('正在创建浏览器通知...');
|
||
const notification = new Notification('【测试】浏览器通知测试', {
|
||
body: '如果您看到这条系统级通知,说明浏览器通知功能正常工作',
|
||
icon: '/logo192.png',
|
||
badge: '/badge.png',
|
||
tag: 'test_notification_' + Date.now(),
|
||
requireInteraction: false,
|
||
});
|
||
|
||
console.log('浏览器通知创建成功:', notification);
|
||
|
||
// 监听通知显示(成功显示)
|
||
notification.onshow = () => {
|
||
console.log('✅ 浏览器通知已显示(onshow 事件触发)');
|
||
setNotificationShown(true);
|
||
};
|
||
|
||
// 监听通知错误
|
||
notification.onerror = (error) => {
|
||
console.error('❌ 浏览器通知错误:', error);
|
||
setNotificationShown(false);
|
||
};
|
||
|
||
// 监听通知关闭
|
||
notification.onclose = () => {
|
||
console.log('浏览器通知已关闭');
|
||
};
|
||
|
||
// 8秒后自动关闭
|
||
setTimeout(() => {
|
||
notification.close();
|
||
console.log('浏览器通知已自动关闭');
|
||
}, 8000);
|
||
|
||
// 点击通知时聚焦窗口
|
||
notification.onclick = () => {
|
||
console.log('浏览器通知被点击');
|
||
window.focus();
|
||
notification.close();
|
||
setNotificationShown(true);
|
||
};
|
||
|
||
setTestCount(prev => prev + 1);
|
||
} catch (error) {
|
||
console.error('创建浏览器通知失败:', error);
|
||
alert('创建浏览器通知失败:' + error.message);
|
||
setIsTestingNotification(false);
|
||
setNotificationShown(false);
|
||
}
|
||
}}
|
||
isLoading={isTestingNotification}
|
||
loadingText={`等待通知... ${testCountdown}s`}
|
||
>
|
||
{isTestingNotification ? `等待通知... ${testCountdown}s` : '测试浏览器通知(直接)'}
|
||
</Button>
|
||
)}
|
||
|
||
{/* 浏览器通知状态说明 */}
|
||
{browserPermission === 'granted' && (
|
||
<Text fontSize="xs" color="green.500">
|
||
✅ 浏览器通知已启用
|
||
</Text>
|
||
)}
|
||
{browserPermission === 'denied' && (
|
||
<Text fontSize="xs" color="red.500">
|
||
❌ 请在浏览器设置中允许通知
|
||
</Text>
|
||
)}
|
||
|
||
{/* 实时权限状态 */}
|
||
<HStack spacing={2} justify="center">
|
||
<Text fontSize="xs" color="gray.500">
|
||
实际权限:
|
||
</Text>
|
||
<Badge
|
||
colorScheme={
|
||
('Notification' in window && Notification.permission === 'granted') ? 'green' :
|
||
('Notification' in window && Notification.permission === 'denied') ? 'red' : 'gray'
|
||
}
|
||
>
|
||
{('Notification' in window) ? Notification.permission : '不支持'}
|
||
</Badge>
|
||
</HStack>
|
||
|
||
{/* 环境警告 */}
|
||
{isFullscreen && (
|
||
<Alert status="warning" size="sm" borderRadius="md">
|
||
<AlertIcon />
|
||
<Box fontSize="xs">
|
||
<Text fontWeight="bold">全屏模式</Text>
|
||
<Text>某些浏览器在全屏模式下不显示通知</Text>
|
||
</Box>
|
||
</Alert>
|
||
)}
|
||
|
||
{isMacOS && notificationShown === false && (
|
||
<Alert status="error" size="sm" borderRadius="md">
|
||
<AlertIcon />
|
||
<Box fontSize="xs">
|
||
<Text fontWeight="bold">未检测到通知显示</Text>
|
||
<Text>可能是专注模式阻止了通知</Text>
|
||
</Box>
|
||
</Alert>
|
||
)}
|
||
|
||
<Divider />
|
||
|
||
{/* 故障排查面板 */}
|
||
<VStack spacing={2} align="stretch">
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
colorScheme="orange"
|
||
leftIcon={<MdWarning />}
|
||
onClick={onTroubleshootToggle}
|
||
>
|
||
{isTroubleshootOpen ? '收起' : '故障排查指南'}
|
||
</Button>
|
||
|
||
<Collapse in={isTroubleshootOpen} animateOpacity>
|
||
<VStack spacing={3} align="stretch" p={3} bg="orange.50" borderRadius="md">
|
||
<Text fontSize="xs" fontWeight="bold" color="orange.800">
|
||
如果看不到浏览器通知,请检查:
|
||
</Text>
|
||
|
||
{/* macOS 专注模式 */}
|
||
{isMacOS && (
|
||
<Alert status="warning" size="sm">
|
||
<AlertIcon />
|
||
<Box fontSize="xs">
|
||
<AlertTitle fontSize="xs">macOS 专注模式</AlertTitle>
|
||
<AlertDescription>
|
||
<UnorderedList spacing={1} mt={1}>
|
||
<ListItem>点击右上角控制中心</ListItem>
|
||
<ListItem>关闭「专注模式」或「勿扰模式」</ListItem>
|
||
<ListItem>或者:系统设置 → 专注模式 → 关闭</ListItem>
|
||
</UnorderedList>
|
||
</AlertDescription>
|
||
</Box>
|
||
</Alert>
|
||
)}
|
||
|
||
{/* macOS 系统通知设置 */}
|
||
{isMacOS && (
|
||
<Alert status="info" size="sm">
|
||
<AlertIcon />
|
||
<Box fontSize="xs">
|
||
<AlertTitle fontSize="xs">macOS 系统通知设置</AlertTitle>
|
||
<AlertDescription>
|
||
<UnorderedList spacing={1} mt={1}>
|
||
<ListItem>系统设置 → 通知</ListItem>
|
||
<ListItem>找到 <Code fontSize="xs">Google Chrome</Code> 或 <Code fontSize="xs">Microsoft Edge</Code></ListItem>
|
||
<ListItem>确保「允许通知」已开启</ListItem>
|
||
<ListItem>通知样式设置为「横幅」或「提醒」</ListItem>
|
||
</UnorderedList>
|
||
</AlertDescription>
|
||
</Box>
|
||
</Alert>
|
||
)}
|
||
|
||
{/* Chrome 浏览器设置 */}
|
||
<Alert status="info" size="sm">
|
||
<AlertIcon />
|
||
<Box fontSize="xs">
|
||
<AlertTitle fontSize="xs">Chrome 浏览器设置</AlertTitle>
|
||
<AlertDescription>
|
||
<UnorderedList spacing={1} mt={1}>
|
||
<ListItem>地址栏输入: <Code fontSize="xs">chrome://settings/content/notifications</Code></ListItem>
|
||
<ListItem>确保「网站可以请求发送通知」已开启</ListItem>
|
||
<ListItem>检查本站点是否在「允许」列表中</ListItem>
|
||
</UnorderedList>
|
||
</AlertDescription>
|
||
</Box>
|
||
</Alert>
|
||
|
||
{/* 全屏模式提示 */}
|
||
{isFullscreen && (
|
||
<Alert status="warning" size="sm">
|
||
<AlertIcon />
|
||
<Box fontSize="xs">
|
||
<AlertTitle fontSize="xs">退出全屏模式</AlertTitle>
|
||
<AlertDescription>
|
||
按 <Code fontSize="xs">ESC</Code> 键退出全屏,然后重新测试
|
||
</AlertDescription>
|
||
</Box>
|
||
</Alert>
|
||
)}
|
||
|
||
{/* 测试结果反馈 */}
|
||
{notificationShown === true && (
|
||
<Alert status="success" size="sm">
|
||
<AlertIcon />
|
||
<Text fontSize="xs">✅ 通知功能正常!</Text>
|
||
</Alert>
|
||
)}
|
||
</VStack>
|
||
</Collapse>
|
||
</VStack>
|
||
|
||
<Divider />
|
||
|
||
{/* 功能按钮 */}
|
||
<HStack spacing={2}>
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
colorScheme="gray"
|
||
onClick={clearAllNotifications}
|
||
flex={1}
|
||
>
|
||
清空全部
|
||
</Button>
|
||
|
||
<IconButton
|
||
size="sm"
|
||
icon={soundEnabled ? <MdVolumeUp /> : <MdVolumeOff />}
|
||
colorScheme={soundEnabled ? 'blue' : 'gray'}
|
||
onClick={toggleSound}
|
||
aria-label="切换音效"
|
||
/>
|
||
</HStack>
|
||
|
||
{/* 统计信息 */}
|
||
<VStack spacing={1}>
|
||
<HStack justify="space-between" w="full">
|
||
<Text fontSize="xs" color="gray.500">
|
||
当前队列:
|
||
</Text>
|
||
<Badge colorScheme={notifications.length >= 5 ? 'red' : 'blue'}>
|
||
{notifications.length} / 5
|
||
</Badge>
|
||
</HStack>
|
||
<Text fontSize="xs" color="gray.400" textAlign="center">
|
||
已测试: {testCount} 条通知
|
||
</Text>
|
||
</VStack>
|
||
</VStack>
|
||
</Collapse>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
export default NotificationTestTool;
|