CodeActionProvider.test.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import * as vscode from 'vscode';
  2. import { CodeActionProvider } from '../CodeActionProvider';
  3. // Mock VSCode API
  4. jest.mock('vscode', () => ({
  5. CodeAction: jest.fn().mockImplementation((title, kind) => ({
  6. title,
  7. kind,
  8. command: undefined
  9. })),
  10. CodeActionKind: {
  11. QuickFix: { value: 'quickfix' },
  12. RefactorRewrite: { value: 'refactor.rewrite' }
  13. },
  14. Range: jest.fn().mockImplementation((startLine, startChar, endLine, endChar) => ({
  15. start: { line: startLine, character: startChar },
  16. end: { line: endLine, character: endChar }
  17. })),
  18. Position: jest.fn().mockImplementation((line, character) => ({
  19. line,
  20. character
  21. })),
  22. workspace: {
  23. getWorkspaceFolder: jest.fn()
  24. },
  25. DiagnosticSeverity: {
  26. Error: 0,
  27. Warning: 1,
  28. Information: 2,
  29. Hint: 3
  30. }
  31. }));
  32. describe('CodeActionProvider', () => {
  33. let provider: CodeActionProvider;
  34. let mockDocument: any;
  35. let mockRange: any;
  36. let mockContext: any;
  37. beforeEach(() => {
  38. provider = new CodeActionProvider();
  39. // Mock document
  40. mockDocument = {
  41. getText: jest.fn(),
  42. lineAt: jest.fn(),
  43. lineCount: 10,
  44. uri: { fsPath: '/test/file.ts' }
  45. };
  46. // Mock range
  47. mockRange = new vscode.Range(0, 0, 0, 10);
  48. // Mock context
  49. mockContext = {
  50. diagnostics: []
  51. };
  52. });
  53. describe('getEffectiveRange', () => {
  54. it('should return selected text when available', () => {
  55. mockDocument.getText.mockReturnValue('selected text');
  56. const result = (provider as any).getEffectiveRange(mockDocument, mockRange);
  57. expect(result).toEqual({
  58. range: mockRange,
  59. text: 'selected text'
  60. });
  61. });
  62. it('should return null for empty line', () => {
  63. mockDocument.getText.mockReturnValue('');
  64. mockDocument.lineAt.mockReturnValue({ text: '', lineNumber: 0 });
  65. const result = (provider as any).getEffectiveRange(mockDocument, mockRange);
  66. expect(result).toBeNull();
  67. });
  68. });
  69. describe('getFilePath', () => {
  70. it('should return relative path when in workspace', () => {
  71. const mockWorkspaceFolder = {
  72. uri: { fsPath: '/test' }
  73. };
  74. (vscode.workspace.getWorkspaceFolder as jest.Mock).mockReturnValue(mockWorkspaceFolder);
  75. const result = (provider as any).getFilePath(mockDocument);
  76. expect(result).toBe('file.ts');
  77. });
  78. it('should return absolute path when not in workspace', () => {
  79. (vscode.workspace.getWorkspaceFolder as jest.Mock).mockReturnValue(null);
  80. const result = (provider as any).getFilePath(mockDocument);
  81. expect(result).toBe('/test/file.ts');
  82. });
  83. });
  84. describe('provideCodeActions', () => {
  85. beforeEach(() => {
  86. mockDocument.getText.mockReturnValue('test code');
  87. mockDocument.lineAt.mockReturnValue({ text: 'test code', lineNumber: 0 });
  88. });
  89. it('should provide explain and improve actions by default', () => {
  90. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext);
  91. expect(actions).toHaveLength(2);
  92. expect((actions as any)[0].title).toBe('Roo Cline: Explain Code');
  93. expect((actions as any)[1].title).toBe('Roo Cline: Improve Code');
  94. });
  95. it('should provide fix action when diagnostics exist', () => {
  96. mockContext.diagnostics = [{
  97. message: 'test error',
  98. severity: vscode.DiagnosticSeverity.Error,
  99. range: mockRange
  100. }];
  101. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext);
  102. expect(actions).toHaveLength(3);
  103. expect((actions as any).some((a: any) => a.title === 'Roo Cline: Fix Code')).toBe(true);
  104. });
  105. it('should handle errors gracefully', () => {
  106. const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
  107. mockDocument.getText.mockImplementation(() => {
  108. throw new Error('Test error');
  109. });
  110. mockDocument.lineAt.mockReturnValue({ text: 'test', lineNumber: 0 });
  111. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext);
  112. expect(actions).toEqual([]);
  113. expect(consoleErrorSpy).toHaveBeenCalledWith('Error getting effective range:', expect.any(Error));
  114. consoleErrorSpy.mockRestore();
  115. });
  116. });
  117. });