diff --git a/scripts/test-names.mjs b/scripts/test-names.mjs new file mode 100644 index 0000000..5611baf --- /dev/null +++ b/scripts/test-names.mjs @@ -0,0 +1,56 @@ +import assert from 'node:assert/strict'; +import { getAvatarForName, generateUsername, USERNAME_ADJECTIVES, USERNAME_NOUNS } from '../shared/names.js'; + +// --- getAvatarForName (deterministic) --- + +// Exact matches +assert.equal(getAvatarForName('Koala'), '🐨', 'Koala'); +assert.equal(getAvatarForName('Tiger'), '🐯', 'Tiger'); +assert.equal(getAvatarForName('Panda'), '🐼', 'Panda'); +assert.equal(getAvatarForName('Fox'), '🦊', 'Fox'); + +// Case insensitive +assert.equal(getAvatarForName('koala'), '🐨', 'lowercase'); +assert.equal(getAvatarForName('MyKoalaUser'), '🐨', 'embedded uppercase'); + +// Longest match wins (caterpillar > cat) +assert.equal(getAvatarForName('CaterpillarCat'), 'šŸ›', 'caterpillar before cat'); +assert.equal(getAvatarForName('Cat'), '🐱', 'cat alone'); + +// Emoji with ZWJ sequences (multi-codepoint) +assert.equal(getAvatarForName('Polar'), '🐻\u200Dā„ļø', 'polar bear ZWJ'); +assert.equal(getAvatarForName('Crow'), '🐦\u200D⬛', 'crow ZWJ'); + +// Human-like characters +assert.equal(getAvatarForName('Ninja'), '🄷', 'ninja'); +assert.equal(getAvatarForName('Wizard'), 'šŸ§™', 'wizard'); +assert.equal(getAvatarForName('Pirate'), 'šŸ“', 'pirate'); +assert.equal(getAvatarForName('Alien'), 'šŸ‘¾', 'alien'); +assert.equal(getAvatarForName('Robot'), 'šŸ¤–', 'robot'); + +// Fallback +assert.equal(getAvatarForName(''), 'šŸ‘¤', 'empty string'); +assert.equal(getAvatarForName('Xyzzy123'), 'šŸ‘¤', 'unknown name'); +assert.equal(getAvatarForName(null), 'šŸ‘¤', 'null'); +assert.equal(getAvatarForName(undefined), 'šŸ‘¤', 'undefined'); + +// --- generateUsername (format check) --- +for (let i = 0; i < 10; i++) { + const name = generateUsername(); + // Format: AdjectiveNoun (e.g. "HappyKoala") + assert.ok(/^[A-Z][a-z]+[A-Z][a-z]+$/.test(name), `format: ${name}`); + // Adjective from list + const adj = USERNAME_ADJECTIVES.some(a => name.startsWith(a)); + assert.ok(adj, `adjective from list: ${name}`); + // Noun from list + const noun = USERNAME_NOUNS.some(n => name.endsWith(n)); + assert.ok(noun, `noun from list: ${name}`); +} + +// Every noun has an emoji (no broken usernames) +for (const noun of USERNAME_NOUNS) { + const avatar = getAvatarForName(noun); + assert.notEqual(avatar, 'šŸ‘¤', `noun "${noun}" has no emoji — add to ANIMAL_EMOJI_MAP`); +} + +console.log('names tests passed'); diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs index 0ed31be..94ade13 100644 --- a/scripts/verify-release.mjs +++ b/scripts/verify-release.mjs @@ -14,6 +14,7 @@ const checks = [ ['rate-limiter unit tests', 'node', ['scripts/test-rate-limiter.mjs']], ['episode-utils unit tests', 'node', ['scripts/test-episode-utils.mjs']], ['server WebSocket integration', 'node', ['scripts/test-server-ws.mjs']], + ['names generator', 'node', ['scripts/test-names.mjs']], ['content video finder', 'node', ['scripts/test-content-video-finder.cjs']], ['audio settings', 'node', ['scripts/test-audio-settings.mjs']], ['popup refresh cooldown', 'node', ['scripts/test-popup-refresh-cooldown.mjs']],