diff --git a/src/components/modalTransparent/__test__/TransparentModal.test.tsx b/src/components/modalTransparent/__test__/TransparentModal.test.tsx index 246e801..1d277f3 100644 --- a/src/components/modalTransparent/__test__/TransparentModal.test.tsx +++ b/src/components/modalTransparent/__test__/TransparentModal.test.tsx @@ -130,4 +130,11 @@ describe('TransparentModal Component', () => { const container = document.querySelector('.fixed.inset-0.z-50'); expect(container).toHaveClass('pointer-events-none'); }); + + it('should not call onClose when clicking inside the modal content', () => { + renderTransparentModal(); + const modalContent = screen.getByText('Modal Content'); + fireEvent.mouseDown(modalContent); + expect(onCloseMock).not.toHaveBeenCalled(); + }); }); diff --git a/src/components/popover/__test__/Popover.test.tsx b/src/components/popover/__test__/Popover.test.tsx index 09d1dad..29a1592 100644 --- a/src/components/popover/__test__/Popover.test.tsx +++ b/src/components/popover/__test__/Popover.test.tsx @@ -135,4 +135,12 @@ describe('Popover', () => { fireEvent.mouseDown(button); expect(stopPropagationSpy).toHaveBeenCalled(); }); + + it('aligns panel to the left when align is left', async () => { + const { getByText, container } = renderPopover({ align: 'left' }); + fireEvent.click(getByText('Open Popover')); + await waitFor(() => expect(getByText('Popover Content')).toBeInTheDocument()); + const panel = container.querySelector('.left-0.origin-top-left'); + expect(panel).toBeInTheDocument(); + }); }); diff --git a/src/components/sidenav/__test__/Sidenav.test.tsx b/src/components/sidenav/__test__/Sidenav.test.tsx index 0c6e7e4..f7c759e 100644 --- a/src/components/sidenav/__test__/Sidenav.test.tsx +++ b/src/components/sidenav/__test__/Sidenav.test.tsx @@ -287,4 +287,45 @@ describe('Sidenav Component', () => { expect(onToggleCollapse).toHaveBeenCalled(); }); }); + + it('renders storage without upgradeLabel', () => { + const { queryByText } = renderSidenav({ + storage: { + usage: '1 GB', + limit: '5 GB', + percentage: 20, + onUpgradeClick: vi.fn(), + isLoading: false, + }, + }); + expect(queryByText('1 GB')).toBeInTheDocument(); + expect(queryByText('Upgrade')).not.toBeInTheDocument(); + }); + + it('renders storage with default isLoading (shows skeleton)', () => { + const { container, queryByText } = renderSidenav({ + storage: { + usage: '1 GB', + limit: '5 GB', + percentage: 20, + onUpgradeClick: vi.fn(), + }, + }); + expect(queryByText('1 GB')).not.toBeInTheDocument(); + const skeletons = container.querySelectorAll('.animate-pulse'); + expect(skeletons.length).toBeGreaterThan(0); + }); + + it('renders header with suiteLauncher', () => { + const MockIcon = React.forwardRef(({ size = 20 }, ref) => ( + + )); + const { getByTestId } = renderSidenav({ + suiteLauncher: { + suiteArray: [{ icon: , title: 'Drive', onClick: vi.fn() }], + soonText: 'Soon', + }, + }); + expect(getByTestId('popover-button')).toBeInTheDocument(); + }); }); diff --git a/src/components/sidenav/__test__/SidenavItem.test.tsx b/src/components/sidenav/__test__/SidenavItem.test.tsx new file mode 100644 index 0000000..70919ad --- /dev/null +++ b/src/components/sidenav/__test__/SidenavItem.test.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import SidenavItem from '../SidenavItem'; + +const MockIcon = React.forwardRef(({ size = 20 }, ref) => ( + +)); +MockIcon.displayName = 'MockIcon'; + +describe('SidenavItem Component', () => { + it('renders correctly with default optional props', () => { + const { getByText, getByTestId } = render(); + + expect(getByText('Dashboard')).toBeInTheDocument(); + expect(getByTestId('mock-icon')).toBeInTheDocument(); + + // Default inactive state includes text-gray-80 + expect(getByText('Dashboard').parentElement).toHaveClass('text-gray-80'); + }); + + it('renders subsection with correct styling', () => { + const { getByRole } = render(); + const button = getByRole('button'); + expect(button).toHaveClass('pl-5'); + }); +}); diff --git a/src/components/suiteLauncher/__test__/SuiteLauncher.test.tsx b/src/components/suiteLauncher/__test__/SuiteLauncher.test.tsx index 1c4d986..433d62c 100644 --- a/src/components/suiteLauncher/__test__/SuiteLauncher.test.tsx +++ b/src/components/suiteLauncher/__test__/SuiteLauncher.test.tsx @@ -59,4 +59,27 @@ describe('SuiteLauncher', () => { fireEvent.click(screen.getByText('App 3')); expect(onClick3).toHaveBeenCalled(); }); + + it('shows default "Soon" text when soonText is not provided', () => { + const suiteArray = [ + { icon: , title: 'App Soon', onClick: vi.fn(), availableSoon: true }, + ]; + render(); + fireEvent.click(screen.getByTestId('popover-button')); + expect(screen.getByText('Soon')).toBeInTheDocument(); + }); + + it('renders non-JSX icon as-is when it is not a valid React element', () => { + const suiteArray = [ + { icon: 'not-an-element' as any, title: 'App Raw', onClick: vi.fn() }, + ]; + render(); + fireEvent.click(screen.getByTestId('popover-button')); + expect(screen.getByText('App Raw')).toBeInTheDocument(); + }); + + it('renders with align left', () => { + render(); + expect(screen.getByTestId('popover-button')).toBeInTheDocument(); + }); }); diff --git a/src/hooks/__test__/useHotKeys.test.ts b/src/hooks/__test__/useHotKeys.test.ts index f7e977e..aa30149 100644 --- a/src/hooks/__test__/useHotKeys.test.ts +++ b/src/hooks/__test__/useHotKeys.test.ts @@ -69,4 +69,58 @@ describe('useHotkeys', () => { unmount(); expect(removeEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function)); }); + + it('should not call handler when key is pressed in an input element', () => { + const callback = vi.fn(); + renderHook(() => useHotkeys({ a: callback })); + + const input = document.createElement('input'); + document.body.appendChild(input); + input.focus(); + + fireKeyDown('a'); + expect(callback).not.toHaveBeenCalled(); + + document.body.removeChild(input); + }); + + it('should call handler for Escape even when focus is in an input element', () => { + const escapeCallback = vi.fn(); + renderHook(() => useHotkeys({ escape: escapeCallback })); + + const input = document.createElement('input'); + document.body.appendChild(input); + input.focus(); + + fireKeyDown('Escape'); + expect(escapeCallback).toHaveBeenCalledOnce(); + + document.body.removeChild(input); + }); + + it('should not call handler when key is pressed in a textarea element', () => { + const callback = vi.fn(); + renderHook(() => useHotkeys({ r: callback })); + + const textarea = document.createElement('textarea'); + document.body.appendChild(textarea); + textarea.focus(); + + fireKeyDown('r'); + expect(callback).not.toHaveBeenCalled(); + + document.body.removeChild(textarea); + }); + + it('should handle null activeElement gracefully and still trigger handler', () => { + const callback = vi.fn(); + renderHook(() => useHotkeys({ a: callback })); + + const activeElementSpy = vi.spyOn(document, 'activeElement', 'get').mockReturnValue(null); + + fireKeyDown('a'); + expect(callback).toHaveBeenCalledOnce(); + + activeElementSpy.mockRestore(); + }); });