Skip to content

Commit c8ccbae

Browse files
committed
implementing the initial setup for the framework
1 parent 5da6336 commit c8ccbae

File tree

7 files changed

+216
-274
lines changed

7 files changed

+216
-274
lines changed

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

Lines changed: 0 additions & 166 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { By, WebviewView } from 'vscode-extension-tester'
6+
import { loginToAmazonQ } from './framework/loginHelper'
7+
import { closeAllTabs } from './framework/cleanupHelper'
8+
import { waitForElement } from './framework/generalHelper'
9+
import { waitForChatResponse } from './framework/chatHelper'
10+
11+
describe('Amazon Q Chat Basic Functionality', function () {
12+
// this timeout is the general timeout for the entire test suite
13+
this.timeout(150000)
14+
let webviewView: WebviewView
15+
16+
before(async function () {
17+
const result = await loginToAmazonQ()
18+
webviewView = result.webviewView
19+
})
20+
21+
afterEach(async () => {
22+
await closeAllTabs(webviewView)
23+
})
24+
25+
it('Chat Prompt Test', async () => {
26+
const chatInput = await waitForElement(webviewView, By.css('.mynah-chat-prompt-input'))
27+
await chatInput.sendKeys('Hello, Amazon Q!')
28+
const sendButton = await waitForElement(webviewView, By.css('.mynah-chat-prompt-button'))
29+
await sendButton.click()
30+
const responseReceived = await waitForChatResponse(webviewView)
31+
if (!responseReceived) {
32+
throw new Error('Chat response not received within timeout')
33+
}
34+
35+
console.log('Chat response detected successfully')
36+
})
37+
})
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { By, WebviewView } from 'vscode-extension-tester'
6+
7+
/* Waits for a chat response and outputs whether the response is "correct"
8+
9+
Logic:
10+
The overall conversation container's css is .mynah-chat-items-conversation-container.
11+
Within that container we can check how many elements exist. If there is 2 elements,
12+
we can assume that the chat response has been generated. However, we must grab the
13+
latest conversation container, as there can be multiple conversations in the webview. */
14+
15+
export async function waitForChatResponse(webview: WebviewView, timeout = 15000): Promise<boolean> {
16+
const startTime = Date.now()
17+
18+
while (Date.now() - startTime < timeout) {
19+
const conversationContainers = await webview.findWebElements(By.css('.mynah-chat-items-conversation-container'))
20+
21+
if (conversationContainers.length > 0) {
22+
const latestContainer = conversationContainers[conversationContainers.length - 1]
23+
24+
const chatItems = await latestContainer.findElements(By.css('*'))
25+
26+
if (chatItems.length >= 2) {
27+
return true
28+
}
29+
}
30+
await new Promise((resolve) => setTimeout(resolve, 500))
31+
}
32+
33+
return false
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { By, WebviewView } from 'vscode-extension-tester'
6+
7+
/* Finds all the tabs by looking for the close buttons and then closes them one by one.
8+
9+
Logic:
10+
There is a button with the css mynah-tabs-close-button, we need to click that button and
11+
close all the tabs after the test is done to avoid memory from a previous test. To double
12+
check if all the tabs are closed, we can check if the mynah-tabs-container is empty. */
13+
14+
export async function closeAllTabs(webview: WebviewView) {
15+
try {
16+
const closeButtons = await webview.findWebElements(By.css('.mynah-tabs-close-button'))
17+
18+
for (const button of closeButtons) {
19+
await button.click()
20+
await new Promise((resolve) => setTimeout(resolve, 500))
21+
}
22+
23+
// double check that all tabs are closed by checking if the mynah-tabs-container is empty
24+
const tabsContainer = await webview.findWebElements(By.css('.mynah-tabs-container'))
25+
if (
26+
tabsContainer.length === 0 ||
27+
(await tabsContainer[0].findElements(By.css('.mynah-tab-item-label'))).length === 0
28+
) {
29+
console.log('All chat tabs successfully closed')
30+
}
31+
} catch (error) {
32+
console.log('Error closing tabs:', error)
33+
}
34+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { By, WebviewView, WebElement } from 'vscode-extension-tester'
6+
import { until } from 'selenium-webdriver'
7+
8+
/* Waits for an element (or multiple elements) to appear based on the parameters
9+
10+
Logic:
11+
The function utilizes the Selenium wait driver. We can call that driver from our
12+
WebviewView but it can also be called on parts of the VSCode Editor that are not
13+
part of the WebviewView.
14+
15+
(TO-DO: create a more general function that can be called on any part of the VSCode
16+
Editor. Will do when a use case appears for it.*/
17+
18+
export async function waitForElement(
19+
webview: WebviewView,
20+
locator: By,
21+
multiple: true,
22+
timeout?: number
23+
): Promise<WebElement[]>
24+
export async function waitForElement(
25+
webview: WebviewView,
26+
locator: By,
27+
multiple?: false,
28+
timeout?: number
29+
): Promise<WebElement>
30+
export async function waitForElement(
31+
webview: WebviewView,
32+
locator: By,
33+
multiple = false,
34+
timeout = 15000
35+
): Promise<WebElement | WebElement[]> {
36+
const driver = webview.getDriver()
37+
await driver.wait(until.elementsLocated(locator), timeout)
38+
return multiple ? await webview.findWebElements(locator) : await webview.findWebElement(locator)
39+
}
40+
41+
/* General function for finding WebElement by their text content
42+
43+
Logic:
44+
It searches through an array of WebElements and looks for an element with
45+
the ".tittle" CSS class within each item. Compares the text content and returns
46+
the first matching parent element, or throws an error if not found. */
47+
48+
export async function findItemByText(items: WebElement[], text: string) {
49+
for (const item of items) {
50+
const titleDivs = await item.findElements(By.css('.title'))
51+
for (const titleDiv of titleDivs) {
52+
const titleText = await titleDiv.getText()
53+
if (titleText?.trim().startsWith(text)) {
54+
return item
55+
}
56+
}
57+
}
58+
throw new Error(`Item with text "${text}" not found`)
59+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import { Workbench, By, WebviewView } from 'vscode-extension-tester'
6+
import { waitForElement, findItemByText } from './generalHelper'
7+
8+
/* Completes the entire Amazon Q login flow
9+
10+
Currently, the function will
11+
1. Open AmazonQ
12+
2. Clicks Company Account
13+
3. Inputs the Start URL
14+
4. IMPORTANT: you must click manually open yourself when the popup window asks to open the browser and complete the authentication in the browser**
15+
16+
TO-DO: Currently this loginToAmazonQ is not fully autonomous as we ran into a blocker when the browser window pops up
17+
Documentation: https://quip-amazon.com/PoJOAyt4ja8H/Authentication-for-UI-Tests-Documentation */
18+
19+
export async function loginToAmazonQ(): Promise<{ workbench: Workbench; webviewView: WebviewView }> {
20+
const workbench = new Workbench()
21+
await workbench.executeCommand('Amazon Q: Open Chat')
22+
23+
await new Promise((resolve) => setTimeout(resolve, 5000))
24+
let webviewView = new WebviewView()
25+
await webviewView.switchToFrame()
26+
27+
const selectableItems = await waitForElement(webviewView, By.css('.selectable-item'), true)
28+
if (selectableItems.length === 0) {
29+
throw new Error('No selectable login options found')
30+
}
31+
32+
const companyItem = await findItemByText(selectableItems, 'Company account')
33+
await companyItem.click()
34+
const signInContinue = await webviewView.findWebElement(By.css('#connection-selection-continue-button'))
35+
await signInContinue.click()
36+
const startUrlInput = await webviewView.findWebElement(By.id('startUrl'))
37+
await startUrlInput.clear()
38+
await startUrlInput.sendKeys('https://amzn.awsapps.com/start')
39+
const UrlContinue = await webviewView.findWebElement(By.css('button.continue-button.topMargin'))
40+
await UrlContinue.click()
41+
console.log('Waiting for manual authentication...')
42+
await new Promise((resolve) => setTimeout(resolve, 12000))
43+
console.log('Manual authentication should be done')
44+
await webviewView.switchBack()
45+
46+
const editorView = workbench.getEditorView()
47+
await editorView.closeAllEditors()
48+
webviewView = new WebviewView()
49+
await webviewView.switchToFrame()
50+
51+
return { workbench, webviewView }
52+
}

0 commit comments

Comments
 (0)