forked from tsai/budibase
committed by
GitHub
37 changed files with 1090 additions and 1092 deletions
@ -1,8 +1,8 @@ |
|||||
{ |
{ |
||||
"baseUrl": "http://localhost:4005/_builder/", |
"baseUrl": "http://localhost:4001/_builder/", |
||||
"video": true, |
"video": true, |
||||
"projectId": "bmbemn", |
"projectId": "bmbemn", |
||||
"env": { |
"env": { |
||||
"PORT": "4005" |
"PORT": "4001" |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,18 +1,7 @@ |
|||||
context('Create an Application', () => { |
context("Create an Application", () => { |
||||
|
it("should create a new application", () => { |
||||
beforeEach(() => { |
cy.createTestApp() |
||||
cy.server() |
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) |
||||
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) |
cy.contains("Cypress Tests").should("exist") |
||||
}) |
}) |
||||
|
|
||||
// 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') |
|
||||
}) |
|
||||
}) |
}) |
||||
|
|||||
@ -1,18 +1,59 @@ |
|||||
xcontext('Create a Binding', () => { |
context("Create Bindings", () => { |
||||
before(() => { |
before(() => { |
||||
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) |
cy.createTestApp() |
||||
cy.createApp('Binding App', 'Binding App Description') |
cy.navigateToFrontend() |
||||
cy.navigateToFrontend() |
}) |
||||
|
|
||||
|
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('add an input binding', () => { |
it("should add a URL param binding", () => { |
||||
cy.get(".nav-items-container").contains('Home').click() |
const paramName = "foo" |
||||
cy.contains("Add").click() |
cy.createScreen("Test Param", `/test/:${paramName}`) |
||||
cy.get("[data-cy=Input]").click() |
cy.addComponent("Elements", "Paragraph").then(componentId => { |
||||
cy.get("[data-cy=Textfield]").click() |
addSettingBinding("text", `URL.${paramName}`) |
||||
cy.contains("Heading").click() |
// The builder preview pages don't have a real URL, so all we can do
|
||||
cy.get("[data-cy=text-binding-button]").click() |
// is check that we were able to bind to the property, and that the
|
||||
cy.get("[data-cy=binding-dropdown-modal]").contains('Input 1').click() |
// component exists on the page
|
||||
cy.get("[data-cy=binding-dropdown-modal] textarea").should('have.value', 'Home{{ Input 1 }}') |
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() |
||||
|
}) |
||||
|
} |
||||
|
|||||
@ -1,60 +1,90 @@ |
|||||
xcontext("Create Components", () => { |
context("Create Components", () => { |
||||
|
let headlineId |
||||
|
|
||||
before(() => { |
before(() => { |
||||
cy.server() |
cy.createTestApp() |
||||
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) |
cy.createTable("dog") |
||||
// https://on.cypress.io/type
|
cy.addColumn("dog", "name", "string") |
||||
cy.createApp("Table App", "Table App Description") |
cy.addColumn("dog", "age", "number") |
||||
cy.createTable("dog", "name", "age") |
cy.addColumn("dog", "type", "options") |
||||
cy.addRow("bob", "15") |
cy.navigateToFrontend() |
||||
}) |
}) |
||||
|
|
||||
// https://on.cypress.io/interacting-with-elements
|
|
||||
it("should add a container", () => { |
it("should add a container", () => { |
||||
cy.navigateToFrontend() |
cy.addComponent(null, "Container").then(componentId => { |
||||
cy.get(".switcher > :nth-child(2)").click() |
cy.getComponent(componentId).should("exist") |
||||
cy.contains("Container").click() |
}) |
||||
}) |
}) |
||||
|
|
||||
it("should add a headline", () => { |
it("should add a headline", () => { |
||||
cy.addHeadlineComponent("An Amazing headline!") |
cy.addComponent("Elements", "Headline").then(componentId => { |
||||
|
headlineId = componentId |
||||
|
cy.getComponent(headlineId).should("exist") |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
getIframeBody().contains("An Amazing headline!") |
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("change the font size of the headline", () => { |
|
||||
|
it("should change the size of the headline", () => { |
||||
|
cy.get("[data-cy=Design]").click() |
||||
cy.contains("Typography").click() |
cy.contains("Typography").click() |
||||
cy.get("[data-cy=font-size-prop-control]").click() |
cy.get("[data-cy=font-size-prop-control]").click() |
||||
cy.contains("60px").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") |
|
||||
}) |
}) |
||||
}) |
|
||||
|
|
||||
const getIframeDocument = () => { |
it("should create a form and reset to match schema", () => { |
||||
return ( |
cy.addComponent("Form", "Form").then(() => { |
||||
cy |
cy.get("[data-cy=Settings]").click() |
||||
.get("iframe") |
cy.get("[data-cy=setting-datasource]") |
||||
// Cypress yields jQuery element, which has the real
|
.contains("Choose option") |
||||
// DOM element under property "0".
|
.click() |
||||
// From the real DOM iframe element we can get
|
cy.get(".dropdown") |
||||
// the "document" element, it is stored in "contentDocument" property
|
.contains("dog") |
||||
// Cypress "its" command can access deep properties using dot notation
|
.click() |
||||
// https://on.cypress.io/its
|
cy.addComponent("Form", "Field Group").then(fieldGroupId => { |
||||
.its("0.contentDocument") |
cy.get("[data-cy=Settings]").click() |
||||
.should("exist") |
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) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
const getIframeBody = () => { |
it("deletes a component", () => { |
||||
// get the document
|
cy.addComponent("Elements", "Paragraph").then(componentId => { |
||||
return ( |
cy.get("[data-cy=setting-_instanceName] input") |
||||
getIframeDocument() |
.type(componentId) |
||||
// automatically retries until body is loaded
|
.blur() |
||||
.its("body") |
cy.get(".ui-nav ul .nav-item.selected .ri-more-line").click({ |
||||
.should("not.be.undefined") |
force: true, |
||||
// wraps "body" DOM element to allow
|
}) |
||||
// chaining more Cypress commands, like ".find(...)"
|
cy.get(".dropdown-container") |
||||
.then(cy.wrap) |
.contains("Delete") |
||||
) |
.click() |
||||
} |
cy.get(".modal") |
||||
|
.contains("Delete Component") |
||||
|
.click() |
||||
|
cy.getComponent(componentId).should("not.exist") |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
|||||
@ -0,0 +1,10 @@ |
|||||
|
context("Screen Tests", () => { |
||||
|
before(() => { |
||||
|
cy.createTestApp() |
||||
|
cy.navigateToFrontend() |
||||
|
}) |
||||
|
|
||||
|
it("Should successfully create a screen", () => { |
||||
|
cy.createScreen("Test Screen", "/test") |
||||
|
}) |
||||
|
}) |
||||
@ -1,17 +1,10 @@ |
|||||
context('Create a User', () => { |
context("Create a User", () => { |
||||
|
before(() => { |
||||
|
cy.createTestApp() |
||||
|
}) |
||||
|
|
||||
before(() => { |
it("should create a user", () => { |
||||
cy.server() |
cy.createUser("bbuser@test.com", "test", "ADMIN") |
||||
cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) |
cy.contains("bbuser").should("be.visible") |
||||
// 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') |
|
||||
}) |
|
||||
}) |
}) |
||||
|
|||||
@ -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") |
|
||||
}) |
|
||||
}) |
|
||||
@ -1,326 +0,0 @@ |
|||||
const CouchDB = require("../../../db") |
|
||||
const supertest = require("supertest") |
|
||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") |
|
||||
const packageJson = require("../../../../package") |
|
||||
const jwt = require("jsonwebtoken") |
|
||||
const env = require("../../../environment") |
|
||||
const { |
|
||||
BUILTIN_PERMISSION_IDS, |
|
||||
} = require("../../../utilities/security/permissions") |
|
||||
|
|
||||
const TEST_CLIENT_ID = "test-client-id" |
|
||||
|
|
||||
exports.TEST_CLIENT_ID = TEST_CLIENT_ID |
|
||||
exports.supertest = async () => { |
|
||||
let request |
|
||||
let server |
|
||||
env.PORT = 4002 |
|
||||
server = require("../../../app") |
|
||||
|
|
||||
request = supertest(server) |
|
||||
return { request, server } |
|
||||
} |
|
||||
|
|
||||
exports.defaultHeaders = appId => { |
|
||||
const builderUser = { |
|
||||
userId: "BUILDER", |
|
||||
roleId: BUILTIN_ROLE_IDS.BUILDER, |
|
||||
} |
|
||||
|
|
||||
const builderToken = jwt.sign(builderUser, env.JWT_SECRET) |
|
||||
|
|
||||
const headers = { |
|
||||
Accept: "application/json", |
|
||||
Cookie: [`budibase:builder:local=${builderToken}`], |
|
||||
} |
|
||||
if (appId) { |
|
||||
headers["x-budibase-app-id"] = appId |
|
||||
} |
|
||||
|
|
||||
return headers |
|
||||
} |
|
||||
|
|
||||
exports.publicHeaders = appId => { |
|
||||
const headers = { |
|
||||
Accept: "application/json", |
|
||||
} |
|
||||
if (appId) { |
|
||||
headers["x-budibase-app-id"] = appId |
|
||||
} |
|
||||
|
|
||||
return headers |
|
||||
} |
|
||||
|
|
||||
exports.BASE_TABLE = { |
|
||||
name: "TestTable", |
|
||||
type: "table", |
|
||||
key: "name", |
|
||||
schema: { |
|
||||
name: { |
|
||||
type: "string", |
|
||||
constraints: { |
|
||||
type: "string", |
|
||||
}, |
|
||||
}, |
|
||||
description: { |
|
||||
type: "string", |
|
||||
constraints: { |
|
||||
type: "string", |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
exports.createTable = async (request, appId, table, removeId = true) => { |
|
||||
if (removeId && table != null && table._id) { |
|
||||
delete table._id |
|
||||
} |
|
||||
table = table || exports.BASE_TABLE |
|
||||
|
|
||||
const res = await request |
|
||||
.post(`/api/tables`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.send(table) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.makeBasicRow = tableId => { |
|
||||
return { |
|
||||
name: "Test Contact", |
|
||||
description: "original description", |
|
||||
status: "new", |
|
||||
tableId: tableId, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
exports.createRow = async (request, appId, tableId, row = null) => { |
|
||||
row = row || exports.makeBasicRow(tableId) |
|
||||
const res = await request |
|
||||
.post(`/api/${tableId}/rows`) |
|
||||
.send(row) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.expect("Content-Type", /json/) |
|
||||
.expect(200) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.createRole = async (request, appId) => { |
|
||||
const roleBody = { |
|
||||
name: "NewRole", |
|
||||
inherits: BUILTIN_ROLE_IDS.BASIC, |
|
||||
permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY, |
|
||||
} |
|
||||
const res = await request |
|
||||
.post(`/api/roles`) |
|
||||
.send(roleBody) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.expect("Content-Type", /json/) |
|
||||
.expect(200) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.addPermission = async ( |
|
||||
request, |
|
||||
appId, |
|
||||
role, |
|
||||
resource, |
|
||||
level = "read" |
|
||||
) => { |
|
||||
const res = await request |
|
||||
.post(`/api/permission/${role}/${resource}/${level}`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.expect("Content-Type", /json/) |
|
||||
.expect(200) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.createLinkedTable = async (request, appId) => { |
|
||||
// get the ID to link to
|
|
||||
const table = await exports.createTable(request, appId) |
|
||||
table.primaryDisplay = "name" |
|
||||
table.schema.link = { |
|
||||
type: "link", |
|
||||
fieldName: "link", |
|
||||
tableId: table._id, |
|
||||
} |
|
||||
return exports.createTable(request, appId, table, false) |
|
||||
} |
|
||||
|
|
||||
exports.createAttachmentTable = async (request, appId) => { |
|
||||
const table = await exports.createTable(request, appId) |
|
||||
table.schema.attachment = { |
|
||||
type: "attachment", |
|
||||
} |
|
||||
return exports.createTable(request, appId, table, false) |
|
||||
} |
|
||||
|
|
||||
exports.getAllFromTable = async (request, appId, tableId) => { |
|
||||
const res = await request |
|
||||
.get(`/api/${tableId}/rows`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.createView = async (request, appId, tableId, view) => { |
|
||||
view = view || { |
|
||||
map: "function(doc) { emit(doc[doc.key], doc._id); } ", |
|
||||
tableId: tableId, |
|
||||
} |
|
||||
|
|
||||
const res = await request |
|
||||
.post(`/api/views`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.send(view) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.createApplication = async (request, name = "test_application") => { |
|
||||
const res = await request |
|
||||
.post("/api/applications") |
|
||||
.send({ |
|
||||
name, |
|
||||
}) |
|
||||
.set(exports.defaultHeaders()) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
exports.clearApplications = async request => { |
|
||||
const res = await request |
|
||||
.get("/api/applications") |
|
||||
.set(exports.defaultHeaders()) |
|
||||
for (let app of res.body) { |
|
||||
const appId = app._id |
|
||||
await request |
|
||||
.delete(`/api/applications/${appId}`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
exports.createUser = async ( |
|
||||
request, |
|
||||
appId, |
|
||||
email = "babs@babs.com", |
|
||||
password = "babs_password" |
|
||||
) => { |
|
||||
const res = await request |
|
||||
.post(`/api/users`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.send({ |
|
||||
name: "Bill", |
|
||||
email, |
|
||||
password, |
|
||||
roleId: BUILTIN_ROLE_IDS.POWER, |
|
||||
}) |
|
||||
return res.body |
|
||||
} |
|
||||
|
|
||||
const createUserWithRole = async (request, appId, roleId, email) => { |
|
||||
const password = `password_${email}` |
|
||||
await request |
|
||||
.post(`/api/users`) |
|
||||
.set(exports.defaultHeaders(appId)) |
|
||||
.send({ |
|
||||
email, |
|
||||
password, |
|
||||
roleId, |
|
||||
}) |
|
||||
|
|
||||
const anonUser = { |
|
||||
userId: "ANON", |
|
||||
roleId: BUILTIN_ROLE_IDS.PUBLIC, |
|
||||
appId: appId, |
|
||||
version: packageJson.version, |
|
||||
} |
|
||||
|
|
||||
const anonToken = jwt.sign(anonUser, env.JWT_SECRET) |
|
||||
|
|
||||
const loginResult = await request |
|
||||
.post(`/api/authenticate`) |
|
||||
.set({ |
|
||||
Cookie: `budibase:${appId}:local=${anonToken}`, |
|
||||
"x-budibase-app-id": appId, |
|
||||
}) |
|
||||
.send({ email, password }) |
|
||||
|
|
||||
// returning necessary request headers
|
|
||||
return { |
|
||||
Accept: "application/json", |
|
||||
Cookie: loginResult.headers["set-cookie"], |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
exports.testPermissionsForEndpoint = async ({ |
|
||||
request, |
|
||||
method, |
|
||||
url, |
|
||||
body, |
|
||||
appId, |
|
||||
passRole, |
|
||||
failRole, |
|
||||
}) => { |
|
||||
const passHeader = await createUserWithRole( |
|
||||
request, |
|
||||
appId, |
|
||||
passRole, |
|
||||
"passUser@budibase.com" |
|
||||
) |
|
||||
|
|
||||
await createRequest(request, method, url, body) |
|
||||
.set(passHeader) |
|
||||
.expect(200) |
|
||||
|
|
||||
const failHeader = await createUserWithRole( |
|
||||
request, |
|
||||
appId, |
|
||||
failRole, |
|
||||
"failUser@budibase.com" |
|
||||
) |
|
||||
|
|
||||
await createRequest(request, method, url, body) |
|
||||
.set(failHeader) |
|
||||
.expect(403) |
|
||||
} |
|
||||
|
|
||||
exports.builderEndpointShouldBlockNormalUsers = async ({ |
|
||||
request, |
|
||||
method, |
|
||||
url, |
|
||||
body, |
|
||||
appId, |
|
||||
}) => { |
|
||||
const headers = await createUserWithRole( |
|
||||
request, |
|
||||
appId, |
|
||||
BUILTIN_ROLE_IDS.BASIC, |
|
||||
"basicUser@budibase.com" |
|
||||
) |
|
||||
|
|
||||
await createRequest(request, method, url, body) |
|
||||
.set(headers) |
|
||||
.expect(403) |
|
||||
} |
|
||||
|
|
||||
const createRequest = (request, method, url, body) => { |
|
||||
let req |
|
||||
|
|
||||
if (method === "POST") req = request.post(url).send(body) |
|
||||
else if (method === "GET") req = request.get(url) |
|
||||
else if (method === "DELETE") req = request.delete(url) |
|
||||
else if (method === "PATCH") req = request.patch(url).send(body) |
|
||||
else if (method === "PUT") req = request.put(url).send(body) |
|
||||
|
|
||||
return req |
|
||||
} |
|
||||
|
|
||||
exports.insertDocument = async (databaseId, document) => { |
|
||||
const { id, ...documentFields } = document |
|
||||
return await new CouchDB(databaseId).put({ _id: id, ...documentFields }) |
|
||||
} |
|
||||
|
|
||||
exports.destroyDocument = async (databaseId, documentId) => { |
|
||||
return await new CouchDB(databaseId).destroy(documentId) |
|
||||
} |
|
||||
|
|
||||
exports.getDocument = async (databaseId, documentId) => { |
|
||||
return await new CouchDB(databaseId).get(documentId) |
|
||||
} |
|
||||
@ -1 +0,0 @@ |
|||||
module.exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) |
|
||||
@ -0,0 +1,248 @@ |
|||||
|
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") |
||||
|
const jwt = require("jsonwebtoken") |
||||
|
const env = require("../../../../environment") |
||||
|
const { |
||||
|
basicTable, |
||||
|
basicRow, |
||||
|
basicRole, |
||||
|
basicAutomation, |
||||
|
basicDatasource, |
||||
|
basicQuery, |
||||
|
} = require("./structures") |
||||
|
const controllers = require("./controllers") |
||||
|
const supertest = require("supertest") |
||||
|
|
||||
|
const EMAIL = "babs@babs.com" |
||||
|
const PASSWORD = "babs_password" |
||||
|
|
||||
|
class TestConfiguration { |
||||
|
constructor() { |
||||
|
env.PORT = 4002 |
||||
|
this.server = require("../../../../app") |
||||
|
// we need the request for logging in, involves cookies, hard to fake
|
||||
|
this.request = supertest(this.server) |
||||
|
this.appId = null |
||||
|
} |
||||
|
|
||||
|
getRequest() { |
||||
|
return this.request |
||||
|
} |
||||
|
|
||||
|
getAppId() { |
||||
|
return this.appId |
||||
|
} |
||||
|
|
||||
|
async _req(config, params, controlFunc) { |
||||
|
const request = {} |
||||
|
// fake cookies, we don't need them
|
||||
|
request.cookies = { set: () => {}, get: () => {} } |
||||
|
request.config = { jwtSecret: env.JWT_SECRET } |
||||
|
request.appId = this.appId |
||||
|
request.user = { appId: this.appId } |
||||
|
request.request = { |
||||
|
body: config, |
||||
|
} |
||||
|
if (params) { |
||||
|
request.params = params |
||||
|
} |
||||
|
await controlFunc(request) |
||||
|
return request.body |
||||
|
} |
||||
|
|
||||
|
async init(appName = "test_application") { |
||||
|
return this.createApp(appName) |
||||
|
} |
||||
|
|
||||
|
end() { |
||||
|
this.server.close() |
||||
|
} |
||||
|
|
||||
|
defaultHeaders() { |
||||
|
const builderUser = { |
||||
|
userId: "BUILDER", |
||||
|
roleId: BUILTIN_ROLE_IDS.BUILDER, |
||||
|
} |
||||
|
const builderToken = jwt.sign(builderUser, env.JWT_SECRET) |
||||
|
const headers = { |
||||
|
Accept: "application/json", |
||||
|
Cookie: [`budibase:builder:local=${builderToken}`], |
||||
|
} |
||||
|
if (this.appId) { |
||||
|
headers["x-budibase-app-id"] = this.appId |
||||
|
} |
||||
|
return headers |
||||
|
} |
||||
|
|
||||
|
publicHeaders() { |
||||
|
const headers = { |
||||
|
Accept: "application/json", |
||||
|
} |
||||
|
if (this.appId) { |
||||
|
headers["x-budibase-app-id"] = this.appId |
||||
|
} |
||||
|
return headers |
||||
|
} |
||||
|
|
||||
|
async createApp(appName) { |
||||
|
this.app = await this._req({ name: appName }, null, controllers.app.create) |
||||
|
this.appId = this.app._id |
||||
|
return this.app |
||||
|
} |
||||
|
|
||||
|
async updateTable(config = null) { |
||||
|
config = config || basicTable() |
||||
|
this.table = await this._req(config, null, controllers.table.save) |
||||
|
return this.table |
||||
|
} |
||||
|
|
||||
|
async createTable(config = null) { |
||||
|
if (config != null && config._id) { |
||||
|
delete config._id |
||||
|
} |
||||
|
return this.updateTable(config) |
||||
|
} |
||||
|
|
||||
|
async getTable(tableId = null) { |
||||
|
tableId = tableId || this.table._id |
||||
|
return this._req(null, { id: tableId }, controllers.table.find) |
||||
|
} |
||||
|
|
||||
|
async createLinkedTable() { |
||||
|
if (!this.table) { |
||||
|
throw "Must have created a table first." |
||||
|
} |
||||
|
const tableConfig = basicTable() |
||||
|
tableConfig.primaryDisplay = "name" |
||||
|
tableConfig.schema.link = { |
||||
|
type: "link", |
||||
|
fieldName: "link", |
||||
|
tableId: this.table._id, |
||||
|
} |
||||
|
const linkedTable = await this.createTable(tableConfig) |
||||
|
this.linkedTable = linkedTable |
||||
|
return linkedTable |
||||
|
} |
||||
|
|
||||
|
async createAttachmentTable() { |
||||
|
const table = basicTable() |
||||
|
table.schema.attachment = { |
||||
|
type: "attachment", |
||||
|
} |
||||
|
return this.createTable(table) |
||||
|
} |
||||
|
|
||||
|
async createRow(config = null) { |
||||
|
if (!this.table) { |
||||
|
throw "Test requires table to be configured." |
||||
|
} |
||||
|
config = config || basicRow(this.table._id) |
||||
|
return this._req(config, { tableId: this.table._id }, controllers.row.save) |
||||
|
} |
||||
|
|
||||
|
async createRole(config = null) { |
||||
|
config = config || basicRole() |
||||
|
return this._req(config, null, controllers.role.save) |
||||
|
} |
||||
|
|
||||
|
async addPermission(roleId, resourceId, level = "read") { |
||||
|
return this._req( |
||||
|
null, |
||||
|
{ |
||||
|
roleId, |
||||
|
resourceId, |
||||
|
level, |
||||
|
}, |
||||
|
controllers.perms.addPermission |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
async createView(config) { |
||||
|
if (!this.table) { |
||||
|
throw "Test requires table to be configured." |
||||
|
} |
||||
|
const view = config || { |
||||
|
map: "function(doc) { emit(doc[doc.key], doc._id); } ", |
||||
|
tableId: this.table._id, |
||||
|
} |
||||
|
return this._req(view, null, controllers.view.save) |
||||
|
} |
||||
|
|
||||
|
async createAutomation(config) { |
||||
|
config = config || basicAutomation() |
||||
|
if (config._rev) { |
||||
|
delete config._rev |
||||
|
} |
||||
|
this.automation = ( |
||||
|
await this._req(config, null, controllers.automation.create) |
||||
|
).automation |
||||
|
return this.automation |
||||
|
} |
||||
|
|
||||
|
async getAllAutomations() { |
||||
|
return this._req(null, null, controllers.automation.fetch) |
||||
|
} |
||||
|
|
||||
|
async deleteAutomation(automation = null) { |
||||
|
automation = automation || this.automation |
||||
|
if (!automation) { |
||||
|
return |
||||
|
} |
||||
|
return this._req( |
||||
|
null, |
||||
|
{ id: automation._id, rev: automation._rev }, |
||||
|
controllers.automation.destroy |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
async createDatasource(config = null) { |
||||
|
config = config || basicDatasource() |
||||
|
this.datasource = await this._req(config, null, controllers.datasource.save) |
||||
|
return this.datasource |
||||
|
} |
||||
|
|
||||
|
async createQuery(config = null) { |
||||
|
if (!this.datasource && !config) { |
||||
|
throw "No data source created for query." |
||||
|
} |
||||
|
config = config || basicQuery(this.datasource._id) |
||||
|
return this._req(config, null, controllers.query.save) |
||||
|
} |
||||
|
|
||||
|
async createUser( |
||||
|
email = EMAIL, |
||||
|
password = PASSWORD, |
||||
|
roleId = BUILTIN_ROLE_IDS.POWER |
||||
|
) { |
||||
|
return this._req( |
||||
|
{ |
||||
|
email, |
||||
|
password, |
||||
|
roleId, |
||||
|
}, |
||||
|
null, |
||||
|
controllers.user.create |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
async login(email, password) { |
||||
|
if (!email || !password) { |
||||
|
await this.createUser() |
||||
|
email = EMAIL |
||||
|
password = PASSWORD |
||||
|
} |
||||
|
const result = await this.request |
||||
|
.post(`/api/authenticate`) |
||||
|
.set({ |
||||
|
"x-budibase-app-id": this.appId, |
||||
|
}) |
||||
|
.send({ email, password }) |
||||
|
|
||||
|
// returning necessary request headers
|
||||
|
return { |
||||
|
Accept: "application/json", |
||||
|
Cookie: result.headers["set-cookie"], |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = TestConfiguration |
||||
@ -0,0 +1,79 @@ |
|||||
|
const rowController = require("../../../controllers/row") |
||||
|
const appController = require("../../../controllers/application") |
||||
|
|
||||
|
function Request(appId, params) { |
||||
|
this.user = { appId } |
||||
|
this.params = params |
||||
|
} |
||||
|
|
||||
|
exports.getAllTableRows = async config => { |
||||
|
const req = new Request(config.appId, { tableId: config.table._id }) |
||||
|
await rowController.fetchTableRows(req) |
||||
|
return req.body |
||||
|
} |
||||
|
|
||||
|
exports.clearAllApps = async () => { |
||||
|
const req = {} |
||||
|
await appController.fetch(req) |
||||
|
const apps = req.body |
||||
|
if (!apps || apps.length <= 0) { |
||||
|
return |
||||
|
} |
||||
|
for (let app of apps) { |
||||
|
const appId = app._id |
||||
|
await appController.delete(new Request(null, { appId })) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.clearAllAutomations = async config => { |
||||
|
const automations = await config.getAllAutomations() |
||||
|
for (let auto of automations) { |
||||
|
await config.deleteAutomation(auto) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.createRequest = (request, method, url, body) => { |
||||
|
let req |
||||
|
|
||||
|
if (method === "POST") req = request.post(url).send(body) |
||||
|
else if (method === "GET") req = request.get(url) |
||||
|
else if (method === "DELETE") req = request.delete(url) |
||||
|
else if (method === "PATCH") req = request.patch(url).send(body) |
||||
|
else if (method === "PUT") req = request.put(url).send(body) |
||||
|
|
||||
|
return req |
||||
|
} |
||||
|
|
||||
|
exports.checkBuilderEndpoint = async ({ config, method, url, body }) => { |
||||
|
const headers = await config.login() |
||||
|
await exports |
||||
|
.createRequest(config.request, method, url, body) |
||||
|
.set(headers) |
||||
|
.expect(403) |
||||
|
} |
||||
|
|
||||
|
exports.checkPermissionsEndpoint = async ({ |
||||
|
config, |
||||
|
method, |
||||
|
url, |
||||
|
body, |
||||
|
passRole, |
||||
|
failRole, |
||||
|
}) => { |
||||
|
const password = "PASSWORD" |
||||
|
await config.createUser("passUser@budibase.com", password, passRole) |
||||
|
const passHeader = await config.login("passUser@budibase.com", password) |
||||
|
|
||||
|
await exports |
||||
|
.createRequest(config.request, method, url, body) |
||||
|
.set(passHeader) |
||||
|
.expect(200) |
||||
|
|
||||
|
await config.createUser("failUser@budibase.com", password, failRole) |
||||
|
const failHeader = await config.login("failUser@budibase.com", password) |
||||
|
|
||||
|
await exports |
||||
|
.createRequest(config.request, method, url, body) |
||||
|
.set(failHeader) |
||||
|
.expect(403) |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
module.exports = { |
||||
|
table: require("../../../controllers/table"), |
||||
|
row: require("../../../controllers/row"), |
||||
|
role: require("../../../controllers/role"), |
||||
|
perms: require("../../../controllers/permission"), |
||||
|
view: require("../../../controllers/view"), |
||||
|
app: require("../../../controllers/application"), |
||||
|
user: require("../../../controllers/user"), |
||||
|
automation: require("../../../controllers/automation"), |
||||
|
datasource: require("../../../controllers/datasource"), |
||||
|
query: require("../../../controllers/query"), |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
const TestConfig = require("./TestConfiguration") |
||||
|
|
||||
|
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) |
||||
|
|
||||
|
let request, config |
||||
|
|
||||
|
exports.beforeAll = () => { |
||||
|
config = new TestConfig() |
||||
|
request = config.getRequest() |
||||
|
} |
||||
|
|
||||
|
exports.afterAll = () => { |
||||
|
if (config) { |
||||
|
config.end() |
||||
|
} |
||||
|
request = null |
||||
|
config = null |
||||
|
} |
||||
|
|
||||
|
exports.getRequest = () => { |
||||
|
if (!request) { |
||||
|
exports.beforeAll() |
||||
|
} |
||||
|
return request |
||||
|
} |
||||
|
|
||||
|
exports.getConfig = () => { |
||||
|
if (!config) { |
||||
|
exports.beforeAll() |
||||
|
} |
||||
|
return config |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") |
||||
|
const { |
||||
|
BUILTIN_PERMISSION_IDS, |
||||
|
} = require("../../../../utilities/security/permissions") |
||||
|
|
||||
|
exports.basicTable = () => { |
||||
|
return { |
||||
|
name: "TestTable", |
||||
|
type: "table", |
||||
|
key: "name", |
||||
|
schema: { |
||||
|
name: { |
||||
|
type: "string", |
||||
|
constraints: { |
||||
|
type: "string", |
||||
|
}, |
||||
|
}, |
||||
|
description: { |
||||
|
type: "string", |
||||
|
constraints: { |
||||
|
type: "string", |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.basicAutomation = () => { |
||||
|
return { |
||||
|
name: "My Automation", |
||||
|
screenId: "kasdkfldsafkl", |
||||
|
live: true, |
||||
|
uiTree: {}, |
||||
|
definition: { |
||||
|
trigger: { |
||||
|
inputs: {}, |
||||
|
}, |
||||
|
steps: [], |
||||
|
}, |
||||
|
type: "automation", |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.basicRow = tableId => { |
||||
|
return { |
||||
|
name: "Test Contact", |
||||
|
description: "original description", |
||||
|
status: "new", |
||||
|
tableId: tableId, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.basicRole = () => { |
||||
|
return { |
||||
|
name: "NewRole", |
||||
|
inherits: BUILTIN_ROLE_IDS.BASIC, |
||||
|
permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.basicDatasource = () => { |
||||
|
return { |
||||
|
type: "datasource", |
||||
|
name: "Test", |
||||
|
source: "POSTGRES", |
||||
|
config: {}, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.basicQuery = datasourceId => { |
||||
|
return { |
||||
|
datasourceId: datasourceId, |
||||
|
name: "New Query", |
||||
|
parameters: [], |
||||
|
fields: {}, |
||||
|
schema: {}, |
||||
|
queryVerb: "read", |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exports.basicUser = role => { |
||||
|
return { |
||||
|
email: "bill@bill.com", |
||||
|
password: "yeeooo", |
||||
|
roleId: role, |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue