/** * @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(); 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( , ); 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( , ); 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( , ); 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( , ); 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( , ); 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( , ); 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( , ); 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(); }); });