Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
449 changes: 236 additions & 213 deletions .github/workflows/node.js.yml

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions buildspec/linuxE2ETests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ env:
# followed by Error: Could not delete obsolete instance handle Error: ENOENT: no such file or directory, unlink <path>
AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/'
AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/'
AUTH_UTIL_LAMBDA_ARN: '/GitHubBot/AuthLambdaArn'

phases:
install:
Expand All @@ -38,8 +39,9 @@ phases:
build:
commands:
- export HOME=/home/codebuild-user
# Ignore failure until throttling issues are fixed.
- xvfb-run npm run testE2E; npm run mergeReports -- "$?"
- export NODE_OPTIONS='--max-old-space-size=8192'
- npm ci
- xvfb-run npm run test:ui
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g')
- CI_BUILD_ID="${CODEBUILD_BUILD_ID}"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"testE2E": "npm run testE2E -w packages/ --if-present",
"test:ui:prepare": "./node_modules/.bin/extest get-vscode -s ~/.vscode-test-resources -n && extest get-chromedriver -s ~/.vscode-test-resources -n",
"test:ui:install": "cd packages/amazonq && npm run package 2>&1 | grep -o 'VSIX Version: [^ ]*' | cut -d' ' -f3 | xargs -I{} bash -c 'cd ../../ && ./node_modules/.bin/extest install-vsix -f amazon-q-vscode-{}.vsix -e packages/amazonq/test/e2e_new/amazonq/resources -s ~/.vscode-test-resources'",
"test:ui:run": "npm run testCompile && ./node_modules/.bin/extest run-tests -s ~/.vscode-test-resources -e packages/amazonq/test/e2e_new/amazonq/resources packages/amazonq/dist/test/e2e_new/amazonq/tests/*.test.js 2>&1 | tee packages/amazonq/test/e2e_new/amazonq/logs/ui_e2e_testlog_$(date +%Y%m%d_%H%M%S).log",
"test:ui:run": "npm run testCompile && ./node_modules/.bin/extest run-tests -s ~/.vscode-test-resources -e packages/amazonq/test/e2e_new/amazonq/resources packages/amazonq/dist/test/e2e_new/amazonq/tests/*.test.js",
"test:ui": "npm run test:ui:prepare && npm run test:ui:install && npm run test:ui:run",
"testInteg": "npm run testInteg -w packages/ --if-present",
"package": "npm run package -w packages/toolkit -w packages/amazonq",
Expand Down
131 changes: 109 additions & 22 deletions packages/amazonq/test/e2e_new/amazonq/utils/authUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* 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 { findItemByText, sleep, waitForElements } from './generalUtils'
import { Workbench, WebviewView, By, ModalDialog, until } from 'vscode-extension-tester'
import { printElementHTML, sleep } from './generalUtils'
import { testContext } from './testContext'

/* Completes the entire Amazon Q login flow
Expand All @@ -16,35 +16,122 @@ Currently, the function will

TO-DO: Currently this signInToAmazonQ is not fully autonomous as we ran into a blocker when the browser window pops up */
export async function signInToAmazonQ(): Promise<void> {
// if (isRunningInGitHubActionsE2E()) {
// console.log('CI Environment detected: Using automated authentication')
// const workbench = new Workbench()
// await workbench.executeCommand('Amazon Q: Open Chat')

// await sleep(5000)
// let webviewView = new WebviewView()
// await webviewView.switchToFrame()

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

// // find the button / input + click the button / input
// 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()

// await sleep(3000)
// await authenticateForCI()
// console.log('Waiting for manual authentication...')
// await sleep(12000)
// console.log('Manual authentication should be done')

// await webviewView.switchBack()

// const editorView = workbench.getEditorView()
// await editorView.closeAllEditors()
// webviewView = new WebviewView()
// await webviewView.switchToFrame()
// const body = webviewView.findElement(By.css('*'))
// const body2 = workbench.findElement(By.css('*'))
// await printElementHTML(body)
// await printElementHTML(body2)

// testContext.workbench = workbench
// testContext.webviewView = webviewView

// // // Set up minimal test context for CI
// // const workbench = new Workbench()
// // testContext.workbench = workbench
// // // Skip webview setup for CI as authentication is handled by Lambda
// // await workbench.executeCommand('Amazon Q: Open Chat')
// // console.log('THIS WORKED 1')
// // const editorView = workbench.getEditorView()
// // console.log('THIS WORKED 2')
// // await editorView.closeAllEditors()
// // console.log('THIS WORKED 3')
// // const webviewView = new WebviewView()
// // console.log('THIS WORKED 4')
// // await webviewView.switchToFrame()
// // console.log('THIS WORKED 5')

// // testContext.webviewView = webviewView
// // console.log('IT WORKED')
// // const body = webviewView.findElement(By.css('*'))
// // const body2 = workbench.findElement(By.css('*'))
// // await printElementHTML(body)
// // await printElementHTML(body2)
// //if were not getting the print that we're expecting josh's registerhook works the moment the browser popup happens so we can probs use that
// return
// }

// Normal manual authentication flow for local development

//UN COMMENT
const workbench = new Workbench()
await workbench.executeCommand('Amazon Q: Open Chat')
// await sleep(5000)
// await workbench.executeCommand('Amazon Q: Open Chat')

await sleep(5000)
let webviewView = new WebviewView()
await webviewView.switchToFrame()
// const selectableItems = await waitForElements(webviewView, By.css('.selectable-item'))
// if (selectableItems.length === 0) {
// throw new Error('No selectable login options found')
// }

const selectableItems = await waitForElements(webviewView, By.css('.selectable-item'))
if (selectableItems.length === 0) {
throw new Error('No selectable login options found')
}
// // find the button / input + click the button / input
// const companyItem = await findItemByText(selectableItems, 'Company account')
// await companyItem.click()

// find the button / input + click the button / input
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 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 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()

const UrlContinue = await webviewView.findWebElement(By.css('button.continue-button.topMargin'))
await UrlContinue.click()
// console.log('Waiting for manual authentication...')
// await sleep(12000)
const driver = workbench.getDriver()
const modalWnd = By.className('monaco-dialog-box')
await driver.wait(until.elementLocated(modalWnd), 10_000)
const dialog = new ModalDialog()
await dialog.pushButton('Open')
await sleep(5000)
await workbench.executeCommand('Amazon Q: Open Chat')
await sleep(5000)
let webviewView = new WebviewView()
await webviewView.switchToFrame()
const body = webviewView.findElement(By.css('*'))
await printElementHTML(body)

console.log('Waiting for manual authentication...')
await sleep(12000)
console.log('Manual authentication should be done')

await webviewView.switchBack()

const editorView = workbench.getEditorView()
Expand Down
27 changes: 27 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/utils/ciOidcClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import { isRunningInGitHubActionsE2E, invokeAuthLambda } from './ciUtils'

/**
* CI-specific authentication that bypasses browser interaction
*/
export async function authenticateForCI(): Promise<void> {
if (!isRunningInGitHubActionsE2E()) {
throw new Error('This function should only be called in CI environments')
}

// Mock the device authorization flow for CI
const mockUserCode = 'CI_AUTO_CODE'
const mockVerificationUri = 'https://amzn.awsapps.com/start'

console.log('CI Authentication: Invoking Lambda with mock device authorization')
await invokeAuthLambda(mockUserCode, mockVerificationUri)

// Give the Lambda time to complete the authentication
await new Promise((resolve) => setTimeout(resolve, 10000))

console.log('CI Authentication: Lambda invocation completed')
}
79 changes: 79 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/utils/ciUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

// import * as vscode from 'vscode'
// import { getTestWindow } from 'vscode-extension-tester'
// import { patchObject } from '../../../core/test/shared/utilities/patchObject'
// import { invokeLambda } from './ciOidcClient'

interface AuthorizeRequest {
readonly secret: string
readonly userCode: string
readonly verificationUri: string
}

// const proceedToBrowser = 'Proceed to browser'

/**
* Checks if the current environment is running in GitHub Actions CI for e2e tests
*/
export function isRunningInGitHubActionsE2E(): boolean {
return (
true == true
// process.env.GITHUB_ACTIONS === 'true' &&
// process.env.CI === 'true' &&
// (process.env.GITHUB_JOB?.includes('e2e') === true ||
// process.env.GITHUB_WORKFLOW?.toLowerCase().includes('e2e') === true)
)
}

/**
* Invokes the auth Lambda function for CI automation
*
*
*
* Josh's implementation
*/
export async function invokeAuthLambda(userCode: string, verificationUri: string): Promise<void> {
const lambdaArn = process.env.AUTH_UTIL_LAMBDA_ARN
if (!lambdaArn) {
throw new Error('AUTH_UTIL_LAMBDA_ARN environment variable is required for CI authentication')
}

const AWS = require('aws-sdk')
const lambda = new AWS.Lambda()

const request: AuthorizeRequest = {
secret: 'GitHubBot/AuthSecret', // Default secret name
userCode,
verificationUri,
}

await lambda
.invoke({
FunctionName: lambdaArn,
Payload: JSON.stringify(request),
})
.promise()
}

// export async function registerAuthHook(secret: string, lambdaId = process.env['AUTH_UTIL_LAMBDA_ARN']) {
// // Latest eg: 'https://nkomonen.awsapps.com/start/#/device?user_code=JXZC-NVRK'
// const urlString = 'https://oidc.us-east-1.amazonaws.com/authorize?response_type=code&client_id=-yit8OUGd-Hnuxi-wde3dHVzLWVhc3QtMQ&redirect_uri=http://127.0.0.1:53085/oauth/callback&scopes=codewhisperer:completions,codewhisperer:analysis,codewhisperer:conversations,codewhisperer:transformations,codewhisperer:taskassist&state=2f35c7c1-0398-489d-8839-26323587cab6&code_challenge=iL8MZPd_SldEB3B4Y0tKrPjHGRlFt5_r3FYziVNbm9g&code_challenge_method=S256'

// // Drop the user_code parameter since the auth lambda does not support it yet, and keeping it
// // would trigger a slightly different UI flow which breaks the automation.
// // TODO: If the auth lambda supports user_code in the parameters then we can skip this step
// const verificationUri = urlString.split('?')[0]

// const params = urlString.split('?')[1]
// const userCode = new URLSearchParams(params).get('user_code')

// await invokeLambda(lambdaId, {
// secret,
// userCode,
// verificationUri,
// })
// }
2 changes: 2 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/utils/cleanupUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { sleep } from './generalUtils'
*/
export async function closeAllTabs(webview: WebviewView): Promise<void> {
const closeButtons = await webview.findWebElements(By.css('.mynah-tabs-close-button'))
console.log('THIS WORKED 6')

for (const button of closeButtons) {
await button.click()
await sleep(500)
}

const tabsContainer = await webview.findWebElements(By.css('.mynah-tabs-container'))
console.log('THIS WORKED 7')
const allClosed =
tabsContainer.length === 1 ||
(await tabsContainer[0].findElements(By.css('.mynah-tab-item-label'))).length === 0
Expand Down
2 changes: 2 additions & 0 deletions packages/amazonq/test/e2e_new/amazonq/utils/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { closeAllTabs } from './cleanupUtils'

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 signInToAmazonQ()
const webviewView = testContext.webviewView
await closeAllTabs(webviewView)
Expand Down
Loading