mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 03:16:46 -07:00
728 lines
22 KiB
TypeScript
728 lines
22 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { createRendererState } from '../state.js';
|
|
import { createControllerSelectModal } from './controller-select.js';
|
|
|
|
function createClassList(initialTokens: string[] = []) {
|
|
const tokens = new Set(initialTokens);
|
|
return {
|
|
add: (...entries: string[]) => {
|
|
for (const entry of entries) tokens.add(entry);
|
|
},
|
|
remove: (...entries: string[]) => {
|
|
for (const entry of entries) tokens.delete(entry);
|
|
},
|
|
toggle: (entry: string, force?: boolean) => {
|
|
if (force === undefined) {
|
|
if (tokens.has(entry)) tokens.delete(entry);
|
|
else tokens.add(entry);
|
|
return tokens.has(entry);
|
|
}
|
|
if (force) tokens.add(entry);
|
|
else tokens.delete(entry);
|
|
return force;
|
|
},
|
|
contains: (entry: string) => tokens.has(entry),
|
|
};
|
|
}
|
|
|
|
test('controller select modal saves the selected preferred controller', async () => {
|
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
|
const previousWindow = globals.window;
|
|
const previousDocument = globals.document;
|
|
const saved: Array<{ preferredGamepadId: string; preferredGamepadLabel: string }> = [];
|
|
|
|
Object.defineProperty(globalThis, 'window', {
|
|
configurable: true,
|
|
value: {
|
|
focus: () => {},
|
|
electronAPI: {
|
|
saveControllerPreference: async (update: {
|
|
preferredGamepadId: string;
|
|
preferredGamepadLabel: string;
|
|
}) => {
|
|
saved.push(update);
|
|
},
|
|
notifyOverlayModalClosed: () => {},
|
|
},
|
|
},
|
|
});
|
|
Object.defineProperty(globalThis, 'document', {
|
|
configurable: true,
|
|
value: {
|
|
createElement: () => ({
|
|
className: '',
|
|
textContent: '',
|
|
classList: createClassList(),
|
|
appendChild: () => {},
|
|
addEventListener: () => {},
|
|
}),
|
|
},
|
|
});
|
|
|
|
try {
|
|
const overlayClassList = createClassList();
|
|
const state = createRendererState();
|
|
state.controllerConfig = {
|
|
enabled: true,
|
|
preferredGamepadId: 'pad-2',
|
|
preferredGamepadLabel: 'pad-2',
|
|
smoothScroll: true,
|
|
scrollPixelsPerSecond: 960,
|
|
horizontalJumpPixels: 160,
|
|
stickDeadzone: 0.2,
|
|
triggerInputMode: 'auto',
|
|
triggerDeadzone: 0.5,
|
|
repeatDelayMs: 220,
|
|
repeatIntervalMs: 80,
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
buttonEast: 1,
|
|
buttonWest: 2,
|
|
buttonNorth: 3,
|
|
leftShoulder: 4,
|
|
rightShoulder: 5,
|
|
leftStickPress: 9,
|
|
rightStickPress: 10,
|
|
leftTrigger: 6,
|
|
rightTrigger: 7,
|
|
},
|
|
bindings: {
|
|
toggleLookup: 'buttonSouth',
|
|
closeLookup: 'buttonEast',
|
|
toggleKeyboardOnlyMode: 'buttonNorth',
|
|
mineCard: 'buttonWest',
|
|
quitMpv: 'select',
|
|
previousAudio: 'leftShoulder',
|
|
nextAudio: 'rightShoulder',
|
|
playCurrentAudio: 'rightTrigger',
|
|
toggleMpvPause: 'leftTrigger',
|
|
leftStickHorizontal: 'leftStickX',
|
|
leftStickVertical: 'leftStickY',
|
|
rightStickHorizontal: 'rightStickX',
|
|
rightStickVertical: 'rightStickY',
|
|
},
|
|
};
|
|
state.connectedGamepads = [
|
|
{ id: 'pad-1', index: 0, mapping: 'standard', connected: true },
|
|
{ id: 'pad-2', index: 1, mapping: 'standard', connected: true },
|
|
];
|
|
state.activeGamepadId = 'pad-2';
|
|
|
|
const ctx = {
|
|
dom: {
|
|
overlay: { classList: overlayClassList, focus: () => {} },
|
|
controllerSelectModal: {
|
|
classList: createClassList(['hidden']),
|
|
setAttribute: () => {},
|
|
},
|
|
controllerSelectClose: { addEventListener: () => {} },
|
|
controllerSelectHint: { textContent: '' },
|
|
controllerSelectStatus: { textContent: '', classList: createClassList() },
|
|
controllerSelectList: {
|
|
innerHTML: '',
|
|
appendChild: () => {},
|
|
},
|
|
controllerSelectSave: { addEventListener: () => {} },
|
|
},
|
|
state,
|
|
};
|
|
|
|
const modal = createControllerSelectModal(ctx as never, {
|
|
modalStateReader: { isAnyModalOpen: () => false },
|
|
syncSettingsModalSubtitleSuppression: () => {},
|
|
});
|
|
|
|
modal.openControllerSelectModal();
|
|
assert.equal(state.controllerDeviceSelectedIndex, 1);
|
|
|
|
await modal.handleControllerSelectKeydown({
|
|
key: 'Enter',
|
|
preventDefault: () => {},
|
|
} as KeyboardEvent);
|
|
|
|
assert.deepEqual(saved, [
|
|
{
|
|
preferredGamepadId: 'pad-2',
|
|
preferredGamepadLabel: 'pad-2',
|
|
},
|
|
]);
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
|
}
|
|
});
|
|
|
|
test('controller select modal preserves manual selection while controller polling updates', async () => {
|
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
|
const previousWindow = globals.window;
|
|
const previousDocument = globals.document;
|
|
|
|
Object.defineProperty(globalThis, 'window', {
|
|
configurable: true,
|
|
value: {
|
|
focus: () => {},
|
|
electronAPI: {
|
|
saveControllerPreference: async () => {},
|
|
notifyOverlayModalClosed: () => {},
|
|
},
|
|
},
|
|
});
|
|
Object.defineProperty(globalThis, 'document', {
|
|
configurable: true,
|
|
value: {
|
|
createElement: () => ({
|
|
className: '',
|
|
textContent: '',
|
|
classList: createClassList(),
|
|
appendChild: () => {},
|
|
addEventListener: () => {},
|
|
}),
|
|
},
|
|
});
|
|
|
|
try {
|
|
const state = createRendererState();
|
|
state.controllerConfig = {
|
|
enabled: true,
|
|
preferredGamepadId: 'pad-1',
|
|
preferredGamepadLabel: 'pad-1',
|
|
smoothScroll: true,
|
|
scrollPixelsPerSecond: 960,
|
|
horizontalJumpPixels: 160,
|
|
stickDeadzone: 0.2,
|
|
triggerInputMode: 'auto',
|
|
triggerDeadzone: 0.5,
|
|
repeatDelayMs: 220,
|
|
repeatIntervalMs: 80,
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
buttonEast: 1,
|
|
buttonWest: 2,
|
|
buttonNorth: 3,
|
|
leftShoulder: 4,
|
|
rightShoulder: 5,
|
|
leftStickPress: 9,
|
|
rightStickPress: 10,
|
|
leftTrigger: 6,
|
|
rightTrigger: 7,
|
|
},
|
|
bindings: {
|
|
toggleLookup: 'buttonSouth',
|
|
closeLookup: 'buttonEast',
|
|
toggleKeyboardOnlyMode: 'buttonNorth',
|
|
mineCard: 'buttonWest',
|
|
quitMpv: 'select',
|
|
previousAudio: 'none',
|
|
nextAudio: 'rightShoulder',
|
|
playCurrentAudio: 'leftShoulder',
|
|
toggleMpvPause: 'leftStickPress',
|
|
leftStickHorizontal: 'leftStickX',
|
|
leftStickVertical: 'leftStickY',
|
|
rightStickHorizontal: 'rightStickX',
|
|
rightStickVertical: 'rightStickY',
|
|
},
|
|
};
|
|
state.connectedGamepads = [
|
|
{ id: 'pad-1', index: 0, mapping: 'standard', connected: true },
|
|
{ id: 'pad-2', index: 1, mapping: 'standard', connected: true },
|
|
];
|
|
state.activeGamepadId = 'pad-1';
|
|
|
|
const ctx = {
|
|
dom: {
|
|
overlay: { classList: createClassList(), focus: () => {} },
|
|
controllerSelectModal: {
|
|
classList: createClassList(['hidden']),
|
|
setAttribute: () => {},
|
|
},
|
|
controllerSelectClose: { addEventListener: () => {} },
|
|
controllerSelectHint: { textContent: '' },
|
|
controllerSelectStatus: { textContent: '', classList: createClassList() },
|
|
controllerSelectList: {
|
|
innerHTML: '',
|
|
appendChild: () => {},
|
|
},
|
|
controllerSelectSave: { addEventListener: () => {} },
|
|
},
|
|
state,
|
|
};
|
|
|
|
const modal = createControllerSelectModal(ctx as never, {
|
|
modalStateReader: { isAnyModalOpen: () => false },
|
|
syncSettingsModalSubtitleSuppression: () => {},
|
|
});
|
|
|
|
modal.openControllerSelectModal();
|
|
assert.equal(state.controllerDeviceSelectedIndex, 0);
|
|
|
|
modal.handleControllerSelectKeydown({
|
|
key: 'ArrowDown',
|
|
preventDefault: () => {},
|
|
} as KeyboardEvent);
|
|
assert.equal(state.controllerDeviceSelectedIndex, 1);
|
|
|
|
modal.updateDevices();
|
|
|
|
assert.equal(state.controllerDeviceSelectedIndex, 1);
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
|
}
|
|
});
|
|
|
|
test('controller select modal prefers active controller over saved preferred controller', () => {
|
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
|
const previousWindow = globals.window;
|
|
const previousDocument = globals.document;
|
|
|
|
Object.defineProperty(globalThis, 'window', {
|
|
configurable: true,
|
|
value: {
|
|
focus: () => {},
|
|
electronAPI: {
|
|
saveControllerPreference: async () => {},
|
|
notifyOverlayModalClosed: () => {},
|
|
},
|
|
},
|
|
});
|
|
Object.defineProperty(globalThis, 'document', {
|
|
configurable: true,
|
|
value: {
|
|
createElement: () => ({
|
|
className: '',
|
|
textContent: '',
|
|
classList: createClassList(),
|
|
appendChild: () => {},
|
|
addEventListener: () => {},
|
|
}),
|
|
},
|
|
});
|
|
|
|
try {
|
|
const state = createRendererState();
|
|
state.controllerConfig = {
|
|
enabled: true,
|
|
preferredGamepadId: 'pad-1',
|
|
preferredGamepadLabel: 'pad-1',
|
|
smoothScroll: true,
|
|
scrollPixelsPerSecond: 960,
|
|
horizontalJumpPixels: 160,
|
|
stickDeadzone: 0.2,
|
|
triggerInputMode: 'auto',
|
|
triggerDeadzone: 0.5,
|
|
repeatDelayMs: 220,
|
|
repeatIntervalMs: 80,
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
buttonEast: 1,
|
|
buttonWest: 2,
|
|
buttonNorth: 3,
|
|
leftShoulder: 4,
|
|
rightShoulder: 5,
|
|
leftStickPress: 9,
|
|
rightStickPress: 10,
|
|
leftTrigger: 6,
|
|
rightTrigger: 7,
|
|
},
|
|
bindings: {
|
|
toggleLookup: 'buttonSouth',
|
|
closeLookup: 'buttonEast',
|
|
toggleKeyboardOnlyMode: 'buttonNorth',
|
|
mineCard: 'buttonWest',
|
|
quitMpv: 'select',
|
|
previousAudio: 'none',
|
|
nextAudio: 'rightShoulder',
|
|
playCurrentAudio: 'leftShoulder',
|
|
toggleMpvPause: 'leftStickPress',
|
|
leftStickHorizontal: 'leftStickX',
|
|
leftStickVertical: 'leftStickY',
|
|
rightStickHorizontal: 'rightStickX',
|
|
rightStickVertical: 'rightStickY',
|
|
},
|
|
};
|
|
state.connectedGamepads = [
|
|
{ id: 'pad-1', index: 0, mapping: 'standard', connected: true },
|
|
{ id: 'pad-2', index: 1, mapping: 'standard', connected: true },
|
|
];
|
|
state.activeGamepadId = 'pad-2';
|
|
|
|
const ctx = {
|
|
dom: {
|
|
overlay: { classList: createClassList(), focus: () => {} },
|
|
controllerSelectModal: {
|
|
classList: createClassList(['hidden']),
|
|
setAttribute: () => {},
|
|
},
|
|
controllerSelectClose: { addEventListener: () => {} },
|
|
controllerSelectHint: { textContent: '' },
|
|
controllerSelectStatus: { textContent: '', classList: createClassList() },
|
|
controllerSelectList: {
|
|
innerHTML: '',
|
|
appendChild: () => {},
|
|
},
|
|
controllerSelectSave: { addEventListener: () => {} },
|
|
},
|
|
state,
|
|
};
|
|
|
|
const modal = createControllerSelectModal(ctx as never, {
|
|
modalStateReader: { isAnyModalOpen: () => false },
|
|
syncSettingsModalSubtitleSuppression: () => {},
|
|
});
|
|
|
|
modal.openControllerSelectModal();
|
|
|
|
assert.equal(state.controllerDeviceSelectedIndex, 1);
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
|
}
|
|
});
|
|
|
|
test('controller select modal preserves saved status across polling updates', async () => {
|
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
|
const previousWindow = globals.window;
|
|
const previousDocument = globals.document;
|
|
|
|
Object.defineProperty(globalThis, 'window', {
|
|
configurable: true,
|
|
value: {
|
|
focus: () => {},
|
|
electronAPI: {
|
|
saveControllerPreference: async () => {},
|
|
notifyOverlayModalClosed: () => {},
|
|
},
|
|
},
|
|
});
|
|
Object.defineProperty(globalThis, 'document', {
|
|
configurable: true,
|
|
value: {
|
|
createElement: () => ({
|
|
className: '',
|
|
textContent: '',
|
|
classList: createClassList(),
|
|
appendChild: () => {},
|
|
addEventListener: () => {},
|
|
}),
|
|
},
|
|
});
|
|
|
|
try {
|
|
const state = createRendererState();
|
|
state.controllerConfig = {
|
|
enabled: true,
|
|
preferredGamepadId: 'pad-1',
|
|
preferredGamepadLabel: 'pad-1',
|
|
smoothScroll: true,
|
|
scrollPixelsPerSecond: 960,
|
|
horizontalJumpPixels: 160,
|
|
stickDeadzone: 0.2,
|
|
triggerInputMode: 'auto',
|
|
triggerDeadzone: 0.5,
|
|
repeatDelayMs: 220,
|
|
repeatIntervalMs: 80,
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
buttonEast: 1,
|
|
buttonWest: 2,
|
|
buttonNorth: 3,
|
|
leftShoulder: 4,
|
|
rightShoulder: 5,
|
|
leftStickPress: 9,
|
|
rightStickPress: 10,
|
|
leftTrigger: 6,
|
|
rightTrigger: 7,
|
|
},
|
|
bindings: {
|
|
toggleLookup: 'buttonSouth',
|
|
closeLookup: 'buttonEast',
|
|
toggleKeyboardOnlyMode: 'buttonNorth',
|
|
mineCard: 'buttonWest',
|
|
quitMpv: 'select',
|
|
previousAudio: 'none',
|
|
nextAudio: 'rightShoulder',
|
|
playCurrentAudio: 'leftShoulder',
|
|
toggleMpvPause: 'leftStickPress',
|
|
leftStickHorizontal: 'leftStickX',
|
|
leftStickVertical: 'leftStickY',
|
|
rightStickHorizontal: 'rightStickX',
|
|
rightStickVertical: 'rightStickY',
|
|
},
|
|
};
|
|
state.connectedGamepads = [{ id: 'pad-1', index: 0, mapping: 'standard', connected: true }];
|
|
state.activeGamepadId = 'pad-1';
|
|
|
|
const ctx = {
|
|
dom: {
|
|
overlay: { classList: createClassList(), focus: () => {} },
|
|
controllerSelectModal: {
|
|
classList: createClassList(['hidden']),
|
|
setAttribute: () => {},
|
|
},
|
|
controllerSelectClose: { addEventListener: () => {} },
|
|
controllerSelectHint: { textContent: '' },
|
|
controllerSelectStatus: { textContent: '', classList: createClassList() },
|
|
controllerSelectList: {
|
|
innerHTML: '',
|
|
appendChild: () => {},
|
|
},
|
|
controllerSelectSave: { addEventListener: () => {} },
|
|
},
|
|
state,
|
|
};
|
|
|
|
const modal = createControllerSelectModal(ctx as never, {
|
|
modalStateReader: { isAnyModalOpen: () => false },
|
|
syncSettingsModalSubtitleSuppression: () => {},
|
|
});
|
|
|
|
modal.openControllerSelectModal();
|
|
await modal.handleControllerSelectKeydown({
|
|
key: 'Enter',
|
|
preventDefault: () => {},
|
|
} as KeyboardEvent);
|
|
modal.updateDevices();
|
|
|
|
assert.match(ctx.dom.controllerSelectStatus.textContent, /Saved preferred controller/);
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
|
}
|
|
});
|
|
|
|
test('controller select modal surfaces save errors without mutating saved preference', async () => {
|
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
|
const previousWindow = globals.window;
|
|
const previousDocument = globals.document;
|
|
|
|
Object.defineProperty(globalThis, 'window', {
|
|
configurable: true,
|
|
value: {
|
|
focus: () => {},
|
|
electronAPI: {
|
|
saveControllerPreference: async () => {
|
|
throw new Error('disk write failed');
|
|
},
|
|
notifyOverlayModalClosed: () => {},
|
|
},
|
|
},
|
|
});
|
|
Object.defineProperty(globalThis, 'document', {
|
|
configurable: true,
|
|
value: {
|
|
createElement: () => ({
|
|
className: '',
|
|
textContent: '',
|
|
classList: createClassList(),
|
|
appendChild: () => {},
|
|
addEventListener: () => {},
|
|
}),
|
|
},
|
|
});
|
|
|
|
try {
|
|
const state = createRendererState();
|
|
state.controllerConfig = {
|
|
enabled: true,
|
|
preferredGamepadId: 'pad-1',
|
|
preferredGamepadLabel: 'pad-1',
|
|
smoothScroll: true,
|
|
scrollPixelsPerSecond: 960,
|
|
horizontalJumpPixels: 160,
|
|
stickDeadzone: 0.2,
|
|
triggerInputMode: 'auto',
|
|
triggerDeadzone: 0.5,
|
|
repeatDelayMs: 220,
|
|
repeatIntervalMs: 80,
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
buttonEast: 1,
|
|
buttonWest: 2,
|
|
buttonNorth: 3,
|
|
leftShoulder: 4,
|
|
rightShoulder: 5,
|
|
leftStickPress: 9,
|
|
rightStickPress: 10,
|
|
leftTrigger: 6,
|
|
rightTrigger: 7,
|
|
},
|
|
bindings: {
|
|
toggleLookup: 'buttonSouth',
|
|
closeLookup: 'buttonEast',
|
|
toggleKeyboardOnlyMode: 'buttonNorth',
|
|
mineCard: 'buttonWest',
|
|
quitMpv: 'select',
|
|
previousAudio: 'none',
|
|
nextAudio: 'rightShoulder',
|
|
playCurrentAudio: 'leftShoulder',
|
|
toggleMpvPause: 'leftStickPress',
|
|
leftStickHorizontal: 'leftStickX',
|
|
leftStickVertical: 'leftStickY',
|
|
rightStickHorizontal: 'rightStickX',
|
|
rightStickVertical: 'rightStickY',
|
|
},
|
|
};
|
|
state.connectedGamepads = [{ id: 'pad-2', index: 1, mapping: 'standard', connected: true }];
|
|
state.activeGamepadId = 'pad-2';
|
|
|
|
const ctx = {
|
|
dom: {
|
|
overlay: { classList: createClassList(), focus: () => {} },
|
|
controllerSelectModal: {
|
|
classList: createClassList(['hidden']),
|
|
setAttribute: () => {},
|
|
},
|
|
controllerSelectClose: { addEventListener: () => {} },
|
|
controllerSelectHint: { textContent: '' },
|
|
controllerSelectStatus: { textContent: '', classList: createClassList() },
|
|
controllerSelectList: {
|
|
innerHTML: '',
|
|
appendChild: () => {},
|
|
},
|
|
controllerSelectSave: { addEventListener: () => {} },
|
|
},
|
|
state,
|
|
};
|
|
|
|
const modal = createControllerSelectModal(ctx as never, {
|
|
modalStateReader: { isAnyModalOpen: () => false },
|
|
syncSettingsModalSubtitleSuppression: () => {},
|
|
});
|
|
|
|
modal.openControllerSelectModal();
|
|
await modal.handleControllerSelectKeydown({
|
|
key: 'Enter',
|
|
preventDefault: () => {},
|
|
} as KeyboardEvent);
|
|
|
|
assert.match(ctx.dom.controllerSelectStatus.textContent, /Failed to save preferred controller/);
|
|
assert.equal(state.controllerConfig.preferredGamepadId, 'pad-1');
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
|
}
|
|
});
|
|
|
|
test('controller select modal does not rerender unchanged device snapshots every poll', () => {
|
|
const globals = globalThis as typeof globalThis & { window?: unknown; document?: unknown };
|
|
const previousWindow = globals.window;
|
|
const previousDocument = globals.document;
|
|
let appendCount = 0;
|
|
|
|
Object.defineProperty(globalThis, 'window', {
|
|
configurable: true,
|
|
value: {
|
|
focus: () => {},
|
|
electronAPI: {
|
|
saveControllerPreference: async () => {},
|
|
notifyOverlayModalClosed: () => {},
|
|
},
|
|
},
|
|
});
|
|
Object.defineProperty(globalThis, 'document', {
|
|
configurable: true,
|
|
value: {
|
|
createElement: () => ({
|
|
className: '',
|
|
textContent: '',
|
|
classList: createClassList(),
|
|
appendChild: () => {},
|
|
addEventListener: () => {},
|
|
}),
|
|
},
|
|
});
|
|
|
|
try {
|
|
const state = createRendererState();
|
|
state.controllerConfig = {
|
|
enabled: true,
|
|
preferredGamepadId: 'pad-1',
|
|
preferredGamepadLabel: 'pad-1',
|
|
smoothScroll: true,
|
|
scrollPixelsPerSecond: 960,
|
|
horizontalJumpPixels: 160,
|
|
stickDeadzone: 0.2,
|
|
triggerInputMode: 'auto',
|
|
triggerDeadzone: 0.5,
|
|
repeatDelayMs: 220,
|
|
repeatIntervalMs: 80,
|
|
buttonIndices: {
|
|
select: 6,
|
|
buttonSouth: 0,
|
|
buttonEast: 1,
|
|
buttonWest: 2,
|
|
buttonNorth: 3,
|
|
leftShoulder: 4,
|
|
rightShoulder: 5,
|
|
leftStickPress: 9,
|
|
rightStickPress: 10,
|
|
leftTrigger: 6,
|
|
rightTrigger: 7,
|
|
},
|
|
bindings: {
|
|
toggleLookup: 'buttonSouth',
|
|
closeLookup: 'buttonEast',
|
|
toggleKeyboardOnlyMode: 'buttonNorth',
|
|
mineCard: 'buttonWest',
|
|
quitMpv: 'select',
|
|
previousAudio: 'none',
|
|
nextAudio: 'rightShoulder',
|
|
playCurrentAudio: 'leftShoulder',
|
|
toggleMpvPause: 'leftStickPress',
|
|
leftStickHorizontal: 'leftStickX',
|
|
leftStickVertical: 'leftStickY',
|
|
rightStickHorizontal: 'rightStickX',
|
|
rightStickVertical: 'rightStickY',
|
|
},
|
|
};
|
|
state.connectedGamepads = [
|
|
{ id: 'pad-1', index: 0, mapping: 'standard', connected: true },
|
|
{ id: 'pad-2', index: 1, mapping: 'standard', connected: true },
|
|
];
|
|
state.activeGamepadId = 'pad-1';
|
|
|
|
const ctx = {
|
|
dom: {
|
|
overlay: { classList: createClassList(), focus: () => {} },
|
|
controllerSelectModal: {
|
|
classList: createClassList(['hidden']),
|
|
setAttribute: () => {},
|
|
},
|
|
controllerSelectClose: { addEventListener: () => {} },
|
|
controllerSelectHint: { textContent: '' },
|
|
controllerSelectStatus: { textContent: '', classList: createClassList() },
|
|
controllerSelectList: {
|
|
innerHTML: '',
|
|
appendChild: () => {
|
|
appendCount += 1;
|
|
},
|
|
},
|
|
controllerSelectSave: { addEventListener: () => {} },
|
|
},
|
|
state,
|
|
};
|
|
|
|
const modal = createControllerSelectModal(ctx as never, {
|
|
modalStateReader: { isAnyModalOpen: () => false },
|
|
syncSettingsModalSubtitleSuppression: () => {},
|
|
});
|
|
|
|
modal.openControllerSelectModal();
|
|
const initialAppendCount = appendCount;
|
|
|
|
modal.updateDevices();
|
|
|
|
assert.equal(appendCount, initialAppendCount);
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
|
Object.defineProperty(globalThis, 'document', { configurable: true, value: previousDocument });
|
|
}
|
|
});
|