PromucFlow_constructor/app/client/cypress/support/Pages/GitSync.ts
subratadeypappu 3981590006
chore: Add changes for new API contracts in GitSync.ts (#41101)
## Description
EE Counterpart: https://github.com/appsmithorg/appsmith-ee/pull/7963

Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.Git"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/16177814474>
> Commit: fddb1629889967638a5675cb6f005b08c113f770
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=16177814474&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Git`
> Spec:
> <hr>Wed, 09 Jul 2025 19:50:34 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for using updated API endpoints during Git connection
setup in tests via an optional parameter.

* **Tests**
* Enhanced test setup flexibility by allowing selection between original
and new API endpoints for Git-related operations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-10 11:13:17 +06:00

555 lines
19 KiB
TypeScript

import { ObjectsRegistry } from "../Objects/Registry";
//const GITEA_API_BASE = "http://35.154.225.218";
export class GitSync {
public agHelper = ObjectsRegistry.AggregateHelper;
private commonLocators = ObjectsRegistry.CommonLocators;
private dataManager = ObjectsRegistry.DataManager;
private assertHelper = ObjectsRegistry.AssertHelper;
private homePage = ObjectsRegistry.HomePage;
public locators = {
quickActionConnectBtn: "[data-testid='t--git-quick-actions-connect']",
quickActionsCommitBtn: "[data-testid='t--git-quick-actions-commit'] button",
quickActionsCommitCount:
"[data-testid='t--git-quick-actions-commit-count']",
quickActionsPullBtn: "[data-testid='t--git-quick-actions-pull'] button",
quickActionsBranchBtn: "[data-testid='t--git-quick-actions-branch']",
quickActionsMergeBtn: "[data-testid='t--git-quick-actions-merge'] button",
quickActionsSettingsBtn: "[data-testid='t--git-quick-actions-settings']",
branchSearchInput: "[data-testid='t--git-branch-search-input'] input",
branchSyncBtn: "[data-testid='t--git-branch-sync']",
branchCloseBtn: "[data-testid='t--git-branch-close']",
branchItem: "[data-testid='t--git-branch-item']",
branchItemMenu: "[data-testid='t--git-branch-item-menu']",
branchItemMenuBtn: "[data-testid='t--git-branch-item-menu-btn']",
branchItemMenuDeleteBtn: "[data-testid='t--git-branch-item-menu-delete']",
connectModal: "[data-testid='t--git-connect-modal']",
connectModalCloseBtn:
"//div[@data-testid='t--git-connect-modal']//button[@aria-label='Close']",
connectModalNextBtn: "[data-testid='t--git-connect-next']",
connectProviderRadioOthers:
"[data-testid='t--git-connect-provider-radio-others']",
connectEmptyRepoYes: "[data-testid='t--git-connect-empty-repo-yes']",
connectRemoteInput: "[data-testid='t--git-connect-remote-input']",
connectDeployKeyCheckbox:
"[data-testid='t--git-connect-deploy-key-checkbox']",
importExistingRepoCheckbox:
"[data-testid='t--git-import-existing-repo-checkbox']",
disconnectModal: "[data-testid='t--git-disconnect-modal']",
disconnectModalCloseBtn:
"//div[@data-testid='t--git-disconnect-modal']//button[@aria-label='Close']",
disconnectModalInput: "[data-testid='t--git-disconnect-modal-input']",
disconnectModalBackBtn: "[data-testid='t--git-disconnect-modal-back-btn']",
disconnectModalRevokeBtn:
"[data-testid='t--git-disconnect-modal-revoke-btn']",
disconnectModalLearnMoreLink:
"[data-testid='t--git-disconnect-learn-more']",
connectSuccessModal: "[data-testid='t--git-con-success-modal']",
connectSuccessModalCloseBtn:
"//div[@data-testid='t--git-success-modal']//button[@aria-label='Close']",
connectSuccessStartUsingBtn:
"[data-testid='t--git-con-success-start-using']",
connectSuccessOpenSettingsBtn:
"[data-testid='t--git-con-success-open-settings']",
disconnectBtn: "[data-testid='t--git-disconnect-btn']",
settingsModal: "[data-testid='t--git-settings-modal']",
settingsModalCloseBtn:
"//div[@data-testid='t--git-settings-modal']//button[@aria-label='Close']",
settingsModalTabGeneral: "[data-testid='t--git-settings-tab-general']",
settingsModalTabBranch: "[data-testid='t--git-settings-tab-branch']",
settingsModalTabCD: "[data-testid='t--git-settings-tab-cd']",
opsModal: "[data-testid='t--git-ops-modal']",
opsModalTabDeploy: "[data-testid='t--git-ops-tab-deploy']",
opsModalTabMerge: "[data-testid='t--git-ops-tab-merge']",
opsModalCloseBtn:
"//div[@data-testid='t--git-ops-modal']//button[@aria-label='Close']",
opsCommitInput: "[data-testid='t--git-ops-commit-input']",
opsCommitBtn: "[data-testid='t--git-ops-commit-btn']",
opsDiscardBtn: "[data-testid='t--git-ops-discard-btn']",
opsDiscardWarningCallout:
"[data-testid='t--git-ops-discard-warning-callout']",
opsPullBtn: "[data-testid='t--git-ops-pull-btn']",
opsMergeBranchSelect: "[data-testid='t--git-ops-merge-branch-select']",
opsMergeBranchSelectMenu:
"[data-testid='t--git-ops-merge-branch-select'] .rc-select-selection-search-input",
opsMergeLoader: "[data-testid='t--git-ops-merge-loader']",
opsMergeStatus: "[data-testid='t--git-ops-merge-status']",
opsMergeBtn: "[data-testid='t--git-ops-merge-button']",
branchProtectionSelect: "[data-testid='t--git-branch-protection-select']",
branchProtectionUpdateBtn:
"[data-testid='t--git-branch-protection-update-btn']",
status: "[data-testid='t--git-status']",
autocommitLoader: "[data-testid='t--git-autocommit-loader']",
conflictErrorOpenRepo: "[data-testid='t--git-conflict-error-open-repo']",
repoLimitErrorModal: "[data-testid='t--git-repo-limit-error-modal']",
repoLimitErrorModalConnectedArtifact:
"[data-testid='t--git-repo-limit-error-connected-artifact']",
repoLimitErrorModalDisconnectLink:
"[data-testid='t--git-repo-limit-error-disconnect-link']",
deployMenuConnect: "[data-testid='t--git-deploy-menu-connect']",
};
public OpenConnectModal() {
this.agHelper.GetNClick(this.locators.quickActionConnectBtn);
this.agHelper.AssertElementVisibility(this.locators.connectModal);
}
public CloseConnectModal() {
this.agHelper.GetNClick(this.locators.connectModalCloseBtn);
this.agHelper.AssertElementAbsence(this.locators.connectModal);
}
public OpenOpsModal() {
this.agHelper.GetNClick(this.locators.opsModal);
this.agHelper.AssertElementVisibility(this.locators.opsModal);
}
public CloseOpsModal() {
this.agHelper.GetNClick(this.locators.opsModalCloseBtn);
this.agHelper.AssertElementAbsence(this.locators.opsModal);
}
public OpenSettingsModal(tab: "GENERAL" | "BRANCH" | "CD" = "GENERAL") {
this.agHelper.GetNClick(this.locators.quickActionsSettingsBtn);
this.agHelper.AssertElementVisibility(this.locators.settingsModal);
const lookup = {
GENERAL: this.locators.settingsModalTabGeneral,
BRANCH: this.locators.settingsModalTabBranch,
CD: this.locators.settingsModalTabCD,
};
const tabSelector = lookup[tab];
if (tabSelector) {
this.agHelper.AssertElementExist(tabSelector);
this.agHelper.GetNClick(tabSelector);
}
}
public CloseGitSettingsModal() {
this.agHelper.GetNClick(this.locators.settingsModalCloseBtn);
this.agHelper.AssertElementAbsence(this.locators.settingsModal);
}
public CreateTestGiteaRepo(repo: string, privateFlag = false) {
cy.request({
method: "POST",
url: `${this.dataManager.GIT_API_BASE}/api/v1/git/repos`,
body: {
name: repo,
private: privateFlag,
},
});
}
public DeleteTestGithubRepo(repo: any) {
cy.request({
method: "DELETE",
url: `${this.dataManager.GIT_API_BASE}/api/v1/git/repos/${repo}`,
});
}
public DeleteDeployKey(repo: any, id: number) {
cy.request({
method: "DELETE",
url: `${this.dataManager.GIT_API_BASE}/api/v1/git/keys/${id}`,
});
}
public CreateNConnectToGit(
repoName = "Repo",
assertConnect = true,
privateFlag = false,
useNewAPI = false,
) {
this.agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
repoName += uid;
this.CreateTestGiteaRepo(repoName, privateFlag);
if (useNewAPI) {
cy.intercept("POST", "/api/v1/git/applications/*/ssh-keypair*").as(
`generateKey-${repoName}`,
);
cy.intercept("GET", "/api/v1/git/applications/*/protected-branches").as(
`protected-${repoName}`,
);
cy.intercept("GET", "/api/v1/git/applications/*/refs").as(
`branches-${repoName}`,
);
} else {
cy.intercept("POST", "/api/v1/applications/ssh-keypair/*").as(
`generateKey-${repoName}`,
);
cy.intercept("GET", "/api/v1/git/branch/app/*/protected").as(
`protected-${repoName}`,
);
cy.intercept("GET", "/api/v1/git/branch/app/*").as(
`branches-${repoName}`,
);
}
this.OpenConnectModal();
this.agHelper.GetNClick(this.locators.connectProviderRadioOthers);
this.agHelper.GetNClick(this.locators.connectEmptyRepoYes);
this.agHelper.GetNClick(this.locators.connectModalNextBtn);
this.agHelper.AssertAttribute(
this.locators.connectRemoteInput,
"placeholder",
"git@example.com:user/repository.git",
);
this.agHelper.TypeText(
this.locators.connectRemoteInput,
`${this.dataManager.GIT_CLONE_URL}/${repoName}.git`,
);
this.agHelper.GetNClick(this.locators.connectModalNextBtn);
this.agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
cy.wait(`@generateKey-${repoName}`).then((result: any) => {
let generatedKey = result.response.body.data.publicKey;
// fetch the generated key and post to the github repo
cy.request({
method: "POST",
url: `${this.dataManager.GIT_API_BASE}/api/v1/git/keys/${repoName}`,
body: {
title: "key_" + uid,
key: generatedKey,
read_only: false,
},
}).then((resp: any) => {
cy.log("Deploy Key Id ", resp.body.key_id);
cy.wrap(resp.body.key_id).as("deployKeyId");
});
});
});
this.agHelper.GetNClick(this.locators.connectDeployKeyCheckbox, 0, true);
this.agHelper.GetNClick(this.locators.connectModalNextBtn);
if (assertConnect) {
this.assertHelper.AssertNetworkStatus("@connectGitLocalRepo");
this.agHelper.GetNClick(this.locators.connectSuccessStartUsingBtn);
this.agHelper.AssertElementExist(
this.locators.quickActionsCommitBtn,
0,
30000,
);
}
cy.wrap(repoName).as("gitRepoName");
});
}
public ImportAppFromGit(
workspaceName: string,
repoName: string,
assertConnect = true,
) {
cy.intercept("GET", "api/v1/git/import/keys?keyType=ECDSA").as(
`importKey-${repoName}`,
);
this.homePage.ImportGitApp(workspaceName);
this.agHelper.GetNClick(this.locators.connectProviderRadioOthers);
this.agHelper.GetNClick(this.locators.importExistingRepoCheckbox, 0, true);
this.agHelper.GetNClick(this.locators.connectModalNextBtn);
this.agHelper.AssertAttribute(
this.locators.connectRemoteInput,
"placeholder",
"git@example.com:user/repository.git",
);
this.agHelper.TypeText(
this.locators.connectRemoteInput,
`${this.dataManager.GIT_CLONE_URL}/${repoName}.git`,
);
this.agHelper.GetNClick(this.locators.connectModalNextBtn);
this.agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
cy.wait(`@importKey-${repoName}`).then((result: any) => {
let generatedKey = result.response.body.data.publicKey;
generatedKey = generatedKey.slice(0, generatedKey.length - 1);
// fetch the generated key and post to the github repo
cy.request({
method: "POST",
url: `${this.dataManager.GIT_API_BASE}/api/v1/git/keys/${repoName}`,
body: {
title: "key_" + uid,
key: generatedKey,
read_only: false,
},
}).then((resp: any) => {
cy.log("Deploy Key Id ", resp.body.key_id);
cy.wrap(resp.body.key_id).as("deployKeyId");
});
});
});
this.agHelper.GetNClick(this.locators.connectDeployKeyCheckbox, 0, true);
this.agHelper.GetNClick(this.locators.connectModalNextBtn);
if (assertConnect) {
this.assertHelper.AssertNetworkStatus("@importFromGit", 201);
}
}
public CreateGitBranch(
branch = "br",
toUseNewGuid = false,
assertCreateBranch = true,
) {
this.agHelper.AssertElementVisibility(this.locators.quickActionsPullBtn);
if (toUseNewGuid) this.agHelper.GenerateUUID();
this.agHelper.AssertElementExist(this.locators.quickActionsCommitBtn);
cy.waitUntil(
() => {
this.agHelper.GetNClick(this.locators.quickActionsBranchBtn, 0, true);
if (this.agHelper.IsElementVisible(this.locators.branchSearchInput)) {
return true; //visible, return true to stop waiting
}
return false; //not visible, return false to continue waiting
},
{ timeout: Cypress.config("pageLoadTimeout") },
);
cy.get("@guid").then((uid) => {
//using the same uid as generated during CreateNConnectToGit
this.agHelper.TypeText(
this.locators.branchSearchInput,
`{selectall}` + `${branch + uid}` + `{enter}`,
{ parseSpecialCharSeq: true },
);
assertCreateBranch &&
this.assertHelper.AssertNetworkStatus("createBranch", 201);
this.agHelper.AssertElementAbsence(
this.commonLocators._specificToast(
Cypress.env("MESSAGES").UNABLE_TO_IMPORT_APP(),
),
);
this.agHelper.WaitUntilEleAppear(this.locators.quickActionsBranchBtn);
this.agHelper.AssertElementVisibility(
this.locators.quickActionsBranchBtn,
);
this.agHelper.GetNAssertContains(
this.locators.quickActionsBranchBtn,
branch + uid,
);
this.assertHelper.AssertNetworkStatus("getBranch");
cy.wrap(branch + uid).as("gitbranchName");
});
}
public SwitchGitBranch(
branch: string,
expectError = false,
refreshList = false,
) {
this.agHelper.AssertElementExist(this.locators.quickActionsPullBtn);
this.agHelper.GetNClick(this.locators.quickActionsBranchBtn);
if (refreshList) {
this.agHelper.GetNClick(this.locators.branchSyncBtn);
}
this.agHelper.TypeText(
this.locators.branchSearchInput,
`{selectall}` + `${branch}`,
{ parseSpecialCharSeq: true },
);
cy.wait(1000);
// this slows down the checkout api by 1 sec
cy.intercept(
{
method: "GET",
url: "/api/v1/git/checkout-branch/app/**",
},
async (req) => {
return new Promise((resolve) => {
setTimeout(() => resolve(req.continue()), 1000);
});
},
).as("gitCheckoutAPI");
//cy.get(gitSync.locators.branchItem).contains(branch).click();
this.agHelper.GetNClickByContains(this.locators.branchItem, branch);
// checks if the spinner exists
cy.get(
`div${this.locators.branchItem} ${this.commonLocators._btnSpinner}`,
{
timeout: 500,
},
).should("exist");
cy.wait("@gitCheckoutAPI");
if (!expectError) {
// increasing timeout to reduce flakyness
cy.get(this.commonLocators._btnSpinner, { timeout: 45000 }).should(
"exist",
);
cy.get(this.commonLocators._btnSpinner, { timeout: 45000 }).should(
"not.exist",
);
}
this.agHelper.Sleep(2000);
}
public CreateRemoteBranch(repo: string, branchName: string) {
cy.request({
method: "POST",
url: `${this.dataManager.GIT_API_BASE}/api/v1/git/repos/${repo}/branches`,
body: {
new_branch_name: branchName,
},
});
}
public GetCurrentBranchName() {
return this.agHelper.GetText(
this.locators.quickActionsBranchBtn,
"text",
0,
);
}
public AssertBranchName(branch: string) {
this.agHelper.AssertElementVisibility(this.locators.quickActionsBranchBtn);
this.agHelper.AssertContains(branch);
}
public CommitAndPush(assertSuccess = true) {
this.agHelper.GetNClick(this.commonLocators._publishButton);
this.agHelper.AssertElementExist(this.locators.quickActionsPullBtn);
this.agHelper.TypeText(this.locators.opsCommitInput, "Initial commit");
this.agHelper.GetNClick(this.locators.opsCommitBtn);
if (assertSuccess) {
this.assertHelper.AssertNetworkStatus("@commit", 201);
cy.wait(3000);
} else {
cy.wait("@commit", { timeout: 35000 }).then((interception: any) => {
const status = interception.response.body.responseMeta.status;
expect(status).to.be.gte(400);
});
}
this.CloseOpsModal();
}
public ClearBranchProtection() {
this.OpenSettingsModal("BRANCH");
this.agHelper.GetNClick(this.locators.branchProtectionSelect);
const selectedOptionsEl = this.agHelper.GetElement(
this.commonLocators._dropdownActiveOption,
);
selectedOptionsEl.each((el) => {
el.trigger("click");
});
this.agHelper.GetNClick(this.locators.branchProtectionUpdateBtn);
this.CloseGitSettingsModal();
}
public AssertAbsenceOfCheckingMergeability() {
this.agHelper.GetNAssertContains(
this.locators.opsMergeStatus,
"Checking mergeability",
"not.exist",
);
}
public CheckMergeConflicts(destinationBranch: string) {
this.agHelper.AssertElementExist(this.locators.quickActionsPullBtn);
this.agHelper.GetNClick(this.locators.quickActionsMergeBtn);
this.agHelper.WaitUntilEleAppear(
this.locators.opsMergeBranchSelectMenu,
false,
);
this.agHelper.WaitUntilEleDisappear(this.locators.opsMergeLoader);
this.assertHelper.AssertNetworkStatus("@getBranch", 200);
this.agHelper.WaitUntilEleAppear(
this.locators.opsMergeBranchSelectMenu,
false,
);
this.agHelper.GetNClick(this.locators.opsMergeBranchSelectMenu, 0, true);
this.agHelper.AssertContains(destinationBranch);
this.agHelper.GetNClickByContains(
this.commonLocators._dropdownOption,
destinationBranch,
);
this.AssertAbsenceOfCheckingMergeability();
}
public MergeToMaster() {
this.CheckMergeConflicts("master");
this.agHelper.AssertElementEnabledDisabled(
this.locators.opsMergeBtn,
0,
false,
);
this.agHelper.GetNClick(this.locators.opsMergeBtn);
this.assertHelper.AssertNetworkStatus("@mergeBranch");
this.agHelper.AssertContains(
Cypress.env("MESSAGES").MERGED_SUCCESSFULLY(),
"be.visible",
);
this.CloseOpsModal();
}
public OpenRepositoryAndVerify() {
this.agHelper.GetNClick(this.locators.conflictErrorOpenRepo);
}
public DiscardChanges() {
this.agHelper.GetNClick(this.locators.quickActionsCommitBtn);
this.agHelper.AssertElementVisibility(this.locators.opsModal);
this.agHelper.AssertElementVisibility(this.locators.opsDiscardBtn);
this.agHelper.ClickButton("Discard & pull");
this.agHelper.AssertContains(
Cypress.env("MESSAGES").DISCARD_CHANGES_WARNING(),
);
this.agHelper.ClickButton("Are you sure?", { waitAfterClick: false });
this.agHelper.AssertContains(
Cypress.env("MESSAGES").DISCARDING_AND_PULLING_CHANGES(),
);
this.agHelper.AssertContains("Discarded changes successfully");
this.assertHelper.AssertNetworkStatus("@discardChanges");
this.assertHelper.AssertNetworkStatus("@gitStatus");
this.agHelper.AssertElementExist(
this.locators.quickActionsCommitBtn,
0,
30000,
);
}
public VerifyChangeLog(uncommitedChanges = false) {
this.agHelper.GetNClick(this.locators.quickActionsCommitBtn);
this.agHelper.AssertElementVisibility(this.locators.opsModal);
if (uncommitedChanges) {
this.agHelper.AssertElementEnabledDisabled(
this.locators.opsCommitInput,
0,
false,
);
} else {
this.agHelper.AssertElementEnabledDisabled(
this.locators.opsCommitInput,
0,
true,
);
}
this.CloseOpsModal();
}
public AssertBranchNameInUrl(branch: string) {
cy.location("search")
.then((searchParams) => new URLSearchParams(searchParams))
.invoke("get", "branch")
.should("equal", branch);
}
}