codenames
Version:
Converts numerical values into human-readable names following a specific theme (e.g., cities)
240 lines (202 loc) • 7.57 kB
text/typescript
import { describe, expect, test } from "bun:test";
import { codename, themes, type Theme } from "./all";
describe("words/all.ts exports", () => {
test("themes array contains all expected themes", () => {
expect(themes).toBeInstanceOf(Array);
expect(themes.length).toBeGreaterThan(0);
// Should contain themes for all main categories
const categories = [
"adjectives",
"animals",
"cities",
"clothing",
"colors",
"countries",
"elements",
"emotions",
"food",
"gems",
"nature",
"snacks",
];
const limits = ["10", "20", "30", "50", "100"];
for (const category of categories) {
for (const limit of limits) {
const themeName = `${category}-${limit}` as Theme;
expect(themes).toContain(themeName);
}
}
});
test("themes array has correct length", () => {
// 12 categories × 5 limits = 60 themes
expect(themes.length).toBe(60);
});
test("all themes in array are unique", () => {
const uniqueThemes = new Set(themes);
expect(uniqueThemes.size).toBe(themes.length);
});
test("codename function works with cities themes", () => {
const result10 = codename(123, "cities-10");
const result20 = codename(123, "cities-20");
const result30 = codename(123, "cities-30");
const result50 = codename(123, "cities-50");
const result100 = codename(123, "cities-100");
expect(typeof result10).toBe("string");
expect(typeof result20).toBe("string");
expect(typeof result30).toBe("string");
expect(typeof result50).toBe("string");
expect(typeof result100).toBe("string");
expect(result10.length).toBeGreaterThan(0);
expect(result20.length).toBeGreaterThan(0);
expect(result30.length).toBeGreaterThan(0);
expect(result50.length).toBeGreaterThan(0);
expect(result100.length).toBeGreaterThan(0);
});
test("codename function works with animals themes", () => {
const result10 = codename(456, "animals-10");
const result20 = codename(456, "animals-20");
const result30 = codename(456, "animals-30");
const result50 = codename(456, "animals-50");
const result100 = codename(456, "animals-100");
expect(typeof result10).toBe("string");
expect(typeof result20).toBe("string");
expect(typeof result30).toBe("string");
expect(typeof result50).toBe("string");
expect(typeof result100).toBe("string");
});
test("codename function works with all theme categories", () => {
const testCategories = [
"adjectives",
"animals",
"cities",
"clothing",
"colors",
"countries",
"elements",
"emotions",
"food",
"gems",
"nature",
"snacks",
];
for (const category of testCategories) {
const themeName = `${category}-20` as Theme;
const result = codename(789, themeName);
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
}
});
test("codename function produces consistent results", () => {
const testNumber = 1234;
const theme: Theme = "colors-20";
const result1 = codename(testNumber, theme);
const result2 = codename(testNumber, theme);
const result3 = codename(testNumber, theme);
expect(result1).toBe(result2);
expect(result2).toBe(result3);
});
test("codename function produces different results for different inputs", () => {
const theme: Theme = "nature-30";
const result1 = codename(100, theme);
const result2 = codename(200, theme);
const result3 = codename(300, theme);
// While not guaranteed, it's very likely these will be different
const allSame = result1 === result2 && result2 === result3;
expect(allSame).toBe(false);
});
test("codename function handles edge cases", () => {
const theme: Theme = "food-10";
expect(() => codename(0, theme)).not.toThrow();
expect(() => codename(1, theme)).not.toThrow();
expect(() => codename(999999, theme)).not.toThrow();
const result0 = codename(0, theme);
const result1 = codename(1, theme);
const resultLarge = codename(999999, theme);
expect(typeof result0).toBe("string");
expect(typeof result1).toBe("string");
expect(typeof resultLarge).toBe("string");
});
test("codename function throws error for invalid theme", () => {
expect(() => {
// @ts-expect-error - Testing invalid theme
codename(123, "invalid-theme");
}).toThrow();
expect(() => {
// @ts-expect-error - Testing invalid theme
codename(123, "cities-5");
}).toThrow();
expect(() => {
// @ts-expect-error - Testing invalid theme
codename(123, "nonexistent-20");
}).toThrow();
});
test("error message includes available themes", () => {
try {
// @ts-expect-error - Testing invalid theme
codename(123, "invalid-theme");
expect(true).toBe(false); // Should not reach here
} catch (error) {
expect(error).toBeInstanceOf(Error);
const message = (error as Error).message;
expect(message).toContain("Unknown theme");
expect(message).toContain("Available themes");
expect(message).toContain("cities-20");
expect(message).toContain("animals-10");
}
});
test("Theme type includes all expected theme names", () => {
// This is a compile-time check, but we can verify at runtime too
const sampleThemes: Theme[] = [
"cities-20",
"animals-10",
"colors-50",
"food-100",
"nature-30",
];
for (const theme of sampleThemes) {
expect(themes).toContain(theme);
expect(() => codename(123, theme)).not.toThrow();
}
});
test("different theme sizes produce valid results", () => {
const testNumber = 555;
const result10 = codename(testNumber, "gems-10");
const result20 = codename(testNumber, "gems-20");
const result30 = codename(testNumber, "gems-30");
const result50 = codename(testNumber, "gems-50");
const result100 = codename(testNumber, "gems-100");
expect(typeof result10).toBe("string");
expect(typeof result20).toBe("string");
expect(typeof result30).toBe("string");
expect(typeof result50).toBe("string");
expect(typeof result100).toBe("string");
// All should be valid gem names (lowercase strings)
expect(result10).toMatch(/^[a-z]+$/);
expect(result20).toMatch(/^[a-z]+$/);
expect(result30).toMatch(/^[a-z]+$/);
expect(result50).toMatch(/^[a-z]+$/);
expect(result100).toMatch(/^[a-z]+$/);
});
test("integration with existing theme files", () => {
// Import a specific theme to compare
import("./cities-20.js").then((citiesModule) => {
const testNumber = 888;
const citiesResult = citiesModule.default(testNumber);
const allResult = codename(testNumber, "cities-20");
expect(allResult).toBe(citiesResult);
});
});
test("themes array is properly typed as readonly", () => {
// TypeScript compile-time check - the array should be typed as readonly
// This test verifies the themes array exists and has the expected structure
expect(Array.isArray(themes)).toBe(true);
expect(themes.length).toBe(60);
// Verify all items are strings matching the expected pattern
for (const theme of themes) {
expect(typeof theme).toBe("string");
expect(theme).toMatch(/^[a-z]+-\d+$/);
}
// Note: TypeScript provides compile-time readonly protection,
// but JavaScript arrays are mutable at runtime by design
});
});