fix(macos): show TCC notice after first prompt

This commit is contained in:
Brennan Benson
2026-07-28 16:25:54 -07:00
parent d3681f6306
commit b5e6fa2c25
2 changed files with 27 additions and 20 deletions
+24 -17
View File
@@ -54,16 +54,11 @@ beforeEach(() => {
})
describe('tcc prompt notice threshold', () => {
it('stays silent until the third dialog, then fires exactly once', () => {
expect(handleTccPromptForTests()).toBeNull()
expect(handleTccPromptForTests()).toBeNull()
const payload = handleTccPromptForTests()
expect(payload).toEqual({
it('fires on the first dialog, then stays silent', () => {
expect(handleTccPromptForTests()).toEqual({
promptCount: TCC_PROMPT_NOTICE_THRESHOLD
})
// Why: a recurring prompt loop must not produce a toast per dialog.
expect(handleTccPromptForTests()).toBeNull()
expect(handleTccPromptForTests()).toBeNull()
})
@@ -109,7 +104,10 @@ describe('tcc prompt notice threshold', () => {
expect(consumePendingTccPromptNotice(2)).toBeNull()
expect(writeFileAtomically).toHaveBeenCalledOnce()
const [, contents] = writeFileAtomically.mock.calls.at(-1) as [string, string]
expect(JSON.parse(contents)).toMatchObject({ promptCount: 3, notified: true })
expect(JSON.parse(contents)).toMatchObject({
promptCount: TCC_PROMPT_NOTICE_THRESHOLD,
notified: true
})
})
it('releases an unacknowledged claim for a replacement renderer', () => {
@@ -123,11 +121,14 @@ describe('tcc prompt notice threshold', () => {
releasePendingTccPromptNotice(1, oldClaim!.claimId)
const replacementClaim = consumePendingTccPromptNotice(2)
expect(oldClaim).toEqual({ claimId: 1, promptCount: 3 })
expect(replacementClaim).toEqual({ claimId: 2, promptCount: 3 })
expect(oldClaim).toEqual({ claimId: 1, promptCount: TCC_PROMPT_NOTICE_THRESHOLD })
expect(replacementClaim).toEqual({ claimId: 2, promptCount: TCC_PROMPT_NOTICE_THRESHOLD })
acknowledgePendingTccPromptNotice(1, oldClaim!.claimId)
releasePendingTccPromptNotice(2)
expect(consumePendingTccPromptNotice(3)).toEqual({ claimId: 3, promptCount: 3 })
expect(consumePendingTccPromptNotice(3)).toEqual({
claimId: 3,
promptCount: TCC_PROMPT_NOTICE_THRESHOLD
})
})
it('invalidates an outstanding claim when the user dismisses the notice', () => {
@@ -262,7 +263,10 @@ describe('tcc prompt notice threshold', () => {
}).not.toThrow()
expect(mainWindow.webContents.send).not.toHaveBeenCalled()
expect(watchStop).toHaveBeenCalledTimes(1)
expect(consumePendingTccPromptNotice(1)).toEqual({ claimId: 1, promptCount: 3 })
expect(consumePendingTccPromptNotice(1)).toEqual({
claimId: 1,
promptCount: TCC_PROMPT_NOTICE_THRESHOLD
})
} finally {
Object.defineProperty(process, 'platform', platform!)
}
@@ -284,17 +288,20 @@ describe('tcc prompt notice threshold', () => {
}).not.toThrow()
expect(watchStop).toHaveBeenCalledOnce()
expect(consumePendingTccPromptNotice(1)).toEqual({ claimId: 1, promptCount: 3 })
expect(consumePendingTccPromptNotice(1)).toEqual({
claimId: 1,
promptCount: TCC_PROMPT_NOTICE_THRESHOLD
})
} finally {
Object.defineProperty(process, 'platform', platform!)
}
})
it('does not respawn the watcher for a persisted pending threshold', () => {
it('delivers a tally persisted below the old threshold without respawning the watcher', () => {
const platform = Object.getOwnPropertyDescriptor(process, 'platform')
Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' })
readTallyFile.mockReturnValue(
JSON.stringify({ promptCount: 3, notified: false, dismissed: false })
JSON.stringify({ promptCount: 2, notified: false, dismissed: false })
)
try {
const mainWindow = createWindowStub()
@@ -302,10 +309,10 @@ describe('tcc prompt notice threshold', () => {
expect(watchStart).not.toHaveBeenCalled()
expect(mainWindow.webContents.send).toHaveBeenCalledWith('macosTccPrompts:threshold', {
promptCount: 3
promptCount: 2
})
expect(mainWindow.once).not.toHaveBeenCalled()
expect(consumePendingTccPromptNotice(1)).toEqual({ claimId: 1, promptCount: 3 })
expect(consumePendingTccPromptNotice(1)).toEqual({ claimId: 1, promptCount: 2 })
} finally {
Object.defineProperty(process, 'platform', platform!)
}
+3 -3
View File
@@ -8,11 +8,11 @@ import { MacosTccPromptWatch } from './macos-tcc-prompt-watch'
/**
* Surfaces Full Disk Access guidance only to users macOS is actually prompting
* (#9756), instead of nudging every Mac user. Counts Orca-attributed TCC
* dialogs across launches and tells the renderer once the third one lands.
* dialogs across launches and tells the renderer when the first one lands.
*/
/** Why: one dialog is normal and two is bad luck; three means it is recurring for this user. */
export const TCC_PROMPT_NOTICE_THRESHOLD = 3
/** Why: the first detected dialog identifies an affected user; the notice remains one-time. */
export const TCC_PROMPT_NOTICE_THRESHOLD = 1
/** Why: preserve detection when Electron never emits `ready-to-show` without competing with startup. */
export const TCC_PROMPT_WATCH_START_FALLBACK_MS = 10_000