Touch: in walk and fly the left thumb drives a virtual joystick while the right thumb looks around; pinch zooms in every view (and climbs when flying); help text switches for coarse pointers. Tour (T or the Tour button) flies the helicopter over the six largest districts with captions of their file and line counts, circling each before moving on, and stops on any input. Copy link encodes the GitHub repository, view, city camera and selected file in the URL fragment. Opening such a link loads the repository (including through GitHub sign-in for private repos) and restores the place. Fragments are validated: only github.com repositories and finite camera values are accepted. Helpers are unit tested.
38 lines
1.9 KiB
JavaScript
38 lines
1.9 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { tourStops, encodePlace, decodePlace, joystick } from '../web/city.js';
|
|
|
|
test('tour visits the largest districts first', () => {
|
|
const districts = [5, 50, 20].map((lines, i) => ({ lines, block: { x: i * 100, y: 0, w: 80, h: 40, node: { name: `d${i}` } } }));
|
|
const stops = tourStops(districts, 2);
|
|
assert.deepEqual(stops.map(stop => stop.district.lines), [50, 20]);
|
|
assert.equal(stops[0].pose.x, 140);
|
|
assert.ok(stops[0].pose.distance >= 120);
|
|
});
|
|
|
|
test('places round-trip through the URL fragment', () => {
|
|
const camera = { view: 'walk', ex: 12.345, ey: 67.891, ez: 1.7, lookYaw: 1.23456, lookPitch: -.1 };
|
|
const hash = encodePlace({ repo: 'https://github.com/prometheus/prometheus', view: 'city', camera, file: 'cmd/prometheus/main.go' });
|
|
const place = decodePlace(hash);
|
|
assert.equal(place.repo, 'https://github.com/prometheus/prometheus');
|
|
assert.equal(place.file, 'cmd/prometheus/main.go');
|
|
assert.deepEqual(place.camera, { view: 'walk', ex: 12.3, ey: 67.9, ez: 1.7, lookYaw: 1.235, lookPitch: -.1 });
|
|
assert.equal(decodePlace(encodePlace({ repo: 'https://github.com/a/b', view: '3d' })).camera, null);
|
|
});
|
|
|
|
test('untrusted fragments are rejected or sanitised', () => {
|
|
assert.equal(decodePlace('#repo=https://evil.example/a/b'), null);
|
|
assert.equal(decodePlace('#repo=javascript:alert(1)'), null);
|
|
const place = decodePlace('#repo=https://github.com/a/b&view=bogus&cam=walk:1,2,NaN,4,5');
|
|
assert.equal(place.view, '2d');
|
|
assert.equal(place.camera, null);
|
|
});
|
|
|
|
test('joystick maps drag to movement with a dead zone and clamp', () => {
|
|
assert.deepEqual(joystick(2, 2), { forward: 0, right: 0 });
|
|
const full = joystick(0, -200);
|
|
assert.ok(Math.abs(full.forward - 1) < 1e-9 && Math.abs(full.right) < 1e-9);
|
|
const half = joystick(28, 0);
|
|
assert.ok(Math.abs(half.right - .5) < 1e-9);
|
|
});
|