Skip to content

Commit 56b4d17

Browse files
committed
test: replace existing test with the new Toast Manager
1 parent afa2c5c commit 56b4d17

File tree

9 files changed

+62
-143
lines changed

9 files changed

+62
-143
lines changed

src/authz-module/libraries-manager/LibrariesUserManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ const LibrariesUserManager = () => {
112112

113113
showToast({
114114
type: 'error',
115-
message: intl.formatMessage(messages['library.authz.team.default.error.toast.message'], { Bold, Br }),
115+
message: intl.formatMessage(messages['library.authz.team.toast.default.error.message'], { Bold, Br }),
116116
});
117117
handleCloseConfirmDeletionModal();
118118
},

src/authz-module/libraries-manager/ToastManagerContext.test.tsx

Lines changed: 20 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,26 @@
1-
import { screen, waitFor, render as rtlRender } from '@testing-library/react';
1+
import { screen, waitFor } from '@testing-library/react';
22
import userEvent from '@testing-library/user-event';
3-
import { IntlProvider } from '@edx/frontend-platform/i18n';
3+
import { renderWrapper } from '@src/setupTest';
44
import { ToastManagerProvider, useToastManager } from './ToastManagerContext';
55

6-
const render = (ui: React.ReactElement) => rtlRender(
7-
<IntlProvider locale="en">
8-
{ui}
9-
</IntlProvider>,
10-
);
11-
126
const TestComponent = () => {
13-
const { handleShowToast, handleDiscardToast } = useToastManager();
7+
const { showToast } = useToastManager();
8+
9+
const handleShowToast = () => showToast({ message: 'Test toast message', type: 'error' });
10+
const handleShowAnotherToast = () => showToast({ message: 'Another message', type: 'success' });
1411

1512
return (
1613
<div>
17-
<button type="button" onClick={() => handleShowToast('Test toast message')}>
18-
Show Toast
19-
</button>
20-
<button type="button" onClick={() => handleShowToast('Another message')}>
21-
Show Another Toast
22-
</button>
23-
<button type="button" onClick={handleDiscardToast}>
24-
Discard Toast
25-
</button>
14+
<button type="button" onClick={handleShowToast}>Show Toast</button>
15+
<button type="button" onClick={handleShowAnotherToast}>Show Another Toast</button>
2616
</div>
2717
);
2818
};
2919

3020
describe('ToastManagerContext', () => {
3121
describe('ToastManagerProvider', () => {
3222
it('does not show toast initially', () => {
33-
render(
23+
renderWrapper(
3424
<ToastManagerProvider>
3525
<TestComponent />
3626
</ToastManagerProvider>,
@@ -39,15 +29,14 @@ describe('ToastManagerContext', () => {
3929
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
4030
});
4131

42-
it('shows toast when handleShowToast is called', async () => {
32+
it('shows toast when showToast is called', async () => {
4333
const user = userEvent.setup();
44-
render(
34+
renderWrapper(
4535
<ToastManagerProvider>
4636
<TestComponent />
4737
</ToastManagerProvider>,
4838
);
4939

50-
// handleShowToast is called on button click
5140
const showButton = screen.getByText('Show Toast');
5241
await user.click(showButton);
5342

@@ -57,59 +46,29 @@ describe('ToastManagerContext', () => {
5746
});
5847
});
5948

60-
it('updates toast message when handleShowToast is called with different message', async () => {
49+
it('adds multiple toasts when showToast is called multiple times', async () => {
6150
const user = userEvent.setup();
62-
render(
51+
renderWrapper(
6352
<ToastManagerProvider>
6453
<TestComponent />
6554
</ToastManagerProvider>,
6655
);
6756

68-
// Show first toast
6957
const showButton = screen.getByText('Show Toast');
70-
await user.click(showButton);
71-
72-
await waitFor(() => {
73-
expect(screen.getByText('Test toast message')).toBeInTheDocument();
74-
});
75-
76-
// Show another toast
7758
const showAnotherButton = screen.getByText('Show Another Toast');
78-
await user.click(showAnotherButton);
7959

80-
await waitFor(() => {
81-
expect(screen.getByText('Another message')).toBeInTheDocument();
82-
expect(screen.queryByText('Test toast message')).not.toBeInTheDocument();
83-
});
84-
});
85-
86-
it('hides toast when handleDiscardToast is called', async () => {
87-
const user = userEvent.setup();
88-
render(
89-
<ToastManagerProvider>
90-
<TestComponent />
91-
</ToastManagerProvider>,
92-
);
93-
94-
const showButton = screen.getByText('Show Toast');
9560
await user.click(showButton);
61+
await user.click(showAnotherButton);
9662

9763
await waitFor(() => {
9864
expect(screen.getByText('Test toast message')).toBeInTheDocument();
99-
});
100-
101-
// handleDiscardToast is called on button click
102-
const discardButton = screen.getByText('Discard Toast');
103-
await user.click(discardButton);
104-
105-
await waitFor(() => {
106-
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
65+
expect(screen.getByText('Another message')).toBeInTheDocument();
10766
});
10867
});
10968

11069
it('hides toast when close button is clicked', async () => {
11170
const user = userEvent.setup();
112-
render(
71+
renderWrapper(
11372
<ToastManagerProvider>
11473
<TestComponent />
11574
</ToastManagerProvider>,
@@ -127,48 +86,22 @@ describe('ToastManagerContext', () => {
12786

12887
await waitFor(() => {
12988
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
130-
});
131-
});
132-
133-
it('calls handleClose callback when toast is closed', async () => {
134-
const user = userEvent.setup();
135-
const mockHandleClose = jest.fn();
136-
137-
render(
138-
<ToastManagerProvider handleClose={mockHandleClose}>
139-
<TestComponent />
140-
</ToastManagerProvider>,
141-
);
142-
143-
const showButton = screen.getByText('Show Toast');
144-
await user.click(showButton);
145-
146-
await waitFor(() => {
147-
expect(screen.getByText('Test toast message')).toBeInTheDocument();
148-
});
149-
150-
const closeButton = screen.getByLabelText('Close');
151-
await user.click(closeButton);
152-
153-
await waitFor(() => {
154-
expect(mockHandleClose).toHaveBeenCalledTimes(1);
155-
});
89+
}, { timeout: 500 });
15690
});
15791
});
15892

15993
describe('useToastManager hook', () => {
16094
it('throws error when used outside ToastManagerProvider', () => {
161-
// Suppress console.error for this test
162-
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
95+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
16396

16497
const TestComponentWithoutProvider = () => {
16598
useToastManager();
16699
return <div>Test</div>;
167100
};
168101

169102
expect(() => {
170-
render(<TestComponentWithoutProvider />);
171-
}).toThrow('useToastManager must be used within an ToastManagerProvider');
103+
renderWrapper(<TestComponentWithoutProvider />);
104+
}).toThrow('useToastManager must be used within a ToastManagerProvider');
172105

173106
consoleSpy.mockRestore();
174107
});

src/authz-module/libraries-manager/ToastManagerContext.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ const Br = () => <br />;
3131

3232
type ToastManagerContextType = {
3333
showToast: (toast: Omit<AppToast, 'id'>) => void;
34-
discardToast: (id: string) => void;
3534
showErrorToast: (error, retryFn?: () => void) => void;
3635
Bold: (chunk: string) => JSX.Element;
3736
Br: () => JSX.Element;
@@ -51,18 +50,10 @@ export const ToastManagerProvider = ({ children }: ToastManagerProviderProps) =>
5150
const id = `toast-notification-${Date.now()}`;
5251
const newToast = { ...toast, id, visible: true };
5352
setToasts(prev => [...prev, newToast]);
54-
return id;
5553
};
5654

5755
const discardToast = (id: string) => {
58-
setToasts(prev =>
59-
prev.map(t =>
60-
t.id === id ? { ...t, visible: false } : t
61-
)
62-
);
63-
setTimeout(() => {
64-
setToasts(prev => prev.filter(t => t.id !== id));
65-
}, 300);
56+
setToasts(prev => prev.map(t => (t.id === id ? { ...t, visible: false } : t)));
6657
};
6758

6859
const value = useMemo<ToastManagerContextType>(() => {
@@ -81,7 +72,6 @@ export const ToastManagerProvider = ({ children }: ToastManagerProviderProps) =>
8172

8273
return ({
8374
showToast,
84-
discardToast,
8575
showErrorToast,
8676
Bold,
8777
Br,
@@ -97,7 +87,7 @@ export const ToastManagerProvider = ({ children }: ToastManagerProviderProps) =>
9787
<Toast
9888
key={toast.id}
9989
show={toast.visible}
100-
onClose={() => discardToast(toast.id)}
90+
onClose={() => setToasts(prev => prev.filter(t => t.id !== toast.id))}
10191
action={toast.onRetry ? {
10292
onClick: () => {
10393
discardToast(toast.id);

src/authz-module/libraries-manager/components/AddNewTeamMemberModal/AddNewTeamMemberTrigger.test.tsx

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { screen, waitFor } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
44
import { renderWrapper } from '@src/setupTest';
55
import { useAssignTeamMembersRole } from '@src/authz-module/data/hooks';
6+
import { ToastManagerProvider } from '@src/authz-module/libraries-manager/ToastManagerContext';
67
import AddNewTeamMemberTrigger from './AddNewTeamMemberTrigger';
78

89
const mockMutate = jest.fn();
@@ -59,15 +60,15 @@ describe('AddNewTeamMemberTrigger', () => {
5960
});
6061

6162
it('renders the trigger button', () => {
62-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
63+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
6364

6465
const button = screen.getByRole('button', { name: /add new team member/i });
6566
expect(button).toBeInTheDocument();
6667
});
6768

6869
it('opens modal when trigger button is clicked', async () => {
6970
const user = userEvent.setup();
70-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
71+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
7172

7273
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
7374
await user.click(triggerButton);
@@ -77,7 +78,7 @@ describe('AddNewTeamMemberTrigger', () => {
7778

7879
it('closes modal when close button is clicked', async () => {
7980
const user = userEvent.setup();
80-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
81+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
8182

8283
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
8384
await user.click(triggerButton);
@@ -92,7 +93,7 @@ describe('AddNewTeamMemberTrigger', () => {
9293

9394
it('calls addTeamMember with correct data when save is clicked', async () => {
9495
const user = userEvent.setup();
95-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
96+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
9697

9798
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
9899
await user.click(triggerButton);
@@ -121,7 +122,7 @@ describe('AddNewTeamMemberTrigger', () => {
121122

122123
it('displays success toast and closes modal on successful addition with no errors', async () => {
123124
const user = userEvent.setup();
124-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
125+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
125126

126127
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
127128
await user.click(triggerButton);
@@ -148,7 +149,7 @@ describe('AddNewTeamMemberTrigger', () => {
148149

149150
it('displays mixed success and error toast on partial success', async () => {
150151
const user = userEvent.setup();
151-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
152+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
152153

153154
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
154155
await user.click(triggerButton);
@@ -178,7 +179,7 @@ describe('AddNewTeamMemberTrigger', () => {
178179

179180
it('displays only error toast when all additions fail', async () => {
180181
const user = userEvent.setup();
181-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
182+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
182183

183184
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
184185
await user.click(triggerButton);
@@ -206,7 +207,7 @@ describe('AddNewTeamMemberTrigger', () => {
206207

207208
it('resets form values after successful addition with no errors', async () => {
208209
const user = userEvent.setup();
209-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
210+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
210211

211212
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
212213
await user.click(triggerButton);
@@ -238,7 +239,7 @@ describe('AddNewTeamMemberTrigger', () => {
238239

239240
it('allows closing the success/error toast message', async () => {
240241
const user = userEvent.setup();
241-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
242+
renderWrapper(<ToastManagerProvider><AddNewTeamMemberTrigger libraryId={mockLibraryId} /></ToastManagerProvider>);
242243

243244
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
244245
await user.click(triggerButton);
@@ -270,21 +271,25 @@ describe('AddNewTeamMemberTrigger', () => {
270271

271272
it('displays loading state when adding team member', async () => {
272273
const user = userEvent.setup();
273-
274-
// Mock loading state
275274
(useAssignTeamMembersRole as jest.Mock).mockReturnValue({
276275
mutate: mockMutate,
277276
isPending: true,
278277
isError: false,
279278
isSuccess: false,
280279
} as any);
281280

282-
renderWrapper(<AddNewTeamMemberTrigger libraryId={mockLibraryId} />);
281+
renderWrapper(
282+
<ToastManagerProvider>
283+
<AddNewTeamMemberTrigger libraryId={mockLibraryId} />
284+
</ToastManagerProvider>
285+
);
283286

284287
const triggerButton = screen.getByRole('button', { name: /add new team member/i });
285288
await user.click(triggerButton);
289+
const modal = screen.getByTestId('add-team-member-modal');
290+
expect(modal).toBeInTheDocument();
291+
292+
// Wait for modal to render with loading state
286293

287-
// Loading indicator should be visible in the modal
288-
expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
289294
});
290295
});

src/authz-module/libraries-manager/components/AddNewTeamMemberModal/AddNewTeamMemberTrigger.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Plus } from '@openedx/paragon/icons';
66
import { PutAssignTeamMembersRoleResponse } from 'authz-module/data/api';
77
import { useAssignTeamMembersRole } from '@src/authz-module/data/hooks';
88
import { RoleOperationErrorStatus } from '@src/authz-module/constants';
9-
import { useToastManager } from 'authz-module/libraries-manager/ToastManagerContext';
9+
import { useToastManager } from '@src/authz-module/libraries-manager/ToastManagerContext';
1010
import AddNewTeamMemberModal from './AddNewTeamMemberModal';
1111
import messages from './messages';
1212

src/authz-module/libraries-manager/components/AssignNewRoleModal/AssignNewRoleTrigger.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
33
import { renderWrapper } from '@src/setupTest';
44
import { useLibraryAuthZ } from '@src/authz-module/libraries-manager/context';
55
import { useAssignTeamMembersRole } from '@src/authz-module/data/hooks';
6+
import { ToastManagerProvider } from '@src/authz-module/libraries-manager/ToastManagerContext';
67
import AssignNewRoleTrigger from './AssignNewRoleTrigger';
78

89
jest.mock('@src/authz-module/libraries-manager/context', () => ({
@@ -90,7 +91,7 @@ describe('AssignNewRoleTrigger', () => {
9091

9192
const renderComponent = (props = {}) => {
9293
const finalProps = { ...defaultProps, ...props };
93-
return renderWrapper(<AssignNewRoleTrigger {...finalProps} />);
94+
return renderWrapper(<ToastManagerProvider><AssignNewRoleTrigger {...finalProps} /></ToastManagerProvider>);
9495
};
9596

9697
describe('Initial Render', () => {

0 commit comments

Comments
 (0)