mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-22 11:04:42 -07:00
fix(cli): Fix type errors in UI hooks tests (#11483)
This commit is contained in:
@@ -149,10 +149,16 @@ describe('useSlashCommandProcessor', () => {
|
||||
openPrivacyNotice: vi.fn(),
|
||||
openSettingsDialog: vi.fn(),
|
||||
openModelDialog: mockOpenModelDialog,
|
||||
openPermissionsDialog: vi.fn(),
|
||||
quit: mockSetQuittingMessages,
|
||||
setDebugMessage: vi.fn(),
|
||||
toggleCorgiMode: vi.fn(),
|
||||
toggleDebugProfiler: vi.fn(),
|
||||
dispatchExtensionStateUpdate: vi.fn(),
|
||||
addConfirmUpdateExtensionRequest: vi.fn(),
|
||||
},
|
||||
new Map(), // extensionsUpdateState
|
||||
true, // isConfigInitialized
|
||||
),
|
||||
);
|
||||
|
||||
@@ -175,7 +181,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
expect(result.current.slashCommands).toHaveLength(1);
|
||||
});
|
||||
|
||||
expect(result.current.slashCommands[0]?.name).toBe('test');
|
||||
expect(result.current.slashCommands?.[0]?.name).toBe('test');
|
||||
expect(mockBuiltinLoadCommands).toHaveBeenCalledTimes(1);
|
||||
expect(mockFileLoadCommands).toHaveBeenCalledTimes(1);
|
||||
expect(mockMcpLoadCommands).toHaveBeenCalledTimes(1);
|
||||
@@ -456,7 +462,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
name: 'loadwiththoughts',
|
||||
action: vi.fn().mockResolvedValue({
|
||||
type: 'load_history',
|
||||
history: [{ type: MessageType.MODEL, text: 'response' }],
|
||||
history: [{ type: MessageType.GEMINI, text: 'response' }],
|
||||
clientHistory: historyWithThoughts,
|
||||
}),
|
||||
});
|
||||
@@ -901,18 +907,26 @@ describe('useSlashCommandProcessor', () => {
|
||||
mockClearItems,
|
||||
mockLoadHistory,
|
||||
vi.fn(), // refreshStatic
|
||||
vi.fn(), // onDebugMessage
|
||||
vi.fn(), // openThemeDialog
|
||||
mockOpenAuthDialog,
|
||||
vi.fn(), // openEditorDialog
|
||||
vi.fn(), // toggleCorgiMode
|
||||
mockSetQuittingMessages,
|
||||
vi.fn(), // openPrivacyNotice
|
||||
|
||||
vi.fn(), // openSettingsDialog
|
||||
vi.fn(), // toggleVimEnabled
|
||||
vi.fn().mockResolvedValue(false), // toggleVimEnabled
|
||||
vi.fn(), // setIsProcessing
|
||||
vi.fn(), // setGeminiMdFileCount
|
||||
{
|
||||
openAuthDialog: vi.fn(),
|
||||
openThemeDialog: vi.fn(),
|
||||
openEditorDialog: vi.fn(),
|
||||
openPrivacyNotice: vi.fn(),
|
||||
openSettingsDialog: vi.fn(),
|
||||
openModelDialog: vi.fn(),
|
||||
openPermissionsDialog: vi.fn(),
|
||||
quit: vi.fn(),
|
||||
setDebugMessage: vi.fn(),
|
||||
toggleCorgiMode: vi.fn(),
|
||||
toggleDebugProfiler: vi.fn(),
|
||||
dispatchExtensionStateUpdate: vi.fn(),
|
||||
addConfirmUpdateExtensionRequest: vi.fn(),
|
||||
},
|
||||
new Map(), // extensionsUpdateState
|
||||
true, // isConfigInitialized
|
||||
),
|
||||
);
|
||||
|
||||
@@ -959,7 +973,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
it('should log a simple slash command', async () => {
|
||||
const result = setupProcessorHook(loggingTestCommands);
|
||||
await waitFor(() =>
|
||||
expect(result.current.slashCommands.length).toBeGreaterThan(0),
|
||||
expect(result.current.slashCommands?.length).toBeGreaterThan(0),
|
||||
);
|
||||
await act(async () => {
|
||||
await result.current.handleSlashCommand('/logtest');
|
||||
@@ -978,7 +992,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
it('logs nothing for a bogus command', async () => {
|
||||
const result = setupProcessorHook(loggingTestCommands);
|
||||
await waitFor(() =>
|
||||
expect(result.current.slashCommands.length).toBeGreaterThan(0),
|
||||
expect(result.current.slashCommands?.length).toBeGreaterThan(0),
|
||||
);
|
||||
await act(async () => {
|
||||
await result.current.handleSlashCommand('/bogusbogusbogus');
|
||||
@@ -990,7 +1004,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
it('logs a failure event for a failed command', async () => {
|
||||
const result = setupProcessorHook(loggingTestCommands);
|
||||
await waitFor(() =>
|
||||
expect(result.current.slashCommands.length).toBeGreaterThan(0),
|
||||
expect(result.current.slashCommands?.length).toBeGreaterThan(0),
|
||||
);
|
||||
await act(async () => {
|
||||
await result.current.handleSlashCommand('/fail');
|
||||
@@ -1009,7 +1023,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
it('should log a slash command with a subcommand', async () => {
|
||||
const result = setupProcessorHook(loggingTestCommands);
|
||||
await waitFor(() =>
|
||||
expect(result.current.slashCommands.length).toBeGreaterThan(0),
|
||||
expect(result.current.slashCommands?.length).toBeGreaterThan(0),
|
||||
);
|
||||
await act(async () => {
|
||||
await result.current.handleSlashCommand('/logwithsub sub');
|
||||
@@ -1027,7 +1041,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
it('should log the command path when an alias is used', async () => {
|
||||
const result = setupProcessorHook(loggingTestCommands);
|
||||
await waitFor(() =>
|
||||
expect(result.current.slashCommands.length).toBeGreaterThan(0),
|
||||
expect(result.current.slashCommands?.length).toBeGreaterThan(0),
|
||||
);
|
||||
await act(async () => {
|
||||
await result.current.handleSlashCommand('/la');
|
||||
@@ -1043,7 +1057,7 @@ describe('useSlashCommandProcessor', () => {
|
||||
it('should not log for unknown commands', async () => {
|
||||
const result = setupProcessorHook(loggingTestCommands);
|
||||
await waitFor(() =>
|
||||
expect(result.current.slashCommands.length).toBeGreaterThan(0),
|
||||
expect(result.current.slashCommands?.length).toBeGreaterThan(0),
|
||||
);
|
||||
await act(async () => {
|
||||
await result.current.handleSlashCommand('/unknown');
|
||||
|
||||
@@ -148,6 +148,7 @@ describe('useAtCompletion', () => {
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
enableRecursiveFileSearch: true,
|
||||
disableFuzzySearch: false,
|
||||
});
|
||||
@@ -239,8 +240,8 @@ describe('useAtCompletion', () => {
|
||||
initialize: vi.fn().mockResolvedValue(undefined),
|
||||
search: vi
|
||||
.fn()
|
||||
.mockImplementation(async (...args) =>
|
||||
realFileSearch.search(...args),
|
||||
.mockImplementation(async (pattern, options) =>
|
||||
realFileSearch.search(pattern, options),
|
||||
),
|
||||
};
|
||||
vi.spyOn(FileSearchFactory, 'create').mockReturnValue(mockFileSearch);
|
||||
|
||||
@@ -6,7 +6,15 @@
|
||||
|
||||
/** @vitest-environment jsdom */
|
||||
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
vi,
|
||||
afterEach,
|
||||
type Mock,
|
||||
} from 'vitest';
|
||||
import { renderHook, act, waitFor } from '@testing-library/react';
|
||||
import { useCommandCompletion } from './useCommandCompletion.js';
|
||||
import type { CommandContext } from '../commands/types.js';
|
||||
@@ -45,7 +53,7 @@ const setupMocks = ({
|
||||
slashCompletionRange?: { completionStart: number; completionEnd: number };
|
||||
}) => {
|
||||
// Mock for @-completions
|
||||
(useAtCompletion as vi.Mock).mockImplementation(
|
||||
(useAtCompletion as Mock).mockImplementation(
|
||||
({
|
||||
enabled,
|
||||
setSuggestions,
|
||||
@@ -61,7 +69,7 @@ const setupMocks = ({
|
||||
);
|
||||
|
||||
// Mock for /-completions
|
||||
(useSlashCompletion as vi.Mock).mockImplementation(
|
||||
(useSlashCompletion as Mock).mockImplementation(
|
||||
({
|
||||
enabled,
|
||||
setSuggestions,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
import { useConsoleMessages } from './useConsoleMessages';
|
||||
import { useConsoleMessages } from './useConsoleMessages.js';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
describe('useConsoleMessages', () => {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { useFocus } from './useFocus.js';
|
||||
import { vi } from 'vitest';
|
||||
import { vi, type Mock } from 'vitest';
|
||||
import { useStdin, useStdout } from 'ink';
|
||||
import { KeypressProvider } from '../contexts/KeypressContext.js';
|
||||
import React from 'react';
|
||||
@@ -29,15 +29,18 @@ const wrapper = ({ children }: { children: React.ReactNode }) =>
|
||||
React.createElement(KeypressProvider, null, children);
|
||||
|
||||
describe('useFocus', () => {
|
||||
let stdin: EventEmitter;
|
||||
let stdout: { write: vi.Func };
|
||||
let stdin: EventEmitter & { resume: Mock; pause: Mock };
|
||||
let stdout: { write: Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
stdin = new EventEmitter();
|
||||
stdin.resume = vi.fn();
|
||||
stdin.pause = vi.fn();
|
||||
stdin = Object.assign(new EventEmitter(), {
|
||||
resume: vi.fn(),
|
||||
pause: vi.fn(),
|
||||
});
|
||||
stdout = { write: vi.fn() };
|
||||
mockedUseStdin.mockReturnValue({ stdin } as ReturnType<typeof useStdin>);
|
||||
mockedUseStdin.mockReturnValue({ stdin } as unknown as ReturnType<
|
||||
typeof useStdin
|
||||
>);
|
||||
mockedUseStdout.mockReturnValue({ stdout } as unknown as ReturnType<
|
||||
typeof useStdout
|
||||
>);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
import { vi, type Mock, type MockInstance } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useFolderTrust } from './useFolderTrust.js';
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
@@ -28,10 +28,10 @@ vi.mock('node:process', async () => {
|
||||
describe('useFolderTrust', () => {
|
||||
let mockSettings: LoadedSettings;
|
||||
let mockTrustedFolders: LoadedTrustedFolders;
|
||||
let loadTrustedFoldersSpy: vi.SpyInstance;
|
||||
let isWorkspaceTrustedSpy: vi.SpyInstance;
|
||||
let loadTrustedFoldersSpy: MockInstance;
|
||||
let isWorkspaceTrustedSpy: MockInstance;
|
||||
let onTrustChange: (isTrusted: boolean | undefined) => void;
|
||||
let addItem: vi.Mock;
|
||||
let addItem: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSettings = {
|
||||
|
||||
@@ -1396,10 +1396,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirm,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Replace text?',
|
||||
displayedText: 'Replace old with new',
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: 'old',
|
||||
newContent: 'new',
|
||||
},
|
||||
tool: {
|
||||
name: 'replace',
|
||||
@@ -1422,10 +1426,10 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'info',
|
||||
title: 'Read File',
|
||||
onConfirm: mockOnConfirm,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Read file?',
|
||||
displayedText: 'Read /test/file.txt',
|
||||
prompt: 'Read /test/file.txt?',
|
||||
},
|
||||
tool: {
|
||||
name: 'read_file',
|
||||
@@ -1474,10 +1478,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirmReplace,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Replace text?',
|
||||
displayedText: 'Replace old with new',
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: 'old',
|
||||
newContent: 'new',
|
||||
},
|
||||
tool: {
|
||||
name: 'replace',
|
||||
@@ -1500,10 +1508,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirmWrite,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Write file?',
|
||||
displayedText: 'Write to /test/new.txt',
|
||||
fileName: 'new.txt',
|
||||
filePath: '/test/new.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: null,
|
||||
newContent: 'content',
|
||||
},
|
||||
tool: {
|
||||
name: 'write_file',
|
||||
@@ -1526,10 +1538,10 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'info',
|
||||
title: 'Read File',
|
||||
onConfirm: mockOnConfirmRead,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Read file?',
|
||||
displayedText: 'Read /test/file.txt',
|
||||
prompt: 'Read /test/file.txt?',
|
||||
},
|
||||
tool: {
|
||||
name: 'read_file',
|
||||
@@ -1577,10 +1589,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirm,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Replace text?',
|
||||
displayedText: 'Replace old with new',
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: 'old',
|
||||
newContent: 'new',
|
||||
},
|
||||
tool: {
|
||||
name: 'replace',
|
||||
@@ -1597,9 +1613,7 @@ describe('useGeminiStream', () => {
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleApprovalModeChange(
|
||||
ApprovalMode.REQUIRE_CONFIRMATION,
|
||||
);
|
||||
await result.current.handleApprovalModeChange(ApprovalMode.DEFAULT);
|
||||
});
|
||||
|
||||
// No tools should be auto-approved
|
||||
@@ -1627,10 +1641,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirmSuccess,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Replace text?',
|
||||
displayedText: 'Replace old with new',
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: 'old',
|
||||
newContent: 'new',
|
||||
},
|
||||
tool: {
|
||||
name: 'replace',
|
||||
@@ -1653,10 +1671,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirmError,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Write file?',
|
||||
displayedText: 'Write to /test/file.txt',
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: null,
|
||||
newContent: 'content',
|
||||
},
|
||||
tool: {
|
||||
name: 'write_file',
|
||||
@@ -1711,7 +1733,7 @@ describe('useGeminiStream', () => {
|
||||
invocation: {
|
||||
getDescription: () => 'Mock description',
|
||||
} as unknown as AnyToolInvocation,
|
||||
} as TrackedWaitingToolCall,
|
||||
} as unknown as TrackedWaitingToolCall,
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
@@ -1735,10 +1757,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
onCancel: vi.fn(),
|
||||
message: 'Replace text?',
|
||||
displayedText: 'Replace old with new',
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
// No onConfirm method
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: 'old',
|
||||
newContent: 'new',
|
||||
} as any,
|
||||
tool: {
|
||||
name: 'replace',
|
||||
@@ -1776,10 +1802,14 @@ describe('useGeminiStream', () => {
|
||||
status: 'awaiting_approval',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
type: 'edit',
|
||||
title: 'Confirm Edit',
|
||||
onConfirm: mockOnConfirmAwaiting,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Replace text?',
|
||||
displayedText: 'Replace old with new',
|
||||
fileName: 'file.txt',
|
||||
filePath: '/test/file.txt',
|
||||
fileDiff: 'fake diff',
|
||||
originalContent: 'old',
|
||||
newContent: 'new',
|
||||
},
|
||||
tool: {
|
||||
name: 'replace',
|
||||
@@ -1801,12 +1831,6 @@ describe('useGeminiStream', () => {
|
||||
},
|
||||
status: 'executing',
|
||||
responseSubmittedToGemini: false,
|
||||
confirmationDetails: {
|
||||
onConfirm: mockOnConfirmExecuting,
|
||||
onCancel: vi.fn(),
|
||||
message: 'Write file?',
|
||||
displayedText: 'Write to /test/file.txt',
|
||||
},
|
||||
tool: {
|
||||
name: 'write_file',
|
||||
displayName: 'write_file',
|
||||
|
||||
@@ -12,6 +12,7 @@ import { KeypressProvider } from '../contexts/KeypressContext.js';
|
||||
import { useStdin } from 'ink';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { PassThrough } from 'node:stream';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
// Mock the 'ink' module to control stdin
|
||||
vi.mock('ink', async (importOriginal) => {
|
||||
@@ -56,8 +57,8 @@ class MockStdin extends EventEmitter {
|
||||
isTTY = true;
|
||||
isRaw = false;
|
||||
setRawMode = vi.fn();
|
||||
on = this.addListener;
|
||||
removeListener = this.removeListener;
|
||||
override on = this.addListener;
|
||||
override removeListener = super.removeListener;
|
||||
write = vi.fn();
|
||||
resume = vi.fn();
|
||||
|
||||
@@ -112,7 +113,7 @@ describe('useKeypress', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
stdin = new MockStdin();
|
||||
(useStdin as vi.Mock).mockReturnValue({
|
||||
(useStdin as Mock).mockReturnValue({
|
||||
stdin,
|
||||
setRawMode: mockSetRawMode,
|
||||
});
|
||||
|
||||
@@ -50,7 +50,6 @@ describe('usePhraseCycler', () => {
|
||||
const { result } = renderHook(() => usePhraseCycler(true, false));
|
||||
// Initial phrase should be one of the witty phrases
|
||||
expect(WITTY_LOADING_PHRASES).toContain(result.current);
|
||||
const _initialPhrase = result.current;
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS);
|
||||
@@ -58,7 +57,6 @@ describe('usePhraseCycler', () => {
|
||||
// Phrase should change and be one of the witty phrases
|
||||
expect(WITTY_LOADING_PHRASES).toContain(result.current);
|
||||
|
||||
const _secondPhrase = result.current;
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS);
|
||||
});
|
||||
@@ -159,7 +157,7 @@ describe('usePhraseCycler', () => {
|
||||
randomMock.mockRestore();
|
||||
vi.spyOn(Math, 'random').mockImplementation(() => 0.5); // Always witty
|
||||
|
||||
rerender({ isActive: true, isWaiting: false, customPhrases: undefined });
|
||||
rerender({ isActive: true, isWaiting: false, customPhrases: [] });
|
||||
|
||||
expect(WITTY_LOADING_PHRASES).toContain(result.current);
|
||||
});
|
||||
@@ -188,8 +186,7 @@ describe('usePhraseCycler', () => {
|
||||
{ initialProps: { isActive: true, isWaiting: false } },
|
||||
);
|
||||
|
||||
const _initialPhrase = result.current;
|
||||
expect(WITTY_LOADING_PHRASES).toContain(_initialPhrase);
|
||||
expect(WITTY_LOADING_PHRASES).toContain(result.current);
|
||||
|
||||
// Cycle to a different phrase (potentially)
|
||||
act(() => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user