1import { afterEach, describe, expect, it, vi } from "vite-plus/test";
2import {
3 resolveServerBackedAppDisplayName,
4 resolveServerBackedAppStageLabel,
5} from "./branding.logic";
6
7const originalWindow = globalThis.window;
8
9afterEach(() => {
10 vi.resetModules();
11
12 if (originalWindow === undefined) {
13 Reflect.deleteProperty(globalThis, "window");
14 return;
15 }
16
17 globalThis.window = originalWindow;
18});
19
20describe("branding", () => {
21 it("uses injected desktop branding when available", async () => {
22 Object.defineProperty(globalThis, "window", {
23 configurable: true,
24 value: {
25 desktopBridge: {
26 getAppBranding: () => ({
27 baseName: "The Knife",
28 stageLabel: "Nightly",
29 displayName: "The Knife (Nightly)",
30 }),
31 },
32 },
33 });
34
35 const branding = await import("./branding");
36
37 expect(branding.APP_BASE_NAME).toBe("The Knife");
38 expect(branding.APP_STAGE_LABEL).toBe("Nightly");
39 expect(branding.APP_DISPLAY_NAME).toBe("The Knife (Nightly)");
40 });
41
42 it("normalizes hosted app channel metadata", async () => {
43 vi.stubEnv("VITE_HOSTED_APP_CHANNEL", "nightly");
44
45 const branding = await import("./branding");
46
47 expect(branding.HOSTED_APP_CHANNEL).toBe("nightly");
48 expect(branding.HOSTED_APP_CHANNEL_LABEL).toBe("Nightly");
49 expect(branding.APP_STAGE_LABEL).toBe("Nightly");
50 expect(branding.APP_DISPLAY_NAME).toBe("The Knife (Nightly)");
51 });
52
53 it("does not label the latest hosted app channel", async () => {
54 vi.stubEnv("VITE_HOSTED_APP_CHANNEL", "latest");
55
56 const branding = await import("./branding");
57
58 expect(branding.HOSTED_APP_CHANNEL).toBe("latest");
59 expect(branding.HOSTED_APP_CHANNEL_LABEL).toBe("Latest");
60 expect(branding.APP_STAGE_LABEL).toBe("Latest");
61 expect(branding.APP_DISPLAY_NAME).toBe("The Knife");
62 });
63
64 it("ignores unknown hosted app channels", async () => {
65 vi.stubEnv("VITE_HOSTED_APP_CHANNEL", "preview");
66
67 const branding = await import("./branding");
68
69 expect(branding.HOSTED_APP_CHANNEL).toBeNull();
70 expect(branding.HOSTED_APP_CHANNEL_LABEL).toBeNull();
71 });
72});
73
74describe("branding logic", () => {
75 it("returns Nightly for nightly primary server versions", () => {
76 expect(
77 resolveServerBackedAppStageLabel({
78 primaryServerVersion: "0.0.28-nightly.20260616.12",
79 fallbackStageLabel: "Alpha",
80 }),
81 ).toBe("Nightly");
82 });
83
84 it("updates the display name for nightly primary server versions", () => {
85 expect(
86 resolveServerBackedAppDisplayName({
87 baseName: "The Knife",
88 fallbackDisplayName: "The Knife (Alpha)",
89 fallbackStageLabel: "Alpha",
90 primaryServerVersion: "0.0.28-nightly.20260616.12",
91 }),
92 ).toBe("The Knife (Nightly)");
93 });
94
95 it("keeps the fallback display name for stable primary server versions", () => {
96 expect(
97 resolveServerBackedAppDisplayName({
98 baseName: "The Knife",
99 fallbackDisplayName: "The Knife (Alpha)",
100 fallbackStageLabel: "Alpha",
101 primaryServerVersion: "0.0.27",
102 }),
103 ).toBe("The Knife (Alpha)");
104 });
105
106 it("keeps the fallback display name for malformed nightly primary server versions", () => {
107 expect(
108 resolveServerBackedAppDisplayName({
109 baseName: "The Knife",
110 fallbackDisplayName: "The Knife (Alpha)",
111 fallbackStageLabel: "Alpha",
112 primaryServerVersion: "0.0.28-nightly.20260616",
113 }),
114 ).toBe("The Knife (Alpha)");
115 });
116});