summaryrefslogtreecommitdiff
path: root/packages/cli/src/test-utils
diff options
context:
space:
mode:
authorAbhi <[email protected]>2025-07-07 16:45:44 -0400
committerGitHub <[email protected]>2025-07-07 20:45:44 +0000
commitaa10ccba713d49bef6bf474bfd72c0852e3da611 (patch)
tree92f1de8bec31cdb10a02fe8ddac1fbde41b75e7f /packages/cli/src/test-utils
parent6eccb474c77e41aa88d1d1d4ea7eada3e85e746c (diff)
feature(commands) - Refactor Slash Command + Vision For the Future (#3175)
Diffstat (limited to 'packages/cli/src/test-utils')
-rw-r--r--packages/cli/src/test-utils/mockCommandContext.test.ts62
-rw-r--r--packages/cli/src/test-utils/mockCommandContext.ts94
2 files changed, 156 insertions, 0 deletions
diff --git a/packages/cli/src/test-utils/mockCommandContext.test.ts b/packages/cli/src/test-utils/mockCommandContext.test.ts
new file mode 100644
index 00000000..310bf748
--- /dev/null
+++ b/packages/cli/src/test-utils/mockCommandContext.test.ts
@@ -0,0 +1,62 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { vi, describe, it, expect } from 'vitest';
+import { createMockCommandContext } from './mockCommandContext.js';
+
+describe('createMockCommandContext', () => {
+ it('should return a valid CommandContext object with default mocks', () => {
+ const context = createMockCommandContext();
+
+ // Just a few spot checks to ensure the structure is correct
+ // and functions are mocks.
+ expect(context).toBeDefined();
+ expect(context.ui.addItem).toBeInstanceOf(Function);
+ expect(vi.isMockFunction(context.ui.addItem)).toBe(true);
+ });
+
+ it('should apply top-level overrides correctly', () => {
+ const mockClear = vi.fn();
+ const overrides = {
+ ui: {
+ clear: mockClear,
+ },
+ };
+
+ const context = createMockCommandContext(overrides);
+
+ // Call the function to see if the override was used
+ context.ui.clear();
+
+ // Assert that our specific mock was called, not the default
+ expect(mockClear).toHaveBeenCalled();
+ // And that other defaults are still in place
+ expect(vi.isMockFunction(context.ui.addItem)).toBe(true);
+ });
+
+ it('should apply deeply nested overrides correctly', () => {
+ // This is the most important test for factory's logic.
+ const mockConfig = {
+ getProjectRoot: () => '/test/project',
+ getModel: () => 'gemini-pro',
+ };
+
+ const overrides = {
+ services: {
+ config: mockConfig,
+ },
+ };
+
+ const context = createMockCommandContext(overrides);
+
+ expect(context.services.config).toBeDefined();
+ expect(context.services.config?.getModel()).toBe('gemini-pro');
+ expect(context.services.config?.getProjectRoot()).toBe('/test/project');
+
+ // Verify a default property on the same nested object is still there
+ expect(context.services.logger).toBeDefined();
+ });
+});
diff --git a/packages/cli/src/test-utils/mockCommandContext.ts b/packages/cli/src/test-utils/mockCommandContext.ts
new file mode 100644
index 00000000..bf7d814d
--- /dev/null
+++ b/packages/cli/src/test-utils/mockCommandContext.ts
@@ -0,0 +1,94 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { vi } from 'vitest';
+import { CommandContext } from '../ui/commands/types.js';
+import { LoadedSettings } from '../config/settings.js';
+import { GitService } from '@google/gemini-cli-core';
+import { SessionStatsState } from '../ui/contexts/SessionContext.js';
+
+// A utility type to make all properties of an object, and its nested objects, partial.
+type DeepPartial<T> = T extends object
+ ? {
+ [P in keyof T]?: DeepPartial<T[P]>;
+ }
+ : T;
+
+/**
+ * Creates a deep, fully-typed mock of the CommandContext for use in tests.
+ * All functions are pre-mocked with `vi.fn()`.
+ *
+ * @param overrides - A deep partial object to override any default mock values.
+ * @returns A complete, mocked CommandContext object.
+ */
+export const createMockCommandContext = (
+ overrides: DeepPartial<CommandContext> = {},
+): CommandContext => {
+ const defaultMocks: CommandContext = {
+ services: {
+ config: null,
+ settings: { merged: {} } as LoadedSettings,
+ git: undefined as GitService | undefined,
+ logger: {
+ log: vi.fn(),
+ logMessage: vi.fn(),
+ saveCheckpoint: vi.fn(),
+ loadCheckpoint: vi.fn().mockResolvedValue([]),
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } as any, // Cast because Logger is a class.
+ },
+ ui: {
+ addItem: vi.fn(),
+ clear: vi.fn(),
+ setDebugMessage: vi.fn(),
+ },
+ session: {
+ stats: {
+ sessionStartTime: new Date(),
+ lastPromptTokenCount: 0,
+ metrics: {
+ models: {},
+ tools: {
+ totalCalls: 0,
+ totalSuccess: 0,
+ totalFail: 0,
+ totalDurationMs: 0,
+ totalDecisions: { accept: 0, reject: 0, modify: 0 },
+ byName: {},
+ },
+ },
+ } as SessionStatsState,
+ },
+ };
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const merge = (target: any, source: any): any => {
+ const output = { ...target };
+
+ for (const key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ const sourceValue = source[key];
+ const targetValue = output[key];
+
+ if (
+ sourceValue &&
+ typeof sourceValue === 'object' &&
+ !Array.isArray(sourceValue) &&
+ targetValue &&
+ typeof targetValue === 'object' &&
+ !Array.isArray(targetValue)
+ ) {
+ output[key] = merge(targetValue, sourceValue);
+ } else {
+ output[key] = sourceValue;
+ }
+ }
+ }
+ return output;
+ };
+
+ return merge(defaultMocks, overrides);
+};