From 3b08f9f8f1c29645161386d578440a4a1caf83d5 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 30 Sep 2022 15:03:38 +0100 Subject: [PATCH 1/5] create app tests --- qa-core/README.md | 22 ++++++++++ .../TestConfiguration/applications.ts | 22 +++++++++- .../internal-api/applications/create.spec.ts | 42 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 qa-core/README.md diff --git a/qa-core/README.md b/qa-core/README.md new file mode 100644 index 0000000000..b812742ab3 --- /dev/null +++ b/qa-core/README.md @@ -0,0 +1,22 @@ +# QA Core API Tests + +The QA Core API tests are a jest suite that run directly against the budibase backend APIs. + +## Auto Setup +You can run the whole test suite with one command, that spins up the budibase server and runs the jest tests: + +`yarn api:test` + +## Setup Server Only +You can also just stand up the budibase server alone. + +`yarn api:server:setup` + +## Run Tests +If you configured the server using the previous command, you can run the whole test suite by using: + +`yarn test` + +for watch mode, where the tests will run on every change: + +`yarn test:watch` \ No newline at end of file diff --git a/qa-core/src/config/internal-api/TestConfiguration/applications.ts b/qa-core/src/config/internal-api/TestConfiguration/applications.ts index afee5d707a..20e40fa3f2 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/applications.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/applications.ts @@ -1,6 +1,7 @@ import { Application, } from "@budibase/server/api/controllers/public/mapping/types" +import { App } from "@budibase/types" import { Response } from "node-fetch" import InternalAPIClient from "./InternalAPIClient" @@ -17,9 +18,28 @@ export default class AppApi { return [response, json] } + async canRender(appId: string): Promise<[Response, string]> { + const response = await this.api.get(`/${appId}`, {}) + const html = await response.text() + return [response, html] + } + + async getAppPackage(appId: string): Promise<[Response, any]> { + const response = await this.api.get(`/applications/${appId}/appPackage`) + const json = await response.json() + return [response, json] + } + + // TODO: 500 Error: Missing/invalid DB name when called + async publish(): Promise<[Response, string]> { + const response = await this.api.post("/deploy") + const json = await response.json() + return [response, json] + } + async create( body: any - ): Promise<[Response, Application]> { + ): Promise<[Response, Partial]> { const response = await this.api.post(`/applications`, { body }) const json = await response.json() return [response, json] diff --git a/qa-core/src/tests/internal-api/applications/create.spec.ts b/qa-core/src/tests/internal-api/applications/create.spec.ts index a11640153c..dedff13e8e 100644 --- a/qa-core/src/tests/internal-api/applications/create.spec.ts +++ b/qa-core/src/tests/internal-api/applications/create.spec.ts @@ -2,6 +2,7 @@ import TestConfiguration from "../../../config/internal-api/TestConfiguration" import { Application } from "@budibase/server/api/controllers/public/mapping/types" import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" import generateApp from "../../../config/internal-api/fixtures/applications" +import generator from "../../../config/generator" describe("Internal API - /applications endpoints", () => { const api = new InternalAPIClient() @@ -37,4 +38,45 @@ describe("Internal API - /applications endpoints", () => { expect(response).toHaveStatusCode(200) expect(app._id).toBeDefined() }) + + it("POST - Create an application from a template", async () => { + const appName = generator.word() + const [response, app] = await config.applications.create({ + name: appName, + url: `/${generator.word()}`, + useTemplate: true, + templateName: "Car Rental Admin Panel", + templateKey: "app/car-rental-admin-panel", + templateFile: undefined + }) + expect(response).toHaveStatusCode(200) + expect(app.appId).toBeDefined() + const [_, appPackage] = await config.applications.getAppPackage(app.appId as string) + expect(appPackage.application.appId).toBe(app.appId) + expect(appPackage.application.name).toBe(appName) + }) + + it("POST - Publish app from template", async () => { + const appUrl = `/${generator.word()}` + const [response, app] = await config.applications.create({ + name: generator.word(), + url: appUrl, + useTemplate: true, + templateName: "Car Rental Admin Panel", + templateKey: "app/car-rental-admin-panel", + templateFile: undefined + }) + expect(response).toHaveStatusCode(200) + expect(app.appId).toBeDefined() + + config.applications.api.appId = app.appId + + const [publishResponse, json] = await config.applications.publish() + expect(publishResponse).toHaveStatusCode(200) + expect(json).toEqual({ + _id: expect.any(String), + appUrl, + status: "SUCCESS" + }) + }) }) From 06de974ef5085cdc44d169f6ea606f7d948370f1 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 30 Sep 2022 15:35:13 +0100 Subject: [PATCH 2/5] create app with formdata --- qa-core/package.json | 3 ++- .../TestConfiguration/InternalAPIClient.ts | 2 +- .../TestConfiguration/applications.ts | 19 +++++++++++++------ .../internal-api/applications/create.spec.ts | 19 ++++++++++--------- qa-core/yarn.lock | 9 +++++++++ 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/qa-core/package.json b/qa-core/package.json index 7cd566587e..d4ca0f3809 100644 --- a/qa-core/package.json +++ b/qa-core/package.json @@ -51,6 +51,7 @@ }, "dependencies": { "@budibase/backend-core": "^2.0.5", + "form-data": "^4.0.0", "node-fetch": "2" } -} \ No newline at end of file +} diff --git a/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts b/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts index bfcbb9f4e2..e926363216 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts @@ -29,7 +29,7 @@ class InternalAPIClient { async (url = "", options: ApiOptions = {}) => { const requestOptions = { method, - body: JSON.stringify(options.body), + body: options.body, headers: { "x-budibase-app-id": this.appId, "Content-Type": "application/json", diff --git a/qa-core/src/config/internal-api/TestConfiguration/applications.ts b/qa-core/src/config/internal-api/TestConfiguration/applications.ts index 20e40fa3f2..19cc7fac1d 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/applications.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/applications.ts @@ -4,6 +4,7 @@ import { import { App } from "@budibase/types" import { Response } from "node-fetch" import InternalAPIClient from "./InternalAPIClient" +import FormData from "form-data" export default class AppApi { api: InternalAPIClient @@ -18,10 +19,10 @@ export default class AppApi { return [response, json] } - async canRender(appId: string): Promise<[Response, string]> { - const response = await this.api.get(`/${appId}`, {}) - const html = await response.text() - return [response, html] + async canRender(): Promise<[Response, boolean]> { + const response = await this.api.get("/routing/client") + const json = await response.json() + return [response, Object.keys(json.routes).length > 0] } async getAppPackage(appId: string): Promise<[Response, any]> { @@ -30,7 +31,6 @@ export default class AppApi { return [response, json] } - // TODO: 500 Error: Missing/invalid DB name when called async publish(): Promise<[Response, string]> { const response = await this.api.post("/deploy") const json = await response.json() @@ -40,7 +40,14 @@ export default class AppApi { async create( body: any ): Promise<[Response, Partial]> { - const response = await this.api.post(`/applications`, { body }) + const data = new FormData() + data.append("name", body.name) + data.append("url", body.url) + data.append("useTemplate", true) + data.append("templateName", body.templateName) + data.append("templateKey", body.templateKey) + + const response = await this.api.post(`/applications`, { body: data }) const json = await response.json() return [response, json] } diff --git a/qa-core/src/tests/internal-api/applications/create.spec.ts b/qa-core/src/tests/internal-api/applications/create.spec.ts index dedff13e8e..7dce577efc 100644 --- a/qa-core/src/tests/internal-api/applications/create.spec.ts +++ b/qa-core/src/tests/internal-api/applications/create.spec.ts @@ -18,28 +18,28 @@ describe("Internal API - /applications endpoints", () => { await config.auth.logout() }) - it("POST - Can login", async () => { + xit("POST - Can login", async () => { const [response] = await config.auth.login() expect(response).toHaveStatusCode(200) }) - it("GET - fetch applications", async () => { + xit("GET - fetch applications", async () => { await config.applications.create({ ...generateApp(), useTemplate: false }) const [response, apps] = await config.applications.fetch() expect(response).toHaveStatusCode(200) - expect(apps.length).toBeGreaterThan(1) + expect(apps.length).toBeGreaterThanOrEqual(1) }) - it("POST - Create an application", async () => { + xit("POST - Create an application", async () => { const [response, app] = await config.applications.create(generateApp()) expect(response).toHaveStatusCode(200) expect(app._id).toBeDefined() }) - it("POST - Create an application from a template", async () => { + it("POST - Create an application from a template and check it renders", async () => { const appName = generator.word() const [response, app] = await config.applications.create({ name: appName, @@ -51,12 +51,13 @@ describe("Internal API - /applications endpoints", () => { }) expect(response).toHaveStatusCode(200) expect(app.appId).toBeDefined() - const [_, appPackage] = await config.applications.getAppPackage(app.appId as string) - expect(appPackage.application.appId).toBe(app.appId) - expect(appPackage.application.name).toBe(appName) + + config.applications.api.appId = app.appId + const [_, renderable] = await config.applications.canRender() + expect(renderable).toBe(true) }) - it("POST - Publish app from template", async () => { + xit("POST - Publish app from template", async () => { const appUrl = `/${generator.word()}` const [response, app] = await config.applications.create({ name: generator.word(), diff --git a/qa-core/yarn.lock b/qa-core/yarn.lock index 71c3d3efe4..5b86c6084f 100644 --- a/qa-core/yarn.lock +++ b/qa-core/yarn.lock @@ -1839,6 +1839,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" From c7db9588ce82cee8aba66d4a78224cff6580b3e0 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sat, 1 Oct 2022 02:54:51 +0100 Subject: [PATCH 3/5] QA Core tests for app creation --- qa-core/package.json | 5 +- qa-core/scripts/jestSetup.js | 2 + .../TestConfiguration/InternalAPIClient.ts | 2 +- .../TestConfiguration/applications.ts | 9 +- .../internal-api/TestConfiguration/index.ts | 5 +- qa-core/src/module.d.ts | 14 ++++ .../internal-api/applications/create.spec.ts | 83 ++++++++++--------- qa-core/tsconfig.json | 4 +- 8 files changed, 71 insertions(+), 53 deletions(-) create mode 100644 qa-core/src/module.d.ts diff --git a/qa-core/package.json b/qa-core/package.json index d4ca0f3809..e624b2d62d 100644 --- a/qa-core/package.json +++ b/qa-core/package.json @@ -25,7 +25,8 @@ "moduleNameMapper": { "@budibase/types": "/../packages/types/src", "@budibase/server": "/../packages/server/src", - "@budibase/backend-core": "/../packages/backend-core/src" + "@budibase/backend-core": "/../packages/backend-core/src", + "@budibase/backend-core/(.*)": "/../packages/backend-core/$1" }, "setupFiles": [ "./scripts/jestSetup.js" @@ -54,4 +55,4 @@ "form-data": "^4.0.0", "node-fetch": "2" } -} +} \ No newline at end of file diff --git a/qa-core/scripts/jestSetup.js b/qa-core/scripts/jestSetup.js index 8254fd6d34..77565783c3 100644 --- a/qa-core/scripts/jestSetup.js +++ b/qa-core/scripts/jestSetup.js @@ -15,3 +15,5 @@ tk.freeze(MOCK_DATE) if (!process.env.DEBUG) { global.console.log = jest.fn() // console.log are ignored in tests } + +jest.setTimeout(10000) diff --git a/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts b/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts index e926363216..bfcbb9f4e2 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/InternalAPIClient.ts @@ -29,7 +29,7 @@ class InternalAPIClient { async (url = "", options: ApiOptions = {}) => { const requestOptions = { method, - body: options.body, + body: JSON.stringify(options.body), headers: { "x-budibase-app-id": this.appId, "Content-Type": "application/json", diff --git a/qa-core/src/config/internal-api/TestConfiguration/applications.ts b/qa-core/src/config/internal-api/TestConfiguration/applications.ts index 19cc7fac1d..10e4a6657b 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/applications.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/applications.ts @@ -40,14 +40,7 @@ export default class AppApi { async create( body: any ): Promise<[Response, Partial]> { - const data = new FormData() - data.append("name", body.name) - data.append("url", body.url) - data.append("useTemplate", true) - data.append("templateName", body.templateName) - data.append("templateKey", body.templateKey) - - const response = await this.api.post(`/applications`, { body: data }) + const response = await this.api.post(`/applications`, { body }) const json = await response.json() return [response, json] } diff --git a/qa-core/src/config/internal-api/TestConfiguration/index.ts b/qa-core/src/config/internal-api/TestConfiguration/index.ts index 69c7e8df96..b433fd98ea 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/index.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/index.ts @@ -13,9 +13,12 @@ export default class TestConfiguration { this.context = {} } - async beforeAll() {} + async beforeAll() { + await this.auth.login() + } async afterAll() { this.context = {} + await this.auth.logout() } } diff --git a/qa-core/src/module.d.ts b/qa-core/src/module.d.ts new file mode 100644 index 0000000000..a15ebcbe3d --- /dev/null +++ b/qa-core/src/module.d.ts @@ -0,0 +1,14 @@ +declare module "@budibase/backend-core/tenancy" +declare module "@budibase/backend-core/db" +declare module "@budibase/backend-core/context" +declare module "@budibase/backend-core/cache" +declare module "@budibase/backend-core/permissions" +declare module "@budibase/backend-core/roles" +declare module "@budibase/backend-core/constants" +declare module "@budibase/backend-core/auth" +declare module "@budibase/backend-core/sessions" +declare module "@budibase/backend-core/encryption" +declare module "@budibase/backend-core/utils" +declare module "@budibase/backend-core/redis" +declare module "@budibase/backend-core/objectStore" +declare module "@budibase/backend-core/plugins" \ No newline at end of file diff --git a/qa-core/src/tests/internal-api/applications/create.spec.ts b/qa-core/src/tests/internal-api/applications/create.spec.ts index 7dce577efc..7d6a79e8e1 100644 --- a/qa-core/src/tests/internal-api/applications/create.spec.ts +++ b/qa-core/src/tests/internal-api/applications/create.spec.ts @@ -1,5 +1,6 @@ import TestConfiguration from "../../../config/internal-api/TestConfiguration" import { Application } from "@budibase/server/api/controllers/public/mapping/types" +import { db } from "@budibase/backend-core" import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" import generateApp from "../../../config/internal-api/fixtures/applications" import generator from "../../../config/generator" @@ -10,20 +11,24 @@ describe("Internal API - /applications endpoints", () => { beforeAll(async () => { await config.beforeAll() - await config.auth.login() }) afterAll(async () => { await config.afterAll() - await config.auth.logout() }) - xit("POST - Can login", async () => { - const [response] = await config.auth.login() - expect(response).toHaveStatusCode(200) - }) + async function createAppFromTemplate() { + return config.applications.create({ + name: generator.word(), + url: `/${generator.word()}`, + useTemplate: "true", + templateName: "Near Miss Register", + templateKey: "app/near-miss-register", + templateFile: undefined + }) + } - xit("GET - fetch applications", async () => { + it("GET - fetch applications", async () => { await config.applications.create({ ...generateApp(), useTemplate: false @@ -33,51 +38,49 @@ describe("Internal API - /applications endpoints", () => { expect(apps.length).toBeGreaterThanOrEqual(1) }) - xit("POST - Create an application", async () => { + it("POST - Create an application", async () => { const [response, app] = await config.applications.create(generateApp()) expect(response).toHaveStatusCode(200) expect(app._id).toBeDefined() }) - it("POST - Create an application from a template and check it renders", async () => { - const appName = generator.word() - const [response, app] = await config.applications.create({ - name: appName, - url: `/${generator.word()}`, - useTemplate: true, - templateName: "Car Rental Admin Panel", - templateKey: "app/car-rental-admin-panel", - templateFile: undefined - }) + it("POST - Publish application", async () => { + // create app + const [response, app] = await config.applications.create(generateApp()) expect(response).toHaveStatusCode(200) expect(app.appId).toBeDefined() + // publish app config.applications.api.appId = app.appId - const [_, renderable] = await config.applications.canRender() - expect(renderable).toBe(true) - }) - - xit("POST - Publish app from template", async () => { - const appUrl = `/${generator.word()}` - const [response, app] = await config.applications.create({ - name: generator.word(), - url: appUrl, - useTemplate: true, - templateName: "Car Rental Admin Panel", - templateKey: "app/car-rental-admin-panel", - templateFile: undefined - }) - expect(response).toHaveStatusCode(200) - expect(app.appId).toBeDefined() - - config.applications.api.appId = app.appId - - const [publishResponse, json] = await config.applications.publish() + const [publishResponse, publish] = await config.applications.publish() expect(publishResponse).toHaveStatusCode(200) - expect(json).toEqual({ + expect(publish).toEqual({ _id: expect.any(String), - appUrl, + appUrl: app.url, status: "SUCCESS" }) }) + + it("POST - Create an application from a template, publish and check it renders", async () => { + // create the app + const appName = generator.word() + const [response, app] = await createAppFromTemplate({ name: appName }) + expect(response).toHaveStatusCode(200) + expect(app.appId).toBeDefined() + config.applications.api.appId = app.appId + + // check preview renders + const [previewResponse, previewRenders] = await config.applications.canRender() + expect(previewResponse).toHaveStatusCode(200) + expect(previewRenders).toBe(true) + + // publish app + await config.applications.publish() + + // check published app renders + config.applications.api.appId = db.getProdAppID(app.appId) + const [publishedAppResponse, publishedAppRenders] = await config.applications.canRender() + expect(publishedAppRenders).toBe(true) + }) + }) diff --git a/qa-core/tsconfig.json b/qa-core/tsconfig.json index 028b4457f9..8cd0c30d46 100644 --- a/qa-core/tsconfig.json +++ b/qa-core/tsconfig.json @@ -14,7 +14,8 @@ "skipLibCheck": true, "paths": { "@budibase/types": ["../packages/types/src"], - "@budibase/backend-core": ["../packages/backend-core"], + "@budibase/backend-core": ["../packages/backend-core/src"], + "@budibase/backend-core/*": ["../packages/backend-core/*"], "@budibase/server/*": ["../packages/server/src/*"], } }, @@ -23,6 +24,7 @@ }, "references": [ { "path": "../packages/types" }, + { "path": "../packages/backend-core" }, ], "include": [ "src/**/*", From 40a01977bd4086ffd6fcce36a2ad648c643556c7 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sat, 1 Oct 2022 02:56:08 +0100 Subject: [PATCH 4/5] tidy up --- qa-core/src/tests/internal-api/applications/create.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa-core/src/tests/internal-api/applications/create.spec.ts b/qa-core/src/tests/internal-api/applications/create.spec.ts index 7d6a79e8e1..81d43d9c91 100644 --- a/qa-core/src/tests/internal-api/applications/create.spec.ts +++ b/qa-core/src/tests/internal-api/applications/create.spec.ts @@ -64,7 +64,7 @@ describe("Internal API - /applications endpoints", () => { it("POST - Create an application from a template, publish and check it renders", async () => { // create the app const appName = generator.word() - const [response, app] = await createAppFromTemplate({ name: appName }) + const [response, app] = await createAppFromTemplate() expect(response).toHaveStatusCode(200) expect(app.appId).toBeDefined() config.applications.api.appId = app.appId From abc3e7ddc1852d1fa20927adb2ab4dc605b081a6 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 3 Oct 2022 10:04:15 +0100 Subject: [PATCH 5/5] remove module file --- qa-core/src/module.d.ts | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 qa-core/src/module.d.ts diff --git a/qa-core/src/module.d.ts b/qa-core/src/module.d.ts deleted file mode 100644 index a15ebcbe3d..0000000000 --- a/qa-core/src/module.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -declare module "@budibase/backend-core/tenancy" -declare module "@budibase/backend-core/db" -declare module "@budibase/backend-core/context" -declare module "@budibase/backend-core/cache" -declare module "@budibase/backend-core/permissions" -declare module "@budibase/backend-core/roles" -declare module "@budibase/backend-core/constants" -declare module "@budibase/backend-core/auth" -declare module "@budibase/backend-core/sessions" -declare module "@budibase/backend-core/encryption" -declare module "@budibase/backend-core/utils" -declare module "@budibase/backend-core/redis" -declare module "@budibase/backend-core/objectStore" -declare module "@budibase/backend-core/plugins" \ No newline at end of file