managarten/uload/apps/web/src/lib/actions/touch.test.ts
Till-JS c712a2504a feat: integrate uload and picture, unify package naming
- Add uload project with apps/web structure
  - Reorganize from flat to monorepo structure
  - Remove PocketBase binary and local data
  - Update to pnpm and @uload/web namespace

- Add picture project to monorepo
  - Remove embedded git repository

- Unify all package names to @{project}/{app} schema:
  - @maerchenzauber/* (was @storyteller/*)
  - @manacore/* (was manacore-*, manacore)
  - @manadeck/* (was web, backend, manadeck)
  - @memoro/* (was memoro-web, landing, memoro)
  - @picture/* (already unified)
  - @uload/web

- Add convenient dev scripts for all apps:
  - pnpm dev:{project}:web
  - pnpm dev:{project}:landing
  - pnpm dev:{project}:mobile
  - pnpm dev:{project}:backend

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 04:00:36 +01:00

203 lines
No EOL
6.2 KiB
TypeScript

import { describe, test, expect, beforeEach, vi } from 'vitest';
import { isTouchDevice, isOptimalTouchTarget } from './touch';
// Mock DOM APIs für Tests
const mockEventListener = vi.fn();
const mockRemoveEventListener = vi.fn();
const createMockElement = (width = 44, height = 44) => ({
addEventListener: mockEventListener,
removeEventListener: mockRemoveEventListener,
getBoundingClientRect: () => ({ width, height, top: 0, left: 0, right: width, bottom: height }),
style: {},
appendChild: vi.fn(),
remove: vi.fn()
});
// Mock global objects
Object.defineProperty(window, 'navigator', {
value: {
maxTouchPoints: 0,
userAgent: 'Mozilla/5.0 (Test Browser)'
},
writable: true
});
describe('Touch Utilities', () => {
beforeEach(() => {
vi.clearAllMocks();
// Reset touch support
delete (window as any).ontouchstart;
(window.navigator as any).maxTouchPoints = 0;
});
describe('isTouchDevice', () => {
test('should detect touch support via ontouchstart', () => {
(window as any).ontouchstart = true;
expect(isTouchDevice()).toBe(true);
});
test('should detect touch support via maxTouchPoints', () => {
(window.navigator as any).maxTouchPoints = 1;
expect(isTouchDevice()).toBe(true);
});
test('should return false for non-touch devices', () => {
expect(isTouchDevice()).toBe(false);
});
});
describe('isOptimalTouchTarget', () => {
test('should return true for 44x44 elements', () => {
const element = createMockElement(44, 44);
expect(isOptimalTouchTarget(element as any)).toBe(true);
});
test('should return true for larger elements', () => {
const element = createMockElement(50, 60);
expect(isOptimalTouchTarget(element as any)).toBe(true);
});
test('should return false for small width', () => {
const element = createMockElement(30, 44);
expect(isOptimalTouchTarget(element as any)).toBe(false);
});
test('should return false for small height', () => {
const element = createMockElement(44, 30);
expect(isOptimalTouchTarget(element as any)).toBe(false);
});
test('should return false for small elements', () => {
const element = createMockElement(20, 20);
expect(isOptimalTouchTarget(element as any)).toBe(false);
});
});
});
describe('Touch Actions (Integration)', () => {
let mockElement: any;
beforeEach(() => {
mockElement = createMockElement();
vi.clearAllMocks();
});
describe('Event Registration', () => {
test('should register touch and pointer events', () => {
// Diese Tests würden die tatsächlichen Touch-Actions testen
// Für jetzt testen wir nur die Utility-Funktionen
expect(mockEventListener).not.toHaveBeenCalled();
});
});
describe('Gesture Recognition', () => {
test('should calculate touch distances correctly', () => {
const touch1 = { clientX: 0, clientY: 0 };
const touch2 = { clientX: 100, clientY: 100 };
// Math.sqrt(100^2 + 100^2) = Math.sqrt(20000) ≈ 141.42
const expectedDistance = Math.sqrt(20000);
const actualDistance = Math.sqrt(
Math.pow(touch2.clientX - touch1.clientX, 2) +
Math.pow(touch2.clientY - touch1.clientY, 2)
);
expect(actualDistance).toBeCloseTo(expectedDistance, 2);
});
test('should detect horizontal swipes', () => {
const startTouch = { clientX: 0, clientY: 100 };
const endTouch = { clientX: 100, clientY: 100 };
const deltaX = endTouch.clientX - startTouch.clientX;
const deltaY = endTouch.clientY - startTouch.clientY;
const absDeltaX = Math.abs(deltaX);
const absDeltaY = Math.abs(deltaY);
// Horizontal swipe: |deltaX| > |deltaY|
expect(absDeltaX).toBeGreaterThan(absDeltaY);
expect(deltaX).toBeGreaterThan(0); // Right swipe
});
test('should detect vertical swipes', () => {
const startTouch = { clientX: 100, clientY: 0 };
const endTouch = { clientX: 100, clientY: 100 };
const deltaX = endTouch.clientX - startTouch.clientX;
const deltaY = endTouch.clientY - startTouch.clientY;
const absDeltaX = Math.abs(deltaX);
const absDeltaY = Math.abs(deltaY);
// Vertical swipe: |deltaY| > |deltaX|
expect(absDeltaY).toBeGreaterThan(absDeltaX);
expect(deltaY).toBeGreaterThan(0); // Down swipe
});
});
describe('Touch Target Validation', () => {
test('should validate minimum touch target sizes', () => {
const sizes = [
{ width: 44, height: 44, expected: true },
{ width: 48, height: 48, expected: true },
{ width: 40, height: 40, expected: false },
{ width: 44, height: 40, expected: false },
{ width: 40, height: 44, expected: false }
];
sizes.forEach(({ width, height, expected }) => {
const element = createMockElement(width, height);
expect(isOptimalTouchTarget(element as any)).toBe(expected);
});
});
});
describe('Performance Considerations', () => {
test('should handle rapid touch events', () => {
// Simuliere viele schnelle Touch-Events
const events = Array.from({ length: 100 }, (_, i) => ({
clientX: i,
clientY: i,
timestamp: Date.now() + i
}));
// In einer echten Implementation würden wir Throttling/Debouncing testen
expect(events).toHaveLength(100);
// Teste dass Events innerhalb vernünftiger Zeit verarbeitet werden können
const startTime = Date.now();
events.forEach(event => {
// Simuliere Event-Verarbeitung
const deltaX = event.clientX;
const deltaY = event.clientY;
Math.sqrt(deltaX * deltaX + deltaY * deltaY);
});
const endTime = Date.now();
expect(endTime - startTime).toBeLessThan(100); // Sollte sehr schnell sein
});
});
describe('Accessibility Considerations', () => {
test('should maintain focus accessibility', () => {
// Touch-Actions sollten Keyboard-Navigation nicht beeinträchtigen
const element = createMockElement();
// Simuliere dass Element fokussierbar bleibt
element.tabIndex = 0;
element.setAttribute = vi.fn();
expect(element.tabIndex).toBe(0);
});
test('should work with screen readers', () => {
// Touch-Targets sollten Screen-Reader-kompatibel bleiben
const element = createMockElement();
element.getAttribute = vi.fn().mockReturnValue('button');
element.textContent = 'Touch Button';
expect(element.getAttribute('role')).toBe('button');
expect(element.textContent).toBe('Touch Button');
});
});
});