Skip to content

Commit 170a3b2

Browse files
test: adding tests to increase code coverage
1 parent 6cde551 commit 170a3b2

4 files changed

Lines changed: 360 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
import { IntlProvider } from '@edx/frontend-platform/i18n';
4+
import FileInfoModalSidebar from './FileInfoModalSidebar';
5+
6+
const mockAsset = {
7+
id: 'test-file-id',
8+
displayName: 'test-file.png',
9+
wrapperType: 'image',
10+
externalUrl: 'https://example.com/test-file.png',
11+
portableUrl: '/static/test-file.png',
12+
locked: false,
13+
thumbnail: 'https://example.com/thumbnail.png',
14+
dateAdded: '2024-01-15T10:30:00Z',
15+
fileSize: 1024000,
16+
usageLocations: [],
17+
};
18+
19+
const mockHandleLockedAsset = jest.fn();
20+
21+
const renderComponent = (props = {}) =>
22+
render(
23+
<IntlProvider locale="en">
24+
<FileInfoModalSidebar
25+
asset={mockAsset}
26+
handleLockedAsset={mockHandleLockedAsset}
27+
{...props}
28+
/>
29+
</IntlProvider>,
30+
);
31+
32+
describe('FileInfoModalSidebar', () => {
33+
beforeEach(() => {
34+
jest.clearAllMocks();
35+
Object.assign(navigator, {
36+
clipboard: {
37+
writeText: jest.fn(),
38+
},
39+
});
40+
});
41+
42+
it('renders asset information correctly', () => {
43+
renderComponent();
44+
45+
expect(screen.getByText('Date added')).toBeInTheDocument();
46+
expect(screen.getByText('File size')).toBeInTheDocument();
47+
expect(screen.getByText('Studio URL')).toBeInTheDocument();
48+
expect(screen.getByText('Web URL')).toBeInTheDocument();
49+
expect(screen.getByText('Lock file')).toBeInTheDocument();
50+
});
51+
52+
it('displays the portable URL', () => {
53+
renderComponent();
54+
expect(screen.getByText('/static/test-file.png')).toBeInTheDocument();
55+
});
56+
57+
it('displays the external URL', () => {
58+
renderComponent();
59+
expect(screen.getByText('https://example.com/test-file.png')).toBeInTheDocument();
60+
});
61+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
import { IntlProvider } from '@edx/frontend-platform/i18n';
4+
import FileMenu from './FileMenu';
5+
6+
const mockHandlers = {
7+
handleLock: jest.fn(),
8+
onDownload: jest.fn(),
9+
openAssetInfo: jest.fn(),
10+
openDeleteConfirmation: jest.fn(),
11+
};
12+
13+
const defaultProps = {
14+
id: 'test-file-id',
15+
externalUrl: 'https://example.com/test-file.png',
16+
portableUrl: '/static/test-file.png',
17+
locked: false,
18+
fileType: 'file',
19+
...mockHandlers,
20+
};
21+
22+
const renderComponent = (props = {}) =>
23+
render(
24+
<IntlProvider locale="en">
25+
<FileMenu {...defaultProps} {...props} />
26+
</IntlProvider>,
27+
);
28+
29+
describe('FileMenu', () => {
30+
beforeEach(() => {
31+
jest.clearAllMocks();
32+
Object.assign(navigator, {
33+
clipboard: {
34+
writeText: jest.fn(),
35+
},
36+
});
37+
});
38+
39+
it('renders the menu toggle button', () => {
40+
renderComponent();
41+
expect(screen.getByRole('button', { name: 'file-menu-toggle' })).toBeInTheDocument();
42+
});
43+
44+
it('opens dropdown menu when toggle is clicked', async () => {
45+
const user = userEvent.setup();
46+
renderComponent();
47+
48+
const toggleButton = screen.getByRole('button', { name: 'file-menu-toggle' });
49+
await user.click(toggleButton);
50+
51+
expect(screen.getByText('Download')).toBeInTheDocument();
52+
expect(screen.getByText('Info')).toBeInTheDocument();
53+
});
54+
});
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
import { IntlProvider } from '@edx/frontend-platform/i18n';
4+
import GalleryCard from './GalleryCard';
5+
6+
const mockOriginal = {
7+
id: 'test-file-id',
8+
displayName: 'test-file.png',
9+
wrapperType: 'image',
10+
externalUrl: 'https://example.com/test-file.png',
11+
portableUrl: '/static/test-file.png',
12+
locked: false,
13+
thumbnail: 'https://example.com/thumbnail.png',
14+
status: 'active',
15+
transcripts: [],
16+
downloadLink: 'https://example.com/download/test-file.png',
17+
};
18+
19+
const mockHandlers = {
20+
handleBulkDownload: jest.fn(),
21+
handleLockFile: jest.fn(),
22+
handleOpenDeleteConfirmation: jest.fn(),
23+
handleOpenFileInfo: jest.fn(),
24+
thumbnailPreview: jest.fn(() => <div data-testid="thumbnail-preview" />),
25+
};
26+
27+
const renderComponent = (props = {}) =>
28+
render(
29+
<IntlProvider locale="en">
30+
<GalleryCard
31+
original={mockOriginal}
32+
fileType="file"
33+
{...mockHandlers}
34+
{...props}
35+
/>
36+
</IntlProvider>,
37+
);
38+
39+
describe('GalleryCard', () => {
40+
beforeEach(() => {
41+
jest.clearAllMocks();
42+
});
43+
44+
it('renders the card with file display name', () => {
45+
renderComponent();
46+
expect(screen.getByText('test-file.png')).toBeInTheDocument();
47+
});
48+
49+
it('renders the card with file type chip', () => {
50+
renderComponent();
51+
expect(screen.getByText('image')).toBeInTheDocument();
52+
});
53+
54+
it('renders with custom className', () => {
55+
const { container } = renderComponent({ className: 'custom-class' });
56+
expect(container.querySelector('.custom-class')).toBeInTheDocument();
57+
});
58+
59+
it('renders closed caption icon when transcripts are present', () => {
60+
renderComponent({
61+
original: { ...mockOriginal, transcripts: ['en', 'es'] },
62+
});
63+
// The card should be present when transcripts exist
64+
expect(screen.getByText('test-file.png')).toBeInTheDocument();
65+
});
66+
67+
describe('permissions', () => {
68+
it('passes permissions to FileMenu with default values when not provided', async () => {
69+
const user = userEvent.setup();
70+
renderComponent();
71+
72+
// The FileMenu should be rendered with default permissions
73+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
74+
expect(menuButton).toBeInTheDocument();
75+
76+
// Open the menu and check delete option is visible (default permission)
77+
await user.click(menuButton);
78+
expect(screen.getByText('Delete')).toBeInTheDocument();
79+
});
80+
81+
it('passes permissions to FileMenu - delete visible when canDeleteFiles is true', async () => {
82+
const user = userEvent.setup();
83+
renderComponent({
84+
permissions: { canEditFiles: true, canDeleteFiles: true },
85+
});
86+
87+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
88+
await user.click(menuButton);
89+
90+
expect(screen.getByText('Delete')).toBeInTheDocument();
91+
});
92+
93+
it('passes permissions to FileMenu - delete hidden when canDeleteFiles is false', async () => {
94+
const user = userEvent.setup();
95+
renderComponent({
96+
permissions: { canEditFiles: true, canDeleteFiles: false },
97+
});
98+
99+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
100+
await user.click(menuButton);
101+
102+
expect(screen.queryByText('Delete')).not.toBeInTheDocument();
103+
});
104+
});
105+
106+
describe('menu actions', () => {
107+
it('calls handleLockFile when lock is triggered from menu', async () => {
108+
const user = userEvent.setup();
109+
renderComponent();
110+
111+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
112+
await user.click(menuButton);
113+
114+
const lockButton = screen.getByText('Lock');
115+
await user.click(lockButton);
116+
117+
expect(mockHandlers.handleLockFile).toHaveBeenCalledWith('test-file-id', true);
118+
});
119+
120+
it('calls handleLockFile with false when file is already locked', async () => {
121+
const user = userEvent.setup();
122+
renderComponent({
123+
original: { ...mockOriginal, locked: true },
124+
});
125+
126+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
127+
await user.click(menuButton);
128+
129+
const unlockButton = screen.getByText('Unlock');
130+
await user.click(unlockButton);
131+
132+
expect(mockHandlers.handleLockFile).toHaveBeenCalledWith('test-file-id', false);
133+
});
134+
135+
it('calls handleOpenFileInfo when info is triggered from menu', async () => {
136+
const user = userEvent.setup();
137+
renderComponent();
138+
139+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
140+
await user.click(menuButton);
141+
142+
const infoButton = screen.getByText('Info');
143+
await user.click(infoButton);
144+
145+
expect(mockHandlers.handleOpenFileInfo).toHaveBeenCalledWith(mockOriginal);
146+
});
147+
148+
it('calls handleBulkDownload when download is triggered from menu', async () => {
149+
const user = userEvent.setup();
150+
renderComponent();
151+
152+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
153+
await user.click(menuButton);
154+
155+
const downloadButton = screen.getByText('Download');
156+
await user.click(downloadButton);
157+
158+
expect(mockHandlers.handleBulkDownload).toHaveBeenCalledWith([{
159+
original: {
160+
id: 'test-file-id',
161+
displayName: 'test-file.png',
162+
downloadLink: 'https://example.com/download/test-file.png',
163+
},
164+
}]);
165+
});
166+
167+
it('calls handleOpenDeleteConfirmation when delete is triggered from menu', async () => {
168+
const user = userEvent.setup();
169+
renderComponent({
170+
permissions: { canEditFiles: true, canDeleteFiles: true },
171+
});
172+
173+
const menuButton = screen.getByRole('button', { name: 'file-menu-toggle' });
174+
await user.click(menuButton);
175+
176+
const deleteButton = screen.getByText('Delete');
177+
await user.click(deleteButton);
178+
179+
expect(mockHandlers.handleOpenDeleteConfirmation).toHaveBeenCalledWith([{ original: mockOriginal }]);
180+
});
181+
});
182+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
import { IntlProvider } from '@edx/frontend-platform/i18n';
4+
import MoreInfoColumn from './MoreInfoColumn';
5+
6+
const mockRow = {
7+
original: {
8+
id: 'test-file-id',
9+
displayName: 'test-file.png',
10+
externalUrl: 'https://example.com/test-file.png',
11+
portableUrl: '/static/test-file.png',
12+
locked: false,
13+
downloadLink: 'https://example.com/download/test-file.png',
14+
},
15+
};
16+
17+
const mockHandlers = {
18+
handleLock: jest.fn(),
19+
handleBulkDownload: jest.fn(),
20+
handleOpenFileInfo: jest.fn(),
21+
handleOpenDeleteConfirmation: jest.fn(),
22+
};
23+
24+
const renderComponent = (props = {}) =>
25+
render(
26+
<IntlProvider locale="en">
27+
<MoreInfoColumn
28+
row={mockRow}
29+
fileType="file"
30+
{...mockHandlers}
31+
{...props}
32+
/>
33+
</IntlProvider>,
34+
);
35+
36+
describe('MoreInfoColumn', () => {
37+
beforeEach(() => {
38+
jest.clearAllMocks();
39+
Object.assign(navigator, {
40+
clipboard: {
41+
writeText: jest.fn(),
42+
},
43+
});
44+
});
45+
46+
it('renders the more info icon button', () => {
47+
renderComponent();
48+
expect(screen.getByRole('button', { name: /more info/i })).toBeInTheDocument();
49+
});
50+
51+
it('opens menu when icon button is clicked', async () => {
52+
const user = userEvent.setup();
53+
renderComponent();
54+
55+
const iconButton = screen.getByRole('button', { name: /more info/i });
56+
await user.click(iconButton);
57+
58+
expect(screen.getByText('Copy Studio Url')).toBeInTheDocument();
59+
expect(screen.getByText('Copy Web Url')).toBeInTheDocument();
60+
expect(screen.getByText('Download')).toBeInTheDocument();
61+
expect(screen.getByText('Info')).toBeInTheDocument();
62+
});
63+
});

0 commit comments

Comments
 (0)