Navigation manette : focus spatial, Gamepad API Xbox/PlayStation, répétition, glyphes adaptés, vibration, i18n FR/EN
This commit is contained in:
parent
ae54bbd8e9
commit
e7478d3c64
12 changed files with 421 additions and 0 deletions
13
tests/i18n.test.ts
Normal file
13
tests/i18n.test.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { en } from '../src/renderer/src/i18n/en'
|
||||
import { fr } from '../src/renderer/src/i18n/fr'
|
||||
|
||||
describe('traductions', () => {
|
||||
it('même clés et mêmes variables en FR et EN', () => {
|
||||
expect(Object.keys(en).sort()).toEqual(Object.keys(fr).sort())
|
||||
for (const [k, v] of Object.entries(fr)) {
|
||||
const vars = (s: string) => (s.match(/\{\w+\}/g) ?? []).sort()
|
||||
expect(vars(en[k as keyof typeof en]), k).toEqual(vars(v))
|
||||
}
|
||||
})
|
||||
})
|
||||
38
tests/input.test.ts
Normal file
38
tests/input.test.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { padActions, padKind, Repeater } from '../src/renderer/src/nav/input'
|
||||
import { glyph } from '../src/renderer/src/nav/glyph-map'
|
||||
|
||||
const pad = (pressed: number[], axes = [0, 0]) => ({ buttons: Array.from({ length: 17 }, (_, i) => ({ pressed: pressed.includes(i), touched: false, value: 0 })), axes })
|
||||
|
||||
describe('entrées manette', () => {
|
||||
it('traduit boutons et stick', () => {
|
||||
expect([...padActions(pad([0, 13]))].sort()).toEqual(['accept', 'down'])
|
||||
expect([...padActions(pad([], [-0.9, 0.1]))]).toEqual(['left'])
|
||||
expect(padActions(pad([], [0.3, -0.3])).size).toBe(0) // zone morte
|
||||
})
|
||||
it('reconnaît les manettes PlayStation', () => {
|
||||
expect(padKind('054c-0ce6-Wireless Controller')).toBe('ps')
|
||||
expect(padKind('DualSense Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 0ce6)')).toBe('ps')
|
||||
expect(padKind('Xbox 360 Controller (XInput STANDARD GAMEPAD)')).toBe('xbox')
|
||||
})
|
||||
it('affiche le bon bouton', () => {
|
||||
expect(glyph('accept', 'xbox')).toBe('A')
|
||||
expect(glyph('accept', 'ps')).toBe('✕')
|
||||
expect(glyph('back', 'ps')).toBe('○')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Repeater', () => {
|
||||
it('répète une direction tenue, pas un bouton', () => {
|
||||
const r = new Repeater()
|
||||
expect(r.step('down', true, 0)).toBe(true)
|
||||
expect(r.step('down', true, 200)).toBe(false)
|
||||
expect(r.step('down', true, 400)).toBe(true)
|
||||
expect(r.step('down', true, 430)).toBe(false)
|
||||
expect(r.step('down', true, 490)).toBe(true)
|
||||
expect(r.step('accept', true, 0)).toBe(true)
|
||||
expect(r.step('accept', true, 1000)).toBe(false)
|
||||
r.step('down', false, 500)
|
||||
expect(r.step('down', true, 510)).toBe(true)
|
||||
})
|
||||
})
|
||||
32
tests/spatial.test.ts
Normal file
32
tests/spatial.test.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { pickNext, type Box } from '../src/renderer/src/nav/spatial'
|
||||
|
||||
/* grille 3x3 de cartes 100x150, espacées de 20 */
|
||||
const grid = Array.from({ length: 9 }, (_, i) => ({ id: i, box: { x: (i % 3) * 120, y: Math.floor(i / 3) * 170, w: 100, h: 150 } as Box }))
|
||||
const at = (i: number) => grid[i].box
|
||||
const next = (i: number, d: 'up' | 'down' | 'left' | 'right', items = grid) => pickNext(at(i), items.filter((g) => g.id !== i), d)?.id ?? null
|
||||
|
||||
describe('pickNext', () => {
|
||||
it('se déplace dans une grille', () => {
|
||||
expect(next(4, 'right')).toBe(5)
|
||||
expect(next(4, 'left')).toBe(3)
|
||||
expect(next(4, 'up')).toBe(1)
|
||||
expect(next(4, 'down')).toBe(7)
|
||||
})
|
||||
it('ne sort pas de la grille', () => {
|
||||
expect(next(2, 'right')).toBeNull()
|
||||
expect(next(0, 'up')).toBeNull()
|
||||
})
|
||||
it('préfère le même axe à la diagonale plus proche', () => {
|
||||
const items = [{ id: 'loin', box: { x: 600, y: 0, w: 100, h: 50 } }, { id: 'diag', box: { x: 150, y: 80, w: 100, h: 50 } }]
|
||||
expect(pickNext({ x: 0, y: 0, w: 100, h: 50 }, items, 'right')?.id).toBe('loin')
|
||||
})
|
||||
it('descend vers un bouton large sous la grille', () => {
|
||||
const items = [...grid, { id: 99, box: { x: 0, y: 600, w: 400, h: 60 } }]
|
||||
expect(next(8, 'down', items)).toBe(99)
|
||||
})
|
||||
it('remonte de la barre vers la carte la plus alignée', () => {
|
||||
const items = [...grid, { id: 99, box: { x: 240, y: 600, w: 100, h: 60 } }]
|
||||
expect(pickNext(items[9].box, grid, 'up')?.id).toBe(8)
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue