66import * as vscode from 'vscode'
77import * as sinon from 'sinon'
88import assert from 'assert'
9- import { EditDecorationManager } from '../../../../../src/app/inline/EditRendering/displayImage'
9+ import { EditDecorationManager , displaySvgDecoration } from '../../../../../src/app/inline/EditRendering/displayImage'
10+ import { EditSuggestionState } from '../../../../../src/app/inline/editSuggestionState'
11+
12+ // Shared helper function to create common stubs
13+ function createCommonStubs ( sandbox : sinon . SinonSandbox ) {
14+ const documentStub = {
15+ getText : sandbox . stub ( ) . returns ( 'Original code content' ) ,
16+ uri : vscode . Uri . file ( '/test/file.ts' ) ,
17+ lineAt : sandbox . stub ( ) . returns ( {
18+ text : 'Line text content' ,
19+ range : new vscode . Range ( 0 , 0 , 0 , 18 ) ,
20+ rangeIncludingLineBreak : new vscode . Range ( 0 , 0 , 0 , 19 ) ,
21+ firstNonWhitespaceCharacterIndex : 0 ,
22+ isEmptyOrWhitespace : false ,
23+ } ) ,
24+ } as unknown as sinon . SinonStubbedInstance < vscode . TextDocument >
25+
26+ const editorStub = {
27+ document : documentStub ,
28+ setDecorations : sandbox . stub ( ) ,
29+ } as unknown as sinon . SinonStubbedInstance < vscode . TextEditor >
30+
31+ return { documentStub, editorStub }
32+ }
1033
1134describe ( 'EditDecorationManager' , function ( ) {
1235 let sandbox : sinon . SinonSandbox
@@ -25,23 +48,13 @@ describe('EditDecorationManager', function () {
2548 dispose : sandbox . stub ( ) ,
2649 } as unknown as sinon . SinonStubbedInstance < vscode . TextEditorDecorationType >
2750
28- documentStub = {
29- getText : sandbox . stub ( ) . returns ( 'Original code content' ) ,
30- lineCount : 5 ,
31- lineAt : sandbox . stub ( ) . returns ( {
32- text : 'Line text content' ,
33- range : new vscode . Range ( 0 , 0 , 0 , 18 ) ,
34- rangeIncludingLineBreak : new vscode . Range ( 0 , 0 , 0 , 19 ) ,
35- firstNonWhitespaceCharacterIndex : 0 ,
36- isEmptyOrWhitespace : false ,
37- } ) ,
38- } as unknown as sinon . SinonStubbedInstance < vscode . TextDocument >
39-
40- editorStub = {
41- document : documentStub ,
42- setDecorations : sandbox . stub ( ) ,
43- edit : sandbox . stub ( ) . resolves ( true ) ,
44- } as unknown as sinon . SinonStubbedInstance < vscode . TextEditor >
51+ const commonStubs = createCommonStubs ( sandbox )
52+ documentStub = commonStubs . documentStub
53+ editorStub = commonStubs . editorStub
54+
55+ // Add additional properties needed for this test suite
56+ documentStub . lineCount = 5
57+ editorStub . edit = sandbox . stub ( ) . resolves ( true )
4558
4659 windowStub = sandbox . stub ( vscode . window )
4760 windowStub . createTextEditorDecorationType . returns ( decorationTypeStub as any )
@@ -174,3 +187,126 @@ describe('EditDecorationManager', function () {
174187 sinon . assert . calledWith ( editorStub . setDecorations . secondCall , manager [ 'removedCodeDecorationType' ] , [ ] )
175188 } )
176189} )
190+
191+ describe ( 'displaySvgDecoration cursor distance auto-reject' , function ( ) {
192+ let sandbox : sinon . SinonSandbox
193+ let editorStub : sinon . SinonStubbedInstance < vscode . TextEditor >
194+ let documentStub : sinon . SinonStubbedInstance < vscode . TextDocument >
195+ let windowStub : sinon . SinonStub
196+ let commandsStub : sinon . SinonStub
197+ let editSuggestionStateStub : sinon . SinonStub
198+ let onDidChangeTextEditorSelectionStub : sinon . SinonStub
199+ let selectionChangeListener : ( e : vscode . TextEditorSelectionChangeEvent ) => void
200+
201+ // Helper function to setup displaySvgDecoration
202+ async function setupDisplaySvgDecoration ( startLine : number ) {
203+ return await displaySvgDecoration (
204+ editorStub as unknown as vscode . TextEditor ,
205+ vscode . Uri . parse ( 'data:image/svg+xml;base64,test' ) ,
206+ startLine ,
207+ 'new code' ,
208+ [ ] ,
209+ { } as any ,
210+ { } as any ,
211+ { itemId : 'test' , insertText : 'patch' } as any
212+ )
213+ }
214+
215+ // Helper function to create selection change event
216+ function createSelectionChangeEvent ( line : number ) : vscode . TextEditorSelectionChangeEvent {
217+ const position = new vscode . Position ( line , 0 )
218+ const selection = new vscode . Selection ( position , position )
219+ return {
220+ textEditor : editorStub ,
221+ selections : [ selection ] ,
222+ kind : vscode . TextEditorSelectionChangeKind . Mouse ,
223+ } as vscode . TextEditorSelectionChangeEvent
224+ }
225+
226+ beforeEach ( function ( ) {
227+ sandbox = sinon . createSandbox ( )
228+
229+ const commonStubs = createCommonStubs ( sandbox )
230+ documentStub = commonStubs . documentStub
231+ editorStub = commonStubs . editorStub
232+
233+ // Mock vscode.window.onDidChangeTextEditorSelection
234+ onDidChangeTextEditorSelectionStub = sandbox . stub ( )
235+ onDidChangeTextEditorSelectionStub . returns ( { dispose : sandbox . stub ( ) } )
236+ windowStub = sandbox . stub ( vscode . window , 'onDidChangeTextEditorSelection' )
237+ windowStub . callsFake ( ( callback ) => {
238+ selectionChangeListener = callback
239+ return { dispose : sandbox . stub ( ) }
240+ } )
241+
242+ // Mock vscode.commands.executeCommand
243+ commandsStub = sandbox . stub ( vscode . commands , 'executeCommand' )
244+
245+ // Mock EditSuggestionState
246+ editSuggestionStateStub = sandbox . stub ( EditSuggestionState , 'isEditSuggestionActive' )
247+ editSuggestionStateStub . returns ( true )
248+
249+ // Mock other required dependencies
250+ sandbox . stub ( vscode . workspace , 'onDidChangeTextDocument' ) . returns ( { dispose : sandbox . stub ( ) } )
251+ } )
252+
253+ afterEach ( function ( ) {
254+ sandbox . restore ( )
255+ } )
256+
257+ it ( 'should not reject when cursor moves less than 25 lines away' , async function ( ) {
258+ const startLine = 50
259+ await setupDisplaySvgDecoration ( startLine )
260+
261+ selectionChangeListener ( createSelectionChangeEvent ( startLine + 24 ) )
262+
263+ sinon . assert . notCalled ( commandsStub )
264+ } )
265+
266+ it ( 'should not reject when cursor moves exactly 25 lines away' , async function ( ) {
267+ const startLine = 50
268+ await setupDisplaySvgDecoration ( startLine )
269+
270+ selectionChangeListener ( createSelectionChangeEvent ( startLine + 25 ) )
271+
272+ sinon . assert . notCalled ( commandsStub )
273+ } )
274+
275+ it ( 'should reject when cursor moves more than 25 lines away' , async function ( ) {
276+ const startLine = 50
277+ await setupDisplaySvgDecoration ( startLine )
278+
279+ selectionChangeListener ( createSelectionChangeEvent ( startLine + 26 ) )
280+
281+ sinon . assert . calledOnceWithExactly ( commandsStub , 'aws.amazonq.inline.rejectEdit' )
282+ } )
283+
284+ it ( 'should reject when cursor moves more than 25 lines before the edit' , async function ( ) {
285+ const startLine = 50
286+ await setupDisplaySvgDecoration ( startLine )
287+
288+ selectionChangeListener ( createSelectionChangeEvent ( startLine - 26 ) )
289+
290+ sinon . assert . calledOnceWithExactly ( commandsStub , 'aws.amazonq.inline.rejectEdit' )
291+ } )
292+
293+ it ( 'should not reject when edit is near beginning of file and cursor cannot move far enough' , async function ( ) {
294+ const startLine = 10
295+ await setupDisplaySvgDecoration ( startLine )
296+
297+ selectionChangeListener ( createSelectionChangeEvent ( 0 ) )
298+
299+ sinon . assert . notCalled ( commandsStub )
300+ } )
301+
302+ it ( 'should not reject when edit suggestion is not active' , async function ( ) {
303+ editSuggestionStateStub . returns ( false )
304+
305+ const startLine = 50
306+ await setupDisplaySvgDecoration ( startLine )
307+
308+ selectionChangeListener ( createSelectionChangeEvent ( startLine + 100 ) )
309+
310+ sinon . assert . notCalled ( commandsStub )
311+ } )
312+ } )
0 commit comments