1import type { BrowserLinkTarget } from "@t3tools/contracts";
2import { describe, expect, it, vi } from "vite-plus/test";
3
4import { ensureClientSettingsHydrated } from "~/hooks/useSettings";
5
6import { resolveBrowserLinkTargetPreference, resolveLinkTarget } from "./browserLinkTarget";
7
8const settings = vi.hoisted(() => ({ browserLinkTarget: "system" as BrowserLinkTarget }));
9
10vi.mock("~/hooks/useSettings", () => ({
11 ensureClientSettingsHydrated: vi.fn(async () => undefined),
12 getClientSettings: () => settings,
13}));
14
15const click = { metaKey: false, ctrlKey: false };
16
17describe("resolveLinkTarget", () => {
18 it("keeps the system browser unless the user asked for in-app", () => {
19 expect(
20 resolveLinkTarget({
21 url: "https://example.com/",
22 event: click,
23 preference: "system",
24 canOpenInApp: true,
25 }),
26 ).toBe("system");
27 });
28
29 it("opens in-app when asked and the runtime can", () => {
30 expect(
31 resolveLinkTarget({
32 url: "https://example.com/",
33 event: click,
34 preference: "app",
35 canOpenInApp: true,
36 }),
37 ).toBe("app");
38 });
39
40 it("falls back to the system browser where there is no in-app browser", () => {
41 // The hosted web app and mobile have nowhere to open a tab, so the
42 // preference cannot be honoured there and the link still has to open.
43 expect(
44 resolveLinkTarget({
45 url: "https://example.com/",
46 event: click,
47 preference: "app",
48 canOpenInApp: false,
49 }),
50 ).toBe("system");
51 });
52
53 it("treats a modifier click as the way out of the in-app default", () => {
54 expect(
55 resolveLinkTarget({
56 url: "https://example.com/",
57 event: { metaKey: true, ctrlKey: false },
58 preference: "app",
59 canOpenInApp: true,
60 }),
61 ).toBe("system");
62 expect(
63 resolveLinkTarget({
64 url: "https://example.com/",
65 event: { metaKey: false, ctrlKey: true },
66 preference: "app",
67 canOpenInApp: true,
68 }),
69 ).toBe("system");
70 });
71
72 it("leaves non-web schemes to the shell", () => {
73 for (const url of ["mailto:someone@example.com", "vscode://file/x", "not a url"]) {
74 expect(resolveLinkTarget({ url, event: click, preference: "app", canOpenInApp: true })).toBe(
75 "system",
76 );
77 }
78 });
79});
80
81describe("resolveBrowserLinkTargetPreference", () => {
82 it.each(["system", "app"] as const)(
83 "rejects failed reads instead of using the current %s preference",
84 async (preference) => {
85 settings.browserLinkTarget = preference;
86 const failure = new Error("Settings read failed");
87 vi.mocked(ensureClientSettingsHydrated).mockRejectedValueOnce(failure);
88
89 await expect(resolveBrowserLinkTargetPreference()).rejects.toBe(failure);
90 await expect(resolveBrowserLinkTargetPreference()).resolves.toBe(preference);
91 },
92 );
93});