* fix(hopper): keep macOS and Linux launcher paths POSIX * fix(hopper): keep Linux and macOS bundle paths POSIX Linux package archives and macOS app-bundle launcher paths describe the target host, not the host running the test. Build them with path.posix so exercising these installers from Windows produces the same paths as the target platform. * fix(electron): follow Windows symlink targets with native separators readlink returns backslash-separated targets on Windows. Splitting only on '/' kept a whole relative target as one component, so nested links were not followed and a relative target could pass the framework-root check without being walked. Split on both separators on Windows and add a regression for an escaping target. * test(windows): align host-specific path and lifecycle expectations Compare generated skill and provider-command paths with native joins, bind ADB device-path validation to an explicit platform, and prepare simulated POSIX Ghidra install roots on Windows. Skip POSIX-only Hopper lease, permission-bit, and symlink-parent cases, assert Windows' unsupported Hopper state instead, and cover the Windows Ghidra native-source admission step before snapshot replay. Launch Node child seams with SystemRoot available and expect Windows close-failure resource retention. * test(windows): close review coverage gaps --------- Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
import { mkdtemp, mkdir, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join, posix, relative } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
macHopperCanonicalLauncherCandidates,
|
|
macHopperLauncherCandidates,
|
|
macHopperRenamedLauncherCandidates,
|
|
} from "./MacHopperPaths.js";
|
|
|
|
describe("macOS Hopper launcher paths", () => {
|
|
const home = "/fixture/home";
|
|
|
|
it("keeps canonical system and user candidates ahead of renamed bundles", () => {
|
|
expect(macHopperCanonicalLauncherCandidates(home)).toEqual([
|
|
"/Applications/Hopper Disassembler.app/Contents/MacOS/hopper",
|
|
"/fixture/home/Applications/Hopper Disassembler.app/Contents/MacOS/hopper",
|
|
]);
|
|
expect(
|
|
macHopperRenamedLauncherCandidates(
|
|
home,
|
|
new Map([
|
|
[
|
|
"/Applications",
|
|
["Hopper z.app", "Hopper a.app", "Hopper Disassembler.app"],
|
|
],
|
|
["/fixture/home/Applications", ["Hopper User.app"]],
|
|
]),
|
|
),
|
|
).toEqual([
|
|
"/Applications/Hopper a.app/Contents/MacOS/hopper",
|
|
"/Applications/Hopper z.app/Contents/MacOS/hopper",
|
|
"/fixture/home/Applications/Hopper User.app/Contents/MacOS/hopper",
|
|
]);
|
|
});
|
|
|
|
it("always includes the installer's canonical user destination", () => {
|
|
expect(macHopperLauncherCandidates(home)).toContain(
|
|
"/fixture/home/Applications/Hopper Disassembler.app/Contents/MacOS/hopper",
|
|
);
|
|
});
|
|
|
|
it("finds renamed user bundles through the runtime filesystem scan", async () => {
|
|
// Keep native ownership for all I/O, including UNC temporary directories.
|
|
// On Windows, a checkout-relative home models POSIX separators without
|
|
// discarding the drive/share or normalizing a UNC prefix through posix.join.
|
|
const root =
|
|
process.platform === "win32" ? join(process.cwd(), "build") : tmpdir();
|
|
await mkdir(root, { recursive: true });
|
|
const temporary = await mkdtemp(join(root, "rea-hopper-paths-"));
|
|
const home =
|
|
process.platform === "win32"
|
|
? relative(process.cwd(), temporary).replaceAll("\\", "/")
|
|
: temporary;
|
|
const appDirectory = join(temporary, "Applications", "Hopper Preview.app");
|
|
try {
|
|
await mkdir(appDirectory, { recursive: true });
|
|
expect([...macHopperLauncherCandidates(home)]).toContain(
|
|
posix.join(
|
|
home,
|
|
"Applications/Hopper Preview.app/Contents/MacOS/hopper",
|
|
),
|
|
);
|
|
} finally {
|
|
await rm(temporary, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|