Skip to content

Commit 9f9ff90

Browse files
authored
test(amazonq): welcome page and explore agents page tests (#6232)
## Problem We have no tests for the welcome page and explore agents page ## Solution Add them
1 parent c449b0d commit 9f9ff90

File tree

6 files changed

+227
-10
lines changed

6 files changed

+227
-10
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import { Messenger } from './framework/messenger'
8+
9+
export function assertQuickActions(tab: Messenger, commands: string[]) {
10+
const commandGroup = tab
11+
.getCommands()
12+
.map((groups) => groups.commands)
13+
.flat()
14+
if (!commandGroup) {
15+
assert.fail(`Could not find commands for ${tab.tabID}`)
16+
}
17+
18+
const commandNames = commandGroup.map((cmd) => cmd.command)
19+
20+
const missingCommands = []
21+
for (const command of commands) {
22+
if (!commandNames.includes(command)) {
23+
missingCommands.push(command)
24+
}
25+
}
26+
27+
if (missingCommands.length > 0) {
28+
assert.fail(`Could not find commands: ${missingCommands.join(', ')} for ${tab.tabID}`)
29+
}
30+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import sinon from 'sinon'
8+
import { qTestingFramework } from './framework/framework'
9+
import { Messenger } from './framework/messenger'
10+
11+
describe('Amazon Q Explore page', function () {
12+
let framework: qTestingFramework
13+
let tab: Messenger
14+
15+
beforeEach(() => {
16+
framework = new qTestingFramework('agentWalkthrough', true, [], 0)
17+
const welcomeTab = framework.getTabs()[0]
18+
welcomeTab.clickInBodyButton('explore')
19+
20+
// Find the new explore tab
21+
const exploreTab = framework.findTab('Explore')
22+
if (!exploreTab) {
23+
assert.fail('Explore tab not found')
24+
}
25+
tab = exploreTab
26+
})
27+
28+
afterEach(() => {
29+
framework.removeTab(tab.tabID)
30+
framework.dispose()
31+
sinon.restore()
32+
})
33+
34+
// TODO refactor page objects so we can associate clicking user guides with actual urls
35+
// TODO test that clicking quick start changes the tab title, etc
36+
it('should have correct button IDs', async () => {
37+
const features = ['featuredev', 'testgen', 'doc', 'review', 'gumby']
38+
39+
features.forEach((feature, index) => {
40+
const buttons = (tab.getStore().chatItems ?? [])[index].buttons ?? []
41+
assert.deepStrictEqual(buttons[0].id, `user-guide-${feature}`)
42+
assert.deepStrictEqual(buttons[1].id, `quick-start-${feature}`)
43+
})
44+
})
45+
})

packages/amazonq/test/e2e/amazonq/framework/framework.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { injectJSDOM } from './jsdomInjector'
88
// This needs to be ran before all other imports so that mynah ui gets loaded inside of jsdom
99
injectJSDOM()
1010

11+
import assert from 'assert'
1112
import * as vscode from 'vscode'
1213
import { MynahUI, MynahUIProps } from '@aws/mynah-ui'
1314
import { DefaultAmazonQAppInitContext, TabType, createMynahUI } from 'aws-core-vscode/amazonq'
@@ -24,7 +25,12 @@ export class qTestingFramework {
2425

2526
lastEventId: string = ''
2627

27-
constructor(featureName: TabType, amazonQEnabled: boolean, featureConfigsSerialized: [string, FeatureContext][]) {
28+
constructor(
29+
featureName: TabType,
30+
amazonQEnabled: boolean,
31+
featureConfigsSerialized: [string, FeatureContext][],
32+
welcomeCount = 0
33+
) {
2834
/**
2935
* Instantiate the UI and override the postMessage to publish using the app message
3036
* publishers directly.
@@ -44,7 +50,8 @@ export class qTestingFramework {
4450
},
4551
},
4652
amazonQEnabled,
47-
featureConfigsSerialized
53+
featureConfigsSerialized,
54+
welcomeCount
4855
)
4956
this.mynahUI = ui.mynahUI
5057
this.mynahUIProps = (this.mynahUI as any).props
@@ -79,18 +86,35 @@ export class qTestingFramework {
7986
* functionality against a specific tab
8087
*/
8188
public createTab(options?: MessengerOptions) {
82-
const newTabID = this.mynahUI.updateStore('', {})
89+
const oldTabs = Object.keys(this.mynahUI.getAllTabs())
90+
91+
// simulate pressing the new tab button
92+
;(document.querySelectorAll('.mynah-nav-tabs-wrapper > button.mynah-button')[0] as HTMLButtonElement).click()
93+
const newTabs = Object.keys(this.mynahUI.getAllTabs())
94+
95+
const newTabID = newTabs.find((tab) => !oldTabs.includes(tab))
8396
if (!newTabID) {
84-
throw new Error('Could not create tab id')
97+
assert.fail('Could not find new tab')
8598
}
99+
86100
return new Messenger(newTabID, this.mynahUIProps, this.mynahUI, options)
87101
}
88102

103+
public getTabs() {
104+
const tabs = this.mynahUI.getAllTabs()
105+
return Object.entries(tabs).map(([tabId]) => new Messenger(tabId, this.mynahUIProps, this.mynahUI))
106+
}
107+
108+
public findTab(title: string) {
109+
return Object.values(this.getTabs()).find((tab) => tab.getStore().tabTitle === title)
110+
}
111+
89112
public removeTab(tabId: string) {
90113
this.mynahUI.removeTab(tabId, this.lastEventId)
91114
}
92115

93116
public dispose() {
94117
vscode.Disposable.from(...this.disposables).dispose()
118+
this.mynahUI.destroy()
95119
}
96120
}

packages/amazonq/test/e2e/amazonq/framework/messenger.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,28 @@ export class Messenger {
5151
}
5252

5353
const lastChatItem = this.getChatItems().pop()
54-
const option = lastChatItem?.followUp?.options?.filter((option) => option.type === type)
55-
if (!option?.length || option.length > 1) {
56-
assert.fail('Could not find follow up option')
54+
const followupOption = lastChatItem?.followUp?.options?.filter((option) => option.type === type)
55+
if (followupOption && followupOption.length > 0) {
56+
this.mynahUIProps.onFollowUpClicked(this.tabID, lastChatItem?.messageId ?? '', followupOption[0])
57+
return
5758
}
5859

59-
this.mynahUIProps.onFollowUpClicked(this.tabID, lastChatItem?.messageId ?? '', option[0])
60+
assert.fail(`Could not find a button with id ${type} on tabID: ${this.tabID}`)
61+
}
62+
63+
clickInBodyButton(type: string) {
64+
if (!this.mynahUIProps.onInBodyButtonClicked) {
65+
assert.fail('onInBodyButtonClicked must be defined to use it in the tests')
66+
}
67+
68+
const lastChatItem = this.getChatItems().pop()
69+
const followupButton = lastChatItem?.buttons?.filter((option) => option.id === type)
70+
if (followupButton && followupButton.length > 0) {
71+
this.mynahUIProps.onInBodyButtonClicked(this.tabID, lastChatItem?.messageId ?? '', followupButton[0])
72+
return
73+
}
74+
75+
assert.fail(`Could not find a button with id ${type} on tabID: ${this.tabID}`)
6076
}
6177

6278
clickCustomFormButton(action: { id: string; text?: string; formItemValues?: Record<string, string> }) {
@@ -193,7 +209,7 @@ export class Messenger {
193209
}
194210
}
195211

196-
private getStore(): MynahUIDataModel {
212+
getStore(): MynahUIDataModel {
197213
const store = this.mynahUI.getAllTabs()[this.tabID].store
198214
if (!store) {
199215
assert.fail(`${this.tabID} does not have a store`)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import { qTestingFramework } from './framework/framework'
8+
import sinon from 'sinon'
9+
import { Messenger } from './framework/messenger'
10+
import { MynahUIDataModel } from '@aws/mynah-ui'
11+
import { assertQuickActions } from './assert'
12+
13+
describe('Amazon Q Welcome page', function () {
14+
let framework: qTestingFramework
15+
let tab: Messenger
16+
let store: MynahUIDataModel
17+
18+
const availableCommands = ['/dev', '/test', '/review', '/doc', '/transform']
19+
20+
beforeEach(() => {
21+
framework = new qTestingFramework('welcome', true, [], 0)
22+
tab = framework.getTabs()[0] // use the default tab that gets created
23+
store = tab.getStore()
24+
})
25+
26+
afterEach(() => {
27+
framework.removeTab(tab.tabID)
28+
framework.dispose()
29+
sinon.restore()
30+
})
31+
32+
it(`Shows quick actions: ${availableCommands.join(', ')}`, async () => {
33+
assertQuickActions(tab, availableCommands)
34+
})
35+
36+
it('Shows @workspace', async () => {
37+
assert.deepStrictEqual(
38+
store.contextCommands
39+
?.map((x) => x.commands)
40+
.flat()
41+
.map((x) => x.command),
42+
['@workspace']
43+
)
44+
})
45+
46+
describe('shows 3 times', async () => {
47+
it('new tabs', () => {
48+
framework.createTab()
49+
framework.createTab()
50+
framework.createTab()
51+
framework.createTab()
52+
53+
let welcomeCount = 0
54+
framework.getTabs().forEach((tab) => {
55+
if (tab.getStore().tabTitle === 'Welcome to Q') {
56+
welcomeCount++
57+
}
58+
})
59+
// 3 welcome tabs
60+
assert.deepStrictEqual(welcomeCount, 3)
61+
62+
// 2 normal tabs
63+
assert.deepStrictEqual(framework.getTabs().length - welcomeCount, 2)
64+
})
65+
66+
it('new windows', () => {
67+
// check the initial window
68+
assert.deepStrictEqual(store.tabTitle, 'Welcome to Q')
69+
framework.dispose()
70+
71+
// check when theres already been two welcome tabs shown
72+
framework = new qTestingFramework('welcome', true, [], 2)
73+
const secondStore = framework.getTabs()[0].getStore()
74+
assert.deepStrictEqual(secondStore.tabTitle, 'Welcome to Q')
75+
framework.dispose()
76+
77+
// check when theres already been three welcome tabs shown
78+
framework = new qTestingFramework('welcome', true, [], 3)
79+
const thirdStore = framework.getTabs()[0].getStore()
80+
assert.deepStrictEqual(thirdStore.tabTitle, 'Chat')
81+
framework.dispose()
82+
})
83+
})
84+
85+
describe('Welcome actions', () => {
86+
it('explore', () => {
87+
tab.clickInBodyButton('explore')
88+
89+
// explore opens in a new tab
90+
const exploreTabStore = framework.findTab('Explore')?.getStore()
91+
assert.strictEqual(exploreTabStore?.tabTitle, 'Explore')
92+
})
93+
94+
it('quick-start', async () => {
95+
tab.clickInBodyButton('quick-start')
96+
97+
// clicking quick start opens in the current tab and changes the compact mode
98+
assert.deepStrictEqual(tab.getStore().compactMode, false)
99+
})
100+
})
101+
})

packages/core/src/amazonq/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ export function createMynahUI(
5555
ideApi: any,
5656
amazonQEnabled: boolean,
5757
featureConfigsSerialized: [string, FeatureContext][],
58+
welcomeCount: number,
5859
disabledCommands?: string[]
5960
) {
6061
if (typeof window !== 'undefined') {
6162
const mynahUI = require('./webview/ui/main')
62-
return mynahUI.createMynahUI(ideApi, amazonQEnabled, featureConfigsSerialized, true, disabledCommands)
63+
return mynahUI.createMynahUI(ideApi, amazonQEnabled, featureConfigsSerialized, welcomeCount, disabledCommands)
6364
}
6465
throw new Error('Not implemented for node')
6566
}

0 commit comments

Comments
 (0)