675 lines
23 KiB
JavaScript
675 lines
23 KiB
JavaScript
/**
|
|
* Tests for PlayerPrivacyManager.
|
|
* @module tests/unit/core/PlayerPrivacyManager.test
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { PlayerPrivacyManager } from "../../../src/core/PlayerPrivacyManager.js";
|
|
import { createFoundryAdapterMock } from "../../helpers/foundryAdapterMock.js";
|
|
import { PRIVACY_SETTINGS_DEFAULT } from "../../../src/contracts/privacy-settings.js";
|
|
|
|
describe("PlayerPrivacyManager", () => {
|
|
/** @type {import('../../../src/foundry/FoundryAdapter.js').FoundryAdapter} */
|
|
let adapter;
|
|
/** @type {PlayerPrivacyManager} */
|
|
let manager;
|
|
|
|
beforeEach(() => {
|
|
adapter = createFoundryAdapterMock({
|
|
users: {
|
|
get: vi.fn(),
|
|
all: vi.fn(),
|
|
},
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("constructor", () => {
|
|
it("should construct with valid adapter", () => {
|
|
expect(() => new PlayerPrivacyManager(adapter)).not.toThrow();
|
|
});
|
|
|
|
it("should throw TypeError for null adapter", () => {
|
|
expect(() => new PlayerPrivacyManager(null)).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager(null)).toThrow(
|
|
"PlayerPrivacyManager: adapter argument is required and must be an object"
|
|
);
|
|
});
|
|
|
|
it("should throw TypeError for undefined adapter", () => {
|
|
expect(() => new PlayerPrivacyManager(undefined)).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager(undefined)).toThrow(
|
|
"PlayerPrivacyManager: adapter argument is required and must be an object"
|
|
);
|
|
});
|
|
|
|
it("should throw TypeError for non-object adapter", () => {
|
|
expect(() => new PlayerPrivacyManager("not an object")).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager("not an object")).toThrow(
|
|
"PlayerPrivacyManager: adapter argument is required and must be an object"
|
|
);
|
|
});
|
|
|
|
it("should throw TypeError when adapter.users is not an object", () => {
|
|
const badAdapter = { users: "not an object" };
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(
|
|
"PlayerPrivacyManager: adapter.users must be an object"
|
|
);
|
|
});
|
|
|
|
it("should throw TypeError when adapter.users is null", () => {
|
|
const badAdapter = { users: null };
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(
|
|
"PlayerPrivacyManager: adapter.users must be an object"
|
|
);
|
|
});
|
|
|
|
it("should throw TypeError when adapter.users.get is not a function", () => {
|
|
const badAdapter = { users: { get: "not a function", all: vi.fn() } };
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(
|
|
"PlayerPrivacyManager: adapter.users.get must be a function"
|
|
);
|
|
});
|
|
|
|
it("should throw TypeError when adapter.users.all is not a function", () => {
|
|
const badAdapter = { users: { get: vi.fn(), all: "not a function" } };
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(TypeError);
|
|
expect(() => new PlayerPrivacyManager(badAdapter)).toThrow(
|
|
"PlayerPrivacyManager: adapter.users.all must be a function"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("getSettings", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
it("should return default settings when no flag exists", () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
const result = manager.getSettings("user1");
|
|
expect(result).toEqual(PRIVACY_SETTINGS_DEFAULT);
|
|
});
|
|
|
|
it("should return saved settings when flag exists", () => {
|
|
const savedSettings = { reactionCamEnabled: true, customPortraitFallback: null };
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (scope === "scrying-pool") {
|
|
return savedSettings[key];
|
|
}
|
|
return undefined;
|
|
}),
|
|
});
|
|
const result = manager.getSettings("user1");
|
|
expect(result).toEqual(savedSettings);
|
|
});
|
|
|
|
it("should return partial settings merged with defaults", () => {
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (scope === "scrying-pool" && key === "reactionCamEnabled") {
|
|
return true;
|
|
}
|
|
return undefined;
|
|
}),
|
|
});
|
|
const result = manager.getSettings("user1");
|
|
expect(result).toEqual({
|
|
reactionCamEnabled: true,
|
|
customPortraitFallback: null,
|
|
});
|
|
});
|
|
|
|
it("should handle null user gracefully", () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
const result = manager.getSettings("nonexistent");
|
|
expect(result).toEqual(PRIVACY_SETTINGS_DEFAULT);
|
|
});
|
|
|
|
it("should handle user without getFlag method", () => {
|
|
adapter.users.get.mockReturnValue({});
|
|
const result = manager.getSettings("user1");
|
|
expect(result).toEqual(PRIVACY_SETTINGS_DEFAULT);
|
|
});
|
|
});
|
|
|
|
describe("setSetting", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
it("should validate key is known", async () => {
|
|
adapter.users.get.mockReturnValue({
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
});
|
|
await expect(
|
|
manager.setSetting("user1", "invalidKey", true)
|
|
).rejects.toThrow(TypeError);
|
|
await expect(
|
|
manager.setSetting("user1", "invalidKey", true)
|
|
).rejects.toThrow("unknown key");
|
|
});
|
|
|
|
it("should validate value is boolean", async () => {
|
|
adapter.users.get.mockReturnValue({
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
});
|
|
await expect(
|
|
manager.setSetting("user1", "reactionCamEnabled", "not boolean")
|
|
).rejects.toThrow(TypeError);
|
|
await expect(
|
|
manager.setSetting("user1", "reactionCamEnabled", "not boolean")
|
|
).rejects.toThrow("value must be a boolean");
|
|
});
|
|
|
|
it("should call adapter.users.get with userId", async () => {
|
|
const mockUser = { setFlag: vi.fn().mockResolvedValue(undefined) };
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
await manager.setSetting("user1", "reactionCamEnabled", true);
|
|
expect(adapter.users.get).toHaveBeenCalledWith("user1");
|
|
});
|
|
|
|
it("should call user.setFlag with correct parameters", async () => {
|
|
const mockUser = { setFlag: vi.fn().mockResolvedValue(undefined) };
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
await manager.setSetting("user1", "reactionCamEnabled", true);
|
|
expect(mockUser.setFlag).toHaveBeenCalledWith(
|
|
"scrying-pool",
|
|
"reactionCamEnabled",
|
|
true
|
|
);
|
|
});
|
|
|
|
it("should throw when user does not exist", async () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
await expect(
|
|
manager.setSetting("nonexistent", "reactionCamEnabled", true)
|
|
).rejects.toThrow(TypeError);
|
|
await expect(
|
|
manager.setSetting("nonexistent", "reactionCamEnabled", true)
|
|
).rejects.toThrow("User 'nonexistent' not found");
|
|
});
|
|
|
|
it("should throw when user does not have setFlag method", async () => {
|
|
adapter.users.get.mockReturnValue({});
|
|
await expect(
|
|
manager.setSetting("user1", "reactionCamEnabled", true)
|
|
).rejects.toThrow(TypeError);
|
|
await expect(
|
|
manager.setSetting("user1", "reactionCamEnabled", true)
|
|
).rejects.toThrow("User 'user1' does not support setFlag");
|
|
});
|
|
|
|
it("should emit change event after successful update", async () => {
|
|
const mockUser = { setFlag: vi.fn().mockResolvedValue(undefined) };
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
// First call to getSettings returns false (default), then we set to true
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn(() => false),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
});
|
|
const callback = vi.fn();
|
|
manager.onChange(callback);
|
|
await manager.setSetting("user1", "reactionCamEnabled", true);
|
|
expect(callback).toHaveBeenCalledWith("user1", "reactionCamEnabled", true, false);
|
|
});
|
|
});
|
|
|
|
describe("isOptedIn", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
it("should throw for unknown feature", () => {
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn(() => false),
|
|
});
|
|
expect(() => manager.isOptedIn("user1", "invalidFeature")).toThrow(TypeError);
|
|
expect(() => manager.isOptedIn("user1", "invalidFeature")).toThrow("unknown feature");
|
|
});
|
|
|
|
it("should return true when setting is enabled", () => {
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "reactionCamEnabled") return true;
|
|
return false;
|
|
}),
|
|
});
|
|
expect(manager.isOptedIn("user1", "reactionCam")).toBe(true);
|
|
});
|
|
|
|
it("should return false when setting is disabled", () => {
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "reactionCamEnabled") return false;
|
|
return false;
|
|
}),
|
|
});
|
|
expect(manager.isOptedIn("user1", "reactionCam")).toBe(false);
|
|
});
|
|
|
|
it("should return false when setting is not found (defaults to false)", () => {
|
|
adapter.users.get.mockReturnValue({
|
|
getFlag: vi.fn(() => undefined),
|
|
});
|
|
expect(manager.isOptedIn("user1", "reactionCam")).toBe(false);
|
|
});
|
|
|
|
it("should return false for non-existent user", () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
expect(manager.isOptedIn("nonexistent", "reactionCam")).toBe(false);
|
|
});
|
|
|
|
});
|
|
|
|
describe("getAllSettings", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
it("should return empty Map when no users", () => {
|
|
adapter.users.all.mockReturnValue([]);
|
|
const result = manager.getAllSettings();
|
|
expect(result).toEqual(new Map());
|
|
});
|
|
|
|
it("should aggregate settings from all users", () => {
|
|
const user1 = {
|
|
id: "user1",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "reactionCamEnabled") return true;
|
|
return undefined; // customPortraitFallback and other keys
|
|
}),
|
|
};
|
|
const user2 = {
|
|
id: "user2",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "reactionCamEnabled") return false;
|
|
return undefined; // customPortraitFallback and other keys
|
|
}),
|
|
};
|
|
adapter.users.all.mockReturnValue([user1, user2]);
|
|
adapter.users.get.mockImplementation((id) => {
|
|
if (id === "user1") return user1;
|
|
if (id === "user2") return user2;
|
|
return null;
|
|
});
|
|
|
|
const result = manager.getAllSettings();
|
|
expect(result.size).toBe(2);
|
|
expect(result.get("user1")).toEqual({
|
|
reactionCamEnabled: true,
|
|
customPortraitFallback: null,
|
|
});
|
|
expect(result.get("user2")).toEqual({
|
|
reactionCamEnabled: false,
|
|
customPortraitFallback: null,
|
|
});
|
|
});
|
|
|
|
it("should skip users without getFlag method", () => {
|
|
const user1 = {
|
|
id: "user1",
|
|
getFlag: vi.fn(() => true),
|
|
};
|
|
const user2 = { id: "user2" };
|
|
adapter.users.all.mockReturnValue([user1, user2]);
|
|
adapter.users.get.mockImplementation((id) => {
|
|
if (id === "user1") return user1;
|
|
if (id === "user2") return user2;
|
|
return null;
|
|
});
|
|
|
|
const result = manager.getAllSettings();
|
|
expect(result.size).toBe(1);
|
|
expect(result.has("user1")).toBe(true);
|
|
expect(result.has("user2")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("onChange", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
it("should allow multiple subscribers", () => {
|
|
const callback1 = vi.fn();
|
|
const callback2 = vi.fn();
|
|
manager.onChange(callback1);
|
|
manager.onChange(callback2);
|
|
|
|
const mockUser = { setFlag: vi.fn().mockResolvedValue(undefined) };
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
return manager.setSetting("user1", "reactionCamEnabled", true).then(() => {
|
|
expect(callback1).toHaveBeenCalled();
|
|
expect(callback2).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("should pass correct parameters to subscribers", async () => {
|
|
const callback = vi.fn();
|
|
manager.onChange(callback);
|
|
|
|
const mockUser = {
|
|
getFlag: vi.fn(() => false),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
await manager.setSetting("user1", "reactionCamEnabled", true);
|
|
expect(callback).toHaveBeenCalledWith("user1", "reactionCamEnabled", true, false);
|
|
});
|
|
|
|
it("should allow unsubscribing", async () => {
|
|
const callback = vi.fn();
|
|
const unsubscribe = manager.onChange(callback);
|
|
|
|
const mockUser = {
|
|
getFlag: vi.fn(() => false),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
// Subscribe, unsubscribe, then set setting
|
|
unsubscribe();
|
|
await manager.setSetting("user1", "reactionCamEnabled", true);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("Integration scenarios", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
it("should handle player enabling Reaction Cam", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => false),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
// Initially opted out
|
|
expect(manager.isOptedIn("player1", "reactionCam")).toBe(false);
|
|
|
|
// Enable Reaction Cam
|
|
await manager.setSetting("player1", "reactionCamEnabled", true);
|
|
// After setting, the mock should return true for reactionCamEnabled
|
|
adapter.users.get.mockReturnValue({
|
|
id: "player1",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "reactionCamEnabled") return true;
|
|
return false;
|
|
}),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
});
|
|
expect(manager.isOptedIn("player1", "reactionCam")).toBe(true);
|
|
});
|
|
|
|
it("should allow GM to view all players' settings", () => {
|
|
const gm = { id: "gm1", isGM: true, getFlag: vi.fn(() => false) };
|
|
const player1 = {
|
|
id: "player1",
|
|
getFlag: vi.fn((scope, key) => (key === "reactionCamEnabled" ? true : false)),
|
|
};
|
|
const player2 = {
|
|
id: "player2",
|
|
getFlag: vi.fn((scope, key) => (key === "reactionCamEnabled" ? false : false)),
|
|
};
|
|
|
|
adapter.users.all.mockReturnValue([gm, player1, player2]);
|
|
adapter.users.get.mockImplementation((id) => {
|
|
if (id === "gm1") return gm;
|
|
if (id === "player1") return player1;
|
|
if (id === "player2") return player2;
|
|
return null;
|
|
});
|
|
|
|
const allSettings = manager.getAllSettings();
|
|
expect(allSettings.size).toBe(3);
|
|
});
|
|
});
|
|
|
|
// ==================== PORTRAIT FALLBACK TESTS ====================
|
|
|
|
describe("portrait fallback methods", () => {
|
|
beforeEach(() => {
|
|
manager = new PlayerPrivacyManager(adapter);
|
|
});
|
|
|
|
describe("setPortraitFallback", () => {
|
|
it("should validate DataURL format", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => null),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const invalidDataURL = "https://example.com/image.png";
|
|
await expect(
|
|
manager.setPortraitFallback("player1", invalidDataURL)
|
|
).rejects.toThrow(TypeError);
|
|
});
|
|
|
|
it("should accept valid PNG DataURL", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => null),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
unsetFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const dataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg==";
|
|
await expect(
|
|
manager.setPortraitFallback("player1", dataURL)
|
|
).resolves.not.toThrow();
|
|
|
|
expect(mockUser.setFlag).toHaveBeenCalledWith(
|
|
"scrying-pool",
|
|
"customPortraitFallback",
|
|
dataURL
|
|
);
|
|
});
|
|
|
|
it("should emit change event with type 'portrait'", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => null),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
unsetFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const changeCallback = vi.fn();
|
|
manager.onChange(changeCallback);
|
|
|
|
const dataURL = "data:image/png;base64,test";
|
|
await manager.setPortraitFallback("player1", dataURL);
|
|
|
|
expect(changeCallback).toHaveBeenCalled();
|
|
// Check that the callback was called with portrait-related parameters
|
|
const calls = changeCallback.mock.calls;
|
|
expect(calls.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("should reject invalid MIME type", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => null),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
unsetFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const svgDataURL = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==";
|
|
await expect(
|
|
manager.setPortraitFallback("player1", svgDataURL)
|
|
).rejects.toThrow(TypeError);
|
|
});
|
|
|
|
it("should throw TypeError for non-existent user", async () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
|
|
const dataURL = "data:image/png;base64,test";
|
|
await expect(
|
|
manager.setPortraitFallback("nonexistent", dataURL)
|
|
).rejects.toThrow(TypeError);
|
|
});
|
|
});
|
|
|
|
describe("getPortraitFallback", () => {
|
|
it("should return null when no custom portrait is set", () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "customPortraitFallback") return undefined;
|
|
return undefined;
|
|
}),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const result = manager.getPortraitFallback("player1");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return DataURL when custom portrait is set", () => {
|
|
const dataURL = "data:image/png;base64,test123";
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "customPortraitFallback") return dataURL;
|
|
return undefined;
|
|
}),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const result = manager.getPortraitFallback("player1");
|
|
expect(result).toBe(dataURL);
|
|
});
|
|
|
|
it("should return null for non-existent user", () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
|
|
const result = manager.getPortraitFallback("nonexistent");
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("getPortraitFallbackDataURL", () => {
|
|
it("should return DataURL directly", () => {
|
|
const dataURL = "data:image/png;base64,test123";
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "customPortraitFallback") return dataURL;
|
|
return undefined;
|
|
}),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const result = manager.getPortraitFallbackDataURL("player1");
|
|
expect(result).toBe(dataURL);
|
|
});
|
|
|
|
it("should return null when not set", () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const result = manager.getPortraitFallbackDataURL("player1");
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("removePortraitFallback", () => {
|
|
it("should call unsetFlag for customPortraitFallback", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => "data:image/png;base64,old"),
|
|
unsetFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
await manager.removePortraitFallback("player1");
|
|
|
|
expect(mockUser.unsetFlag).toHaveBeenCalledWith(
|
|
"scrying-pool",
|
|
"customPortraitFallback"
|
|
);
|
|
});
|
|
|
|
it("should emit change event with type 'portrait'", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => "data:image/png;base64,old"),
|
|
unsetFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const changeCallback = vi.fn();
|
|
manager.onChange(changeCallback);
|
|
|
|
await manager.removePortraitFallback("player1");
|
|
|
|
expect(changeCallback).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should throw TypeError for non-existent user", async () => {
|
|
adapter.users.get.mockReturnValue(null);
|
|
|
|
await expect(
|
|
manager.removePortraitFallback("nonexistent")
|
|
).rejects.toThrow(TypeError);
|
|
});
|
|
});
|
|
|
|
describe("getSettings with portrait fallback", () => {
|
|
it("should include customPortraitFallback in returned settings", () => {
|
|
const dataURL = "data:image/png;base64,test";
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn((scope, key) => {
|
|
if (key === "customPortraitFallback") return dataURL;
|
|
if (key === "reactionCamEnabled") return true;
|
|
return undefined;
|
|
}),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const settings = manager.getSettings("player1");
|
|
expect(settings).toHaveProperty("customPortraitFallback");
|
|
expect(settings.customPortraitFallback).toBe(dataURL);
|
|
});
|
|
});
|
|
|
|
describe("setSetting rejection of customPortraitFallback", () => {
|
|
it("should reject customPortraitFallback key in setSetting", async () => {
|
|
const mockUser = {
|
|
id: "player1",
|
|
getFlag: vi.fn(() => null),
|
|
setFlag: vi.fn().mockResolvedValue(undefined),
|
|
};
|
|
adapter.users.get.mockReturnValue(mockUser);
|
|
|
|
const dataURL = "data:image/png;base64,test";
|
|
await expect(
|
|
manager.setSetting("player1", "customPortraitFallback", dataURL)
|
|
).rejects.toThrow(TypeError);
|
|
});
|
|
});
|
|
});
|
|
});
|