UNPKG

@razorpay/blade-mcp

Version:

Model Context Protocol server for Blade

199 lines 10.5 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { getBladeComponentDocsHttpCallback, getBladeComponentDocsStdioCallback, } from '../getBladeComponentDocs.js'; import * as analyticsUtils from '../../utils/analyticsUtils.js'; import * as cursorRulesUtils from '../../utils/cursorRulesUtils.js'; import * as getBladeDocsResponseText from '../../utils/getBladeDocsResponseText.js'; import * as generalUtils from '../../utils/generalUtils.js'; import { CURSOR_RULES_VERSION } from '../../utils/tokens.js'; // Mock the analytics and utility functions vi.mock('../../utils/analyticsUtils.js', async () => { const actual = await vi.importActual('../../utils/analyticsUtils.js'); return { ...actual, sendAnalytics: vi.fn(), }; }); vi.mock('../../utils/cursorRulesUtils.js'); vi.mock('../../utils/getBladeDocsResponseText.js'); vi.mock('../../utils/generalUtils.js', () => ({ getBladeDocsList: vi.fn(() => ['Button', 'Accordion', 'Input']), })); // Create a mock context object for tool callbacks // eslint-disable-next-line @typescript-eslint/no-explicit-any const createMockContext = () => ({ signal: new AbortController().signal, requestId: 'test-request-id', sendNotification: vi.fn().mockResolvedValue(undefined), sendRequest: vi.fn().mockResolvedValue({}), }); describe('getBladeComponentDocs Tool', () => { beforeEach(() => { vi.clearAllMocks(); // Setup default mocks vi.spyOn(generalUtils, 'getBladeDocsList').mockReturnValue(['Button', 'Accordion', 'Input']); vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); }); it('should return component docs for valid components', () => { const mockCurrentProjectRootDirectory = '/Users/test/project'; const mockComponentsList = 'Button, Accordion'; const mockResponseText = 'Mock component documentation'; // Mock the getBladeDocsResponseText function vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockReturnValue(mockResponseText); // Get the HTTP callback const httpCallback = getBladeComponentDocsHttpCallback; // Call the tool callback const result = httpCallback({ componentsList: mockComponentsList, currentProjectRootDirectory: mockCurrentProjectRootDirectory, clientName: 'cursor', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Verify analytics was called expect(analyticsUtils.sendAnalytics).toHaveBeenCalledWith({ eventName: expect.any(String), properties: { toolName: 'get_blade_component_docs', componentsList: mockComponentsList, rootDirectoryName: 'project', cursorRuleVersion: CURSOR_RULES_VERSION, clientName: 'cursor', }, }); // Verify the result structure expect(result).toHaveProperty('content'); if ('content' in result && !('isError' in result)) { expect(result).toMatchObject({ content: [ { type: 'text', text: mockResponseText.trim(), }, ], }); } // Verify getBladeDocsResponseText was called with correct parameters expect(getBladeDocsResponseText.getBladeDocsResponseText).toHaveBeenCalledWith({ docsList: mockComponentsList, documentationType: 'components', }); }); it('should return error for invalid components', () => { const mockCurrentProjectRootDirectory = '/Users/test/project'; const mockComponentsList = 'InvalidComponent, AnotherInvalid'; // Get the HTTP callback const httpCallback = getBladeComponentDocsHttpCallback; // Call the tool callback const result = httpCallback({ componentsList: mockComponentsList, currentProjectRootDirectory: mockCurrentProjectRootDirectory, clientName: 'cursor', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Verify the result is an error expect(result).toBeDefined(); expect(result).toHaveProperty('isError', true); if ('isError' in result && result.isError) { expect(result).toMatchObject({ content: [ { type: 'text', text: expect.any(String), }, ], }); } // Verify analytics was not called for invalid components expect(analyticsUtils.sendAnalytics).not.toHaveBeenCalled(); }); it('should return consistent component docs response (snapshot)', async () => { const testProjectRootDirectory = '/Users/test/project'; const testComponentsList = 'Button, Accordion'; // Get the actual implementations (not mocked) to test real output const actualGetBladeDocsResponseText = await vi.importActual('../../utils/getBladeDocsResponseText.js'); const actualGeneralUtils = await vi.importActual('../../utils/generalUtils.js'); // Unmock analytics to allow the actual function to run, but we'll still spy on it vi.restoreAllMocks(); vi.spyOn(analyticsUtils, 'sendAnalytics').mockImplementation(() => { // Mock implementation that doesn't throw }); if (actualGetBladeDocsResponseText && actualGeneralUtils) { // Temporarily replace the mocked functions with the actual ones vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockImplementation(actualGetBladeDocsResponseText.getBladeDocsResponseText); vi.spyOn(generalUtils, 'getBladeDocsList').mockImplementation(actualGeneralUtils.getBladeDocsList); } // Mock cursor rules as not needing update vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); // Get the HTTP callback const httpCallback = getBladeComponentDocsHttpCallback; // Call the tool callback with actual implementation const result = httpCallback({ componentsList: testComponentsList, currentProjectRootDirectory: testProjectRootDirectory, clientName: 'cursor', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Snapshot test to ensure the output format remains consistent expect(result).toMatchSnapshot(); }); it('should return consistent component docs response for claude agent (snapshot)', async () => { const testProjectRootDirectory = '/Users/test/project'; const testComponentsList = 'Button, Accordion'; // Get the actual implementations (not mocked) to test real output const actualGetBladeDocsResponseText = await vi.importActual('../../utils/getBladeDocsResponseText.js'); const actualGeneralUtils = await vi.importActual('../../utils/generalUtils.js'); // Unmock analytics to allow the actual function to run, but we'll still spy on it vi.restoreAllMocks(); vi.spyOn(analyticsUtils, 'sendAnalytics').mockImplementation(() => { // Mock implementation that doesn't throw }); if (actualGetBladeDocsResponseText && actualGeneralUtils) { // Temporarily replace the mocked functions with the actual ones vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockImplementation(actualGetBladeDocsResponseText.getBladeDocsResponseText); vi.spyOn(generalUtils, 'getBladeDocsList').mockImplementation(actualGeneralUtils.getBladeDocsList); } // Mock cursor rules as not needing update vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); // Get the HTTP callback const httpCallback = getBladeComponentDocsHttpCallback; // Call the tool callback with actual implementation const result = httpCallback({ componentsList: testComponentsList, currentProjectRootDirectory: testProjectRootDirectory, clientName: 'claude', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Snapshot test to ensure the output format remains consistent expect(result).toMatchSnapshot(); }); it('should return consistent component docs response for stdio transport', async () => { const testProjectRootDirectory = '/Users/test/project'; const testComponentsList = 'Button, Accordion'; // Get the actual implementations (not mocked) to test real output const actualGetBladeDocsResponseText = await vi.importActual('../../utils/getBladeDocsResponseText.js'); const actualGeneralUtils = await vi.importActual('../../utils/generalUtils.js'); // Unmock analytics to allow the actual function to run, but we'll still spy on it vi.restoreAllMocks(); vi.spyOn(analyticsUtils, 'sendAnalytics').mockImplementation(() => { // Mock implementation that doesn't throw }); if (actualGetBladeDocsResponseText && actualGeneralUtils) { // Temporarily replace the mocked functions with the actual ones vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockImplementation(actualGetBladeDocsResponseText.getBladeDocsResponseText); vi.spyOn(generalUtils, 'getBladeDocsList').mockImplementation(actualGeneralUtils.getBladeDocsList); } // Mock cursor rules as not needing update vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); // Get the stdio callback // eslint-disable-next-line @typescript-eslint/no-explicit-any const stdioCallback = getBladeComponentDocsStdioCallback; // Call the tool callback with actual implementation const result = stdioCallback({ componentsList: testComponentsList, currentProjectRootDirectory: testProjectRootDirectory, clientName: 'cursor', }, createMockContext()); // Snapshot test to ensure the output format remains consistent expect(result).toMatchSnapshot(); }); }); //# sourceMappingURL=getBladeComponentDocs.test.js.map