CodeActionProvider.spec.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import type { Mock } from "vitest"
  2. import * as vscode from "vscode"
  3. import { EditorUtils } from "../../integrations/editor/EditorUtils"
  4. import { CodeActionProvider, TITLES } from "../CodeActionProvider"
  5. vi.mock("vscode", () => ({
  6. CodeAction: vi.fn().mockImplementation((title, kind) => ({
  7. title,
  8. kind,
  9. command: undefined,
  10. })),
  11. CodeActionKind: {
  12. QuickFix: { value: "quickfix" },
  13. RefactorRewrite: { value: "refactor.rewrite" },
  14. },
  15. Range: vi.fn().mockImplementation((startLine, startChar, endLine, endChar) => ({
  16. start: { line: startLine, character: startChar },
  17. end: { line: endLine, character: endChar },
  18. })),
  19. DiagnosticSeverity: {
  20. Error: 0,
  21. Warning: 1,
  22. Information: 2,
  23. Hint: 3,
  24. },
  25. workspace: {
  26. getConfiguration: vi.fn().mockReturnValue({
  27. get: vi.fn().mockReturnValue(true),
  28. }),
  29. },
  30. }))
  31. vi.mock("../../integrations/editor/EditorUtils", () => ({
  32. EditorUtils: {
  33. getEffectiveRange: vi.fn(),
  34. getFilePath: vi.fn(),
  35. hasIntersectingRange: vi.fn(),
  36. createDiagnosticData: vi.fn(),
  37. },
  38. }))
  39. describe("CodeActionProvider", () => {
  40. let provider: CodeActionProvider
  41. let mockDocument: any
  42. let mockRange: any
  43. let mockContext: any
  44. beforeEach(() => {
  45. provider = new CodeActionProvider()
  46. mockDocument = {
  47. getText: vi.fn(),
  48. lineAt: vi.fn(),
  49. lineCount: 10,
  50. uri: { fsPath: "/test/file.ts" },
  51. }
  52. mockRange = new vscode.Range(0, 0, 0, 10)
  53. mockContext = { diagnostics: [] }
  54. ;(EditorUtils.getEffectiveRange as Mock).mockReturnValue({
  55. range: mockRange,
  56. text: "test code",
  57. })
  58. ;(EditorUtils.getFilePath as Mock).mockReturnValue("/test/file.ts")
  59. ;(EditorUtils.hasIntersectingRange as Mock).mockReturnValue(true)
  60. ;(EditorUtils.createDiagnosticData as Mock).mockImplementation((d) => d)
  61. })
  62. describe("provideCodeActions", () => {
  63. it("should provide explain, improve, fix logic, and add to context actions by default", () => {
  64. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
  65. expect(actions).toHaveLength(3)
  66. expect((actions as any)[0].title).toBe(TITLES.ADD_TO_CONTEXT)
  67. expect((actions as any)[1].title).toBe(TITLES.EXPLAIN)
  68. expect((actions as any)[2].title).toBe(TITLES.IMPROVE)
  69. })
  70. it("should provide fix action instead of fix logic when diagnostics exist", () => {
  71. mockContext.diagnostics = [
  72. { message: "test error", severity: vscode.DiagnosticSeverity.Error, range: mockRange },
  73. ]
  74. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
  75. expect(actions).toHaveLength(2)
  76. expect((actions as any).some((a: any) => a.title === `${TITLES.FIX}`)).toBe(true)
  77. expect((actions as any).some((a: any) => a.title === `${TITLES.ADD_TO_CONTEXT}`)).toBe(true)
  78. })
  79. it("should return empty array when no effective range", () => {
  80. ;(EditorUtils.getEffectiveRange as Mock).mockReturnValue(null)
  81. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
  82. expect(actions).toEqual([])
  83. })
  84. it("should return empty array when enableCodeActions is disabled", () => {
  85. // Mock the configuration to return false for enableCodeActions
  86. const mockGet = vi.fn().mockReturnValue(false)
  87. const mockGetConfiguration = vi.fn().mockReturnValue({
  88. get: mockGet,
  89. })
  90. ;(vscode.workspace.getConfiguration as Mock).mockReturnValue(mockGetConfiguration())
  91. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
  92. expect(actions).toEqual([])
  93. expect(vscode.workspace.getConfiguration).toHaveBeenCalledWith("roo-cline")
  94. expect(mockGet).toHaveBeenCalledWith("enableCodeActions", true)
  95. })
  96. it("should handle errors gracefully", () => {
  97. const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
  98. // Reset the workspace mock to return true for enableCodeActions
  99. const mockGet = vi.fn().mockReturnValue(true)
  100. const mockGetConfiguration = vi.fn().mockReturnValue({
  101. get: mockGet,
  102. })
  103. ;(vscode.workspace.getConfiguration as Mock).mockReturnValue(mockGetConfiguration())
  104. ;(EditorUtils.getEffectiveRange as Mock).mockImplementation(() => {
  105. throw new Error("Test error")
  106. })
  107. const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
  108. expect(actions).toEqual([])
  109. expect(consoleErrorSpy).toHaveBeenCalledWith("Error providing code actions:", expect.any(Error))
  110. consoleErrorSpy.mockRestore()
  111. })
  112. })
  113. })