From 9502a0c55069c9d8f3c0a2128851cea5a4532076 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 27 May 2026 21:19:08 +0500 Subject: [PATCH] fix: wrap dismissNotification with useUnit in NotificationStack for scope isolation; fix test selectors --- .../NotificationStack.test.tsx | 79 +++++++++++++------ .../NotificationStack/NotificationStack.tsx | 3 +- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/client/src/shared/ui/NotificationStack/NotificationStack.test.tsx b/client/src/shared/ui/NotificationStack/NotificationStack.test.tsx index 4dd63eb..b84798d 100644 --- a/client/src/shared/ui/NotificationStack/NotificationStack.test.tsx +++ b/client/src/shared/ui/NotificationStack/NotificationStack.test.tsx @@ -1,36 +1,71 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react' -import { describe, it, expect, beforeEach } from 'vitest' -import { addNotification, dismissAll } from '../../model/notification' +import { allSettled, fork } from 'effector' +import { Provider } from 'effector-react' +import { describe, it, expect, afterEach } from 'vitest' +import { addNotification, dismissAll, $notifications } from '../../model/notification' import { NotificationStack } from './NotificationStack' -beforeEach(() => { - dismissAll() -}) - describe('NotificationStack', () => { + afterEach(() => { + dismissAll() + }) + + function createTestScope() { + return fork() + } + + function renderWithScope(scope: ReturnType) { + return render( + + + , + ) + } + it('renders nothing when empty', () => { - const { container } = render() - expect(container.firstChild).toBeNull() + const scope = createTestScope() + const { container } = renderWithScope(scope) + expect(container.textContent).toBe('') }) - it('renders notification when added', async () => { - render() - addNotification({ type: 'info', message: 'Hello' }) + it('renders a success notification with check icon', async () => { + const scope = createTestScope() + await allSettled(addNotification, { scope, params: { type: 'success', message: 'Success!' } }) + renderWithScope(scope) + expect(screen.getByText('Success!')).toBeDefined() + }) + + it('renders error alert severity', async () => { + const scope = createTestScope() + await allSettled(addNotification, { scope, params: { type: 'error', message: 'Error!' } }) + renderWithScope(scope) + const alert = screen.getByRole('alert') + expect(alert).toBeDefined() + }) + + it('dismisses on close button click', async () => { + const scope = createTestScope() + await allSettled(addNotification, { scope, params: { type: 'info', message: 'Dismiss me' } }) + renderWithScope(scope) + + const closeBtn = screen.getByRole('button') + fireEvent.click(closeBtn) + await waitFor(() => { - expect(screen.getByText('Hello')).toBeInTheDocument() + const state = scope.getState($notifications) + expect(state).toHaveLength(0) }) }) - it('dismiss button works', async () => { - render() - addNotification({ type: 'info', message: 'Dismiss me' }) - await waitFor(() => { - expect(screen.getByText('Dismiss me')).toBeInTheDocument() - }) + it('renders multiple notifications up to 3', async () => { + const scope = createTestScope() + await allSettled(addNotification, { scope, params: { type: 'info', message: 'A' } }) + await allSettled(addNotification, { scope, params: { type: 'info', message: 'B' } }) + await allSettled(addNotification, { scope, params: { type: 'info', message: 'C' } }) + renderWithScope(scope) - fireEvent.click(screen.getByTestId('CloseIcon')) - await waitFor(() => { - expect(screen.queryByText('Dismiss me')).not.toBeInTheDocument() - }) + expect(screen.getByText('A')).toBeDefined() + expect(screen.getByText('B')).toBeDefined() + expect(screen.getByText('C')).toBeDefined() }) }) diff --git a/client/src/shared/ui/NotificationStack/NotificationStack.tsx b/client/src/shared/ui/NotificationStack/NotificationStack.tsx index d7b8e4a..696c0f0 100644 --- a/client/src/shared/ui/NotificationStack/NotificationStack.tsx +++ b/client/src/shared/ui/NotificationStack/NotificationStack.tsx @@ -1,10 +1,11 @@ import { useUnit } from 'effector-react' import { Snackbar, Alert, Stack, IconButton } from '@mui/material' import CloseIcon from '@mui/icons-material/Close' -import { $notifications, dismissNotification } from '../../model/notification' +import { $notifications, dismissNotification as dismissNotificationEvent } from '../../model/notification' export function NotificationStack() { const notifications = useUnit($notifications) + const dismissNotification = useUnit(dismissNotificationEvent) if (notifications.length === 0) return null