Skip to content

Commit 57a6ba9

Browse files
authored
Merge branch 'feature/ui-e2e-tests' into basic-chat-test
2 parents 7b0d077 + e97c6e9 commit 57a6ba9

File tree

5 files changed

+235
-25
lines changed

5 files changed

+235
-25
lines changed

.github/workflows/release.yml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ on:
1212
required: false
1313
default: prerelease
1414
push:
15-
branches: [master, feature/*]
15+
branches: [master, feature/*, release/*]
1616
# tags:
1717
# - v[0-9]+.[0-9]+.[0-9]+
1818

@@ -40,12 +40,16 @@ jobs:
4040
# run: echo 'TAG_NAME=prerelease' >> $GITHUB_ENV
4141
- if: github.event_name == 'workflow_dispatch'
4242
run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV
43-
- if: github.ref_name != 'master'
43+
- if: startsWith(github.ref_name, 'feature/')
4444
run: |
45-
TAG_NAME=${{ github.ref_name }}
46-
FEAT_NAME=$(echo $TAG_NAME | sed 's/feature\///')
45+
FEAT_NAME=$(echo ${{ github.ref_name }} | sed 's/feature\///')
4746
echo "FEAT_NAME=$FEAT_NAME" >> $GITHUB_ENV
4847
echo "TAG_NAME=pre-$FEAT_NAME" >> $GITHUB_ENV
48+
- if: startsWith(github.ref_name, 'release/')
49+
run: |
50+
RC_NAME=$(echo ${{ github.ref_name }} | sed 's/release\///')
51+
echo "FEAT_NAME=" >> $GITHUB_ENV
52+
echo "TAG_NAME=$RC_NAME" >> $GITHUB_ENV
4953
- if: github.ref_name == 'master'
5054
run: |
5155
echo "FEAT_NAME=" >> $GITHUB_ENV
@@ -105,10 +109,14 @@ jobs:
105109
- uses: actions/checkout@v4
106110
- uses: actions/download-artifact@v4
107111
- name: Delete existing prerelease
108-
# "prerelease" (main branch) or "pre-<feature>"
109-
if: "env.TAG_NAME == 'prerelease' || startsWith(env.TAG_NAME, 'pre-')"
112+
# "prerelease" (main branch), "pre-<feature>", or "rc-<date>"
113+
if: env.TAG_NAME == 'prerelease' || startsWith(env.TAG_NAME, 'pre-') || startsWith(env.TAG_NAME, 'rc-')
110114
run: |
111-
echo "SUBJECT=AWS IDE Extensions: ${FEAT_NAME:-${TAG_NAME}}" >> $GITHUB_ENV
115+
if [[ "$TAG_NAME" == rc-* ]]; then
116+
echo "SUBJECT=AWS IDE Extensions Release Candidate: ${TAG_NAME#rc-}" >> $GITHUB_ENV
117+
else
118+
echo "SUBJECT=AWS IDE Extensions: ${FEAT_NAME:-${TAG_NAME}}" >> $GITHUB_ENV
119+
fi
112120
gh release delete "$TAG_NAME" --cleanup-tag --yes || true
113121
# git push origin :"$TAG_NAME" || true
114122
- name: Publish Prerelease
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Setup Release Candidate
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
commitId:
7+
description: 'Commit ID to create RC from'
8+
required: true
9+
type: string
10+
11+
jobs:
12+
setup-rc:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: write
16+
steps:
17+
- uses: actions/checkout@v4
18+
with:
19+
ref: ${{ inputs.commitId }}
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
persist-credentials: true
22+
23+
- name: Setup Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: '18'
27+
cache: 'npm'
28+
29+
- name: Generate Branch Name
30+
id: branch-name
31+
run: |
32+
echo "BRANCH_NAME=release/rc-$(date +%Y%m%d)" >> $GITHUB_OUTPUT
33+
34+
- name: Create RC Branch
35+
env:
36+
BRANCH_NAME: ${{ steps.branch-name.outputs.BRANCH_NAME }}
37+
run: |
38+
git config user.name "aws-toolkit-automation"
39+
git config user.email "<>"
40+
41+
# Create RC branch from specified commit
42+
git checkout -b $BRANCH_NAME
43+
44+
# Push RC branch
45+
git push origin $BRANCH_NAME

packages/amazonq/src/app/inline/EditRendering/displayImage.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { EditSuggestionState } from '../editSuggestionState'
1616
import type { AmazonQInlineCompletionItemProvider } from '../completion'
1717
import { vsCodeState } from 'aws-core-vscode/codewhisperer'
1818

19+
const autoRejectEditCursorDistance = 25
20+
1921
export class EditDecorationManager {
2022
private imageDecorationType: vscode.TextEditorDecorationType
2123
private removedCodeDecorationType: vscode.TextEditorDecorationType
@@ -346,6 +348,19 @@ export async function displaySvgDecoration(
346348
void vscode.commands.executeCommand('aws.amazonq.inline.rejectEdit')
347349
}
348350
})
351+
const cursorChangeListener = vscode.window.onDidChangeTextEditorSelection((e) => {
352+
if (!EditSuggestionState.isEditSuggestionActive()) {
353+
return
354+
}
355+
if (e.textEditor !== editor) {
356+
return
357+
}
358+
const currentPosition = e.selections[0].active
359+
const distance = Math.abs(currentPosition.line - startLine)
360+
if (distance > autoRejectEditCursorDistance) {
361+
void vscode.commands.executeCommand('aws.amazonq.inline.rejectEdit')
362+
}
363+
})
349364
await decorationManager.displayEditSuggestion(
350365
editor,
351366
svgImage,
@@ -371,6 +386,7 @@ export async function displaySvgDecoration(
371386

372387
await decorationManager.clearDecorations(editor)
373388
documentChangeListener.dispose()
389+
cursorChangeListener.dispose()
374390
const params: LogInlineCompletionSessionResultsParams = {
375391
sessionId: session.sessionId,
376392
completionSessionResult: {
@@ -405,6 +421,7 @@ export async function displaySvgDecoration(
405421
getLogger().info('Edit suggestion rejected')
406422
await decorationManager.clearDecorations(editor)
407423
documentChangeListener.dispose()
424+
cursorChangeListener.dispose()
408425
const params: LogInlineCompletionSessionResultsParams = {
409426
sessionId: session.sessionId,
410427
completionSessionResult: {

packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ describe('RecommendationService', () => {
129129

130130
describe('getAllRecommendations', () => {
131131
it('should handle single request with no partial result token', async () => {
132+
// Mock EditSuggestionState to return false (no edit suggestion active)
133+
sandbox.stub(EditSuggestionState, 'isEditSuggestionActive').returns(false)
134+
132135
const mockFirstResult = {
133136
sessionId: 'test-session',
134137
items: [mockInlineCompletionItemOne],
@@ -172,6 +175,9 @@ describe('RecommendationService', () => {
172175
})
173176

174177
it('should handle multiple request with partial result token', async () => {
178+
// Mock EditSuggestionState to return false (no edit suggestion active)
179+
sandbox.stub(EditSuggestionState, 'isEditSuggestionActive').returns(false)
180+
175181
const mockFirstResult = {
176182
sessionId: 'test-session',
177183
items: [mockInlineCompletionItemOne],

packages/amazonq/test/unit/app/inline/EditRendering/displayImage.test.ts

Lines changed: 152 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,30 @@
66
import * as vscode from 'vscode'
77
import * as sinon from 'sinon'
88
import 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

1134
describe('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 - extend the stub objects
56+
Object.assign(documentStub, { lineCount: 5 })
57+
Object.assign(editorStub, { edit: sandbox.stub().resolves(true) })
4558

4659
windowStub = sandbox.stub(vscode.window)
4760
windowStub.createTextEditorDecorationType.returns(decorationTypeStub as any)
@@ -174,3 +187,124 @@ 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 windowStub: sinon.SinonStub
195+
let commandsStub: sinon.SinonStub
196+
let editSuggestionStateStub: sinon.SinonStub
197+
let onDidChangeTextEditorSelectionStub: sinon.SinonStub
198+
let selectionChangeListener: (e: vscode.TextEditorSelectionChangeEvent) => void
199+
200+
// Helper function to setup displaySvgDecoration
201+
async function setupDisplaySvgDecoration(startLine: number) {
202+
return await displaySvgDecoration(
203+
editorStub as unknown as vscode.TextEditor,
204+
vscode.Uri.parse('data:image/svg+xml;base64,test'),
205+
startLine,
206+
'new code',
207+
[],
208+
{} as any,
209+
{} as any,
210+
{ itemId: 'test', insertText: 'patch' } as any
211+
)
212+
}
213+
214+
// Helper function to create selection change event
215+
function createSelectionChangeEvent(line: number): vscode.TextEditorSelectionChangeEvent {
216+
const position = new vscode.Position(line, 0)
217+
const selection = new vscode.Selection(position, position)
218+
return {
219+
textEditor: editorStub,
220+
selections: [selection],
221+
kind: vscode.TextEditorSelectionChangeKind.Mouse,
222+
} as vscode.TextEditorSelectionChangeEvent
223+
}
224+
225+
beforeEach(function () {
226+
sandbox = sinon.createSandbox()
227+
228+
const commonStubs = createCommonStubs(sandbox)
229+
editorStub = commonStubs.editorStub
230+
231+
// Mock vscode.window.onDidChangeTextEditorSelection
232+
onDidChangeTextEditorSelectionStub = sandbox.stub()
233+
onDidChangeTextEditorSelectionStub.returns({ dispose: sandbox.stub() })
234+
windowStub = sandbox.stub(vscode.window, 'onDidChangeTextEditorSelection')
235+
windowStub.callsFake((callback) => {
236+
selectionChangeListener = callback
237+
return { dispose: sandbox.stub() }
238+
})
239+
240+
// Mock vscode.commands.executeCommand
241+
commandsStub = sandbox.stub(vscode.commands, 'executeCommand')
242+
243+
// Mock EditSuggestionState
244+
editSuggestionStateStub = sandbox.stub(EditSuggestionState, 'isEditSuggestionActive')
245+
editSuggestionStateStub.returns(true)
246+
247+
// Mock other required dependencies
248+
sandbox.stub(vscode.workspace, 'onDidChangeTextDocument').returns({ dispose: sandbox.stub() })
249+
})
250+
251+
afterEach(function () {
252+
sandbox.restore()
253+
})
254+
255+
it('should not reject when cursor moves less than 25 lines away', async function () {
256+
const startLine = 50
257+
await setupDisplaySvgDecoration(startLine)
258+
259+
selectionChangeListener(createSelectionChangeEvent(startLine + 24))
260+
261+
sinon.assert.notCalled(commandsStub)
262+
})
263+
264+
it('should not reject when cursor moves exactly 25 lines away', async function () {
265+
const startLine = 50
266+
await setupDisplaySvgDecoration(startLine)
267+
268+
selectionChangeListener(createSelectionChangeEvent(startLine + 25))
269+
270+
sinon.assert.notCalled(commandsStub)
271+
})
272+
273+
it('should reject when cursor moves more than 25 lines away', async function () {
274+
const startLine = 50
275+
await setupDisplaySvgDecoration(startLine)
276+
277+
selectionChangeListener(createSelectionChangeEvent(startLine + 26))
278+
279+
sinon.assert.calledOnceWithExactly(commandsStub, 'aws.amazonq.inline.rejectEdit')
280+
})
281+
282+
it('should reject when cursor moves more than 25 lines before the edit', async function () {
283+
const startLine = 50
284+
await setupDisplaySvgDecoration(startLine)
285+
286+
selectionChangeListener(createSelectionChangeEvent(startLine - 26))
287+
288+
sinon.assert.calledOnceWithExactly(commandsStub, 'aws.amazonq.inline.rejectEdit')
289+
})
290+
291+
it('should not reject when edit is near beginning of file and cursor cannot move far enough', async function () {
292+
const startLine = 10
293+
await setupDisplaySvgDecoration(startLine)
294+
295+
selectionChangeListener(createSelectionChangeEvent(0))
296+
297+
sinon.assert.notCalled(commandsStub)
298+
})
299+
300+
it('should not reject when edit suggestion is not active', async function () {
301+
editSuggestionStateStub.returns(false)
302+
303+
const startLine = 50
304+
await setupDisplaySvgDecoration(startLine)
305+
306+
selectionChangeListener(createSelectionChangeEvent(startLine + 100))
307+
308+
sinon.assert.notCalled(commandsStub)
309+
})
310+
})

0 commit comments

Comments
 (0)