File size: 3,917 Bytes
7a1ad33 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs/promises';
import { coreEvents } from './events.js';
import { Storage } from '../config/storage.js';
// Mock fs/promises before importing the module under test
vi.mock('node:fs/promises', () => ({
access: vi.fn(),
mkdir: vi.fn().mockResolvedValue(undefined),
writeFile: vi.fn().mockResolvedValue(undefined),
}));
// Mock Storage to return a predictable directory
vi.mock('../config/storage.js', () => ({
Storage: {
getGlobalGeminiDir: vi.fn(),
},
}));
import { getBrowserConsentIfNeeded } from './browserConsent.js';
describe('browserConsent', () => {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(Storage.getGlobalGeminiDir).mockReturnValue('/mock/.gemini');
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should return true if consent file already exists', async () => {
// Consent file exists — fs.access resolves
vi.mocked(fs.access).mockResolvedValue(undefined);
const result = await getBrowserConsentIfNeeded();
expect(result).toBe(true);
// Should not emit a consent request
const emitSpy = vi.spyOn(coreEvents, 'emitConsentRequest');
expect(emitSpy).not.toHaveBeenCalled();
});
it('should auto-accept in non-interactive mode (no listeners) without persisting consent', async () => {
// Consent file does not exist
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
// No listeners registered
vi.spyOn(coreEvents, 'listenerCount').mockReturnValue(0);
const result = await getBrowserConsentIfNeeded();
expect(result).toBe(true);
// Should NOT persist the consent — an interactive user on the same machine
// must still see the dialog the first time they use the browser agent.
expect(fs.writeFile).not.toHaveBeenCalled();
});
it('should request consent interactively and return true when accepted', async () => {
// Consent file does not exist
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
// Simulate interactive mode: there is at least one listener
vi.spyOn(coreEvents, 'listenerCount').mockReturnValue(1);
// Mock emitConsentRequest to auto-confirm
vi.spyOn(coreEvents, 'emitConsentRequest').mockImplementation((payload) => {
payload.onConfirm(true);
});
const result = await getBrowserConsentIfNeeded();
expect(result).toBe(true);
expect(coreEvents.emitConsentRequest).toHaveBeenCalledWith(
expect.objectContaining({
prompt: expect.stringContaining('Privacy Notice'),
}),
);
expect(fs.writeFile).toHaveBeenCalled();
});
it('should return false when user declines consent', async () => {
// Consent file does not exist
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
// Simulate interactive mode
vi.spyOn(coreEvents, 'listenerCount').mockReturnValue(1);
// Mock emitConsentRequest to decline
vi.spyOn(coreEvents, 'emitConsentRequest').mockImplementation((payload) => {
payload.onConfirm(false);
});
const result = await getBrowserConsentIfNeeded();
expect(result).toBe(false);
// Should NOT persist consent
expect(fs.writeFile).not.toHaveBeenCalled();
});
it('should include privacy policy link in the prompt', async () => {
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
vi.spyOn(coreEvents, 'listenerCount').mockReturnValue(1);
vi.spyOn(coreEvents, 'emitConsentRequest').mockImplementation((payload) => {
payload.onConfirm(true);
});
await getBrowserConsentIfNeeded();
expect(coreEvents.emitConsentRequest).toHaveBeenCalledWith(
expect.objectContaining({
prompt: expect.stringContaining('policies.google.com/privacy'),
}),
);
});
});
|