Skip to content

Commit 085ad38

Browse files
authored
test(amazonq): E2E tests for /test #6431
## Problem - There are currently no E2E tests for /test ## Solution - Created E2E tests for /test: view diff, accept, reject, unsupported language, external file
1 parent 3c40aca commit 085ad38

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import vscode from 'vscode'
8+
import { qTestingFramework } from './framework/framework'
9+
import sinon from 'sinon'
10+
import { Messenger } from './framework/messenger'
11+
import { FollowUpTypes } from 'aws-core-vscode/amazonq'
12+
import { registerAuthHook, using, TestFolder } from 'aws-core-vscode/test'
13+
import { loginToIdC } from './utils/setup'
14+
import { waitUntil, workspaceUtils } from 'aws-core-vscode/shared'
15+
16+
describe('Amazon Q Test Generation', function () {
17+
let framework: qTestingFramework
18+
let tab: Messenger
19+
20+
const testFiles = [
21+
{
22+
language: 'python',
23+
filePath: 'python3.7-image-sam-app/hello_world/app.py',
24+
},
25+
{
26+
language: 'java',
27+
filePath: 'java17-gradle/HelloWorldFunction/src/main/java/helloworld/App.java',
28+
},
29+
]
30+
31+
const unsupportedLanguages = [
32+
// move these over to testFiles once these languages are supported
33+
// must be atleast one unsupported language here for testing
34+
{
35+
language: 'typescript',
36+
filePath: 'ts-plain-sam-app/src/app.ts',
37+
},
38+
{
39+
language: 'javascript',
40+
filePath: 'js-plain-sam-app/src/app.js',
41+
},
42+
]
43+
44+
async function setupTestDocument(filePath: string, language: string) {
45+
const document = await waitUntil(async () => {
46+
const doc = await workspaceUtils.openTextDocument(filePath)
47+
return doc
48+
}, {})
49+
50+
if (!document) {
51+
assert.fail(`Failed to open ${language} file`)
52+
}
53+
54+
await vscode.window.showTextDocument(document, { preview: false })
55+
56+
const activeEditor = vscode.window.activeTextEditor
57+
if (!activeEditor || activeEditor.document !== document) {
58+
assert.fail(`Failed to make temp file active`)
59+
}
60+
}
61+
62+
async function waitForChatItems(index: number) {
63+
await tab.waitForEvent(() => tab.getChatItems().length > index, {
64+
waitTimeoutInMs: 5000,
65+
waitIntervalInMs: 1000,
66+
})
67+
}
68+
69+
before(async function () {
70+
await using(registerAuthHook('amazonq-test-account'), async () => {
71+
await loginToIdC()
72+
})
73+
})
74+
75+
beforeEach(async () => {
76+
registerAuthHook('amazonq-test-account')
77+
framework = new qTestingFramework('testgen', true, [])
78+
tab = framework.createTab()
79+
})
80+
81+
afterEach(async () => {
82+
// Close all editors to prevent conflicts with subsequent tests trying to open the same file
83+
await vscode.commands.executeCommand('workbench.action.closeAllEditors')
84+
framework.removeTab(tab.tabID)
85+
framework.dispose()
86+
sinon.restore()
87+
})
88+
89+
describe('Quick action availability', () => {
90+
it('Shows /test when test generation is enabled', async () => {
91+
const command = tab.findCommand('/test')
92+
if (!command.length) {
93+
assert.fail('Could not find command')
94+
}
95+
if (command.length > 1) {
96+
assert.fail('Found too many commands with the name /test')
97+
}
98+
})
99+
100+
it('Does NOT show /test when test generation is NOT enabled', () => {
101+
// The beforeEach registers a framework which accepts requests. If we don't dispose before building a new one we have duplicate messages
102+
framework.dispose()
103+
framework = new qTestingFramework('testgen', false, [])
104+
const tab = framework.createTab()
105+
const command = tab.findCommand('/test')
106+
if (command.length > 0) {
107+
assert.fail('Found command when it should not have been found')
108+
}
109+
})
110+
})
111+
112+
describe('/test entry', () => {
113+
describe('Unsupported language', () => {
114+
const { language, filePath } = unsupportedLanguages[0]
115+
116+
beforeEach(async () => {
117+
await setupTestDocument(filePath, language)
118+
})
119+
120+
it(`/test for unsupported language redirects to chat`, async () => {
121+
tab.addChatMessage({ command: '/test' })
122+
await tab.waitForChatFinishesLoading()
123+
124+
await waitForChatItems(3)
125+
const unsupportedLanguageMessage = tab.getChatItems()[3]
126+
127+
assert.deepStrictEqual(unsupportedLanguageMessage.type, 'answer')
128+
assert.deepStrictEqual(
129+
unsupportedLanguageMessage.body,
130+
`<span style="color: #EE9D28;">&#9888;<b>I'm sorry, but /test only supports Python and Java</b><br></span> While ${language.charAt(0).toUpperCase() + language.slice(1)} is not supported, I will generate a suggestion below.`
131+
)
132+
})
133+
})
134+
135+
describe('External file', async () => {
136+
let testFolder: TestFolder
137+
let fileName: string
138+
139+
beforeEach(async () => {
140+
testFolder = await TestFolder.create()
141+
fileName = 'test.py'
142+
const filePath = await testFolder.write(fileName, 'def add(a, b): return a + b')
143+
144+
const document = await vscode.workspace.openTextDocument(filePath)
145+
await vscode.window.showTextDocument(document, { preview: false })
146+
})
147+
148+
it('/test for external file redirects to chat', async () => {
149+
tab.addChatMessage({ command: '/test' })
150+
await tab.waitForChatFinishesLoading()
151+
152+
await waitForChatItems(3)
153+
const externalFileMessage = tab.getChatItems()[3]
154+
155+
assert.deepStrictEqual(externalFileMessage.type, 'answer')
156+
assert.deepStrictEqual(
157+
externalFileMessage.body,
158+
`<span style="color: #EE9D28;">&#9888;<b>I can't generate tests for ${fileName}</b> because the file is outside of workspace scope.<br></span> I can still provide examples, instructions and code suggestions.`
159+
)
160+
})
161+
})
162+
163+
for (const { language, filePath } of testFiles) {
164+
describe(`${language} file`, () => {
165+
beforeEach(async () => {
166+
await setupTestDocument(filePath, language)
167+
168+
tab.addChatMessage({ command: '/test' })
169+
await tab.waitForChatFinishesLoading()
170+
171+
await tab.waitForButtons([FollowUpTypes.ViewDiff])
172+
tab.clickButton(FollowUpTypes.ViewDiff)
173+
await tab.waitForChatFinishesLoading()
174+
})
175+
176+
describe('View diff', async () => {
177+
it('Clicks on view diff', async () => {
178+
const chatItems = tab.getChatItems()
179+
const viewDiffMessage = chatItems[5]
180+
181+
assert.deepStrictEqual(viewDiffMessage.type, 'answer')
182+
assert.deepStrictEqual(
183+
viewDiffMessage.body,
184+
'Please see the unit tests generated below. Click “View diff” to review the changes in the code editor.'
185+
)
186+
})
187+
})
188+
189+
describe('Accept code', async () => {
190+
it('Clicks on accept', async () => {
191+
await tab.waitForButtons([FollowUpTypes.AcceptCode, FollowUpTypes.RejectCode])
192+
tab.clickButton(FollowUpTypes.AcceptCode)
193+
await tab.waitForChatFinishesLoading()
194+
195+
await waitForChatItems(7)
196+
const acceptedMessage = tab.getChatItems()[7]
197+
198+
assert.deepStrictEqual(acceptedMessage?.type, 'answer-part')
199+
assert.deepStrictEqual(acceptedMessage?.followUp?.options?.[0].pillText, 'Accepted')
200+
})
201+
})
202+
203+
describe('Reject code', async () => {
204+
it('Clicks on reject', async () => {
205+
await tab.waitForButtons([FollowUpTypes.AcceptCode, FollowUpTypes.RejectCode])
206+
tab.clickButton(FollowUpTypes.RejectCode)
207+
await tab.waitForChatFinishesLoading()
208+
209+
await waitForChatItems(7)
210+
const rejectedMessage = tab.getChatItems()[7]
211+
212+
assert.deepStrictEqual(rejectedMessage?.type, 'answer-part')
213+
assert.deepStrictEqual(rejectedMessage?.followUp?.options?.[0].pillText, 'Rejected')
214+
})
215+
})
216+
})
217+
}
218+
})
219+
})

packages/core/src/shared/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export { AmazonqCreateUpload, Metric } from './telemetry/telemetry'
3030
export { getClientId, getOperatingSystem } from './telemetry/util'
3131
export { extensionVersion } from './vscode/env'
3232
export { cast } from './utilities/typeConstructors'
33+
export * as workspaceUtils from './utilities/workspaceUtils'
3334
export {
3435
CodewhispererUserTriggerDecision,
3536
CodewhispererLanguage,

0 commit comments

Comments
 (0)