Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f59006f
AXON-537 added e2e test for pullrequest creation
mbohoslavskyi Aug 5, 2025
9f87ee6
Merge branch 'main' into AXON-537/e2e-test-bb-create-pullrequest
mbohoslavskyi Aug 6, 2025
8cb8e94
AXON-537 turned on trace on build failure
mbohoslavskyi Aug 6, 2025
edb1ac0
AXON-537 refactored repo adding to remove flakiness
mbohoslavskyi Aug 7, 2025
49c7807
package-lock fix
mbohoslavskyi Aug 7, 2025
46d1283
reworked e2e tests
mbohoslavskyi Aug 7, 2025
2fb7f6f
Merge branch 'main' into AXON-537/e2e-test-bb-create-pullrequest
mbohoslavskyi Aug 7, 2025
328e163
change for test purpose
mbohoslavskyi Aug 7, 2025
c2ff53d
Merge branch 'AXON-537/e2e-test-bb-create-pullrequest' of github.com:…
mbohoslavskyi Aug 7, 2025
afdcf41
reverted change for test purpose
mbohoslavskyi Aug 7, 2025
e40d980
added timeout
mbohoslavskyi Aug 7, 2025
8464b95
added timeout
mbohoslavskyi Aug 7, 2025
f0006aa
added comments
mbohoslavskyi Aug 7, 2025
cf0291b
change for test
mbohoslavskyi Aug 7, 2025
3c7df5a
Merge branch 'main' into AXON-537/e2e-test-bb-create-pullrequest
mbohoslavskyi Aug 7, 2025
2995914
Merge branch 'main' of github.com:atlassian/atlascode into AXON-537/e…
mbohoslavskyi Aug 11, 2025
91f3ed6
trying to change response
mbohoslavskyi Aug 11, 2025
810ea25
AXON-537 added additional check
mbohoslavskyi Aug 11, 2025
24fe246
removed only
mbohoslavskyi Aug 11, 2025
b5d0881
Merge branch 'main' into AXON-537/e2e-test-bb-create-pullrequest
mbohoslavskyi Aug 12, 2025
9bcb730
cleanup
mbohoslavskyi Aug 12, 2025
51e56c5
Merge branch 'AXON-537/e2e-test-bb-create-pullrequest' of github.com:…
mbohoslavskyi Aug 12, 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
68 changes: 55 additions & 13 deletions e2e/helpers/bitbucket-connect-repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Page } from '@playwright/test';
import type { APIRequestContext, Page } from '@playwright/test';
import { setupPullrequests } from 'e2e/helpers/setup-mock';

const goToExtensionTab = async (page: Page) => {
// sometimes page is redirected to Explorer tab and this is workaround so we sure extension tab will be opened
Expand All @@ -9,36 +10,77 @@ const goToExtensionTab = async (page: Page) => {
};

const addRepo = async (page: Page) => {
await page.getByRole('treeitem', { name: 'Add a repository to this workspace' }).click();

const pathInput = page.getByRole('textbox', { name: '' });
await pathInput.waitFor({ state: 'visible' });
const addRepoButton = page.getByRole('treeitem', { name: 'Add a repository to this workspace' });
await addRepoButton.click();
await page.waitForTimeout(250);

// sometimes vs code can go to Explorer tab if we click 'Add a repo...' for the second time, so we make sure to return to extension
const noFolderButton = page.getByRole('button', { name: 'No Folder Opened Section' });
if (await noFolderButton.isVisible()) {
await goToExtensionTab(page);
await addRepoButton.click();
await page.waitForTimeout(250);
}

const pathInput = page.getByRole('textbox', { name: 'Type to narrow down results. - Add Folder to Workspace' });
await pathInput.waitFor({ state: 'visible' });
await page.waitForTimeout(250);

await pathInput.fill('/mock-repository/');
await page.waitForTimeout(250);

await page.getByRole('option', { name: '.git' }).waitFor({ state: 'visible' });
await page.waitForTimeout(250);

await page.getByRole('button', { name: 'Add' }).click();
};

const waitForExplorerLoading = async (page: Page) => {
await page
.locator('.pane:has([aria-label="Bitbucket pull requests Section"])')
.getByRole('progressbar')
.waitFor({ state: 'hidden' });
};

/**
* Helper function to connect Bitbucket repository
*/
export const connectRepository = async (page: Page) => {
export const connectRepository = async (page: Page, request: APIRequestContext) => {
// waiting for loading
await waitForExplorerLoading(page);

// setting request for pullrequests which is used in Bitbucket explorer
const id = await setupPullrequests(request, []);

// trying to add mock-repository
await addRepo(page);
await page.waitForTimeout(1000);

// after first adding repo vs code automatically opens Explorer tab
const mockRepo = page.getByRole('treeitem', { name: 'mock-repository' });
const noFolderButton = page.getByRole('button', { name: 'No Folder Opened Section' });
await mockRepo.or(noFolderButton).waitFor({ state: 'visible' });

// if first try was unsuccessfull we can see 'No folder opened' section name
const isRepoAddFailed = await noFolderButton.isVisible();

// return to extension
await goToExtensionTab(page);
await page.waitForTimeout(1000);

const addRepoButtonCount = await page.getByRole('treeitem', { name: 'Add a repository to this workspace' }).count();
if (addRepoButtonCount !== 0) {
await addRepo(page);
await page.waitForTimeout(1000);
const addRepoButton = page.getByRole('treeitem', { name: 'Add a repository to this workspace' });
const createPRButton = page.getByRole('treeitem', { name: 'Create pull request' });

// waiting for extension load
await addRepoButton.or(createPRButton).waitFor({ state: 'visible' });

// if repo wasn't added trying again
if (isRepoAddFailed) {
await page.waitForTimeout(500); // timeout to get bitbucket explorer time to refresh state
await waitForExplorerLoading(page);

await addRepo(page); // attempt #2

// after attemp #2 vs code doesn't open Explorer tab, so we just wait for bitbucket explorer refresh
await createPRButton.waitFor({ state: 'visible' });
}

return id;
};
26 changes: 26 additions & 0 deletions e2e/helpers/create-pr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { APIRequestContext, Page } from '@playwright/test';
import { setupPullrequests } from 'e2e/helpers/setup-mock';
import { pullrequest } from 'e2e/mock-data/pullrequest';

export const createPullrequest = async (page: Page, request: APIRequestContext) => {
await setupPullrequests(request, [pullrequest]);

const createPRFrame = page.frameLocator('iframe.webview').frameLocator('iframe[title="Create pull request"]');
const sourceBranchInput = createPRFrame.getByRole('combobox').filter({ hasText: 'Source branch' }).locator('input');
await sourceBranchInput.waitFor({ state: 'visible' });
await sourceBranchInput.click();

const testBranchOption = createPRFrame.getByRole('option', { name: 'test-branch' });
await testBranchOption.waitFor({ state: 'visible' });
await testBranchOption.click();
await page.waitForTimeout(250);

await createPRFrame
.locator('div:has-text("Push latest changes from local to remote branch")')
.locator('input[type="checkbox"]')
.first()
.click();

await createPRFrame.getByRole('button', { name: 'Create pull request' }).click();
await page.waitForTimeout(250);
};
1 change: 1 addition & 0 deletions e2e/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { updateIssueField } from './update-jira-issue';
export { authenticateWithBitbucketDC, authenticateWithBitbucketCloud } from './bitbucket-auth';
export { connectRepository } from './bitbucket-connect-repository';
export { createPullrequest } from './create-pr';
export { authenticateWithJira, authenticateWithJiraDC } from './jira-auth';
export { getIssueFrame, openAtlassianSettings } from './common';
export { cleanupWireMockMapping, setupWireMockMapping, setupSearchMock, setupIssueMock } from './setup-mock';
45 changes: 45 additions & 0 deletions e2e/helpers/setup-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@ export const setupWireMockMapping = async (request: APIRequestContext, method: s
return await response.json();
};

/**
* Helper function to set up WireMock mapping for Bitbucket
*/
export const setupWireMockMappingBitbucket = async (
request: APIRequestContext,
method: string,
body: any,
urlPath: string,
queryParameters?: any,
) => {
const response = await request.post('http://wiremock-bitbucket:8080/__admin/mappings', {
data: {
request: {
method,
urlPathPattern: urlPath,
queryParameters,
},
response: {
status: 200,
jsonBody: body,
headers: {
'Content-Type': 'application/json',
},
},
},
});
return await response.json();
};

/**
* Helper function to clean up WireMock mapping
*/
Expand Down Expand Up @@ -63,3 +92,19 @@ export async function setupIssueMock(
);
return () => cleanupWireMockMapping(request, id);
}

export async function setupPullrequests(request: APIRequestContext, values: Array<any>) {
const { id } = await setupWireMockMappingBitbucket(
request,
'GET',
{ values, pagelen: 25, size: 0, page: 1 },
'/2.0/repositories/mockuser/test-repository/pullrequests',
{
pagelen: {
equalTo: '25',
},
},
);

return id;
}
184 changes: 184 additions & 0 deletions e2e/mock-data/pullrequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
export const pullrequest = {
type: 'pullrequest',
id: 123,
title: 'New Feature Implementation',
description: 'This pull request implements a new feature with comprehensive tests and documentation.',
state: 'OPEN',
created_on: '2025-07-03T14:17:40+00:00',
updated_on: '2025-07-03T14:17:40+00:00',
destination: {
branch: {
name: 'main',
},
commit: {
hash: '35c37c0bb16c21ef943fb16890a2209253bfcc83',
type: 'commit',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/commit/35c37c0bb16c21ef943fb16890a2209253bfcc83',
},
},
},
repository: {
type: 'repository',
name: 'test-repository',
full_name: 'mockuser/test-repository',
uuid: '{mock-uuid}',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository',
},
html: {
href: 'https://bitbucket.org/mockuser/test-repository',
},
avatar: {
href: 'https://example.com/avatar.png',
},
},
is_private: true,
owner: {
type: 'user',
display_name: 'Mock User',
uuid: '{mock-uuid}',
username: 'mockuser',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/users/%7Bmock-uuid%7D',
},
html: {
href: 'https://bitbucket.org/%7Bmock-uuid%7D/',
},
avatar: {
href: 'https://example.com/avatar.png',
},
},
},
},
},
source: {
branch: {
name: 'feature/new-feature',
},
commit: {
hash: 'def456abc789012345678901234567890123456',
type: 'commit',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/commit/def456abc789012345678901234567890123456',
},
},
},
repository: {
type: 'repository',
name: 'test-repository',
full_name: 'mockuser/test-repository',
uuid: '{mock-uuid}',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository',
},
html: {
href: 'https://bitbucket.org/mockuser/test-repository',
},
avatar: {
href: 'https://example.com/avatar.png',
},
},
is_private: true,
owner: {
type: 'user',
display_name: 'Mock User',
uuid: '{mock-uuid}',
username: 'mockuser',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/users/%7Bmock-uuid%7D',
},
html: {
href: 'https://bitbucket.org/%7Bmock-uuid%7D/',
},
avatar: {
href: 'https://example.com/avatar.png',
},
},
},
},
},
author: {
type: 'user',
display_name: 'Mock User',
uuid: '{mock-uuid}',
account_id: '123',
username: 'mockuser',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/users/%7Bmock-uuid%7D',
},
html: {
href: 'https://bitbucket.org/%7Bmock-uuid%7D/',
},
avatar: {
href: 'https://example.com/avatar.png',
},
},
},
reviewers: [
{
type: 'user',
display_name: 'Mock User',
uuid: '{mock-uuid}',
account_id: '123',
username: 'mockuser',
links: {
self: {
href: 'https://api.bitbucket.org/2.0/users/%7Bmock-uuid%7D',
},
html: {
href: 'https://bitbucket.org/%7Bmock-uuid%7D/',
},
avatar: {
href: 'https://example.com/avatar.png',
},
},
},
],
participants: [],
close_source_branch: false,
closed_by: null,
reason: '',
merge_commit: null,
comment_count: 0,
task_count: 0,
links: {
self: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123',
},
html: {
href: 'https://bitbucket.org/mockuser/test-repository/pull-requests/123',
},
commits: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123/commits',
},
comments: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123/comments',
},
merge: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123/merge',
},
decline: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123/decline',
},
diffstat: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123/diffstat',
},
diff: {
href: 'https://api.bitbucket.org/2.0/repositories/mockuser/test-repository/pullrequests/123/diff',
},
},
summary: {
type: 'rendered',
raw: 'This pull request implements a new feature with comprehensive tests and documentation.',
markup: 'markdown',
html: '<p>This pull request implements a new feature with comprehensive tests and documentation.</p>',
},
};
Loading
Loading