Files
OpenFrontIO/tests/Colors.test.ts
Restart2008 215511de5d fix: Resolve userSettings is null error in worker
This commit fixes the "userSettings is null" error that occurred in the worker when trying to join or create a game.

- Introduced IUserSettings interface to define the contract for user settings used in the worker.

- Updated UserSettings class to implement IUserSettings and provide a getData() method for serialization.

- Modified WorkerMessages to include serialized user settings in the InitMessage.

- Passed user settings from ClientGameRunner to WorkerClient, and then to the worker.

- Updated createGameRunner to accept IUserSettings and pass it to getConfig.

- Corrected type inconsistencies across various configuration and theme classes to align with IUserSettings.

- Re-added missing imports in relevant files.
2025-10-26 17:44:11 -07:00

198 lines
5.7 KiB
TypeScript

import { colord, Colord } from "colord";
import {
ColorAllocator,
selectDistinctColorIndex,
} from "../src/core/configuration/ColorAllocator";
import {
blue,
botColor,
green,
orange,
purple,
red,
teal,
yellow,
} from "../src/core/configuration/Colors";
import { ColoredTeams } from "../src/core/game/Game";
import { IUserSettings } from "../src/core/game/UserSettings";
const mockColors: Colord[] = [
colord({ r: 255, g: 0, b: 0 }),
colord({ r: 0, g: 255, b: 0 }),
colord({ r: 0, g: 0, b: 255 }),
];
const fallbackMockColors: Colord[] = [
colord({ r: 0, g: 0, b: 0 }),
colord({ r: 255, g: 255, b: 255 }),
];
const fallbackColors = [...fallbackMockColors, ...mockColors];
const mockUserSettings = {
colorblindMode: () => false,
} as IUserSettings;
describe("ColorAllocator", () => {
let allocator: ColorAllocator;
beforeEach(() => {
allocator = new ColorAllocator(
mockColors,
fallbackMockColors,
mockUserSettings,
);
});
test("returns a unique color for each new ID", () => {
const c1 = allocator.assignColor("a");
const c2 = allocator.assignColor("b");
const c3 = allocator.assignColor("c");
expect(c1.isEqual(c2)).toBe(false);
expect(c1.isEqual(c3)).toBe(false);
expect(c2.isEqual(c3)).toBe(false);
});
test("returns the same color for the same ID", () => {
const c1 = allocator.assignColor("a");
const c2 = allocator.assignColor("a");
expect(c1.isEqual(c2)).toBe(true);
});
test("falls back when colors are exhausted", () => {
allocator.assignColor("1");
allocator.assignColor("2");
allocator.assignColor("3");
const fallback = allocator.assignColor("4");
const fallback2 = allocator.assignColor("5");
const match = fallbackColors.some((color) => color.isEqual(fallback));
expect(match).toBe(true);
const match2 = fallback.isEqual(fallback2);
expect(match2).toBe(false);
});
test("assignBotColor returns deterministic color from botColors", () => {
const allocator = new ColorAllocator(
mockColors,
mockColors,
mockUserSettings,
);
const id1 = "bot123";
const id2 = "bot456";
const c1 = allocator.assignColor(id1);
const c2 = allocator.assignColor(id2);
const c1Again = allocator.assignColor(id1);
const c2Again = allocator.assignColor(id2);
expect(c1.isEqual(c1Again)).toBe(true);
expect(c2.isEqual(c2Again)).toBe(true);
});
test("assignTeamColor returns the base color from the team", () => {
expect(allocator.assignTeamColor(ColoredTeams.Blue)).toEqual(blue);
expect(allocator.assignTeamColor(ColoredTeams.Red)).toEqual(red);
expect(allocator.assignTeamColor(ColoredTeams.Teal)).toEqual(teal);
expect(allocator.assignTeamColor(ColoredTeams.Purple)).toEqual(purple);
expect(allocator.assignTeamColor(ColoredTeams.Yellow)).toEqual(yellow);
expect(allocator.assignTeamColor(ColoredTeams.Orange)).toEqual(orange);
expect(allocator.assignTeamColor(ColoredTeams.Green)).toEqual(green);
expect(allocator.assignTeamColor(ColoredTeams.Bot)).toEqual(botColor);
});
test("assignTeamPlayerColor always returns the same color for the same playerID", () => {
const playerId = "player123";
const blueColor1 = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerId,
);
const blueColor2 = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerId,
);
expect(blueColor1.isEqual(blueColor2)).toBe(true);
const redColor1 = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerId,
);
const redColor2 = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerId,
);
expect(redColor1.isEqual(redColor2)).toBe(true);
});
test("assignTeamPlayerColor returns a different color when the playerID is different", () => {
const playerIdOne = "player1";
const playerIdTwo = "player2";
const blueColorPlayerOne = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerIdOne,
);
const blueColorPlayerTwo = allocator.assignTeamPlayerColor(
ColoredTeams.Blue,
playerIdTwo,
);
expect(blueColorPlayerOne.isEqual(blueColorPlayerTwo)).toBe(false);
const redColorPlayerOne = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerIdOne,
);
const redColorPlayerTwo = allocator.assignTeamPlayerColor(
ColoredTeams.Red,
playerIdTwo,
);
expect(redColorPlayerOne.isEqual(redColorPlayerTwo)).toBe(false);
});
test("assignTeamColor returns colorblind-friendly colors when colorblind mode is enabled", () => {
const mockUserSettingsColorblind = {
colorblindMode: () => true,
} as IUserSettings;
const allocator = new ColorAllocator(
mockColors,
fallbackMockColors,
mockUserSettingsColorblind,
);
const redColor = allocator.assignTeamColor(ColoredTeams.Red);
const greenColor = allocator.assignTeamColor(ColoredTeams.Green);
expect(redColor.toHex()).toBe(colord({ h: 30, s: 100, l: 50 }).toHex()); // Orange
expect(greenColor.toHex()).toBe(colord({ h: 210, s: 100, l: 50 }).toHex()); // Blue
});
});
describe("selectDistinctColor", () => {
test("returns the most distant color", () => {
const assignedColors = [colord({ r: 255, g: 0, b: 0 })]; // bright red
const availableColors = [
colord({ r: 254, g: 1, b: 1 }), // too close
colord({ r: 0, g: 255, b: 0 }), // distinct green
colord({ r: 0, g: 0, b: 255 }), // distinct blue
];
const result = selectDistinctColorIndex(availableColors, assignedColors);
expect(result).not.toBeNull();
const rgb = availableColors[result!].toRgb();
expect([
{ r: 0, g: 255, b: 0, a: 1 },
{ r: 0, g: 0, b: 255, a: 1 },
]).toContainEqual(rgb);
});
});