Skip to content

Commit 33e562d

Browse files
committed
test: notification badge
1 parent c60dc87 commit 33e562d

5 files changed

Lines changed: 97 additions & 45 deletions

File tree

src/course-outline/CourseOutline.test.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
act, fireEvent, initializeMocks, render, screen, waitFor, within,
1818
} from '@src/testUtils';
1919
import { XBlock } from '@src/data/types';
20+
import { userEvent } from '@testing-library/user-event';
2021
import {
2122
getCourseBestPracticesApiUrl,
2223
getCourseLaunchApiUrl,
@@ -2491,9 +2492,13 @@ describe('<CourseOutline />', () => {
24912492
ENABLE_COURSE_OUTLINE_NEW_DESIGN: 'true',
24922493
});
24932494
renderComponent();
2494-
expect(await screen.findByRole('button', { name: 'Collapse all' })).toBeInTheDocument();
2495+
const btn = await screen.findByRole('button', { name: 'Collapse all' });
2496+
expect(btn).toBeInTheDocument();
24952497
expect(await screen.findByRole('link', { name: 'View live' })).toBeInTheDocument();
24962498
expect(await screen.findByRole('button', { name: 'Add' })).toBeInTheDocument();
24972499
expect(await screen.findByRole('button', { name: 'More actions' })).toBeInTheDocument();
2500+
const user = userEvent.setup();
2501+
await user.click(btn);
2502+
expect(await screen.findByRole('button', { name: 'Expand all' })).toBeInTheDocument();
24982503
});
24992504
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { initializeMocks, render, screen } from '@src/testUtils';
2+
import { NotificationStatusIcon } from './NotificationStatusIcon';
3+
4+
let mockCount = 0;
5+
6+
jest.mock('./hooks.ts', () => ({
7+
useDynamicHookShim: () => () => ({
8+
notificationAppData: {
9+
tabsCount: {
10+
count: mockCount,
11+
},
12+
},
13+
}),
14+
}));
15+
16+
const renderComponent = () => render(
17+
<NotificationStatusIcon />,
18+
);
19+
20+
describe('NotificationStatusIcon', () => {
21+
beforeEach(() => {
22+
initializeMocks();
23+
});
24+
25+
test('should display a status icon', async () => {
26+
mockCount = 2;
27+
renderComponent();
28+
expect(await screen.findByText('2 notifications')).toBeInTheDocument();
29+
});
30+
31+
test('check 1 notification text', async () => {
32+
mockCount = 1;
33+
renderComponent();
34+
expect(await screen.findByText('1 notification')).toBeInTheDocument();
35+
});
36+
37+
test('should not display a status icon if 0 notifications', async () => {
38+
mockCount = 0;
39+
renderComponent();
40+
expect(await screen.findByTestId('redux-provider')).toBeEmptyDOMElement();
41+
});
42+
});

src/course-outline/status-bar/NotificationStatusIcon.tsx

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,9 @@
11
import { FormattedMessage } from '@edx/frontend-platform/i18n';
22
import { Icon } from '@openedx/paragon';
33
import { NotificationsNone } from '@openedx/paragon/icons';
4-
import React from 'react';
4+
import { HooKType, useDynamicHookShim } from './hooks';
55
import messages from './messages';
66

7-
interface HooKType {
8-
notificationAppData: {
9-
tabsCount?: {
10-
count?: number;
11-
}
12-
}
13-
}
14-
15-
// Load the hook module asynchronously
16-
function useDynamicHookShim() {
17-
const [hook, setHook] = React.useState<(() => HooKType) | null>(null);
18-
19-
React.useEffect(() => {
20-
let cancelled = false;
21-
22-
async function load() {
23-
try {
24-
// @ts-ignore
25-
// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
26-
const module = await import('@edx/frontend-plugin-notifications');
27-
const hookFn = module.useAppNotifications ?? module.default;
28-
if (!cancelled) {
29-
// `module.useAppNotifications` is itself a hook
30-
setHook(() => hookFn);
31-
}
32-
} catch (err: any) {
33-
// eslint-disable-next-line no-console
34-
console.error('Failed to load notifications plugin:', err);
35-
}
36-
}
37-
38-
load();
39-
40-
return () => {
41-
cancelled = true;
42-
};
43-
}, []);
44-
45-
return hook;
46-
}
47-
487
// Component that actually calls the loaded hook
498
const NotificationHookConsumer = ({ hook }: { hook: () => HooKType }) => {
509
// The hook is now called on **every** render of this component
@@ -54,7 +13,6 @@ const NotificationHookConsumer = ({ hook }: { hook: () => HooKType }) => {
5413
return null;
5514
}
5615

57-
// You can use `hookResult` here as needed
5816
return (
5917
<small className="d-flex">
6018
<Icon className="mr-1" size="md" src={NotificationsNone} />
@@ -70,6 +28,7 @@ const NotificationHookConsumer = ({ hook }: { hook: () => HooKType }) => {
7028
export const NotificationStatusIcon = () => {
7129
const loadedHook = useDynamicHookShim();
7230

31+
// istanbul ignore if
7332
if (!loadedHook) {
7433
return null;
7534
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* istanbul ignore file */
2+
import React from 'react';
3+
4+
export interface HooKType {
5+
notificationAppData: {
6+
tabsCount?: {
7+
count?: number;
8+
}
9+
}
10+
}
11+
12+
// Load the hook module asynchronously
13+
export function useDynamicHookShim() {
14+
const [hook, setHook] = React.useState<(() => HooKType) | null>(null);
15+
16+
React.useEffect(() => {
17+
let cancelled = false;
18+
19+
async function load() {
20+
try {
21+
// @ts-ignore
22+
// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
23+
const module = await import('@edx/frontend-plugin-notifications');
24+
const hookFn = module.useAppNotifications ?? module.default;
25+
if (!cancelled) {
26+
// `module.useAppNotifications` is itself a hook
27+
setHook(() => hookFn);
28+
}
29+
} catch (err: any) {
30+
// eslint-disable-next-line no-console
31+
console.error('Failed to load notifications plugin:', err);
32+
}
33+
}
34+
35+
load();
36+
37+
return () => {
38+
cancelled = true;
39+
};
40+
}, []);
41+
42+
return hook;
43+
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* istanbul ignore file */
12
export const useAppNotifications = () => ({
23
notificationAppData: {
34
tabsCount: {
@@ -6,4 +7,6 @@ export const useAppNotifications = () => ({
67
},
78
});
89

9-
export const NotificationsTray = () => null;
10+
export const NotificationsTray: React.FC = () => null;
11+
12+
export default NotificationsTray;

0 commit comments

Comments
 (0)