Files
gemini-cli/packages/cli/src/ui/components/PermissionsModifyTrustDialog.test.tsx

237 lines
7.6 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { Mock } from 'vitest';
import { renderWithProviders } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
import { PermissionsModifyTrustDialog } from './PermissionsModifyTrustDialog.js';
import { TrustLevel } from '../../config/trustedFolders.js';
import { act } from 'react';
import * as processUtils from '../../utils/processUtils.js';
import { usePermissionsModifyTrust } from '../hooks/usePermissionsModifyTrust.js';
// Hoist mocks for dependencies of the usePermissionsModifyTrust hook
const mockedCwd = vi.hoisted(() => vi.fn());
const mockedLoadTrustedFolders = vi.hoisted(() => vi.fn());
const mockedIsWorkspaceTrusted = vi.hoisted(() => vi.fn());
// Mock the modules themselves
vi.mock('node:process', async (importOriginal) => {
const actual = await importOriginal<typeof import('node:process')>();
return {
...actual,
cwd: mockedCwd,
};
});
vi.mock('../../config/trustedFolders.js', () => ({
loadTrustedFolders: mockedLoadTrustedFolders,
isWorkspaceTrusted: mockedIsWorkspaceTrusted,
TrustLevel: {
TRUST_FOLDER: 'TRUST_FOLDER',
TRUST_PARENT: 'TRUST_PARENT',
DO_NOT_TRUST: 'DO_NOT_TRUST',
},
}));
vi.mock('../hooks/usePermissionsModifyTrust.js');
describe('PermissionsModifyTrustDialog', () => {
let mockUpdateTrustLevel: Mock;
let mockCommitTrustLevelChange: Mock;
beforeEach(() => {
mockedCwd.mockReturnValue('/test/dir');
mockUpdateTrustLevel = vi.fn();
mockCommitTrustLevelChange = vi.fn();
vi.mocked(usePermissionsModifyTrust).mockReturnValue({
cwd: '/test/dir',
currentTrustLevel: TrustLevel.DO_NOT_TRUST,
isInheritedTrustFromParent: false,
isInheritedTrustFromIde: false,
needsRestart: false,
updateTrustLevel: mockUpdateTrustLevel,
commitTrustLevelChange: mockCommitTrustLevelChange,
isFolderTrustEnabled: true,
});
});
afterEach(() => {
vi.resetAllMocks();
});
it('should render the main dialog with current trust level', async () => {
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={vi.fn()} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => {
expect(lastFrame()).toContain('Modify Trust Level');
expect(lastFrame()).toContain('Folder: /test/dir');
expect(lastFrame()).toContain('Current Level: DO_NOT_TRUST');
});
unmount();
});
it('should display the inherited trust note from parent', async () => {
vi.mocked(usePermissionsModifyTrust).mockReturnValue({
cwd: '/test/dir',
currentTrustLevel: TrustLevel.DO_NOT_TRUST,
isInheritedTrustFromParent: true,
isInheritedTrustFromIde: false,
needsRestart: false,
updateTrustLevel: mockUpdateTrustLevel,
commitTrustLevelChange: mockCommitTrustLevelChange,
isFolderTrustEnabled: true,
});
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={vi.fn()} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => {
expect(lastFrame()).toContain(
'Note: This folder behaves as a trusted folder because one of the parent folders is trusted.',
);
});
unmount();
});
it('should display the inherited trust note from IDE', async () => {
vi.mocked(usePermissionsModifyTrust).mockReturnValue({
cwd: '/test/dir',
currentTrustLevel: TrustLevel.DO_NOT_TRUST,
isInheritedTrustFromParent: false,
isInheritedTrustFromIde: true,
needsRestart: false,
updateTrustLevel: mockUpdateTrustLevel,
commitTrustLevelChange: mockCommitTrustLevelChange,
isFolderTrustEnabled: true,
});
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={vi.fn()} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => {
expect(lastFrame()).toContain(
'Note: This folder behaves as a trusted folder because the connected IDE workspace is trusted.',
);
});
unmount();
});
it('should render the labels with folder names', async () => {
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={vi.fn()} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => {
expect(lastFrame()).toContain('Trust this folder (dir)');
expect(lastFrame()).toContain('Trust parent folder (test)');
});
unmount();
});
it('should call onExit when escape is pressed', async () => {
const onExit = vi.fn();
const { stdin, lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={onExit} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => expect(lastFrame()).not.toContain('Loading...'));
await act(async () => {
stdin.write('\u001b[27u'); // Kitty escape key
});
// Escape key has a 50ms timeout in KeypressContext, so we need to wrap waitUntilReady in act
await act(async () => {
await waitUntilReady();
});
await waitFor(() => {
expect(onExit).toHaveBeenCalled();
});
unmount();
});
it('should commit and restart `r` keypress', async () => {
const mockRelaunchApp = vi
.spyOn(processUtils, 'relaunchApp')
.mockResolvedValue(undefined);
mockCommitTrustLevelChange.mockReturnValue(true);
vi.mocked(usePermissionsModifyTrust).mockReturnValue({
cwd: '/test/dir',
currentTrustLevel: TrustLevel.DO_NOT_TRUST,
isInheritedTrustFromParent: false,
isInheritedTrustFromIde: false,
needsRestart: true,
updateTrustLevel: mockUpdateTrustLevel,
commitTrustLevelChange: mockCommitTrustLevelChange,
isFolderTrustEnabled: true,
});
const onExit = vi.fn();
const { stdin, lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={onExit} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => expect(lastFrame()).not.toContain('Loading...'));
await act(async () => {
stdin.write('r'); // Press 'r' to restart
});
await waitUntilReady();
await waitFor(() => {
expect(mockCommitTrustLevelChange).toHaveBeenCalled();
expect(mockRelaunchApp).toHaveBeenCalled();
});
mockRelaunchApp.mockRestore();
unmount();
});
it('should not commit when escape is pressed during restart prompt', async () => {
vi.mocked(usePermissionsModifyTrust).mockReturnValue({
cwd: '/test/dir',
currentTrustLevel: TrustLevel.DO_NOT_TRUST,
isInheritedTrustFromParent: false,
isInheritedTrustFromIde: false,
needsRestart: true,
updateTrustLevel: mockUpdateTrustLevel,
commitTrustLevelChange: mockCommitTrustLevelChange,
isFolderTrustEnabled: true,
});
const onExit = vi.fn();
const { stdin, lastFrame, waitUntilReady, unmount } = renderWithProviders(
<PermissionsModifyTrustDialog onExit={onExit} addItem={vi.fn()} />,
);
await waitUntilReady();
await waitFor(() => expect(lastFrame()).not.toContain('Loading...'));
await act(async () => {
stdin.write('\u001b[27u'); // Press kitty escape key
});
await act(async () => {
await waitUntilReady();
});
await waitFor(() => {
expect(mockCommitTrustLevelChange).not.toHaveBeenCalled();
expect(onExit).toHaveBeenCalled();
});
unmount();
});
});