diff --git a/packages/builder/cypress.json b/packages/builder/cypress.json index d76078f509..09b38ae985 100644 --- a/packages/builder/cypress.json +++ b/packages/builder/cypress.json @@ -1,8 +1,8 @@ { - "baseUrl": "http://localhost:4005/_builder/", + "baseUrl": "http://localhost:4001/_builder/", "video": true, "projectId": "bmbemn", "env": { - "PORT": "4005" + "PORT": "4001" } } diff --git a/packages/builder/cypress/integration/createApp.spec.js b/packages/builder/cypress/integration/createApp.spec.js index a32782867a..74c9e2999d 100644 --- a/packages/builder/cypress/integration/createApp.spec.js +++ b/packages/builder/cypress/integration/createApp.spec.js @@ -1,18 +1,7 @@ -context('Create an Application', () => { - - beforeEach(() => { - cy.server() - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - }) - - // https://on.cypress.io/interacting-with-elements - - it('should create a new application', () => { - // https://on.cypress.io/type - cy.createApp('My Cool App', 'This is a description') - - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - - cy.contains('My Cool App').should('exist') - }) +context("Create an Application", () => { + it("should create a new application", () => { + cy.createTestApp() + cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) + cy.contains("Cypress Tests").should("exist") + }) }) diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js index 97b67cf15c..36db9797c3 100644 --- a/packages/builder/cypress/integration/createAutomation.spec.js +++ b/packages/builder/cypress/integration/createAutomation.spec.js @@ -1,12 +1,6 @@ context("Create a automation", () => { before(() => { - cy.server() - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - - cy.createApp( - "Automation Test App", - "This app is used to test that automations do in fact work!" - ) + cy.createTestApp() }) // https://on.cypress.io/interacting-with-elements diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js index 4074aca112..99d741ab46 100644 --- a/packages/builder/cypress/integration/createBinding.spec.js +++ b/packages/builder/cypress/integration/createBinding.spec.js @@ -1,18 +1,59 @@ -xcontext('Create a Binding', () => { - before(() => { - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - cy.createApp('Binding App', 'Binding App Description') - cy.navigateToFrontend() - }) +context("Create Bindings", () => { + before(() => { + cy.createTestApp() + cy.navigateToFrontend() + }) - it('add an input binding', () => { - cy.get(".nav-items-container").contains('Home').click() - cy.contains("Add").click() - cy.get("[data-cy=Input]").click() - cy.get("[data-cy=Textfield]").click() - cy.contains("Heading").click() - cy.get("[data-cy=text-binding-button]").click() - cy.get("[data-cy=binding-dropdown-modal]").contains('Input 1').click() - cy.get("[data-cy=binding-dropdown-modal] textarea").should('have.value', 'Home{{ Input 1 }}') + it("should add a current user binding", () => { + cy.addComponent("Elements", "Paragraph").then(componentId => { + addSettingBinding("text", "Current User._id") + cy.getComponent(componentId).should( + "have.text", + `ro_ta_users_us_test@test.com` + ) }) + }) + + it("should handle an invalid binding", () => { + cy.addComponent("Elements", "Paragraph").then(componentId => { + // Cypress needs to escape curly brackets + cy.get("[data-cy=setting-text] input") + .type("{{}{{}{{} Current User._id {}}{}}") + .blur() + cy.getComponent(componentId).should("have.text", "{{{ user._id }}") + }) + }) + + it("should add a URL param binding", () => { + const paramName = "foo" + cy.createScreen("Test Param", `/test/:${paramName}`) + cy.addComponent("Elements", "Paragraph").then(componentId => { + addSettingBinding("text", `URL.${paramName}`) + // The builder preview pages don't have a real URL, so all we can do + // is check that we were able to bind to the property, and that the + // component exists on the page + cy.getComponent(componentId).should("have.text", "") + }) + }) + + it("should add a binding with a handlebars helper", () => { + cy.addComponent("Elements", "Paragraph").then(componentId => { + // Cypress needs to escape curly brackets + addSettingBinding("text", "{{}{{} add 1 2 {}}{}}", false) + cy.getComponent(componentId).should("have.text", "3") + }) + }) }) + +const addSettingBinding = (setting, bindingText, clickOption = true) => { + cy.get(`[data-cy="setting-${setting}"] [data-cy=text-binding-button]`).click() + cy.get(".drawer").within(() => { + if (clickOption) { + cy.contains(bindingText).click() + cy.get("textarea").should("have.value", `{{ ${bindingText} }}`) + } else { + cy.get("textarea").type(bindingText) + } + cy.get("button").click() + }) +} diff --git a/packages/builder/cypress/integration/createComponents.spec.js b/packages/builder/cypress/integration/createComponents.spec.js index b87e863e53..56aca88837 100644 --- a/packages/builder/cypress/integration/createComponents.spec.js +++ b/packages/builder/cypress/integration/createComponents.spec.js @@ -1,60 +1,90 @@ -xcontext("Create Components", () => { +context("Create Components", () => { + let headlineId + before(() => { - cy.server() - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - // https://on.cypress.io/type - cy.createApp("Table App", "Table App Description") - cy.createTable("dog", "name", "age") - cy.addRow("bob", "15") - }) - - // https://on.cypress.io/interacting-with-elements - it("should add a container", () => { + cy.createTestApp() + cy.createTable("dog") + cy.addColumn("dog", "name", "string") + cy.addColumn("dog", "age", "number") + cy.addColumn("dog", "type", "options") cy.navigateToFrontend() - cy.get(".switcher > :nth-child(2)").click() - cy.contains("Container").click() }) - it("should add a headline", () => { - cy.addHeadlineComponent("An Amazing headline!") - getIframeBody().contains("An Amazing headline!") + it("should add a container", () => { + cy.addComponent(null, "Container").then(componentId => { + cy.getComponent(componentId).should("exist") + }) }) - it("change the font size of the headline", () => { + + it("should add a headline", () => { + cy.addComponent("Elements", "Headline").then(componentId => { + headlineId = componentId + cy.getComponent(headlineId).should("exist") + }) + }) + + it("should change the text of the headline", () => { + const text = "Lorem ipsum dolor sit amet." + cy.get("[data-cy=Settings]").click() + cy.get("[data-cy=setting-text] input") + .type(text) + .blur() + cy.getComponent(headlineId).should("have.text", text) + }) + + it("should change the size of the headline", () => { + cy.get("[data-cy=Design]").click() cy.contains("Typography").click() cy.get("[data-cy=font-size-prop-control]").click() cy.contains("60px").click() - cy.contains("Design").click() + cy.getComponent(headlineId).should("have.css", "font-size", "60px") + }) - getIframeBody() - .contains("An Amazing headline!") - .should("have.css", "font-size", "60px") + it("should create a form and reset to match schema", () => { + cy.addComponent("Form", "Form").then(() => { + cy.get("[data-cy=Settings]").click() + cy.get("[data-cy=setting-datasource]") + .contains("Choose option") + .click() + cy.get(".dropdown") + .contains("dog") + .click() + cy.addComponent("Form", "Field Group").then(fieldGroupId => { + cy.get("[data-cy=Settings]").click() + cy.contains("Update Form Fields").click() + cy.get(".modal") + .get("button.primary") + .click() + cy.getComponent(fieldGroupId).within(() => { + cy.contains("name").should("exist") + cy.contains("age").should("exist") + cy.contains("type").should("exist") + }) + cy.getComponent(fieldGroupId) + .find("input") + .should("have.length", 2) + cy.getComponent(fieldGroupId) + .find(".spectrum-Picker") + .should("have.length", 1) + }) + }) + }) + + it("deletes a component", () => { + cy.addComponent("Elements", "Paragraph").then(componentId => { + cy.get("[data-cy=setting-_instanceName] input") + .type(componentId) + .blur() + cy.get(".ui-nav ul .nav-item.selected .ri-more-line").click({ + force: true, + }) + cy.get(".dropdown-container") + .contains("Delete") + .click() + cy.get(".modal") + .contains("Delete Component") + .click() + cy.getComponent(componentId).should("not.exist") + }) }) }) - -const getIframeDocument = () => { - return ( - cy - .get("iframe") - // Cypress yields jQuery element, which has the real - // DOM element under property "0". - // From the real DOM iframe element we can get - // the "document" element, it is stored in "contentDocument" property - // Cypress "its" command can access deep properties using dot notation - // https://on.cypress.io/its - .its("0.contentDocument") - .should("exist") - ) -} - -const getIframeBody = () => { - // get the document - return ( - getIframeDocument() - // automatically retries until body is loaded - .its("body") - .should("not.be.undefined") - // wraps "body" DOM element to allow - // chaining more Cypress commands, like ".find(...)" - .then(cy.wrap) - ) -} diff --git a/packages/builder/cypress/integration/createScreen.js b/packages/builder/cypress/integration/createScreen.js new file mode 100644 index 0000000000..c658ab51e1 --- /dev/null +++ b/packages/builder/cypress/integration/createScreen.js @@ -0,0 +1,10 @@ +context("Screen Tests", () => { + before(() => { + cy.createTestApp() + cy.navigateToFrontend() + }) + + it("Should successfully create a screen", () => { + cy.createScreen("Test Screen", "/test") + }) +}) diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js index 3a8b466502..bbdb2e67fe 100644 --- a/packages/builder/cypress/integration/createTable.spec.js +++ b/packages/builder/cypress/integration/createTable.spec.js @@ -1,7 +1,6 @@ context("Create a Table", () => { before(() => { - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - cy.createApp("Table App", "Table App Description") + cy.createTestApp() }) it("should create a new Table", () => { @@ -26,7 +25,9 @@ context("Create a Table", () => { .trigger("mouseover") .find(".ri-pencil-line") .click({ force: true }) - cy.get(".actions input").first().type("updated") + cy.get(".actions input") + .first() + .type("updated") // Unset table display column cy.contains("display column").click() cy.contains("Save Column").click() @@ -44,7 +45,9 @@ context("Create a Table", () => { it("deletes a row", () => { cy.get(".ag-checkbox-input").check({ force: true }) cy.contains("Delete 1 row(s)").click() - cy.get(".modal").contains("Delete").click() + cy.get(".modal") + .contains("Delete") + .click() cy.contains("RoverUpdated").should("not.exist") }) @@ -55,12 +58,16 @@ context("Create a Table", () => { .click({ force: true }) cy.contains("Delete").click() cy.wait(50) - cy.get(".buttons").contains("Delete").click() + cy.get(".buttons") + .contains("Delete") + .click() cy.contains("nameupdated").should("not.exist") }) it("deletes a table", () => { - cy.get(".ri-more-line").first().click({ force: true }) + cy.get(".ri-more-line") + .first() + .click({ force: true }) cy.get("[data-cy=delete-table]").click() cy.contains("Delete Table").click() cy.contains("dog").should("not.exist") diff --git a/packages/builder/cypress/integration/createUser.spec.js b/packages/builder/cypress/integration/createUser.spec.js index 45a1ffcc68..0beaf5b80f 100644 --- a/packages/builder/cypress/integration/createUser.spec.js +++ b/packages/builder/cypress/integration/createUser.spec.js @@ -1,17 +1,10 @@ -context('Create a User', () => { +context("Create a User", () => { + before(() => { + cy.createTestApp() + }) - before(() => { - cy.server() - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - // https://on.cypress.io/type - cy.createApp('User App', 'This app is used to test user creation') - }) - - // https://on.cypress.io/interacting-with-elements - it('should create a user', () => { - cy.createUser("bbuser@test.com", "test", "ADMIN") - - // // Check to make sure user was created! - cy.contains("bbuser").should('be.visible') - }) + it("should create a user", () => { + cy.createUser("bbuser@test.com", "test", "ADMIN") + cy.contains("bbuser").should("be.visible") + }) }) diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index 6f321ab16f..4d8e2a8134 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -1,15 +1,6 @@ -function removeSpacing(headers) { - let newHeaders = [] - for (let header of headers) { - newHeaders.push(header.replace(/\s\s+/g, " ")) - } - return newHeaders -} - context("Create a View", () => { before(() => { - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - cy.createApp("View App", "View App Description") + cy.createTestApp() cy.createTable("data") cy.addColumn("data", "group", "Text") cy.addColumn("data", "age", "Number") @@ -36,16 +27,28 @@ context("Create a View", () => { const headers = Array.from($headers).map(header => header.textContent.trim() ) - expect(removeSpacing(headers)).to.deep.eq([ "rating Number", "age Number", "group Text" ]) + expect(removeSpacing(headers)).to.deep.eq([ + "rating Number", + "age Number", + "group Text", + ]) }) }) it("filters the view by age over 10", () => { cy.contains("Filter").click() cy.contains("Add Filter").click() - cy.get(".menu-container").find("select").first().select("age") - cy.get(".menu-container").find("select").eq(1).select("More Than") - cy.get(".menu-container").find("input").type(18) + cy.get(".menu-container") + .find("select") + .first() + .select("age") + cy.get(".menu-container") + .find("select") + .eq(1) + .select("More Than") + cy.get(".menu-container") + .find("input") + .type(18) cy.contains("Save").click() cy.get("[role=rowgroup] .ag-row").get($values => { expect($values).to.have.length(5) @@ -57,9 +60,15 @@ context("Create a View", () => { cy.viewport("macbook-15") cy.contains("Calculate").click() - cy.get(".menu-container").find("select").eq(0).select("Statistics") + cy.get(".menu-container") + .find("select") + .eq(0) + .select("Statistics") cy.wait(50) - cy.get(".menu-container").find("select").eq(1).select("age") + cy.get(".menu-container") + .find("select") + .eq(1) + .select("age") cy.contains("Save").click() cy.wait(100) cy.get(".ag-center-cols-viewport").scrollTo("100%") @@ -68,19 +77,19 @@ context("Create a View", () => { const headers = Array.from($headers).map(header => header.textContent.trim() ) - expect(removeSpacing(headers)).to.deep.eq([ "avg Number", + expect(removeSpacing(headers)).to.deep.eq([ + "avg Number", "sumsqr Number", "count Number", "max Number", "min Number", "sum Number", - "field Text" ]) + "field Text", + ]) }) cy.get(".ag-cell").then($values => { - let values = Array.from($values).map(header => - header.textContent.trim() - ) - expect(values).to.deep.eq([ "31", "5347", "5", "49", "20", "155", "age" ]) + let values = Array.from($values).map(header => header.textContent.trim()) + expect(values).to.deep.eq(["31", "5347", "5", "49", "20", "155", "age"]) }) }) @@ -99,7 +108,15 @@ context("Create a View", () => { .find(".ag-cell") .then($values => { const values = Array.from($values).map(value => value.textContent) - expect(values).to.deep.eq([ "Students", "23.333333333333332", "1650", "3", "25", "20", "70" ]) + expect(values).to.deep.eq([ + "Students", + "23.333333333333332", + "1650", + "3", + "25", + "20", + "70", + ]) }) }) @@ -124,3 +141,11 @@ context("Create a View", () => { cy.contains("TestView Updated").should("not.be.visible") }) }) + +function removeSpacing(headers) { + let newHeaders = [] + for (let header of headers) { + newHeaders.push(header.replace(/\s\s+/g, " ")) + } + return newHeaders +} diff --git a/packages/builder/cypress/integration/screens.spec.js b/packages/builder/cypress/integration/screens.spec.js deleted file mode 100644 index d219e7daa7..0000000000 --- a/packages/builder/cypress/integration/screens.spec.js +++ /dev/null @@ -1,13 +0,0 @@ - -context('Screen Tests', () => { - before(() => { - cy.server() - cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) - cy.createApp('Conor Cy App', 'Table App Description') - cy.navigateToFrontend() - }) - - it('Should successfully create a screen', () => { - cy.createScreen("test Screen", "/test") - }) -}) \ No newline at end of file diff --git a/packages/builder/cypress/setup.js b/packages/builder/cypress/setup.js index d6b60d194e..6848d38cba 100644 --- a/packages/builder/cypress/setup.js +++ b/packages/builder/cypress/setup.js @@ -3,15 +3,12 @@ // 2. Initialises using `.budibase` // 3. Runs the server using said folder -const rimraf = require("rimraf") const { join, resolve } = require("path") const initialiseBudibase = require("../../server/src/utilities/initialiseBudibase") const cypressConfig = require("../cypress.json") const homedir = join(require("os").homedir(), ".budibase") -rimraf.sync(homedir) - process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE" process.env.NODE_ENV = "cypress" process.env.ENABLE_ANALYTICS = "false" diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index e92034c120..a157477665 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -25,8 +25,8 @@ // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) Cypress.Commands.add("createApp", name => { + cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) cy.contains("Create New Web App").click() - cy.get("body") .then($body => { if ($body.find("input[name=apiKey]").length) { @@ -41,9 +41,7 @@ Cypress.Commands.add("createApp", name => { cy.get("input[name=applicationName]") .type(name) .should("have.value", name) - cy.contains("Next").click() - cy.get("input[name=email]") .click() .type("test@test.com") @@ -57,6 +55,28 @@ Cypress.Commands.add("createApp", name => { }) }) +Cypress.Commands.add("deleteApp", name => { + cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) + cy.get("body").then($body => { + cy.wait(1000) + if ($body.find(`[data-cy="app-${name}"]`).length) { + cy.get(`[data-cy="app-${name}"] a`).click() + cy.get("[data-cy=settings-icon]").click() + cy.get(".modal-content").within(() => { + cy.contains("Danger Zone").click() + cy.get("input").type("DELETE") + cy.contains("Delete Entire App").click() + }) + } + }) +}) + +Cypress.Commands.add("createTestApp", () => { + const appName = "Cypress Tests" + cy.deleteApp(appName) + cy.createApp(appName, "This app is used for Cypress testing.") +}) + Cypress.Commands.add("createTestTableWithData", () => { cy.createTable("dog") cy.addColumn("dog", "name", "Text") @@ -87,6 +107,7 @@ Cypress.Commands.add("addColumn", (tableName, columnName, type) => { cy.get("input") .first() .type(columnName) + // Unset table display column cy.contains("display column").click() cy.get("select").select(type) @@ -96,15 +117,12 @@ Cypress.Commands.add("addColumn", (tableName, columnName, type) => { Cypress.Commands.add("addRow", values => { cy.contains("Create New Row").click() - cy.get(".modal").within(() => { for (let i = 0; i < values.length; i++) { cy.get("input") .eq(i) .type(values[i]) } - - // Save cy.get(".buttons") .contains("Create") .click() @@ -114,9 +132,7 @@ Cypress.Commands.add("addRow", values => { Cypress.Commands.add("createUser", (email, password, role) => { // Create User cy.contains("Users").click() - cy.contains("Create New User").click() - cy.get(".modal").within(() => { cy.get("input") .first() @@ -135,20 +151,29 @@ Cypress.Commands.add("createUser", (email, password, role) => { }) }) -Cypress.Commands.add("addHeadlineComponent", text => { - cy.get(".switcher > :nth-child(2)").click() - - cy.get("[data-cy=Text]").click() - cy.get("[data-cy=Headline]").click() - cy.get(".tabs > :nth-child(2)").click() - cy.contains("Settings").click() - cy.get('input[name="text"]').type(text) - cy.contains("Design").click() +Cypress.Commands.add("addComponent", (category, component) => { + if (category) { + cy.get(`[data-cy="category-${category}"]`).click() + } + cy.get(`[data-cy="component-${component}"]`).click() + cy.wait(500) + cy.location().then(loc => { + const params = loc.pathname.split("/") + const componentId = params[params.length - 1] + cy.getComponent(componentId).should("exist") + return cy.wrap(componentId) + }) }) -Cypress.Commands.add("addButtonComponent", () => { - cy.get(".switcher > :nth-child(2)").click() - cy.get("[data-cy=Button]").click() +Cypress.Commands.add("getComponent", componentId => { + return cy + .get("iframe") + .its("0.contentDocument") + .should("exist") + .its("body") + .should("not.be.null") + .then(cy.wrap) + .find(`[data-component-id=${componentId}]`) }) Cypress.Commands.add("navigateToFrontend", () => { diff --git a/packages/builder/package.json b/packages/builder/package.json index e19514d2ac..682867c313 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -14,8 +14,8 @@ "cy:run": "cypress run", "cy:open": "cypress open", "cy:run:ci": "cypress run --browser electron --record --key f308590b-6070-41af-b970-794a3823d451", - "cy:test": "start-server-and-test cy:setup http://localhost:4005/_builder cy:run", - "cy:ci": "start-server-and-test cy:setup http://localhost:4005/_builder cy:run:ci" + "cy:test": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run", + "cy:ci": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run:ci" }, "jest": { "globals": { diff --git a/packages/builder/src/components/design/AppPreview/ComponentSelectionList.svelte b/packages/builder/src/components/design/AppPreview/ComponentSelectionList.svelte index d161fc80a1..a1798d9ad7 100644 --- a/packages/builder/src/components/design/AppPreview/ComponentSelectionList.svelte +++ b/packages/builder/src/components/design/AppPreview/ComponentSelectionList.svelte @@ -57,6 +57,7 @@