From f55c5dec000dfd8cbd91600c41371af150c02d62 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Mar 2021 17:52:41 +0000 Subject: [PATCH 01/22] Initial work on test refactoring, laying the utilities down which will be the basis of new testing framework, interacting directly with the controllers. --- packages/server/package.json | 3 +- .../src/api/routes/tests/application.spec.js | 32 ++-- .../tests/utilities/TestConfiguration.js | 159 ++++++++++++++++++ .../routes/tests/utilities/TestFunctions.js | 75 +++++++++ .../src/api/routes/tests/utilities/index.js | 47 ++++++ .../api/routes/tests/utilities/structures.js | 43 +++++ 6 files changed, 341 insertions(+), 18 deletions(-) create mode 100644 packages/server/src/api/routes/tests/utilities/TestConfiguration.js create mode 100644 packages/server/src/api/routes/tests/utilities/TestFunctions.js create mode 100644 packages/server/src/api/routes/tests/utilities/index.js create mode 100644 packages/server/src/api/routes/tests/utilities/structures.js diff --git a/packages/server/package.json b/packages/server/package.json index 2e8c742ccd..fe80923f00 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -52,7 +52,8 @@ "collectCoverageFrom": [ "src/**/*.js", "!**/node_modules/**", - "!src/db/views/*.js" + "!src/db/views/*.js", + "!src/api/routes/tests" ], "coverageReporters": [ "lcov", diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index 6068d783cb..c5e9daabbd 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -1,21 +1,23 @@ const { - createApplication, - builderEndpointShouldBlockNormalUsers, supertest, - clearApplications, defaultHeaders, -} = require("./couchTestUtils") +} = require("./utilities") +const TestConfig = require("./utilities/TestConfiguration") +const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/applications", () => { let request let server + let config beforeAll(async () => { ({ request, server } = await supertest()) }); beforeEach(async () => { - await clearApplications(request) + await clearAllApps() + config = new TestConfig(request) + await config.init() }) afterAll(() => { @@ -35,23 +37,20 @@ describe("/applications", () => { }) it("should apply authorization to endpoint", async () => { - const otherApplication = await createApplication(request) - const appId = otherApplication.instance._id - await builderEndpointShouldBlockNormalUsers({ + await checkBuilderEndpoint({ + config, request, method: "POST", url: `/api/applications`, - appId: appId, body: { name: "My App" } }) }) - }) describe("fetch", () => { it("lists all applications", async () => { - await createApplication(request, "app1") - await createApplication(request, "app2") + await config.createApp(request, "app1") + await config.createApp(request, "app2") const res = await request .get("/api/applications") @@ -59,17 +58,16 @@ describe("/applications", () => { .expect('Content-Type', /json/) .expect(200) - expect(res.body.length).toBe(2) + // two created apps + the inited app + expect(res.body.length).toBe(3) }) it("should apply authorization to endpoint", async () => { - const otherApplication = await createApplication(request) - const appId = otherApplication.instance._id - await builderEndpointShouldBlockNormalUsers({ + await checkBuilderEndpoint({ + config, request, method: "GET", url: `/api/applications`, - appId: appId, }) }) }) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js new file mode 100644 index 0000000000..be9192b7f2 --- /dev/null +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -0,0 +1,159 @@ +const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") +const env = require("../../../../environment") +const { basicTable, basicRow, basicRole } = require("./structures") +const tableController = require("../../../controllers/table") +const rowController = require("../../../controllers/row") +const roleController = require("../../../controllers/role") +const permsController = require("../../../controllers/permission") +const viewController = require("../../../controllers/view") +const appController = require("../../../controllers/application") +const userController = require("../../../controllers/user") + +const EMAIL = "babs@babs.com" +const PASSWORD = "babs_password" + +class TestConfiguration { + constructor(request) { + // we need the request for logging in, involves cookies, hard to fake + this.request = request + this.appId = null + this.table = null + this.linkedTable = null + } + + 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) + } + + async createApp(appName) { + this.app = await this._req({ name: appName }, null, appController.create) + this.appId = this.app._id + return this.app + } + + async updateTable(config = null) { + config = config || basicTable() + this.table = await this._req(config, null, tableController.save) + return this.table + } + + async createTable(config = null) { + if (config != null && config._id) { + delete config._id + } + return this.updateTable(config) + } + + async createLinkedTables() { + const table = await this.createTable() + table.primaryDisplay = "name" + table.schema.link = { + type: "link", + fieldName: "link", + tableId: table._id, + } + const linkedTable = await this.createTable(table) + this.table = table + 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 }, rowController.save) + } + + async createRole(config = null) { + config = config || basicRole() + return this._req(config, null, roleController.save) + } + + async addPermission(roleId, resourceId, level = "read") { + return this._req( + null, + { + roleId, + resourceId, + level, + }, + permsController.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, viewController.save) + } + + async createUser( + email = EMAIL, + password = PASSWORD, + roleId = BUILTIN_ROLE_IDS.POWER + ) { + return this._req( + { + email, + password, + roleId, + }, + null, + userController.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 diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js new file mode 100644 index 0000000000..dcb944b0a1 --- /dev/null +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -0,0 +1,75 @@ +const rowController = require("../../../controllers/row") +const appController = require("../../../controllers/application") +const CouchDB = require("../../../../db") + +function Request(appId, params) { + this.user = { appId } + this.params = params +} + +exports.getAllTableRows = async (appId, tableId) => { + const req = new Request(appId, { tableId }) + 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.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, + request, + method, + url, + body, +}) => { + const headers = await config.login() + await exports + .createRequest(request, method, url, body) + .set(headers) + .expect(403) +} + +/** + * Raw DB insert utility. + */ +exports.insertDocument = async (databaseId, document) => { + const { id, ...documentFields } = document + return await new CouchDB(databaseId).put({ _id: id, ...documentFields }) +} + +/** + * Raw DB delete utility. + */ +exports.destroyDocument = async (databaseId, documentId) => { + return await new CouchDB(databaseId).destroy(documentId) +} + +/** + * Raw DB get utility. + */ +exports.getDocument = async (databaseId, documentId) => { + return await new CouchDB(databaseId).get(documentId) +} diff --git a/packages/server/src/api/routes/tests/utilities/index.js b/packages/server/src/api/routes/tests/utilities/index.js new file mode 100644 index 0000000000..9c58364aaa --- /dev/null +++ b/packages/server/src/api/routes/tests/utilities/index.js @@ -0,0 +1,47 @@ +const supertest = require("supertest") +const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") +const jwt = require("jsonwebtoken") +const env = require("../../../../environment") + +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 +} diff --git a/packages/server/src/api/routes/tests/utilities/structures.js b/packages/server/src/api/routes/tests/utilities/structures.js new file mode 100644 index 0000000000..cf98a2eb79 --- /dev/null +++ b/packages/server/src/api/routes/tests/utilities/structures.js @@ -0,0 +1,43 @@ +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.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, + } +} From 79818122fda33381a6f46dc122bbcafc4a0b6c5c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Mar 2021 18:41:49 +0000 Subject: [PATCH 02/22] Re-writing automation tests. --- .../src/api/routes/tests/application.spec.js | 2 - .../src/api/routes/tests/automation.spec.js | 108 +++++++----------- .../tests/utilities/TestConfiguration.js | 36 +++++- .../routes/tests/utilities/TestFunctions.js | 22 ++-- .../api/routes/tests/utilities/structures.js | 16 +++ 5 files changed, 104 insertions(+), 80 deletions(-) diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index c5e9daabbd..e5100573ff 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -39,7 +39,6 @@ describe("/applications", () => { it("should apply authorization to endpoint", async () => { await checkBuilderEndpoint({ config, - request, method: "POST", url: `/api/applications`, body: { name: "My App" } @@ -65,7 +64,6 @@ describe("/applications", () => { it("should apply authorization to endpoint", async () => { await checkBuilderEndpoint({ config, - request, method: "GET", url: `/api/applications`, }) diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 128d3530e9..3f3fa93192 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -1,34 +1,18 @@ const { - createApplication, - createTable, - getAllFromTable, defaultHeaders, supertest, - insertDocument, - destroyDocument, - builderEndpointShouldBlockNormalUsers -} = require("./couchTestUtils") -let { generateAutomationID } = require("../../../db/utils") +} = require("./utilities") +const TestConfig = require("./utilities/TestConfiguration") +const { + checkBuilderEndpoint, + getAllTableRows, + clearAllAutomations, +} = require("./utilities/TestFunctions") +const { basicAutomation } = require("./utilities/structures") const { delay } = require("./testUtils") const MAX_RETRIES = 4 -const AUTOMATION_ID = generateAutomationID() -const TEST_AUTOMATION = { - _id: AUTOMATION_ID, - name: "My Automation", - screenId: "kasdkfldsafkl", - live: true, - uiTree: { - - }, - definition: { - trigger: {}, - steps: [ - ], - }, - type: "automation", -} let ACTION_DEFINITIONS = {} let TRIGGER_DEFINITIONS = {} @@ -39,34 +23,26 @@ describe("/automations", () => { let server let app let appId + let config let automation - let automationId beforeAll(async () => { ({ request, server } = await supertest()) }) beforeEach(async () => { - app = await createApplication(request) + config = new TestConfig(request) + app = await config.init() appId = app.instance._id - if (automation) await destroyDocument(automation.id) }) afterAll(() => { server.close() }) - const createAutomation = async () => { - automation = await insertDocument(appId, { - type: "automation", - ...TEST_AUTOMATION - }) - automation = { ...automation, ...TEST_AUTOMATION } - } - - const triggerWorkflow = async (automationId) => { + const triggerWorkflow = async automation => { return await request - .post(`/api/automations/${automationId}/trigger`) + .post(`/api/automations/${automation._id}/trigger`) .send({ name: "Test", description: "TEST" }) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) @@ -121,6 +97,7 @@ describe("/automations", () => { }) describe("create", () => { + const autoConfig = basicAutomation() it("should setup the automation fully", () => { let trigger = TRIGGER_DEFINITIONS["ROW_SAVED"] trigger.id = "wadiawdo34" @@ -131,52 +108,51 @@ describe("/automations", () => { } createAction.id = "awde444wk" - TEST_AUTOMATION.definition.steps.push(createAction) - TEST_AUTOMATION.definition.trigger = trigger + autoConfig.definition.steps.push(createAction) + autoConfig.definition.trigger = trigger }) it("returns a success message when the automation is successfully created", async () => { const res = await request .post(`/api/automations`) .set(defaultHeaders(appId)) - .send(TEST_AUTOMATION) + .send(autoConfig) .expect('Content-Type', /json/) .expect(200) expect(res.body.message).toEqual("Automation created successfully") expect(res.body.automation.name).toEqual("My Automation") expect(res.body.automation._id).not.toEqual(null) - automationId = res.body.automation._id + automation = res.body.automation }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "POST", url: `/api/automations`, - appId: appId, - body: TEST_AUTOMATION + body: autoConfig }) }) }) describe("trigger", () => { it("trigger the automation successfully", async () => { - let table = await createTable(request, appId) - TEST_AUTOMATION.definition.trigger.inputs.tableId = table._id - TEST_AUTOMATION.definition.steps[0].inputs.row.tableId = table._id - await createAutomation() + let table = await config.createTable() + automation.definition.trigger.inputs.tableId = table._id + automation.definition.steps[0].inputs.row.tableId = table._id + automation = await config.createAutomation(automation) await delay(500) - const res = await triggerWorkflow(automation._id) + const res = await triggerWorkflow(automation) // this looks a bit mad but we don't actually have a way to wait for a response from the automation to // know that it has finished all of its actions - this is currently the best way // also when this runs in CI it is very temper-mental so for now trying to make run stable by repeating until it works // TODO: update when workflow logs are a thing for (let tries = 0; tries < MAX_RETRIES; tries++) { expect(res.body.message).toEqual(`Automation ${automation._id} has been triggered.`) - expect(res.body.automation.name).toEqual(TEST_AUTOMATION.name) + expect(res.body.automation.name).toEqual(automation.name) await delay(500) - let elements = await getAllFromTable(request, appId, table._id) + let elements = await getAllTableRows(config) // don't test it unless there are values to test if (elements.length > 1) { expect(elements.length).toEqual(5) @@ -191,9 +167,7 @@ describe("/automations", () => { describe("update", () => { it("updates a automations data", async () => { - await createAutomation() - automation._id = automation.id - automation._rev = automation.rev + automation = await config.createAutomation(automation) automation.name = "Updated Name" automation.type = "automation" @@ -204,52 +178,52 @@ describe("/automations", () => { .expect('Content-Type', /json/) .expect(200) - expect(res.body.message).toEqual(`Automation ${AUTOMATION_ID} updated successfully.`) + expect(res.body.message).toEqual(`Automation ${automation._id} updated successfully.`) expect(res.body.automation.name).toEqual("Updated Name") }) }) describe("fetch", () => { it("return all the automations for an instance", async () => { - await createAutomation() + await clearAllAutomations(config) + const autoConfig = basicAutomation() + automation = await config.createAutomation(autoConfig) const res = await request .get(`/api/automations`) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) - expect(res.body[0]).toEqual(expect.objectContaining(TEST_AUTOMATION)) + expect(res.body[0]).toEqual(expect.objectContaining(autoConfig)) }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "GET", url: `/api/automations`, - appId: appId, }) }) }) describe("destroy", () => { it("deletes a automation by its ID", async () => { - await createAutomation() + const automation = await config.createAutomation() const res = await request .delete(`/api/automations/${automation.id}/${automation.rev}`) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) - expect(res.body.id).toEqual(TEST_AUTOMATION._id) + expect(res.body.id).toEqual(automation._id) }) it("should apply authorization to endpoint", async () => { - await createAutomation() - await builderEndpointShouldBlockNormalUsers({ - request, + const automation = await config.createAutomation() + await checkBuilderEndpoint({ + config, method: "DELETE", url: `/api/automations/${automation.id}/${automation._rev}`, - appId: appId, }) }) }) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index be9192b7f2..50ce00b11f 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -1,6 +1,11 @@ const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") const env = require("../../../../environment") -const { basicTable, basicRow, basicRole } = require("./structures") +const { + basicTable, + basicRow, + basicRole, + basicAutomation, +} = require("./structures") const tableController = require("../../../controllers/table") const rowController = require("../../../controllers/row") const roleController = require("../../../controllers/role") @@ -8,6 +13,7 @@ const permsController = require("../../../controllers/permission") const viewController = require("../../../controllers/view") const appController = require("../../../controllers/application") const userController = require("../../../controllers/user") +const autoController = require("../../../controllers/automation") const EMAIL = "babs@babs.com" const PASSWORD = "babs_password" @@ -19,6 +25,7 @@ class TestConfiguration { this.appId = null this.table = null this.linkedTable = null + this.automation = null } async _req(config, params, controlFunc) { @@ -119,6 +126,33 @@ class TestConfiguration { return this._req(view, null, viewController.save) } + async createAutomation(config) { + config = config || basicAutomation() + if (config._rev) { + delete config._rev + } + this.automation = ( + await this._req(config, null, autoController.create) + ).automation + return this.automation + } + + async getAllAutomations() { + return this._req(null, null, autoController.fetch) + } + + async deleteAutomation(automation) { + automation = automation || this.automation + if (!automation) { + return + } + return this._req( + null, + { id: automation._id, rev: automation._rev }, + autoController.destroy + ) + } + async createUser( email = EMAIL, password = PASSWORD, diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index dcb944b0a1..d7a32f4416 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -1,5 +1,6 @@ const rowController = require("../../../controllers/row") const appController = require("../../../controllers/application") +const autoController = require("../../../controllers/automation") const CouchDB = require("../../../../db") function Request(appId, params) { @@ -7,8 +8,8 @@ function Request(appId, params) { this.params = params } -exports.getAllTableRows = async (appId, tableId) => { - const req = new Request(appId, { tableId }) +exports.getAllTableRows = async config => { + const req = new Request(config.appId, { tableId: config.table._id }) await rowController.fetchTableRows(req) return req.body } @@ -26,6 +27,13 @@ exports.clearAllApps = async () => { } } +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 @@ -38,16 +46,10 @@ exports.createRequest = (request, method, url, body) => { return req } -exports.checkBuilderEndpoint = async ({ - config, - request, - method, - url, - body, -}) => { +exports.checkBuilderEndpoint = async ({ config, method, url, body }) => { const headers = await config.login() await exports - .createRequest(request, method, url, body) + .createRequest(config.request, method, url, body) .set(headers) .expect(403) } diff --git a/packages/server/src/api/routes/tests/utilities/structures.js b/packages/server/src/api/routes/tests/utilities/structures.js index cf98a2eb79..1aa81ea2a3 100644 --- a/packages/server/src/api/routes/tests/utilities/structures.js +++ b/packages/server/src/api/routes/tests/utilities/structures.js @@ -25,6 +25,22 @@ exports.basicTable = () => { } } +exports.basicAutomation = () => { + return { + name: "My Automation", + screenId: "kasdkfldsafkl", + live: true, + uiTree: {}, + definition: { + trigger: { + inputs: {}, + }, + steps: [], + }, + type: "automation", + } +} + exports.basicRow = tableId => { return { name: "Test Contact", From f10ac0a1781eae3ec32778177d674dac9a21732e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 10:05:50 +0000 Subject: [PATCH 03/22] Refactoring data source test cases. --- .../src/api/routes/tests/datasource.spec.js | 69 ++++++------------- .../tests/utilities/TestConfiguration.js | 50 ++++++++------ .../api/routes/tests/utilities/controllers.js | 12 ++++ .../api/routes/tests/utilities/structures.js | 20 ++++++ 4 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 packages/server/src/api/routes/tests/utilities/controllers.js diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 768d54fbcd..0bded03020 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -1,42 +1,22 @@ const { supertest, - createApplication, defaultHeaders, - builderEndpointShouldBlockNormalUsers, - getDocument, - insertDocument -} = require("./couchTestUtils") -let { generateDatasourceID, generateQueryID } = require("../../../db/utils") - -const DATASOURCE_ID = generateDatasourceID() -const TEST_DATASOURCE = { - _id: DATASOURCE_ID, - type: "datasource", - name: "Test", - source: "POSTGRES", - config: {}, - type: "datasource", -} - -const TEST_QUERY = { - _id: generateQueryID(DATASOURCE_ID), - datasourceId: DATASOURCE_ID, - name:"New Query", - parameters:[], - fields:{}, - schema:{}, - queryVerb:"read", -} +} = require("./utilities") +let TestConfig = require("./utilities/TestConfiguration") +let { basicDatasource } = require("./utilities/structures") +let { checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/datasources", () => { let request let server let app let appId + let config let datasource beforeAll(async () => { ({ request, server } = await supertest()) + config = new TestConfig(request) }); afterAll(() => { @@ -44,23 +24,15 @@ describe("/datasources", () => { }) beforeEach(async () => { - app = await createApplication(request) + app = await config.init() appId = app.instance._id }); - async function createDatasource() { - return await insertDocument(appId, TEST_DATASOURCE) - } - - async function createQuery() { - return await insertDocument(appId, TEST_QUERY) - } - describe("create", () => { it("should create a new datasource", async () => { const res = await request .post(`/api/datasources`) - .send(TEST_DATASOURCE) + .send(basicDatasource()) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) @@ -74,7 +46,7 @@ describe("/datasources", () => { let datasource beforeEach(async () => { - datasource = await createDatasource() + datasource = await config.createDatasource() }); afterEach(() => { @@ -88,30 +60,30 @@ describe("/datasources", () => { .expect('Content-Type', /json/) .expect(200) - const datasources = res.body; + const datasources = res.body expect(datasources).toEqual([ { + "_id": datasources[0]._id, "_rev": datasources[0]._rev, - ...TEST_DATASOURCE + ...basicDatasource() } ]); }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "GET", url: `/api/datasources`, - appId: appId, }) }) }); describe("destroy", () => { - let datasource; + let datasource beforeEach(async () => { - datasource = await createDatasource() + datasource = await config.createDatasource() }); afterEach(() => { @@ -119,10 +91,10 @@ describe("/datasources", () => { }); it("deletes queries for the datasource after deletion and returns a success message", async () => { - await createQuery(datasource.id) + await config.createQuery() await request - .delete(`/api/datasources/${datasource.id}/${datasource.rev}`) + .delete(`/api/datasources/${datasource._id}/${datasource._rev}`) .set(defaultHeaders(appId)) .expect(200) @@ -136,11 +108,10 @@ describe("/datasources", () => { }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "DELETE", url: `/api/datasources/${datasource._id}/${datasource._rev}`, - appId: appId, }) }) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 50ce00b11f..544c5434e6 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -5,15 +5,10 @@ const { basicRow, basicRole, basicAutomation, + basicDatasource, + basicQuery, } = require("./structures") -const tableController = require("../../../controllers/table") -const rowController = require("../../../controllers/row") -const roleController = require("../../../controllers/role") -const permsController = require("../../../controllers/permission") -const viewController = require("../../../controllers/view") -const appController = require("../../../controllers/application") -const userController = require("../../../controllers/user") -const autoController = require("../../../controllers/automation") +const controllers = require("./controllers") const EMAIL = "babs@babs.com" const PASSWORD = "babs_password" @@ -23,9 +18,6 @@ class TestConfiguration { // we need the request for logging in, involves cookies, hard to fake this.request = request this.appId = null - this.table = null - this.linkedTable = null - this.automation = null } async _req(config, params, controlFunc) { @@ -50,14 +42,14 @@ class TestConfiguration { } async createApp(appName) { - this.app = await this._req({ name: appName }, null, appController.create) + 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, tableController.save) + this.table = await this._req(config, null, controllers.table.save) return this.table } @@ -95,12 +87,12 @@ class TestConfiguration { throw "Test requires table to be configured." } config = config || basicRow(this.table._id) - return this._req(config, { tableId: this.table._id }, rowController.save) + return this._req(config, { tableId: this.table._id }, controllers.row.save) } async createRole(config = null) { config = config || basicRole() - return this._req(config, null, roleController.save) + return this._req(config, null, controllers.role.save) } async addPermission(roleId, resourceId, level = "read") { @@ -111,7 +103,7 @@ class TestConfiguration { resourceId, level, }, - permsController.addPermission + controllers.perms.addPermission ) } @@ -123,7 +115,7 @@ class TestConfiguration { map: "function(doc) { emit(doc[doc.key], doc._id); } ", tableId: this.table._id, } - return this._req(view, null, viewController.save) + return this._req(view, null, controllers.view.save) } async createAutomation(config) { @@ -132,16 +124,16 @@ class TestConfiguration { delete config._rev } this.automation = ( - await this._req(config, null, autoController.create) + await this._req(config, null, controllers.automation.create) ).automation return this.automation } async getAllAutomations() { - return this._req(null, null, autoController.fetch) + return this._req(null, null, controllers.automation.fetch) } - async deleteAutomation(automation) { + async deleteAutomation(automation = null) { automation = automation || this.automation if (!automation) { return @@ -149,10 +141,24 @@ class TestConfiguration { return this._req( null, { id: automation._id, rev: automation._rev }, - autoController.destroy + 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, @@ -165,7 +171,7 @@ class TestConfiguration { roleId, }, null, - userController.create + controllers.user.create ) } diff --git a/packages/server/src/api/routes/tests/utilities/controllers.js b/packages/server/src/api/routes/tests/utilities/controllers.js new file mode 100644 index 0000000000..d1e23d4527 --- /dev/null +++ b/packages/server/src/api/routes/tests/utilities/controllers.js @@ -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") +} diff --git a/packages/server/src/api/routes/tests/utilities/structures.js b/packages/server/src/api/routes/tests/utilities/structures.js index 1aa81ea2a3..4a21602e40 100644 --- a/packages/server/src/api/routes/tests/utilities/structures.js +++ b/packages/server/src/api/routes/tests/utilities/structures.js @@ -57,3 +57,23 @@ exports.basicRole = () => { 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", + } +} From f7cdced688c2e396b8ec2203babc1f2683228129 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 10:40:27 +0000 Subject: [PATCH 04/22] Switching out how headers are handled, and updating the permissions test. --- .../src/api/routes/tests/application.spec.js | 5 +-- .../src/api/routes/tests/automation.spec.js | 21 ++++------ .../src/api/routes/tests/datasource.spec.js | 15 +++---- .../src/api/routes/tests/permissions.spec.js | 41 ++++++++----------- .../tests/utilities/TestConfiguration.js | 27 ++++++++++++ .../src/api/routes/tests/utilities/index.js | 33 +-------------- 6 files changed, 62 insertions(+), 80 deletions(-) diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index e5100573ff..8aa3d0288a 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -1,6 +1,5 @@ const { supertest, - defaultHeaders, } = require("./utilities") const TestConfig = require("./utilities/TestConfiguration") const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions") @@ -29,7 +28,7 @@ describe("/applications", () => { const res = await request .post("/api/applications") .send({ name: "My App" }) - .set(defaultHeaders()) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) expect(res.res.statusMessage).toEqual("Application My App created successfully") @@ -53,7 +52,7 @@ describe("/applications", () => { const res = await request .get("/api/applications") - .set(defaultHeaders()) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 3f3fa93192..3118f36903 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -1,5 +1,4 @@ const { - defaultHeaders, supertest, } = require("./utilities") const TestConfig = require("./utilities/TestConfiguration") @@ -22,7 +21,6 @@ describe("/automations", () => { let request let server let app - let appId let config let automation @@ -33,7 +31,6 @@ describe("/automations", () => { beforeEach(async () => { config = new TestConfig(request) app = await config.init() - appId = app.instance._id }) afterAll(() => { @@ -44,7 +41,7 @@ describe("/automations", () => { return await request .post(`/api/automations/${automation._id}/trigger`) .send({ name: "Test", description: "TEST" }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) } @@ -53,7 +50,7 @@ describe("/automations", () => { it("returns a list of definitions for actions", async () => { const res = await request .get(`/api/automations/action/list`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -64,7 +61,7 @@ describe("/automations", () => { it("returns a list of definitions for triggers", async () => { const res = await request .get(`/api/automations/trigger/list`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -75,7 +72,7 @@ describe("/automations", () => { it("returns a list of definitions for actions", async () => { const res = await request .get(`/api/automations/logic/list`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -86,7 +83,7 @@ describe("/automations", () => { it("returns all of the definitions in one", async () => { const res = await request .get(`/api/automations/definitions/list`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -115,7 +112,7 @@ describe("/automations", () => { it("returns a success message when the automation is successfully created", async () => { const res = await request .post(`/api/automations`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .send(autoConfig) .expect('Content-Type', /json/) .expect(200) @@ -173,7 +170,7 @@ describe("/automations", () => { const res = await request .put(`/api/automations`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .send(automation) .expect('Content-Type', /json/) .expect(200) @@ -190,7 +187,7 @@ describe("/automations", () => { automation = await config.createAutomation(autoConfig) const res = await request .get(`/api/automations`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -211,7 +208,7 @@ describe("/automations", () => { const automation = await config.createAutomation() const res = await request .delete(`/api/automations/${automation.id}/${automation.rev}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 0bded03020..1f78c65f07 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -1,6 +1,5 @@ const { supertest, - defaultHeaders, } = require("./utilities") let TestConfig = require("./utilities/TestConfiguration") let { basicDatasource } = require("./utilities/structures") @@ -9,10 +8,7 @@ let { checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/datasources", () => { let request let server - let app - let appId let config - let datasource beforeAll(async () => { ({ request, server } = await supertest()) @@ -24,8 +20,7 @@ describe("/datasources", () => { }) beforeEach(async () => { - app = await config.init() - appId = app.instance._id + await config.init() }); describe("create", () => { @@ -33,7 +28,7 @@ describe("/datasources", () => { const res = await request .post(`/api/datasources`) .send(basicDatasource()) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -56,7 +51,7 @@ describe("/datasources", () => { it("returns all the datasources from the server", async () => { const res = await request .get(`/api/datasources`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -95,12 +90,12 @@ describe("/datasources", () => { await request .delete(`/api/datasources/${datasource._id}/${datasource._rev}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect(200) const res = await request .get(`/api/datasources`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) diff --git a/packages/server/src/api/routes/tests/permissions.spec.js b/packages/server/src/api/routes/tests/permissions.spec.js index bb1f072efc..046a0dc00b 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.js +++ b/packages/server/src/api/routes/tests/permissions.spec.js @@ -1,14 +1,9 @@ const { - createApplication, - createTable, - createRow, supertest, - defaultHeaders, - addPermission, - publicHeaders, - makeBasicRow, -} = require("./couchTestUtils") +} = require("./utilities") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") +const TestConfig = require("./utilities/TestConfiguration") +const { basicRow } = require("./utilities/structures") const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC @@ -16,13 +11,14 @@ const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC describe("/permission", () => { let server let request - let appId let table let perms let row + let config beforeAll(async () => { ;({ request, server } = await supertest()) + config = new TestConfig(request) }) afterAll(() => { @@ -30,17 +26,16 @@ describe("/permission", () => { }) beforeEach(async () => { - let app = await createApplication(request) - appId = app.instance._id - table = await createTable(request, appId) - perms = await addPermission(request, appId, STD_ROLE_ID, table._id) - row = await createRow(request, appId, table._id) + await config.init() + table = await config.createTable() + row = await config.createRow() + perms = await config.addPermission(STD_ROLE_ID, table._id) }) async function getTablePermissions() { return request .get(`/api/permission/${table._id}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) } @@ -49,7 +44,7 @@ describe("/permission", () => { it("should be able to get levels", async () => { const res = await request .get(`/api/permission/levels`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.body).toBeDefined() @@ -68,7 +63,7 @@ describe("/permission", () => { it("should get the resource permissions", async () => { const res = await request .get(`/api/permission/${table._id}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.body["read"]).toEqual(STD_ROLE_ID) @@ -76,13 +71,13 @@ describe("/permission", () => { }) it("should get resource permissions with multiple roles", async () => { - perms = await addPermission(request, appId, HIGHER_ROLE_ID, table._id, "write") + perms = await config.addPermission(HIGHER_ROLE_ID, table._id, "write") const res = await getTablePermissions() expect(res.body["read"]).toEqual(STD_ROLE_ID) expect(res.body["write"]).toEqual(HIGHER_ROLE_ID) const allRes = await request .get(`/api/permission`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID) @@ -94,7 +89,7 @@ describe("/permission", () => { it("should be able to remove the permission", async () => { const res = await request .delete(`/api/permission/${STD_ROLE_ID}/${table._id}/read`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.body[0]._id).toEqual(STD_ROLE_ID) @@ -107,7 +102,7 @@ describe("/permission", () => { it("should be able to read the row", async () => { const res = await request .get(`/api/${table._id}/rows`) - .set(publicHeaders(appId)) + .set(config.publicHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.body[0]._id).toEqual(row._id) @@ -116,8 +111,8 @@ describe("/permission", () => { it("shouldn't allow writing from a public user", async () => { const res = await request .post(`/api/${table._id}/rows`) - .send(makeBasicRow(table._id)) - .set(publicHeaders(appId)) + .send(basicRow(table._id)) + .set(config.publicHeaders()) .expect("Content-Type", /json/) .expect(403) expect(res.status).toEqual(403) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 544c5434e6..1c07a8f7ff 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -1,4 +1,5 @@ const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") +const jwt = require("jsonwebtoken") const env = require("../../../../environment") const { basicTable, @@ -41,6 +42,32 @@ class TestConfiguration { return this.createApp(appName) } + 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 diff --git a/packages/server/src/api/routes/tests/utilities/index.js b/packages/server/src/api/routes/tests/utilities/index.js index 9c58364aaa..1421521a26 100644 --- a/packages/server/src/api/routes/tests/utilities/index.js +++ b/packages/server/src/api/routes/tests/utilities/index.js @@ -1,11 +1,10 @@ const supertest = require("supertest") -const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles") -const jwt = require("jsonwebtoken") const env = require("../../../../environment") const TEST_CLIENT_ID = "test-client-id" exports.TEST_CLIENT_ID = TEST_CLIENT_ID + exports.supertest = async () => { let request let server @@ -15,33 +14,3 @@ exports.supertest = async () => { 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 -} From 26c63c7d9bc82f6328179b8e0702e60008f80f6e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 10:57:41 +0000 Subject: [PATCH 05/22] Refactoring query tests. --- .../server/src/api/routes/tests/query.spec.js | 90 ++++++------------- 1 file changed, 29 insertions(+), 61 deletions(-) diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index e97887846e..1151d7e622 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -1,43 +1,18 @@ const { supertest, - createApplication, - defaultHeaders, - builderEndpointShouldBlockNormalUsers, - getDocument, - insertDocument, -} = require("./couchTestUtils") -let { generateDatasourceID, generateQueryID } = require("../../../db/utils") - -const DATASOURCE_ID = generateDatasourceID() -const TEST_DATASOURCE = { - _id: DATASOURCE_ID, - type: "datasource", - name: "Test", - source: "POSTGRES", - config: {}, - type: "datasource", -} - -const TEST_QUERY = { - _id: generateQueryID(DATASOURCE_ID), - datasourceId: DATASOURCE_ID, - name: "New Query", - parameters: [], - fields: {}, - schema: {}, - queryVerb: "read", -} +} = require("./utilities") +const TestConfig = require("./utilities/TestConfiguration") +const { checkBuilderEndpoint } = require("./utilities/TestFunctions") +const { basicQuery } = require("./utilities/structures") describe("/queries", () => { let request let server - let app - let appId - let datasource - let query + let config beforeAll(async () => { ;({ request, server } = await supertest()) + config = new TestConfig(request) }) afterAll(() => { @@ -45,33 +20,27 @@ describe("/queries", () => { }) beforeEach(async () => { - app = await createApplication(request) - appId = app.instance._id + await config.init() }) - async function createDatasource() { - return await insertDocument(appId, TEST_DATASOURCE) - } - - async function createQuery() { - return await insertDocument(appId, TEST_QUERY) - } - describe("create", () => { it("should create a new query", async () => { + const { _id } = await config.createDatasource() + const query = basicQuery(_id) const res = await request .post(`/api/queries`) - .send(TEST_QUERY) - .set(defaultHeaders(appId)) + .send(query) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.res.statusMessage).toEqual( - `Query ${TEST_QUERY.name} saved successfully.` + `Query ${query.name} saved successfully.` ) expect(res.body).toEqual({ _rev: res.body._rev, - ...TEST_QUERY, + _id: res.body._id, + ...query, }) }) }) @@ -80,7 +49,7 @@ describe("/queries", () => { let datasource beforeEach(async () => { - datasource = await createDatasource() + datasource = await config.createDatasource() }) afterEach(() => { @@ -88,29 +57,29 @@ describe("/queries", () => { }) it("returns all the queries from the server", async () => { - const query = await createQuery() + const query = await config.createQuery() const res = await request .get(`/api/queries`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) const queries = res.body expect(queries).toEqual([ { - _rev: query.rev, - ...TEST_QUERY, + _rev: query._rev, + _id: query._id, + ...basicQuery(datasource._id), readable: true, }, ]) }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "GET", url: `/api/datasources`, - appId: appId, }) }) }) @@ -119,7 +88,7 @@ describe("/queries", () => { let datasource beforeEach(async () => { - datasource = await createDatasource() + datasource = await config.createDatasource() }) afterEach(() => { @@ -127,16 +96,16 @@ describe("/queries", () => { }) it("deletes a query and returns a success message", async () => { - const query = await createQuery() + const query = await config.createQuery() await request - .delete(`/api/queries/${query.id}/${query.rev}`) - .set(defaultHeaders(appId)) + .delete(`/api/queries/${query._id}/${query._rev}`) + .set(config.defaultHeaders()) .expect(200) const res = await request .get(`/api/queries`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -144,11 +113,10 @@ describe("/queries", () => { }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "DELETE", url: `/api/datasources/${datasource._id}/${datasource._rev}`, - appId: appId, }) }) }) From 678423300d87b6fc291f155033913b4773582ba2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 12:32:31 +0000 Subject: [PATCH 06/22] Refactoring to remove the use of the supertest, making this part of the test config. --- .../src/api/routes/tests/application.spec.js | 12 ++---- .../src/api/routes/tests/automation.spec.js | 13 ++---- .../src/api/routes/tests/datasource.spec.js | 12 ++---- .../src/api/routes/tests/permissions.spec.js | 10 ++--- .../server/src/api/routes/tests/query.spec.js | 10 ++--- .../server/src/api/routes/tests/role.spec.js | 40 +++++++------------ .../tests/utilities/TestConfiguration.js | 11 ++++- .../src/api/routes/tests/utilities/index.js | 13 ------ 8 files changed, 42 insertions(+), 79 deletions(-) diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index 8aa3d0288a..a453e60c0a 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -1,26 +1,22 @@ -const { - supertest, -} = require("./utilities") const TestConfig = require("./utilities/TestConfiguration") const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/applications", () => { let request - let server let config beforeAll(async () => { - ({ request, server } = await supertest()) - }); + config = new TestConfig() + request = config.request + }) beforeEach(async () => { await clearAllApps() - config = new TestConfig(request) await config.init() }) afterAll(() => { - server.close() + config.end() }) describe("create", () => { diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 3118f36903..8038ea8481 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -1,6 +1,3 @@ -const { - supertest, -} = require("./utilities") const TestConfig = require("./utilities/TestConfiguration") const { checkBuilderEndpoint, @@ -19,22 +16,20 @@ let LOGIC_DEFINITIONS = {} describe("/automations", () => { let request - let server - let app let config let automation beforeAll(async () => { - ({ request, server } = await supertest()) + config = new TestConfig() + request = config.request }) beforeEach(async () => { - config = new TestConfig(request) - app = await config.init() + await config.init() }) afterAll(() => { - server.close() + config.end() }) const triggerWorkflow = async automation => { diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 1f78c65f07..280c54f94b 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -1,22 +1,18 @@ -const { - supertest, -} = require("./utilities") let TestConfig = require("./utilities/TestConfiguration") let { basicDatasource } = require("./utilities/structures") let { checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/datasources", () => { let request - let server let config beforeAll(async () => { - ({ request, server } = await supertest()) - config = new TestConfig(request) - }); + config = new TestConfig() + request = config.request + }) afterAll(() => { - server.close() + config.end() }) beforeEach(async () => { diff --git a/packages/server/src/api/routes/tests/permissions.spec.js b/packages/server/src/api/routes/tests/permissions.spec.js index 046a0dc00b..13b07eeb2a 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.js +++ b/packages/server/src/api/routes/tests/permissions.spec.js @@ -1,6 +1,3 @@ -const { - supertest, -} = require("./utilities") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const TestConfig = require("./utilities/TestConfiguration") const { basicRow } = require("./utilities/structures") @@ -9,7 +6,6 @@ const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC describe("/permission", () => { - let server let request let table let perms @@ -17,12 +13,12 @@ describe("/permission", () => { let config beforeAll(async () => { - ;({ request, server } = await supertest()) - config = new TestConfig(request) + config = new TestConfig() + request = config.request }) afterAll(() => { - server.close() + config.end() }) beforeEach(async () => { diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 1151d7e622..98ef8f78cb 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -1,22 +1,18 @@ -const { - supertest, -} = require("./utilities") const TestConfig = require("./utilities/TestConfiguration") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { basicQuery } = require("./utilities/structures") describe("/queries", () => { let request - let server let config beforeAll(async () => { - ;({ request, server } = await supertest()) - config = new TestConfig(request) + config = new TestConfig() + request = config.request }) afterAll(() => { - server.close() + config.end() }) beforeEach(async () => { diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index b86b285dbc..122afdb07e 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -1,43 +1,33 @@ -const { - createApplication, - supertest, - defaultHeaders, -} = require("./couchTestUtils") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const { BUILTIN_PERMISSION_IDS, } = require("../../../utilities/security/permissions") - -const roleBody = { - name: "NewRole", - inherits: BUILTIN_ROLE_IDS.BASIC, - permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY, -} +const { basicRole } = require("./utilities/structures") +const TestConfig = require("./utilities/TestConfiguration") describe("/roles", () => { - let server let request - let appId + let config beforeAll(async () => { - ;({ request, server } = await supertest()) + config = new TestConfig() + request = config.request }) afterAll(() => { - server.close() + config.close() }) beforeEach(async () => { - let app = await createApplication(request) - appId = app.instance._id + await config.init() }) describe("create", () => { it("returns a success message when role is successfully created", async () => { const res = await request .post(`/api/roles`) - .send(roleBody) - .set(defaultHeaders(appId)) + .send(basicRole()) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -53,8 +43,8 @@ describe("/roles", () => { it("should list custom roles, plus 2 default roles", async () => { const createRes = await request .post(`/api/roles`) - .send(roleBody) - .set(defaultHeaders(appId)) + .send(basicRole()) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -62,7 +52,7 @@ describe("/roles", () => { const res = await request .get(`/api/roles`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -92,7 +82,7 @@ describe("/roles", () => { const createRes = await request .post(`/api/roles`) .send({ name: "user", permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -100,12 +90,12 @@ describe("/roles", () => { await request .delete(`/api/roles/${customRole._id}/${customRole._rev}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect(200) await request .get(`/api/roles/${customRole._id}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect(404) }) }) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 1c07a8f7ff..4e411b04bf 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -10,14 +10,17 @@ const { basicQuery, } = require("./structures") const controllers = require("./controllers") +const supertest = require("supertest") const EMAIL = "babs@babs.com" const PASSWORD = "babs_password" class TestConfiguration { - constructor(request) { + constructor() { + env.PORT = 4002 + this.server = require("../../../../app") + this.request = supertest(this.server) // we need the request for logging in, involves cookies, hard to fake - this.request = request this.appId = null } @@ -42,6 +45,10 @@ class TestConfiguration { return this.createApp(appName) } + end() { + this.server.close() + } + defaultHeaders() { const builderUser = { userId: "BUILDER", diff --git a/packages/server/src/api/routes/tests/utilities/index.js b/packages/server/src/api/routes/tests/utilities/index.js index 1421521a26..6e7825b599 100644 --- a/packages/server/src/api/routes/tests/utilities/index.js +++ b/packages/server/src/api/routes/tests/utilities/index.js @@ -1,16 +1,3 @@ -const supertest = require("supertest") -const env = require("../../../../environment") - 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 } -} From eacf3387dee4d026d167396c4e59a346f37fa0fe Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 13:07:33 +0000 Subject: [PATCH 07/22] Refactoring row test cases. --- .../server/src/api/routes/tests/row.spec.js | 93 ++++++++----------- .../tests/utilities/TestConfiguration.js | 16 ++-- packages/server/src/utilities/rowProcessor.js | 6 ++ 3 files changed, 55 insertions(+), 60 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 777a99b091..ebb2e009e8 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -1,66 +1,55 @@ -const { - createApplication, - createTable, - supertest, - defaultHeaders, - createLinkedTable, - createAttachmentTable, - makeBasicRow, -} = require("./couchTestUtils"); const { outputProcessing } = require("../../../utilities/rowProcessor") const env = require("../../../environment") +const TestConfig = require("./utilities/TestConfiguration") +const { basicRow } = require("./utilities/structures") describe("/rows", () => { let request - let server let appId let table let row let app + let config beforeAll(async () => { - ({ request, server } = await supertest()) - - }); + config = new TestConfig() + request = config.request + }) afterAll(() => { - server.close() + config.end() }) beforeEach(async () => { - app = await createApplication(request) + app = await config.init() appId = app.instance._id - table = await createTable(request, appId) - row = makeBasicRow(table._id) + table = await config.createTable() + row = basicRow(table._id) }) - const createRow = async r => - await request - .post(`/api/${r ? r.tableId : row.tableId}/rows`) - .send(r || row) - .set(defaultHeaders(appId)) - .expect('Content-Type', /json/) - .expect(200) - const loadRow = async id => await request .get(`/api/${table._id}/rows/${id}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) describe("save, load, update, delete", () => { it("returns a success message when the row is created", async () => { - const res = await createRow() + const res = await request + .post(`/api/${row.tableId}/rows`) + .send(row) + .set(config.defaultHeaders()) + .expect('Content-Type', /json/) + .expect(200) expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`) expect(res.body.name).toEqual("Test Contact") expect(res.body._rev).toBeDefined() }) it("updates a row successfully", async () => { - const rec = await createRow() - const existing = rec.body + const existing = await config.createRow() const res = await request .post(`/api/${table._id}/rows`) @@ -70,7 +59,7 @@ describe("/rows", () => { tableId: table._id, name: "Updated Name", }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -79,12 +68,11 @@ describe("/rows", () => { }) it("should load a row", async () => { - const rec = await createRow() - const existing = rec.body + const existing = await config.createRow() const res = await request .get(`/api/${table._id}/rows/${existing._id}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -102,12 +90,12 @@ describe("/rows", () => { name: "Second Contact", status: "new" } - await createRow() - await createRow(newRow) + await config.createRow() + await config.createRow(newRow) const res = await request .get(`/api/${table._id}/rows`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -117,10 +105,10 @@ describe("/rows", () => { }) it("load should return 404 when row does not exist", async () => { - await createRow() + await config.createRow() await request .get(`/api/${table._id}/rows/not-a-valid-id`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(404) }) @@ -132,7 +120,7 @@ describe("/rows", () => { const number = {type:"number", constraints: { type: "number", presence: false }} const datetime = {type:"datetime", constraints: { type: "string", presence: false, datetime: {earliest:"", latest: ""} }} - table = await createTable(request, appId, { + table = await config.createTable({ name: "TestTable2", type: "table", key: "name", @@ -187,7 +175,7 @@ describe("/rows", () => { attachmentEmpty : "", } - const id = (await createRow(row)).body._id + const id = (await config.createRow(row))._id const saved = (await loadRow(id)).body @@ -217,8 +205,7 @@ describe("/rows", () => { describe("patch", () => { it("should update only the fields that are supplied", async () => { - const rec = await createRow() - const existing = rec.body + const existing = await config.createRow() const res = await request .patch(`/api/${table._id}/rows/${existing._id}`) @@ -228,7 +215,7 @@ describe("/rows", () => { tableId: table._id, name: "Updated Name", }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -249,7 +236,7 @@ describe("/rows", () => { const result = await request .post(`/api/${table._id}/rows/validate`) .send({ name: "ivan" }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -261,7 +248,7 @@ describe("/rows", () => { const result = await request .post(`/api/${table._id}/rows/validate`) .send({ name: 1 }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -273,18 +260,18 @@ describe("/rows", () => { describe("enrich row unit test", () => { it("should allow enriching some linked rows", async () => { - const table = await createLinkedTable(request, appId) - const firstRow = (await createRow({ + const table = await config.createLinkedTable() + const firstRow = await config.createRow({ name: "Test Contact", description: "original description", tableId: table._id - })).body - const secondRow = (await createRow({ + }) + const secondRow = await config.createRow({ name: "Test 2", description: "og desc", link: [{_id: firstRow._id}], tableId: table._id, - })).body + }) const enriched = await outputProcessing(appId, table, [secondRow]) expect(enriched[0].link.length).toBe(1) expect(enriched[0].link[0]._id).toBe(firstRow._id) @@ -293,15 +280,15 @@ describe("/rows", () => { }) it("should allow enriching attachment rows", async () => { - const table = await createAttachmentTable(request, appId) - const row = (await createRow({ + const table = await config.createAttachmentTable() + const row = await config.createRow({ name: "test", description: "test", attachment: [{ url: "/test/thing", }], tableId: table._id, - })).body + }) // the environment needs configured for this env.CLOUD = 1 env.SELF_HOSTED = 1 diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 4e411b04bf..093a19c9d1 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -94,16 +94,18 @@ class TestConfiguration { return this.updateTable(config) } - async createLinkedTables() { - const table = await this.createTable() - table.primaryDisplay = "name" - table.schema.link = { + 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: table._id, + tableId: this.table._id, } - const linkedTable = await this.createTable(table) - this.table = table + const linkedTable = await this.createTable(tableConfig) this.linkedTable = linkedTable return linkedTable } diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index b76177865b..eddd597459 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -49,6 +49,12 @@ const TYPE_TRANSFORM_MAP = { "": null, [undefined]: undefined, [null]: null, + parse: date => { + if (date instanceof Date) { + return date.toISOString() + } + return date + }, }, [FieldTypes.ATTACHMENT]: { "": [], From 2a962b1c033baa38980bfef773d9b732ed5fed99 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 14:36:59 +0000 Subject: [PATCH 08/22] Refactoring table testcases. --- .../server/src/api/routes/tests/table.spec.js | 125 ++++++++---------- .../tests/utilities/TestConfiguration.js | 5 + 2 files changed, 62 insertions(+), 68 deletions(-) diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index 67d53a489b..976db6ee56 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -1,30 +1,22 @@ -const { - createTable, - supertest, - createApplication, - defaultHeaders, - builderEndpointShouldBlockNormalUsers, - getDocument -} = require("./couchTestUtils") +const TestConfig = require("./utilities/TestConfiguration") +const { checkBuilderEndpoint } = require("./utilities/TestFunctions") describe("/tables", () => { let request - let server - let app - let appId + let config beforeAll(async () => { - ({ request, server } = await supertest()) - }); + config = new TestConfig() + request = config.request + }) afterAll(() => { - server.close() + config.end() }) beforeEach(async () => { - app = await createApplication(request) - appId = app.instance._id - }); + await config.init() + }) describe("create", () => { it("returns a success message when the table is successfully created", done => { @@ -37,25 +29,25 @@ describe("/tables", () => { name: { type: "string" } } }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) .end(async (err, res) => { - expect(res.res.statusMessage).toEqual("Table TestTable saved successfully."); - expect(res.body.name).toEqual("TestTable"); - done(); - }); + expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.") + expect(res.body.name).toEqual("TestTable") + done() + }) }) it("renames all the row fields for a table when a schema key is renamed", async () => { - const testTable = await createTable(request, appId); + const testTable = await config.createTable() const testRow = await request .post(`/api/${testTable._id}/rows`) .send({ name: "test" }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) @@ -74,29 +66,28 @@ describe("/tables", () => { updatedName: { type: "string" } } }) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) - expect(updatedTable.res.statusMessage).toEqual("Table TestTable saved successfully."); - expect(updatedTable.body.name).toEqual("TestTable"); + expect(updatedTable.res.statusMessage).toEqual("Table TestTable saved successfully.") + expect(updatedTable.body.name).toEqual("TestTable") const res = await request .get(`/api/${testTable._id}/rows/${testRow.body._id}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) - expect(res.body.updatedName).toEqual("test"); - expect(res.body.name).toBeUndefined(); - }); + expect(res.body.updatedName).toEqual("test") + expect(res.body.name).toBeUndefined() + }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "POST", url: `/api/tables`, - appId: appId, body: { name: "TestTable", key: "name", @@ -106,68 +97,67 @@ describe("/tables", () => { } }) }) - }); + }) describe("fetch", () => { let testTable beforeEach(async () => { - testTable = await createTable(request, appId, testTable) - }); + testTable = await config.createTable(testTable) + }) afterEach(() => { delete testTable._rev - }); + }) it("returns all the tables for that instance in the response body", done => { request .get(`/api/tables`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) .end(async (_, res) => { - const fetchedTable = res.body[0]; - expect(fetchedTable.name).toEqual(testTable.name); - expect(fetchedTable.type).toEqual("table"); - done(); - }); + const fetchedTable = res.body[0] + expect(fetchedTable.name).toEqual(testTable.name) + expect(fetchedTable.type).toEqual("table") + done() + }) }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "GET", url: `/api/tables`, - appId: appId, }) }) - }); + }) describe("destroy", () => { - let testTable; + let testTable beforeEach(async () => { - testTable = await createTable(request, appId, testTable) - }); + testTable = await config.createTable(testTable) + }) afterEach(() => { delete testTable._rev - }); + }) it("returns a success response when a table is deleted.", async done => { request .delete(`/api/tables/${testTable._id}/${testTable._rev}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) .end(async (_, res) => { - expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`); - done(); - }); + expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`) + done() + }) }) it("deletes linked references to the table after deletion", async done => { - const linkedTable = await createTable(request, appId, { + const linkedTable = await config.createTable({ name: "LinkedTable", type: "table", key: "name", @@ -190,25 +180,24 @@ describe("/tables", () => { request .delete(`/api/tables/${testTable._id}/${testTable._rev}`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) .end(async (_, res) => { - expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`); - const dependentTable = await getDocument(appId, linkedTable._id) - expect(dependentTable.schema.TestTable).not.toBeDefined(); - done(); - }); + expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`) + const dependentTable = await config.getTable(linkedTable._id) + expect(dependentTable.schema.TestTable).not.toBeDefined() + done() + }) }) it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, + await checkBuilderEndpoint({ + config, method: "DELETE", url: `/api/tables/${testTable._id}/${testTable._rev}`, - appId: appId, }) }) - }); -}); + }) +}) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 093a19c9d1..6ecf9fc1c2 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -94,6 +94,11 @@ class TestConfiguration { 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." From 50f1535f2c90f7f828797a6a36da9c2c1c9b0130 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Mar 2021 16:54:44 +0000 Subject: [PATCH 09/22] refactor user test --- .../server/src/api/routes/tests/user.spec.js | 22 +++++++++---------- .../api/routes/tests/utilities/structures.js | 8 +++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 771b547ef8..1f83ddfe33 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -1,11 +1,8 @@ const { - createApplication, - supertest, - defaultHeaders, - createUser, testPermissionsForEndpoint, } = require("./couchTestUtils") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") +const TestConfig = require("./utilities/TestConfiguration") const { cloneDeep } = require("lodash/fp") const baseBody = { @@ -19,28 +16,29 @@ describe("/users", () => { let server let app let appId + let config beforeAll(async () => { - ;({ request, server } = await supertest(server)) + config = new TestConfig() + request = config.request }) beforeEach(async () => { - app = await createApplication(request) + app = await config.init() appId = app.instance._id }) afterAll(() => { - server.close() - server.destroy() + config.end() }) describe("fetch", () => { it("returns a list of users from an instance db", async () => { - await createUser(request, appId, "brenda@brenda.com", "brendas_password") - await createUser(request, appId, "pam@pam.com", "pam_password") + await config.createUser("brenda@brenda.com", "brendas_password") + await config.createUser("pam@pam.com", "pam_password") const res = await request .get(`/api/users`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -50,7 +48,7 @@ describe("/users", () => { }) it("should apply authorization to endpoint", async () => { - await createUser(request, appId, "brenda@brenda.com", "brendas_password") + await config.createUser("brenda@brenda.com", "brendas_password") await testPermissionsForEndpoint({ request, method: "GET", diff --git a/packages/server/src/api/routes/tests/utilities/structures.js b/packages/server/src/api/routes/tests/utilities/structures.js index 4a21602e40..922228aadf 100644 --- a/packages/server/src/api/routes/tests/utilities/structures.js +++ b/packages/server/src/api/routes/tests/utilities/structures.js @@ -77,3 +77,11 @@ exports.basicQuery = datasourceId => { queryVerb: "read", } } + +exports.basicUser = role => { + return { + email: "bill@bill.com", + password: "yeeooo", + roleId: role, + } +} From 06e95e11eacb5ee8f629fdbfee8bf03e444dd47b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 17:06:33 +0000 Subject: [PATCH 10/22] Small amount more user refactoring, removing use of couchTestUtils. --- .../server/src/api/routes/tests/user.spec.js | 21 ++++------ .../routes/tests/utilities/TestFunctions.js | 40 ++++++++++--------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 1f83ddfe33..3405f0716d 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -1,8 +1,6 @@ -const { - testPermissionsForEndpoint, -} = require("./couchTestUtils") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") const TestConfig = require("./utilities/TestConfiguration") +const { checkPermissionsEndpoint } = require("./utilities/TestFunctions") const { cloneDeep } = require("lodash/fp") const baseBody = { @@ -13,9 +11,6 @@ const baseBody = { describe("/users", () => { let request - let server - let app - let appId let config beforeAll(async () => { @@ -24,8 +19,7 @@ describe("/users", () => { }) beforeEach(async () => { - app = await config.init() - appId = app.instance._id + await config.init() }) afterAll(() => { @@ -49,11 +43,11 @@ describe("/users", () => { it("should apply authorization to endpoint", async () => { await config.createUser("brenda@brenda.com", "brendas_password") - await testPermissionsForEndpoint({ + await checkPermissionsEndpoint({ + config, request, method: "GET", url: `/api/users`, - appId: appId, passRole: BUILTIN_ROLE_IDS.ADMIN, failRole: BUILTIN_ROLE_IDS.PUBLIC, }) @@ -66,7 +60,7 @@ describe("/users", () => { body.email = "bill@budibase.com" const res = await request .post(`/api/users`) - .set(defaultHeaders(appId)) + .set(config.defaultHeaders()) .send(body) .expect(200) .expect("Content-Type", /json/) @@ -78,12 +72,11 @@ describe("/users", () => { it("should apply authorization to endpoint", async () => { const body = cloneDeep(baseBody) body.email = "brandNewUser@user.com" - await testPermissionsForEndpoint({ - request, + await checkPermissionsEndpoint({ + config, method: "POST", body, url: `/api/users`, - appId: appId, passRole: BUILTIN_ROLE_IDS.ADMIN, failRole: BUILTIN_ROLE_IDS.PUBLIC, }) diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index d7a32f4416..02e4d8dfe5 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -54,24 +54,28 @@ exports.checkBuilderEndpoint = async ({ config, method, url, body }) => { .expect(403) } -/** - * Raw DB insert utility. - */ -exports.insertDocument = async (databaseId, document) => { - const { id, ...documentFields } = document - return await new CouchDB(databaseId).put({ _id: id, ...documentFields }) -} +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) -/** - * Raw DB delete utility. - */ -exports.destroyDocument = async (databaseId, documentId) => { - return await new CouchDB(databaseId).destroy(documentId) -} + await exports + .createRequest(config.request, method, url, body) + .set(passHeader) + .expect(200) -/** - * Raw DB get utility. - */ -exports.getDocument = async (databaseId, documentId) => { - return await new CouchDB(databaseId).get(documentId) + 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) } From 0be5eb0b98066fa9d906b65dc4717e76cb2618c7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Mar 2021 17:12:44 +0000 Subject: [PATCH 11/22] Moving comment. --- .../server/src/api/routes/tests/utilities/TestConfiguration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 6ecf9fc1c2..8482d27118 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -19,8 +19,8 @@ class TestConfiguration { constructor() { env.PORT = 4002 this.server = require("../../../../app") - this.request = supertest(this.server) // we need the request for logging in, involves cookies, hard to fake + this.request = supertest(this.server) this.appId = null } From e2a6bb935e30bf3a14ef76b2ed38b007ec6685c3 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Mar 2021 17:55:24 +0000 Subject: [PATCH 12/22] refactor view tests --- .../src/api/routes/tests/couchTestUtils.js | 326 ------------------ .../server/src/api/routes/tests/view.spec.js | 202 ++++++----- 2 files changed, 107 insertions(+), 421 deletions(-) delete mode 100644 packages/server/src/api/routes/tests/couchTestUtils.js diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js deleted file mode 100644 index 90d15612f0..0000000000 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ /dev/null @@ -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) -} diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index f1ee5d8218..8a7f9579a0 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -1,65 +1,67 @@ -const { - createApplication, - createTable, - supertest, - defaultHeaders, - getDocument -} = require("./couchTestUtils") +const TestConfig = require("./utilities/TestConfiguration"); describe("/views", () => { - let request - let server - let app - let appId - let table - - const createView = async (config = { - name: "TestView", - field: "Price", - calculation: "stats", - tableId: table._id - }) => - await request - .post(`/api/views`) - .send(config) - .set(defaultHeaders(appId)) - .expect('Content-Type', /json/) - .expect(200) - - const createRow = async row => request - .post(`/api/${table._id}/rows`) - .send(row) - .set(defaultHeaders(appId)) - .expect('Content-Type', /json/) - .expect(200) + let request; + let server; + let app; + let config; + let appId; + let table; beforeAll(async () => { - ({ request, server } = await supertest()) - }) + config = new TestConfig(); + request = config.request; + }); beforeEach(async () => { - app = await createApplication(request) - appId = app.instance._id - }) + app = await config.init(); + appId = app.instance._id; + }); afterAll(() => { - server.close() - }) + config.end(); + }); describe("create", () => { beforeEach(async () => { - table = await createTable(request, appId); - }) + table = await config.createTable(); + }); it("returns a success message when the view is successfully created", async () => { - const res = await createView() - expect(res.res.statusMessage).toEqual("View TestView saved successfully."); - }) + const res = await request + .post(`/api/views`) + .send({ + name: "TestView", + field: "Price", + calculation: "stats", + tableId: table._id, + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200); + + expect(res.res.statusMessage).toEqual( + "View TestView saved successfully." + ); + }); it("updates the table row with the new view metadata", async () => { - const res = await createView() - expect(res.res.statusMessage).toEqual("View TestView saved successfully."); - const updatedTable = await getDocument(appId, table._id) + const res = await request + .post(`/api/views`) + .send({ + name: "TestView", + field: "Price", + calculation: "stats", + tableId: table._id, + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200); + + expect(res.res.statusMessage).toEqual( + "View TestView saved successfully." + ); + const updatedTable = await config.getTable(table._id); expect(updatedTable.views).toEqual({ TestView: { field: "Price", @@ -88,88 +90,98 @@ describe("/views", () => { field: { type: "string", }, - } - } + }, + }, }); - }) + }); }); describe("fetch", () => { beforeEach(async () => { - table = await createTable(request, appId); + table = await config.createTable(); }); it("returns only custom views", async () => { - await createView() + await config.createView({ + name: "TestView", + field: "Price", + calculation: "stats", + tableId: table._id, + }); const res = await request .get(`/api/views`) - .set(defaultHeaders(appId)) - .expect('Content-Type', /json/) - .expect(200) - expect(res.body.length).toBe(1) - expect(res.body.find(({ name }) => name === "TestView")).toBeDefined() - }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200); + expect(res.body.length).toBe(1); + expect(res.body.find(({ name }) => name === "TestView")).toBeDefined(); + }); }); describe("query", () => { beforeEach(async () => { - table = await createTable(request, appId); + table = await config.createTable(); }); it("returns data for the created view", async () => { - await createView() - await createRow({ + await config.createView({ + name: "TestView", + field: "Price", + calculation: "stats", tableId: table._id, - Price: 1000 - }) - await createRow({ + }); + await config.createRow({ tableId: table._id, - Price: 2000 - }) - await createRow({ + Price: 1000, + }); + await config.createRow({ tableId: table._id, - Price: 4000 - }) + Price: 2000, + }); + await config.createRow({ + tableId: table._id, + Price: 4000, + }); const res = await request .get(`/api/views/TestView?calculation=stats`) - .set(defaultHeaders(appId)) - .expect('Content-Type', /json/) - .expect(200) - expect(res.body.length).toBe(1) - expect(res.body).toMatchSnapshot() - }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200); + expect(res.body.length).toBe(1); + expect(res.body).toMatchSnapshot(); + }); it("returns data for the created view using a group by", async () => { - await createView({ + await config.createView({ calculation: "stats", name: "TestView", field: "Price", groupBy: "Category", - tableId: table._id - }) - await createRow({ + tableId: table._id, + }); + await config.createRow({ tableId: table._id, Price: 1000, - Category: "One" - }) - await createRow({ + Category: "One", + }); + await config.createRow({ tableId: table._id, Price: 2000, - Category: "One" - }) - await createRow({ + Category: "One", + }); + await config.createRow({ tableId: table._id, Price: 4000, - Category: "Two" - }) + Category: "Two", + }); const res = await request .get(`/api/views/TestView?calculation=stats&group=Category`) - .set(defaultHeaders(appId)) - .expect('Content-Type', /json/) - .expect(200) - - expect(res.body.length).toBe(2) - expect(res.body).toMatchSnapshot() - }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200); + + expect(res.body.length).toBe(2); + expect(res.body).toMatchSnapshot(); + }); }); -}); \ No newline at end of file +}); From 78c550ff3032f6a342a9e3a771760c92a603af75 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Mar 2021 17:55:58 +0000 Subject: [PATCH 13/22] remove variable --- packages/server/src/api/routes/tests/view.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index 8a7f9579a0..50abb1b855 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -2,7 +2,6 @@ const TestConfig = require("./utilities/TestConfiguration"); describe("/views", () => { let request; - let server; let app; let config; let appId; From af3d84a1d96d609a05fbb3fb6ce086a4df6b61a3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Mar 2021 11:24:15 +0000 Subject: [PATCH 14/22] Some more quick modifications to test re-factor before PR. --- .../src/api/routes/tests/automation.spec.js | 3 +- .../server/src/api/routes/tests/testUtils.js | 1 - .../api/routes/tests/utilities/controllers.js | 2 +- .../src/api/routes/tests/utilities/index.js | 2 + .../server/src/api/routes/tests/view.spec.js | 103 +++++++++--------- 5 files changed, 54 insertions(+), 57 deletions(-) delete mode 100644 packages/server/src/api/routes/tests/testUtils.js diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 8038ea8481..c737f1da6b 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -5,8 +5,7 @@ const { clearAllAutomations, } = require("./utilities/TestFunctions") const { basicAutomation } = require("./utilities/structures") - -const { delay } = require("./testUtils") +const { delay } = require("./utilities") const MAX_RETRIES = 4 diff --git a/packages/server/src/api/routes/tests/testUtils.js b/packages/server/src/api/routes/tests/testUtils.js deleted file mode 100644 index 0e66b47c3d..0000000000 --- a/packages/server/src/api/routes/tests/testUtils.js +++ /dev/null @@ -1 +0,0 @@ -module.exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) diff --git a/packages/server/src/api/routes/tests/utilities/controllers.js b/packages/server/src/api/routes/tests/utilities/controllers.js index d1e23d4527..541495bec8 100644 --- a/packages/server/src/api/routes/tests/utilities/controllers.js +++ b/packages/server/src/api/routes/tests/utilities/controllers.js @@ -8,5 +8,5 @@ module.exports = { user: require("../../../controllers/user"), automation: require("../../../controllers/automation"), datasource: require("../../../controllers/datasource"), - query: require("../../../controllers/query") + query: require("../../../controllers/query"), } diff --git a/packages/server/src/api/routes/tests/utilities/index.js b/packages/server/src/api/routes/tests/utilities/index.js index 6e7825b599..7786dc2bcc 100644 --- a/packages/server/src/api/routes/tests/utilities/index.js +++ b/packages/server/src/api/routes/tests/utilities/index.js @@ -1,3 +1,5 @@ const TEST_CLIENT_ID = "test-client-id" exports.TEST_CLIENT_ID = TEST_CLIENT_ID + +exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms)) diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index 50abb1b855..39549daa98 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -1,30 +1,27 @@ -const TestConfig = require("./utilities/TestConfiguration"); +const TestConfig = require("./utilities/TestConfiguration") describe("/views", () => { - let request; - let app; - let config; - let appId; - let table; + let request + let config + let table beforeAll(async () => { - config = new TestConfig(); - request = config.request; - }); + config = new TestConfig() + request = config.request + }) beforeEach(async () => { - app = await config.init(); - appId = app.instance._id; - }); + await config.init() + }) afterAll(() => { - config.end(); - }); + config.end() + }) describe("create", () => { beforeEach(async () => { - table = await config.createTable(); - }); + table = await config.createTable() + }) it("returns a success message when the view is successfully created", async () => { const res = await request @@ -37,12 +34,12 @@ describe("/views", () => { }) .set(config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200); + .expect(200) expect(res.res.statusMessage).toEqual( "View TestView saved successfully." - ); - }); + ) + }) it("updates the table row with the new view metadata", async () => { const res = await request @@ -55,12 +52,12 @@ describe("/views", () => { }) .set(config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200); + .expect(200) expect(res.res.statusMessage).toEqual( "View TestView saved successfully." - ); - const updatedTable = await config.getTable(table._id); + ) + const updatedTable = await config.getTable(table._id) expect(updatedTable.views).toEqual({ TestView: { field: "Price", @@ -91,14 +88,14 @@ describe("/views", () => { }, }, }, - }); - }); - }); + }) + }) + }) describe("fetch", () => { beforeEach(async () => { - table = await config.createTable(); - }); + table = await config.createTable() + }) it("returns only custom views", async () => { await config.createView({ @@ -106,21 +103,21 @@ describe("/views", () => { field: "Price", calculation: "stats", tableId: table._id, - }); + }) const res = await request .get(`/api/views`) .set(config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200); - expect(res.body.length).toBe(1); - expect(res.body.find(({ name }) => name === "TestView")).toBeDefined(); - }); - }); + .expect(200) + expect(res.body.length).toBe(1) + expect(res.body.find(({ name }) => name === "TestView")).toBeDefined() + }) + }) describe("query", () => { beforeEach(async () => { - table = await config.createTable(); - }); + table = await config.createTable() + }) it("returns data for the created view", async () => { await config.createView({ @@ -128,27 +125,27 @@ describe("/views", () => { field: "Price", calculation: "stats", tableId: table._id, - }); + }) await config.createRow({ tableId: table._id, Price: 1000, - }); + }) await config.createRow({ tableId: table._id, Price: 2000, - }); + }) await config.createRow({ tableId: table._id, Price: 4000, - }); + }) const res = await request .get(`/api/views/TestView?calculation=stats`) .set(config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200); - expect(res.body.length).toBe(1); - expect(res.body).toMatchSnapshot(); - }); + .expect(200) + expect(res.body.length).toBe(1) + expect(res.body).toMatchSnapshot() + }) it("returns data for the created view using a group by", async () => { await config.createView({ @@ -157,30 +154,30 @@ describe("/views", () => { field: "Price", groupBy: "Category", tableId: table._id, - }); + }) await config.createRow({ tableId: table._id, Price: 1000, Category: "One", - }); + }) await config.createRow({ tableId: table._id, Price: 2000, Category: "One", - }); + }) await config.createRow({ tableId: table._id, Price: 4000, Category: "Two", - }); + }) const res = await request .get(`/api/views/TestView?calculation=stats&group=Category`) .set(config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200); + .expect(200) - expect(res.body.length).toBe(2); - expect(res.body).toMatchSnapshot(); - }); - }); -}); + expect(res.body.length).toBe(2) + expect(res.body).toMatchSnapshot() + }) + }) +}) From 67f47df7188b8b608ecaa72bc1f88782250e17dd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Mar 2021 12:11:44 +0000 Subject: [PATCH 15/22] Reducing the boilerplate needed around setting up a test case. --- .../server/src/api/controllers/datasource.js | 3 +- .../src/api/routes/tests/application.spec.js | 15 +++------ .../src/api/routes/tests/automation.spec.js | 20 ++++------- .../src/api/routes/tests/datasource.spec.js | 17 +++------- .../src/api/routes/tests/permissions.spec.js | 15 +++------ .../server/src/api/routes/tests/query.spec.js | 15 +++------ .../server/src/api/routes/tests/role.spec.js | 15 +++------ .../server/src/api/routes/tests/row.spec.js | 26 +++++---------- .../server/src/api/routes/tests/table.spec.js | 15 +++------ .../server/src/api/routes/tests/user.spec.js | 27 ++++----------- .../tests/utilities/TestConfiguration.js | 8 +++++ .../src/api/routes/tests/utilities/index.js | 33 +++++++++++++++++-- .../server/src/api/routes/tests/view.spec.js | 15 +++------ 13 files changed, 89 insertions(+), 135 deletions(-) diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 8fc3efc8a9..15cea72da7 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -8,14 +8,13 @@ const { exports.fetch = async function(ctx) { const database = new CouchDB(ctx.user.appId) - const datasources = ( + ctx.body = ( await database.allDocs( getDatasourceParams(null, { include_docs: true, }) ) ).rows.map(row => row.doc) - ctx.body = datasources } exports.save = async function(ctx) { diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index a453e60c0a..c146cc63fb 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -1,24 +1,17 @@ -const TestConfig = require("./utilities/TestConfiguration") const { clearAllApps, checkBuilderEndpoint } = require("./utilities/TestFunctions") +const setup = require("./utilities") describe("/applications", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) + afterAll(setup.afterAll) beforeEach(async () => { await clearAllApps() await config.init() }) - afterAll(() => { - config.end() - }) - describe("create", () => { it("returns a success message when the application is successfully created", async () => { const res = await request diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index c737f1da6b..0648bfefa5 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -1,11 +1,10 @@ -const TestConfig = require("./utilities/TestConfiguration") const { checkBuilderEndpoint, getAllTableRows, clearAllAutomations, } = require("./utilities/TestFunctions") const { basicAutomation } = require("./utilities/structures") -const { delay } = require("./utilities") +const setup = require("./utilities") const MAX_RETRIES = 4 @@ -14,23 +13,16 @@ let TRIGGER_DEFINITIONS = {} let LOGIC_DEFINITIONS = {} describe("/automations", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() let automation - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() }) - afterAll(() => { - config.end() - }) - const triggerWorkflow = async automation => { return await request .post(`/api/automations/${automation._id}/trigger`) @@ -133,7 +125,7 @@ describe("/automations", () => { automation.definition.trigger.inputs.tableId = table._id automation.definition.steps[0].inputs.row.tableId = table._id automation = await config.createAutomation(automation) - await delay(500) + await setup.delay(500) const res = await triggerWorkflow(automation) // this looks a bit mad but we don't actually have a way to wait for a response from the automation to // know that it has finished all of its actions - this is currently the best way @@ -142,7 +134,7 @@ describe("/automations", () => { for (let tries = 0; tries < MAX_RETRIES; tries++) { expect(res.body.message).toEqual(`Automation ${automation._id} has been triggered.`) expect(res.body.automation.name).toEqual(automation.name) - await delay(500) + await setup.delay(500) let elements = await getAllTableRows(config) // don't test it unless there are values to test if (elements.length > 1) { diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 280c54f94b..7602b027e1 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -1,23 +1,16 @@ -let TestConfig = require("./utilities/TestConfiguration") let { basicDatasource } = require("./utilities/structures") let { checkBuilderEndpoint } = require("./utilities/TestFunctions") +let setup = require("./utilities") describe("/datasources", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) - - afterAll(() => { - config.end() - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() - }); + }) describe("create", () => { it("should create a new datasource", async () => { diff --git a/packages/server/src/api/routes/tests/permissions.spec.js b/packages/server/src/api/routes/tests/permissions.spec.js index 13b07eeb2a..93e6e29131 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.js +++ b/packages/server/src/api/routes/tests/permissions.spec.js @@ -1,25 +1,18 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") -const TestConfig = require("./utilities/TestConfiguration") +const setup = require("./utilities") const { basicRow } = require("./utilities/structures") const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC describe("/permission", () => { - let request + let request = setup.getRequest() + let config = setup.getConfig() let table let perms let row - let config - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) - - afterAll(() => { - config.end() - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 98ef8f78cb..765baa4426 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -1,19 +1,12 @@ -const TestConfig = require("./utilities/TestConfiguration") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { basicQuery } = require("./utilities/structures") +const setup = require("./utilities") describe("/queries", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) - - afterAll(() => { - config.end() - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index 122afdb07e..9c08bf289a 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -3,20 +3,13 @@ const { BUILTIN_PERMISSION_IDS, } = require("../../../utilities/security/permissions") const { basicRole } = require("./utilities/structures") -const TestConfig = require("./utilities/TestConfiguration") +const setup = require("./utilities") describe("/roles", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) - - afterAll(() => { - config.close() - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index ebb2e009e8..cc50520b77 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -1,28 +1,18 @@ const { outputProcessing } = require("../../../utilities/rowProcessor") const env = require("../../../environment") -const TestConfig = require("./utilities/TestConfiguration") const { basicRow } = require("./utilities/structures") +const setup = require("./utilities") describe("/rows", () => { - let request - let appId + let request = setup.getRequest() + let config = setup.getConfig() let table let row - let app - let config - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) - - afterAll(() => { - config.end() - }) + afterAll(setup.afterAll) beforeEach(async () => { - app = await config.init() - appId = app.instance._id + await config.init() table = await config.createTable() row = basicRow(table._id) }) @@ -272,7 +262,7 @@ describe("/rows", () => { link: [{_id: firstRow._id}], tableId: table._id, }) - const enriched = await outputProcessing(appId, table, [secondRow]) + const enriched = await outputProcessing(config.getAppId(), table, [secondRow]) expect(enriched[0].link.length).toBe(1) expect(enriched[0].link[0]._id).toBe(firstRow._id) expect(enriched[0].link[0].primaryDisplay).toBe("Test Contact") @@ -292,8 +282,8 @@ describe("/rows", () => { // the environment needs configured for this env.CLOUD = 1 env.SELF_HOSTED = 1 - const enriched = await outputProcessing(appId, table, [row]) - expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${appId}/test/thing`) + const enriched = await outputProcessing(config.getAppId(), table, [row]) + expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${config.getAppId()}/test/thing`) // remove env config env.CLOUD = undefined env.SELF_HOSTED = undefined diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index 976db6ee56..1a2df624f1 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -1,18 +1,11 @@ -const TestConfig = require("./utilities/TestConfiguration") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") +const setup = require("./utilities") describe("/tables", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) - - afterAll(() => { - config.end() - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 3405f0716d..6ec607a093 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -1,31 +1,18 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") -const TestConfig = require("./utilities/TestConfiguration") const { checkPermissionsEndpoint } = require("./utilities/TestFunctions") -const { cloneDeep } = require("lodash/fp") - -const baseBody = { - email: "bill@bill.com", - password: "yeeooo", - roleId: BUILTIN_ROLE_IDS.POWER, -} +const { basicUser } = require("./utilities/structures") +const setup = require("./utilities") describe("/users", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() }) - afterAll(() => { - config.end() - }) - describe("fetch", () => { it("returns a list of users from an instance db", async () => { await config.createUser("brenda@brenda.com", "brendas_password") @@ -56,7 +43,7 @@ describe("/users", () => { describe("create", () => { it("returns a success message when a user is successfully created", async () => { - const body = cloneDeep(baseBody) + const body = basicUser(BUILTIN_ROLE_IDS.POWER) body.email = "bill@budibase.com" const res = await request .post(`/api/users`) @@ -70,7 +57,7 @@ describe("/users", () => { }) it("should apply authorization to endpoint", async () => { - const body = cloneDeep(baseBody) + const body = basicUser(BUILTIN_ROLE_IDS.POWER) body.email = "brandNewUser@user.com" await checkPermissionsEndpoint({ config, diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index 8482d27118..5f6b1cc267 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -24,6 +24,14 @@ class TestConfiguration { 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 diff --git a/packages/server/src/api/routes/tests/utilities/index.js b/packages/server/src/api/routes/tests/utilities/index.js index 7786dc2bcc..7e9260ce18 100644 --- a/packages/server/src/api/routes/tests/utilities/index.js +++ b/packages/server/src/api/routes/tests/utilities/index.js @@ -1,5 +1,32 @@ -const TEST_CLIENT_ID = "test-client-id" - -exports.TEST_CLIENT_ID = TEST_CLIENT_ID +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 +} diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index 39549daa98..a80b09d3a0 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -1,23 +1,16 @@ -const TestConfig = require("./utilities/TestConfiguration") +const setup = require("./utilities") describe("/views", () => { - let request - let config + let request = setup.getRequest() + let config = setup.getConfig() let table - beforeAll(async () => { - config = new TestConfig() - request = config.request - }) + afterAll(setup.afterAll) beforeEach(async () => { await config.init() }) - afterAll(() => { - config.end() - }) - describe("create", () => { beforeEach(async () => { table = await config.createTable() From 28a644958f7ef74101a7446e8b7b7dcdc95b197f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Mar 2021 12:55:52 +0000 Subject: [PATCH 16/22] linting. --- packages/server/src/api/routes/tests/utilities/TestFunctions.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index 02e4d8dfe5..534119d279 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -1,7 +1,5 @@ const rowController = require("../../../controllers/row") const appController = require("../../../controllers/application") -const autoController = require("../../../controllers/automation") -const CouchDB = require("../../../../db") function Request(appId, params) { this.user = { appId } From c03e361710ba24a914a6fee8cfe53c731a090ebc Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 5 Mar 2021 13:52:26 +0000 Subject: [PATCH 17/22] Add cypress tests for data bindings and component creation --- packages/builder/cypress.json | 4 +- .../cypress/integration/createApp.spec.js | 26 ++--- .../cypress/integration/createBinding.spec.js | 49 +++++--- .../integration/createComponents.spec.js | 110 ++++++++++-------- packages/builder/cypress/support/commands.js | 58 +++++---- .../AppPreview/ComponentSelectionList.svelte | 2 + .../PropertyControls/PropertyControl.svelte | 2 +- .../src/components/settings/Link.svelte | 5 +- .../src/components/start/AppCard.svelte | 2 +- 9 files changed, 153 insertions(+), 105 deletions(-) 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..7195da24ce 100644 --- a/packages/builder/cypress/integration/createApp.spec.js +++ b/packages/builder/cypress/integration/createApp.spec.js @@ -1,18 +1,12 @@ -context('Create an Application', () => { +context("Create an Application", () => { + beforeEach(() => { + cy.server() + cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) + }) - 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') - }) + it("should create a new application", () => { + cy.createApp("My Cool App", "This is a description") + cy.visit(`localhost:${Cypress.env("PORT")}/_builder`) + cy.contains("My Cool App").should("exist") + }) }) diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js index 4074aca112..0550f4204b 100644 --- a/packages/builder/cypress/integration/createBinding.spec.js +++ b/packages/builder/cypress/integration/createBinding.spec.js @@ -1,18 +1,37 @@ -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.createApp("Cypress Tests", "Cypress test app") + 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 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", "") + }) + }) }) + +const addSettingBinding = (setting, bindingText) => { + cy.get(`[data-cy="setting-${setting}"] [data-cy=text-binding-button]`).click() + cy.get(".drawer").within(() => { + cy.contains(bindingText).click() + cy.get("textarea").should("have.value", `{{ ${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..c668734f4b 100644 --- a/packages/builder/cypress/integration/createComponents.spec.js +++ b/packages/builder/cypress/integration/createComponents.spec.js @@ -1,60 +1,72 @@ -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.createApp("Cypress Tests", "Cypress test app") + 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) + }) + }) }) }) - -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/support/commands.js b/packages/builder/cypress/support/commands.js index e92034c120..b961181241 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -25,8 +25,9 @@ // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) Cypress.Commands.add("createApp", name => { + cy.deleteApp(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 +42,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 +56,22 @@ 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("createTestTableWithData", () => { cy.createTable("dog") cy.addColumn("dog", "name", "Text") @@ -87,6 +102,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 +112,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 +127,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 +146,27 @@ 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("/") + return cy.wrap(params[params.length - 1]) + }) }) -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/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 @@
onItemChosen(item, idx)} class:active={idx === selectedIndex}> {#if item.icon}{/if} @@ -74,6 +75,7 @@ {#each enrichedStructure[selectedIndex].children as item} {#if !item.showOnAsset || item.showOnAsset.includes($currentAssetName)} onItemChosen(item)} /> diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte index d1c7ec8e49..6b56910eed 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte @@ -69,7 +69,7 @@ } -
+
{label}
-
+
diff --git a/packages/builder/src/components/start/AppCard.svelte b/packages/builder/src/components/start/AppCard.svelte index cf8bcce688..dedbd91203 100644 --- a/packages/builder/src/components/start/AppCard.svelte +++ b/packages/builder/src/components/start/AppCard.svelte @@ -30,7 +30,7 @@
{name} -