From c54a8c73ce2aa11f15106c05248e2486b56f2cae Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 15:25:18 +0100 Subject: [PATCH 1/8] feat: enhance GIF check functionality to include missing state reporting --- src/test/gifs.ts | 115 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 35 deletions(-) diff --git a/src/test/gifs.ts b/src/test/gifs.ts index 56d55555..c65faa73 100644 --- a/src/test/gifs.ts +++ b/src/test/gifs.ts @@ -1,48 +1,93 @@ import * as fs from 'fs'; -import { PokemonColor } from '../common/types'; +import { + PokemonColor, + PokemonExtraSprite, + PokemonGeneration, +} from '../common/types'; import { getAllPokemon, POKEMON_DATA } from '../common/pokemon-data'; -const defaultPokemonConfig = { - colors: [PokemonColor.default], - states: ['idle', 'walk'], +type MissingGif = { + generation: number; + pokemon: string; + states: string[]; }; -const allPokemon = getAllPokemon().reduce( - (acc, pokemon) => ({ - ...acc, - [pokemon]: defaultPokemonConfig, - }), - {} as { [key: string]: { colors: string[]; states: string[] } }, -); -function checkGifFilenames(folder: string) { - for (const pokemon in allPokemon) { - const allowedColors = allPokemon[pokemon].colors; - const allowedStates = allPokemon[pokemon].states; - if (!allowedColors) { - console.error(`No colors found for pokemon "${pokemon}"`); - return; - } +const mediaFolder = './media'; - // Get the generation number from POKEMON_DATA +function runGifCheck(folder: string): number { + // Group pokemon by generation + const genMap: Record = {}; + getAllPokemon().forEach((pokemon) => { const generation = POKEMON_DATA[pokemon]?.generation || 1; - const genFolder = `gen${generation}`; - - allowedColors.forEach((color) => { - allowedStates.forEach((state) => { - const filename = `${color}_${state}_8fps.gif`; - const filePath = `${folder}/${genFolder}/${pokemon}/${filename}`; - if (!fs.existsSync(filePath)) { - // \x1b[31m is the ANSI escape code for red, and \x1b[0m resets the color back to the terminal's default. - console.error(`\x1b[31mFile "${filePath}" does not exist.\x1b[0m`); - return false; - } else { - console.log(`File "${filePath}" exists.`); + if (!genMap[generation]) genMap[generation] = []; + genMap[generation].push(pokemon); + }); + + const missingPokemon: MissingGif[] = []; + // Iterate generations starting at 1 + for ( + let generation = PokemonGeneration.Gen1; + generation <= PokemonGeneration.Gen4; + generation++ + ) { + console.log(`Checking generation ${generation}...`); + const pokes = genMap[generation] || []; + // Order by POKEMON_DATA id when available + pokes.sort( + (a, b) => (POKEMON_DATA[a]?.id || 0) - (POKEMON_DATA[b]?.id || 0), + ); + + for (const pokemon of pokes) { + const cfg = POKEMON_DATA[pokemon]; + console.log(` Checking ${pokemon}...`); + const colors = + cfg?.possibleColors && cfg.possibleColors.length > 0 + ? cfg.possibleColors + : [PokemonColor.default]; + const states = ['idle', 'walk']; + if ( + cfg?.extraSprites && + cfg.extraSprites.includes(PokemonExtraSprite.leftFacing) + ) { + states.push('walk_left'); + } + + const missing: string[] = []; + + for (const color of colors) { + for (const state of states) { + const filename = `${color}_${state}_8fps.gif`; + const filePath = `${folder}/gen${generation}/${pokemon}/${filename}`; + if (!fs.existsSync(filePath)) { + missing.push(`${color}_${state}`); + } } - }); + } + + if (missing.length > 0) { + console.error( + ` \x1b[31m${pokemon}: missing ${missing.join(', ')}\x1b[0m`, + ); + missingPokemon.push({ generation, pokemon, states: missing }); + } + } + } + + if (missingPokemon.length === 0) { + console.log('All GIFs are present!'); + } else { + console.error(`\nMissing GIFs:`); + missingPokemon.forEach(({ generation, pokemon, states }) => { + console.error(` Gen ${generation} - ${pokemon}: ${states.join(', ')}`); }); } + return missingPokemon.length; } -const mediaFolder = './media'; -checkGifFilenames(mediaFolder); +const missing = runGifCheck(mediaFolder); +if (missing > 0) { + // Non-zero exit to fail CI when there are missing GIFs + process.exit(1); +} +process.exit(0); From b14bcfb485d56491278e277d72098a8c190a1368 Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 18:00:34 +0100 Subject: [PATCH 2/8] feat: update runGifCheck function to return MissingGif[] and improve missing GIF reporting --- src/test/gifs.ts | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/test/gifs.ts b/src/test/gifs.ts index c65faa73..4d5b4dfd 100644 --- a/src/test/gifs.ts +++ b/src/test/gifs.ts @@ -15,7 +15,7 @@ type MissingGif = { const mediaFolder = './media'; -function runGifCheck(folder: string): number { +function runGifCheck(folder: string): MissingGif[] { // Group pokemon by generation const genMap: Record = {}; getAllPokemon().forEach((pokemon) => { @@ -66,28 +66,27 @@ function runGifCheck(folder: string): number { } if (missing.length > 0) { - console.error( - ` \x1b[31m${pokemon}: missing ${missing.join(', ')}\x1b[0m`, - ); + console.error(` \x1b[31mmissing ${missing.join(', ')}\x1b[0m`); missingPokemon.push({ generation, pokemon, states: missing }); } } } - if (missingPokemon.length === 0) { - console.log('All GIFs are present!'); - } else { + return missingPokemon; +} + +const missing = runGifCheck(mediaFolder); +if (missing.length > 0) { + setTimeout(() => { console.error(`\nMissing GIFs:`); - missingPokemon.forEach(({ generation, pokemon, states }) => { + missing.forEach(({ generation, pokemon, states }) => { console.error(` Gen ${generation} - ${pokemon}: ${states.join(', ')}`); }); - } - return missingPokemon.length; -} -const missing = runGifCheck(mediaFolder); -if (missing > 0) { - // Non-zero exit to fail CI when there are missing GIFs - process.exit(1); + // Non-zero exit to fail CI when there are missing GIFs + process.exit(1); + }, 1000); +} else { + console.log('All GIFs are present!'); + process.exit(0); } -process.exit(0); From 5673ed2553d1f192071669da2992fa9170ef96fa Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 19:02:22 +0100 Subject: [PATCH 3/8] feat: rename workflow from Verify Gifs Filename to Verify Sprites and restructure steps --- .github/workflows/gifs.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gifs.yml b/.github/workflows/gifs.yml index 6c6b75eb..c962c83c 100644 --- a/.github/workflows/gifs.yml +++ b/.github/workflows/gifs.yml @@ -1,4 +1,4 @@ -name: Verify Gifs Filename +name: Verify Sprites on: pull_request: @@ -8,9 +8,20 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Verify Gifs Filename - run: | - npm install - npm run compile:test - npm run test:gifs + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Compile tests + run: npm run compile:test + + - name: Verify Sprites + run: npm run test:gifs From d381256f033cc7d70185295dd0107c61145b78aa Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 19:03:48 +0100 Subject: [PATCH 4/8] style: format YAML workflow for better readability --- .github/workflows/gifs.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/gifs.yml b/.github/workflows/gifs.yml index c962c83c..763d9731 100644 --- a/.github/workflows/gifs.yml +++ b/.github/workflows/gifs.yml @@ -8,20 +8,20 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm - - name: Install dependencies - run: npm ci + - name: Install dependencies + run: npm ci - - name: Compile tests - run: npm run compile:test + - name: Compile tests + run: npm run compile:test - - name: Verify Sprites - run: npm run test:gifs + - name: Verify Sprites + run: npm run test:gifs From c089a6dbe1b515e805154fbe2139a3bb464aedd7 Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 19:07:57 +0100 Subject: [PATCH 5/8] style: improve YAML workflow indentation for consistency --- .github/workflows/gifs.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/gifs.yml b/.github/workflows/gifs.yml index 763d9731..c6949a71 100644 --- a/.github/workflows/gifs.yml +++ b/.github/workflows/gifs.yml @@ -5,23 +5,23 @@ on: branches: [main] jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' - - name: Install dependencies - run: npm ci + - name: Install dependencies + run: npm ci - - name: Compile tests - run: npm run compile:test + - name: Compile tests + run: npm run compile:test - - name: Verify Sprites - run: npm run test:gifs + - name: Verify Sprites + run: npm run test:gifs From e3b508cbf1800ba98514ed75692ba11726213da9 Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 19:12:47 +0100 Subject: [PATCH 6/8] feat: enhance GIF existence check with case-insensitive fallback and path module usage --- src/test/gifs.ts | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/test/gifs.ts b/src/test/gifs.ts index 4d5b4dfd..d1260384 100644 --- a/src/test/gifs.ts +++ b/src/test/gifs.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import * as path from 'path'; import { PokemonColor, @@ -58,9 +59,31 @@ function runGifCheck(folder: string): MissingGif[] { for (const color of colors) { for (const state of states) { const filename = `${color}_${state}_8fps.gif`; - const filePath = `${folder}/gen${generation}/${pokemon}/${filename}`; - if (!fs.existsSync(filePath)) { + const filePath = path.join( + folder, + `gen${generation}`, + pokemon, + filename, + ); + + const exists = + fs.existsSync(filePath) || + (() => { + // Fallback: case-insensitive look-up inside the pokemon directory + try { + const dir = path.join(folder, `gen${generation}`, pokemon); + if (!fs.existsSync(dir)) return false; + const files = fs.readdirSync(dir); + const target = filename.toLowerCase(); + return files.some((f) => f.toLowerCase() === target); + } catch (e) { + return false; + } + })(); + + if (!exists) { missing.push(`${color}_${state}`); + console.log(` checked path: ${filePath}`); } } } From 9bbf6194321c868d2e9a8005d55ef9a4cfdaa324 Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 19:18:01 +0100 Subject: [PATCH 7/8] feat: refactor runGifCheck to use async/await and improve missing GIF reporting --- src/test/gifs.ts | 61 +++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/src/test/gifs.ts b/src/test/gifs.ts index d1260384..ba23524d 100644 --- a/src/test/gifs.ts +++ b/src/test/gifs.ts @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import * as path from 'path'; import { PokemonColor, @@ -7,6 +6,7 @@ import { PokemonGeneration, } from '../common/types'; import { getAllPokemon, POKEMON_DATA } from '../common/pokemon-data'; +import * as path from 'path'; type MissingGif = { generation: number; @@ -15,8 +15,13 @@ type MissingGif = { }; const mediaFolder = './media'; +const DELAY_MS = 10; -function runGifCheck(folder: string): MissingGif[] { +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function runGifCheck(folder: string): Promise { // Group pokemon by generation const genMap: Record = {}; getAllPokemon().forEach((pokemon) => { @@ -65,25 +70,8 @@ function runGifCheck(folder: string): MissingGif[] { pokemon, filename, ); - - const exists = - fs.existsSync(filePath) || - (() => { - // Fallback: case-insensitive look-up inside the pokemon directory - try { - const dir = path.join(folder, `gen${generation}`, pokemon); - if (!fs.existsSync(dir)) return false; - const files = fs.readdirSync(dir); - const target = filename.toLowerCase(); - return files.some((f) => f.toLowerCase() === target); - } catch (e) { - return false; - } - })(); - - if (!exists) { + if (!fs.existsSync(filePath)) { missing.push(`${color}_${state}`); - console.log(` checked path: ${filePath}`); } } } @@ -92,24 +80,29 @@ function runGifCheck(folder: string): MissingGif[] { console.error(` \x1b[31mmissing ${missing.join(', ')}\x1b[0m`); missingPokemon.push({ generation, pokemon, states: missing }); } + + // Wait a short time between pokemon checks to reduce CI flakiness + await sleep(DELAY_MS); } } return missingPokemon; } -const missing = runGifCheck(mediaFolder); -if (missing.length > 0) { - setTimeout(() => { - console.error(`\nMissing GIFs:`); - missing.forEach(({ generation, pokemon, states }) => { - console.error(` Gen ${generation} - ${pokemon}: ${states.join(', ')}`); - }); +(async () => { + const missing = await runGifCheck(mediaFolder); + if (missing.length > 0) { + setTimeout(() => { + console.error(`\nMissing GIFs:`); + missing.forEach(({ generation, pokemon, states }) => { + console.error(` Gen ${generation} - ${pokemon}: ${states.join(', ')}`); + }); - // Non-zero exit to fail CI when there are missing GIFs - process.exit(1); - }, 1000); -} else { - console.log('All GIFs are present!'); - process.exit(0); -} + // Non-zero exit to fail CI when there are missing GIFs + process.exit(1); + }, 1000); + } else { + console.log('All GIFs are present!'); + process.exit(0); + } +})(); From 0768fd7e71415fb36a0cbc8f1a1d4d4fbad319b6 Mon Sep 17 00:00:00 2001 From: Kasper Wissendorf Date: Fri, 13 Feb 2026 19:20:09 +0100 Subject: [PATCH 8/8] fix: reduce delay in GIF checks and improve logging format --- src/test/gifs.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/gifs.ts b/src/test/gifs.ts index ba23524d..8a21ff34 100644 --- a/src/test/gifs.ts +++ b/src/test/gifs.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import * as path from 'path'; import { PokemonColor, @@ -6,7 +7,6 @@ import { PokemonGeneration, } from '../common/types'; import { getAllPokemon, POKEMON_DATA } from '../common/pokemon-data'; -import * as path from 'path'; type MissingGif = { generation: number; @@ -15,13 +15,15 @@ type MissingGif = { }; const mediaFolder = './media'; -const DELAY_MS = 10; +const DELAY_MS = 5; function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function runGifCheck(folder: string): Promise { + console.log(`Checking GIFs in folder: ${folder}`); + // Group pokemon by generation const genMap: Record = {}; getAllPokemon().forEach((pokemon) => { @@ -37,7 +39,7 @@ async function runGifCheck(folder: string): Promise { generation <= PokemonGeneration.Gen4; generation++ ) { - console.log(`Checking generation ${generation}...`); + console.log(`\nChecking generation ${generation}...`); const pokes = genMap[generation] || []; // Order by POKEMON_DATA id when available pokes.sort(