- Maps gain worldMap flag, description, freeform weather, latitude/longitude, easeOfAccess and factionControl; picker cards render them as stat tags - resource-nodes gain a shared icon set and may exist with no resources as plain marker nodes (placement ignores marker-only nodes) - sidebar gains the World Map entry; Infrastructure section gets defaultOpen - migrations 20260921_120000/123000/130500/134000; includes the generated types/schema sweep (currency emblem fields land in the next commit)
205 lines
6.4 KiB
TypeScript
205 lines
6.4 KiB
TypeScript
import { describe, it, expect, beforeAll } from "vitest";
|
|
import config from "@payload-config";
|
|
import { getPayload, type Payload } from "payload";
|
|
import type { Campaign, Map as MapDoc } from "@/payload-types";
|
|
import {
|
|
easeStatClass,
|
|
isWorldMapEntry,
|
|
weatherStatClass,
|
|
weatherStatLabel,
|
|
worldMapScreenQuery,
|
|
} from "@/lib/map/screen";
|
|
|
|
const RUN = `wm${Date.now().toString(36)}`;
|
|
|
|
describe("world map screen eligibility", () => {
|
|
let payload: Payload;
|
|
let officialCampaign: Campaign;
|
|
let customCampaign: Campaign;
|
|
let createdIds: number[] = [];
|
|
|
|
const makeMap = async (data: {
|
|
name: string;
|
|
worldMap?: boolean;
|
|
isActive?: boolean;
|
|
campaignId?: number;
|
|
}): Promise<MapDoc> => {
|
|
return (await payload.create({
|
|
collection: "maps",
|
|
data: {
|
|
name: data.name,
|
|
worldSizeWidth: 2048,
|
|
worldSizeHeight: 2048,
|
|
basemapMode: "tiles",
|
|
tileUrlTemplate: "http://localhost/{z}/{x}/{y}.png",
|
|
isActive: data.isActive ?? true,
|
|
worldMap: data.worldMap ?? false,
|
|
...(data.campaignId ? { campaign: data.campaignId } : {}),
|
|
},
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
})) as unknown as MapDoc;
|
|
};
|
|
|
|
beforeAll(async () => {
|
|
payload = await getPayload({ config });
|
|
|
|
officialCampaign = (await payload.create({
|
|
collection: "campaigns",
|
|
data: {
|
|
name: `${RUN} Official`,
|
|
summary: "Eligibility test official campaign.",
|
|
campaignMode: "official",
|
|
status: "inProgress",
|
|
},
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
})) as unknown as Campaign;
|
|
|
|
customCampaign = (await payload.create({
|
|
collection: "campaigns",
|
|
data: {
|
|
name: `${RUN} Custom`,
|
|
summary: "Eligibility test custom campaign.",
|
|
campaignMode: "custom",
|
|
status: "inProgress",
|
|
},
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
})) as unknown as Campaign;
|
|
|
|
createdIds.push(
|
|
(await makeMap({ name: `${RUN} flagged`, worldMap: true })).id,
|
|
(await makeMap({ name: `${RUN} unflagged` })).id,
|
|
(
|
|
await makeMap({
|
|
name: `${RUN} flagged custom campaign`,
|
|
worldMap: true,
|
|
campaignId: customCampaign.id,
|
|
})
|
|
).id,
|
|
(
|
|
await makeMap({
|
|
name: `${RUN} flagged official campaign`,
|
|
worldMap: true,
|
|
campaignId: officialCampaign.id,
|
|
})
|
|
).id,
|
|
(await makeMap({ name: `${RUN} flagged inactive`, worldMap: true, isActive: false })).id,
|
|
);
|
|
});
|
|
|
|
it("lists only flagged, active maps without a custom campaign", async () => {
|
|
const res = await payload.find({
|
|
collection: "maps",
|
|
where: worldMapScreenQuery(),
|
|
limit: 1000,
|
|
depth: 1,
|
|
overrideAccess: true,
|
|
});
|
|
|
|
const listedIds = res.docs.map((m) => m.id);
|
|
|
|
expect(listedIds).toContain(createdIds[0]);
|
|
expect(listedIds).not.toContain(createdIds[1]);
|
|
expect(listedIds).not.toContain(createdIds[2]);
|
|
expect(listedIds).toContain(createdIds[3]);
|
|
expect(listedIds).not.toContain(createdIds[4]);
|
|
|
|
for (const doc of res.docs) {
|
|
expect(isWorldMapEntry(doc as unknown as MapDoc)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("persists the optional intel fields", async () => {
|
|
const faction = (await payload.create({
|
|
collection: "factions",
|
|
data: { name: `${RUN} Faction`, type: "minorFaction", reputation: 0, relationships: [] },
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
})) as unknown as { id: number };
|
|
|
|
const map = (await payload.create({
|
|
collection: "maps",
|
|
data: {
|
|
name: `${RUN} intel fields`,
|
|
worldSizeWidth: 4096,
|
|
worldSizeHeight: 4096,
|
|
basemapMode: "tiles",
|
|
tileUrlTemplate: "http://localhost/{z}/{x}/{y}.png",
|
|
worldMap: true,
|
|
description: "A test map with every intel field.",
|
|
weather: "Ashfall",
|
|
latitude: 35.2,
|
|
longitude: 33.1,
|
|
easeOfAccess: "difficult",
|
|
factionControl: faction.id,
|
|
},
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
})) as unknown as MapDoc;
|
|
|
|
const after = (await payload.findByID({
|
|
collection: "maps",
|
|
id: map.id,
|
|
depth: 1,
|
|
overrideAccess: true,
|
|
})) as unknown as MapDoc;
|
|
|
|
expect(after.description).toBe("A test map with every intel field.");
|
|
expect(after.weather).toBe("Ashfall");
|
|
expect(after.latitude).toBeCloseTo(35.2, 5);
|
|
expect(after.longitude).toBeCloseTo(33.1, 5);
|
|
expect(after.easeOfAccess).toBe("difficult");
|
|
expect(
|
|
typeof after.factionControl === "object" && after.factionControl !== null
|
|
? (after.factionControl as { id: number }).id
|
|
: after.factionControl,
|
|
).toBe(faction.id);
|
|
});
|
|
|
|
it("weather and ease stat helpers color by value and label any text", () => {
|
|
expect(weatherStatClass("Monsoon")).toBe("text-sky-400");
|
|
expect(weatherStatClass("thunderstorm")).toBe("text-violet-400");
|
|
expect(weatherStatClass("Blizzard")).toBe("text-cyan-300");
|
|
expect(weatherStatClass("Dense fog")).toBe("text-slate-400");
|
|
expect(weatherStatClass("Sunny")).toBe("text-amber-400");
|
|
expect(weatherStatClass("Sandstorm")).toBe("text-orange-400");
|
|
expect(weatherStatClass("Ashfall")).toBe("text-stone-400");
|
|
expect(weatherStatClass("Aurora")).toBe("text-muted-foreground");
|
|
expect(weatherStatLabel("ashfall")).toBe("Ashfall");
|
|
expect(weatherStatLabel(" monsoon ")).toBe("Monsoon");
|
|
expect(weatherStatLabel(" ")).toBe("");
|
|
|
|
expect(easeStatClass("easy")).toBe("text-emerald-400");
|
|
expect(easeStatClass("moderate")).toBe("text-amber-400");
|
|
expect(easeStatClass("difficult")).toBe("text-red-400");
|
|
expect(easeStatClass("other")).toBe("text-muted-foreground");
|
|
});
|
|
|
|
it("isWorldMapEntry rejects custom-campaign maps even when flagged", () => {
|
|
expect(
|
|
isWorldMapEntry({
|
|
id: 1,
|
|
name: "x",
|
|
worldMap: true,
|
|
isActive: true,
|
|
campaign: { campaignMode: "custom" },
|
|
}),
|
|
).toBe(false);
|
|
|
|
expect(
|
|
isWorldMapEntry({
|
|
id: 2,
|
|
name: "x",
|
|
worldMap: true,
|
|
isActive: true,
|
|
campaign: { campaignMode: "official" },
|
|
}),
|
|
).toBe(true);
|
|
|
|
expect(isWorldMapEntry({ id: 3, name: "x", worldMap: true, isActive: true })).toBe(true);
|
|
expect(isWorldMapEntry({ id: 4, name: "x", worldMap: false, isActive: true })).toBe(false);
|
|
expect(isWorldMapEntry({ id: 5, name: "x", worldMap: true, isActive: false })).toBe(false);
|
|
});
|
|
});
|