Spaces:
Sleeping
Sleeping
File size: 3,111 Bytes
5e442e4 df81806 aaa664a ec03da9 5e442e4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import { describe, it, expect, beforeEach } from 'vitest';
import * as THREE from 'three';
import { stickToAxes, isUiTouchTarget } from '../src/ui/TouchControls.js';
import { Navigation } from '../src/engine/Navigation.js';
describe('stickToAxes', () => {
it('maps stick-up to forward (−z) and right to +x', () => {
const up = stickToAxes(0, -48, 48);
expect(up.z).toBeCloseTo(1, 5);
expect(Math.abs(up.x)).toBeLessThan(0.01);
const right = stickToAxes(48, 0, 48);
expect(right.x).toBeCloseTo(1, 5);
expect(Math.abs(right.z)).toBeLessThan(0.01);
});
it('clamps magnitude to unit circle', () => {
const a = stickToAxes(100, 100, 48);
expect(Math.hypot(a.x, a.z)).toBeCloseTo(1, 5);
});
});
describe('isUiTouchTarget', () => {
it('treats dock/hud ancestors as UI', () => {
const dock = { id: 'left-dock', tagName: 'DIV', closest: (sel) => (sel.includes('#left-dock') ? dock : null) };
const child = { tagName: 'BUTTON', closest: (sel) => dock.closest(sel) };
expect(isUiTouchTarget(child)).toBe(true);
});
it('treats Visualization sheet as UI', () => {
const sheet = {
id: 'visualization-controls-container',
tagName: 'DIV',
closest: (sel) => (sel.includes('#visualization-controls-container') ? sheet : null),
};
const child = { tagName: 'BUTTON', closest: (sel) => sheet.closest(sel) };
expect(isUiTouchTarget(child)).toBe(true);
});
it('treats field-info tip as UI', () => {
const tip = {
className: 'field-info-tip',
tagName: 'DIV',
closest: (sel) => (sel.includes('.field-info-tip') ? tip : null),
};
expect(isUiTouchTarget(tip)).toBe(true);
});
it('treats boot/Galaxy progress overlay as UI', () => {
const overlay = {
id: 'boot-progress',
tagName: 'DIV',
closest: (sel) => (sel.includes('#boot-progress') ? overlay : null),
};
expect(isUiTouchTarget(overlay)).toBe(true);
});
it('allows bare canvas', () => {
const canvas = { tagName: 'CANVAS', closest: () => null };
expect(isUiTouchTarget(canvas)).toBe(false);
});
});
describe('Navigation touch API', () => {
let nav;
let camera;
beforeEach(() => {
camera = new THREE.PerspectiveCamera(60, 1, 0.1, 2000);
nav = new Navigation(camera, null);
});
it('setMoveAxes and setVertical feed update without breaking desktop keys', () => {
nav.setMoveAxes(0, -1);
nav.setVertical(1);
expect(nav.touchAxes.z).toBeCloseTo(-1);
expect(nav.touchVertical).toBe(1);
const before = camera.position.clone();
nav.update(0.016);
expect(camera.position.distanceTo(before)).toBeGreaterThan(0);
nav.setMoveAxes(0, 0);
nav.setVertical(0);
expect(nav.touchAxes.x).toBe(0);
expect(nav.touchVertical).toBe(0);
});
it('applyLookDelta updates euler pitch/yaw clamps', () => {
nav.applyLookDelta(100, 0);
expect(nav.euler.y).toBeLessThan(0);
nav.applyLookDelta(0, 10000);
expect(nav.euler.x).toBeGreaterThanOrEqual(-Math.PI / 2 + 0.05);
expect(nav.euler.x).toBeLessThanOrEqual(Math.PI / 2 - 0.05);
});
});
|