Skip to content

Commit bce65ce

Browse files
refactor: fonts (#1323)
* refactor: add font management module and update global types * fix: declare global declareFont * test: all map loading test
1 parent cff57dc commit bce65ce

7 files changed

Lines changed: 553 additions & 121 deletions

File tree

src/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8501,7 +8501,6 @@
85018501
<script defer src="libs/polylabel.min.js?v1.105.0"></script>
85028502
<script defer src="libs/lineclip.min.js?v1.105.0"></script>
85038503
<script defer src="libs/simplify.js?v1.105.6"></script>
8504-
<script defer src="modules/fonts.js?v=1.99.03"></script>
85058504
<script defer src="modules/ui/layers.js?v=1.111.0"></script>
85068505
<script defer src="modules/ui/measurers.js?v=1.99.00"></script>
85078506
<script defer src="modules/ui/style-presets.js?v=1.100.00"></script>
Lines changed: 151 additions & 118 deletions
Large diffs are not rendered by default.

src/modules/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ import "./religions-generator";
1515
import "./provinces-generator";
1616
import "./emblem";
1717
import "./ice";
18+
import "./fonts";

src/types/global.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ declare global {
4646
var defs: Selection<SVGDefsElement, unknown, null, undefined>;
4747
var coastline: Selection<SVGGElement, unknown, null, undefined>;
4848
var lakes: Selection<SVGGElement, unknown, null, undefined>;
49+
var provs: Selection<SVGGElement, unknown, null, undefined>;
4950
var getColorScheme: (scheme: string | null) => (t: number) => string;
5051
var getColor: (height: number, scheme: (t: number) => string) => string;
5152
var svgWidth: number;
@@ -78,10 +79,12 @@ declare global {
7879
var tip: (
7980
message: string,
8081
autoHide?: boolean,
81-
type?: "info" | "warning" | "error",
82+
type?: "info" | "warn" | "error" | "success",
83+
timeout?: number,
8284
) => void;
8385
var locked: (settingId: string) => boolean;
8486
var unlock: (settingId: string) => void;
8587
var $: (selector: any) => any;
8688
var scale: number;
89+
var changeFont: () => void;
8790
}

src/utils/shorthands.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
export const byId = document.getElementById.bind(document);
1+
export const byId = <T extends HTMLElement>(id: string): T | undefined =>
2+
document.getElementById(id) as T;
23

34
declare global {
45
interface Window {

tests/e2e/load-map.spec.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import { test, expect } from "@playwright/test";
2+
import path from "path";
3+
4+
test.describe("Map loading", () => {
5+
test.beforeEach(async ({ context, page }) => {
6+
await context.clearCookies();
7+
8+
await page.goto("/");
9+
await page.evaluate(() => {
10+
localStorage.clear();
11+
sessionStorage.clear();
12+
});
13+
14+
// Wait for the hidden file input to be available
15+
await page.waitForSelector("#mapToLoad", { state: "attached" });
16+
});
17+
18+
test("should load a saved map file", async ({ page }) => {
19+
// Track errors during map loading
20+
const errors: string[] = [];
21+
page.on("pageerror", (error) => errors.push(`pageerror: ${error.message}`));
22+
page.on("console", (msg) => {
23+
if (msg.type() === "error") {
24+
errors.push(`console.error: ${msg.text()}`);
25+
}
26+
});
27+
28+
// Get the file input element and upload the map file
29+
const fileInput = page.locator("#mapToLoad");
30+
const mapFilePath = path.join(__dirname, "../fixtures/demo.map");
31+
await fileInput.setInputFiles(mapFilePath);
32+
33+
// Wait for map to be fully loaded
34+
// mapId is set at the very end of map loading in showStatistics()
35+
await page.waitForFunction(() => (window as any).mapId !== undefined, {
36+
timeout: 120000,
37+
});
38+
39+
// Additional wait for rendering to settle
40+
await page.waitForTimeout(500);
41+
42+
// Verify map data is loaded
43+
const mapData = await page.evaluate(() => {
44+
const pack = (window as any).pack;
45+
return {
46+
hasStates: pack.states && pack.states.length > 1,
47+
hasBurgs: pack.burgs && pack.burgs.length > 1,
48+
hasCells: pack.cells && pack.cells.i && pack.cells.i.length > 0,
49+
hasRivers: pack.rivers && pack.rivers.length > 0,
50+
mapId: (window as any).mapId,
51+
};
52+
});
53+
54+
expect(mapData.hasStates).toBe(true);
55+
expect(mapData.hasBurgs).toBe(true);
56+
expect(mapData.hasCells).toBe(true);
57+
expect(mapData.hasRivers).toBe(true);
58+
expect(mapData.mapId).toBeDefined();
59+
60+
// Ensure no JavaScript errors occurred during loading
61+
// Filter out expected errors (external resources like Google Analytics, fonts)
62+
const criticalErrors = errors.filter(
63+
(e) =>
64+
!e.includes("fonts.googleapis.com") &&
65+
!e.includes("google-analytics") &&
66+
!e.includes("googletagmanager") &&
67+
!e.includes("Failed to load resource")
68+
);
69+
expect(criticalErrors).toEqual([]);
70+
});
71+
72+
test("loaded map should have correct SVG structure", async ({ page }) => {
73+
const errors: string[] = [];
74+
page.on("pageerror", (error) => errors.push(`pageerror: ${error.message}`));
75+
page.on("console", (msg) => {
76+
if (msg.type() === "error") {
77+
errors.push(`console.error: ${msg.text()}`);
78+
}
79+
});
80+
81+
const fileInput = page.locator("#mapToLoad");
82+
const mapFilePath = path.join(__dirname, "../fixtures/demo.map");
83+
await fileInput.setInputFiles(mapFilePath);
84+
85+
await page.waitForFunction(() => (window as any).mapId !== undefined, {
86+
timeout: 120000,
87+
});
88+
await page.waitForTimeout(500);
89+
90+
// Check essential SVG layers exist
91+
const layers = await page.evaluate(() => {
92+
return {
93+
ocean: !!document.getElementById("ocean"),
94+
lakes: !!document.getElementById("lakes"),
95+
coastline: !!document.getElementById("coastline"),
96+
rivers: !!document.getElementById("rivers"),
97+
borders: !!document.getElementById("borders"),
98+
burgs: !!document.getElementById("burgIcons"),
99+
labels: !!document.getElementById("labels"),
100+
};
101+
});
102+
103+
expect(layers.ocean).toBe(true);
104+
expect(layers.lakes).toBe(true);
105+
expect(layers.coastline).toBe(true);
106+
expect(layers.rivers).toBe(true);
107+
expect(layers.borders).toBe(true);
108+
expect(layers.burgs).toBe(true);
109+
expect(layers.labels).toBe(true);
110+
111+
const criticalErrors = errors.filter(
112+
(e) =>
113+
!e.includes("fonts.googleapis.com") &&
114+
!e.includes("google-analytics") &&
115+
!e.includes("googletagmanager") &&
116+
!e.includes("Failed to load resource")
117+
);
118+
expect(criticalErrors).toEqual([]);
119+
});
120+
121+
test("loaded map should preserve state data", async ({ page }) => {
122+
const errors: string[] = [];
123+
page.on("pageerror", (error) => errors.push(`pageerror: ${error.message}`));
124+
page.on("console", (msg) => {
125+
if (msg.type() === "error") {
126+
errors.push(`console.error: ${msg.text()}`);
127+
}
128+
});
129+
130+
const fileInput = page.locator("#mapToLoad");
131+
const mapFilePath = path.join(__dirname, "../fixtures/demo.map");
132+
await fileInput.setInputFiles(mapFilePath);
133+
134+
await page.waitForFunction(() => (window as any).mapId !== undefined, {
135+
timeout: 120000,
136+
});
137+
await page.waitForTimeout(500);
138+
139+
// Verify states have proper structure
140+
const statesData = await page.evaluate(() => {
141+
const pack = (window as any).pack;
142+
const states = pack.states.filter((s: any) => s.i !== 0); // exclude neutral
143+
144+
return {
145+
count: states.length,
146+
allHaveNames: states.every((s: any) => s.name && s.name.length > 0),
147+
allHaveCells: states.every((s: any) => s.cells > 0),
148+
allHaveArea: states.every((s: any) => s.area > 0),
149+
};
150+
});
151+
152+
expect(statesData.count).toBeGreaterThan(0);
153+
expect(statesData.allHaveNames).toBe(true);
154+
expect(statesData.allHaveCells).toBe(true);
155+
expect(statesData.allHaveArea).toBe(true);
156+
157+
const criticalErrors = errors.filter(
158+
(e) =>
159+
!e.includes("fonts.googleapis.com") &&
160+
!e.includes("google-analytics") &&
161+
!e.includes("googletagmanager") &&
162+
!e.includes("Failed to load resource")
163+
);
164+
expect(criticalErrors).toEqual([]);
165+
});
166+
167+
test("loaded map should preserve burg data", async ({ page }) => {
168+
const errors: string[] = [];
169+
page.on("pageerror", (error) => errors.push(`pageerror: ${error.message}`));
170+
page.on("console", (msg) => {
171+
if (msg.type() === "error") {
172+
errors.push(`console.error: ${msg.text()}`);
173+
}
174+
});
175+
176+
const fileInput = page.locator("#mapToLoad");
177+
const mapFilePath = path.join(__dirname, "../fixtures/demo.map");
178+
await fileInput.setInputFiles(mapFilePath);
179+
180+
await page.waitForFunction(() => (window as any).mapId !== undefined, {
181+
timeout: 120000,
182+
});
183+
await page.waitForTimeout(500);
184+
185+
// Verify burgs have proper structure
186+
const burgsData = await page.evaluate(() => {
187+
const pack = (window as any).pack;
188+
// Filter out placeholder (i=0) and removed burgs (removed=true or no name)
189+
const activeBurgs = pack.burgs.filter(
190+
(b: any) => b.i !== 0 && !b.removed && b.name
191+
);
192+
193+
return {
194+
count: activeBurgs.length,
195+
allHaveNames: activeBurgs.every(
196+
(b: any) => b.name && b.name.length > 0
197+
),
198+
allHaveCoords: activeBurgs.every(
199+
(b: any) => typeof b.x === "number" && typeof b.y === "number"
200+
),
201+
allHaveCells: activeBurgs.every(
202+
(b: any) => typeof b.cell === "number"
203+
),
204+
};
205+
});
206+
207+
expect(burgsData.count).toBeGreaterThan(0);
208+
expect(burgsData.allHaveNames).toBe(true);
209+
expect(burgsData.allHaveCoords).toBe(true);
210+
expect(burgsData.allHaveCells).toBe(true);
211+
212+
const criticalErrors = errors.filter(
213+
(e) =>
214+
!e.includes("fonts.googleapis.com") &&
215+
!e.includes("google-analytics") &&
216+
!e.includes("googletagmanager") &&
217+
!e.includes("Failed to load resource")
218+
);
219+
expect(criticalErrors).toEqual([]);
220+
});
221+
});

tests/fixtures/demo.map

Lines changed: 174 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)