mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-01 07:21:33 -07:00
142 lines
4.1 KiB
TypeScript
142 lines
4.1 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
import { applyControllerConfigUpdate } from './controller-config-update.js';
|
|
|
|
test('SM-012 controller config update path does not use JSON serialize-clone helpers', () => {
|
|
const source = fs.readFileSync(
|
|
path.join(process.cwd(), 'src/main/controller-config-update.ts'),
|
|
'utf-8',
|
|
);
|
|
assert.equal(source.includes('JSON.parse(JSON.stringify('), false);
|
|
});
|
|
|
|
test('applyControllerConfigUpdate replaces binding descriptors instead of deep-merging them', () => {
|
|
const next = applyControllerConfigUpdate(
|
|
{
|
|
preferredGamepadId: 'pad-1',
|
|
bindings: {
|
|
toggleLookup: { kind: 'axis', axisIndex: 4, direction: 'positive' },
|
|
closeLookup: { kind: 'button', buttonIndex: 1 },
|
|
},
|
|
},
|
|
{
|
|
bindings: {
|
|
toggleLookup: { kind: 'button', buttonIndex: 11 },
|
|
},
|
|
},
|
|
);
|
|
|
|
assert.deepEqual(next.bindings?.toggleLookup, { kind: 'button', buttonIndex: 11 });
|
|
assert.deepEqual(next.bindings?.closeLookup, { kind: 'button', buttonIndex: 1 });
|
|
});
|
|
|
|
test('applyControllerConfigUpdate merges buttonIndices while replacing only updated binding leaves', () => {
|
|
const next = applyControllerConfigUpdate(
|
|
{
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
},
|
|
bindings: {
|
|
toggleLookup: { kind: 'button', buttonIndex: 0 },
|
|
closeLookup: { kind: 'button', buttonIndex: 1 },
|
|
},
|
|
},
|
|
{
|
|
buttonIndices: {
|
|
buttonSouth: 9,
|
|
},
|
|
bindings: {
|
|
closeLookup: { kind: 'none' },
|
|
},
|
|
},
|
|
);
|
|
|
|
assert.deepEqual(next.buttonIndices, {
|
|
select: 6,
|
|
buttonSouth: 9,
|
|
});
|
|
assert.deepEqual(next.bindings?.toggleLookup, { kind: 'button', buttonIndex: 0 });
|
|
assert.deepEqual(next.bindings?.closeLookup, { kind: 'none' });
|
|
});
|
|
|
|
test('applyControllerConfigUpdate detaches updated binding values from the patch object', () => {
|
|
const update = {
|
|
bindings: {
|
|
toggleLookup: { kind: 'button' as const, buttonIndex: 7 },
|
|
},
|
|
};
|
|
|
|
const next = applyControllerConfigUpdate(undefined, update);
|
|
update.bindings.toggleLookup.buttonIndex = 99;
|
|
|
|
assert.deepEqual(next.bindings?.toggleLookup, { kind: 'button', buttonIndex: 7 });
|
|
});
|
|
|
|
test('applyControllerConfigUpdate merges per-controller profile binding leaves', () => {
|
|
const next = applyControllerConfigUpdate(
|
|
{
|
|
profiles: {
|
|
'pad-1': {
|
|
label: 'Pad 1',
|
|
bindings: {
|
|
toggleLookup: { kind: 'button', buttonIndex: 0 },
|
|
closeLookup: { kind: 'button', buttonIndex: 1 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
profiles: {
|
|
'pad-1': {
|
|
bindings: {
|
|
toggleLookup: { kind: 'button', buttonIndex: 11 },
|
|
},
|
|
},
|
|
'pad-2': {
|
|
label: 'Pad 2',
|
|
bindings: {
|
|
mineCard: { kind: 'button', buttonIndex: 8 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
);
|
|
|
|
assert.deepEqual(next.profiles?.['pad-1']?.bindings?.toggleLookup, {
|
|
kind: 'button',
|
|
buttonIndex: 11,
|
|
});
|
|
assert.deepEqual(next.profiles?.['pad-1']?.bindings?.closeLookup, {
|
|
kind: 'button',
|
|
buttonIndex: 1,
|
|
});
|
|
assert.deepEqual(next.profiles?.['pad-2']?.bindings?.mineCard, {
|
|
kind: 'button',
|
|
buttonIndex: 8,
|
|
});
|
|
});
|
|
|
|
test('applyControllerConfigUpdate ignores reserved profile ids', () => {
|
|
const reservedProfiles = Object.create(null) as NonNullable<
|
|
Parameters<typeof applyControllerConfigUpdate>[1]['profiles']
|
|
>;
|
|
reservedProfiles.__proto__ = { label: 'polluted' };
|
|
reservedProfiles['constructor'] = { label: 'ctor' };
|
|
reservedProfiles['prototype'] = { label: 'proto' };
|
|
reservedProfiles['pad-1'] = { label: 'Pad 1' };
|
|
|
|
const next = applyControllerConfigUpdate(undefined, {
|
|
profiles: reservedProfiles,
|
|
});
|
|
|
|
assert.equal(Object.getPrototypeOf(next.profiles), Object.prototype);
|
|
assert.equal(Object.hasOwn(next.profiles ?? {}, '__proto__'), false);
|
|
assert.equal(Object.hasOwn(next.profiles ?? {}, 'constructor'), false);
|
|
assert.equal(Object.hasOwn(next.profiles ?? {}, 'prototype'), false);
|
|
assert.equal(next.profiles?.['pad-1']?.label, 'Pad 1');
|
|
});
|