diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 7944e17375c..c378767ff90 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -66,7 +66,6 @@ import { SshAgentPromptType } from "../../autofill/models/ssh-agent-setting"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutotypeService } from "../../autofill/services/desktop-autotype.service"; import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; -import { AutoStartService } from "../../platform/auto-start"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; import { DesktopPremiumUpgradePromptService } from "../../services/desktop-premium-upgrade-prompt.service"; import { NativeMessagingManifestService } from "../services/native-messaging-manifest.service"; @@ -221,7 +220,6 @@ export class SettingsComponent implements OnInit, OnDestroy { private changeDetectorRef: ChangeDetectorRef, private toastService: ToastService, private billingAccountProfileStateService: BillingAccountProfileStateService, - private autoStartService: AutoStartService, ) { this.isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; this.isLinux = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop; @@ -247,8 +245,8 @@ export class SettingsComponent implements OnInit, OnDestroy { this.startToTrayDescText = this.i18nService.t(startToTrayKey + "Desc"); // Only show the auto-start setting if it's supported on this platform. - // The service handles platform-specific checks (Windows Store, Snap, etc.) - this.showOpenAtLoginOption = this.autoStartService.shouldDisplaySetting(); + // Windows Store apps and Snap packages don't support user-configurable auto-start. + this.showOpenAtLoginOption = this.desktopSettingsService.shouldDisplayAutoStartSetting(); // DuckDuckGo browser is only for macos initially this.showDuckDuckGoIntegrationOption = this.isMac; diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 404bd7edbc2..b4fff0b0490 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -222,7 +222,10 @@ export class Main { this.mainCryptoFunctionService, ); - this.autoStartService = new DefaultAutoStartService(this.logService); + this.autoStartService = new DefaultAutoStartService( + this.logService, + this.desktopSettingsService, + ); this.messagingMain = new MessagingMain( this, this.desktopSettingsService, diff --git a/apps/desktop/src/platform/auto-start/auto-start.service.abstraction.ts b/apps/desktop/src/platform/auto-start/auto-start.service.abstraction.ts index 71440144ffd..2170dcf6dbc 100644 --- a/apps/desktop/src/platform/auto-start/auto-start.service.abstraction.ts +++ b/apps/desktop/src/platform/auto-start/auto-start.service.abstraction.ts @@ -31,12 +31,4 @@ export abstract class AutoStartService { * @returns The auto-start status: `Enabled`, `Disabled`, or `Unknown` if the state cannot be determined. */ abstract isEnabled(): Promise; - - /** - * Determines whether the auto-start setting should be displayed in the application UI. - * Some platforms (e.g., Snap) manage auto-start externally via package configuration, - * so the setting should be hidden from the user. - * @returns `true` if the setting should be shown, `false` if it should be hidden. - */ - abstract shouldDisplaySetting(): boolean; } diff --git a/apps/desktop/src/platform/auto-start/auto-start.service.spec.ts b/apps/desktop/src/platform/auto-start/auto-start.service.spec.ts index a6bdec4ad3d..4facf24652f 100644 --- a/apps/desktop/src/platform/auto-start/auto-start.service.spec.ts +++ b/apps/desktop/src/platform/auto-start/auto-start.service.spec.ts @@ -8,6 +8,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { autostart } from "@bitwarden/desktop-napi"; import * as utils from "../../utils"; +import { DesktopSettingsService } from "../services/desktop-settings.service"; import { DefaultAutoStartService } from "./auto-start.service"; import { AutoStartStatus } from "./auto-start.service.abstraction"; @@ -36,19 +37,25 @@ jest.mock("../../utils", () => ({ describe("DefaultAutoStartService", () => { let service: DefaultAutoStartService; let logService: MockProxy; + let desktopSettingsService: MockProxy; let originalPlatform: NodeJS.Platform; beforeEach(() => { logService = mock(); - service = new DefaultAutoStartService(logService); + desktopSettingsService = mock(); + service = new DefaultAutoStartService(logService, desktopSettingsService); originalPlatform = process.platform; jest.clearAllMocks(); // Default mock implementations (app.getVersion as jest.Mock).mockReturnValue("1.0.0"); (app.getPath as jest.Mock).mockImplementation((name: string) => { - if (name === "exe") {return "/usr/bin/bitwarden";} - if (name === "home") {return "/home/user";} + if (name === "exe") { + return "/usr/bin/bitwarden"; + } + if (name === "home") { + return "/home/user"; + } return ""; }); (utils.isFlatpak as jest.Mock).mockReturnValue(false); @@ -116,12 +123,6 @@ describe("DefaultAutoStartService", () => { expect(result).toBe(AutoStartStatus.Unknown); }); - - it("should display setting in UI", () => { - const result = service.shouldDisplaySetting(); - - expect(result).toBe(true); - }); }); describe("Linux (Snap)", () => { @@ -151,12 +152,6 @@ describe("DefaultAutoStartService", () => { expect(result).toBe(AutoStartStatus.Unknown); }); - - it("should hide setting from UI (snap manages autostart)", () => { - const result = service.shouldDisplaySetting(); - - expect(result).toBe(false); - }); }); describe("Linux (Standard)", () => { @@ -229,12 +224,6 @@ Terminal=false`; expect(result).toBe(AutoStartStatus.Disabled); }); - - it("should display setting in UI", () => { - const result = service.shouldDisplaySetting(); - - expect(result).toBe(true); - }); }); describe("macOS", () => { @@ -277,12 +266,6 @@ Terminal=false`; expect(result).toBe(AutoStartStatus.Disabled); }); - - it("should display setting in UI", () => { - const result = service.shouldDisplaySetting(); - - expect(result).toBe(true); - }); }); describe("Windows", () => { @@ -327,26 +310,5 @@ Terminal=false`; expect(result).toBe(AutoStartStatus.Disabled); }); - - it("should display setting in UI", () => { - const result = service.shouldDisplaySetting(); - - expect(result).toBe(true); - }); - }); - - describe("Windows Store", () => { - beforeEach(() => { - Object.defineProperty(process, "platform", { - value: "win32", - }); - (utils.isWindowsStore as jest.Mock).mockReturnValue(true); - }); - - it("should hide setting from UI (Windows Store doesn't support auto-start)", () => { - const result = service.shouldDisplaySetting(); - - expect(result).toBe(false); - }); }); }); diff --git a/apps/desktop/src/platform/auto-start/auto-start.service.ts b/apps/desktop/src/platform/auto-start/auto-start.service.ts index cdc9362e0e9..6522ee8dc3d 100644 --- a/apps/desktop/src/platform/auto-start/auto-start.service.ts +++ b/apps/desktop/src/platform/auto-start/auto-start.service.ts @@ -6,7 +6,8 @@ import { app } from "electron"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { autostart } from "@bitwarden/desktop-napi"; -import { isFlatpak, isSnapStore, isWindowsStore } from "../../utils"; +import { isFlatpak, isSnapStore } from "../../utils"; +import { DesktopSettingsService } from "../services/desktop-settings.service"; import { AutoStartService, AutoStartStatus } from "./auto-start.service.abstraction"; @@ -20,7 +21,10 @@ import { AutoStartService, AutoStartStatus } from "./auto-start.service.abstract * - **macOS/Windows**: Uses Electron's app.setLoginItemSettings() API */ export class DefaultAutoStartService implements AutoStartService { - constructor(private logService: LogService) {} + constructor( + private logService: LogService, + private desktopSettingsService: DesktopSettingsService, + ) {} async enable(): Promise { if (process.platform === "linux") { @@ -110,17 +114,6 @@ Terminal=false`; } } - shouldDisplaySetting(): boolean { - // Windows Store apps don't support auto-start functionality. - // On Snap, auto-start is managed by the snap configuration (electron-builder.json). - if (isWindowsStore() || isSnapStore()) { - return false; - } - - // All other platforms support user-configurable auto-start - return true; - } - /** * Gets the path to the Linux autostart .desktop file. */ diff --git a/apps/desktop/src/platform/services/desktop-settings.service.spec.ts b/apps/desktop/src/platform/services/desktop-settings.service.spec.ts new file mode 100644 index 00000000000..f24b9ae4816 --- /dev/null +++ b/apps/desktop/src/platform/services/desktop-settings.service.spec.ts @@ -0,0 +1,73 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { StateProvider, GlobalState } from "@bitwarden/common/platform/state"; + +import * as utils from "../../utils"; + +import { DesktopSettingsService } from "./desktop-settings.service"; + +// Mock the utils module +jest.mock("../../utils", () => ({ + isWindowsStore: jest.fn(), + isSnapStore: jest.fn(), +})); + +describe("DesktopSettingsService", () => { + let service: DesktopSettingsService; + let stateProvider: MockProxy; + + beforeEach(() => { + stateProvider = mock(); + + // Mock getGlobal to return a mock state with state$ observable + const mockState = { + state$: of(null), + update: jest.fn(), + } as unknown as GlobalState; + + stateProvider.getGlobal.mockReturnValue(mockState); + stateProvider.getActive.mockReturnValue(mockState as any); + stateProvider.getUser.mockReturnValue(mockState as any); + + service = new DesktopSettingsService(stateProvider); + jest.clearAllMocks(); + + // Default: not Windows Store, not Snap + (utils.isWindowsStore as jest.Mock).mockReturnValue(false); + (utils.isSnapStore as jest.Mock).mockReturnValue(false); + }); + + describe("shouldDisplayAutoStartSetting", () => { + it("should return true for standard platforms", () => { + const result = service.shouldDisplayAutoStartSetting(); + + expect(result).toBe(true); + }); + + it("should return false for Windows Store", () => { + (utils.isWindowsStore as jest.Mock).mockReturnValue(true); + + const result = service.shouldDisplayAutoStartSetting(); + + expect(result).toBe(false); + }); + + it("should return false for Snap", () => { + (utils.isSnapStore as jest.Mock).mockReturnValue(true); + + const result = service.shouldDisplayAutoStartSetting(); + + expect(result).toBe(false); + }); + + it("should return false when both Windows Store and Snap are true", () => { + (utils.isWindowsStore as jest.Mock).mockReturnValue(true); + (utils.isSnapStore as jest.Mock).mockReturnValue(true); + + const result = service.shouldDisplayAutoStartSetting(); + + expect(result).toBe(false); + }); + }); +}); diff --git a/apps/desktop/src/platform/services/desktop-settings.service.ts b/apps/desktop/src/platform/services/desktop-settings.service.ts index d7c17433471..1a16907e10f 100644 --- a/apps/desktop/src/platform/services/desktop-settings.service.ts +++ b/apps/desktop/src/platform/services/desktop-settings.service.ts @@ -19,6 +19,7 @@ import { import { UserId } from "@bitwarden/common/types/guid"; import { SshAgentPromptType } from "../../autofill/models/ssh-agent-setting"; +import { isSnapStore, isWindowsStore } from "../../utils"; import { ModalModeState, WindowState } from "../models/domain/window-state"; export const HARDWARE_ACCELERATION = new KeyDefinition( @@ -354,4 +355,22 @@ export class DesktopSettingsService { async setPreventScreenshots(value: boolean) { await this.preventScreenshotState.update(() => value); } + + /** + * Determines whether the auto-start setting should be displayed in the application UI. + * Some platforms (e.g., Windows Store, Snap) manage auto-start externally via + * package configuration, so the setting should be hidden from users on those platforms. + * + * @returns `true` if the setting should be shown, `false` if it should be hidden. + */ + shouldDisplayAutoStartSetting(): boolean { + // Windows Store apps don't support auto-start functionality. + // On Snap, auto-start is managed by the snap configuration (electron-builder.json). + if (isWindowsStore() || isSnapStore()) { + return false; + } + + // All other platforms support user-configurable auto-start + return true; + } }