fix(macos): hidden autostart dock indicator + 자동실행 mismatch false positive

두 macOS 한정 버그 묶음:

1. autostart --hidden 으로 spawn 시 quickCapture (NSPanel) 만 떠 있어
   dock running indicator (점) 가 표출 안 됨 — NSPanel 은 NSApp main window
   로 register 안 됨. inboxWindow 를 hidden 상태로 미리 create + ready-to-show
   시점에 showInactive → hide trick 으로 NSApp 에 register, 사용자 화면
   깜빡임 없이 dock 점 켜짐.

2. SettingsPage 의 자동실행 mismatch 경고가 macOS 에서 false positive.
   macOS 13+ 의 SMAppService API 가 args 옵션 무시 + unsigned/Electron
   앱에 대해 executableWillLaunchAtLogin 을 자주 false 로 반환 → 정상 등록
   상태에서도 경고 떠 있음. AutostartDiagnostic 결과에 platform 필드 추가,
   willLaunch 신호는 win32 에서만 mismatch 판정에 사용.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
th-kim0823
2026-05-14 13:11:17 +09:00
parent 64935d943c
commit a68feae20e
7 changed files with 89 additions and 18 deletions

View File

@@ -42,6 +42,7 @@ describe('AutostartDiagnostic — collectAutostartState', () => {
expect(state.withArgs).toEqual({ openAtLogin: true, executableWillLaunchAtLogin: true });
expect(state.noArgs).toEqual({ openAtLogin: false, executableWillLaunchAtLogin: true });
expect(state.execPath).toBe(process.execPath);
expect(state.platform).toBe('darwin');
});
it('passes args=["--hidden"] for the first call, no args for the second', async () => {

View File

@@ -3,15 +3,17 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import '@testing-library/jest-dom/vitest';
import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react';
function makeDiag(open: boolean): {
function makeDiag(open: boolean, platform: NodeJS.Platform = 'win32'): {
withArgs: { openAtLogin: boolean; executableWillLaunchAtLogin: boolean };
noArgs: { openAtLogin: boolean; executableWillLaunchAtLogin: boolean };
execPath: string;
platform: NodeJS.Platform;
} {
return {
withArgs: { openAtLogin: open, executableWillLaunchAtLogin: open },
noArgs: { openAtLogin: open, executableWillLaunchAtLogin: open },
execPath: '/path/to/exe'
execPath: '/path/to/exe',
platform
};
}
@@ -51,7 +53,8 @@ describe('AutostartSection', () => {
diagnostic: {
withArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
noArgs: { openAtLogin: false, executableWillLaunchAtLogin: true },
execPath: '/path/to/Inkling.exe'
execPath: '/path/to/Inkling.exe',
platform: 'win32'
}
});
render(<AutostartSection />);
@@ -71,6 +74,7 @@ describe('AutostartSection', () => {
withArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
noArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
execPath: 'C:\\app.exe',
platform: 'win32',
registryPath: 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Inkling',
registryValue: '"C:\\app.exe" --hidden'
}
@@ -89,7 +93,8 @@ describe('AutostartSection', () => {
diagnostic: {
withArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
noArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
execPath: '/p'
execPath: '/p',
platform: 'win32'
}
});
render(<AutostartSection />);
@@ -97,6 +102,38 @@ describe('AutostartSection', () => {
expect(screen.queryByText(/⚠️/)).not.toBeInTheDocument();
});
it('macOS: no false-positive mismatch when willLaunch=false (SMAppService 한계)', async () => {
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
vi.mocked(inboxApi.getAutostart).mockResolvedValueOnce({
openAtLogin: true,
diagnostic: {
withArgs: { openAtLogin: true, executableWillLaunchAtLogin: false },
noArgs: { openAtLogin: true, executableWillLaunchAtLogin: false },
execPath: '/Applications/Inkling.app',
platform: 'darwin'
}
});
render(<AutostartSection />);
await screen.findByRole('checkbox');
expect(screen.queryByText(/⚠️/)).not.toBeInTheDocument();
});
it('Win: mismatch warning when openAtLogin=true but willLaunch=false', async () => {
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
vi.mocked(inboxApi.getAutostart).mockResolvedValueOnce({
openAtLogin: true,
diagnostic: {
withArgs: { openAtLogin: true, executableWillLaunchAtLogin: false },
noArgs: { openAtLogin: true, executableWillLaunchAtLogin: false },
execPath: 'C:\\app.exe',
platform: 'win32'
}
});
render(<AutostartSection />);
await screen.findByRole('checkbox');
expect(await screen.findByText(/⚠️/)).toBeInTheDocument();
});
it('"재등록" button calls setAutostart with current openAtLogin value', async () => {
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
vi.mocked(inboxApi.getAutostart).mockResolvedValueOnce({
@@ -104,7 +141,8 @@ describe('AutostartSection', () => {
diagnostic: {
withArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
noArgs: { openAtLogin: true, executableWillLaunchAtLogin: true },
execPath: '/p'
execPath: '/p',
platform: 'win32'
}
});
render(<AutostartSection />);