Skip to content

Commit 56b0654

Browse files
committed
Merge branch 'main' of https://github.com/atlassian/atlascode into we-dont-need-chai
2 parents 47b7f8b + e296026 commit 56b0654

File tree

15 files changed

+194
-37
lines changed

15 files changed

+194
-37
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## What's new in 3.4.8
2+
3+
### Features
4+
5+
- Added a new toggle switch in 'Start work' page to choose if the new branch should be automatically pushed to remote.
6+
17
## What's new in 3.4.7
28

39
### Features

src/analytics.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { viewScreenEvent } from './analytics';
2+
3+
interface MockedData {
4+
getFirstAAID_value?: boolean;
5+
}
6+
7+
const mockedData: MockedData = {};
8+
9+
jest.mock('./container', () => ({
10+
Container: { siteManager: { getFirstAAID: () => mockedData.getFirstAAID_value } },
11+
}));
12+
13+
function setProcessPlatform(platform: NodeJS.Platform) {
14+
Object.defineProperty(process, 'platform', {
15+
value: platform,
16+
writable: false,
17+
});
18+
}
19+
20+
describe('viewScreenEvent', () => {
21+
const originalPlatform = process.platform;
22+
23+
beforeEach(() => {
24+
setProcessPlatform('win32');
25+
mockedData.getFirstAAID_value = true;
26+
});
27+
28+
afterAll(() => {
29+
setProcessPlatform(originalPlatform);
30+
});
31+
32+
it('should create a screen event with the correct screen name', async () => {
33+
const screenName = 'testScreen';
34+
const event = await viewScreenEvent(screenName);
35+
expect(event.name).toEqual(screenName);
36+
expect(event.screenEvent.attributes).toBeUndefined();
37+
});
38+
39+
it('should exclude from activity if screen name is atlascodeWelcomeScreen', async () => {
40+
const screenName = 'atlascodeWelcomeScreen';
41+
const event = await viewScreenEvent(screenName);
42+
expect(event.screenEvent.attributes.excludeFromActivity).toBeTruthy();
43+
});
44+
45+
it('should include site information if provided (cloud)', async () => {
46+
const screenName = 'testScreen';
47+
const site: any = {
48+
id: 'siteId',
49+
product: { name: 'Jira', key: 'jira' },
50+
isCloud: true,
51+
};
52+
const event = await viewScreenEvent(screenName, site);
53+
expect(event.screenEvent.attributes.instanceType).toEqual('cloud');
54+
expect(event.screenEvent.attributes.hostProduct).toEqual('Jira');
55+
});
56+
57+
it('should include site information if provided (server)', async () => {
58+
const screenName = 'testScreen';
59+
const site: any = {
60+
id: 'siteId',
61+
product: { name: 'Jira', key: 'jira' },
62+
isCloud: false,
63+
};
64+
const event = await viewScreenEvent(screenName, site);
65+
expect(event.screenEvent.attributes.instanceType).toEqual('server');
66+
expect(event.screenEvent.attributes.hostProduct).toEqual('Jira');
67+
});
68+
69+
it('should include product information if provided', async () => {
70+
const screenName = 'testScreen';
71+
const product = { name: 'Bitbucket', key: 'bitbucket' };
72+
const event = await viewScreenEvent(screenName, undefined, product);
73+
expect(event.screenEvent.attributes.hostProduct).toEqual('Bitbucket');
74+
});
75+
76+
it('should set platform based on process.platform (win32)', async () => {
77+
setProcessPlatform('win32');
78+
const screenName = 'testScreen';
79+
const event = await viewScreenEvent(screenName);
80+
expect(event.screenEvent.platform).toEqual('windows');
81+
});
82+
83+
it('should set platform based on process.platform (darwin)', async () => {
84+
setProcessPlatform('darwin');
85+
const screenName = 'testScreen';
86+
const event = await viewScreenEvent(screenName);
87+
expect(event.screenEvent.platform).toEqual('mac');
88+
});
89+
90+
it('should set platform based on process.platform (linux)', async () => {
91+
setProcessPlatform('linux');
92+
const screenName = 'testScreen';
93+
const event = await viewScreenEvent(screenName);
94+
expect(event.screenEvent.platform).toEqual('linux');
95+
});
96+
97+
it('should set platform based on process.platform (aix)', async () => {
98+
setProcessPlatform('aix');
99+
const screenName = 'testScreen';
100+
const event = await viewScreenEvent(screenName);
101+
expect(event.screenEvent.platform).toEqual('desktop');
102+
});
103+
});

src/analytics.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const Registry = {
2121
};
2222

2323
class AnalyticsPlatform {
24-
private static nodeJsPlatformMapping = {
24+
private static nodeJsPlatformMapping: Record<NodeJS.Platform, string> = {
2525
aix: 'desktop',
2626
android: 'android',
2727
darwin: 'mac',
@@ -31,9 +31,11 @@ class AnalyticsPlatform {
3131
sunos: 'desktop',
3232
win32: 'windows',
3333
cygwin: 'windows',
34+
haiku: 'unknown',
35+
netbsd: 'unknown',
3436
};
3537

36-
static for(p: string): string {
38+
static for(p: NodeJS.Platform): string {
3739
return this.nodeJsPlatformMapping[p] || 'unknown';
3840
}
3941
}
@@ -107,8 +109,13 @@ export async function issueCommentEvent(site: DetailedSiteInfo): Promise<TrackEv
107109
return instanceTrackEvent(site, 'created', 'issueComment');
108110
}
109111

110-
export async function issueWorkStartedEvent(site: DetailedSiteInfo): Promise<TrackEvent> {
111-
return instanceTrackEvent(site, 'workStarted', 'issue');
112+
export async function issueWorkStartedEvent(
113+
site: DetailedSiteInfo,
114+
pushBranchToRemoteChecked: boolean,
115+
): Promise<TrackEvent> {
116+
const attributesObject = instanceType({}, site);
117+
attributesObject.attributes.pushBranchToRemoteChecked = pushBranchToRemoteChecked;
118+
return instanceTrackEvent(site, 'workStarted', 'issue', attributesObject);
112119
}
113120

114121
export async function issueUpdatedEvent(
@@ -164,7 +171,7 @@ export async function prCommentEvent(site: DetailedSiteInfo): Promise<TrackEvent
164171
}
165172

166173
export async function prTaskEvent(site: DetailedSiteInfo, source: string): Promise<TrackEvent> {
167-
const attributesObject: any = instanceType({}, site);
174+
const attributesObject = instanceType({}, site);
168175
attributesObject.attributes.source = source;
169176
return trackEvent('created', 'pullRequestComment', attributesObject);
170177
}
@@ -657,33 +664,27 @@ function tenantOrNull<T>(e: Object, tenantId?: string): T {
657664
return newObj as T;
658665
}
659666

660-
function instanceType(eventProps: Object, site?: DetailedSiteInfo, product?: Product): Object {
661-
let attrs: Object | undefined = undefined;
662-
const newObj = eventProps;
663-
667+
function instanceType(
668+
eventProps: Record<string, any>,
669+
site?: DetailedSiteInfo,
670+
product?: Product,
671+
): Record<string, any> {
664672
if (product) {
665-
attrs = { hostProduct: product.name };
673+
eventProps.attributes = eventProps.attributes || {};
674+
eventProps.attributes.hostProduct = product.name;
666675
}
667676

668677
if (site && !isEmptySiteInfo(site)) {
669-
const instanceType: string = site.isCloud ? 'cloud' : 'server';
670-
attrs = { instanceType: instanceType, hostProduct: site.product.name };
678+
eventProps.attributes = eventProps.attributes || {};
679+
eventProps.attributes.instanceType = site.isCloud ? 'cloud' : 'server';
680+
eventProps.attributes.hostProduct = site.product.name;
671681
}
672682

673-
if (attrs) {
674-
newObj['attributes'] = { ...newObj['attributes'], ...attrs };
675-
}
676-
677-
return newObj;
683+
return eventProps;
678684
}
679685

680-
function excludeFromActivity(eventProps: Object): Object {
681-
const newObj = eventProps;
682-
683-
if (newObj['attributes']) {
684-
newObj['attributes'] = { ...newObj['attributes'], ...{ excludeFromActivity: true } };
685-
} else {
686-
Object.assign(newObj, { attributes: { excludeFromActivity: true } });
687-
}
688-
return newObj;
686+
function excludeFromActivity(eventProps: Record<string, any>): Record<string, any> {
687+
eventProps.attributes = eventProps.attributes || {};
688+
eventProps.attributes.excludeFromActivity = true;
689+
return eventProps;
689690
}

src/ipc/issueActions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export interface StartWorkAction extends Action {
120120
remoteName: string;
121121
setupJira: boolean;
122122
setupBitbucket: boolean;
123+
pushBranchToRemote: boolean;
123124
}
124125

125126
export interface OpenStartWorkPageAction extends Action {

src/lib/analyticsApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface AnalyticsApi {
1212
fireIssueTransitionedEvent(site: DetailedSiteInfo, issueKey: string): Promise<void>;
1313
fireIssueUrlCopiedEvent(): Promise<void>;
1414
fireIssueCommentEvent(site: DetailedSiteInfo): Promise<void>;
15-
fireIssueWorkStartedEvent(site: DetailedSiteInfo): Promise<void>;
15+
fireIssueWorkStartedEvent(site: DetailedSiteInfo, pushBranchToRemoteChecked: boolean): Promise<void>;
1616
fireIssueUpdatedEvent(site: DetailedSiteInfo, issueKey: string, fieldName: string, fieldKey: string): Promise<void>;
1717
fireStartIssueCreationEvent(source: string, product: Product): Promise<void>;
1818
fireBBIssueCreatedEvent(site: DetailedSiteInfo): Promise<void>;

src/lib/ipc/fromUI/startWork.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface StartRequestAction {
2727
sourceBranch: Branch;
2828
targetBranch: string;
2929
upstream: string;
30+
pushBranchToRemote: boolean;
3031
}
3132

3233
export interface OpenSettingsAction {

src/lib/webview/controller/startwork/startWorkActionApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface StartWorkActionApi {
1717
destinationBranch: string,
1818
sourceBranch: Branch,
1919
remote: string,
20+
pushBranchToRemote: boolean,
2021
): Promise<void>;
2122
closePage(): void;
2223
getStartWorkConfig(): StartWorkBranchTemplate;

src/lib/webview/controller/startwork/startWorkWebviewController.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export class StartWorkWebviewController implements WebviewController<StartWorkIs
134134
msg.targetBranch,
135135
msg.sourceBranch,
136136
msg.upstream,
137+
msg.pushBranchToRemote,
137138
);
138139
}
139140
this.postMessage({
@@ -142,7 +143,7 @@ export class StartWorkWebviewController implements WebviewController<StartWorkIs
142143
branch: msg.branchSetupEnabled ? msg.targetBranch : undefined,
143144
upstream: msg.branchSetupEnabled ? msg.upstream : undefined,
144145
});
145-
this.analytics.fireIssueWorkStartedEvent(this.initData.issue.siteDetails);
146+
this.analytics.fireIssueWorkStartedEvent(this.initData.issue.siteDetails, msg.pushBranchToRemote);
146147
} catch (e) {
147148
this.logger.error(new Error(`error executing start work action: ${e}`));
148149
this.postMessage({

src/react/atlascode/startwork/StartWorkPage.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const StartWorkPage: React.FunctionComponent = () => {
9595

9696
const [transitionIssueEnabled, setTransitionIssueEnabled] = useState(true);
9797
const [branchSetupEnabled, setbranchSetupEnabled] = useState(true);
98+
const [pushBranchEnabled, setPushBranchEnabled] = useState(true);
9899
const [transition, setTransition] = useState<Transition>(emptyTransition);
99100
const [repository, setRepository] = useState<RepoData>(emptyRepoData);
100101
const [branchType, setBranchType] = useState<BranchType>(emptyPrefix);
@@ -120,6 +121,8 @@ const StartWorkPage: React.FunctionComponent = () => {
120121
[branchSetupEnabled],
121122
);
122123

124+
const togglePushBranchEnabled = useCallback(() => setPushBranchEnabled(!pushBranchEnabled), [pushBranchEnabled]);
125+
123126
const handleTransitionChange = useCallback(
124127
(event: React.ChangeEvent<{ name?: string | undefined; value: any }>) => {
125128
setTransition(event.target.value);
@@ -248,6 +251,7 @@ const StartWorkPage: React.FunctionComponent = () => {
248251
sourceBranch,
249252
localBranch,
250253
upstream,
254+
pushBranchEnabled,
251255
);
252256
setSubmitState('submit-success');
253257
setSubmitResponse(response);
@@ -264,6 +268,7 @@ const StartWorkPage: React.FunctionComponent = () => {
264268
sourceBranch,
265269
localBranch,
266270
upstream,
271+
pushBranchEnabled,
267272
]);
268273

269274
const handleOpenSettings = useCallback(() => {
@@ -704,6 +709,28 @@ const StartWorkPage: React.FunctionComponent = () => {
704709
</Grid>
705710
</Collapse>
706711
</Grid>
712+
<Grid item hidden={submitState === 'submit-success'}>
713+
<Divider />
714+
</Grid>
715+
<Grid item hidden={submitState === 'submit-success'}>
716+
<Grid container spacing={1} direction="row">
717+
<Grid item>
718+
<Switch
719+
color="primary"
720+
size="small"
721+
checked={pushBranchEnabled}
722+
onClick={togglePushBranchEnabled}
723+
/>
724+
</Grid>
725+
<Grid item>
726+
<Typography variant="h4">
727+
<Box fontWeight="fontWeightBold">
728+
Push the new branch to remote
729+
</Box>
730+
</Typography>
731+
</Grid>
732+
</Grid>
733+
</Grid>
707734
<Grid item hidden={submitState === 'submit-success'}>
708735
<Button
709736
variant="contained"

src/react/atlascode/startwork/startWorkController.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface StartWorkControllerApi {
3030
sourceBranch: Branch,
3131
targetBranch: string,
3232
upstream: string,
33+
pushBranchToRemote: boolean,
3334
) => Promise<{ transistionStatus?: string; branch?: string; upstream?: string }>;
3435
closePage: () => void;
3536
openJiraIssue: () => void;
@@ -122,6 +123,7 @@ export function useStartWorkController(): [StartWorkState, StartWorkControllerAp
122123
sourceBranch: Branch,
123124
targetBranch: string,
124125
upstream: string,
126+
pushBranchToRemote: boolean,
125127
): Promise<StartWorkResponseMessage> => {
126128
return new Promise<StartWorkResponseMessage>((resolve, reject) => {
127129
(async () => {
@@ -136,6 +138,7 @@ export function useStartWorkController(): [StartWorkState, StartWorkControllerAp
136138
sourceBranch,
137139
targetBranch,
138140
upstream,
141+
pushBranchToRemote,
139142
},
140143
StartWorkMessageType.StartWorkResponse,
141144
ConnectionTimeout,

src/vscAnalyticsApi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ export class VSCAnalyticsApi implements AnalyticsApi {
124124
});
125125
}
126126

127-
public async fireIssueWorkStartedEvent(site: DetailedSiteInfo): Promise<void> {
128-
return issueWorkStartedEvent(site).then((e) => {
127+
public async fireIssueWorkStartedEvent(site: DetailedSiteInfo, pushBranchToRemoteChecked: boolean): Promise<void> {
128+
return issueWorkStartedEvent(site, pushBranchToRemoteChecked).then((e) => {
129129
this._analyticsClient.sendTrackEvent(e);
130130
});
131131
}

src/webview/startwork/vscStartWorkActionApi.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export class VSCStartWorkActionApi implements StartWorkActionApi {
5050
destinationBranch: string,
5151
sourceBranch: Branch,
5252
remote: string,
53+
pushBranchToRemote: boolean,
5354
): Promise<void> {
5455
const scm = Container.bitbucketContext.getRepositoryScm(wsRepo.rootUri)!;
5556

@@ -74,8 +75,10 @@ export class VSCStartWorkActionApi implements StartWorkActionApi {
7475
true,
7576
`${sourceBranch.type === RefType.RemoteHead ? 'remotes/' : ''}${sourceBranch.name}`,
7677
);
77-
await scm.push(remote, destinationBranch, true);
78-
return;
78+
79+
if (pushBranchToRemote) {
80+
await scm.push(remote, destinationBranch, true);
81+
}
7982
}
8083

8184
getStartWorkConfig(): StartWorkBranchTemplate {

src/webviews/components/issue/StartWorkPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ export default class StartWorkPage extends WebviewComponent<Emit, Accept, {}, St
261261
transition: this.state.transition,
262262
setupJira: this.state.jiraSetupEnabled,
263263
setupBitbucket: this.isEmptyRepo(this.state.repo) ? false : this.state.bitbucketSetupEnabled,
264+
pushBranchToRemote: false,
264265
});
265266
};
266267

0 commit comments

Comments
 (0)