From 3964c22c6677c18ce45c6e8942ac4f5496684a9a Mon Sep 17 00:00:00 2001 From: Maulana Sodiqin Date: Sun, 16 Mar 2025 00:16:58 +0700 Subject: [PATCH] feat: navbar unit test --- libs/ui/src/organisms/navbar/navbar.spec.tsx | 176 +++++++++++++++++++ package.json | 3 +- 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 libs/ui/src/organisms/navbar/navbar.spec.tsx diff --git a/libs/ui/src/organisms/navbar/navbar.spec.tsx b/libs/ui/src/organisms/navbar/navbar.spec.tsx new file mode 100644 index 0000000..99385ea --- /dev/null +++ b/libs/ui/src/organisms/navbar/navbar.spec.tsx @@ -0,0 +1,176 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { + render, + screen, + fireEvent, + RenderResult, +} from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; +import { Navbar } from './navbar'; + +interface MatchMediaResult { + matches: boolean; + media: string; + onchange: null; + addListener: ReturnType; + removeListener: ReturnType; + addEventListener: ReturnType; + removeEventListener: ReturnType; + dispatchEvent: ReturnType; +} + +function mockMatchMedia(width: { matches: boolean }): void { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation( + (query: string): MatchMediaResult => ({ + matches: width.matches, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + }) + ), + }); +} + +describe('Navbar', () => { + beforeEach(() => { + document.body.innerHTML = ''; + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('renders the logo', () => { + render( + + + + ); + const logo: HTMLElement = screen.getByAltText(/IMPHNEN Logo/i); + expect(logo).toBeInTheDocument(); + }); + + it('renders the Home link', () => { + render( + + + + ); + const homeLink: HTMLElement = screen.getByRole('link', { name: /home/i }); + expect(homeLink).toBeInTheDocument(); + }); + + it('renders the Merch Gacha link', () => { + render( + + + + ); + const merchLink: HTMLElement = screen.getByRole('link', { + name: /merch gacha/i, + }); + expect(merchLink).toBeInTheDocument(); + }); + + it('displays horizontal menu on desktop screens', () => { + mockMatchMedia({ matches: true }); + + global.innerWidth = 1024; + global.dispatchEvent(new Event('resize')); + + render( + + + + ); + + const navItems: HTMLElement = screen.getByRole('list'); + expect(navItems).toHaveClass('md:flex'); + + const hamburgerButton: HTMLElement = screen.getByRole('button'); + expect(hamburgerButton).toHaveClass('md:hidden'); + }); + + it('displays hamburger menu on mobile screens and can toggle dropdown', () => { + mockMatchMedia({ matches: false }); + + global.innerWidth = 375; + global.dispatchEvent(new Event('resize')); + + render( + + + + ); + + const hamburgerButton: HTMLElement = screen.getByRole('button'); + expect(hamburgerButton).toBeVisible(); + + fireEvent.click(hamburgerButton); + + const dropdownMenu: HTMLElement[] = screen.getAllByRole('list'); + expect(dropdownMenu[1]).toBeVisible(); + + fireEvent.click(hamburgerButton); + + const updatedDropdownMenus: HTMLElement[] = screen.getAllByRole('list'); + expect(updatedDropdownMenus.length).toBe(1); + }); + + it('has correct height on tablet screens', () => { + mockMatchMedia({ matches: true }); + + global.innerWidth = 768; + global.dispatchEvent(new Event('resize')); + + const { container }: RenderResult = render( + + + + ); + + const header: HTMLElement | null = container.querySelector('header'); + expect(header).not.toBeNull(); + if (header) { + expect(header).toHaveClass('md:min-h-[60px]'); + expect(header).toHaveClass('md:max-h-[60px]'); + } + }); + + it('has max width constraint on large screens', () => { + mockMatchMedia({ matches: true }); + + global.innerWidth = 3840; + global.dispatchEvent(new Event('resize')); + + const { container }: RenderResult = render( + + + + ); + + const header: HTMLElement | null = container.querySelector('header'); + expect(header).not.toBeNull(); + if (header) { + expect(header).toHaveClass('max-w-[1280px]'); + expect(header).toHaveClass('xl:mx-auto'); + } + }); + + it('has correct ARIA role for navigation', () => { + const { container }: RenderResult = render( + + + + ); + + const header: HTMLElement | null = container.querySelector('header'); + expect(header).toHaveAttribute('role', 'navigation'); + }); +}); diff --git a/package.json b/package.json index 606c21f..13f4033 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "dimentorin:dev": "nx serve dimentorin", "dimentorin:build": "nx build dimentorin", "dimentorin:prod": "serve ./dist/apps/dimentorin", - "ui:test": "nx test ui" + "ui:test": "nx test ui", + "ui:build": "nx build ui" }, "private": true, "dependencies": {