Story 4.2 completed

This commit is contained in:
2026-05-24 00:37:21 +02:00
parent de1b33c453
commit 56eeb7cc83
21 changed files with 3836 additions and 56 deletions
+242 -3
View File
@@ -98,7 +98,7 @@ describe("PlayerPrivacyManager", () => {
});
it("should return saved settings when flag exists", () => {
const savedSettings = { reactionCamEnabled: true, hpReactiveCamStylingEnabled: false };
const savedSettings = { reactionCamEnabled: true, hpReactiveCamStylingEnabled: false, customPortraitFallback: null };
adapter.users.get.mockReturnValue({
getFlag: vi.fn((scope, key) => {
if (scope === "video-view-manager") {
@@ -124,6 +124,7 @@ describe("PlayerPrivacyManager", () => {
expect(result).toEqual({
reactionCamEnabled: true,
hpReactiveCamStylingEnabled: false,
customPortraitFallback: null,
});
});
@@ -294,14 +295,16 @@ describe("PlayerPrivacyManager", () => {
id: "user1",
getFlag: vi.fn((scope, key) => {
if (key === "reactionCamEnabled") return true;
return false;
if (key === "hpReactiveCamStylingEnabled") return false;
return undefined; // customPortraitFallback and other keys
}),
};
const user2 = {
id: "user2",
getFlag: vi.fn((scope, key) => {
if (key === "reactionCamEnabled") return false;
if (key === "hpReactiveCamStylingEnabled") return true;
return false;
return undefined; // customPortraitFallback and other keys
}),
};
adapter.users.all.mockReturnValue([user1, user2]);
@@ -316,10 +319,12 @@ describe("PlayerPrivacyManager", () => {
expect(result.get("user1")).toEqual({
reactionCamEnabled: true,
hpReactiveCamStylingEnabled: false,
customPortraitFallback: null,
});
expect(result.get("user2")).toEqual({
reactionCamEnabled: false,
hpReactiveCamStylingEnabled: true,
customPortraitFallback: null,
});
});
@@ -475,4 +480,238 @@ describe("PlayerPrivacyManager", () => {
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(
"video-view-manager",
"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(
"video-view-manager",
"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;
if (key === "hpReactiveCamStylingEnabled") return false;
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);
});
});
});
});