diff --git a/src/browser/chrome.default-browser.test.ts b/src/browser/chrome.default-browser.test.ts index 6dfa5d05472..4c29939e820 100644 --- a/src/browser/chrome.default-browser.test.ts +++ b/src/browser/chrome.default-browser.test.ts @@ -82,6 +82,54 @@ describe("browser default executable detection", () => { expect(exe?.kind).toBe("chrome"); }); + it("detects Edge via LaunchServices bundle ID (com.microsoft.edgemac)", async () => { + const edgeExecutablePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"; + // macOS LaunchServices registers Edge as "com.microsoft.edgemac", which + // differs from the CFBundleIdentifier "com.microsoft.Edge" in the app's + // own Info.plist. Both must be recognised. + // + // The existsSync mock deliberately only returns true for the Edge path + // when checked via the resolved osascript/defaults path — Chrome's + // fallback candidate path is the only other "existing" binary. This + // ensures the test fails if the default-browser detection branch is + // broken, because the fallback candidate list would return Chrome, not + // Edge. + vi.mocked(execFileSync).mockImplementation((cmd, args) => { + const argsStr = Array.isArray(args) ? args.join(" ") : ""; + if (cmd === "/usr/bin/plutil" && argsStr.includes("LSHandlers")) { + return JSON.stringify([ + { LSHandlerURLScheme: "http", LSHandlerRoleAll: "com.microsoft.edgemac" }, + ]); + } + if (cmd === "/usr/bin/osascript" && argsStr.includes("path to application id")) { + return "/Applications/Microsoft Edge.app/"; + } + if (cmd === "/usr/bin/defaults") { + return "Microsoft Edge"; + } + return ""; + }); + vi.mocked(fs.existsSync).mockImplementation((p) => { + const value = String(p); + if (value.includes(launchServicesPlist)) { + return true; + } + // Only Edge (via osascript resolution) and Chrome (fallback candidate) + // "exist". If default-browser detection breaks, the resolver would + // return Chrome from the fallback list — not Edge — failing the assert. + return value === edgeExecutablePath || value.includes(chromeExecutablePath); + }); + const resolveBrowserExecutableForPlatform = await loadResolveBrowserExecutableForPlatform(); + + const exe = resolveBrowserExecutableForPlatform( + {} as Parameters[0], + "darwin", + ); + + expect(exe?.path).toBe(edgeExecutablePath); + expect(exe?.kind).toBe("edge"); + }); + it("falls back when default browser is non-Chromium on macOS", async () => { mockMacDefaultBrowser("com.apple.Safari"); mockChromeExecutableExists(); diff --git a/src/browser/chrome.executables.ts b/src/browser/chrome.executables.ts index 6ef7bc0b155..9a45e92a0a9 100644 --- a/src/browser/chrome.executables.ts +++ b/src/browser/chrome.executables.ts @@ -23,6 +23,12 @@ const CHROMIUM_BUNDLE_IDS = new Set([ "com.microsoft.EdgeBeta", "com.microsoft.EdgeDev", "com.microsoft.EdgeCanary", + // Edge LaunchServices IDs (used in macOS default browser registration — + // these differ from CFBundleIdentifier and are what plutil returns) + "com.microsoft.edgemac", + "com.microsoft.edgemac.beta", + "com.microsoft.edgemac.dev", + "com.microsoft.edgemac.canary", "org.chromium.Chromium", "com.vivaldi.Vivaldi", "com.operasoftware.Opera",