diff --git a/scripts/mobile-showcase.test.ts b/scripts/mobile-showcase.test.ts index 7e6c9e0f0e6df..25e926fcda5e4 100644 --- a/scripts/mobile-showcase.test.ts +++ b/scripts/mobile-showcase.test.ts @@ -1,13 +1,23 @@ -// @effect-diagnostics nodeBuiltinImport:off - expectations mirror the host path joins the script under test performs. +// @effect-diagnostics nodeBuiltinImport:off - Clears capture outputs in a real temporary folder. +import * as NodeFSP from "node:fs/promises"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; + import { assert, it } from "@effect/vitest"; import { PNG } from "pngjs"; -import type { ShowcaseStoreAssetSpec } from "./mobile-showcase.config.ts"; +import showcaseConfig, { + type ShowcaseScene, + type ShowcaseStoreAssetSpec, +} from "./mobile-showcase.config.ts"; import { + clearShowcaseCaptureOutputs, encodeAndroidPairingUrls, normalizeStorePng, parsePairingCredentialOutput, + planShowcaseCaptures, readPngMetadata, + showcaseCaptureDirectory, validateStoreAsset, } from "./mobile-showcase.ts"; @@ -91,3 +101,40 @@ it("encodes Android pairing URLs without shell-sensitive JSON quotes", () => { assert.deepStrictEqual(JSON.parse(decodeURIComponent(encoded.slice("json-uri:".length))), urls); assert.equal(encoded.includes('"'), false); }); + +it("keeps unselected scenes on a partial capture and clears the folder on a full one", async () => { + const pixel = showcaseConfig.devices.find((device) => device.id === "pixel"); + assert.ok(pixel); + const seeded = [...pixel.scenes.map((scene) => `${scene}.png`), "stale.png"]; + const cases = [ + { + scenes: new Set(["thread"]), + remaining: seeded.filter((file) => file !== "thread.png"), + }, + { scenes: new Set(), remaining: [] }, + ]; + const outputDirectory = await NodeFSP.mkdtemp( + NodePath.join(NodeOS.tmpdir(), "t3-showcase-test-"), + ); + try { + for (const { scenes, remaining } of cases) { + const [capture] = planShowcaseCaptures(showcaseConfig, { + platforms: new Set(), + deviceIds: new Set(["pixel"]), + scenes, + appearances: new Set(), + themes: new Set(), + }); + assert.ok(capture); + const directory = showcaseCaptureDirectory(outputDirectory, capture); + await NodeFSP.mkdir(directory, { recursive: true }); + for (const file of seeded) { + await NodeFSP.writeFile(NodePath.join(directory, file), "png"); + } + await clearShowcaseCaptureOutputs(outputDirectory, capture); + assert.deepStrictEqual((await NodeFSP.readdir(directory)).sort(), remaining.sort()); + } + } finally { + await NodeFSP.rm(outputDirectory, { recursive: true, force: true }); + } +}); diff --git a/scripts/mobile-showcase.ts b/scripts/mobile-showcase.ts index 425c371fd0c2a..a49fab976b458 100644 --- a/scripts/mobile-showcase.ts +++ b/scripts/mobile-showcase.ts @@ -243,6 +243,27 @@ export function showcaseCaptureDirectory( ); } +function isFullCapture(capture: ShowcaseCapture): boolean { + return capture.scenes.length === capture.device.scenes.length; +} + +export async function clearShowcaseCaptureOutputs( + outputDirectory: string, + capture: ShowcaseCapture, +): Promise { + const directory = showcaseCaptureDirectory(outputDirectory, capture); + if (isFullCapture(capture)) { + await NodeFSP.rm(directory, { recursive: true, force: true }); + } else { + await Promise.all( + capture.scenes.map((scene) => + NodeFSP.rm(NodePath.join(directory, `${scene}.png`), { force: true }), + ), + ); + } + await NodeFSP.mkdir(directory, { recursive: true }); +} + async function finalizeCapture(destination: string, device: ShowcaseDevice): Promise { const normalized = normalizeStorePng(await NodeFSP.readFile(destination)); await NodeFSP.writeFile(destination, normalized); @@ -1537,11 +1558,7 @@ async function main(): Promise { const outputDirectory = NodePath.resolve(REPO_ROOT, showcaseConfig.outputDirectory); if (options.validateOnly) { for (const capture of captures) { - await validateCaptureSet( - capture, - outputDirectory, - capture.scenes.length === capture.device.scenes.length, - ); + await validateCaptureSet(capture, outputDirectory, isFullCapture(capture)); } return; } @@ -1550,9 +1567,7 @@ async function main(): Promise { const metroHost = hasIos ? lanIpv4Address() : "127.0.0.1"; await NodeFSP.mkdir(outputDirectory, { recursive: true }); for (const capture of captures) { - const directory = showcaseCaptureDirectory(outputDirectory, capture); - await NodeFSP.rm(directory, { recursive: true, force: true }); - await NodeFSP.mkdir(directory, { recursive: true }); + await clearShowcaseCaptureOutputs(outputDirectory, capture); } const showcaseRootDir = await NodeFSP.mkdtemp( @@ -1622,11 +1637,7 @@ async function main(): Promise { (cleanup) => androidCleanups.push(cleanup), ); } - await validateCaptureSet( - capture, - outputDirectory, - capture.scenes.length === capture.device.scenes.length, - ); + await validateCaptureSet(capture, outputDirectory, isFullCapture(capture)); } NodeProcess.stdout.write(