mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 20:25:35 +00:00
7b785ea79a
## Description: NOTE: Applies to current main / beta version. Needs to be included in v30. When a player clicked "Renew Alliance", the `AllianceExtensionUpdate` broadcast caused both players' renewal prompts to be removed, even the one who hadn't yet acted. This happened because `onAllianceExtensionEvent` called `removeAllianceRenewalEvents` unconditionally on every client. This PR fixes the behavior by calling `removeAllianceRenewalEvents` only for the player that executed the action. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: deshack_82603
212 lines
5.7 KiB
TypeScript
212 lines
5.7 KiB
TypeScript
import { GameUpdateType } from "../../../../src/core/game/GameUpdates";
|
||
|
||
vi.mock("lit", () => ({
|
||
html: () => {},
|
||
LitElement: class {},
|
||
}));
|
||
|
||
vi.mock("lit/decorators.js", () => ({
|
||
customElement: () => (clazz: any) => clazz,
|
||
query: () => () => {},
|
||
state: () => () => {},
|
||
property: () => () => {},
|
||
}));
|
||
|
||
vi.mock("lit/directive.js", () => ({
|
||
DirectiveResult: class {},
|
||
}));
|
||
|
||
vi.mock("lit/directives/unsafe-html.js", () => ({
|
||
unsafeHTML: () => {},
|
||
UnsafeHTMLDirective: class {},
|
||
}));
|
||
|
||
import { EventsDisplay } from "../../../../src/client/graphics/layers/EventsDisplay";
|
||
import { MessageType } from "../../../../src/core/game/Game";
|
||
|
||
describe("EventsDisplay - alliance renewal cleanup (allianceID based)", () => {
|
||
function makeRenewal(
|
||
allianceID: number,
|
||
focusID: number,
|
||
description = "Alliance about to expire",
|
||
) {
|
||
return {
|
||
description,
|
||
type: MessageType.RENEW_ALLIANCE,
|
||
allianceID,
|
||
focusID,
|
||
createdAt: 0,
|
||
};
|
||
}
|
||
|
||
test("removes ONLY renewal events for the broken alliance", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
const allianceAB = 1;
|
||
const allianceAC = 2;
|
||
const allianceBC = 3;
|
||
|
||
(display as any).events = [
|
||
makeRenewal(allianceAB, 1), // A–B
|
||
makeRenewal(allianceAC, 1), // A–C
|
||
makeRenewal(allianceBC, 2), // B–C
|
||
];
|
||
|
||
// Break alliance A–B
|
||
(display as any).removeAllianceRenewalEvents(allianceAB);
|
||
|
||
const remaining = (display as any).events;
|
||
|
||
// A–B renewal removed
|
||
expect(remaining.some((e: any) => e.allianceID === allianceAB)).toBe(false);
|
||
|
||
// Other alliances untouched
|
||
expect(remaining.some((e: any) => e.allianceID === allianceAC)).toBe(true);
|
||
|
||
expect(remaining.some((e: any) => e.allianceID === allianceBC)).toBe(true);
|
||
});
|
||
|
||
test("does NOT remove renewals just because the same player is involved", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
const allianceAB = 10;
|
||
const allianceAC = 11;
|
||
|
||
(display as any).events = [
|
||
makeRenewal(allianceAB, 1), // Player 1 involved
|
||
makeRenewal(allianceAC, 1), // Same player, different alliance
|
||
];
|
||
|
||
(display as any).removeAllianceRenewalEvents(allianceAB);
|
||
|
||
const remaining = (display as any).events;
|
||
|
||
expect(remaining.length).toBe(1);
|
||
expect(remaining[0].allianceID).toBe(allianceAC);
|
||
});
|
||
|
||
test("breaking one alliance does not affect renewals between other players", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
const allianceAB = 100;
|
||
const allianceCD = 200;
|
||
|
||
(display as any).events = [
|
||
makeRenewal(allianceAB, 1), // A–B
|
||
makeRenewal(allianceCD, 3), // C–D
|
||
];
|
||
|
||
(display as any).removeAllianceRenewalEvents(allianceAB);
|
||
|
||
const remaining = (display as any).events;
|
||
|
||
expect(remaining.length).toBe(1);
|
||
expect(remaining[0].allianceID).toBe(allianceCD);
|
||
});
|
||
|
||
test("onAllianceExtensionEvent removes renewal when playerID matches myPlayer", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
const allianceID = 42;
|
||
const mySmallID = 7;
|
||
|
||
(display as any).game = {
|
||
myPlayer: () => ({ smallID: () => mySmallID }),
|
||
};
|
||
(display as any).requestUpdate = () => {};
|
||
(display as any).events = [makeRenewal(allianceID, mySmallID)];
|
||
|
||
(display as any).onAllianceExtensionEvent({
|
||
type: GameUpdateType.AllianceExtension,
|
||
playerID: mySmallID,
|
||
allianceID,
|
||
});
|
||
|
||
const remaining = (display as any).events;
|
||
expect(remaining.some((e: any) => e.allianceID === allianceID)).toBe(false);
|
||
});
|
||
|
||
test("onAllianceExtensionEvent keeps renewal when playerID does not match myPlayer", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
const allianceID = 42;
|
||
const mySmallID = 7;
|
||
const otherSmallID = 9;
|
||
|
||
(display as any).game = {
|
||
myPlayer: () => ({ smallID: () => mySmallID }),
|
||
};
|
||
(display as any).requestUpdate = () => {};
|
||
(display as any).events = [makeRenewal(allianceID, mySmallID)];
|
||
|
||
(display as any).onAllianceExtensionEvent({
|
||
type: "AllianceExtension",
|
||
playerID: otherSmallID,
|
||
allianceID,
|
||
});
|
||
|
||
const remaining = (display as any).events;
|
||
expect(remaining.some((e: any) => e.allianceID === allianceID)).toBe(true);
|
||
});
|
||
|
||
test("onAllianceExtensionEvent keeps renewal when myPlayer is null", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
const allianceID = 42;
|
||
|
||
(display as any).game = {
|
||
myPlayer: () => null,
|
||
};
|
||
(display as any).requestUpdate = () => {};
|
||
(display as any).events = [makeRenewal(allianceID, 1)];
|
||
|
||
(display as any).onAllianceExtensionEvent({
|
||
type: "AllianceExtension",
|
||
playerID: 1,
|
||
allianceID,
|
||
});
|
||
|
||
const remaining = (display as any).events;
|
||
expect(remaining.some((e: any) => e.allianceID === allianceID)).toBe(true);
|
||
});
|
||
|
||
test("does not affect non-RENEW_ALLIANCE events", () => {
|
||
const display = new EventsDisplay();
|
||
|
||
(display as any).events = [
|
||
{
|
||
description: "Alliance broken",
|
||
type: MessageType.ALLIANCE_BROKEN,
|
||
createdAt: 0,
|
||
},
|
||
{
|
||
description: "Alliance accepted",
|
||
type: MessageType.ALLIANCE_ACCEPTED,
|
||
createdAt: 0,
|
||
},
|
||
{
|
||
description: "Renewal",
|
||
type: MessageType.RENEW_ALLIANCE,
|
||
allianceID: 999,
|
||
createdAt: 0,
|
||
},
|
||
];
|
||
|
||
(display as any).removeAllianceRenewalEvents(999);
|
||
|
||
const remaining = (display as any).events;
|
||
|
||
expect(
|
||
remaining.some((e: any) => e.type === MessageType.ALLIANCE_BROKEN),
|
||
).toBe(true);
|
||
|
||
expect(
|
||
remaining.some((e: any) => e.type === MessageType.ALLIANCE_ACCEPTED),
|
||
).toBe(true);
|
||
|
||
expect(
|
||
remaining.some((e: any) => e.type === MessageType.RENEW_ALLIANCE),
|
||
).toBe(false);
|
||
});
|
||
});
|