-
Notifications
You must be signed in to change notification settings - Fork 746
feat(amazonq): adding a simple chat prompt test #7643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
98688bf
5959a38
4540f16
f1afe95
4fa90ec
e1c33d8
ea46464
66f198d
dd62e27
866478a
35c656f
ed3c220
824635e
b30f7f4
5da6336
c8ccbae
667619e
ade9906
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,61 +2,126 @@ | |
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| import { Workbench, By, WebviewView, WebElement } from 'vscode-extension-tester' | ||
| import { until } from 'selenium-webdriver' | ||
|
|
||
| import { Workbench, By, EditorView, WebviewView, WebElement } from 'vscode-extension-tester' | ||
|
|
||
| describe('Amazon Q Login Flow', function () { | ||
| describe('Amazon Q E2E UI Test', function () { | ||
| // need this timeout because Amazon Q takes awhile to load | ||
| this.timeout(30000) | ||
| let webviewView: WebviewView | ||
|
|
||
| // NOTE: I tested all the timeouts and they are necessary for the webview to load properly | ||
| // need this timeout | ||
| this.timeout(150000) | ||
| let webviewView: WebviewView | ||
| let workbench: Workbench | ||
| before(async function () { | ||
| const workbench = new Workbench() | ||
| /* TO-DO | ||
| possibly before the workbench executes Amazon Q: Open Chat, we can make sure that all the tabs are closed first*/ | ||
| workbench = new Workbench() | ||
| await workbench.executeCommand('Amazon Q: Open Chat') | ||
|
|
||
| await new Promise((resolve) => setTimeout(resolve, 15000)) | ||
| // need this timeout | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know its somewhat of a mystery, but it could be worth investigating why we need these timeouts? Maybe we can find another example of someone running into the same issue and link that here. |
||
| await new Promise((resolve) => setTimeout(resolve, 5000)) | ||
| webviewView = new WebviewView() | ||
| await webviewView.switchToFrame() | ||
| }) | ||
|
|
||
| after(async () => { | ||
| await webviewView.switchBack() | ||
| try { | ||
| await new EditorView().closeAllEditors() | ||
| } catch {} | ||
| }) | ||
|
|
||
| it('Should click through the Amazon Q login screen', async () => { | ||
| // Select company account option | ||
| const selectableItems = await webviewView.findWebElements(By.css('.selectable-item')) | ||
| console.log(typeof selectableItems) | ||
| const selectableItems = await waitForElement(webviewView, By.css('.selectable-item'), true) | ||
| if (selectableItems.length === 0) { | ||
| throw new Error('No selectable login options found') | ||
| } | ||
|
|
||
| // Find and click company account | ||
| const companyItem = await findItemByText(selectableItems, 'Company account') | ||
| await companyItem.click() | ||
| await new Promise((resolve) => setTimeout(resolve, 2000)) | ||
|
|
||
| // Click first continue button | ||
| const signInContinue = await webviewView.findWebElement(By.css('#connection-selection-continue-button')) | ||
| await signInContinue.click() | ||
| await new Promise((resolve) => setTimeout(resolve, 2000)) | ||
|
|
||
| // Enter start URL | ||
| const startUrlInput = await webviewView.findWebElement(By.id('startUrl')) | ||
| await startUrlInput.clear() | ||
| await startUrlInput.sendKeys('https://amzn.awsapps.com/start') | ||
| await new Promise((resolve) => setTimeout(resolve, 1000)) | ||
|
|
||
| // Click second continue button | ||
| const UrlContinue = await webviewView.findWebElement(By.css('button.continue-button.topMargin')) | ||
| await UrlContinue.click() | ||
| await new Promise((resolve) => setTimeout(resolve, 2000)) | ||
| console.log('Waiting for manual authentication...') | ||
| // need this timeout | ||
| await new Promise((resolve) => setTimeout(resolve, 12000)) | ||
| console.log('Manual authentication should be done') | ||
| await webviewView.switchBack() | ||
|
|
||
| // AFTER AUTHENTICATION WE MUST RELOAD THE WEBVIEW BECAUSE MULTIPLE WEVIEWS CANNOT BE READ AT THE SAME TIME | ||
| const editorView = workbench.getEditorView() | ||
| console.log('editorview successfully created') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume these are just debugging logs? We should be careful of this becoming super noisy, but if its helpful we should keep them. I imagine a subset of these are helpful? |
||
| await editorView.closeAllEditors() | ||
| console.log('Closed all editors') | ||
| webviewView = new WebviewView() | ||
| console.log('Reopened webview view') | ||
| await webviewView.switchToFrame() | ||
| }) | ||
|
|
||
| after(async () => { | ||
| /* | ||
| mynah-tabs-container is the css that contains all the mynah ui tabs | ||
| inside that there are two spans that have key values | ||
| inside those spans there is a div with the css mynah-tab-item-label | ||
| 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 | ||
|
|
||
| Logic: | ||
| 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. | ||
| */ | ||
| try { | ||
| const closeButtons = await webviewView.findWebElements(By.css('.mynah-tabs-close-button')) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need to wait here as well? |
||
|
|
||
| for (const button of closeButtons) { | ||
| await button.click() | ||
| await new Promise((resolve) => setTimeout(resolve, 500)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we use |
||
| } | ||
|
|
||
| // double check that all tabs are closed by checking if the mynah-tabs-container is empty | ||
| const tabsContainer = await webviewView.findWebElements(By.css('.mynah-tabs-container')) | ||
| if ( | ||
| tabsContainer.length === 0 || | ||
| (await tabsContainer[0].findElements(By.css('.mynah-tab-item-label'))).length === 0 | ||
| ) { | ||
| console.log('All chat tabs successfully closed') | ||
| } | ||
| } catch (error) { | ||
| console.log('Error closing tabs:', error) | ||
| } | ||
| await webviewView.switchBack() | ||
| }) | ||
|
|
||
| it('Chat Prompt Test', async () => { | ||
| const chatInput = await waitForElement(webviewView, By.css('.mynah-chat-prompt-input')) | ||
| await chatInput.sendKeys('Hello, Amazon Q!') | ||
| const sendButton = await waitForElement(webviewView, By.css('.mynah-chat-prompt-button')) | ||
| await sendButton.click() | ||
| const responseReceived = await waitForChatResponse(webviewView) | ||
| if (!responseReceived) { | ||
| throw new Error('Chat response not received within timeout') | ||
| } | ||
|
|
||
| console.log('Chat response detected successfully') | ||
| }) | ||
|
|
||
| // Helper to wait for ui elements to load, utilizes typescript function overloading to account for all possible edge cases | ||
| async function waitForElement( | ||
| webview: WebviewView, | ||
| locator: By, | ||
| multiple: true, | ||
| timeout?: number | ||
| ): Promise<WebElement[]> | ||
| async function waitForElement( | ||
| webview: WebviewView, | ||
| locator: By, | ||
| multiple?: false, | ||
| timeout?: number | ||
| ): Promise<WebElement> | ||
| async function waitForElement( | ||
| webview: WebviewView, | ||
| locator: By, | ||
| multiple = false, | ||
| timeout = 15000 | ||
| ): Promise<WebElement | WebElement[]> { | ||
| const driver = webview.getDriver() | ||
| await driver.wait(until.elementsLocated(locator), timeout) | ||
| return multiple ? await webview.findWebElements(locator) : await webview.findWebElement(locator) | ||
| } | ||
|
|
||
| // Helper to find item by text content | ||
| async function findItemByText(items: WebElement[], text: string) { | ||
| for (const item of items) { | ||
|
|
@@ -70,4 +135,32 @@ describe('Amazon Q Login Flow', function () { | |
| } | ||
| throw new Error(`Item with text "${text}" not found`) | ||
| } | ||
|
|
||
| /* My Idea: Basically the conversation container's css is .mynah-chat-items-conversation-container | ||
| Instead of looking for a specific message like how we look for other elements in the test, | ||
| I can check how many elements there are in our specific conversation container. If there is 2 elements, | ||
| we can assume that the chat response has been generated. The challenge is, we must grab the latest | ||
| conversation container, as there can be multiple conversations in the webview. */ | ||
| async function waitForChatResponse(webview: WebviewView, timeout = 15000): Promise<boolean> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice, I like the use of a default here! It helps simplify the interface for the common case. |
||
| const startTime = Date.now() | ||
|
|
||
| while (Date.now() - startTime < timeout) { | ||
| const conversationContainers = await webview.findWebElements( | ||
| By.css('.mynah-chat-items-conversation-container') | ||
| ) | ||
|
|
||
| if (conversationContainers.length > 0) { | ||
| const latestContainer = conversationContainers[conversationContainers.length - 1] | ||
|
|
||
| const chatItems = await latestContainer.findElements(By.css('*')) | ||
|
|
||
| if (chatItems.length >= 2) { | ||
Hweinstock marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return true | ||
| } | ||
| } | ||
| await new Promise((resolve) => setTimeout(resolve, 500)) | ||
| } | ||
|
|
||
| return false | ||
| } | ||
| }) | ||
Uh oh!
There was an error while loading. Please reload this page.