Merge branch 'master' of https://github.com/Budibase/budibase into plus-datasources

This commit is contained in:
Martin McKeaveney 2021-06-07 18:09:03 +01:00
commit 905c8f461f
34 changed files with 278 additions and 250 deletions

View File

@ -45,9 +45,10 @@ jobs:
- name: Build and Push Staging Docker Image - name: Build and Push Staging Docker Image
# Only run on push # Only run on push
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
run: | run: |
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
yarn build
yarn build:docker:staging yarn build:docker:staging
env: env:
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}

View File

@ -3,7 +3,7 @@
tag=$1 tag=$1
tag=${tag:-latest} tag=${tag:-latest}
echo "Tagging images with SHA: $GITHUB_SHA and version: $BUDIBASE_VERSION" echo "Tagging images with SHA: $GITHUB_SHA and tag: $tag"
docker tag app-service budibase/apps:$tag docker tag app-service budibase/apps:$tag
docker tag worker-service budibase/worker:$tag docker tag worker-service budibase/worker:$tag

View File

@ -1,5 +1,5 @@
{ {
"version": "0.9.27", "version": "0.9.29",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -39,6 +39,6 @@
"test:e2e": "lerna run cy:test", "test:e2e": "lerna run cy:test",
"test:e2e:ci": "lerna run cy:ci", "test:e2e:ci": "lerna run cy:ci",
"build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -", "build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -",
"build:docker:staging": "cd hosting/scripts/linux/ && ./release-to-docker-hub.sh staging && cd -" "build:docker:staging": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh staging && cd -"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.27", "version": "0.9.29",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -1,6 +1,5 @@
const { newid } = require("../hashing") const { newid } = require("../hashing")
const Replication = require("./Replication") const Replication = require("./Replication")
const { getCouch } = require("./index")
const UNICODE_MAX = "\ufff0" const UNICODE_MAX = "\ufff0"
const SEPARATOR = "_" const SEPARATOR = "_"
@ -164,8 +163,7 @@ exports.getDeployedAppID = appId => {
* different users/companies apps as there is no security around it - all apps are returned. * different users/companies apps as there is no security around it - all apps are returned.
* @return {Promise<object[]>} returns the app information document stored in each app database. * @return {Promise<object[]>} returns the app information document stored in each app database.
*/ */
exports.getAllApps = async ({ dev, all } = {}) => { exports.getAllApps = async ({ CouchDB, dev, all } = {}) => {
const CouchDB = getCouch()
let allDbs = await CouchDB.allDbs() let allDbs = await CouchDB.allDbs()
const appDbNames = allDbs.filter(dbName => const appDbNames = allDbs.filter(dbName =>
dbName.startsWith(exports.APP_PREFIX) dbName.startsWith(exports.APP_PREFIX)

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "0.9.27", "version": "0.9.29",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -1,9 +1,9 @@
{ {
"baseUrl": "http://localhost:10000/builder/", "baseUrl": "http://localhost:10001/builder/",
"video": true, "video": true,
"projectId": "bmbemn", "projectId": "bmbemn",
"env": { "env": {
"PORT": "10000", "PORT": "10001",
"JWT_SECRET": "test" "JWT_SECRET": "test"
} }
} }

View File

@ -8,31 +8,27 @@ context("Create a automation", () => {
it("should create a automation", () => { it("should create a automation", () => {
cy.createTestTableWithData() cy.createTestTableWithData()
cy.contains("automate").click() cy.contains("Automate").click()
cy.get("[data-cy=new-automation]").click() cy.get("[data-cy='new-screen'] > .spectrum-Icon").click()
cy.get(".modal").within(() => { cy.get(".spectrum-Dialog-grid").within(() => {
cy.get("input").type("Add Row") cy.get("input").type("Add Row")
cy.get(".buttons") cy.get(".spectrum-Button--cta").click()
.contains("Create")
.click()
}) })
// Add trigger // Add trigger
cy.contains("Trigger").click() cy.contains("Trigger").click()
cy.contains("Row Created").click() cy.contains("Row Created").click()
cy.get(".setup").within(() => { cy.get(".setup").within(() => {
cy.get("select") cy.get(".spectrum-Picker-label").click()
.first() cy.contains("dog").click()
.select("dog")
}) })
// Create action // Create action
cy.contains("Action").click() cy.contains("Action").click()
cy.contains("Create Row").click() cy.contains("Create Row").click()
cy.get(".setup").within(() => { cy.get(".setup").within(() => {
cy.get("select") cy.get(".spectrum-Picker-label").click()
.first() cy.contains("dog").click()
.select("dog")
cy.get("input") cy.get("input")
.first() .first()
.type("goodboy") .type("goodboy")
@ -45,12 +41,11 @@ context("Create a automation", () => {
cy.contains("Save Automation").click() cy.contains("Save Automation").click()
// Activate Automation // Activate Automation
cy.get("[data-cy=activate-automation]").click() cy.get("[aria-label=PlayCircle]").click()
cy.get(".ri-stop-circle-fill.highlighted").should("be.visible")
}) })
it("should add row when a new row is added", () => { it("should add row when a new row is added", () => {
cy.contains("data").click() cy.contains("Data").click()
cy.addRow(["Rover", 15]) cy.addRow(["Rover", 15])
cy.reload() cy.reload()
cy.contains("goodboy").should("have.text", "goodboy") cy.contains("goodboy").should("have.text", "goodboy")

View File

@ -36,7 +36,9 @@ context("Create Bindings", () => {
it("should add a binding with a handlebars helper", () => { it("should add a binding with a handlebars helper", () => {
cy.addComponent("Elements", "Paragraph").then(componentId => { cy.addComponent("Elements", "Paragraph").then(componentId => {
// Cypress needs to escape curly brackets // Cypress needs to escape curly brackets
addSettingBinding("text", "{{}{{} add 1 2 {}}{}}", false) cy.get("[data-cy=setting-text] input")
.type("{{}{{} add 1 2 {}}{}}")
.blur()
cy.getComponent(componentId).should("have.text", "3") cy.getComponent(componentId).should("have.text", "3")
}) })
}) })
@ -51,6 +53,6 @@ const addSettingBinding = (setting, bindingText, clickOption = true) => {
} else { } else {
cy.get("textarea").type(bindingText) cy.get("textarea").type(bindingText)
} }
cy.get("button").click() cy.contains("Save").click()
}) })
} }

View File

@ -1,13 +1,14 @@
context("Create Components", () => { // TODO for now components are skipped, might not be good to keep doing this
xcontext("Create Components", () => {
let headlineId let headlineId
before(() => { before(() => {
cy.login() cy.login()
cy.createTestApp() cy.createTestApp()
cy.createTable("dog") cy.createTable("dog")
cy.addColumn("dog", "name", "string") cy.addColumn("dog", "name", "Text")
cy.addColumn("dog", "age", "number") cy.addColumn("dog", "age", "Number")
cy.addColumn("dog", "type", "options") cy.addColumn("dog", "type", "Options")
cy.navigateToFrontend() cy.navigateToFrontend()
}) })

View File

@ -22,54 +22,49 @@ context("Create a Table", () => {
}) })
it("updates a column on the table", () => { it("updates a column on the table", () => {
cy.contains("header", "name") cy.get(".title").click()
.trigger("mouseover") cy.get(".spectrum-Table-editIcon > use").click()
.find(".ri-pencil-line") cy.get("input")
.click({ force: true }) .eq(1)
cy.get(".actions input") .type("updated", { force: true })
.first()
.type("updated")
// Unset table display column // Unset table display column
cy.contains("display column").click() cy.get(".spectrum-Switch-input").eq(1).click()
cy.contains("Save Column").click() cy.contains("Save Column").click()
cy.contains("nameupdated ").should("have.text", "nameupdated ") cy.contains("nameupdated ").should("contain", "nameupdated")
}) })
it("edits a row", () => { it("edits a row", () => {
cy.contains("button", "Edit").click({ force: true }) cy.contains("button", "Edit").click({ force: true })
cy.wait(1000) cy.wait(1000)
cy.get(".modal input").type("Updated") cy.get(".spectrum-Modal input").type("Updated")
cy.contains("Save").click() cy.contains("Save").click()
cy.contains("RoverUpdated").should("have.text", "RoverUpdated") cy.contains("RoverUpdated").should("have.text", "RoverUpdated")
}) })
it("deletes a row", () => { it("deletes a row", () => {
cy.get(".ag-checkbox-input").check({ force: true }) cy.get(".spectrum-Checkbox-input").check({ force: true })
cy.contains("Delete 1 row(s)").click() cy.contains("Delete 1 row(s)").click()
cy.get(".modal") cy.get(".spectrum-Modal")
.contains("Delete") .contains("Delete")
.click() .click()
cy.contains("RoverUpdated").should("not.exist") cy.contains("RoverUpdated").should("not.exist")
}) })
it("deletes a column", () => { it("deletes a column", () => {
cy.contains("header", "name") cy.get(".title").click()
.trigger("mouseover") cy.get(".spectrum-Table-editIcon > use").click()
.find(".ri-pencil-line")
.click({ force: true })
cy.contains("Delete").click() cy.contains("Delete").click()
cy.wait(50) cy.wait(50)
cy.get(".buttons") cy.contains("Delete Column")
.contains("Delete")
.click() .click()
cy.contains("nameupdated").should("not.exist") cy.contains("nameupdated").should("not.exist")
}) })
it("deletes a table", () => { it("deletes a table", () => {
cy.get(".ri-more-line") cy.get(".actions > :nth-child(1) > .icon > .spectrum-Icon > use")
.first() .first()
.click({ force: true }) .click({ force: true })
cy.get("[data-cy=delete-table]").click() cy.get(".spectrum-Menu > :nth-child(2)").click()
cy.contains("Delete Table").click() cy.contains("Delete Table").click()
cy.contains("dog").should("not.exist") cy.contains("dog").should("not.exist")
}) })

View File

@ -1,11 +1,10 @@
context("Create a User", () => { context("Create a User", () => {
before(() => { before(() => {
cy.login() cy.login()
cy.createTestApp()
}) })
it("should create a user", () => { it("should create a user", () => {
cy.createUser("bbuser@test.com", "test", "ADMIN") cy.createUser("bbuser@test.com")
cy.contains("bbuser").should("be.visible") cy.contains("bbuser").should("be.visible")
}) })
}) })

View File

@ -17,21 +17,21 @@ context("Create a View", () => {
}) })
it("creates a view", () => { it("creates a view", () => {
cy.contains("Create New View").click() cy.contains("Create view").click()
cy.get(".menu-container").within(() => { cy.get(".modal-inner-wrapper").within(() => {
cy.get("input").type("Test View") cy.get("input").type("Test View")
cy.contains("Save View").click() cy.get("button").contains("Create View").click({ force: true })
}) })
cy.get(".table-title h1").contains("Test View") cy.get(".table-title h1").contains("Test View")
cy.get("[data-cy=table-header]").then($headers => { cy.get(".title").then($headers => {
expect($headers).to.have.length(3) expect($headers).to.have.length(3)
const headers = Array.from($headers).map(header => const headers = Array.from($headers).map(header =>
header.textContent.trim() header.textContent.trim()
) )
expect(removeSpacing(headers)).to.deep.eq([ expect(removeSpacing(headers)).to.deep.eq([
"rating Number", "group",
"age Number", "age",
"group Text", "rating",
]) ])
}) })
}) })
@ -39,97 +39,95 @@ context("Create a View", () => {
it("filters the view by age over 10", () => { it("filters the view by age over 10", () => {
cy.contains("Filter").click() cy.contains("Filter").click()
cy.contains("Add Filter").click() cy.contains("Add Filter").click()
cy.get(".menu-container")
.find("select") cy.get(".modal-inner-wrapper").within(() => {
.first() cy.get(".spectrum-Picker-label").eq(0).click()
.select("age") cy.contains("age").click({ force: true })
cy.get(".menu-container")
.find("select") cy.get(".spectrum-Picker-label").eq(1).click()
.eq(1) cy.contains("More Than").click({ force: true })
.select("More Than")
cy.get(".menu-container") cy.get("input").type(18)
.find("input") cy.contains("Save").click()
.type(18) })
cy.contains("Save").click()
cy.get("[role=rowgroup] .ag-row").get($values => { cy.get(".spectrum-Table-row").get($values => {
expect($values).to.have.length(5) expect($values).to.have.length(5)
}) })
}) })
it("creates a stats calculation view based on age", () => { it("creates a stats calculation view based on age", () => {
// Required due to responsive bug with ag grid in cypress
cy.viewport("macbook-15")
cy.contains("Calculate").click() cy.contains("Calculate").click()
cy.get(".menu-container") cy.get(".modal-inner-wrapper").within(() => {
.find("select") cy.get(".spectrum-Picker-label").eq(0).click()
.eq(0) cy.contains("Statistics").click()
.select("Statistics")
cy.wait(50) cy.get(".spectrum-Picker-label").eq(1).click()
cy.get(".menu-container") cy.contains("age").click({ force: true })
.find("select")
.eq(1) cy.contains("Save").click()
.select("age") })
cy.contains("Save").click()
cy.wait(1000) cy.wait(1000)
cy.get(".ag-center-cols-viewport").scrollTo("100%")
cy.get("[data-cy=table-header]").then($headers => { cy.get(".title").then($headers => {
expect($headers).to.have.length(7) expect($headers).to.have.length(7)
const headers = Array.from($headers).map(header => const headers = Array.from($headers).map(header =>
header.textContent.trim() header.textContent.trim()
) )
expect(removeSpacing(headers)).to.deep.eq([ expect(removeSpacing(headers)).to.deep.eq([
"avg Number", "field",
"sumsqr Number", "sum",
"count Number", "min",
"max Number", "max",
"min Number", "count",
"sum Number", "sumsqr",
"field Text", "avg",
]) ])
}) })
cy.get(".ag-cell").then($values => { cy.get(".spectrum-Table-cell").then($values => {
let values = Array.from($values).map(header => header.textContent.trim()) let values = Array.from($values).map(header => header.textContent.trim())
expect(values).to.deep.eq(["31", "5347", "5", "49", "20", "155", "age"]) expect(values).to.deep.eq(["age", "155", "20", "49", "5", "5347", "31"])
}) })
}) })
it("groups the view by group", () => { it("groups the view by group", () => {
// Required due to responsive bug with ag grid in cypress cy.contains("Group by").click()
cy.viewport("macbook-15") cy.get(".modal-inner-wrapper").within(() => {
cy.get(".spectrum-Picker-label").eq(0).click()
cy.contains("Group By").click() cy.contains("group").click()
cy.get("select").select("group") cy.contains("Save").click()
cy.contains("Save").click() })
cy.wait(1000) cy.wait(1000)
cy.get(".ag-center-cols-viewport").scrollTo("100%")
cy.contains("Students").should("be.visible") cy.contains("Students").should("be.visible")
cy.contains("Teachers").should("be.visible") cy.contains("Teachers").should("be.visible")
cy.get(".ag-row[row-index=0]") cy.get(".spectrum-Table-cell").then($values => {
.find(".ag-cell") let values = Array.from($values).map(header => header.textContent.trim())
.then($values => { expect(values).to.deep.eq([
const values = Array.from($values).map(value => value.textContent)
expect(values.sort()).to.deep.eq(
[
"Students", "Students",
"23.333333333333332",
"1650",
"3",
"25",
"20",
"70", "70",
].sort() "20",
) "25",
}) "3",
"1650",
"23.333333333333332",
"Teachers",
"85",
"36",
"49",
"2",
"3697",
"42.5",
])
})
}) })
it("renames a view", () => { it("renames a view", () => {
cy.contains(".nav-item", "Test View") cy.contains(".nav-item", "Test View")
.find(".ri-more-line") .find(".actions .icon")
.click({ force: true }) .click({ force: true })
cy.get("[data-cy=edit-view]").click() cy.contains("Edit").click()
cy.get(".menu-container").within(() => { cy.get(".modal-inner-wrapper").within(() => {
cy.get("input").type(" Updated") cy.get("input").type(" Updated")
cy.contains("Save").click() cy.contains("Save").click()
}) })
@ -139,9 +137,9 @@ context("Create a View", () => {
it("deletes a view", () => { it("deletes a view", () => {
cy.contains(".nav-item", "Test View Updated") cy.contains(".nav-item", "Test View Updated")
.find(".ri-more-line") .find(".actions .icon")
.click({ force: true }) .click({ force: true })
cy.get("[data-cy=delete-view]").click() cy.contains("Delete").click()
cy.contains("Delete View").click() cy.contains("Delete View").click()
cy.wait(1000) cy.wait(1000)
cy.contains("TestView Updated").should("not.be.visible") cy.contains("TestView Updated").should("not.be.visible")

View File

@ -3,7 +3,9 @@ const path = require("path")
const tmpdir = path.join(require("os").tmpdir(), ".budibase") const tmpdir = path.join(require("os").tmpdir(), ".budibase")
const WORKER_PORT = "4002" // these run on ports we don't normally use so that they can run alongside the
// normal development system
const WORKER_PORT = "10002"
const MAIN_PORT = cypressConfig.env.PORT const MAIN_PORT = cypressConfig.env.PORT
process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE" process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE"
process.env.NODE_ENV = "cypress" process.env.NODE_ENV = "cypress"
@ -12,8 +14,8 @@ process.env.PORT = MAIN_PORT
process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/` process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
process.env.SELF_HOSTED = 1 process.env.SELF_HOSTED = 1
process.env.WORKER_URL = "http://localhost:4002/" process.env.WORKER_URL = "http://localhost:10002/"
process.env.MINIO_URL = "http://localhost:10000/" process.env.MINIO_URL = `http://localhost:${MAIN_PORT}/`
process.env.MINIO_ACCESS_KEY = "budibase" process.env.MINIO_ACCESS_KEY = "budibase"
process.env.MINIO_SECRET_KEY = "budibase" process.env.MINIO_SECRET_KEY = "budibase"
process.env.COUCH_DB_USER = "budibase" process.env.COUCH_DB_USER = "budibase"

View File

@ -6,80 +6,61 @@
// //
Cypress.Commands.add("login", () => { Cypress.Commands.add("login", () => {
cy.getCookie("budibase:auth").then(cookie => {
// Already logged in
if (cookie) return
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
// cy.get("button").then(btn => {
// const adminUserButton = "Create super admin user"
// console.log(btn.first().first())
// if (!btn.first().contains(adminUserButton)) {
// // create admin user
// cy.get("input").first().type("test@test.com")
// cy.get('input[type="password"]').first().type("test")
// cy.get('input[type="password"]').eq(1).type("test")
// cy.contains(adminUserButton).click()
// }
// login
cy.get("input").first().type("test@test.com")
cy.get('input[type="password"]').type("test")
cy.contains("Login").click()
// })
})
})
Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
// wait for init API calls on visit cy.wait(500)
cy.wait(100) cy.url().then(url => {
cy.contains("Create New Web App").click() if (url.includes("builder/admin")) {
cy.get("body") // create admin user
.then($body => { cy.get("input").first().type("test@test.com")
if ($body.find("input[name=apiKey]").length) { cy.get('input[type="password"]').first().type("test")
// input was found, do something else here cy.get('input[type="password"]').eq(1).type("test")
cy.get("input[name=apiKey]").type(name).should("have.value", name) cy.contains("Create super admin user").click()
cy.contains("Next").click() }
} if (url.includes("builder/auth/login") || url.includes("builder/admin")) {
}) // login
.then(() => { cy.contains("Sign in to Budibase").then(() => {
cy.get(".spectrum-Modal") cy.get("input").first().type("test@test.com")
.within(() => { cy.get('input[type="password"]').type("test")
cy.get("input").eq(0).type(name).should("have.value", name).blur() cy.get("button").first().click()
cy.contains("Next").click()
cy.get("input").eq(1).type("test@test.com").blur()
cy.get("input").eq(2).type("test").blur()
cy.contains("Submit").click()
})
.then(() => {
cy.get("[data-cy=new-table]", {
timeout: 20000,
}).should("be.visible")
})
})
})
Cypress.Commands.add("deleteApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.get(".apps").then($apps => {
cy.wait(1000)
if ($apps.find(`[data-cy="app-${name}"]`).length) {
cy.get(`[data-cy="app-${name}"]`).contains("Open").click()
cy.get("[data-cy=settings-icon]").click()
cy.get(".spectrum-Dialog").within(() => {
cy.contains("Danger Zone").click()
cy.get("input").type("DELETE").blur()
cy.contains("Delete Entire App").click()
}) })
} }
}) })
}) })
Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.wait(500)
cy.contains(/Create (new )?app/).click()
cy.get(".spectrum-Modal")
.within(() => {
cy.get("input").eq(0).type(name).should("have.value", name).blur()
cy.contains("Create app").click()
})
.then(() => {
cy.get("[data-cy=new-table]", {
timeout: 20000,
}).should("be.visible")
})
})
Cypress.Commands.add("deleteApp", () => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.wait(1000)
cy.request(`localhost:${Cypress.env("PORT")}/api/applications?status=all`)
.its("body")
.then(val => {
console.log(val)
if (val.length > 0) {
cy.get(".hoverable > use").click()
cy.contains("Delete").click()
cy.get(".spectrum-Button--warning").click()
}
})
})
Cypress.Commands.add("createTestApp", () => { Cypress.Commands.add("createTestApp", () => {
const appName = "Cypress Tests" const appName = "Cypress Tests"
cy.deleteApp(appName) cy.deleteApp()
cy.createApp(appName, "This app is used for Cypress testing.") cy.createApp(appName, "This app is used for Cypress testing.")
}) })
@ -110,8 +91,10 @@ Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
// Unset table display column // Unset table display column
cy.contains("display column").click({ force: true }) cy.contains("display column").click({ force: true })
cy.get("select").select(type) cy.get(".spectrum-Picker-label").click()
cy.contains("Save").click() cy.contains(type).click()
cy.contains("Save Column").click()
}) })
}) })
@ -125,18 +108,18 @@ Cypress.Commands.add("addRow", values => {
}) })
}) })
Cypress.Commands.add("createUser", (email, password, role) => { Cypress.Commands.add("createUser", email => {
// Create User // quick hacky recorded way to create a user
cy.contains("Users").click() cy.contains("Users").click()
cy.contains("Create user").click() cy.get(".spectrum-Button--primary").click()
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Picker-label").click()
cy.get("input").first().type(email).blur() cy.get(".spectrum-Menu-item:nth-child(2) > .spectrum-Menu-itemLabel").click()
cy.get("input").eq(1).type(password).blur() cy.get(
cy.get("select").first().select(role) ":nth-child(2) > .spectrum-Form-itemField > .spectrum-Textfield > .spectrum-Textfield-input"
)
// Save .first()
cy.get(".spectrum-ButtonGroup").contains("Create User").click() .type(email, { force: true })
}) cy.get(".spectrum-Button--cta").click({ force: true })
}) })
Cypress.Commands.add("addComponent", (category, component) => { Cypress.Commands.add("addComponent", (category, component) => {
@ -165,17 +148,16 @@ Cypress.Commands.add("getComponent", componentId => {
}) })
Cypress.Commands.add("navigateToFrontend", () => { Cypress.Commands.add("navigateToFrontend", () => {
cy.contains("design").click() cy.wait(1000)
cy.contains("Design").click()
}) })
Cypress.Commands.add("createScreen", (screenName, route) => { Cypress.Commands.add("createScreen", (screenName, route) => {
cy.get("[data-cy=new-screen]").click() cy.get("[aria-label=AddCircle]").click()
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Modal").within(() => {
cy.get("input").eq(0).type(screenName).blur() cy.get("input").first().type(screenName)
if (route) { cy.get("input").eq(1).type(route)
cy.get("input").eq(1).type(route).blur() cy.get(".spectrum-Button--cta").click()
}
cy.contains("Create Screen").click()
}) })
cy.get(".nav-items-container").within(() => { cy.get(".nav-items-container").within(() => {
cy.contains(route).should("exist") cy.contains(route).should("exist")

View File

@ -14,8 +14,8 @@
// *********************************************************** // ***********************************************************
// Import commands.js using ES2015 syntax: // Import commands.js using ES2015 syntax:
import "./cookies"
import "./commands" import "./commands"
import "./cookies"
// Alternatively you can use CommonJS syntax: // Alternatively you can use CommonJS syntax:
// require('./commands') // require('./commands')

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.27", "version": "0.9.29",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -15,9 +15,9 @@
"cy:run": "cypress run", "cy:run": "cypress run",
"cy:open": "cypress open", "cy:open": "cypress open",
"cy:run:ci": "cypress run --record --key f308590b-6070-41af-b970-794a3823d451", "cy:run:ci": "cypress run --record --key f308590b-6070-41af-b970-794a3823d451",
"cy:test": "start-server-and-test cy:setup http://localhost:10000/builder cy:run", "cy:test": "start-server-and-test cy:setup http://localhost:10001/builder cy:run",
"cy:ci": "start-server-and-test cy:setup http://localhost:10000/builder cy:run:ci", "cy:ci": "start-server-and-test cy:setup http://localhost:10001/builder cy:run:ci",
"cy:debug": "start-server-and-test cy:setup http://localhost:10000/builder cy:open" "cy:debug": "start-server-and-test cy:setup http://localhost:10001/builder cy:open"
}, },
"jest": { "jest": {
"globals": { "globals": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.27", "@budibase/bbui": "^0.9.29",
"@budibase/client": "^0.9.27", "@budibase/client": "^0.9.29",
"@budibase/colorpicker": "1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.27", "@budibase/string-templates": "^0.9.29",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -100,6 +100,7 @@
tables.deleteField(field) tables.deleteField(field)
notifications.success(`Column ${field.name} deleted.`) notifications.success(`Column ${field.name} deleted.`)
confirmDeleteDialog.hide() confirmDeleteDialog.hide()
hide()
deletion = false deletion = false
} }
} }

View File

@ -101,6 +101,12 @@ export function createTablesStore() {
// Optionally set display column // Optionally set display column
if (primaryDisplay) { if (primaryDisplay) {
state.draft.primaryDisplay = field.name state.draft.primaryDisplay = field.name
} else if (state.draft.primaryDisplay === originalName) {
const fields = Object.keys(state.draft.schema)
// pick another display column randomly if unselecting
state.draft.primaryDisplay = fields.filter(
name => name !== originalName || name !== field
)[0]
} }
if (indexes) { if (indexes) {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.27", "version": "0.9.29",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.27", "version": "0.9.29",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -18,13 +18,13 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/string-templates": "^0.9.27", "@budibase/string-templates": "^0.9.29",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@budibase/standard-components": "^0.9.27", "@budibase/standard-components": "^0.9.29",
"@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-node-resolve": "^11.2.1",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.27", "version": "0.9.29",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/electron.js", "main": "src/electron.js",
"repository": { "repository": {
@ -55,9 +55,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.27", "@budibase/auth": "^0.9.29",
"@budibase/client": "^0.9.27", "@budibase/client": "^0.9.29",
"@budibase/string-templates": "^0.9.27", "@budibase/string-templates": "^0.9.29",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",
@ -109,7 +109,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4", "@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.27", "@budibase/standard-components": "^0.9.29",
"@jest/test-sequencer": "^24.8.0", "@jest/test-sequencer": "^24.8.0",
"babel-jest": "^27.0.2", "babel-jest": "^27.0.2",
"docker-compose": "^0.23.6", "docker-compose": "^0.23.6",

View File

@ -127,7 +127,7 @@ async function createInstance(template) {
exports.fetch = async function (ctx) { exports.fetch = async function (ctx) {
const dev = ctx.query && ctx.query.status === AppStatus.DEV const dev = ctx.query && ctx.query.status === AppStatus.DEV
const all = ctx.query && ctx.query.status === AppStatus.ALL const all = ctx.query && ctx.query.status === AppStatus.ALL
const apps = await getAllApps({ dev, all }) const apps = await getAllApps({ CouchDB, dev, all })
// get the locks for all the dev apps // get the locks for all the dev apps
if (dev || all) { if (dev || all) {
@ -203,9 +203,6 @@ exports.create = async function (ctx) {
instance: instance, instance: instance,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
deployment: {
type: "cloud",
},
} }
if (instance._rev) { if (instance._rev) {
newApplication._rev = instance._rev newApplication._rev = instance._rev

View File

@ -75,8 +75,6 @@ exports.save = async function (ctx) {
/* istanbul ignore next */ /* istanbul ignore next */
if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) { if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
ctx.throw(400, "Cannot rename a linked column.") ctx.throw(400, "Cannot rename a linked column.")
} else if (_rename && tableToSave.primaryDisplay === _rename.old) {
ctx.throw(400, "Cannot rename the display column.")
} }
tableToSave = await tableSaveFunctions.mid(tableToSave) tableToSave = await tableSaveFunctions.mid(tableToSave)

View File

@ -37,7 +37,37 @@ describe("run misc tests", () => {
describe("test table utilities", () => { describe("test table utilities", () => {
it("should be able to import a CSV", async () => { it("should be able to import a CSV", async () => {
const table = await config.createTable() const table = await config.createTable({
name: "table",
type: "table",
key: "name",
schema: {
a: {
type: "string",
constraints: {
type: "string",
},
},
b: {
type: "string",
constraints: {
type: "string",
},
},
c: {
type: "string",
constraints: {
type: "string",
},
},
d: {
type: "string",
constraints: {
type: "string",
},
},
},
})
const dataImport = { const dataImport = {
csvString: "a,b,c,d\n1,2,3,4" csvString: "a,b,c,d\n1,2,3,4"
} }

View File

@ -125,6 +125,7 @@ describe("/rows", () => {
numberNull: number, numberNull: number,
numberUndefined: number, numberUndefined: number,
numberString: number, numberString: number,
numberNumber: number,
datetimeEmptyString: datetime, datetimeEmptyString: datetime,
datetimeNull: datetime, datetimeNull: datetime,
datetimeUndefined: datetime, datetimeUndefined: datetime,

View File

@ -1,5 +1,25 @@
const setup = require("./utilities") const setup = require("./utilities")
function priceTable() {
return {
name: "table",
type: "table",
key: "name",
schema: {
Price: {
type: "number",
constraints: {},
},
Category: {
type: "string",
constraints: {
type: "string",
},
},
},
}
}
describe("/views", () => { describe("/views", () => {
let request = setup.getRequest() let request = setup.getRequest()
let config = setup.getConfig() let config = setup.getConfig()
@ -13,7 +33,7 @@ describe("/views", () => {
describe("create", () => { describe("create", () => {
beforeEach(async () => { beforeEach(async () => {
table = await config.createTable() table = await config.createTable(priceTable())
}) })
it("returns a success message when the view is successfully created", async () => { it("returns a success message when the view is successfully created", async () => {
@ -83,7 +103,7 @@ describe("/views", () => {
describe("fetch", () => { describe("fetch", () => {
beforeEach(async () => { beforeEach(async () => {
table = await config.createTable() table = await config.createTable(priceTable())
}) })
it("returns only custom views", async () => { it("returns only custom views", async () => {
@ -105,7 +125,7 @@ describe("/views", () => {
describe("query", () => { describe("query", () => {
beforeEach(async () => { beforeEach(async () => {
table = await config.createTable() table = await config.createTable(priceTable())
}) })
it("returns data for the created view", async () => { it("returns data for the created view", async () => {
@ -172,7 +192,7 @@ describe("/views", () => {
describe("destroy", () => { describe("destroy", () => {
it("should be able to delete a view", async () => { it("should be able to delete a view", async () => {
const table = await config.createTable() const table = await config.createTable(priceTable())
const view = await config.createView() const view = await config.createView()
const res = await request const res = await request
.delete(`/api/views/${view.name}`) .delete(`/api/views/${view.name}`)
@ -186,7 +206,7 @@ describe("/views", () => {
describe("exportView", () => { describe("exportView", () => {
it("should be able to delete a view", async () => { it("should be able to delete a view", async () => {
await config.createTable() await config.createTable(priceTable())
await config.createRow() await config.createRow()
const view = await config.createView() const view = await config.createView()
let res = await request let res = await request

View File

@ -46,7 +46,6 @@ exports.basicRow = tableId => {
return { return {
name: "Test Contact", name: "Test Contact",
description: "original description", description: "original description",
status: "new",
tableId: tableId, tableId: tableId,
} }
} }

View File

@ -29,11 +29,11 @@
"keywords": [ "keywords": [
"svelte" "svelte"
], ],
"version": "0.9.27", "version": "0.9.29",
"license": "MIT", "license": "MIT",
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.27", "@budibase/bbui": "^0.9.29",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",
"apexcharts": "^3.22.1", "apexcharts": "^3.22.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.27", "version": "0.9.29",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -16,6 +16,9 @@ registerAll(hbsInstance)
* utility function to check if the object is valid * utility function to check if the object is valid
*/ */
function testObject(object) { function testObject(object) {
if (object == null) {
throw "Unable to process null object"
}
// JSON stringify will fail if there are any cycles, stops infinite recursion // JSON stringify will fail if there are any cycles, stops infinite recursion
try { try {
JSON.stringify(object) JSON.stringify(object)

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.27", "version": "0.9.29",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -21,8 +21,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.27", "@budibase/auth": "^0.9.29",
"@budibase/string-templates": "^0.9.27", "@budibase/string-templates": "^0.9.29",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"aws-sdk": "^2.811.0", "aws-sdk": "^2.811.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",

View File

@ -8,7 +8,7 @@ const CouchDB = require("../../../db")
exports.fetch = async ctx => { exports.fetch = async ctx => {
// always use the dev apps as they'll be most up to date (true) // always use the dev apps as they'll be most up to date (true)
const apps = await getAllApps({ dev: true }) const apps = await getAllApps({ CouchDB, dev: true })
const promises = [] const promises = []
for (let app of apps) { for (let app of apps) {
// use dev app IDs // use dev app IDs