Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
070a152
Adding WaitUntil to Test gen api
laileni-aws Jan 28, 2025
5662e1b
Merge branch 'aws:master' into testGen
laileni-aws Jan 29, 2025
abae26d
Adding back Build and execute
laileni-aws Jan 30, 2025
2de0e4d
Revert "Adding WaitUntil to Test gen api"
laileni-aws Feb 4, 2025
219ac6b
Merge branch 'aws:master' into testGen
laileni-aws Feb 12, 2025
1bdcdc1
Adding build and execute for /tesdt
laileni-aws Feb 18, 2025
ddb1965
Merge branch 'aws:master' into testGen
laileni-aws Feb 20, 2025
afbf326
Adding toolkit telemetry for build and execute in UTG
laileni-aws Feb 21, 2025
5bec46b
Minor edits
laileni-aws Feb 22, 2025
1656cc6
moving telemetry defination to common's repo and bumping package version
laileni-aws Feb 22, 2025
2de6635
Show the Amazon Q Logs while executing client side build
laileni-aws Feb 25, 2025
ad18287
If user tries to generate /test on second or third package in a works…
laileni-aws Feb 25, 2025
01fe31f
Modifying telemetry for unittestgeneration
laileni-aws Feb 26, 2025
6e6cb37
build exec ux changes (#8)
chungjac Feb 26, 2025
f38bb48
Merge branch 'aws:master' into testGen
laileni-aws Feb 28, 2025
d0b4754
UX Changes
laileni-aws Mar 3, 2025
36d3d4c
Fit and finish
laileni-aws Mar 4, 2025
cf38c1f
Fit and finish
laileni-aws Mar 5, 2025
8da4dab
Merge branch 'feature/q-utg' into testGen
laileni-aws Mar 5, 2025
6f3fb02
fixing merge conflict errors
laileni-aws Mar 5, 2025
208b74c
UX text changes
laileni-aws Mar 6, 2025
2293204
Adding ui_click event for fixing tests
laileni-aws Mar 6, 2025
be6d072
Adding status field to unit test generation events
laileni-aws Mar 6, 2025
635fc38
Modifying the UX according to the requirement of V1 version of build …
laileni-aws Mar 6, 2025
0cbb061
Minor ux chnage
laileni-aws Mar 6, 2025
f385057
Merge branch 'feature/q-utg' into testGen
chungjac Mar 11, 2025
610dc77
fix conflicts
chungjac Mar 11, 2025
251d1f6
Build Exec ShortAnswer Refactoring (#10)
chungjac Mar 13, 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
2 changes: 1 addition & 1 deletion packages/core/src/amazonq/webview/ui/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ export const createMynahUI = (
mynahUI.addChatItem(tabID, {
type: ChatItemType.ANSWER_STREAM,
})
} else if (tabType === 'gumby') {
} else if (tabType === 'gumby' || tabType === 'testgen') {
connector.requestAnswer(tabID, {
chatMessage: prompt.prompt ?? '',
})
Expand Down
336 changes: 208 additions & 128 deletions packages/core/src/amazonqTest/chat/controller/controller.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum ButtonActions {
VIEW_DIFF = 'View-Diff',
STOP_TEST_GEN = 'Stop-Test-Generation',
STOP_BUILD = 'Stop-Build-Process',
STOP_FIXING_TEST = 'Stop-Fixing-Test',
PROVIDE_FEEDBACK = 'Provide-Feedback',
}

Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/amazonqTest/chat/session/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { ShortAnswer, Reference } from '../../../codewhisperer/models/model'
import { TargetFileInfo, TestGenerationJob } from '../../../codewhisperer/client/codewhispereruserclient'
import { Reference } from '../../../codewhisperer/models/model'
import { PackageInfo, TargetFileInfo, TestGenerationJob } from '../../../codewhisperer/client/codewhispereruserclient'

export enum ConversationState {
IDLE,
Expand Down Expand Up @@ -37,13 +37,13 @@ export class Session {
// Start Test generation
public isSupportedLanguage: boolean = false
public conversationState: ConversationState = ConversationState.IDLE
public shortAnswer: ShortAnswer | undefined
public sourceFilePath: string = ''
public generatedFilePath: string = ''
public projectRootPath: string = ''
public fileLanguage: string | undefined = 'plaintext'
public stopIteration: boolean = false
public targetFileInfo: TargetFileInfo | undefined
public packageInfo: PackageInfo | undefined
public jobSummary: string = ''

// Telemetry
Expand Down
110 changes: 79 additions & 31 deletions packages/core/src/amazonqTest/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ export const testChat = 'testChat'

export const maxUserPromptLength = 4096 // user prompt character limit from MPS and API model.

const baseProgressField: Partial<ProgressField> = {
status: 'default',
value: -1,
}

export const cancelTestGenButton: ChatItemButton = {
id: ButtonActions.STOP_TEST_GEN,
text: 'Cancel',
icon: 'cancel' as MynahIcons,
}

export const testGenProgressField: ProgressField = {
status: 'default',
value: -1,
...baseProgressField,
text: 'Generating unit tests...',
actions: [cancelTestGenButton],
}
Expand All @@ -46,18 +50,29 @@ export const cancelBuildProgressButton: ChatItemButton = {
icon: 'cancel' as MynahIcons,
}

export const cancelFixingTestButton: ChatItemButton = {
id: ButtonActions.STOP_FIXING_TEST,
text: 'Cancel',
icon: 'cancel' as MynahIcons,
}

export const buildProgressField: ProgressField = {
status: 'default',
value: -1,
text: 'Executing...',
...baseProgressField,
text: 'Project compiling...',
actions: [cancelBuildProgressButton],
}

export const fixingTestProgressField: ProgressField = {
...baseProgressField,
text: 'Fixing test failures...',
actions: [cancelFixingTestButton],
}

export const errorProgressField: ProgressField = {
status: 'error',
text: 'Error...Input needed',
value: -1,
actions: [cancelBuildProgressButton],
actions: [cancelTestGenButton],
}

export const testGenSummaryMessage = (
Expand All @@ -70,67 +85,99 @@ ${planSummary ? `\n\n${planSummary}` : ''}
`

const checkIcons = {
wait: '&#9744;',
current: '&#9744;',
wait: '&#9203;',
current: '&#9203;',
done: '<span style="color: green;">&#10004;</span>',
error: '&#10060;',
}

// TODO: Commenting out this code to do a better UX in the V2 version after science support
/*
interface StepStatus {
step: TestGenerationBuildStep
status: 'wait' | 'current' | 'done' | 'error'
}

const stepStatuses: StepStatus[] = []

*/
export const testGenBuildProgressMessage = (currentStep: TestGenerationBuildStep, status?: string) => {
const session = ChatSessionManager.Instance.getSession()
const statusText = BuildStatus[session.buildStatus].toLowerCase()
const icon = session.buildStatus === BuildStatus.SUCCESS ? checkIcons['done'] : checkIcons['error']
let message = `Sure. This may take a few minutes and I'll share updates on my progress here.
let message = `Sure. This may take a few minutes and I'll update the progress here.\n
**Progress summary**\n\n`

if (currentStep === TestGenerationBuildStep.START_STEP) {
return message.trim()
}

if (currentStep === TestGenerationBuildStep.RUN_BUILD) {
message += `${checkIcons['wait']} Project compiling\n\n`
}
if (currentStep === TestGenerationBuildStep.FIXING_TEST_CASES && session.buildStatus === BuildStatus.FAILURE) {
message += `${checkIcons['wait']} Fixing test failures\n\n`
} else if (
currentStep >= TestGenerationBuildStep.FIXING_TEST_CASES &&
session.buildStatus === BuildStatus.FAILURE
) {
message += `${checkIcons['done']} Fixed test failures\n\n`
}
if (currentStep > TestGenerationBuildStep.RUN_BUILD && session.buildStatus === BuildStatus.SUCCESS) {
message += `${checkIcons['done']} Project compiled\n${checkIcons['done']} All tests passed\n\n`
}
/*
updateStepStatuses(currentStep, status)

if (currentStep >= TestGenerationBuildStep.RUN_BUILD) {
message += `${getIconForStep(TestGenerationBuildStep.RUN_BUILD)} Started build execution\n`
message += `${getIconForStep(TestGenerationBuildStep.RUN_BUILD)} ${
currentStep === TestGenerationBuildStep.RUN_BUILD
? 'Project compiling\n'
: session.buildStatus === BuildStatus.FAILURE
? 'Unable to compile project\n'
: 'Project compiled\n'
}`
}

if (currentStep >= TestGenerationBuildStep.RUN_EXECUTION_TESTS) {
message += `${getIconForStep(TestGenerationBuildStep.RUN_EXECUTION_TESTS)} Executing tests\n`
if (currentStep === TestGenerationBuildStep.RUN_EXECUTION_TESTS) {
message += `${getIconForStep(TestGenerationBuildStep.RUN_EXECUTION_TESTS)} Running tests\n`
} else if (currentStep >= TestGenerationBuildStep.RUN_EXECUTION_TESTS) {
message += `${getIconForStep(TestGenerationBuildStep.RUN_EXECUTION_TESTS)} ${
session.buildStatus === BuildStatus.FAILURE ? 'Tests failed\n' : 'Tests passed\n'
}`
}

if (currentStep >= TestGenerationBuildStep.FIXING_TEST_CASES && session.buildStatus === BuildStatus.FAILURE) {
message += `${getIconForStep(TestGenerationBuildStep.FIXING_TEST_CASES)} Fixing errors in tests\n\n`
if (currentStep === TestGenerationBuildStep.FIXING_TEST_CASES && session.buildStatus === BuildStatus.FAILURE) {
message += `${getIconForStep(TestGenerationBuildStep.FIXING_TEST_CASES)} Fixing test failures\n\n`
} else if (
currentStep >= TestGenerationBuildStep.FIXING_TEST_CASES &&
session.buildStatus === BuildStatus.FAILURE
) {
message += `${checkIcons['done']} Fixed test failures\n\n`
}

if (currentStep > TestGenerationBuildStep.PROCESS_TEST_RESULTS) {
message += `**Test case summary**
${session.shortAnswer?.testCoverage ? `- Unit test coverage ${session.shortAnswer?.testCoverage}%` : ``}
${icon} Build ${statusText}
${icon} Assertion ${statusText}`
// TODO: Update Assertion %
*/
if (currentStep > TestGenerationBuildStep.PROCESS_TEST_RESULTS && session.buildStatus === BuildStatus.FAILURE) {
message += `**Results**\n
Amazon Q executed the tests and identified at least one failure. Below are the suggested fixes.\n\n`
}

return message.trim()
}
// TODO: Work on UX to show the build error in the progress message
/*
const updateStepStatuses = (currentStep: TestGenerationBuildStep, status?: string) => {
const session = ChatSessionManager.Instance.getSession()
for (let step = TestGenerationBuildStep.INSTALL_DEPENDENCIES; step <= currentStep; step++) {
const stepStatus: StepStatus = {
step: step,
status: 'wait',
}

if (step === currentStep) {
stepStatus.status = status === 'failed' ? 'error' : 'current'
} else if (step < currentStep) {
stepStatus.status = 'done'
}
stepStatus.status =
step === currentStep
? status === 'error' || status === 'done'
? status
: 'current'
: step < currentStep
? session.buildStatus === BuildStatus.FAILURE
? 'error'
: 'done'
: stepStatus.status

const existingIndex = stepStatuses.findIndex((s) => s.step === step)
if (existingIndex !== -1) {
Expand All @@ -145,3 +192,4 @@ const getIconForStep = (step: TestGenerationBuildStep) => {
const stepStatus = stepStatuses.find((s) => s.step === step)
return stepStatus ? checkIcons[stepStatus.status] : checkIcons.wait
}
*/
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export async function startTestGenerationProcess(
logger.verbose(`Tab ID mismatch: ${tabID} !== ${session.tabID}`)
return
}
testGenState.setToNotStarted()
/**
* Step 1: Zip the project
*/
Expand All @@ -53,7 +54,6 @@ export async function startTestGenerationProcess(
const projectPath = zipUtil.getProjectPath(filePath) ?? ''
const relativeTargetPath = path.relative(projectPath, filePath)
session.listOfTestGenerationJobId = []
session.shortAnswer = undefined
session.sourceFilePath = relativeTargetPath
session.projectRootPath = projectPath
session.listOfTestGenerationJobId = []
Expand Down Expand Up @@ -212,7 +212,7 @@ function runLocalBuild(
if (spawnResult.stderr) {
spawnResult.stderr.on('data', async (data) => {
const output = data.toString().trim()
getLogger().warn(`BUILD ERROR: ${output}`)
getLogger().warn(`${output}`)
buildLogs += output
})
}
Expand Down
15 changes: 0 additions & 15 deletions packages/core/src/codewhisperer/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1174,18 +1174,3 @@ export interface Reference {
end: number
}
}

// TODO: remove ShortAnswer because it will be deprecated
export interface ShortAnswer {
testFilePath: string
buildCommands: string[]
planSummary: string
sourceFilePath?: string
testFramework?: string
executionCommands?: string[]
testCoverage?: number
stopIteration?: string
errorMessage?: string
codeReferences?: References
numberOfTestMethods?: number
}
27 changes: 15 additions & 12 deletions packages/core/src/codewhisperer/service/testGenHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import fs from '../../shared/fs/fs'
export function throwIfCancelled() {
// TODO: fileName will be '' if user gives propt without opening
if (testGenState.isCancelling()) {
testGenState.setToNotStarted()
throw new TestGenStoppedError()
}
}
Expand Down Expand Up @@ -150,11 +151,14 @@ export async function pollTestJobStatus(
logger.debug('pollTestJobStatus testGenerationJob %O', resp.testGenerationJob)
ChatSessionManager.Instance.getSession().testGenerationJob = resp.testGenerationJob
const progressRate = resp.testGenerationJob?.progressRate ?? 0
testGenState.getChatControllers()?.sendUpdatePromptProgress.fire({
tabID: ChatSessionManager.Instance.getSession().tabID,
status: 'InProgress',
progressRate,
})
if (initialExecution) {
testGenState.getChatControllers()?.sendUpdatePromptProgress.fire({
tabID: ChatSessionManager.Instance.getSession().tabID,
status: 'InProgress',
progressRate,
})
}

const jobSummary = resp.testGenerationJob?.jobSummary ?? ''
const jobSummaryNoBackticks = jobSummary.replace(/^`+|`+$/g, '')
ChatSessionManager.Instance.getSession().jobSummary = jobSummaryNoBackticks
Expand All @@ -163,7 +167,8 @@ export async function pollTestJobStatus(
const targetFileInfo = packageInfo?.targetFileInfoList?.[0]

if (packageInfo) {
// TODO: will need some fields from packageInfo such as buildCommand, packagePlan, packageSummary
// TODO: will use some fields from packageInfo in the future
ChatSessionManager.Instance.getSession().packageInfo = packageInfo
}
if (targetFileInfo) {
if (targetFileInfo.numberOfTestMethods) {
Expand Down Expand Up @@ -252,13 +257,11 @@ export async function exportResultsArchive(
filePath: testFilePath,
projectName,
})

// If User accepts the diff
testGenState.getChatControllers()?.sendUpdatePromptProgress.fire({
tabID: ChatSessionManager.Instance.getSession().tabID,
status: 'Completed',
})
}
testGenState.getChatControllers()?.sendUpdatePromptProgress.fire({
tabID: ChatSessionManager.Instance.getSession().tabID,
status: 'Completed',
})
} catch (e) {
session.numberOfTestsGenerated = 0
downloadErrorMessage = (e as Error).message
Expand Down
Loading
Loading