Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
98688bf
initial VET commit with baseline setup + dependencies
laura-codess Jul 2, 2025
5959a38
deleting unnecessary dependencies and refactoring some of VET.test.ts
laura-codess Jul 2, 2025
4540f16
added back the buffer dependency because it failed the tests without it
laura-codess Jul 3, 2025
f1afe95
it was not any of the dependencies that was causing it to break, it w…
laura-codess Jul 3, 2025
4fa90ec
added back some more node: protocol imports to see if it passes all t…
laura-codess Jul 3, 2025
e1c33d8
forgot to save a webpack change. hoping it will fix the one failing t…
laura-codess Jul 3, 2025
ea46464
added back basically all the dependencies because 1 test keeps failing
laura-codess Jul 3, 2025
66f198d
Adding a simple test to send a chat prompt and check if theres a resp…
laura-codess Jul 11, 2025
dd62e27
Adding the wait to improve the robustness
laura-codess Jul 11, 2025
866478a
added logic to the after function to close all the chat tabs after th…
laura-codess Jul 11, 2025
35c656f
fixed the merge conflicts
laura-codess Jul 14, 2025
ed3c220
tested the timeouts, implemented a general wait function
laura-codess Jul 14, 2025
824635e
Merge branch 'feature/ui-e2e-tests' of https://github.com/aws/aws-too…
laura-codess Jul 15, 2025
b30f7f4
checked out master version
laura-codess Jul 15, 2025
5da6336
fix: ignore scripts change
laura-codess Jul 15, 2025
c8ccbae
implementing the initial setup for the framework
laura-codess Jul 16, 2025
74011d2
added setup.ts and testContext so that auth can be shared across tests
laura-codess Jul 17, 2025
a4d6b32
Removed VET.test.ts
laura-codess Jul 17, 2025
d2bc9dd
small . fix
laura-codess Jul 17, 2025
693404d
fixed the PR based on the comments
laura-codess Jul 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions P261194666.md
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ Based on our investigation of the language-server-runtimes repository and the pr
```

5. **Ensure Auto-login Happens Early** (`packages/amazonq/src/lsp/activation.ts`):

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you meant to modify this file? Might have to explicitly exclude it from PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If its happening on pre-commit hook, you can use -n to bypass that.

```typescript
export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
try {
Expand Down
166 changes: 0 additions & 166 deletions packages/amazonq/test/e2e/amazonq/VET.test.ts

This file was deleted.

37 changes: 37 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/chat.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import '../utils/setup'
import { WebviewView } from 'vscode-extension-tester'
import { closeAllTabs } from './framework/cleanupHelper'
import { testContext } from './utils/testContext'
import { waitForChatResponse, writeToChat } from './framework/chatHelper'
import assert from 'assert'

describe('Amazon Q Chat Basic Functionality', function () {
// this timeout is the general timeout for the entire test suite
this.timeout(150000)
let webviewView: WebviewView

before(async function () {
webviewView = testContext.webviewView!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to assert defined here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, Im a bit confused on what you mean by assert defined?

the testContext is used to keep track of the state that we're in. So we need to grab the webview from testContext because that testContext.webview can be updated depending on what webview we're looking at.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, thanks for the link!

})

afterEach(async () => {
try {
await closeAllTabs(webviewView)
} catch (e) {
assert.fail(`Failed to clean up tabs: ${e}`)
}
})

it('Chat Prompt Test', async () => {
await writeToChat('Hello, Amazon Q!', webviewView)
const responseReceived = await waitForChatResponse(webviewView)
if (!responseReceived) {
throw new Error('Chat response not received within timeout')
}
console.log('Chat response detected successfully')
})
})
35 changes: 35 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/framework/chatHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { By, WebviewView } from 'vscode-extension-tester'
import { waitForElement } from './generalHelper'

export async function writeToChat(prompt: string, webview: WebviewView): Promise<boolean> {
const chatInput = await waitForElement(webview, By.css('.mynah-chat-prompt-input'))
await chatInput.sendKeys(prompt)
const sendButton = await waitForElement(webview, By.css('.mynah-chat-prompt-button'))
await sendButton.click()
return true
}

export async function waitForChatResponse(webview: WebviewView, timeout = 15000): Promise<boolean> {
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) {
return true
}
}
await new Promise((resolve) => setTimeout(resolve, 500))
}

return false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { By, WebviewView } from 'vscode-extension-tester'

export async function closeAllTabs(webview: WebviewView): Promise<boolean> {
try {
const closeButtons = await webview.findWebElements(By.css('.mynah-tabs-close-button'))

for (const button of closeButtons) {
await button.click()
await new Promise((resolve) => setTimeout(resolve, 500))
}

const tabsContainer = await webview.findWebElements(By.css('.mynah-tabs-container'))
const allClosed =
tabsContainer.length === 0 ||
(await tabsContainer[0].findElements(By.css('.mynah-tab-item-label'))).length === 0

if (allClosed) {
console.log('All chat tabs successfully closed')
return true
} else {
throw new Error('Failed to close all tabs')
}
} catch (error) {
console.error('Error closing tabs:', error)
throw error
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { By, WebviewView, WebElement } from 'vscode-extension-tester'
import { until } from 'selenium-webdriver'

/* Note: If multiple is set to False, then it will return the first element it finds that matches the Locator*/
export async function waitForElement(
webview: WebviewView,
locator: By,
multiple: true,
timeout?: number
): Promise<WebElement[]>
export async function waitForElement(
webview: WebviewView,
locator: By,
multiple?: false,
timeout?: number
): Promise<WebElement>
export 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if multiple is false, which element does it pick? What gives one element higher priority over another?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if multiple is false, it will just pick the first element it finds. my goal for the waitForElement function when multiple is false when the writer knows that there is a single element (ie. a button) but the function is flexible enough that it can also be used to find multiple elements (ie. a menu list)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not blocking, but I am also curious what might make something the 'first' element it finds. For example, does it mean it appears first the in the raw html? If so, do we know what determines that?

}

export async function findItemByText(items: WebElement[], text: string) {
for (const item of items) {
const titleDivs = await item.findElements(By.css('.title'))
for (const titleDiv of titleDivs) {
const titleText = await titleDiv.getText()
if (titleText?.trim().startsWith(text)) {
return item
}
}
}
throw new Error(`Item with text "${text}" not found`)
}
52 changes: 52 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/framework/loginHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { Workbench, By, WebviewView } from 'vscode-extension-tester'
import { waitForElement, findItemByText } from './generalHelper'
import { testContext } from '../utils/testContext'
/* Completes the entire Amazon Q login flow

Currently, the function will
1. Open AmazonQ
2. Clicks Company Account
3. Inputs the Start URL
4. IMPORTANT: you must click manually open yourself when the popup window asks to open the browser and complete the authentication in the browser**

TO-DO: Currently this loginToAmazonQ is not fully autonomous as we ran into a blocker when the browser window pops up */

export async function loginToAmazonQ(): Promise<void> {
const workbench = new Workbench()
await workbench.executeCommand('Amazon Q: Open Chat')

await new Promise((resolve) => setTimeout(resolve, 5000))
let webviewView = new WebviewView()
await webviewView.switchToFrame()

const selectableItems = await waitForElement(webviewView, By.css('.selectable-item'), true)
if (selectableItems.length === 0) {
throw new Error('No selectable login options found')
}

const companyItem = await findItemByText(selectableItems, 'Company account')
await companyItem.click()
const signInContinue = await webviewView.findWebElement(By.css('#connection-selection-continue-button'))
await signInContinue.click()
const startUrlInput = await webviewView.findWebElement(By.id('startUrl'))
await startUrlInput.clear()
await startUrlInput.sendKeys('https://amzn.awsapps.com/start')
const UrlContinue = await webviewView.findWebElement(By.css('button.continue-button.topMargin'))
await UrlContinue.click()
console.log('Waiting for manual authentication...')
await new Promise((resolve) => setTimeout(resolve, 12000))
console.log('Manual authentication should be done')
await webviewView.switchBack()

const editorView = workbench.getEditorView()
await editorView.closeAllEditors()
webviewView = new WebviewView()
await webviewView.switchToFrame()

testContext.workbench = workbench
testContext.webviewView = webviewView
}
13 changes: 13 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/utils/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { loginToAmazonQ } from '../framework/loginHelper'

before(async function () {
this.timeout(60000)
console.log('\n\n*** MANUAL INTERVENTION REQUIRED ***')
console.log('When prompted, you must manually click to open the browser and complete authentication')
console.log('You have 60 seconds to complete this step\n\n')
await loginToAmazonQ()
})
Loading
Loading