diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitImport/ImportEmptyRepo_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitImport/ImportEmptyRepo_spec.js new file mode 100644 index 0000000000..a094a2c9c5 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitImport/ImportEmptyRepo_spec.js @@ -0,0 +1,29 @@ +import homePage from "../../../../locators/HomePage"; +import gitSyncLocators from "../../../../locators/gitSyncLocators"; + +describe("Git import empty repository", function() { + let repoName; + const assertConnectFailure = true; + const failureMessage = + "git import failed. \nDetails: Cannot import app from an empty repo"; + + it("Bug #12749 Git Import - Empty Repo NullPointerException", () => { + cy.get(homePage.homeIcon).click(); + cy.get(homePage.optionsIcon) + .first() + .click(); + cy.get(homePage.orgImportAppOption).click({ force: true }); + cy.get(".t--import-json-card") + .next() + .click(); + cy.generateUUID().then((uid) => { + repoName = uid; + cy.createTestGithubRepo(repoName); + cy.importAppFromGit(repoName, true, failureMessage); + }); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + }); + after(() => { + cy.deleteTestGithubRepo(repoName); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitBugs_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitBugs_spec.js index 64475c2566..5c655b3d1e 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitBugs_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitBugs_spec.js @@ -242,3 +242,46 @@ describe("Git synced app with JSObject", function() { cy.deleteTestGithubRepo(repoName); }); }); +describe("Git sync Bug #13385", function() { + it("Bug:13385 : Unable to see application in home page after the git connect flow is aborted in middle", () => { + cy.NavigateToHome(); + cy.createOrg(); + cy.wait("@createOrg").then((interception) => { + const newOrganizationName = interception.response.body.data.name; + cy.CreateAppForOrg(newOrganizationName, `${newOrganizationName}app`); + + cy.generateUUID().then((uid) => { + const owner = Cypress.env("TEST_GITHUB_USER_NAME"); + repoName = uid; + cy.createTestGithubRepo(repoName); + + // open gitSync modal + cy.get(homePage.deployPopupOptionTrigger).click(); + cy.get(homePage.connectToGitBtn).click({ force: true }); + + cy.intercept( + { + url: "api/v1/git/connect/*", + hostname: window.location.host, + }, + (req) => { + req.headers["origin"] = "Cypress"; + }, + ); + cy.intercept("POST", "/api/v1/applications/ssh-keypair/*").as( + `generateKey-${repoName}`, + ); + cy.get(gitSyncLocators.gitRepoInput).type( + `git@github.com:${owner}/${repoName}.git`, + ); + // abort git flow after generating key + cy.get(gitSyncLocators.closeGitSyncModal).click(); + }); + // verify app is visible and open + cy.NavigateToHome(); + cy.reload(); + cy.wait(3000); + cy.SearchApp(`${newOrganizationName}app`); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitSyncedApps_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitSyncedApps_spec.js new file mode 100644 index 0000000000..0183dc569a --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/GitSync/GitSyncedApps_spec.js @@ -0,0 +1,528 @@ +const pages = require("../../../../locators/Pages.json"); +const generatePage = require("../../../../locators/GeneratePage.json"); +const explorer = require("../../../../locators/explorerlocators.json"); +const apiwidget = require("../../../../locators/apiWidgetslocator.json"); +const dynamicInputLocators = require("../../../../locators/DynamicInput.json"); +const queryLocators = require("../../../../locators/QueryEditor.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); +import jsActions from "../../../../locators/jsActionLocators"; +import gitSyncLocators from "../../../../locators/gitSyncLocators"; +import ApiEditor from "../../../../locators/ApiEditor"; +import homePage from "../../../../locators/HomePage"; +import datasource from "../../../../locators/DatasourcesEditor.json"; +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const ee = ObjectsRegistry.EntityExplorer; +const newPage = "ApiCalls_1"; +const pageName = "crudpage_1"; +const tempBranch = "feat/tempBranch"; +const tempBranch1 = "feat/testing"; +const tempBranch0 = "test/tempBranch0"; +const mainBranch = "master"; +let datasourceName; +let repoName; + +describe("Git sync apps", function() { + before(() => { + // cy.NavigateToHome(); + // cy.createOrg(); + // cy.wait("@createOrg").then((interception) => { + // const newOrganizationName = interception.response.body.data.name; + // cy.CreateAppForOrg(newOrganizationName, "gitSyncApp"); + }); + it("Generate postgreSQL crud page , connect to git, clone the page, rename page with special character in it", () => { + cy.NavigateToHome(); + cy.get(homePage.createNew) + .first() + .click({ force: true }); + + cy.wait("@createNewApplication").should( + "have.nested.property", + "response.body.responseMeta.status", + 201, + ); + + // create New App and generate Postgres CRUD page + cy.get(generatePage.generateCRUDPageActionCard).click(); + + cy.get(generatePage.selectDatasourceDropdown).click(); + + cy.contains("Connect New Datasource").click(); + + cy.get(datasource.PostgreSQL).click(); + + cy.fillPostgresDatasourceForm(); + + cy.generateUUID().then((UUID) => { + datasourceName = `${UUID}`; + cy.renameDatasource(datasourceName); + }); + + cy.get(".t--save-datasource").click(); + cy.wait("@saveDatasource").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + + cy.wait("@getDatasourceStructure").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + + cy.get(generatePage.selectTableDropdown).click(); + + cy.get(generatePage.dropdownOption) + .first() + .click(); + // skip optional search column selection. + cy.get(generatePage.generatePageFormSubmitBtn).click(); + + cy.wait("@replaceLayoutWithCRUDPage").should( + "have.nested.property", + "response.body.responseMeta.status", + 201, + ); + cy.wait("@getActions"); + cy.wait("@postExecute").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + + cy.get("span:contains('GOT IT')").click(); + // connect app to git + cy.generateUUID().then((uid) => { + repoName = uid; + + cy.createTestGithubRepo(repoName); + cy.connectToGitRepo(repoName); + }); + // rename page to crud_page + cy.renameEntity("Page1", pageName); + cy.get(`.t--entity-name:contains(${pageName})`) + .trigger("mouseover") + .click({ force: true }); + // create a clone of page + cy.get(`.t--entity-item:contains(${pageName})`).within(() => { + cy.get(".t--context-menu").click({ force: true }); + }); + cy.selectAction("Clone"); + + cy.wait("@clonePage").should( + "have.nested.property", + "response.body.responseMeta.status", + 201, + ); + }); + it("Create api queries from api pane and cURL import , bind it to widget and clone page from page settings", () => { + cy.Createpage(newPage); + cy.get(`.t--entity-item:contains(${newPage})`).click(); + cy.wait(1000); + // create a get api call + cy.NavigateToAPI_Panel(); + cy.CreateAPI("get_data"); + // creating get request using echo + cy.enterDatasourceAndPath("https://mock-api.appsmith.com/echo", "/get"); + cy.get(apiwidget.headerKey).type("info"); + cy.xpath("//span[text()='Key']").click(); + // entering the data in header + cy.get(apiwidget.headerValue).type("This is a test", { + parseSpecialCharSequences: false, + }); + cy.SaveAndRunAPI(); + cy.ResponseStatusCheck("200"); + cy.get(".bp3-icon-chevron-left").click(); + // curl import + cy.get(pages.integrationCreateNew) + .should("be.visible") + .click({ force: true }); + cy.get(ApiEditor.curlImage).click({ force: true }); + cy.get("textarea").type( + 'curl -d \'{"name":"morpheus","job":"leader"}\' -H Content-Type:application/json -X POST https://mock-api.appsmith.com/echo/post', + { + force: true, + parseSpecialCharSequences: false, + }, + ); + cy.importCurl(); + cy.RunAPI(); + cy.ResponseStatusCheck("201 CREATED"); + cy.get("@curlImport").then((response) => { + cy.expect(response.response.body.responseMeta.success).to.eq(true); + cy.get(apiwidget.ApiName) + .invoke("text") + .then((text) => { + const someText = text; + expect(someText).to.equal(response.response.body.data.name); + }); + }); + cy.get(explorer.addWidget).click(); + // bind input widgets to the api calls responses + cy.dragAndDropToCanvas("inputwidgetv2", { x: 300, y: 300 }); + cy.get(".t--widget-inputwidgetv2").should("exist"); + cy.get(dynamicInputLocators.input) + .eq(1) + .click({ force: true }) + .type("{{Api1.data.body.name}}", { parseSpecialCharSequences: false }); + cy.dragAndDropToCanvas("inputwidgetv2", { x: 300, y: 500 }); + cy.get(".t--widget-inputwidgetv2").should("exist"); + cy.get(dynamicInputLocators.input) + .eq(1) + .click({ force: true }) + .type("{{get_data.data.headers.info}}", { + parseSpecialCharSequences: false, + }); + cy.wait(2000); + // clone the page from page settings + cy.xpath("//span[contains(@class,'entity-right-icon')]").click({ + force: true, + }); + cy.xpath("(//button[@type='button'])") + .eq(9) + .click(); + cy.wait("@clonePage").should( + "have.nested.property", + "response.body.responseMeta.status", + 201, + ); + cy.get(`.t--entity-item:contains(${newPage} Copy)`).click(); + cy.wait("@getPage"); + }); + it("Commit and push changes, validate data binding on all pages in edit and deploy mode on master", () => { + // verfiy data binding on all pages in edit mode + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + + cy.get(`.t--entity-item:contains(${newPage})`) + .first() + .click(); + cy.wait("@getPage"); + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + + cy.get(`.t--entity-item:contains(${pageName} Copy)`).click(); + cy.wait("@getPage"); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + cy.get(`.t--entity-item:contains(${pageName})`) + .first() + .click(); + cy.wait("@getPage"); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + // commit and push the changes + cy.commitAndPush(); + cy.wait(2000); + // verify data binding on all pages in deploy mode + cy.latestDeployPreview(); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + cy.get(".t--page-switch-tab") + .contains(`${pageName} Copy`) + .click({ force: true }); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + cy.get(".t--page-switch-tab") + .contains(`${newPage}`) + .click({ force: true }); + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + cy.get(".t--page-switch-tab") + .contains(`${newPage} Copy`) + .click({ force: true }); + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + cy.get(commonlocators.backToEditor).click(); + cy.wait(2000); + }); + it("Create a new branch tempBranch, add jsObject and datasource query, move them to new page i.e. Child_Page and bind to widgets", () => { + cy.createGitBranch(tempBranch); + cy.wait(1000); + // create jsObject and rename it + cy.createJSObject('return "Success";'); + cy.wait(2000); + // create postgres select query + //cy.CheckAndUnfoldEntityItem("DATASOURCES"); + cy.NavigateToQueryEditor(); + cy.NavigateToActiveTab(); + cy.get(datasource.datasourceCard) + .contains(datasourceName) + .scrollIntoView() + .should("be.visible") + .closest(datasource.datasourceCard) + .within(() => { + cy.get(datasource.createQuerty).click(); + }); + cy.get(queryLocators.queryNameField).type("get_users"); + cy.get(queryLocators.switch) + .last() + .click({ force: true }); + cy.get(queryLocators.templateMenu).click(); + cy.get(queryLocators.query).click({ force: true }); + // writing query to get the schema + cy.get(".CodeMirror textarea") + .first() + .focus() + .type("SELECT * FROM users ORDER BY id LIMIT 10;", { + force: true, + parseSpecialCharSequences: false, + }); + cy.WaitAutoSave(); + cy.runQuery(); + // create a new page + cy.CheckAndUnfoldEntityItem("PAGES"); + cy.Createpage("Child_Page"); + cy.wait(1000); + cy.get(`.t--entity-name:contains(${newPage} Copy)`) + .trigger("mouseover") + .click({ force: true }); + // move jsObject and postgres query to new page + cy.CheckAndUnfoldEntityItem("QUERIES/JS"); + ee.ActionContextMenuByEntityName("get_users", "Move to page", "Child_Page"); + cy.wait(2000); + cy.get(`.t--entity-name:contains(${newPage} Copy)`) + .trigger("mouseover") + .click({ force: true }); + ee.ActionContextMenuByEntityName("JSObject1", "Move to page", "Child_Page"); + cy.wait(2000); + cy.get(explorer.addWidget).click(); + // bind input widgets to the jsObject and query response + cy.dragAndDropToCanvas("inputwidgetv2", { x: 300, y: 300 }); + cy.get(".t--widget-inputwidgetv2").should("exist"); + cy.get(dynamicInputLocators.input) + .eq(1) + .click({ force: true }) + .type("{{JSObject1.myFun1()}}", { parseSpecialCharSequences: false }); + cy.dragAndDropToCanvas("inputwidgetv2", { x: 300, y: 500 }); + cy.get(".t--widget-inputwidgetv2").should("exist"); + cy.get(dynamicInputLocators.input) + .eq(1) + .click({ force: true }) + .type("{{get_users.data[0].name}}", { + parseSpecialCharSequences: false, + }); + cy.wait(2000); + }); + it("Commit and push changes, validate data binding on all pages in edit and deploy mode on tempBranch", () => { + // commit and push changes + cy.get(homePage.publishButton).click(); + cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit"); + cy.get(gitSyncLocators.commitButton).click(); + cy.wait(8000); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + // verfiy data binding on all pages in deploy mode + cy.latestDeployPreview(); + cy.get(".bp3-input") + .first() + .should("have.value", "Success"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "Test user 7"); + cy.get(".t--page-switch-tab") + .contains(`${pageName}`) + .click({ force: true }); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + cy.get(".t--page-switch-tab") + .contains(`${pageName} Copy`) + .click({ force: true }); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + cy.get(".t--page-switch-tab") + .contains(`${newPage}`) + .click({ force: true }); + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + cy.get(".t--page-switch-tab") + .contains(`${newPage} Copy`) + .click({ force: true }); + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + cy.get(commonlocators.backToEditor).click(); + cy.wait(2000); + // verfiy data binding on all pages in edit mode + /* cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + cy.get(`.t--entity-item:contains(Child_Page)`) + .first() + .click(); + cy.wait("@getPage"); + cy.reload(); + cy.wait(3000); + cy.get(".bp3-input") + .first() + .should("have.value", "Success"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "Test user 7"); + cy.get(`.t--entity-item:contains(${newPage})`) + .first() + .click(); + cy.wait("@getPage"); + cy.get(".bp3-input") + .first() + .should("have.value", "morpheus"); + cy.get(".bp3-input") + .eq(1) + .should("have.value", "This is a test"); + + cy.get(`.t--entity-item:contains(${pageName} Copy)`).click(); + cy.wait("@getPage"); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); + cy.get(`.t--entity-item:contains(${pageName})`) + .first() + .click(); + cy.wait("@getPage"); + cy.readTabledataPublish("0", "1").then((cellData) => { + expect(cellData).to.be.equal("New Config"); + }); */ + }); + it("Switch to master and verify no uncommitted changes should be shown on master", () => { + cy.switchGitBranch("master"); + cy.wait(2000); + // verify commit input box is disabled + cy.get(homePage.publishButton).click(); + cy.get(".t--commit-comment-input") + .should("be.disabled") + .and("have.text", "No changes to commit"); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + }); + it("Switch to tempBranch , Clone the Child_Page, change it's visiblity to hidden and deploy, merge to master", () => { + cy.switchGitBranch(tempBranch); + cy.wait(2000); + // clone the Child_Page + cy.CheckAndUnfoldEntityItem("PAGES"); + cy.get(`.t--entity-item:contains(Child_Page)`).within(() => { + cy.get(".t--context-menu").click({ force: true }); + }); + cy.selectAction("Clone"); + cy.wait("@clonePage").should( + "have.nested.property", + "response.body.responseMeta.status", + 201, + ); + // change cloned page visiblity to hidden + cy.CheckAndUnfoldEntityItem("PAGES"); + + cy.get(`.t--entity-item:contains(Child_Page Copy)`).within(() => { + cy.get(".t--context-menu").click({ force: true }); + }); + cy.selectAction("Hide"); + + cy.get(`.t--entity-item:contains(Child_Page)`) + .first() + .click(); + cy.wait("@getPage"); + cy.get(homePage.publishButton).click(); + cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit"); + cy.get(gitSyncLocators.commitButton).click(); + cy.wait(8000); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + cy.wait(2000); + cy.merge(mainBranch); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + cy.wait(2000); + cy.latestDeployPreview(); + // verify page is hidden on deploy mode + cy.get(".t--page-switch-tab").should("not.contain", "Child_Page Copy"); + cy.get(commonlocators.backToEditor).click(); + cy.wait(2000); + }); + it("Verify Page visiblity on master in edit and deploy mode", () => { + cy.switchGitBranch(mainBranch); + cy.wait(2000); + cy.latestDeployPreview(); + cy.get(".t--page-switch-tab").should("not.contain", "Child_Page Copy"); + cy.get(commonlocators.backToEditor).click(); + cy.wait(2000); + }); + it("Create new branch, delete a page and merge back to master, verify page is deleted on master", () => { + cy.createGitBranch(tempBranch1); + // delete page from page settings + cy.Deletepage("Child_Page Copy"); + cy.get(homePage.publishButton).click(); + cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit"); + cy.get(gitSyncLocators.commitButton).click(); + cy.wait(8000); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + cy.wait(2000); + cy.merge(mainBranch); + cy.get(gitSyncLocators.closeGitSyncModal).click(); + // verify Child_Page is not on master + cy.switchGitBranch(mainBranch); + cy.CheckAndUnfoldEntityItem("PAGES"); + cy.get(`.t--entity-name:contains("Child_Page Copy")`).should("not.exist"); + // create another branch and verify deleted page doesn't exist on it + cy.createGitBranch(tempBranch0); + cy.CheckAndUnfoldEntityItem("PAGES"); + cy.get(`.t--entity-name:contains("Child_Page Copy")`).should("not.exist"); + }); + + it("Import app from git and verify page order should not change", () => { + cy.get(homePage.homeIcon).click(); + cy.get(homePage.optionsIcon) + .first() + .click(); + cy.get(homePage.orgImportAppOption).click({ force: true }); + cy.get(".t--import-json-card") + .next() + .click(); + // import application from git + cy.importAppFromGit(repoName); + cy.wait(2000); + // verify page order remains same as in orignal app + cy.CheckAndUnfoldEntityItem("PAGES"); + cy.get(".t--entity-item") + .eq(1) + .contains("crudpage_1"); + cy.get(".t--entity-item") + .eq(2) + .contains("crudpage_1 Copy"); + cy.get(".t--entity-item") + .eq(3) + .contains("ApiCalls_1"); + cy.get(".t--entity-item") + .eq(4) + .contains("ApiCalls_1 Copy"); + cy.get(".t--entity-item") + .eq(5) + .contains("Child_Page"); + }); +}); diff --git a/app/client/cypress/support/OrgCommands.js b/app/client/cypress/support/OrgCommands.js index a6a56c54ba..87e440f8a3 100644 --- a/app/client/cypress/support/OrgCommands.js +++ b/app/client/cypress/support/OrgCommands.js @@ -4,13 +4,9 @@ require("cy-verify-downloads").addCustomCommand(); require("cypress-file-upload"); - -const { - addMatchImageSnapshotCommand, -} = require("cypress-image-snapshot/command"); import homePage from "../locators/HomePage"; const generatePage = require("../locators/GeneratePage.json"); - +import explorer from "../locators/explorerlocators"; export const initLocalstorage = () => { cy.window().then((window) => { window.localStorage.setItem("ShowCommentsButtonToolTip", ""); @@ -302,3 +298,12 @@ Cypress.Commands.add("CreateAppInFirstListedOrg", (appname) => { */ cy.wait("@updateLayout"); }); +Cypress.Commands.add("renameEntity", (entityName, renamedEntity) => { + cy.get(`.t--entity-item:contains(${entityName})`).within(() => { + cy.get(".t--context-menu").click({ force: true }); + }); + cy.selectAction("Edit Name"); + cy.get(explorer.editEntity) + .last() + .type(`${renamedEntity}`, { force: true }); +}); diff --git a/app/client/cypress/support/gitSync.js b/app/client/cypress/support/gitSync.js index 73e877a800..0527740b2e 100644 --- a/app/client/cypress/support/gitSync.js +++ b/app/client/cypress/support/gitSync.js @@ -294,7 +294,7 @@ Cypress.Commands.add("merge", (destinationBranch) => { Cypress.Commands.add( "importAppFromGit", - (repo, shouldCommit = true, assertConnectFailure) => { + (repo, assertConnectFailure, failureMessage) => { const testEmail = "test@test.com"; const testUsername = "testusername"; const owner = Cypress.env("TEST_GITHUB_USER_NAME"); @@ -353,7 +353,9 @@ Cypress.Commands.add( } else { cy.wait("@importFromGit").then((interception) => { const status = interception.response.body.responseMeta.status; + const message = interception.response.body.responseMeta.error.message; expect(status).to.be.gte(400); + expect(message).to.contain(failureMessage); }); } });