-
Notifications
You must be signed in to change notification settings - Fork 0
Add Unit Tests for useIsMobile Hook #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1755ffc
19fcbf7
029d8a3
783d315
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,165 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { GlobalRegistrator } from "@happy-dom/global-registrator"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GlobalRegistrator.register(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, expect, test, mock, beforeEach, afterEach } from "bun:test"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { renderHook, act } from "@testing-library/react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useIsMobile } from "./use-mobile"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The hook uses 768 as the breakpoint internally. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MOBILE_BREAKPOINT = 768; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe("useIsMobile", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let listeners: Record<string, ((e: MediaQueryListEvent) => void)[]> = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Store original implementation to restore later | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let originalMatchMedia: any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let originalInnerWidth: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listeners = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Store original implementation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalMatchMedia = window.matchMedia; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalInnerWidth = window.innerWidth; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Mock window.matchMedia | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.defineProperty(window, "matchMedia", { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| writable: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: mock((query: string) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| matches: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| media: query, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onchange: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| addListener: mock(), // Deprecated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeListener: mock(), // Deprecated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| addEventListener: mock((type: string, listener: (e: MediaQueryListEvent) => void) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!listeners[type]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listeners[type] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listeners[type].push(listener); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeEventListener: mock((type: string, listener: (e: MediaQueryListEvent) => void) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (listeners[type]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listeners[type] = listeners[type].filter((l) => l !== listener); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dispatchEvent: mock(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+47
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: mock((query: string) => ({ | |
| matches: false, | |
| media: query, | |
| onchange: null, | |
| addListener: mock(), // Deprecated | |
| removeListener: mock(), // Deprecated | |
| addEventListener: mock((type: string, listener: (e: MediaQueryListEvent) => void) => { | |
| if (!listeners[type]) listeners[type] = []; | |
| listeners[type].push(listener); | |
| }), | |
| removeEventListener: mock((type: string, listener: (e: MediaQueryListEvent) => void) => { | |
| if (listeners[type]) { | |
| listeners[type] = listeners[type].filter((l) => l !== listener); | |
| } | |
| }), | |
| dispatchEvent: mock(), | |
| })), | |
| value: mock((query: string) => { | |
| const width = window.innerWidth; | |
| // Basic support for (max-width: Npx), (min-width: Npx) and combined queries | |
| const maxMatch = query.match(/\(max-width:\s*(\d+)px\)/); | |
| const minMatch = query.match(/\(min-width:\s*(\d+)px\)/); | |
| const maxWidth = maxMatch ? parseInt(maxMatch[1], 10) : undefined; | |
| const minWidth = minMatch ? parseInt(minMatch[1], 10) : undefined; | |
| let matches = true; | |
| if (typeof maxWidth === "number") { | |
| matches = matches && width <= maxWidth; | |
| } | |
| if (typeof minWidth === "number") { | |
| matches = matches && width >= minWidth; | |
| } | |
| // If no constraints parsed, default to false to avoid accidental matches | |
| if (typeof maxWidth === "undefined" && typeof minWidth === "undefined") { | |
| matches = false; | |
| } | |
| return { | |
| matches, | |
| media: query, | |
| onchange: null, | |
| addListener: mock(), // Deprecated | |
| removeListener: mock(), // Deprecated | |
| addEventListener: mock((type: string, listener: (e: MediaQueryListEvent) => void) => { | |
| if (!listeners[type]) listeners[type] = []; | |
| listeners[type].push(listener); | |
| }), | |
| removeEventListener: mock((type: string, listener: (e: MediaQueryListEvent) => void) => { | |
| if (listeners[type]) { | |
| listeners[type] = listeners[type].filter((l) => l !== listener); | |
| } | |
| }), | |
| dispatchEvent: mock(), | |
| }; | |
| }), |
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test suite is missing edge case tests for the exact breakpoint boundaries. The hook checks window.innerWidth < 768, so values of 767px (should be mobile) and 768px (should be desktop) should be explicitly tested to ensure the boundary condition is handled correctly.
| test("should return true when window width is exactly one pixel below MOBILE_BREAKPOINT", () => { | |
| // Set width to just below the breakpoint (mobile) | |
| Object.defineProperty(window, "innerWidth", { | |
| writable: true, | |
| value: MOBILE_BREAKPOINT - 1, | |
| }); | |
| const { result } = renderHook(() => useIsMobile()); | |
| expect(result.current).toBe(true); | |
| }); | |
| test("should return false when window width is exactly MOBILE_BREAKPOINT", () => { | |
| // Set width to the breakpoint (desktop according to '< MOBILE_BREAKPOINT') | |
| Object.defineProperty(window, "innerWidth", { | |
| writable: true, | |
| value: MOBILE_BREAKPOINT, | |
| }); | |
| const { result } = renderHook(() => useIsMobile()); | |
| expect(result.current).toBe(false); | |
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable MOBILE_BREAKPOINT.