Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: fix test and upgrade coverage
  • Loading branch information
filippovskii09 committed Nov 20, 2025
commit aa55953cd7f060ab44f36eae157df25b6b739bcd
22 changes: 21 additions & 1 deletion src/courseware/course/Course.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as celebrationUtils from './celebration/utils';
import { handleNextSectionCelebration } from './celebration';
import Course from './Course';
import setupDiscussionSidebar from './test-utils';
import SidebarProvider from './sidebar/SidebarContextProvider';

jest.mock('@edx/frontend-platform/analytics');
jest.mock('@edx/frontend-lib-special-exams/dist/data/thunks.js', () => ({
Expand Down Expand Up @@ -155,6 +156,13 @@ describe('Course', () => {
it('handles click to open/close discussions sidebar', async () => {
await setupDiscussionSidebar();

render(
<SidebarProvider courseId={mockData.courseId} unitId={mockData.unitId}>
<Course {...mockData} />
</SidebarProvider>,
{ wrapWithRouter: true },
);

waitFor(() => {
expect(screen.getByTestId('sidebar-DISCUSSIONS')).toBeInTheDocument();
expect(screen.getByTestId('sidebar-DISCUSSIONS')).not.toHaveClass('d-none');
Expand Down Expand Up @@ -184,7 +192,12 @@ describe('Course', () => {

await setupDiscussionSidebar();

const { rerender } = render(<Course {...testData} />, { store: testStore });
const { rerender } = render(
<SidebarProvider courseId={courseId} unitId={testData.unitId}>
<Course {...testData} />
</SidebarProvider>,
{ store: testStore },
);
loadUnit();

waitFor(() => {
Expand All @@ -197,6 +210,13 @@ describe('Course', () => {

it('handles click to open/close notification tray', async () => {
await setupDiscussionSidebar();
render(
<SidebarProvider courseId={mockData.courseId} unitId={mockData.unitId}>
<Course {...mockData} />
</SidebarProvider>,
{ wrapWithRouter: true },
);

waitFor(() => {
const notificationShowButton = screen.findByRole('button', { name: /Show notification tray/i });
expect(screen.queryByRole('region', { name: /notification tray/i })).not.toBeInTheDocument();
Expand Down
199 changes: 199 additions & 0 deletions src/courseware/course/sidebar/SidebarContextProvider.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { useContext } from 'react';
import { render, screen } from '@testing-library/react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we import this from setupTest?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, fixed

import userEvent from '@testing-library/user-event';
import { useWindowSize } from '@openedx/paragon';

import { useModel } from '@src/generic/model-store';
import { getLocalStorage, setLocalStorage } from '@src/data/localStorage';
import { getSessionStorage } from '@src/data/sessionStorage';

import SidebarProvider from './SidebarContextProvider';
import SidebarContext from './SidebarContext';
import * as discussionsSidebar from './sidebars/discussions';
import * as notificationsSidebar from './sidebars/notifications';

jest.mock('@openedx/paragon', () => ({
...jest.requireActual('@openedx/paragon'),
useWindowSize: jest.fn(),
breakpoints: {
extraLarge: { minWidth: 1200 },
},
}));

jest.mock('@src/generic/model-store', () => ({
useModel: jest.fn(),
}));

jest.mock('@src/data/localStorage', () => ({
getLocalStorage: jest.fn(),
setLocalStorage: jest.fn(),
}));

jest.mock('@src/data/sessionStorage', () => ({
getSessionStorage: jest.fn(),
}));

jest.mock('./sidebars/discussions', () => ({ ID: 'discussions' }));
jest.mock('./sidebars/notifications', () => ({ ID: 'notifications' }));
jest.mock('./sidebars', () => ({
SIDEBARS: {
discussions: { ID: 'discussions' },
notifications: { ID: 'notifications' },
},
}));

const TestConsumer = () => {
const {
currentSidebar,
toggleSidebar,
onNotificationSeen,
notificationStatus,
} = useContext(SidebarContext);

return (
<div>
<div data-testid="current-sidebar">{currentSidebar || 'none'}</div>
<div data-testid="notification-status">{notificationStatus || 'none'}</div>
<button type="button" onClick={() => toggleSidebar(discussionsSidebar.ID)}>Toggle Discussions</button>
<button type="button" onClick={onNotificationSeen}>See Notifications</button>
</div>
);
};

describe('SidebarContextProvider', () => {
const defaultProps = {
courseId: 'course-v1:test',
unitId: 'unit-1',
};

beforeEach(() => {
jest.clearAllMocks();
useWindowSize.mockReturnValue({ width: 1400 });
useModel.mockReturnValue({});
getLocalStorage.mockReturnValue(null);
getSessionStorage.mockReturnValue(null);
});

it('renders without crashing and provides default context', () => {
render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);
expect(screen.getByTestId('current-sidebar')).toHaveTextContent('none');
});

it('initializes with notifications sidebar if notification tray is open in session storage', () => {
getSessionStorage.mockReturnValue('open');

render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

expect(screen.getByTestId('current-sidebar')).toHaveTextContent(notificationsSidebar.ID);
});

it('loads initial sidebar from local storage on small screens (mobile behavior)', () => {
useWindowSize.mockReturnValue({ width: 800 });
getLocalStorage.mockImplementation((key) => {
if (key === `sidebar.${defaultProps.courseId}`) { return discussionsSidebar.ID; }
return null;
});

render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

expect(screen.getByTestId('current-sidebar')).toHaveTextContent(discussionsSidebar.ID);
});

it('does not load from local storage on large screens (desktop behavior)', () => {
useWindowSize.mockReturnValue({ width: 1400 });
getLocalStorage.mockReturnValue(discussionsSidebar.ID);

render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

expect(screen.getByTestId('current-sidebar')).toHaveTextContent('none');
});

it('toggles sidebar open and updates local storage', async () => {
const user = userEvent.setup();
render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

expect(screen.getByTestId('current-sidebar')).toHaveTextContent('none');

await user.click(screen.getByText('Toggle Discussions'));

expect(screen.getByTestId('current-sidebar')).toHaveTextContent(discussionsSidebar.ID);
expect(setLocalStorage).toHaveBeenCalledWith(`sidebar.${defaultProps.courseId}`, discussionsSidebar.ID);
});

it('toggles sidebar closed (null) if clicking the same sidebar', async () => {
useWindowSize.mockReturnValue({ width: 800 });
getLocalStorage.mockReturnValue(discussionsSidebar.ID);
const user = userEvent.setup();

render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

expect(screen.getByTestId('current-sidebar')).toHaveTextContent(discussionsSidebar.ID);

await user.click(screen.getByText('Toggle Discussions'));

expect(screen.getByTestId('current-sidebar')).toHaveTextContent('none');
expect(setLocalStorage).toHaveBeenCalledWith(`sidebar.${defaultProps.courseId}`, null);
});

it('updates notification status when seen', async () => {
const user = userEvent.setup();
render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

await user.click(screen.getByText('See Notifications'));

expect(setLocalStorage).toHaveBeenCalledWith(`notificationStatus.${defaultProps.courseId}`, 'inactive');
expect(screen.getByTestId('notification-status')).toHaveTextContent('inactive');
});

it('updates current sidebar when unitId changes (Effect trigger)', () => {
useWindowSize.mockReturnValue({ width: 800 });
getLocalStorage.mockReturnValue(notificationsSidebar.ID);

const { rerender } = render(
<SidebarProvider {...defaultProps}>
<TestConsumer />
</SidebarProvider>,
);

expect(screen.getByTestId('current-sidebar')).toHaveTextContent(notificationsSidebar.ID);

useModel.mockImplementation((model) => {
if (model === 'discussionTopics') { return { id: 'topic-1', enabledInContext: true }; }
return {};
});

rerender(
<SidebarProvider {...defaultProps} unitId="unit-2">
<TestConsumer />
</SidebarProvider>,
);
});
});
Loading