@@ -48,6 +48,34 @@ async function selectTextToSpeechTile(page, itemId) {
4848const TEXT_TO_SPEECH_SAMPLE_PRESET_PATH = "/samples/phase-19/1903/sample.1903.text2speech-V2.json";
4949const TEXT_TO_SPEECH_SAMPLE_QUERY = `?samplePresetPath=${encodeURIComponent(TEXT_TO_SPEECH_SAMPLE_PRESET_PATH)}`;
5050const TEXT_TO_SPEECH_SAMPLE_ITEM_IDS = ["narrator-welcome", "hero-ready", "alert-warning", "alert-warning-2"];
51+ const DEFAULT_MOCK_GAME_OPTIONS = ["Select a game", "Asteroids", "Gravity Well", "Pong"];
52+ const ALL_REPO_GAME_MANIFEST_PATHS = [
53+ "/games/AITargetDummy/game.manifest.json",
54+ "/games/Asteroids/game.manifest.json",
55+ "/games/Bouncing-ball/game.manifest.json",
56+ "/games/Breakout/game.manifest.json",
57+ "/games/GravityWell/game.manifest.json",
58+ "/games/Pacman/game.manifest.json",
59+ "/games/Pong/game.manifest.json",
60+ "/games/SolarSystem/game.manifest.json",
61+ "/games/SpaceDuel/game.manifest.json",
62+ "/games/SpaceInvaders/game.manifest.json",
63+ "/games/vector-arcade-sample/game.manifest.json"
64+ ];
65+ const ALL_REPO_GAME_OPTIONS = [
66+ "Select a game",
67+ "AI Target Dummy",
68+ "Asteroids",
69+ "Bouncing Ball",
70+ "Breakout",
71+ "Gravity Well",
72+ "Pacman",
73+ "Pong",
74+ "Solar System",
75+ "Space Duel",
76+ "Space Invaders Next",
77+ "Vector Arcade Sample"
78+ ];
5179
5280async function openToolsIndex(page) {
5381 const server = await startRepoServer();
@@ -425,8 +453,13 @@ async function installMockRepoPicker(page) {
425453 const gameFolder = parts[1];
426454 const gamePath = `${repoName}/games/${gameFolder}`;
427455 const imageChildren = {};
428- if (typeof config.previewFiles?.[gameFolder] === "string") {
429- imageChildren["preview.svg"] = makeFileHandle("preview.svg", config.previewFiles[gameFolder], `${gamePath}/assets/images/preview.svg`, config);
456+ const previewConfig = config.previewFiles?.[gameFolder];
457+ if (typeof previewConfig === "string") {
458+ imageChildren["preview.svg"] = makeFileHandle("preview.svg", previewConfig, `${gamePath}/assets/images/preview.svg`, config);
459+ } else if (previewConfig && typeof previewConfig === "object" && !Array.isArray(previewConfig)) {
460+ Object.entries(previewConfig).forEach(([previewFileName, contents]) => {
461+ imageChildren[previewFileName] = makeFileHandle(previewFileName, String(contents || ""), `${gamePath}/assets/images/${previewFileName}`, config);
462+ });
430463 }
431464 games[gameFolder] = makeDirectoryHandle(gameFolder, {
432465 assets: makeDirectoryHandle("assets", {
@@ -573,20 +606,15 @@ async function installMockSpeechSynthesis(page, { includeAgeVoice = false, inclu
573606 }, { includeAgeVoice, includeNeutralVoice, voicesInitiallyAvailable });
574607}
575608
576- async function selectMockRepo(page, { repoName = "HTML-JavaScript-Gaming", ...config } = {}) {
609+ async function selectMockRepo(page, { repoName = "HTML-JavaScript-Gaming", expectedGameOptions = DEFAULT_MOCK_GAME_OPTIONS, ...config } = {}) {
577610 await page.evaluate(({ nextConfig, nextRepoName }) => {
578611 window.__workspaceManagerV2MockRepoConfig = { ...nextConfig, repoName: nextRepoName };
579612 }, { nextConfig: { repoPath: process.cwd().replaceAll("\\", "/"), ...config }, nextRepoName: repoName });
580613 await page.locator("#pickRepoBtn").click();
581614 await expect(page.locator("#repoSelectedValue")).toHaveText(repoName);
582615 await expect(page.locator("#activeGameSelect")).toBeEnabled();
583616 await expect(page.locator("#activeGameSelect")).toHaveValue("");
584- await expect(page.locator("#activeGameSelect option")).toHaveText([
585- "Select a game",
586- "Asteroids",
587- "Gravity Well",
588- "Pong"
589- ]);
617+ await expect(page.locator("#activeGameSelect option")).toHaveText(expectedGameOptions);
590618 await expectWorkspaceToolsDisabled(page);
591619}
592620
@@ -8979,8 +9007,6 @@ test.describe("Workspace Manager V2 bootstrap", () => {
89799007 await selectMockRepo(page);
89809008 await page.locator("#activeGameSelect").selectOption("Asteroids");
89819009 await expectWorkspaceReturnRehydrated(page);
8982- expect(await page.evaluate(() => Object.hasOwn(window.__workspaceManagerV2App.activeContext.tools, "text2speech-V2"))).toBe(true);
8983- expect(await page.evaluate(() => JSON.parse(sessionStorage.getItem("workspace.tools.text2speech-V2")).data)).toEqual([]);
89849010 const schemaContract = await page.evaluate(async () => {
89859011 const schema = await fetch("/tools/schemas/tools/text2speech-V2.schema.json", { cache: "no-store" }).then((response) => response.json());
89869012 return {
@@ -9895,6 +9921,53 @@ test.describe("Workspace Manager V2 bootstrap", () => {
98959921 }
98969922 });
98979923
9924+ test("resolves game manifest schema refs from the game schema during repo discovery", async ({ page }) => {
9925+ const server = await openWorkspaceManagerV2(page);
9926+ const pageErrors = [];
9927+
9928+ page.on("pageerror", (error) => {
9929+ pageErrors.push(error.message);
9930+ });
9931+
9932+ try {
9933+ await selectMockRepo(page, {
9934+ expectedGameOptions: ALL_REPO_GAME_OPTIONS,
9935+ manifestPaths: ALL_REPO_GAME_MANIFEST_PATHS
9936+ });
9937+ await expect(page.locator("#statusLog")).toHaveValue(/INFO Discovered 11 schema-valid game manifests from HTML-JavaScript-Gaming\./);
9938+ await expect(page.locator("#statusLog")).toHaveValue(/OK Discovered 11 schema-valid game manifests\./);
9939+ await expect(page.locator("#statusLog")).not.toHaveValue(/unresolved schema reference tools\/asset-manager-v2\.schema\.json/);
9940+ await expect(page.locator("#statusLog")).not.toHaveValue(/unresolved schema reference tools\/palette-manager-v2\.schema\.json/);
9941+ await expect(page.locator("#statusLog")).not.toHaveValue(/SKIP games\/Asteroids\/game\.manifest\.json/);
9942+ await expect(page.locator("#statusLog")).not.toHaveValue(/SKIP games\/AITargetDummy\/game\.manifest\.json/);
9943+
9944+ const discoveredGameIds = await page.locator("#activeGameSelect option").evaluateAll((options) => (
9945+ options.map((option) => option.value).filter(Boolean)
9946+ ));
9947+ expect(discoveredGameIds).toEqual([
9948+ "AITargetDummy",
9949+ "Asteroids",
9950+ "Bouncing-ball",
9951+ "Breakout",
9952+ "GravityWell",
9953+ "Pacman",
9954+ "Pong",
9955+ "SolarSystem",
9956+ "SpaceDuel",
9957+ "SpaceInvaders",
9958+ "vector-arcade-sample"
9959+ ]);
9960+
9961+ await page.locator("#activeGameSelect").selectOption("Asteroids");
9962+ await expect(page.locator("#workspaceContextOutput")).toHaveValue(/"gameId": "Asteroids"/);
9963+ await expect(page.locator("#workspaceContextOutput")).toHaveValue(/"asset-manager-v2"/);
9964+ expect(pageErrors).toEqual([]);
9965+ } finally {
9966+ await coverageReporter.stop(page);
9967+ await server.close();
9968+ }
9969+ });
9970+
98989971 test("uses header lifecycle controls and launches tools from fixed Workspace Manager V2 tiles", async ({ page }) => {
98999972 const server = await openWorkspaceManagerV2(page);
99009973 const pageErrors = [];
@@ -10867,7 +10940,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1086710940 }
1086810941 });
1086910942
10870- test("shows Preview Generator tile status from assets/images/ preview.svg existence", async ({ page }) => {
10943+ test("shows Preview Generator tile status from manifest preview asset existence", async ({ page }) => {
1087110944 const server = await openWorkspaceManagerV2(page);
1087210945 const pageErrors = [];
1087310946
@@ -10882,7 +10955,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1088210955
1088310956 await selectMockRepo(page, {
1088410957 previewFiles: {
10885- Asteroids: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 8 8\"></svg>"
10958+ Asteroids: {
10959+ "preview.png": "mock asteroids preview"
10960+ }
1088610961 }
1088710962 });
1088810963 await page.locator("#activeGameSelect").selectOption("Asteroids");
@@ -11407,7 +11482,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1140711482
1140811483 await page.evaluate(() => {
1140911484 const session = JSON.parse(sessionStorage.getItem("workspace.tools.object-vector-studio-v2"));
11410- session.data.unexpected = "blocked";
11485+ session.data.objects[0]. unexpected = "blocked";
1141111486 session.dirty = {
1141211487 isDirty: true,
1141311488 reason: "object-vector-invalid-save",
@@ -12159,7 +12234,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
1215912234 expect(pongManifest.repoPath).toBe(manifestRepoPath(server));
1216012235 expect(Object.keys(pongManifest.tools).sort()).toEqual(["asset-manager-v2", "palette-manager-v2"]);
1216112236 expect(pongManifest.tools["asset-manager-v2"].assets["assets.image.preview.preview"]).toEqual({
12162- path: "assets/images/preview .svg",
12237+ path: "assets/images/preview1 .svg",
1216312238 type: "image",
1216412239 kind: "svg",
1216512240 role: "preview",
0 commit comments