diff --git a/components/settings/PrivacySection.tsx b/components/settings/PrivacySection.tsx index d0d5c605..624451be 100644 --- a/components/settings/PrivacySection.tsx +++ b/components/settings/PrivacySection.tsx @@ -50,13 +50,6 @@ export const PrivacySection: FC = () => { handleSettingChange('privacy', { ...settings.privacy, analyticsEnabled: v }) } /> - - handleSettingChange('privacy', { ...settings.privacy, crashReporting: v }) - } - /> { handleSettingChange('privacy', { ...settings.privacy, localStorageOnly: v }) } /> - - handleSettingChange('privacy', { ...settings.privacy, shareUsageData: v }) - } - /> ): S // isAnalyticsPersistenceAllowed; persisted user choice overrides the default via the spread. validSettings.privacy = { analyticsEnabled: true, - crashReporting: false, dataEncryption: true, localStorageOnly: true, - shareUsageData: false, euDataResidency: true, ...incomingPrivacy, }; diff --git a/tests/unit/ai/aiPolicy.test.ts b/tests/unit/ai/aiPolicy.test.ts index d10f6527..852d442c 100644 --- a/tests/unit/ai/aiPolicy.test.ts +++ b/tests/unit/ai/aiPolicy.test.ts @@ -74,9 +74,7 @@ describe('assertCloudAiAllowedSync', () => { localStorageOnly: false, euDataResidency: false, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }; expect(() => assertCloudAiAllowedSync('gemini', privacy)).not.toThrow(); }); @@ -86,9 +84,7 @@ describe('assertCloudAiAllowedSync', () => { localStorageOnly: true, euDataResidency: false, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }; expect(() => assertCloudAiAllowedSync('gemini', privacy)).toThrow( 'Cloud provider blocked: local-only mode is active.', @@ -100,9 +96,7 @@ describe('assertCloudAiAllowedSync', () => { localStorageOnly: false, euDataResidency: true, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }; expect(() => assertCloudAiAllowedSync('openai', privacy)).toThrow( 'Cloud provider blocked by EU residency policy: openai', @@ -114,9 +108,7 @@ describe('assertCloudAiAllowedSync', () => { localStorageOnly: false, euDataResidency: true, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }; expect(() => assertCloudAiAllowedSync('grok', privacy)).toThrow( 'Cloud provider blocked by EU residency policy: grok', @@ -129,9 +121,7 @@ describe('assertCloudAiAllowedSync', () => { localStorageOnly: false, euDataResidency: false, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }; expect(() => assertCloudAiAllowedSync('gemini', privacy)).toThrow('local-only'); }); @@ -142,9 +132,7 @@ describe('assertCloudAiAllowedSync', () => { localStorageOnly: false, euDataResidency: true, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }; expect(() => assertCloudAiAllowedSync('gemini', privacy)).not.toThrow(); }); diff --git a/tests/unit/aiPolicyAndUtils.test.ts b/tests/unit/aiPolicyAndUtils.test.ts index 3a8637d4..411582ce 100644 --- a/tests/unit/aiPolicyAndUtils.test.ts +++ b/tests/unit/aiPolicyAndUtils.test.ts @@ -34,9 +34,7 @@ import { const basePrivacy = (overrides: { localStorageOnly: boolean; euDataResidency: boolean }) => ({ analyticsEnabled: false, - crashReporting: false, dataEncryption: false, - shareUsageData: false, ...overrides, }); diff --git a/tests/unit/languageToolClient.test.ts b/tests/unit/languageToolClient.test.ts index 60b90ee0..df37dde9 100644 --- a/tests/unit/languageToolClient.test.ts +++ b/tests/unit/languageToolClient.test.ts @@ -44,10 +44,8 @@ const baseSettings = (): Settings => ({ }), privacy: { analyticsEnabled: false, - crashReporting: false, dataEncryption: true, localStorageOnly: true, - shareUsageData: false, euDataResidency: true, }, performance: { diff --git a/tests/unit/services/aiPolicy.test.ts b/tests/unit/services/aiPolicy.test.ts index 359c433e..13f0099d 100644 --- a/tests/unit/services/aiPolicy.test.ts +++ b/tests/unit/services/aiPolicy.test.ts @@ -55,9 +55,7 @@ describe('aiPolicy', () => { localStorageOnly: true, euDataResidency: false, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }), ).toThrow('local-only mode'); }); @@ -68,9 +66,7 @@ describe('aiPolicy', () => { localStorageOnly: false, euDataResidency: true, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }), ).toThrow('EU residency'); }); @@ -81,9 +77,7 @@ describe('aiPolicy', () => { localStorageOnly: false, euDataResidency: true, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }), ).toThrow('EU residency'); }); @@ -94,9 +88,7 @@ describe('aiPolicy', () => { localStorageOnly: false, euDataResidency: true, analyticsEnabled: false, - crashReporting: false, dataEncryption: true, - shareUsageData: false, }), ).not.toThrow(); }); diff --git a/tests/unit/settings/PrivacyAndNotificationsSection.test.tsx b/tests/unit/settings/PrivacyAndNotificationsSection.test.tsx index 298a64d8..174a7b41 100644 --- a/tests/unit/settings/PrivacyAndNotificationsSection.test.tsx +++ b/tests/unit/settings/PrivacyAndNotificationsSection.test.tsx @@ -16,10 +16,8 @@ const mockHandleSettingChange = vi.fn(); const defaultSettings = { privacy: { analyticsEnabled: true, - crashReporting: false, dataEncryption: true, localStorageOnly: false, - shareUsageData: false, euDataResidency: false, }, notifications: { @@ -117,22 +115,15 @@ describe('PrivacySection', () => { ).toBe('true'); }); - it('crashReporting toggle is off', () => { - render(); - expect( - screen - .getByRole('switch', { name: 'settings.privacy.crashReporting' }) - .getAttribute('aria-checked'), - ).toBe('false'); - }); - - it('calls handleSettingChange when toggle clicked', async () => { + it('calls handleSettingChange when a toggle is clicked', async () => { const user = userEvent.setup(); render(); - await user.click(screen.getByRole('switch', { name: 'settings.privacy.crashReporting' })); + // QNBS-v3: localStorageOnly replaces the removed crashReporting toggle here; the mock has it + // off, so clicking flips it on. + await user.click(screen.getByRole('switch', { name: 'settings.privacy.localStorageOnly' })); expect(mockHandleSettingChange).toHaveBeenCalledWith( 'privacy', - expect.objectContaining({ crashReporting: true }), + expect.objectContaining({ localStorageOnly: true }), ); }); }); diff --git a/tests/unit/settings/PrivacySection.test.tsx b/tests/unit/settings/PrivacySection.test.tsx index 8299aa8c..bfc6847d 100644 --- a/tests/unit/settings/PrivacySection.test.tsx +++ b/tests/unit/settings/PrivacySection.test.tsx @@ -21,10 +21,8 @@ const makeCtx = (overrides?: Record) => ({ settings: { privacy: { analyticsEnabled: true, - crashReporting: false, dataEncryption: true, localStorageOnly: false, - shareUsageData: false, euDataResidency: true, }, }, @@ -71,11 +69,6 @@ describe('PrivacySection', () => { expect(screen.getByText('settings.privacy.analyticsEnabled')).toBeInTheDocument(); }); - it('renders crash reporting toggle', () => { - render(); - expect(screen.getByText('settings.privacy.crashReporting')).toBeInTheDocument(); - }); - it('renders data encryption toggle', () => { render(); expect(screen.getByText('settings.privacy.dataEncryption')).toBeInTheDocument(); @@ -86,14 +79,10 @@ describe('PrivacySection', () => { expect(screen.getByText('settings.privacy.localStorageOnly')).toBeInTheDocument(); }); - it('renders share usage data toggle', () => { - render(); - expect(screen.getByText('settings.privacy.shareUsageData')).toBeInTheDocument(); - }); - - it('renders six toggles total (including euDataResidency)', () => { + // QNBS-v3: dead crashReporting + shareUsageData toggles removed (no functional reads); 4 remain. + it('renders four toggles total (analytics, encryption, localStorageOnly, euDataResidency)', () => { render(); - expect(screen.getAllByRole('switch').length).toBe(6); + expect(screen.getAllByRole('switch').length).toBe(4); }); it('analytics toggle calls handleSettingChange when clicked', async () => { diff --git a/tests/unit/settingsSlice.test.ts b/tests/unit/settingsSlice.test.ts index 7a30927d..0f4da4ba 100644 --- a/tests/unit/settingsSlice.test.ts +++ b/tests/unit/settingsSlice.test.ts @@ -31,12 +31,10 @@ describe('settingsSlice', () => { it('defaults privacy.analyticsEnabled to true (local-only metadata; toggle is a functional opt-out)', () => { // QNBS-v3: SEC — analytics default ON preserves the analytics dashboard (data never leaves the - // device); the privacy toggle now gates DuckDB writes via isAnalyticsPersistenceAllowed. Outward - // sharing switches stay opt-in. + // device); the privacy toggle now gates DuckDB writes via isAnalyticsPersistenceAllowed. const privacy = initState().privacy; expect(privacy.analyticsEnabled).toBe(true); - expect(privacy.crashReporting).toBe(false); - expect(privacy.shareUsageData).toBe(false); + expect(privacy.localStorageOnly).toBe(true); }); it('setDesktopSettings merges the desktop group', () => { @@ -251,11 +249,11 @@ describe('settingsSlice', () => { it('setPrivacy merges privacy settings', () => { const state = settingsReducer( initState(), - settingsActions.setPrivacy({ analyticsEnabled: true, shareUsageData: true }), + settingsActions.setPrivacy({ analyticsEnabled: false, localStorageOnly: false }), ); - expect(state.privacy.analyticsEnabled).toBe(true); - expect(state.privacy.shareUsageData).toBe(true); + expect(state.privacy.analyticsEnabled).toBe(false); + expect(state.privacy.localStorageOnly).toBe(false); expect(state.privacy.dataEncryption).toBe(true); }); diff --git a/types.ts b/types.ts index 1bd925d6..8d56bf3a 100644 --- a/types.ts +++ b/types.ts @@ -528,10 +528,8 @@ export interface AccessibilitySettings { export interface PrivacySettings { analyticsEnabled: boolean; - crashReporting: boolean; dataEncryption: boolean; localStorageOnly: boolean; - shareUsageData: boolean; euDataResidency: boolean; // QNBS-v3: SEC one-time migration marker. Before the analytics gate existed, analyticsEnabled was // cosmetic (persistence was controlled solely by enableDuckDbAnalytics, default on), so legacy