pack.test.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
  2. import { existsSync } from "node:fs";
  3. import { tmpdir } from "node:os";
  4. import { dirname, join, resolve } from "node:path";
  5. import { describe, expect, it } from "vitest";
  6. import { runEditorPack } from "../src/pack.js";
  7. const FIXTURE_FILES: Record<string, string> = {
  8. "content/time.yaml": [
  9. "id: default",
  10. "dayLengthTicks: 240",
  11. "seasons:",
  12. " - { name: spring, dayCount: 14 }",
  13. "start: { day: 1, season: spring }",
  14. "",
  15. ].join("\n"),
  16. "content/maps/farm.yaml": [
  17. "id: farm",
  18. "width: 12",
  19. "height: 12",
  20. "tilesetRef: farm-tiles",
  21. "spawn: { x: 2, y: 2 }",
  22. "walkable:",
  23. " water: false",
  24. "layers:",
  25. " - name: ground",
  26. " data:",
  27. ...mapRows(),
  28. "",
  29. ].join("\n"),
  30. "content/items/hoe.yaml": [
  31. "id: hoe",
  32. "name: Hoe",
  33. "kind: tool",
  34. "stackable: false",
  35. "price: 100",
  36. "use: tills a soil tile",
  37. "",
  38. ].join("\n"),
  39. };
  40. function mapRows(): string[] {
  41. const rows: string[] = [];
  42. for (let y = 0; y < 12; y++) {
  43. const row: string[] = [];
  44. for (let x = 0; x < 12; x++) {
  45. row.push(x < 2 && y < 2 ? "water" : "grass");
  46. }
  47. rows.push(` - [${row.join(", ")}]`);
  48. }
  49. return rows;
  50. }
  51. async function fixtureDir(): Promise<string> {
  52. const dir = await mkdtemp(join(tmpdir(), "editor-pack-"));
  53. for (const [rel, content] of Object.entries(FIXTURE_FILES)) {
  54. const target = join(dir, rel);
  55. await mkdir(dirname(target), { recursive: true });
  56. await writeFile(target, content, "utf8");
  57. }
  58. return dir;
  59. }
  60. async function rmRecursive(dir: string): Promise<void> {
  61. await rm(dir, { recursive: true, force: true });
  62. }
  63. function options(overrides: Partial<{ desktop: boolean; installer: boolean; mods: boolean; splitAssets: boolean }> = {}): Parameters<typeof runEditorPack>[1] {
  64. return {
  65. name: "Pinefield Valley",
  66. icon: "",
  67. desktop: overrides.desktop ?? false,
  68. installer: overrides.installer ?? false,
  69. mods: overrides.mods ?? false,
  70. splitAssets: overrides.splitAssets ?? false,
  71. outDir: "",
  72. };
  73. }
  74. describe("runEditorPack (E7 package dialog pipeline)", () => {
  75. it("packs a minimal fixture project into a static site + bundle.json", async () => {
  76. const dir = await fixtureDir();
  77. try {
  78. const result = await runEditorPack(dir, options(), existsSync(join(dir, "mods")));
  79. expect(result.ok).toBe(true);
  80. if (!result.ok) return;
  81. expect(result.outDir).toBe(resolve(dir, "dist-pack"));
  82. expect(existsSync(join(result.outDir, "bundle.json"))).toBe(true);
  83. expect(existsSync(join(result.outDir, "index.html"))).toBe(true);
  84. expect(existsSync(join(result.outDir, "assets"))).toBe(true);
  85. expect(result.stats.kinds).toBeGreaterThan(0);
  86. expect(result.stats.defCount).toBeGreaterThan(0);
  87. expect(result.stats.desktop).toBe(false);
  88. expect(result.stats.installer).toBe(false);
  89. } finally {
  90. await rmRecursive(dir);
  91. }
  92. });
  93. it("desktop mode writes the Electron shell (main.cjs + package.json)", async () => {
  94. const dir = await fixtureDir();
  95. try {
  96. const result = await runEditorPack(dir, options({ desktop: true }), existsSync(join(dir, "mods")));
  97. expect(result.ok).toBe(true);
  98. if (!result.ok) return;
  99. expect(result.stats.desktop).toBe(true);
  100. expect(existsSync(join(result.outDir, "desktop", "main.cjs"))).toBe(true);
  101. expect(existsSync(join(result.outDir, "desktop", "package.json"))).toBe(true);
  102. expect(existsSync(join(result.outDir, "desktop", "README-desktop.md"))).toBe(true);
  103. } finally {
  104. await rmRecursive(dir);
  105. }
  106. });
  107. it("installer mode writes electron-builder.yml and copies the site into desktop/", async () => {
  108. const dir = await fixtureDir();
  109. try {
  110. const result = await runEditorPack(dir, options({ installer: true }), existsSync(join(dir, "mods")));
  111. expect(result.ok).toBe(true);
  112. if (!result.ok) return;
  113. expect(result.stats.installer).toBe(true);
  114. expect(result.stats.desktop).toBe(true);
  115. expect(existsSync(join(result.outDir, "desktop", "electron-builder.yml"))).toBe(true);
  116. expect(existsSync(join(result.outDir, "desktop", "index.html"))).toBe(true);
  117. expect(existsSync(join(result.outDir, "desktop", "bundle.json"))).toBe(true);
  118. expect(existsSync(join(result.outDir, "desktop", "assets"))).toBe(true);
  119. } finally {
  120. await rmRecursive(dir);
  121. }
  122. });
  123. it("surfaces a content-compile failure as {ok: false, error} with the per-def message", async () => {
  124. const dir = await mkdtemp(join(tmpdir(), "editor-pack-bad-"));
  125. try {
  126. await mkdir(join(dir, "content", "maps"), { recursive: true });
  127. await writeFile(join(dir, "content", "maps", "bad.yaml"), ["id: bad", "width: nope", "height: 2", ""].join("\n"));
  128. const result = await runEditorPack(dir, options(), false);
  129. expect(result.ok).toBe(false);
  130. if (result.ok) return;
  131. expect(result.error).toContain("validation failed");
  132. expect(result.error).toContain("map/bad");
  133. } finally {
  134. await rmRecursive(dir);
  135. }
  136. });
  137. });