Skip to content

Commit 8831e6b

Browse files
authored
feat(amazonq): user can generate unit tests for selected code #5577
- Add command to Amazon Q to generate unit tests for selected code. The feature will be limited to internal Amazon users, initially. - Add "Generate Tests" command.
1 parent 47b436f commit 8831e6b

File tree

13 files changed

+85
-12
lines changed

13 files changed

+85
-12
lines changed

packages/amazonq/package.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,14 @@
349349
"command": "aws.amazonq.optimizeCode",
350350
"group": "cw_chat@4"
351351
},
352+
{
353+
"command": "aws.amazonq.generateUnitTests",
354+
"group": "cw_chat@5",
355+
"when": "aws.codewhisperer.connected && aws.isInternalUser"
356+
},
352357
{
353358
"command": "aws.amazonq.sendToPrompt",
354-
"group": "cw_chat@5"
359+
"group": "cw_chat@6"
355360
}
356361
],
357362
"editor/context": [
@@ -423,6 +428,12 @@
423428
"category": "%AWS.amazonq.title%",
424429
"enablement": "aws.codewhisperer.connected"
425430
},
431+
{
432+
"command": "aws.amazonq.generateUnitTests",
433+
"title": "%AWS.command.amazonq.generateUnitTests%",
434+
"category": "%AWS.amazonq.title%",
435+
"enablement": "aws.codewhisperer.connected && aws.isInternalUser"
436+
},
426437
{
427438
"command": "aws.amazonq.reconnect",
428439
"title": "%AWS.command.codewhisperer.reconnect%",
@@ -599,6 +610,13 @@
599610
"mac": "cmd+alt+q",
600611
"linux": "meta+alt+q"
601612
},
613+
{
614+
"command": "aws.amazonq.generateUnitTests",
615+
"key": "win+alt+t",
616+
"mac": "cmd+alt+t",
617+
"linux": "meta+alt+t",
618+
"when": "aws.codewhisperer.connected && aws.isInternalUser"
619+
},
602620
{
603621
"command": "aws.amazonq.invokeInlineCompletion",
604622
"key": "alt+c",

packages/core/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
"AWS.command.amazonq.fixCode": "Fix",
112112
"AWS.command.amazonq.optimizeCode": "Optimize",
113113
"AWS.command.amazonq.sendToPrompt": "Send to prompt",
114+
"AWS.command.amazonq.generateUnitTests": "Generate Tests (Beta)",
114115
"AWS.command.amazonq.security.scan": "Run Project Scan",
115116
"AWS.command.deploySamApplication": "Deploy SAM Application",
116117
"AWS.command.aboutToolkit": "About",

packages/core/src/auth/auth.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,15 @@ import {
5959
AwsConnection,
6060
scopesCodeWhispererCore,
6161
ProfileNotFoundError,
62+
isSsoConnection,
6263
} from './connection'
6364
import { isSageMaker, isCloud9, isAmazonQ } from '../shared/extensionUtilities'
6465
import { telemetry } from '../shared/telemetry/telemetry'
6566
import { randomUUID } from '../shared/crypto'
6667
import { asStringifiedStack } from '../shared/telemetry/spans'
6768
import { withTelemetryContext } from '../shared/telemetry/util'
6869
import { DiskCacheError } from '../shared/utilities/cacheUtils'
70+
import { setContext } from '../shared/vscode/setContext'
6971

7072
interface AuthService {
7173
/**
@@ -166,6 +168,30 @@ export class Auth implements AuthService, ConnectionManager {
166168
return this.#ssoCacheWatcher
167169
}
168170

171+
public get startUrl(): string | undefined {
172+
return isSsoConnection(this.activeConnection)
173+
? this.normalizeStartUrl(this.activeConnection.startUrl)
174+
: undefined
175+
}
176+
177+
public isConnected(): boolean {
178+
return this.activeConnection !== undefined
179+
}
180+
181+
/**
182+
* Normalizes the provided URL
183+
*
184+
* Any trailing '/' and `#` is removed from the URL
185+
* e.g. https://view.awsapps.com/start/# will become https://view.awsapps.com/start
186+
*/
187+
public normalizeStartUrl(startUrl: string | undefined) {
188+
return !startUrl ? undefined : startUrl.replace(/[\/#]+$/g, '')
189+
}
190+
191+
public isInternalAmazonUser(): boolean {
192+
return this.isConnected() && this.startUrl === 'https://amzn.awsapps.com/start'
193+
}
194+
169195
/**
170196
* Map startUrl -> declared connections
171197
*/
@@ -223,6 +249,8 @@ export class Auth implements AuthService, ConnectionManager {
223249
this.#onDidChangeActiveConnection.fire(conn)
224250
await this.store.setCurrentProfileId(id)
225251

252+
await setContext('aws.isInternalUser', this.isInternalAmazonUser())
253+
226254
return conn
227255
}
228256

@@ -373,6 +401,7 @@ export class Auth implements AuthService, ConnectionManager {
373401
}
374402
}
375403
this.#onDidDeleteConnection.fire({ connId, storedProfile: profile })
404+
await setContext('aws.isInternalUser', false)
376405
}
377406

378407
@withTelemetryContext({ name: 'clearStaleLinkedIamConnections', class: authClassName })
@@ -405,6 +434,7 @@ export class Auth implements AuthService, ConnectionManager {
405434
await provider.invalidate('devModeManualExpiration')
406435
// updates the state of the connection
407436
await this.refreshConnectionState(conn)
437+
await setContext('aws.isInternalUser', false)
408438
}
409439

410440
public async getConnection(connection: Pick<Connection, 'id'>): Promise<Connection | undefined> {

packages/core/src/codewhispererChat/commands/registerCommands.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ export function registerCommands(controllerPublishers: ChatControllerMessagePubl
9494
})
9595
})
9696
})
97+
Commands.register('aws.amazonq.generateUnitTests', async (data) => {
98+
return focusAmazonQPanel.execute(placeholder, 'amazonq.generateUnitTests').then(() => {
99+
controllerPublishers.processContextMenuCommand.publish({
100+
type: 'aws.amazonq.generateUnitTests',
101+
triggerType: getCommandTriggerType(data),
102+
})
103+
})
104+
})
97105
}
98106

99107
export type EditorContextBaseCommandType =
@@ -102,6 +110,7 @@ export type EditorContextBaseCommandType =
102110
| 'aws.amazonq.fixCode'
103111
| 'aws.amazonq.optimizeCode'
104112
| 'aws.amazonq.sendToPrompt'
113+
| 'aws.amazonq.generateUnitTests'
105114

106115
export type CodeScanIssueCommandType = 'aws.amazonq.explainIssue'
107116

packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ export class Messenger {
340340
['aws.amazonq.fixCode', 'Fix'],
341341
['aws.amazonq.optimizeCode', 'Optimize'],
342342
['aws.amazonq.sendToPrompt', 'Send to prompt'],
343+
['aws.amazonq.generateUnitTests', 'Generate unit tests for'],
343344
])
344345

345346
public sendStaticTextResponse(type: StaticTextResponseType, triggerID: string, tabID: string) {

packages/core/src/codewhispererChat/controllers/chat/prompts/promptsGenerator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export class PromptsGenerator {
1414
['aws.amazonq.fixCode', 'Fix'],
1515
['aws.amazonq.optimizeCode', 'Optimize'],
1616
['aws.amazonq.sendToPrompt', 'Send to prompt'],
17+
['aws.amazonq.generateUnitTests', 'Generate unit tests for'],
1718
])
1819

1920
public generateForContextMenuCommand(command: EditorContextCommand): string {

packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ export class CWCTelemetryHelper {
8686
return 'explainLineByLine'
8787
case UserIntent.SHOW_EXAMPLES:
8888
return 'showExample'
89+
case UserIntent.GENERATE_UNIT_TESTS:
90+
return 'generateUnitTests'
8991
default:
9092
return undefined
9193
}

packages/core/src/codewhispererChat/controllers/chat/userIntent/userIntentRecognizer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { UserIntent } from '@amzn/codewhisperer-streaming'
77
import { EditorContextCommand } from '../../../commands/registerCommands'
88
import { PromptMessage } from '../model'
9+
import { Auth } from '../../../../auth'
910

1011
export class UserIntentRecognizer {
1112
public getFromContextMenuCommand(command: EditorContextCommand): UserIntent | undefined {
@@ -18,6 +19,8 @@ export class UserIntentRecognizer {
1819
return UserIntent.APPLY_COMMON_BEST_PRACTICES
1920
case 'aws.amazonq.optimizeCode':
2021
return UserIntent.IMPROVE_CODE
22+
case 'aws.amazonq.generateUnitTests':
23+
return UserIntent.GENERATE_UNIT_TESTS
2124
default:
2225
return undefined
2326
}
@@ -36,6 +39,8 @@ export class UserIntentRecognizer {
3639
return UserIntent.APPLY_COMMON_BEST_PRACTICES
3740
} else if (prompt.message.startsWith('Optimize')) {
3841
return UserIntent.IMPROVE_CODE
42+
} else if (prompt.message.startsWith('Generate unit tests') && Auth.instance.isInternalAmazonUser()) {
43+
return UserIntent.GENERATE_UNIT_TESTS
3944
}
4045
return undefined
4146
}

packages/core/src/shared/telemetry/vscodeTelemetry.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"showExample",
5959
"citeSources",
6060
"explainLineByLine",
61-
"explainCodeSelection"
61+
"explainCodeSelection",
62+
"generateUnitTests"
6263
],
6364
"description": "Explict user intent associated with a chat message"
6465
},

packages/core/src/shared/vscode/setContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type contextKey =
1414
| 'aws.isDevMode'
1515
| 'aws.isSageMaker'
1616
| 'aws.isWebExtHost'
17+
| 'aws.isInternalUser'
1718
| 'aws.amazonq.showLoginView'
1819
| 'aws.codecatalyst.connected'
1920
| 'aws.codewhisperer.connected'

0 commit comments

Comments
 (0)