diff --git a/src/renderer/src/components/browser-pane/markup/useMarkupEditor.test.ts b/src/renderer/src/components/browser-pane/markup/useMarkupEditor.test.ts new file mode 100644 index 00000000000..6e6ba294028 --- /dev/null +++ b/src/renderer/src/components/browser-pane/markup/useMarkupEditor.test.ts @@ -0,0 +1,47 @@ +// @vitest-environment happy-dom + +import { act, renderHook } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { useMarkupEditor } from './useMarkupEditor' + +const realCrypto = globalThis.crypto + +function pointerDownEvent(clientX: number, clientY: number): React.PointerEvent { + return { + button: 0, + clientX, + clientY, + currentTarget: { setPointerCapture: vi.fn() }, + pointerId: 1, + preventDefault: vi.fn() + } as unknown as React.PointerEvent +} + +describe('useMarkupEditor.commitPendingText in a non-secure browser context', () => { + beforeEach(() => { + // Match a non-secure browser context (LAN web client over plain HTTP): + // getRandomValues stays, randomUUID is undefined. + Object.defineProperty(globalThis, 'crypto', { + configurable: true, + value: { getRandomValues: realCrypto.getRandomValues.bind(realCrypto) } + }) + }) + + afterEach(() => { + Object.defineProperty(globalThis, 'crypto', { configurable: true, value: realCrypto }) + }) + + it('commits a text shape with a valid id instead of throwing', () => { + const { result } = renderHook(() => useMarkupEditor(false, vi.fn())) + + act(() => result.current.setTool('text')) + act(() => result.current.onPointerDown(pointerDownEvent(5, 5))) + expect(result.current.pendingText).not.toBeNull() + + expect(() => act(() => result.current.commitPendingText('hello'))).not.toThrow() + + const shape = result.current.shapes.at(-1) + expect(shape?.kind).toBe('text') + expect(shape?.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/) + }) +}) diff --git a/src/renderer/src/components/browser-pane/markup/useMarkupPointerHandlers.test.ts b/src/renderer/src/components/browser-pane/markup/useMarkupPointerHandlers.test.ts new file mode 100644 index 00000000000..3792a0f0b7b --- /dev/null +++ b/src/renderer/src/components/browser-pane/markup/useMarkupPointerHandlers.test.ts @@ -0,0 +1,63 @@ +// @vitest-environment happy-dom + +import { act, renderHook } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { useMarkupPointerHandlers, type MarkupPointerParams } from './useMarkupPointerHandlers' +import type { MarkupShape } from './markup-drawing-model' + +const realCrypto = globalThis.crypto + +function pointerDownEvent(clientX: number, clientY: number): React.PointerEvent { + return { + button: 0, + clientX, + clientY, + currentTarget: { setPointerCapture: vi.fn() }, + pointerId: 1 + } as unknown as React.PointerEvent +} + +function baseParams(overrides: Partial = {}): MarkupPointerParams { + return { + busy: false, + tool: 'pen', + color: '#ef4444', + width: 4, + pendingText: null, + canvasRef: { + current: { + getBoundingClientRect: () => ({ left: 0, top: 0 }) + } as unknown as HTMLCanvasElement + }, + setInProgress: vi.fn(), + setPendingText: vi.fn(), + setDoc: vi.fn(), + ...overrides + } +} + +describe('useMarkupPointerHandlers in a non-secure browser context', () => { + beforeEach(() => { + // Match a non-secure browser context (LAN web client over plain HTTP): + // getRandomValues stays, randomUUID is undefined. + Object.defineProperty(globalThis, 'crypto', { + configurable: true, + value: { getRandomValues: realCrypto.getRandomValues.bind(realCrypto) } + }) + }) + + afterEach(() => { + Object.defineProperty(globalThis, 'crypto', { configurable: true, value: realCrypto }) + }) + + it('starts a pen stroke with a valid id instead of throwing', () => { + const setInProgress = vi.fn() + const { result } = renderHook(() => useMarkupPointerHandlers(baseParams({ setInProgress }))) + + expect(() => act(() => result.current.onPointerDown(pointerDownEvent(10, 10)))).not.toThrow() + + expect(setInProgress).toHaveBeenCalledTimes(1) + const shape = setInProgress.mock.calls[0][0] as MarkupShape + expect(shape.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/) + }) +})