mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 20:26:44 +00:00
55e8a4edb7
Resolves #2998 ## Description: Adds a news box to the lobby homepage that advertises upcoming clan tournaments, weekly tournaments, and new player tutorials. The component sits above the username input and cycles through items automatically. <img width="1138" height="591" alt="screenshot-2026-03-31_00-48-33" src="https://github.com/user-attachments/assets/4b79287d-6aca-4c81-9bfe-36aad043f381" /> <img width="1107" height="595" alt="screenshot-2026-03-31_00-48-24" src="https://github.com/user-attachments/assets/598e6b8b-e0f2-4864-a5fb-a00c0cc98f37" /> <img width="1367" height="599" alt="screenshot-2026-03-31_00-48-04" src="https://github.com/user-attachments/assets/14f74e70-9dc0-4d67-af6e-c4708e539490" /> ## 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: deathllotus --------- Co-authored-by: Evan <evanpelle@gmail.com>
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import newsItems from "../../../resources/news.json";
|
|
import {
|
|
getVisibleNewsItems,
|
|
NewsItem,
|
|
} from "../../../src/client/components/NewsBox";
|
|
|
|
const DISMISSED_NEWS_KEY = "dismissedNewsItems";
|
|
const allItems = newsItems as NewsItem[];
|
|
|
|
function createMockLocalStorage(): Storage {
|
|
let store: Record<string, string> = {};
|
|
return {
|
|
getItem: (key: string) => store[key] ?? null,
|
|
setItem: (key: string, value: string) => {
|
|
store[key] = String(value);
|
|
},
|
|
removeItem: (key: string) => {
|
|
delete store[key];
|
|
},
|
|
clear: () => {
|
|
store = {};
|
|
},
|
|
get length() {
|
|
return Object.keys(store).length;
|
|
},
|
|
key: (index: number) => Object.keys(store)[index] ?? null,
|
|
};
|
|
}
|
|
|
|
describe("NewsBox", () => {
|
|
beforeEach(() => {
|
|
vi.stubGlobal("localStorage", createMockLocalStorage());
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe("getVisibleNewsItems", () => {
|
|
it("returns all items when none are dismissed", () => {
|
|
const items = getVisibleNewsItems(allItems);
|
|
expect(items.length).toBe(newsItems.length);
|
|
});
|
|
|
|
it("filters out dismissed items", () => {
|
|
const items = getVisibleNewsItems(allItems);
|
|
const firstId = items[0].id;
|
|
localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify([firstId]));
|
|
const filtered = getVisibleNewsItems(allItems);
|
|
expect(filtered.find((i) => i.id === firstId)).toBeUndefined();
|
|
expect(filtered.length).toBe(items.length - 1);
|
|
});
|
|
|
|
it("returns empty when all items are dismissed", () => {
|
|
const allIds = allItems.map((i) => i.id);
|
|
localStorage.setItem(DISMISSED_NEWS_KEY, JSON.stringify(allIds));
|
|
const items = getVisibleNewsItems(allItems);
|
|
expect(items.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("news items structure", () => {
|
|
it("each item has required fields", () => {
|
|
const items = getVisibleNewsItems(allItems);
|
|
for (const item of items) {
|
|
expect(item.id).toBeDefined();
|
|
expect(typeof item.id).toBe("string");
|
|
expect(item.title).toBeDefined();
|
|
expect(typeof item.title).toBe("string");
|
|
expect(item.description).toBeDefined();
|
|
expect(typeof item.description).toBe("string");
|
|
expect(item.type).toBeDefined();
|
|
expect(["tournament", "tutorial", "announcement"]).toContain(item.type);
|
|
}
|
|
});
|
|
|
|
it("each item has a unique id", () => {
|
|
const items = getVisibleNewsItems(allItems);
|
|
const ids = items.map((i) => i.id);
|
|
expect(new Set(ids).size).toBe(ids.length);
|
|
});
|
|
|
|
it("contains a tournament entry", () => {
|
|
const items = getVisibleNewsItems(allItems);
|
|
expect(items.some((i) => i.type === "tournament")).toBe(true);
|
|
});
|
|
});
|
|
});
|