diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index da8d0c6ea54..fa354b556fb 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -1,216 +1,239 @@ # github actions: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs # setup-node: https://github.com/actions/setup-node -name: CI - -on: - push: - branches: [master, staging] - pull_request: - # By default, CI will trigger on opened/synchronize/reopened event types. - # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request - # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. - branches: [master, feature/*, staging] - -# Cancel old jobs when a pull request is updated. -concurrency: - group: ${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - lint-commits: - # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 20 - - uses: actions/setup-node@v4 - with: - node-version: '20' - - name: Validate Branch name - if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref != ''}} - env: - BRANCH_NAME: ${{ github.event.pull_request.head.ref }} - run: | - node "$GITHUB_WORKSPACE/.github/workflows/lintbranch.js" run "$BRANCH_NAME" - - name: Check PR title - run: | - node "$GITHUB_WORKSPACE/.github/workflows/lintcommit.js" - - lint: - needs: lint-commits - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - vscode-version: [stable] - env: - NODE_OPTIONS: '--max-old-space-size=8192' - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run testCompile - - run: npm run lint - - lint-duplicate-code: - needs: lint-commits - if: ${{ github.event_name == 'pull_request'}} - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - env: - NODE_OPTIONS: '--max-old-space-size=8192' - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Fetch fork upstream - env: - REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }} - run: | - git remote add forkUpstream https://github.com/$REPO_NAME # URL of the fork - git fetch forkUpstream # Fetch fork - - - name: Merge in target branch to avoid false negatives. - env: - TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} - # Note: "git merge" should always succeed here, because GHA won't - # start the job if there are merge conflicts. https://github.com/orgs/community/discussions/11265 - # Also, because `git merge` makes a commit, we need to establish an identity to avoid 'Committer identity unknown' error - run: | - git config --global user.name "aws-toolkit-automation" - git config --global user.email "<>" - git merge origin/$TARGET_BRANCH - - - name: Compute git diff - env: - CURRENT_BRANCH: ${{ github.head_ref }} - TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} - run: git diff origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt - - - run: npm install -g jscpd - - - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" - - - if: always() - uses: actions/upload-artifact@v4 - with: - name: unfiltered-jscpd-report - path: ./jscpd-report.json - - - name: Check for Duplicates - env: - COMMIT_HASH: ${{ github.sha}} - REPO_NAME: ${{ github.repository }} - run: node "$GITHUB_WORKSPACE/.github/workflows/filterDuplicates.js" run diff_output.txt jscpd-report.json $COMMIT_HASH $REPO_NAME - - macos: - needs: lint-commits - name: test macOS - runs-on: macos-latest - strategy: - fail-fast: false - matrix: - node-version: [18.x] - vscode-version: [minimum, stable, insiders] - package: [amazonq, toolkit] - env: - VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} - NODE_OPTIONS: '--max-old-space-size=8192' - AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' - AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - name: Tests - uses: coactions/setup-xvfb@v1 - with: - run: npm run test -w packages/${{ matrix.package }} - - name: Code coverage for ${{ matrix.package }} - env: - # Unset NODE_OPTIONS because of https://github.com/codecov/uploader/issues/475 - NODE_OPTIONS: '' - if: ${{ github.repository == 'aws/aws-toolkit-vscode' && github.event_name == 'pull_request' && github.base_ref == 'master' }} - uses: codecov/codecov-action@v5 - with: - flags: macos-${{ matrix.package }}-unittests - verbose: true - file: ./coverage/${{ matrix.package }}/lcov.info - token: ${{ secrets.CODECOV_TOKEN }} - - web: - needs: lint-commits - name: test Web - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - node-version: [18.x] - vscode-version: [stable, insiders] - env: - VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} - NODE_OPTIONS: '--max-old-space-size=8192' - AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' - AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - name: Tests - uses: coactions/setup-xvfb@v1 - with: - run: npm run testWeb - - windows: - needs: lint-commits - name: test Windows - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - node-version: [18.x] - vscode-version: [stable, insiders] - package: [amazonq, toolkit] - env: - VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} - NODE_OPTIONS: '--max-old-space-size=8192' - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - name: Tests - run: npm run test -w packages/${{ matrix.package }} - - name: Code coverage for ${{ matrix.package }} - env: - # Unset NODE_OPTIONS because of https://github.com/codecov/uploader/issues/475 - NODE_OPTIONS: '' - if: ${{ github.repository == 'aws/aws-toolkit-vscode' && github.event_name == 'pull_request' && github.base_ref == 'master' }} - uses: codecov/codecov-action@v5 - with: - flags: windows-${{ matrix.package }}-unittests - verbose: true - file: ./coverage/${{ matrix.package }}/lcov.info - token: ${{ secrets.CODECOV_TOKEN }} +# FOR THE SAKE OF ITERATING FASTER, COMMENT OUT ALL THE OTHER TESTS +# name: CI + +# on: +# push: +# branches: [master, staging] +# pull_request: +# # By default, CI will trigger on opened/synchronize/reopened event types. +# # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request +# # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. +# branches: [master, feature/*, staging] + +# # Cancel old jobs when a pull request is updated. +# concurrency: +# group: ${{ github.head_ref || github.run_id }} +# cancel-in-progress: true + +# jobs: +# lint-commits: +# # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR. +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# with: +# fetch-depth: 20 +# - uses: actions/setup-node@v4 +# with: +# node-version: '20' +# - name: Validate Branch name +# if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref != ''}} +# env: +# BRANCH_NAME: ${{ github.event.pull_request.head.ref }} +# run: | +# node "$GITHUB_WORKSPACE/.github/workflows/lintbranch.js" run "$BRANCH_NAME" +# - name: Check PR title +# run: | +# node "$GITHUB_WORKSPACE/.github/workflows/lintcommit.js" + +# lint: +# needs: lint-commits +# runs-on: ubuntu-latest +# strategy: +# matrix: +# node-version: [18.x] +# vscode-version: [stable] +# env: +# NODE_OPTIONS: '--max-old-space-size=8192' +# steps: +# - uses: actions/checkout@v4 +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v4 +# with: +# node-version: ${{ matrix.node-version }} +# - run: npm ci +# - run: npm run testCompile +# - run: npm run lint + +# lint-duplicate-code: +# needs: lint-commits +# if: ${{ github.event_name == 'pull_request'}} +# runs-on: ubuntu-latest +# strategy: +# matrix: +# node-version: [18.x] +# env: +# NODE_OPTIONS: '--max-old-space-size=8192' + +# steps: +# - uses: actions/checkout@v4 +# with: +# fetch-depth: 0 + +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v4 +# with: +# node-version: ${{ matrix.node-version }} + +# - name: Fetch fork upstream +# env: +# REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }} +# run: | +# git remote add forkUpstream https://github.com/$REPO_NAME # URL of the fork +# git fetch forkUpstream # Fetch fork + +# - name: Merge in target branch to avoid false negatives. +# env: +# TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} +# # Note: "git merge" should always succeed here, because GHA won't +# # start the job if there are merge conflicts. https://github.com/orgs/community/discussions/11265 +# # Also, because `git merge` makes a commit, we need to establish an identity to avoid 'Committer identity unknown' error +# run: | +# git config --global user.name "aws-toolkit-automation" +# git config --global user.email "<>" +# git merge origin/$TARGET_BRANCH + +# - name: Compute git diff +# env: +# CURRENT_BRANCH: ${{ github.head_ref }} +# TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} +# run: git diff origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt + +# - run: npm install -g jscpd + +# - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" + +# - if: always() +# uses: actions/upload-artifact@v4 +# with: +# name: unfiltered-jscpd-report +# path: ./jscpd-report.json + +# - name: Check for Duplicates +# env: +# COMMIT_HASH: ${{ github.sha}} +# REPO_NAME: ${{ github.repository }} +# run: node "$GITHUB_WORKSPACE/.github/workflows/filterDuplicates.js" run diff_output.txt jscpd-report.json $COMMIT_HASH $REPO_NAME + +# macos: +# needs: lint-commits +# name: test macOS +# runs-on: macos-latest +# strategy: +# fail-fast: false +# matrix: +# node-version: [18.x] +# vscode-version: [minimum, stable, insiders] +# package: [amazonq, toolkit] +# env: +# VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} +# NODE_OPTIONS: '--max-old-space-size=8192' +# AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' +# AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' +# steps: +# - uses: actions/checkout@v4 +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v4 +# with: +# node-version: ${{ matrix.node-version }} +# - run: npm ci +# - name: Tests +# uses: coactions/setup-xvfb@v1 +# with: +# run: npm run test -w packages/${{ matrix.package }} +# - name: Code coverage for ${{ matrix.package }} +# env: +# # Unset NODE_OPTIONS because of https://github.com/codecov/uploader/issues/475 +# NODE_OPTIONS: '' +# if: ${{ github.repository == 'aws/aws-toolkit-vscode' && github.event_name == 'pull_request' && github.base_ref == 'master' }} +# uses: codecov/codecov-action@v5 +# with: +# flags: macos-${{ matrix.package }}-unittests +# verbose: true +# file: ./coverage/${{ matrix.package }}/lcov.info +# token: ${{ secrets.CODECOV_TOKEN }} + +# web: +# needs: lint-commits +# name: test Web +# runs-on: ubuntu-latest +# strategy: +# fail-fast: true +# matrix: +# node-version: [18.x] +# vscode-version: [stable, insiders] +# env: +# VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} +# NODE_OPTIONS: '--max-old-space-size=8192' +# AWS_TOOLKIT_TEST_CACHE_DIR: '/tmp/.vscode-test/' +# AWS_TOOLKIT_TEST_USER_DIR: '/tmp/.vscode-test/user-data/' +# steps: +# - uses: actions/checkout@v4 +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v4 +# with: +# node-version: ${{ matrix.node-version }} +# - run: npm ci +# - name: Tests +# uses: coactions/setup-xvfb@v1 +# with: +# run: npm run testWeb + +# windows: +# needs: lint-commits +# name: test Windows +# runs-on: windows-latest +# strategy: +# fail-fast: false +# matrix: +# node-version: [18.x] +# vscode-version: [stable, insiders] +# package: [amazonq, toolkit] +# env: +# VSCODE_TEST_VERSION: ${{ matrix.vscode-version }} +# NODE_OPTIONS: '--max-old-space-size=8192' +# steps: +# - uses: actions/checkout@v4 +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v4 +# with: +# node-version: ${{ matrix.node-version }} +# - run: npm ci +# - name: Tests +# run: npm run test -w packages/${{ matrix.package }} +# - name: Code coverage for ${{ matrix.package }} +# env: +# # Unset NODE_OPTIONS because of https://github.com/codecov/uploader/issues/475 +# NODE_OPTIONS: '' +# if: ${{ github.repository == 'aws/aws-toolkit-vscode' && github.event_name == 'pull_request' && github.base_ref == 'master' }} +# uses: codecov/codecov-action@v5 +# with: +# flags: windows-${{ matrix.package }}-unittests +# verbose: true +# file: ./coverage/${{ matrix.package }}/lcov.info +# token: ${{ secrets.CODECOV_TOKEN }} +# e2e: +# needs: lint-commits +# name: test E2E +# runs-on: ubuntu-latest +# strategy: +# matrix: +# node-version: [18.x] +# env: +# NODE_OPTIONS: '--max-old-space-size=8192' +# AUTH_UTIL_LAMBDA_ARN: ${{ secrets.AUTH_UTIL_LAMBDA_ARN }} + +# steps: +# - uses: actions/checkout@v4 +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v4 +# with: +# node-version: ${{ matrix.node-version }} +# - run: npm ci +# - name: E2E Tests +# uses: coactions/setup-xvfb@v1 +# with: +# run: npm run test:ui diff --git a/buildspec/linuxE2ETests.yml b/buildspec/linuxE2ETests.yml index 4cb2f08f03d..5d99f263e6f 100644 --- a/buildspec/linuxE2ETests.yml +++ b/buildspec/linuxE2ETests.yml @@ -13,6 +13,7 @@ env: # followed by Error: Could not delete obsolete instance handle Error: ENOENT: no such file or directory, unlink 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: @@ -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}" diff --git a/package.json b/package.json index 0647493580b..38d54b2fa0e 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/amazonq/test/e2e_new/amazonq/utils/authUtils.ts b/packages/amazonq/test/e2e_new/amazonq/utils/authUtils.ts index 226288be667..103bd6a1ba5 100644 --- a/packages/amazonq/test/e2e_new/amazonq/utils/authUtils.ts +++ b/packages/amazonq/test/e2e_new/amazonq/utils/authUtils.ts @@ -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 @@ -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 { + // 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() diff --git a/packages/amazonq/test/e2e_new/amazonq/utils/ciOidcClient.ts b/packages/amazonq/test/e2e_new/amazonq/utils/ciOidcClient.ts new file mode 100644 index 00000000000..c1b6dc34050 --- /dev/null +++ b/packages/amazonq/test/e2e_new/amazonq/utils/ciOidcClient.ts @@ -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 { + 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') +} diff --git a/packages/amazonq/test/e2e_new/amazonq/utils/ciUtils.ts b/packages/amazonq/test/e2e_new/amazonq/utils/ciUtils.ts new file mode 100644 index 00000000000..9bd9c71641e --- /dev/null +++ b/packages/amazonq/test/e2e_new/amazonq/utils/ciUtils.ts @@ -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 { + 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, +// }) +// } diff --git a/packages/amazonq/test/e2e_new/amazonq/utils/cleanupUtils.ts b/packages/amazonq/test/e2e_new/amazonq/utils/cleanupUtils.ts index 83f7d09e6bf..b3bf8818727 100644 --- a/packages/amazonq/test/e2e_new/amazonq/utils/cleanupUtils.ts +++ b/packages/amazonq/test/e2e_new/amazonq/utils/cleanupUtils.ts @@ -12,6 +12,7 @@ import { sleep } from './generalUtils' */ export async function closeAllTabs(webview: WebviewView): Promise { const closeButtons = await webview.findWebElements(By.css('.mynah-tabs-close-button')) + console.log('THIS WORKED 6') for (const button of closeButtons) { await button.click() @@ -19,6 +20,7 @@ export async function closeAllTabs(webview: WebviewView): Promise { } 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 diff --git a/packages/amazonq/test/e2e_new/amazonq/utils/setup.ts b/packages/amazonq/test/e2e_new/amazonq/utils/setup.ts index cf0805c2a69..26d8bb255ff 100644 --- a/packages/amazonq/test/e2e_new/amazonq/utils/setup.ts +++ b/packages/amazonq/test/e2e_new/amazonq/utils/setup.ts @@ -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) diff --git a/packages/core/src/auth/activation.ts b/packages/core/src/auth/activation.ts index 8305610dff7..9af412f72e1 100644 --- a/packages/core/src/auth/activation.ts +++ b/packages/core/src/auth/activation.ts @@ -11,20 +11,45 @@ import { initializeCredentialsProviderManager } from './utils' import { isAmazonQ, isSageMaker } from '../shared/extensionUtilities' import { getLogger } from '../shared/logger/logger' import { getErrorMsg } from '../shared/errors' +import { invokeLambda, patchObject } from '../test/setupUtil' export interface SagemakerCookie { authMode?: 'Sso' | 'Iam' } +// when the extension starts up, it runs this code, have it isolated here +// first, verify with logs before or after auth, if thats true, if all those environment variables +// once +/** + * Based on the debugging messages, this code gets activated the moment i press opne + * + * + */ export async function initialize(loginManager: LoginManager): Promise { + console.log('INITIALIZATION START') + getLogger().info('[DEBUG] Auth activation initialize() called') + + if (true) { + console.log('AUTH LAMBDA HAS BEEN TRIGGERED') + registerAuthHook('amazonq-test-account') + return + } + if (isAmazonQ() && isSageMaker()) { + getLogger().info('[DEBUG] Running in Amazon Q + SageMaker environment') try { + getLogger().info('[DEBUG] Attempting to parse SageMaker cookies') // The command `sagemaker.parseCookies` is registered in VS Code Sagemaker environment. const result = (await vscode.commands.executeCommand('sagemaker.parseCookies')) as SagemakerCookie + getLogger().info('[DEBUG] SageMaker cookie result:', result) if (result.authMode !== 'Sso') { + getLogger().info('[DEBUG] Initializing credentials provider manager for IAM mode') initializeCredentialsProviderManager() + } else { + getLogger().info('[DEBUG] Using SSO mode, skipping credentials provider manager') } } catch (e) { + getLogger().info('[DEBUG] Error parsing SageMaker cookies:', e) const errMsg = getErrorMsg(e as Error) if (errMsg?.includes("command 'sagemaker.parseCookies' not found")) { getLogger().warn(`Failed to execute command "sagemaker.parseCookies": ${e}`) @@ -32,13 +57,51 @@ export async function initialize(loginManager: LoginManager): Promise { throw e } } + } else { + getLogger().info('[DEBUG] Not in Amazon Q + SageMaker environment') } + getLogger().info('[DEBUG] Setting up Auth connection change listener') Auth.instance.onDidChangeActiveConnection(async (conn) => { + getLogger().info('[DEBUG] Auth connection changed:', conn) // This logic needs to be moved to `Auth.useConnection` to correctly record `passive` if (conn?.type === 'iam' && conn.state === 'valid') { + getLogger().info('[DEBUG] Logging in with IAM connection') await loginManager.login({ passive: true, providerId: fromString(conn.id) }) } else { + getLogger().info('[DEBUG] Logging out - connection invalid or not IAM') await loginManager.logout() } }) + + getLogger().info('[DEBUG] Auth activation initialize() completed') +} + +export function registerAuthHook(secret: string, lambdaId = process.env['AUTH_UTIL_LAMBDA_ARN']) { + const openStub = patchObject(vscode.env, 'openExternal', async (target) => { + try { + if (!lambdaId) { + return false + } + + // Latest eg: 'https://nkomonen.awsapps.com/start/#/device?user_code=JXZC-NVRK' + const urlString = target.toString(true) + + // 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, + }) + } finally { + openStub.dispose() + } + return true + }) } diff --git a/packages/core/src/shared/settings-amazonq.gen.ts b/packages/core/src/shared/settings-amazonq.gen.ts index 637c5b1b12e..836b68444f2 100644 --- a/packages/core/src/shared/settings-amazonq.gen.ts +++ b/packages/core/src/shared/settings-amazonq.gen.ts @@ -36,7 +36,8 @@ export const amazonqSettings = { "amazonQ.workspaceIndexMaxFileSize": {}, "amazonQ.workspaceIndexCacheDirPath": {}, "amazonQ.workspaceIndexIgnoreFilePatterns": {}, - "amazonQ.ignoredSecurityIssues": {} + "amazonQ.ignoredSecurityIssues": {}, + "amazonQ.proxy.certificateAuthority": {} } export default amazonqSettings