Skip to content

Commit 78ff15d

Browse files
AXON-1847: New onboarding flow (#1628)
* add main menu quick pick items * start with main menu and independent jira/bitbucket * support main menu mode and back btn for jira/bb * feat: add rovoDevOnboardingInputManager for rovo dev token and site url flow * refactor: remove rovo flow from onboardingQuickInputManager * refactor: remove rovo flow from onboardingQuickInputManager * feat: onboardingProvider * fix: ut * feat: quick input for api token * fix: email step * fix: open extension * fix: handle site url same as auth flow * fix: add email validation * fix: login validation * fix: atlassian site * fix: inline importor * fix: remove redundent main nav * fix: ut mock new intro func
1 parent 769da87 commit 78ff15d

8 files changed

+470
-51
lines changed

src/onboarding/onboardingProvider.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@ jest.mock('../uriHandler/atlascodeUriHandler', () => ({
4747
AtlascodeUriHandler: jest.fn(),
4848
}));
4949

50+
jest.mock('../rovo-dev/rovoDevAuthValidator', () => ({
51+
createValidatedRovoDevAuthInfo: jest.fn(async () => ({
52+
user: { id: 'id', displayName: 'name', email: 'a@b.com', avatarUrl: '' },
53+
state: 'Valid',
54+
username: 'a@b.com',
55+
password: 'token',
56+
host: 'example.atlassian.net',
57+
cloudId: 'cloudId',
58+
})),
59+
}));
60+
61+
jest.mock('../rovo-dev/rovoDevProcessManager', () => ({
62+
RovoDevProcessManager: {
63+
initializeRovoDev: jest.fn(),
64+
},
65+
}));
66+
5067
import { ConfigurationTarget, window } from 'vscode';
5168

5269
import { ProductBitbucket, ProductJira } from '../atlclients/authInfo';
@@ -86,9 +103,12 @@ jest.mock('../config/configuration', () => ({
86103

87104
jest.mock('./utils', () => ({
88105
OnboardingStep: {
106+
MainMenu: 0,
89107
Jira: 1,
90108
Bitbucket: 2,
109+
RovoDev: 3,
91110
},
111+
mainMenuQuickPickItems: jest.fn(() => []),
92112
onboardingQuickPickItems: jest.fn(),
93113
}));
94114

@@ -106,6 +126,16 @@ jest.mock('./onboardingQuickInputManager', () => {
106126
return {
107127
default: jest.fn().mockImplementation(() => ({
108128
start: jest.fn(),
129+
hide: jest.fn(),
130+
})),
131+
};
132+
});
133+
134+
jest.mock('./rovoDevOnboardingInputManager', () => {
135+
return {
136+
default: jest.fn().mockImplementation(() => ({
137+
start: jest.fn(),
138+
hide: jest.fn(),
109139
})),
110140
};
111141
});
@@ -129,16 +159,18 @@ describe('OnboardingProvider', () => {
129159
it('should initialize with correct objects', () => {
130160
expect(provider).toBeDefined();
131161
expect(provider._analyticsClient).toBeDefined();
162+
expect(provider._mainMenuQuickPickManager).toBeDefined();
132163
expect(provider._jiraQuickPickManager).toBeDefined();
133164
expect(provider._bitbucketQuickPickManager).toBeDefined();
134165
expect(provider._quickInputManager).toBeDefined();
166+
expect(provider._rovoDevInputManager).toBeDefined();
135167
});
136168

137-
it('should show Jira onboarding quick pick on start', () => {
169+
it('should show main menu onboarding quick pick on start', () => {
138170
provider.start();
139171

140172
expect(Container.focus).toHaveBeenCalled();
141-
expect(provider._jiraQuickPickManager.show).toHaveBeenCalled();
173+
expect(provider._mainMenuQuickPickManager.show).toHaveBeenCalled();
142174
});
143175

144176
it('should handle Jira quick pick accept for cloud', async () => {

src/onboarding/onboardingProvider.ts

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,31 @@ import { BasicAuthInfo, Product, ProductBitbucket, ProductJira, SiteInfo } from
77
import { configuration } from '../config/configuration';
88
import { BitbucketEnabledKey, Commands, JiraEnabledKey } from '../constants';
99
import { Container } from '../container';
10+
import { createValidatedRovoDevAuthInfo } from '../rovo-dev/rovoDevAuthValidator';
11+
import { RovoDevProcessManager } from '../rovo-dev/rovoDevProcessManager';
1012
import { EXTENSION_URL } from '../uriHandler/atlascodeUriHandler';
1113
import OnboardingQuickInputManager from './onboardingQuickInputManager';
1214
import OnboardingQuickPickManager from './onboardingQuickPickManager';
13-
import { OnboardingInputBoxStep, OnboardingQuickPickItem, onboardingQuickPickItems, OnboardingStep } from './utils';
15+
import RovoDevOnboardingInputManager, { RovoDevOnboardingSubmitArgs } from './rovoDevOnboardingInputManager';
16+
import {
17+
mainMenuQuickPickItems,
18+
OnboardingInputBoxStep,
19+
OnboardingQuickPickItem,
20+
onboardingQuickPickItems,
21+
OnboardingStep,
22+
} from './utils';
1423

1524
class OnboardingProvider {
1625
private id = 'atlascodeOnboardingQuickPick';
1726

1827
private _analyticsClient: AnalyticsClient;
1928

29+
private _mainMenuQuickPickManager: OnboardingQuickPickManager;
2030
private _jiraQuickPickManager: OnboardingQuickPickManager;
2131
private _bitbucketQuickPickManager: OnboardingQuickPickManager;
2232

2333
private _quickInputManager: OnboardingQuickInputManager;
34+
private _rovoDevInputManager: RovoDevOnboardingInputManager;
2435

2536
constructor() {
2637
this._analyticsClient = Container.analyticsClient;
@@ -32,10 +43,28 @@ class OnboardingProvider {
3243
this._handleServerLogin.bind(this),
3344
);
3445

46+
this._rovoDevInputManager = new RovoDevOnboardingInputManager(
47+
() => this._handleBack(OnboardingStep.RovoDev),
48+
(args) => this._onRovoTokenSubmit(args),
49+
);
50+
51+
this._mainMenuQuickPickManager = new OnboardingQuickPickManager(
52+
mainMenuQuickPickItems(),
53+
null,
54+
this._onMainMenuAccept.bind(this),
55+
undefined,
56+
{
57+
title: 'Get started with Atlassian',
58+
showBackButton: false,
59+
step: OnboardingStep.MainMenu,
60+
},
61+
);
62+
3563
this._jiraQuickPickManager = new OnboardingQuickPickManager(
3664
onboardingQuickPickItems(ProductJira),
3765
ProductJira,
3866
this._quickPickOnDidAccept.bind(this),
67+
this._handleBack.bind(this),
3968
);
4069

4170
this._bitbucketQuickPickManager = new OnboardingQuickPickManager(
@@ -46,10 +75,53 @@ class OnboardingProvider {
4675
);
4776
}
4877

49-
// --- Handle Quick Pick Accept ---
50-
private async _quickPickOnDidAccept(item: OnboardingQuickPickItem, product: Product) {
78+
private async _onRovoTokenSubmit(args: RovoDevOnboardingSubmitArgs): Promise<void> {
79+
try {
80+
const host = args.siteUrl.replace(/^https?:\/\//, '').replace(/\/$/, '');
81+
const authInfo = await createValidatedRovoDevAuthInfo(host, args.email, args.token);
82+
await Container.credentialManager.saveRovoDevAuthInfo(authInfo);
83+
await configuration.updateEffective('rovodev.enabled', true, null, true);
84+
await RovoDevProcessManager.initializeRovoDev(Container.context, true);
85+
this.hideQuickPick(OnboardingStep.RovoDev);
86+
await commands.executeCommand('atlascode.views.rovoDev.webView.focus');
87+
} catch (error) {
88+
Logger.error(error, 'Rovo Dev onboarding: failed to save credentials');
89+
throw error;
90+
}
91+
}
92+
93+
// --- Handle Main Menu Accept ---
94+
private _onMainMenuAccept(item: OnboardingQuickPickItem) {
5195
const onboardingId = item.onboardingId;
96+
if (!onboardingId) {
97+
return;
98+
}
99+
switch (onboardingId) {
100+
case 'onboarding:rovo':
101+
this.show(OnboardingStep.RovoDev);
102+
break;
103+
case 'onboarding:jira':
104+
this.show(OnboardingStep.Jira);
105+
break;
106+
case 'onboarding:bitbucket':
107+
this.show(OnboardingStep.Bitbucket);
108+
break;
109+
default:
110+
break;
111+
}
112+
}
113+
114+
private _showRovoDevTokenSetup() {
115+
this._mainMenuQuickPickManager.hide();
116+
this._rovoDevInputManager.start();
117+
}
52118

119+
// --- Handle Quick Pick Accept ---
120+
private async _quickPickOnDidAccept(item: OnboardingQuickPickItem, product: Product | null) {
121+
if (!product) {
122+
return;
123+
}
124+
const onboardingId = item.onboardingId;
53125
if (!onboardingId) {
54126
return;
55127
}
@@ -75,52 +147,48 @@ class OnboardingProvider {
75147
// --- Handle Next Step ---
76148
private _handleNext(step: OnboardingStep) {
77149
if (step === OnboardingStep.Jira) {
78-
// Refresh Jira explorers
79150
commands.executeCommand(Commands.RefreshAssignedWorkItemsExplorer);
80-
81151
commands.executeCommand(Commands.RefreshCustomJqlExplorer);
82152
} else if (step === OnboardingStep.Bitbucket) {
83-
// Refresh Bitbucket explorers
84153
commands.executeCommand(Commands.BitbucketRefreshPullRequests);
85-
86154
commands.executeCommand(Commands.RefreshPipelines);
87-
this.hideQuickPick(step);
88-
return;
89155
} else {
90156
return;
91157
}
92158
Container.focus();
93159
this.hideQuickPick(step);
94-
95-
this.show(step + 1);
160+
this.show(OnboardingStep.MainMenu);
96161
}
97162

98163
// --- Start Onboarding ---
99164
start() {
100165
this._fireViewScreenEvent();
101166
Container.focus();
102-
103-
this.show(OnboardingStep.Jira);
167+
this.show(OnboardingStep.MainMenu);
104168
}
105169

106170
// --- Show QuickPick ---
107171
show(step: OnboardingStep) {
108-
if (step === OnboardingStep.Jira) {
109-
// Show Jira items
172+
if (step === OnboardingStep.MainMenu) {
173+
this._mainMenuQuickPickManager.show();
174+
} else if (step === OnboardingStep.Jira) {
110175
this._jiraQuickPickManager.show();
111176
} else if (step === OnboardingStep.Bitbucket) {
112-
// Show Bitbucket items
113177
this._bitbucketQuickPickManager.show();
114-
} else {
115-
return;
178+
} else if (step === OnboardingStep.RovoDev) {
179+
this._showRovoDevTokenSetup();
116180
}
117181
}
118182

119183
hideQuickPick(step: OnboardingStep) {
120-
if (step === OnboardingStep.Jira) {
184+
if (step === OnboardingStep.MainMenu) {
185+
this._mainMenuQuickPickManager.hide();
186+
} else if (step === OnboardingStep.Jira) {
121187
this._jiraQuickPickManager.hide();
122188
} else if (step === OnboardingStep.Bitbucket) {
123189
this._bitbucketQuickPickManager.hide();
190+
} else if (step === OnboardingStep.RovoDev) {
191+
this._rovoDevInputManager.hide();
124192
}
125193
}
126194

@@ -167,7 +235,7 @@ class OnboardingProvider {
167235

168236
private _handleBack(step: OnboardingStep) {
169237
this.hideQuickPick(step);
170-
this.show(step - 1);
238+
this.show(OnboardingStep.MainMenu);
171239
}
172240

173241
private _handleCloud(product: Product) {

src/onboarding/onboardingQuickInputManager.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ jest.mock('vscode', () => {
1111
window: {
1212
createInputBox: jest.fn(),
1313
},
14+
commands: {
15+
executeCommand: jest.fn(),
16+
},
1417
env: {
1518
openExternal: jest.fn(),
1619
},
@@ -35,6 +38,7 @@ const mockInputBox = (i: number) => ({
3538
hide: jest.fn(),
3639
onDidTriggerButton: jest.fn(),
3740
onDidAccept: jest.fn(),
41+
onDidHide: jest.fn(),
3842
});
3943

4044
describe('OnboardingQuickInputManager', () => {

src/onboarding/onboardingQuickInputManager.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ class OnboardingQuickInputManager {
4343
input.step = i + 1;
4444
input.ignoreFocusOut = true;
4545
input.buttons = [QuickInputButtons.Back, OnboardingButtons.dismiss];
46-
input.onDidTriggerButton((e) => this._quickInputOnDidTriggerButton(e, i));
47-
input.onDidAccept(() => this._onDidInputAccept(i));
46+
input.onDidTriggerButton((e) => this._quickInputOnDidTriggerButton(e, i as OnboardingInputBoxStep));
47+
input.onDidAccept(() => this._onDidInputAccept(i as OnboardingInputBoxStep));
4848
return input;
4949
});
5050
}
5151

52+
hide() {
53+
this._quickInput.forEach((input) => input.hide());
54+
}
55+
5256
start(product: Product, env: string) {
5357
if (!this._quickInput || this._quickInput.length === 0) {
5458
return;

src/onboarding/onboardingQuickPickManager.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,12 @@ describe('OnboardingQuickPickManager', () => {
7676
expect(quickPickMock.totalSteps).toBe(2);
7777
expect(quickPickMock.activeItems).toEqual([items[0]]);
7878
expect(quickPickMock.step).toBe(OnboardingStep.Jira);
79-
expect(quickPickMock.buttons).toEqual([OnboardingButtons.settings, OnboardingButtons.dismiss]);
80-
expect(quickPickMock.placeholder).toBe('Type to search. Select settings for advanced options.');
79+
expect(quickPickMock.buttons).toEqual([
80+
QuickInputButtons.Back,
81+
OnboardingButtons.settings,
82+
OnboardingButtons.dismiss,
83+
]);
84+
expect(quickPickMock.placeholder).toBe('What would you like to do first');
8185
expect(quickPickMock.show).toHaveBeenCalled();
8286
});
8387

@@ -91,7 +95,7 @@ describe('OnboardingQuickPickManager', () => {
9195
OnboardingButtons.settings,
9296
OnboardingButtons.dismiss,
9397
]);
94-
expect(quickPickMock.placeholder).toBe('Type to search. Select settings for advanced options.');
98+
expect(quickPickMock.placeholder).toBe('What would you like to do first');
9599
expect(quickPickMock.show).toHaveBeenCalled();
96100
});
97101

0 commit comments

Comments
 (0)