SOURCE / PINNED RELEASE
Made of little things.
Pursuit PIT
- Release
- 565f4981bdc3…
- Author-recorded commit
- 197afdbed44b…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-951414e7fd7
Archive hash verified: 3b0485589942fd5c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import test from 'node:test';
import ts from 'typescript';
// Compile the two dependency-free input modules in memory; no browser globals or hardware.
const compile = async (path) => ts.transpileModule(await readFile(new URL(path, import.meta.url), 'utf8'), {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
}).outputText;
const url = (source) => `data:text/javascript;base64,${Buffer.from(source).toString('base64')}`;
const helper = url(await compile('../docs/examples/gamepad.ts'));
const source = (await compile('../src/controller-input.ts')).replace('../docs/examples/gamepad.js', helper);
const { createControllerInput, mixDriving } = await import(url(source));
function fixture(index = 2) {
let connected = true, active = true;
const pad = { index, id: 'test', connected: true, mapping: 'standard', axes: [0, 0],
buttons: Array.from({ length: 16 }, () => ({ value: 0 })) };
let pads = [pad];
const input = createControllerInput({ read: () => connected ? pads : [], active: () => active });
let now = 0;
const poll = (ms = 16) => input.poll(now += ms);
const button = (index, value) => { pad.buttons[index].value = value; };
const join = () => {
poll(); button(0, 1);
assert.equal(poll().joined, true);
button(0, 0); poll();
};
return { input, pad, poll, button, join,
disconnect: () => { connected = false; }, reconnect: () => { connected = true; },
active: (value) => { active = value; }, pads: (value) => { pads = value; } };
}
test('join consumes confirm; connection held buttons do not join automatically', () => {
const f = fixture(); f.button(0, 1);
assert.equal(f.poll().joined, false);
f.button(0, 0); f.poll(); f.button(0, 1);
const joined = f.poll();
assert.equal(joined.joined, true);
assert.equal(joined.pressed.size, 0);
assert.equal(f.poll().pressed.size, 0);
f.input.dispose();
});
test('stick drift is neutral, steering is proportional, triggers retain range', () => {
const f = fixture(); f.join();
f.pad.axes[0] = 0.17; f.button(7, 0.3); f.poll();
assert.deepEqual(f.input.driving, { gas: 0.3, brake: 0, steer: 0 });
f.pad.axes[0] = 0.59; f.button(6, 0.2); f.poll();
assert.ok(Math.abs(f.input.driving.steer - 0.5) < 1e-8);
assert.equal(f.input.driving.brake, 0.2);
f.input.dispose();
});
test('mixed sources preserve analog values and cancel equal opposing steering', () => {
assert.deepEqual(mixDriving(new Set(), { gas: 0.6, brake: 0.2, steer: -0.4 }), { throttle: 0.39999999999999997, steer: -0.4 });
assert.deepEqual(mixDriving(new Set(['right', 'gas']), { gas: 0.6, brake: 0.25, steer: -1 }), { throttle: 0.75, steer: 0 });
assert.deepEqual(mixDriving(new Set(['left']), { gas: 0, brake: 0, steer: 0.5 }), { throttle: 0, steer: -1 });
});
test('button edges fire once across polls; continuous driving can feed many physics steps', () => {
const f = fixture(); f.join(); f.button(7, 0.7); f.button(3, 1);
assert.equal(f.poll().pressed.has('radio'), true);
const snapshot = f.input.driving;
for (let step = 0; step < 12; step++) assert.deepEqual(f.input.driving, snapshot);
assert.equal(f.poll().pressed.has('radio'), false);
f.button(3, 0); f.poll(); f.button(3, 1);
assert.equal(f.poll().pressed.has('radio'), true);
f.input.dispose();
});
test('transition reset suppresses held confirm, navigation and driving until neutral', () => {
const f = fixture(); f.join(); f.button(0, 1); f.button(7, 0.8); f.pad.axes[0] = 1; f.poll();
f.input.reset();
assert.deepEqual(f.input.driving, { gas: 0, brake: 0, steer: 0 });
const held = f.poll(); assert.equal(held.pressed.size, 0); assert.equal(held.navigation, null);
assert.equal(f.input.driving.gas, 0);
f.button(0, 0); f.button(7, 0); f.pad.axes[0] = 0; f.poll();
f.button(0, 1); f.button(7, 0.8); f.pad.axes[0] = 1;
assert.equal(f.poll().pressed.has('confirm'), true);
assert.equal(f.input.driving.gas, 0.8);
f.input.dispose();
});
test('navigation threshold, initial delay, repeat and dominant direction', () => {
const f = fixture(); f.join();
f.pad.axes = [0.4, 0]; assert.equal(f.poll().navigation, null);
f.pad.axes = [1, 0.8]; assert.equal(f.poll().navigation, 'right');
assert.equal(f.poll(349).navigation, null);
assert.equal(f.poll(1).navigation, 'right');
assert.equal(f.poll(119).navigation, null);
assert.equal(f.poll(1).navigation, 'right');
f.pad.axes = [-1, 0]; assert.equal(f.poll().navigation, 'left');
f.input.dispose();
});
test('focus loss neutralizes input; held controls do not produce return edges', () => {
const f = fixture(); f.join(); f.button(7, 1); f.button(9, 1); f.poll();
f.active(false); assert.equal(f.poll().pressed.size, 0);
assert.equal(f.input.driving.gas, 0);
f.active(true); assert.equal(f.poll().pressed.size, 0);
assert.equal(f.input.driving.gas, 0);
f.button(7, 0); f.poll(); f.button(7, 1); f.poll();
assert.equal(f.input.driving.gas, 1);
f.input.dispose();
});
test('disconnect and same-slot replacement lose ownership and require a new press', () => {
const f = fixture(); f.join(); f.button(7, 1); f.poll();
f.disconnect(); assert.equal(f.poll().lost, true); assert.equal(f.input.driving.gas, 0);
f.reconnect(); assert.equal(f.poll().joined, false);
f.button(7, 0); f.poll(); f.button(0, 1); assert.equal(f.poll().joined, true);
f.pad.id = 'replacement'; assert.equal(f.poll().lost, true);
assert.equal(f.poll().joined, false);
f.input.dispose();
});
test('sparse second slot cannot drive or pause the active player', () => {
const f = fixture(3); f.join();
const other = { ...f.pad, index: 7, id: 'other', axes: [-1, 0], buttons: Array.from({ length: 16 }, () => ({ value: 0 })) };
f.pads([other, f.pad]); f.poll(); other.buttons[9].value = 1; other.buttons[7].value = 1;
assert.equal(f.poll().pressed.has('pause'), false);
assert.equal(f.input.driving.gas, 0);
f.input.dispose();
});
test('nonstandard mapping remains unmapped; API absence/denial are distinguished', () => {
const f = fixture(); f.pad.mapping = ''; f.poll(); f.button(0, 1);
const frame = f.poll(); assert.equal(frame.joined, false); assert.match(frame.status, /unsupported/);
f.input.dispose();
for (const [name, message] of [['Error', /unavailable/], ['SecurityError', /blocked/]]) {
const input = createControllerInput({ read: () => { const error = new Error(); error.name = name; throw error; }, active: () => true });
assert.match(input.poll(0).status, message); input.dispose();
}
});
