|
3 | 3 | * SPDX-License-Identifier: Apache-2.0 |
4 | 4 | */ |
5 | 5 |
|
6 | | -// jscpd:ignore-start |
7 | 6 | import assert from 'assert' |
8 | 7 | import vscode from 'vscode' |
9 | | -import os from 'os' |
10 | | -import path from 'path' |
11 | | -import fs from 'fs' // eslint-disable-line no-restricted-imports |
12 | 8 | import { qTestingFramework } from './framework/framework' |
13 | 9 | import sinon from 'sinon' |
14 | 10 | import { Messenger } from './framework/messenger' |
15 | 11 | import { FollowUpTypes } from 'aws-core-vscode/amazonq' |
16 | | -import { registerAuthHook, using } from 'aws-core-vscode/test' |
| 12 | +import { registerAuthHook, using, TestFolder } from 'aws-core-vscode/test' |
17 | 13 | import { loginToIdC } from './utils/setup' |
18 | | -import { globals } from 'aws-core-vscode/shared' |
19 | | -import { openTextDocument } from 'aws-core-vscode/shared' |
| 14 | +import { waitUntil, workspaceUtils } from 'aws-core-vscode/shared' |
20 | 15 |
|
21 | 16 | describe('Amazon Q Test Generation', function () { |
22 | 17 | let framework: qTestingFramework |
@@ -46,6 +41,31 @@ describe('Amazon Q Test Generation', function () { |
46 | 41 | }, |
47 | 42 | ] |
48 | 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 | + |
49 | 69 | before(async function () { |
50 | 70 | await using(registerAuthHook('amazonq-test-account'), async () => { |
51 | 71 | await loginToIdC() |
@@ -89,185 +109,111 @@ describe('Amazon Q Test Generation', function () { |
89 | 109 | }) |
90 | 110 | }) |
91 | 111 |
|
92 | | - describe('Unsupported language', () => { |
93 | | - const { language, filePath } = unsupportedLanguages[0] |
94 | | - |
95 | | - beforeEach(async () => { |
96 | | - const document = await openTextDocument(filePath) |
| 112 | + describe('/test entry', () => { |
| 113 | + describe('Unsupported language', () => { |
| 114 | + const { language, filePath } = unsupportedLanguages[0] |
97 | 115 |
|
98 | | - if (!document) { |
99 | | - assert.fail(`Failed to open ${language} file`) |
100 | | - } |
| 116 | + beforeEach(async () => { |
| 117 | + await setupTestDocument(filePath, language) |
| 118 | + }) |
101 | 119 |
|
102 | | - await vscode.window.showTextDocument(document, { preview: false }) |
| 120 | + it(`/test for unsupported language redirects to chat`, async () => { |
| 121 | + tab.addChatMessage({ command: '/test' }) |
| 122 | + await tab.waitForChatFinishesLoading() |
103 | 123 |
|
104 | | - const activeEditor = vscode.window.activeTextEditor |
105 | | - if (!activeEditor || activeEditor.document !== document) { |
106 | | - assert.fail(`Failed to make ${language} file active`) |
107 | | - } |
108 | | - }) |
| 124 | + await waitForChatItems(3) |
| 125 | + const unsupportedLanguageMessage = tab.getChatItems()[3] |
109 | 126 |
|
110 | | - it(`Does not generate tests for unsupported language`, async () => { |
111 | | - tab.addChatMessage({ command: '/test' }) |
112 | | - await tab.waitForChatFinishesLoading() |
113 | | - |
114 | | - await tab.waitForEvent(() => tab.getChatItems().length > 3, { |
115 | | - waitTimeoutInMs: 5000, |
116 | | - waitIntervalInMs: 1000, |
| 127 | + assert.deepStrictEqual(unsupportedLanguageMessage.type, 'answer') |
| 128 | + assert.deepStrictEqual( |
| 129 | + unsupportedLanguageMessage.body, |
| 130 | + `<span style="color: #EE9D28;">⚠<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 | + ) |
117 | 132 | }) |
118 | | - const message = tab.getChatItems()[3] |
119 | | - assert.deepStrictEqual(message.type, 'answer') |
120 | | - assert.deepStrictEqual( |
121 | | - message.body, |
122 | | - `<span style="color: #EE9D28;">⚠<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.` |
123 | | - ) |
124 | 133 | }) |
125 | | - }) |
126 | | - |
127 | | - describe('External file outside of project', async () => { |
128 | | - let tempDir: string |
129 | | - let tempFileName: string |
130 | | - |
131 | | - beforeEach(async () => { |
132 | | - tempDir = path.join(os.tmpdir(), `testgen-test-${globals.clock.Date.now()}`) |
133 | | - await fs.promises.mkdir(tempDir) |
134 | | - tempFileName = `testfile-${globals.clock.Date.now()}.py` |
135 | | - const tempFilePath = path.join(tempDir, tempFileName) |
136 | | - await fs.promises.writeFile(tempFilePath, 'def add(a, b): return a + b') |
137 | | - const uri = vscode.Uri.file(tempFilePath) |
138 | 134 |
|
139 | | - if (!fs.existsSync(tempFilePath)) { |
140 | | - throw new Error('Failed to create temporary file') |
141 | | - } |
142 | | - |
143 | | - const document = await vscode.workspace.openTextDocument(uri) |
144 | | - if (!document) { |
145 | | - assert.fail(`Failed to open temp file`) |
146 | | - } |
| 135 | + describe('External file', async () => { |
| 136 | + let testFolder: TestFolder |
| 137 | + let fileName: string |
147 | 138 |
|
148 | | - await vscode.window.showTextDocument(document, { preview: false }) |
| 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') |
149 | 143 |
|
150 | | - const activeEditor = vscode.window.activeTextEditor |
151 | | - if (!activeEditor || activeEditor.document !== document) { |
152 | | - assert.fail(`Failed to make temp file active`) |
153 | | - } |
154 | | - }) |
| 144 | + const document = await vscode.workspace.openTextDocument(filePath) |
| 145 | + await vscode.window.showTextDocument(document, { preview: false }) |
| 146 | + }) |
155 | 147 |
|
156 | | - afterEach(async () => { |
157 | | - if (fs.existsSync(tempDir)) { |
158 | | - await fs.promises.rm(tempDir, { recursive: true, force: true }) |
159 | | - } |
160 | | - }) |
| 148 | + it('/test for external file redirects to chat', async () => { |
| 149 | + tab.addChatMessage({ command: '/test' }) |
| 150 | + await tab.waitForChatFinishesLoading() |
161 | 151 |
|
162 | | - it('Generate tests for external file redirects to chat', async () => { |
163 | | - tab.addChatMessage({ command: '/test' }) |
164 | | - await tab.waitForChatFinishesLoading() |
| 152 | + await waitForChatItems(3) |
| 153 | + const externalFileMessage = tab.getChatItems()[3] |
165 | 154 |
|
166 | | - await tab.waitForEvent(() => tab.getChatItems().length > 3, { |
167 | | - waitTimeoutInMs: 5000, |
168 | | - waitIntervalInMs: 1000, |
| 155 | + assert.deepStrictEqual(externalFileMessage.type, 'answer') |
| 156 | + assert.deepStrictEqual( |
| 157 | + externalFileMessage.body, |
| 158 | + `<span style="color: #EE9D28;">⚠<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 | + ) |
169 | 160 | }) |
170 | | - const message = tab.getChatItems()[3] |
171 | | - assert.deepStrictEqual(message.type, 'answer') |
172 | | - assert.deepStrictEqual( |
173 | | - message.body, |
174 | | - `<span style="color: #EE9D28;">⚠<b>I can't generate tests for ${tempFileName}</b> because the file is outside of workspace scope.<br></span> I can still provide examples, instructions and code suggestions.` |
175 | | - ) |
176 | 161 | }) |
177 | | - }) |
178 | 162 |
|
179 | | - for (const { language, filePath } of testFiles) { |
180 | | - describe(`Test Generation for ${language}`, () => { |
181 | | - beforeEach(async () => { |
182 | | - // retry mechanism as loading active document can be sometimes flaky |
183 | | - for (let attempt = 1; attempt <= 3; attempt++) { |
184 | | - const document = await openTextDocument(filePath) |
185 | | - |
186 | | - if (!document) { |
187 | | - if (attempt === 3) { |
188 | | - assert.fail(`Failed to open ${language} file`) |
189 | | - } |
190 | | - continue |
191 | | - } |
192 | | - |
193 | | - await vscode.window.showTextDocument(document, { preview: false }) |
194 | | - |
195 | | - const activeEditor = vscode.window.activeTextEditor |
196 | | - if (!activeEditor || activeEditor.document !== document) { |
197 | | - if (attempt === 3) { |
198 | | - assert.fail(`Failed to make ${language} file active`) |
199 | | - } |
200 | | - continue |
201 | | - } |
202 | | - return |
203 | | - } |
204 | | - }) |
| 163 | + for (const { language, filePath } of testFiles) { |
| 164 | + describe(`${language} file`, () => { |
| 165 | + beforeEach(async () => { |
| 166 | + await setupTestDocument(filePath, language) |
205 | 167 |
|
206 | | - describe('View diff', async () => { |
207 | | - it('Clicks on view diff', async () => { |
208 | 168 | tab.addChatMessage({ command: '/test' }) |
209 | 169 | await tab.waitForChatFinishesLoading() |
210 | 170 |
|
211 | 171 | await tab.waitForButtons([FollowUpTypes.ViewDiff]) |
212 | 172 | tab.clickButton(FollowUpTypes.ViewDiff) |
213 | 173 | await tab.waitForChatFinishesLoading() |
214 | | - |
215 | | - const chatItems = tab.getChatItems() |
216 | | - assert.deepStrictEqual(chatItems[5].type, 'answer') |
217 | | - assert.deepStrictEqual( |
218 | | - chatItems[5].body, |
219 | | - 'Please see the unit tests generated below. Click “View diff” to review the changes in the code editor.' |
220 | | - ) |
221 | 174 | }) |
222 | | - }) |
223 | 175 |
|
224 | | - describe('Accept code', async () => { |
225 | | - it('Clicks on accept', async () => { |
226 | | - tab.addChatMessage({ command: '/test' }) |
227 | | - await tab.waitForChatFinishesLoading() |
| 176 | + describe('View diff', async () => { |
| 177 | + it('Clicks on view diff', async () => { |
| 178 | + const chatItems = tab.getChatItems() |
| 179 | + const viewDiffMessage = chatItems[5] |
228 | 180 |
|
229 | | - await tab.waitForButtons([FollowUpTypes.ViewDiff]) |
230 | | - tab.clickButton(FollowUpTypes.ViewDiff) |
231 | | - await tab.waitForChatFinishesLoading() |
| 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 | + }) |
232 | 188 |
|
233 | | - await tab.waitForButtons([FollowUpTypes.AcceptCode, FollowUpTypes.RejectCode]) |
234 | | - tab.clickButton(FollowUpTypes.AcceptCode) |
235 | | - await tab.waitForChatFinishesLoading() |
| 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() |
236 | 194 |
|
237 | | - await tab.waitForEvent(() => tab.getChatItems().length > 7, { |
238 | | - waitTimeoutInMs: 5000, |
239 | | - waitIntervalInMs: 1000, |
| 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') |
240 | 200 | }) |
241 | | - const message = tab.getChatItems()[7] |
242 | | - assert.deepStrictEqual(message?.type, 'answer-part') |
243 | | - assert.deepStrictEqual(message?.followUp?.options?.[0].pillText, 'Accepted') |
244 | 201 | }) |
245 | | - }) |
246 | 202 |
|
247 | | - describe('Reject code', async () => { |
248 | | - it('Clicks on reject', async () => { |
249 | | - tab.addChatMessage({ command: '/test' }) |
250 | | - await tab.waitForChatFinishesLoading() |
| 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() |
251 | 208 |
|
252 | | - await tab.waitForButtons([FollowUpTypes.ViewDiff]) |
253 | | - tab.clickButton(FollowUpTypes.ViewDiff) |
254 | | - await tab.waitForChatFinishesLoading() |
| 209 | + await waitForChatItems(7) |
| 210 | + const rejectedMessage = tab.getChatItems()[7] |
255 | 211 |
|
256 | | - await tab.waitForButtons([FollowUpTypes.AcceptCode, FollowUpTypes.RejectCode]) |
257 | | - tab.clickButton(FollowUpTypes.RejectCode) |
258 | | - await tab.waitForChatFinishesLoading() |
259 | | - |
260 | | - await tab.waitForEvent(() => tab.getChatItems().length > 7, { |
261 | | - waitTimeoutInMs: 5000, |
262 | | - waitIntervalInMs: 1000, |
| 212 | + assert.deepStrictEqual(rejectedMessage?.type, 'answer-part') |
| 213 | + assert.deepStrictEqual(rejectedMessage?.followUp?.options?.[0].pillText, 'Rejected') |
263 | 214 | }) |
264 | | - const message = tab.getChatItems()[7] |
265 | | - assert.deepStrictEqual(message?.type, 'answer-part') |
266 | | - assert.deepStrictEqual(message?.followUp?.options?.[0].pillText, 'Rejected') |
267 | 215 | }) |
268 | 216 | }) |
269 | | - }) |
270 | | - } |
| 217 | + } |
| 218 | + }) |
271 | 219 | }) |
272 | | - |
273 | | -// jscpd:ignore-end |
0 commit comments