Skip to content

Commit e1cf3f9

Browse files
committed
replace unit tests with Playwright screenshot tests for zoom
Remove jsdom-based unit tests for pinch/wheel zoom. Add Playwright screenshot tests that verify zoom visually: wheel scroll up/down, trackpad pinch (ctrlKey+wheel), and two-finger touch pinch via CDP.
1 parent b28e803 commit e1cf3f9

6 files changed

Lines changed: 105 additions & 226 deletions

packages/lib/src/__tests__/AvatarEditor.test.tsx

Lines changed: 0 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -547,230 +547,4 @@ describe('AvatarEditor', () => {
547547
fireEvent.keyDown(canvas, { key: 'ArrowUp' })
548548
})
549549
})
550-
551-
// -------------------------------------------------------
552-
// 14. Pinch-to-zoom
553-
// -------------------------------------------------------
554-
describe('pinch-to-zoom', () => {
555-
// Helper to create a TouchEvent with custom touches (jsdom lacks Touch constructor)
556-
function makeTouchEvent(
557-
type: string,
558-
touches: Array<{ pageX: number; pageY: number }>,
559-
opts: EventInit = {},
560-
) {
561-
const event = new Event(type, {
562-
bubbles: true,
563-
cancelable: true,
564-
...opts,
565-
})
566-
Object.defineProperty(event, 'touches', { value: touches })
567-
if (type === 'touchmove') {
568-
Object.defineProperty(event, 'targetTouches', { value: touches })
569-
}
570-
return event
571-
}
572-
573-
it('calls onRequestScaleChange when pinching with two fingers', () => {
574-
const onRequestScaleChange = vi.fn()
575-
const { container } = render(
576-
<AvatarEditor
577-
width={200}
578-
height={200}
579-
scale={1}
580-
onRequestScaleChange={onRequestScaleChange}
581-
/>,
582-
)
583-
const canvas = container.querySelector('canvas')!
584-
585-
// Start two-finger touch
586-
fireEvent.touchStart(canvas, {
587-
touches: [
588-
{ pageX: 100, pageY: 100 },
589-
{ pageX: 200, pageY: 100 },
590-
],
591-
})
592-
593-
// Move fingers apart (zoom in) - initial distance 100, new distance 200
594-
document.dispatchEvent(
595-
makeTouchEvent('touchmove', [
596-
{ pageX: 50, pageY: 100 },
597-
{ pageX: 250, pageY: 100 },
598-
]),
599-
)
600-
601-
expect(onRequestScaleChange).toHaveBeenCalled()
602-
const newScale = onRequestScaleChange.mock.calls[0][0]
603-
expect(newScale).toBeGreaterThan(1)
604-
})
605-
606-
it('starts pinch when second finger is added during drag', () => {
607-
const onRequestScaleChange = vi.fn()
608-
const { container } = render(
609-
<AvatarEditor
610-
width={200}
611-
height={200}
612-
scale={1}
613-
onRequestScaleChange={onRequestScaleChange}
614-
/>,
615-
)
616-
const canvas = container.querySelector('canvas')!
617-
618-
// Start single-finger drag
619-
fireEvent.touchStart(canvas, {
620-
touches: [{ pageX: 100, pageY: 100 }],
621-
})
622-
623-
// Second finger appears during touchmove - should initialize pinch
624-
document.dispatchEvent(
625-
makeTouchEvent('touchmove', [
626-
{ pageX: 100, pageY: 100 },
627-
{ pageX: 200, pageY: 100 },
628-
]),
629-
)
630-
631-
// Now move fingers apart - should trigger scale change
632-
document.dispatchEvent(
633-
makeTouchEvent('touchmove', [
634-
{ pageX: 50, pageY: 100 },
635-
{ pageX: 250, pageY: 100 },
636-
]),
637-
)
638-
639-
expect(onRequestScaleChange).toHaveBeenCalled()
640-
})
641-
642-
it('resets pinch state on touchend', () => {
643-
const onRequestScaleChange = vi.fn()
644-
const { container } = render(
645-
<AvatarEditor
646-
width={200}
647-
height={200}
648-
scale={1}
649-
onRequestScaleChange={onRequestScaleChange}
650-
/>,
651-
)
652-
const canvas = container.querySelector('canvas')!
653-
654-
// Start pinch
655-
fireEvent.touchStart(canvas, {
656-
touches: [
657-
{ pageX: 100, pageY: 100 },
658-
{ pageX: 200, pageY: 100 },
659-
],
660-
})
661-
662-
// End touch
663-
document.dispatchEvent(new Event('touchend', { bubbles: true }))
664-
665-
onRequestScaleChange.mockClear()
666-
667-
// Single-touch drag should not trigger scale change
668-
fireEvent.touchStart(canvas, {
669-
touches: [{ pageX: 100, pageY: 100 }],
670-
})
671-
672-
document.dispatchEvent(
673-
makeTouchEvent('touchmove', [{ pageX: 110, pageY: 100 }]),
674-
)
675-
676-
expect(onRequestScaleChange).not.toHaveBeenCalled()
677-
})
678-
})
679-
680-
// -------------------------------------------------------
681-
// 15. Wheel zoom
682-
// -------------------------------------------------------
683-
describe('wheel zoom', () => {
684-
it('does not call onRequestScaleChange on wheel by default', () => {
685-
const onRequestScaleChange = vi.fn()
686-
const { container } = render(
687-
<AvatarEditor
688-
width={200}
689-
height={200}
690-
scale={1}
691-
onRequestScaleChange={onRequestScaleChange}
692-
/>,
693-
)
694-
const canvas = container.querySelector('canvas')!
695-
696-
canvas.dispatchEvent(
697-
new WheelEvent('wheel', { deltaY: -100, bubbles: true }),
698-
)
699-
700-
expect(onRequestScaleChange).not.toHaveBeenCalled()
701-
})
702-
703-
it('calls onRequestScaleChange on wheel when enableWheelZoom is true', () => {
704-
const onRequestScaleChange = vi.fn()
705-
const { container } = render(
706-
<AvatarEditor
707-
width={200}
708-
height={200}
709-
scale={1}
710-
onRequestScaleChange={onRequestScaleChange}
711-
enableWheelZoom
712-
/>,
713-
)
714-
const canvas = container.querySelector('canvas')!
715-
716-
canvas.dispatchEvent(
717-
new WheelEvent('wheel', { deltaY: -100, bubbles: true }),
718-
)
719-
720-
expect(onRequestScaleChange).toHaveBeenCalled()
721-
const newScale = onRequestScaleChange.mock.calls[0][0]
722-
expect(newScale).toBeGreaterThan(1)
723-
})
724-
725-
it('zooms out on positive deltaY', () => {
726-
const onRequestScaleChange = vi.fn()
727-
const { container } = render(
728-
<AvatarEditor
729-
width={200}
730-
height={200}
731-
scale={1.5}
732-
onRequestScaleChange={onRequestScaleChange}
733-
enableWheelZoom
734-
/>,
735-
)
736-
const canvas = container.querySelector('canvas')!
737-
738-
canvas.dispatchEvent(
739-
new WheelEvent('wheel', { deltaY: 100, bubbles: true }),
740-
)
741-
742-
expect(onRequestScaleChange).toHaveBeenCalled()
743-
const newScale = onRequestScaleChange.mock.calls[0][0]
744-
expect(newScale).toBeLessThan(1.5)
745-
})
746-
747-
it('does not zoom below 0.1', () => {
748-
const onRequestScaleChange = vi.fn()
749-
const { container } = render(
750-
<AvatarEditor
751-
width={200}
752-
height={200}
753-
scale={0.1}
754-
onRequestScaleChange={onRequestScaleChange}
755-
enableWheelZoom
756-
/>,
757-
)
758-
const canvas = container.querySelector('canvas')!
759-
760-
canvas.dispatchEvent(
761-
new WheelEvent('wheel', { deltaY: 99999, bubbles: true }),
762-
)
763-
764-
expect(onRequestScaleChange).toHaveBeenCalled()
765-
const newScale = onRequestScaleChange.mock.calls[0][0]
766-
expect(newScale).toBeGreaterThanOrEqual(0.1)
767-
})
768-
769-
it('accepts enableWheelZoom prop without error', () => {
770-
const { container } = render(
771-
<AvatarEditor width={200} height={200} enableWheelZoom />,
772-
)
773-
expect(container.querySelector('canvas')).toBeInTheDocument()
774-
})
775-
})
776550
})
106 KB
Loading
121 KB
Loading
4.5 KB
Loading
71.7 KB
Loading

packages/lib/tests/canvas.test.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,111 @@ test('exported image has no color overlay', async ({ page }) => {
8888
expect(await previewImg.screenshot()).toMatchSnapshot()
8989
})
9090

91+
test('canvas zooms in on wheel scroll up', async ({ page }) => {
92+
await page.goto('/')
93+
await page.waitForSelector('canvas')
94+
await page.waitForTimeout(500)
95+
96+
const canvas = page.locator('canvas')
97+
98+
// Wheel scroll up (negative deltaY) should zoom in
99+
await canvas.dispatchEvent('wheel', {
100+
deltaY: -500,
101+
clientX: 150,
102+
clientY: 150,
103+
})
104+
await page.waitForTimeout(200)
105+
expect(await canvas.screenshot()).toMatchSnapshot()
106+
})
107+
108+
test('canvas zooms out on wheel scroll down', async ({ page }) => {
109+
await page.goto('/')
110+
await page.waitForSelector('canvas')
111+
await page.waitForTimeout(500)
112+
113+
const canvas = page.locator('canvas')
114+
115+
// First zoom in, then zoom out past the initial level
116+
await canvas.dispatchEvent('wheel', {
117+
deltaY: -500,
118+
clientX: 150,
119+
clientY: 150,
120+
})
121+
await page.waitForTimeout(100)
122+
await canvas.dispatchEvent('wheel', {
123+
deltaY: 1000,
124+
clientX: 150,
125+
clientY: 150,
126+
})
127+
await page.waitForTimeout(200)
128+
expect(await canvas.screenshot()).toMatchSnapshot()
129+
})
130+
131+
test('canvas zooms on trackpad pinch gesture (ctrlKey+wheel)', async ({
132+
page,
133+
}) => {
134+
await page.goto('/')
135+
await page.waitForSelector('canvas')
136+
await page.waitForTimeout(500)
137+
138+
const canvas = page.locator('canvas')
139+
140+
// Trackpad pinch fires as wheel events with ctrlKey: true
141+
await canvas.dispatchEvent('wheel', {
142+
deltaY: -50,
143+
ctrlKey: true,
144+
clientX: 150,
145+
clientY: 150,
146+
})
147+
await page.waitForTimeout(200)
148+
expect(await canvas.screenshot()).toMatchSnapshot()
149+
})
150+
151+
test('canvas zooms via touch pinch gesture', async ({ page }) => {
152+
await page.goto('/')
153+
await page.waitForSelector('canvas')
154+
await page.waitForTimeout(500)
155+
156+
const canvas = page.locator('canvas')
157+
const box = await canvas.boundingBox()
158+
if (!box) throw new Error('canvas not found')
159+
160+
const cx = box.x + box.width / 2
161+
const cy = box.y + box.height / 2
162+
163+
// Simulate two-finger pinch spread using CDP Touch events
164+
const client = await page.context().newCDPSession(page)
165+
166+
// Touch down with two fingers close together
167+
await client.send('Input.dispatchTouchEvent', {
168+
type: 'touchStart',
169+
touchPoints: [
170+
{ x: cx - 20, y: cy },
171+
{ x: cx + 20, y: cy },
172+
],
173+
})
174+
175+
// Spread fingers apart (zoom in)
176+
for (let i = 1; i <= 5; i++) {
177+
await client.send('Input.dispatchTouchEvent', {
178+
type: 'touchMove',
179+
touchPoints: [
180+
{ x: cx - 20 - i * 15, y: cy },
181+
{ x: cx + 20 + i * 15, y: cy },
182+
],
183+
})
184+
}
185+
186+
// Release
187+
await client.send('Input.dispatchTouchEvent', {
188+
type: 'touchEnd',
189+
touchPoints: [],
190+
})
191+
192+
await page.waitForTimeout(200)
193+
expect(await canvas.screenshot()).toMatchSnapshot()
194+
})
195+
91196
test('canvas is keyboard accessible', async ({ page }) => {
92197
await page.goto('/')
93198
await page.waitForSelector('canvas')

0 commit comments

Comments
 (0)