explodorama / files / test / padGlyph.test.ts
padGlyph.test.ts
| 1 | import { existsSync } from 'node:fs' |
| 2 | import { describe, expect, it } from 'vitest' |
| 3 | import { glyphFor, shoulderGlyph, type GlyphButton } from '../src/ui/padGlyph' |
| 4 | |
| 5 | describe('shoulder glyphs (the board hint)', () => { |
| 6 | it('every family and side resolves to a file in assets/kenney-input-prompts', () => { |
| 7 | for (const family of ['playstation', 'xbox', 'nintendo', 'generic'] as const) { |
| 8 | for (const side of ['l', 'r'] as const) expect(existsSync(`assets/kenney-input-prompts/${shoulderGlyph(family, side)}.png`), `${family} ${side}`).toBe(true) |
| 9 | } |
| 10 | }) |
| 11 | it('names: L1 / R1, LB / RB, L / R; any other pad gets the Xbox ones', () => { |
| 12 | expect(shoulderGlyph('playstation', 'l')).toBe('playstation_trigger_l1') |
| 13 | expect(shoulderGlyph('xbox', 'r')).toBe('xbox_rb') |
| 14 | expect(shoulderGlyph('nintendo', 'l')).toBe('switch_button_l') |
| 15 | expect(shoulderGlyph('generic', 'l')).toBe('xbox_lb') |
| 16 | }) |
| 17 | }) |
| 18 | |
| 19 | const BUTTONS: GlyphButton[] = ['move', 'bomb', 'charge', 'wait'] |
| 20 | |
| 21 | describe('controller glyphs on the action bar', () => { |
| 22 | it('every family and button resolves to a file in assets/kenney-input-prompts', () => { |
| 23 | for (const family of ['playstation', 'xbox', 'nintendo', 'generic'] as const) { |
| 24 | for (const button of BUTTONS) expect(existsSync(`assets/kenney-input-prompts/${glyphFor(family, button)}.png`), `${family} ${button}`).toBe(true) |
| 25 | } |
| 26 | }) |
| 27 | it('PlayStation: Move Cross, Bomb Square, Charge Triangle, Wait Circle', () => { |
| 28 | expect(BUTTONS.map((b) => glyphFor('playstation', b))).toEqual([ |
| 29 | 'playstation_button_color_cross', 'playstation_button_color_square', 'playstation_button_color_triangle', 'playstation_button_color_circle', |
| 30 | ]) |
| 31 | }) |
| 32 | it('Xbox: Move A, Bomb X, Charge Y, Wait B; any other pad gets the same', () => { |
| 33 | expect(BUTTONS.map((b) => glyphFor('xbox', b))).toEqual(['xbox_button_color_a', 'xbox_button_color_x', 'xbox_button_color_y', 'xbox_button_color_b']) |
| 34 | expect(BUTTONS.map((b) => glyphFor('generic', b))).toEqual(BUTTONS.map((b) => glyphFor('xbox', b))) |
| 35 | }) |
| 36 | }) |
| 37 | |
| 38 | describe('Nintendo: A confirms (right), B backs out (bottom)', () => { |
| 39 | it('Move is A, Wait is B, Bomb Y, Charge X', () => { |
| 40 | expect(glyphFor('nintendo', 'move')).toBe('switch_button_a') |
| 41 | expect(glyphFor('nintendo', 'wait')).toBe('switch_button_b') |
| 42 | expect(glyphFor('nintendo', 'bomb')).toBe('switch_button_y') |
| 43 | expect(glyphFor('nintendo', 'charge')).toBe('switch_button_x') |
| 44 | }) |
| 45 | it('the raw buttons are by position: bottom B, right A, left Y, top X', async () => { |
| 46 | const { faceGlyph } = await import('../src/ui/padGlyph') |
| 47 | expect(([0, 1, 2, 3] as const).map((f) => faceGlyph('nintendo', f))).toEqual(['switch_button_b', 'switch_button_a', 'switch_button_y', 'switch_button_x']) |
| 48 | }) |
| 49 | it('every action\'s picture is the picture of the face button that does it, in every family', async () => { |
| 50 | const { faceGlyph } = await import('../src/ui/padGlyph') |
| 51 | const { faceOf } = await import('../src/ui/padMap') |
| 52 | for (const family of ['playstation', 'xbox', 'nintendo', 'generic'] as const) { |
| 53 | for (const b of BUTTONS) expect(glyphFor(family, b), `${family} ${b}`).toBe(faceGlyph(family, faceOf(family, b))) |
| 54 | } |
| 55 | }) |
| 56 | it('the demo caption {move} is A on a Nintendo pad', async () => { |
| 57 | const { padCaptionHtml } = await import('../src/ui/padGlyph') |
| 58 | expect(padCaptionHtml('Hold {move} to play', 'nintendo', (n) => n)).toContain('src="switch_button_a"') |
| 59 | }) |
| 60 | }) |
| 61 | |
| 62 | describe('controller glyphs in dialogs', () => { |
| 63 | it('each raw face button has a file, per family', async () => { |
| 64 | const { faceGlyph } = await import('../src/ui/padGlyph') |
| 65 | for (const family of ['playstation', 'xbox', 'nintendo', 'generic'] as const) { |
| 66 | for (const face of [0, 1, 2, 3] as const) expect(existsSync(`assets/kenney-input-prompts/${faceGlyph(family, face)}.png`), `${family} ${face}`).toBe(true) |
| 67 | } |
| 68 | expect(faceGlyph('playstation', 0)).toBe('playstation_button_color_cross') |
| 69 | expect(faceGlyph('xbox', 1)).toBe('xbox_button_color_b') |
| 70 | expect(faceGlyph('playstation', 3)).toBe('playstation_button_color_triangle') |
| 71 | }) |
| 72 | }) |
| 73 | |
| 74 | const FAMILIES = ['playstation', 'xbox', 'nintendo', 'generic'] as const |
| 75 | |
| 76 | describe('controller glyphs for the demo overlay', () => { |
| 77 | it('every family and direction resolves to a file', async () => { |
| 78 | const { dpadGlyph, stickGlyph, startGlyph } = await import('../src/ui/padGlyph') |
| 79 | for (const family of FAMILIES) { |
| 80 | for (const dir of ['none', 'up', 'down', 'left', 'right'] as const) expect(existsSync(`assets/kenney-input-prompts/${dpadGlyph(family, dir)}.png`), `${family} dpad ${dir}`).toBe(true) |
| 81 | for (const dir of ['none', 'left', 'right', 'up', 'down', 'press'] as const) expect(existsSync(`assets/kenney-input-prompts/${stickGlyph(family, dir)}.png`), `${family} stick ${dir}`).toBe(true) |
| 82 | expect(existsSync(`assets/kenney-input-prompts/${startGlyph(family)}.png`), `${family} start`).toBe(true) |
| 83 | } |
| 84 | }) |
| 85 | it('names, and a pad that is none of the three gets the Xbox ones', async () => { |
| 86 | const { dpadGlyph, stickGlyph, startGlyph } = await import('../src/ui/padGlyph') |
| 87 | expect(dpadGlyph('playstation', 'up')).toBe('playstation_dpad_up') |
| 88 | expect(dpadGlyph('nintendo', 'none')).toBe('switch_dpad') |
| 89 | expect(stickGlyph('xbox', 'none')).toBe('xbox_stick_r') |
| 90 | expect(stickGlyph('xbox', 'press')).toBe('xbox_stick_r_press') |
| 91 | expect(startGlyph('playstation')).toBe('playstation5_button_options') |
| 92 | expect(startGlyph('nintendo')).toBe('switch_button_plus') |
| 93 | expect(dpadGlyph('generic', 'left')).toBe(dpadGlyph('xbox', 'left')) |
| 94 | expect(stickGlyph('generic', 'up')).toBe(stickGlyph('xbox', 'up')) |
| 95 | expect(startGlyph('generic')).toBe(startGlyph('xbox')) |
| 96 | }) |
| 97 | }) |
| 98 | |
| 99 | describe('padCaptionHtml', () => { |
| 100 | it('replaces every token with the family\'s picture', async () => { |
| 101 | const { padCaptionHtml } = await import('../src/ui/padGlyph') |
| 102 | const html = padCaptionHtml('{dpad} {move} {bomb} {charge} {wait} {l1} {r1} {stick} {start}', 'playstation', (n) => `/${n}.png`) |
| 103 | expect(html).not.toContain('{') |
| 104 | expect(html.match(/<img class="pad-glyph"/g)).toHaveLength(9) |
| 105 | expect(html).toContain('src="/playstation_dpad.png"') |
| 106 | expect(html).toContain('src="/playstation_button_color_cross.png"') |
| 107 | expect(html).toContain('src="/playstation5_button_options.png"') |
| 108 | }) |
| 109 | it('leaves the words alone, and throws on an unknown token', async () => { |
| 110 | const { padCaptionHtml } = await import('../src/ui/padGlyph') |
| 111 | expect(padCaptionHtml('Press now.', 'xbox', (n) => n)).toBe('Press now.') |
| 112 | expect(() => padCaptionHtml('Press {nope}.', 'xbox', (n) => n)).toThrow('nope') |
| 113 | }) |
| 114 | }) |