|
| 1 | +import { render } from '@testing-library/react'; |
| 2 | +import { afterEach, describe, expect, it, vi } from 'vitest'; |
| 3 | +import { BoxIcon, CheckIcon, CloseIcon, CopyIcon, ZapIcon } from './Icons'; |
| 4 | + |
| 5 | +afterEach(() => { |
| 6 | + vi.unstubAllGlobals(); |
| 7 | +}); |
| 8 | + |
| 9 | +describe('Icons — responsive breakpoints', () => { |
| 10 | + it('icon dimensions are bounded values that will not cause horizontal overflow on a 375 px mobile viewport', () => { |
| 11 | + const { container: c1 } = render(<CopyIcon />); |
| 12 | + const { container: c2 } = render(<ZapIcon />); |
| 13 | + const { container: c3 } = render(<BoxIcon />); |
| 14 | + const { container: c4 } = render(<CheckIcon />); |
| 15 | + const { container: c5 } = render(<CloseIcon />); |
| 16 | + |
| 17 | + const MOBILE_WIDTH = 375; |
| 18 | + for (const container of [c1, c2, c3, c4, c5]) { |
| 19 | + const svg = container.querySelector('svg'); |
| 20 | + expect(svg).not.toBeNull(); |
| 21 | + const widthAttr = svg!.getAttribute('width'); |
| 22 | + expect(widthAttr).not.toBeNull(); |
| 23 | + const width = Number(widthAttr); |
| 24 | + expect(Number.isFinite(width)).toBe(true); |
| 25 | + expect(width).toBeGreaterThan(0); |
| 26 | + expect(width).toBeLessThanOrEqual(MOBILE_WIDTH); |
| 27 | + } |
| 28 | + }); |
| 29 | + |
| 30 | + it('all icons use explicit numeric width and height attributes — no percentage or auto values that could cause layout instability', () => { |
| 31 | + const icons = [ |
| 32 | + render(<CopyIcon />), |
| 33 | + render(<ZapIcon />), |
| 34 | + render(<BoxIcon />), |
| 35 | + render(<CheckIcon />), |
| 36 | + render(<CloseIcon />), |
| 37 | + ]; |
| 38 | + |
| 39 | + for (const { container } of icons) { |
| 40 | + const svg = container.querySelector('svg'); |
| 41 | + const width = svg?.getAttribute('width'); |
| 42 | + const height = svg?.getAttribute('height'); |
| 43 | + expect(Number(width)).toBeGreaterThan(0); |
| 44 | + expect(Number(height)).toBeGreaterThan(0); |
| 45 | + } |
| 46 | + }); |
| 47 | + |
| 48 | + it('CopyIcon and CheckIcon use compact 20 px sizing appropriate for inline/mobile use', () => { |
| 49 | + const { container: copyContainer } = render(<CopyIcon />); |
| 50 | + const { container: checkContainer } = render(<CheckIcon />); |
| 51 | + |
| 52 | + expect(Number(copyContainer.querySelector('svg')?.getAttribute('width'))).toBe(20); |
| 53 | + expect(Number(copyContainer.querySelector('svg')?.getAttribute('height'))).toBe(20); |
| 54 | + expect(Number(checkContainer.querySelector('svg')?.getAttribute('width'))).toBe(20); |
| 55 | + expect(Number(checkContainer.querySelector('svg')?.getAttribute('height'))).toBe(20); |
| 56 | + }); |
| 57 | + |
| 58 | + it('CloseIcon uses a smaller 18 px footprint suitable for dismissal controls on narrow viewports', () => { |
| 59 | + const { container } = render(<CloseIcon />); |
| 60 | + const svg = container.querySelector('svg'); |
| 61 | + |
| 62 | + expect(Number(svg?.getAttribute('width'))).toBe(18); |
| 63 | + expect(Number(svg?.getAttribute('height'))).toBe(18); |
| 64 | + }); |
| 65 | + |
| 66 | + it('ZapIcon and BoxIcon use 24 px sizing that stays within standard mobile touch-target bounds', () => { |
| 67 | + const { container: zapContainer } = render(<ZapIcon />); |
| 68 | + const { container: boxContainer } = render(<BoxIcon />); |
| 69 | + |
| 70 | + const TOUCH_TARGET = 48; |
| 71 | + for (const container of [zapContainer, boxContainer]) { |
| 72 | + const width = Number(container.querySelector('svg')?.getAttribute('width')); |
| 73 | + const height = Number(container.querySelector('svg')?.getAttribute('height')); |
| 74 | + expect(width).toBe(24); |
| 75 | + expect(height).toBe(24); |
| 76 | + expect(width).toBeLessThanOrEqual(TOUCH_TARGET); |
| 77 | + expect(height).toBeLessThanOrEqual(TOUCH_TARGET); |
| 78 | + } |
| 79 | + }); |
| 80 | +}); |
0 commit comments