• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atlp-rwanda / hackers-ec-Fe / #1342

01 Aug 2024 05:54AM UTC coverage: 88.507% (-0.04%) from 88.549%
#1342

push

Hakizimana-Clement
fix(Notification test): rewrite notifications test file when user role is admin.

880 of 1080 branches covered (81.48%)

Branch coverage included in aggregate %.

9886 of 11084 relevant lines covered (89.19%)

15.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.07
/src/components/notification/Notification.tsx
1
import { BellRing } from 'lucide-react';
1✔
2
import { useEffect, useRef, useState } from 'react';
1✔
3
import { FaCheckDouble } from 'react-icons/fa6';
1✔
4
import { io } from 'socket.io-client';
1✔
5
import { toast } from 'sonner';
1✔
6
import { NotificationTypes } from '../../@types/notification';
1✔
7
import { UserInfoTypes } from '../../@types/userType';
1✔
8
import useToken from '../../hooks/useToken';
1✔
9
import {
1✔
10
        addNotification,
1✔
11
        markAllRead,
1✔
12
        userNotification,
1✔
13
} from '../../redux/features/notificationSlice';
1✔
14
import { useAppDispatch, useAppSelector } from '../../redux/hooks/hooks';
1✔
15
import { notificationSound } from '../../utils/images';
1✔
16
import fetchInfo from '../../utils/userDetails';
1✔
17
import NotificationItem from '../cards/NotificationItem';
1✔
18
import { DynamicData } from '../../@types/DynamicData';
1✔
19
import NotificationSkeleton from '../cards/NotificationSkeleton';
1✔
20

1✔
21
const Notification = () => {
1✔
22
        const dispatch = useAppDispatch();
28✔
23
        const { accessToken } = useToken();
28✔
24
        const notificationPlayer = useRef<HTMLAudioElement>(null);
28✔
25
        const [notificationActive, setNotificationActive] = useState(false);
28✔
26
        const { notifications, value, isLoading } = useAppSelector(
28✔
27
                (state) => state.notifications,
28✔
28
        );
28✔
29

28✔
30
        const playAudio = () => {
28✔
31
                notificationPlayer.current?.play();
×
32
        };
×
33

28✔
34
        useEffect(() => {
28✔
35
                if (accessToken) {
15✔
36
                        const { id } = fetchInfo() as UserInfoTypes;
4✔
37
                        const socket = io(`${import.meta.env.VITE_API_APP_ROOT_URL}`, {
4✔
38
                                transports: ['websocket'],
4✔
39
                                auth: {
4✔
40
                                        token: accessToken,
4✔
41
                                },
4✔
42
                        });
4✔
43

4✔
44
                        socket.on('notifications', (data) => {
4✔
45
                                dispatch(userNotification(data));
×
46
                        });
4✔
47
                        socket.on(`notification-${id}`, (notification) => {
4✔
48
                                if (notification) {
×
49
                                        dispatch(addNotification(notification));
×
50
                                        toast.success('You have new notification 🔔');
×
51
                                        playAudio();
×
52
                                }
×
53
                        });
4✔
54

4✔
55
                        return () => {
4✔
56
                                socket.disconnect();
4✔
57
                        };
4✔
58
                }
4✔
59
        }, [accessToken, dispatch]);
28✔
60

28✔
61
        const sortedNotification = sortNotificationsByDate(
28✔
62
                notifications as unknown as NotificationTypes[],
28✔
63
        );
28✔
64

28✔
65
        const readAllNotification = async () => {
28✔
66
                try {
×
67
                        await dispatch(markAllRead()).unwrap();
×
68
                } catch (e) {
×
69
                        const err = e as DynamicData;
×
70
                        toast.error(
×
71
                                err?.data?.message ||
×
72
                                        err?.message ||
×
73
                                        'Unknown error occurred! Please try again!',
×
74
                        );
×
75
                }
×
76
        };
×
77

28✔
78
        return (
28✔
79
                <div className="relative flex-center transition-colors border border-neutral-grey p-2 h-max rounded-lg cursor-pointer hover:bg-neutral-grey/20">
28✔
80
                        <BellRing
28✔
81
                                size={18}
28✔
82
                                onClick={() => setNotificationActive((prev) => !prev)}
28✔
83
                                className="cursor-pointer"
28✔
84
                                role="img"
28✔
85
                                aria-label="bell-image"
28✔
86
                        />
28✔
87
                        <audio ref={notificationPlayer} src={notificationSound} />
28✔
88
                        {value > 0 && (
28✔
89
                                <div
9✔
90
                                        className="absolute -top-4 -right-4 bg-action-error w-8 text-xs h-8 text-neutral-white rounded-full flex-center"
9✔
91
                                        aria-label="notification-number"
9✔
92
                                >
9✔
93
                                        {value <= 99 ? value : '99+'}
9!
94
                                </div>
9✔
95
                        )}
28✔
96
                        {notificationActive && (
28✔
97
                                <div
5✔
98
                                        className="absolute top-full -right-[100px] ipad:-right-[50px] mt-5 p-2 bg-neutral-white shadow-custom-heavy rounded-xl w-[80vw] ipad:w-[350px] h-max max-h-[80vh] md:max-h-[70vh] overflow-y-scroll no-scrollbar z-50"
5✔
99
                                        aria-label="notification-tab"
5✔
100
                                >
5✔
101
                                        <div className="w-full h-full overflow-hidden">
5✔
102
                                                <h2 className="text-md ipad:text-lg font-semibold text-start pl-4 pt-4">
5✔
103
                                                        Notifications
5✔
104
                                                </h2>
5✔
105
                                                <div className="flex items-center justify-end p-5">
5✔
106
                                                        <div
5✔
107
                                                                onClick={readAllNotification}
5✔
108
                                                                aria-label="mark-button"
5✔
109
                                                                className={`flex items-center gap-2 text-[11px] text-neutral-black bg-primary-lightblue/20 px-3 py-1 rounded-full hover:text-neutral-black/50 ${notifications?.filter((not) => not.unread === true).length === 0 && 'hidden'}`}
5✔
110
                                                        >
5✔
111
                                                                Mark all as read
5✔
112
                                                                <FaCheckDouble fill={'black'} />
5✔
113
                                                        </div>
5✔
114
                                                </div>
5✔
115
                                                <div className="w-full flex-1 h-[90%] no-scrollbar px-2 pb-10">
5✔
116
                                                        {isLoading ? (
5✔
117
                                                                Array.from({ length: 12 }).map((_, i) => (
1✔
118
                                                                        <NotificationSkeleton key={i} />
12✔
119
                                                                ))
1✔
120
                                                        ) : sortedNotification && sortedNotification.length > 0 ? (
4✔
121
                                                                sortedNotification.map((item) => (
3✔
122
                                                                        <NotificationItem
4✔
123
                                                                                key={item.id}
4✔
124
                                                                                id={item.id}
4✔
125
                                                                                unread={item.unread}
4✔
126
                                                                                text={item.message}
4✔
127
                                                                                date={`${item.createdAt.getHours()}:${item.createdAt.getMinutes() < 10 ? `0${item.createdAt.getMinutes()}` : item.createdAt.getMinutes()} - ${item.createdAt.getDate()}/${item.createdAt.getMonth()}`}
4!
128
                                                                        />
4✔
129
                                                                ))
3✔
130
                                                        ) : (
1✔
131
                                                                <div className="text-xs p-4 text-center">
1✔
132
                                                                        No new notification available!
1✔
133
                                                                </div>
1✔
134
                                                        )}
5✔
135
                                                </div>
5✔
136
                                        </div>
5✔
137
                                </div>
5✔
138
                        )}
28✔
139
                </div>
28✔
140
        );
28✔
141
};
28✔
142

1✔
143
function sortNotificationsByDate(
28✔
144
        noties: NotificationTypes[],
28✔
145
): NotificationTypes[] {
28✔
146
        return noties
28✔
147
                .map((notification) => ({
28✔
148
                        ...notification,
13✔
149
                        createdAt: new Date(notification.createdAt),
13✔
150
                }))
28✔
151
                .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
28✔
152
}
28✔
153

1✔
154
export default Notification;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc