Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
113 changes: 113 additions & 0 deletions packages/amazonq/test/e2e/amazonq/doc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import assert from 'assert'
import { qTestingFramework } from './framework/framework'
import sinon from 'sinon'
import { registerAuthHook, using } from 'aws-core-vscode/test'
import { loginToIdC } from './utils/setup'
import { Messenger } from './framework/messenger'
import { FollowUpTypes } from 'aws-core-vscode/amazonq'
import { i18n } from 'aws-core-vscode/shared'
import { docGenerationProgressMessage, DocGenerationStep, Mode } from 'aws-core-vscode/amazonqDoc'

describe('Amazon Q Doc', async function () {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
describe('Amazon Q Doc', async function () {
describe('Amazon Q /doc', async function () {

let framework: qTestingFramework
let tab: Messenger

before(async function () {
/**
* The tests are getting throttled, only run them on stable for now
*
* TODO: Re-enable for all versions once the backend can handle them
*/
const testVersion = process.env['VSCODE_TEST_VERSION']
if (testVersion && testVersion !== 'stable') {
this.skip()
}

await using(registerAuthHook('amazonq-test-account'), async () => {
await loginToIdC()
})
})

beforeEach(() => {
registerAuthHook('amazonq-test-account')
framework = new qTestingFramework('doc', true, [])
tab = framework.createTab()
})

afterEach(() => {
framework.removeTab(tab.tabID)
framework.dispose()
sinon.restore()
Copy link
Contributor

@justinmk3 justinmk3 Dec 12, 2024

Choose a reason for hiding this comment

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

why is sinon needed in e2e tests? if this is part of the qTestingFramework, then I would expect framework.dispose() to take care of it.

})

describe('Quick action availability', () => {
it('Shows /doc when doc generation is enabled', async () => {
const command = tab.findCommand('/doc')
if (!command.length) {
assert.fail('Could not find command')
}

if (command.length > 1) {
assert.fail('Found too many commands with the name /doc')
}
})

it('Does NOT show /doc when doc generation is NOT enabled', () => {
// The beforeEach registers a framework which accepts requests. If we don't dispose before building a new one we have duplicate messages
framework.dispose()
framework = new qTestingFramework('doc', false, [])
const tab = framework.createTab()
const command = tab.findCommand('/doc')
if (command.length > 0) {
assert.fail('Found command when it should not have been found')
}
})
})

describe('/doc entry', () => {
beforeEach(async function () {
tab.addChatMessage({ command: '/doc' })
await tab.waitForChatFinishesLoading()
})

it('Checks for initial follow ups', async () => {
await tab.waitForButtons([FollowUpTypes.CreateDocumentation, FollowUpTypes.UpdateDocumentation])
})
})

describe('Creates a README', () => {
beforeEach(async function () {
tab.addChatMessage({ command: '/doc' })
await tab.waitForChatFinishesLoading()
})

it('Creates a README for root folder', async () => {
await tab.waitForButtons([FollowUpTypes.CreateDocumentation])

tab.clickButton(FollowUpTypes.CreateDocumentation)

await tab.waitForText(i18n('AWS.amazonq.doc.answer.createReadme'))

await tab.waitForButtons([FollowUpTypes.ProceedFolderSelection])

tab.clickButton(FollowUpTypes.ProceedFolderSelection)

await tab.waitForText(docGenerationProgressMessage(DocGenerationStep.SUMMARIZING_FILES, Mode.CREATE))

await tab.waitForText(
`${i18n('AWS.amazonq.doc.answer.readmeCreated')} ${i18n('AWS.amazonq.doc.answer.codeResult')}`
)

await tab.waitForButtons([
FollowUpTypes.AcceptChanges,
FollowUpTypes.MakeChanges,
FollowUpTypes.RejectChanges,
])
})
})
})
41 changes: 15 additions & 26 deletions packages/amazonq/test/e2e/amazonq/featureDev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,11 @@ describe('Amazon Q Feature Dev', function () {
const fileLevelAcceptPrompt = `${prompt} and add a license, and a contributing file`
const tooManyRequestsWaitTime = 100000

function waitForButtons(buttons: FollowUpTypes[]) {
return tab.waitForEvent(() => {
return buttons.every((value) => tab.hasButton(value))
})
}

async function waitForText(text: string) {
await tab.waitForEvent(
() => {
return tab.getChatItems().some((chatItem) => chatItem.body === text)
},
{
waitIntervalInMs: 250,
waitTimeoutInMs: 2000,
}
)
await tab.waitForText(text, {
waitIntervalInMs: 250,
waitTimeoutInMs: 2000,
})
}

async function iterate(prompt: string) {
Expand Down Expand Up @@ -201,12 +190,12 @@ describe('Amazon Q Feature Dev', function () {
it('Clicks accept code and click new task', async () => {
await retryIfRequired(async () => {
await Promise.any([
waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]),
waitForButtons([FollowUpTypes.Retry]),
tab.waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]),
tab.waitForButtons([FollowUpTypes.Retry]),
])
})
tab.clickButton(FollowUpTypes.InsertCode)
await waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
tab.clickButton(FollowUpTypes.NewTask)
await waitForText('What new task would you like to work on?')
assert.deepStrictEqual(tab.getChatItems().pop()?.body, 'What new task would you like to work on?')
Expand All @@ -215,15 +204,15 @@ describe('Amazon Q Feature Dev', function () {
it('Iterates on codegen', async () => {
await retryIfRequired(async () => {
await Promise.any([
waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]),
waitForButtons([FollowUpTypes.Retry]),
tab.waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]),
tab.waitForButtons([FollowUpTypes.Retry]),
])
})
tab.clickButton(FollowUpTypes.ProvideFeedbackAndRegenerateCode)
await tab.waitForChatFinishesLoading()
await iterate(codegenApproachPrompt)
tab.clickButton(FollowUpTypes.InsertCode)
await waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
})
})

Expand All @@ -240,8 +229,8 @@ describe('Amazon Q Feature Dev', function () {
)
await retryIfRequired(async () => {
await Promise.any([
waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]),
waitForButtons([FollowUpTypes.Retry]),
tab.waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]),
tab.waitForButtons([FollowUpTypes.Retry]),
])
})
})
Expand Down Expand Up @@ -271,7 +260,7 @@ describe('Amazon Q Feature Dev', function () {

it('disables all action buttons when new task is clicked', async () => {
tab.clickButton(FollowUpTypes.InsertCode)
await waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
tab.clickButton(FollowUpTypes.NewTask)
await waitForText('What new task would you like to work on?')

Expand All @@ -283,7 +272,7 @@ describe('Amazon Q Feature Dev', function () {

it('disables all action buttons when close session is clicked', async () => {
tab.clickButton(FollowUpTypes.InsertCode)
await waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
tab.clickButton(FollowUpTypes.CloseSession)
await waitForText(
"Okay, I've ended this chat session. You can open a new tab to chat or start another workflow."
Expand Down Expand Up @@ -335,7 +324,7 @@ describe('Amazon Q Feature Dev', function () {
for (const filePath of filePaths) {
await clickActionButton(filePath, 'accept-change')
}
await waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])
await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession])

assert.ok(tab.hasButton(FollowUpTypes.InsertCode) === false)
assert.ok(tab.hasButton(FollowUpTypes.ProvideFeedbackAndRegenerateCode) === false)
Expand Down
23 changes: 15 additions & 8 deletions packages/amazonq/test/e2e/amazonq/framework/messenger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class Messenger {

const lastChatItem = this.getChatItems().pop()
const option = lastChatItem?.followUp?.options?.filter((option) => option.type === type)
if (!option || option.length > 1) {
if (!option?.length || option.length > 1) {
assert.fail('Could not find follow up option')
}

Expand Down Expand Up @@ -153,17 +153,24 @@ export class Messenger {
return this.getActionsByFilePath(filePath).some((action) => action.name === actionName)
}

async waitForText(text: string, waitOverrides?: MessengerOptions) {
await this.waitForEvent(() => {
console.log(this.getChatItems())
Copy link
Contributor

Choose a reason for hiding this comment

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

remove this

Copy link
Contributor

Choose a reason for hiding this comment

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

tbh i think the console logs are useful. When you have a set of e2e tests that take a look time to run its usually a lot easier to see why they failed by looking at what chat items are present

Copy link
Contributor

Choose a reason for hiding this comment

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

We should technically only log chat items when we encounter an error'd test though, otherwise theres just going to be a lot of spam in the logs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah this was accidentally added to the commit

return this.getChatItems().some((chatItem) => chatItem.body === text)
}, waitOverrides)
}

async waitForButtons(buttons: FollowUpTypes[]) {
return this.waitForEvent(() => {
return buttons.every((value) => this.hasButton(value))
})
}

async waitForChatFinishesLoading() {
return this.waitForEvent(() => this.getStore().loadingChat === false || this.hasButton(FollowUpTypes.Retry))
}

async waitForEvent(
event: () => boolean,
waitOverrides?: {
waitIntervalInMs: number
waitTimeoutInMs: number
}
) {
async waitForEvent(event: () => boolean, waitOverrides?: MessengerOptions) {
/**
* Wait until the chat has finished loading. This happens when a backend request
* has finished and responded in the chat
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"./auth": "./dist/src/auth/index.js",
"./amazonqGumby": "./dist/src/amazonqGumby/index.js",
"./amazonqFeatureDev": "./dist/src/amazonqFeatureDev/index.js",
"./amazonqDoc": "./dist/src/amazonqDoc/index.js",
"./amazonqScan": "./dist/src/amazonqScan/index.js",
"./amazonqTest": "./dist/src/amazonqTest/index.js",
"./codewhispererChat": "./dist/src/codewhispererChat/index.js",
Expand Down
Loading