Skip to content

Commit 687427c

Browse files
authored
feat(amazonq): adding a simple chat prompt test (#7643)
## Changes Adding the simple chat prompt test. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 79f4083 commit 687427c

File tree

1 file changed

+124
-31
lines changed

1 file changed

+124
-31
lines changed

packages/amazonq/test/e2e/amazonq/VET.test.ts

Lines changed: 124 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,126 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5+
import { Workbench, By, WebviewView, WebElement } from 'vscode-extension-tester'
6+
import { until } from 'selenium-webdriver'
57

6-
import { Workbench, By, EditorView, WebviewView, WebElement } from 'vscode-extension-tester'
7-
8-
describe('Amazon Q Login Flow', function () {
8+
describe('Amazon Q E2E UI Test', function () {
99
// need this timeout because Amazon Q takes awhile to load
10-
this.timeout(30000)
11-
let webviewView: WebviewView
1210

13-
// NOTE: I tested all the timeouts and they are necessary for the webview to load properly
11+
// need this timeout
12+
this.timeout(150000)
13+
let webviewView: WebviewView
14+
let workbench: Workbench
1415
before(async function () {
15-
const workbench = new Workbench()
16+
/* TO-DO
17+
possibly before the workbench executes Amazon Q: Open Chat, we can make sure that all the tabs are closed first*/
18+
workbench = new Workbench()
1619
await workbench.executeCommand('Amazon Q: Open Chat')
1720

18-
await new Promise((resolve) => setTimeout(resolve, 15000))
21+
// need this timeout
22+
await new Promise((resolve) => setTimeout(resolve, 5000))
1923
webviewView = new WebviewView()
2024
await webviewView.switchToFrame()
21-
})
2225

23-
after(async () => {
24-
await webviewView.switchBack()
25-
try {
26-
await new EditorView().closeAllEditors()
27-
} catch {}
28-
})
29-
30-
it('Should click through the Amazon Q login screen', async () => {
31-
// Select company account option
32-
const selectableItems = await webviewView.findWebElements(By.css('.selectable-item'))
33-
console.log(typeof selectableItems)
26+
const selectableItems = await waitForElement(webviewView, By.css('.selectable-item'), true)
3427
if (selectableItems.length === 0) {
3528
throw new Error('No selectable login options found')
3629
}
3730

38-
// Find and click company account
3931
const companyItem = await findItemByText(selectableItems, 'Company account')
4032
await companyItem.click()
41-
await new Promise((resolve) => setTimeout(resolve, 2000))
42-
43-
// Click first continue button
4433
const signInContinue = await webviewView.findWebElement(By.css('#connection-selection-continue-button'))
4534
await signInContinue.click()
46-
await new Promise((resolve) => setTimeout(resolve, 2000))
47-
48-
// Enter start URL
4935
const startUrlInput = await webviewView.findWebElement(By.id('startUrl'))
5036
await startUrlInput.clear()
5137
await startUrlInput.sendKeys('https://amzn.awsapps.com/start')
52-
await new Promise((resolve) => setTimeout(resolve, 1000))
53-
54-
// Click second continue button
5538
const UrlContinue = await webviewView.findWebElement(By.css('button.continue-button.topMargin'))
5639
await UrlContinue.click()
57-
await new Promise((resolve) => setTimeout(resolve, 2000))
40+
console.log('Waiting for manual authentication...')
41+
// need this timeout
42+
await new Promise((resolve) => setTimeout(resolve, 12000))
43+
console.log('Manual authentication should be done')
44+
await webviewView.switchBack()
45+
46+
// AFTER AUTHENTICATION WE MUST RELOAD THE WEBVIEW BECAUSE MULTIPLE WEVIEWS CANNOT BE READ AT THE SAME TIME
47+
const editorView = workbench.getEditorView()
48+
console.log('editorview successfully created')
49+
await editorView.closeAllEditors()
50+
console.log('Closed all editors')
51+
webviewView = new WebviewView()
52+
console.log('Reopened webview view')
53+
await webviewView.switchToFrame()
5854
})
5955

56+
after(async () => {
57+
/*
58+
mynah-tabs-container is the css that contains all the mynah ui tabs
59+
inside that there are two spans that have key values
60+
inside those spans there is a div with the css mynah-tab-item-label
61+
and finally INSIDE THAT there is a button with the css mynah-tabs-close-button, we need to click that button and close all the tabs after the test is done
62+
63+
Logic:
64+
Find all the tahs by looking for the close buttons and then close them one by one. To check if all the tabs are closed, we can check if the mynah-tabs-container is empty.
65+
*/
66+
try {
67+
const closeButtons = await webviewView.findWebElements(By.css('.mynah-tabs-close-button'))
68+
69+
for (const button of closeButtons) {
70+
await button.click()
71+
await new Promise((resolve) => setTimeout(resolve, 500))
72+
}
73+
74+
// double check that all tabs are closed by checking if the mynah-tabs-container is empty
75+
const tabsContainer = await webviewView.findWebElements(By.css('.mynah-tabs-container'))
76+
if (
77+
tabsContainer.length === 0 ||
78+
(await tabsContainer[0].findElements(By.css('.mynah-tab-item-label'))).length === 0
79+
) {
80+
console.log('All chat tabs successfully closed')
81+
}
82+
} catch (error) {
83+
console.log('Error closing tabs:', error)
84+
}
85+
await webviewView.switchBack()
86+
})
87+
88+
it('Chat Prompt Test', async () => {
89+
const chatInput = await waitForElement(webviewView, By.css('.mynah-chat-prompt-input'))
90+
await chatInput.sendKeys('Hello, Amazon Q!')
91+
const sendButton = await waitForElement(webviewView, By.css('.mynah-chat-prompt-button'))
92+
await sendButton.click()
93+
const responseReceived = await waitForChatResponse(webviewView)
94+
if (!responseReceived) {
95+
throw new Error('Chat response not received within timeout')
96+
}
97+
98+
console.log('Chat response detected successfully')
99+
})
100+
101+
// Helper to wait for ui elements to load, utilizes typescript function overloading to account for all possible edge cases
102+
async function waitForElement(
103+
webview: WebviewView,
104+
locator: By,
105+
multiple: true,
106+
timeout?: number
107+
): Promise<WebElement[]>
108+
async function waitForElement(
109+
webview: WebviewView,
110+
locator: By,
111+
multiple?: false,
112+
timeout?: number
113+
): Promise<WebElement>
114+
async function waitForElement(
115+
webview: WebviewView,
116+
locator: By,
117+
multiple = false,
118+
timeout = 15000
119+
): Promise<WebElement | WebElement[]> {
120+
const driver = webview.getDriver()
121+
await driver.wait(until.elementsLocated(locator), timeout)
122+
return multiple ? await webview.findWebElements(locator) : await webview.findWebElement(locator)
123+
}
124+
60125
// Helper to find item by text content
61126
async function findItemByText(items: WebElement[], text: string) {
62127
for (const item of items) {
@@ -70,4 +135,32 @@ describe('Amazon Q Login Flow', function () {
70135
}
71136
throw new Error(`Item with text "${text}" not found`)
72137
}
138+
139+
/* My Idea: Basically the conversation container's css is .mynah-chat-items-conversation-container
140+
Instead of looking for a specific message like how we look for other elements in the test,
141+
I can check how many elements there are in our specific conversation container. If there is 2 elements,
142+
we can assume that the chat response has been generated. The challenge is, we must grab the latest
143+
conversation container, as there can be multiple conversations in the webview. */
144+
async function waitForChatResponse(webview: WebviewView, timeout = 15000): Promise<boolean> {
145+
const startTime = Date.now()
146+
147+
while (Date.now() - startTime < timeout) {
148+
const conversationContainers = await webview.findWebElements(
149+
By.css('.mynah-chat-items-conversation-container')
150+
)
151+
152+
if (conversationContainers.length > 0) {
153+
const latestContainer = conversationContainers[conversationContainers.length - 1]
154+
155+
const chatItems = await latestContainer.findElements(By.css('*'))
156+
157+
if (chatItems.length >= 2) {
158+
return true
159+
}
160+
}
161+
await new Promise((resolve) => setTimeout(resolve, 500))
162+
}
163+
164+
return false
165+
}
73166
})

0 commit comments

Comments
 (0)