Skip to content

Commit c3ac3d8

Browse files
committed
Merge branch 'master' into auth/loadedConfigFile
2 parents 4befa8c + 08bedbc commit c3ac3d8

File tree

350 files changed

+2137
-1589
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

350 files changed

+2137
-1589
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ module.exports = {
180180
'aws-toolkits/no-console-log': 'error',
181181
'aws-toolkits/no-json-stringify-in-log': 'error',
182182
'aws-toolkits/no-printf-mismatch': 'error',
183+
'aws-toolkits/no-index-import': 'error',
183184
'no-restricted-imports': [
184185
'error',
185186
{
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Amazon Q /test: Q identify active test file and infer source file for test generation."
4+
}

packages/amazonq/test/e2e/amazonq/featureDev.test.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ import sinon from 'sinon'
99
import { registerAuthHook, using } from 'aws-core-vscode/test'
1010
import { loginToIdC } from './utils/setup'
1111
import { Messenger } from './framework/messenger'
12-
import { examples } from 'aws-core-vscode/amazonqFeatureDev'
1312
import { FollowUpTypes } from 'aws-core-vscode/amazonq'
1413
import { sleep } from 'aws-core-vscode/shared'
1514

1615
describe('Amazon Q Feature Dev', function () {
1716
let framework: qTestingFramework
1817
let tab: Messenger
1918

20-
const prompt = 'Add blank.txt file with empty content'
21-
const codegenApproachPrompt = `${prompt} and add a readme that describes the changes`
22-
const fileLevelAcceptPrompt = `${prompt} and add a license, and a contributing file`
19+
const prompt = 'Add current timestamp into blank.txt'
20+
const iteratePrompt = `Add a new section in readme to explain your change`
21+
const fileLevelAcceptPrompt = `${prompt} and ${iteratePrompt}`
22+
const informationCard =
23+
'After you provide a task, I will:\n1. Generate code based on your description and the code in your workspace\n2. Provide a list of suggestions for you to review and add to your workspace\n3. If needed, iterate based on your feedback\nTo learn more, visit the [user guide](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/software-dev.html)'
2324
const tooManyRequestsWaitTime = 100000
2425

2526
async function waitForText(text: string) {
@@ -144,34 +145,35 @@ describe('Amazon Q Feature Dev', function () {
144145
})
145146

146147
describe('/dev entry', () => {
147-
it('Clicks examples', async () => {
148-
const q = framework.createTab()
149-
q.addChatMessage({ command: '/dev' })
148+
before(async () => {
149+
tab = framework.createTab()
150+
tab.addChatMessage({ command: '/dev' }) // This would create a new tab for feature dev.
151+
tab = framework.getSelectedTab()
152+
})
153+
154+
it('should display information card', async () => {
150155
await retryIfRequired(
151156
async () => {
152-
await q.waitForChatFinishesLoading()
157+
await tab.waitForChatFinishesLoading()
153158
},
154159
() => {
155-
q.clickButton(FollowUpTypes.DevExamples)
156-
157-
const lastChatItems = q.getChatItems().pop()
158-
assert.deepStrictEqual(lastChatItems?.body, examples)
160+
const lastChatItems = tab.getChatItems().pop()
161+
assert.deepStrictEqual(lastChatItems?.body, informationCard)
159162
}
160163
)
161164
})
162165
})
163166

164-
// Disable failing tests while investigation. The tests are only failing in CI environments.
165-
describe.skip('/dev {msg} entry', async () => {
167+
describe('/dev {msg} entry', async () => {
166168
beforeEach(async function () {
169+
tab = framework.createTab()
167170
tab.addChatMessage({ command: '/dev', prompt })
171+
tab = framework.getSelectedTab()
168172
await retryIfRequired(
169173
async () => {
170174
await tab.waitForChatFinishesLoading()
171175
},
172-
() => {
173-
tab.addChatMessage({ prompt })
174-
}
176+
() => {}
175177
)
176178
})
177179

@@ -211,15 +213,17 @@ describe('Amazon Q Feature Dev', function () {
211213
})
212214
tab.clickButton(FollowUpTypes.ProvideFeedbackAndRegenerateCode)
213215
await tab.waitForChatFinishesLoading()
214-
await iterate(codegenApproachPrompt)
216+
await iterate(iteratePrompt)
215217
tab.clickButton(FollowUpTypes.InsertCode)
216218
await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
217219
})
218220
})
219221

220-
describe.skip('file-level accepts', async () => {
222+
describe('file-level accepts', async () => {
221223
beforeEach(async function () {
224+
tab = framework.createTab()
222225
tab.addChatMessage({ command: '/dev', prompt: fileLevelAcceptPrompt })
226+
tab = framework.getSelectedTab()
223227
await retryIfRequired(
224228
async () => {
225229
await tab.waitForChatFinishesLoading()

packages/amazonq/test/e2e/amazonq/framework/framework.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ export class qTestingFramework {
105105
return Object.entries(tabs).map(([tabId]) => new Messenger(tabId, this.mynahUIProps, this.mynahUI))
106106
}
107107

108+
public getSelectedTab() {
109+
const selectedTabId = this.mynahUI.getSelectedTabId()
110+
const selectedTab = this.getTabs().find((tab) => tab.tabID === selectedTabId)
111+
112+
if (!selectedTab) {
113+
assert.fail('Selected tab not found')
114+
}
115+
return selectedTab
116+
}
117+
108118
public findTab(title: string) {
109119
return Object.values(this.getTabs()).find((tab) => tab.getStore().tabTitle === title)
110120
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as vscode from 'vscode'
7+
import assert from 'assert'
8+
import {
9+
assertTelemetry,
10+
closeAllEditors,
11+
getTestWindow,
12+
registerAuthHook,
13+
resetCodeWhispererGlobalVariables,
14+
TestFolder,
15+
toTextEditor,
16+
using,
17+
} from 'aws-core-vscode/test'
18+
import { RecommendationHandler, RecommendationService } from 'aws-core-vscode/codewhisperer'
19+
import { Commands, globals, sleep, waitUntil } from 'aws-core-vscode/shared'
20+
import { loginToIdC } from '../amazonq/utils/setup'
21+
22+
describe('Amazon Q Inline', async function () {
23+
let tempFolder: string
24+
const waitOptions = {
25+
interval: 500,
26+
timeout: 10000,
27+
retryOnFail: false,
28+
}
29+
30+
before(async function () {
31+
await using(registerAuthHook('amazonq-test-account'), async () => {
32+
await loginToIdC()
33+
})
34+
})
35+
36+
beforeEach(async function () {
37+
registerAuthHook('amazonq-test-account')
38+
const folder = await TestFolder.create()
39+
tempFolder = folder.path
40+
await closeAllEditors()
41+
await resetCodeWhispererGlobalVariables(false)
42+
})
43+
44+
afterEach(async function () {
45+
await closeAllEditors()
46+
})
47+
48+
async function setupEditor({ name, contents }: { name?: string; contents?: string } = {}) {
49+
const fileName = name ?? 'test.ts'
50+
const textContents =
51+
contents ??
52+
`function fib() {
53+
54+
55+
}`
56+
await toTextEditor(textContents, fileName, tempFolder, {
57+
selection: new vscode.Range(new vscode.Position(1, 4), new vscode.Position(1, 4)),
58+
})
59+
}
60+
61+
async function waitForRecommendations() {
62+
const ok = await waitUntil(async () => RecommendationHandler.instance.isSuggestionVisible(), waitOptions)
63+
if (!ok) {
64+
assert.fail('Suggestions failed to become visible')
65+
}
66+
}
67+
68+
async function waitForTelemetry() {
69+
const ok = await waitUntil(
70+
async () =>
71+
globals.telemetry.logger.query({
72+
metricName: 'codewhisperer_userTriggerDecision',
73+
}).length > 0,
74+
waitOptions
75+
)
76+
if (!ok) {
77+
assert.fail('Telemetry failed to be emitted')
78+
}
79+
}
80+
81+
for (const [name, invokeCompletion] of [
82+
['automatic', async () => await vscode.commands.executeCommand('type', { text: '\n' })],
83+
['manual', async () => Commands.tryExecute('aws.amazonq.invokeInlineCompletion')],
84+
] as const) {
85+
describe(`${name} invoke`, async function () {
86+
let originalEditorContents: string | undefined
87+
88+
describe('supported filetypes', () => {
89+
beforeEach(async () => {
90+
await setupEditor()
91+
92+
/**
93+
* Allow some time between when the editor is opened and when we start typing.
94+
* If we don't do this then the time between the initial editor selection
95+
* and invoking the "type" command is too low, causing completion to never
96+
* activate. AFAICT there isn't anything we can use waitUntil on here.
97+
*
98+
* note: this number is entirely arbitrary
99+
**/
100+
await sleep(1000)
101+
102+
await invokeCompletion()
103+
originalEditorContents = vscode.window.activeTextEditor?.document.getText()
104+
105+
// wait until the ghost text appears
106+
await waitForRecommendations()
107+
})
108+
109+
it(`${name} invoke accept`, async function () {
110+
/**
111+
* keep accepting the suggestion until the text contents change
112+
* this is required because we have no access to the inlineSuggest panel
113+
**/
114+
const suggestionAccepted = await waitUntil(async () => {
115+
// Accept the suggestion
116+
await vscode.commands.executeCommand('editor.action.inlineSuggest.commit')
117+
return vscode.window.activeTextEditor?.document.getText() !== originalEditorContents
118+
}, waitOptions)
119+
120+
assert.ok(suggestionAccepted, 'Editor contents should have changed')
121+
122+
await waitForTelemetry()
123+
assertTelemetry('codewhisperer_userTriggerDecision', {
124+
codewhispererSuggestionState: 'Accept',
125+
})
126+
})
127+
128+
it(`${name} invoke reject`, async function () {
129+
// Reject the suggestion
130+
await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion')
131+
132+
// Contents haven't changed
133+
assert.deepStrictEqual(vscode.window.activeTextEditor?.document.getText(), originalEditorContents)
134+
135+
await waitForTelemetry()
136+
assertTelemetry('codewhisperer_userTriggerDecision', {
137+
codewhispererSuggestionState: 'Reject',
138+
})
139+
})
140+
141+
it(`${name} invoke discard`, async function () {
142+
// Discard the suggestion by moving it back to the original position
143+
const position = new vscode.Position(1, 4)
144+
const editor = vscode.window.activeTextEditor
145+
if (!editor) {
146+
assert.fail('Could not find text editor')
147+
}
148+
editor.selection = new vscode.Selection(position, position)
149+
150+
// Contents are the same
151+
assert.deepStrictEqual(vscode.window.activeTextEditor?.document.getText(), originalEditorContents)
152+
})
153+
})
154+
155+
it(`${name} invoke on unsupported filetype`, async function () {
156+
await setupEditor({
157+
name: 'test.zig',
158+
contents: `fn doSomething() void {
159+
160+
}`,
161+
})
162+
163+
/**
164+
* Add delay between editor loading and invoking completion
165+
* @see beforeEach in supported filetypes for more information
166+
*/
167+
await sleep(1000)
168+
await invokeCompletion()
169+
170+
if (name === 'automatic') {
171+
// It should never get triggered since its not a supported file type
172+
assert.deepStrictEqual(RecommendationService.instance.isRunning, false)
173+
} else {
174+
await getTestWindow().waitForMessage('currently not supported by Amazon Q inline suggestions')
175+
}
176+
})
177+
})
178+
}
179+
})

packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {
1818
sessionWriteFile,
1919
assertTelemetry,
2020
} from 'aws-core-vscode/test'
21-
import { CurrentWsFolders, CodeGenState, FeatureDevClient, featureDevScheme } from 'aws-core-vscode/amazonqFeatureDev'
22-
import { Messenger } from 'aws-core-vscode/amazonq'
21+
import { FeatureDevClient, featureDevScheme, FeatureDevCodeGenState } from 'aws-core-vscode/amazonqFeatureDev'
22+
import { Messenger, CurrentWsFolders } from 'aws-core-vscode/amazonq'
2323
import path from 'path'
2424
import { fs } from 'aws-core-vscode/shared'
2525

@@ -75,7 +75,7 @@ describe('session', () => {
7575
workspaceFolders,
7676
}
7777

78-
const codeGenState = new CodeGenState(
78+
const codeGenState = new FeatureDevCodeGenState(
7979
testConfig,
8080
[
8181
{
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { ServiceOptions } from '../../shared/awsClientBuilder'
7+
8+
export abstract class FeatureClient {
9+
public abstract getClient(options?: Partial<ServiceOptions>): Promise<any>
10+
11+
public abstract createConversation(): Promise<string>
12+
13+
public abstract createUploadUrl(
14+
conversationId: string,
15+
contentChecksumSha256: string,
16+
contentLength: number,
17+
uploadId: string
18+
): Promise<any>
19+
20+
public abstract startCodeGeneration(
21+
conversationId: string,
22+
uploadId: string,
23+
message: string,
24+
intent: any,
25+
codeGenerationId: string,
26+
currentCodeGenerationId?: string,
27+
intentContext?: any
28+
): Promise<any>
29+
30+
public abstract getCodeGeneration(conversationId: string, codeGenerationId: string): Promise<any>
31+
32+
public abstract exportResultArchive(conversationId: string): Promise<any>
33+
}

packages/core/src/amazonq/commons/connector/baseMessenger.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { ChatItemAction, ProgressField } from '@aws/mynah-ui'
77
import { AuthFollowUpType, AuthMessageDataMap } from '../../../amazonq/auth/model'
8-
import { FeatureAuthState } from '../../../codewhisperer'
98
import { i18n } from '../../../shared/i18n-helper'
109
import { CodeReference } from '../../../amazonq/webview/ui/connector'
1110

@@ -25,9 +24,9 @@ import {
2524
UpdatePlaceholderMessage,
2625
UpdatePromptProgressMessage,
2726
} from './connectorMessages'
28-
import { FollowUpTypes } from '../types'
27+
import { DeletedFileInfo, FollowUpTypes, NewFileInfo } from '../types'
2928
import { messageWithConversationId } from '../../../amazonqFeatureDev/userFacingText'
30-
import { DeletedFileInfo, NewFileInfo } from '../../../amazonqFeatureDev/types'
29+
import { FeatureAuthState } from '../../../codewhisperer/util/authUtil'
3130

3231
export class Messenger {
3332
public constructor(

packages/core/src/amazonq/commons/connector/connectorMessages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { MessagePublisher } from '../../messages/messagePublisher'
88
import { CodeReference } from '../../webview/ui/connector'
99
import { ChatItemAction, ProgressField, SourceLink } from '@aws/mynah-ui'
1010
import { ChatItemType } from '../model'
11-
import { DeletedFileInfo, NewFileInfo } from '../../../amazonqFeatureDev/types'
11+
import { DeletedFileInfo, NewFileInfo } from '../types'
1212
import { licenseText } from '../../../amazonqFeatureDev/constants'
1313

1414
class UiMessage {

0 commit comments

Comments
 (0)