Typing and config.api'ing application.spec.ts, WIP

This commit is contained in:
Sam Rose 2024-02-21 17:52:58 +00:00
parent f417c2d8a4
commit b2c4f04aa6
No known key found for this signature in database
11 changed files with 105 additions and 91 deletions

View File

@ -47,6 +47,7 @@ import {
PlanType, PlanType,
Screen, Screen,
UserCtx, UserCtx,
CreateAppRequest,
} from "@budibase/types" } from "@budibase/types"
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
import sdk from "../../sdk" import sdk from "../../sdk"
@ -116,8 +117,8 @@ function checkAppName(
} }
interface AppTemplate { interface AppTemplate {
templateString: string templateString?: string
useTemplate: string useTemplate?: string
file?: { file?: {
type: string type: string
path: string path: string
@ -231,17 +232,21 @@ export async function fetchAppPackage(ctx: UserCtx) {
} }
} }
async function performAppCreate(ctx: UserCtx) { async function performAppCreate(ctx: UserCtx<CreateAppRequest, App>) {
const apps = (await dbCore.getAllApps({ dev: true })) as App[] const apps = (await dbCore.getAllApps({ dev: true })) as App[]
const name = ctx.request.body.name, const {
possibleUrl = ctx.request.body.url, name,
encryptionPassword = ctx.request.body.encryptionPassword url,
encryptionPassword,
useTemplate,
templateKey,
templateString,
} = ctx.request.body
checkAppName(ctx, apps, name) checkAppName(ctx, apps, name)
const url = sdk.applications.getAppUrl({ name, url: possibleUrl }) const appUrl = sdk.applications.getAppUrl({ name, url })
checkAppUrl(ctx, apps, url) checkAppUrl(ctx, apps, appUrl)
const { useTemplate, templateKey, templateString } = ctx.request.body
const instanceConfig: AppTemplate = { const instanceConfig: AppTemplate = {
useTemplate, useTemplate,
key: templateKey, key: templateKey,
@ -268,7 +273,7 @@ async function performAppCreate(ctx: UserCtx) {
version: envCore.VERSION, version: envCore.VERSION,
componentLibraries: ["@budibase/standard-components"], componentLibraries: ["@budibase/standard-components"],
name: name, name: name,
url: url, url: appUrl,
template: templateKey, template: templateKey,
instance, instance,
tenantId: tenancy.getTenantId(), tenantId: tenancy.getTenantId(),

View File

@ -35,41 +35,30 @@ describe("/applications", () => {
describe("create", () => { describe("create", () => {
it("creates empty app", async () => { it("creates empty app", async () => {
const res = await request const app = await config.api.application.create({ name: utils.newid() })
.post("/api/applications") expect(app._id).toBeDefined()
.field("name", utils.newid())
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
expect(res.body._id).toBeDefined()
expect(events.app.created).toBeCalledTimes(1) expect(events.app.created).toBeCalledTimes(1)
}) })
it("creates app from template", async () => { it("creates app from template", async () => {
const res = await request const app = await config.api.application.create({
.post("/api/applications") name: utils.newid(),
.field("name", utils.newid()) useTemplate: "true",
.field("useTemplate", "true") templateKey: "test",
.field("templateKey", "test") templateString: "{}",
.field("templateString", "{}") // override the file download })
.set(config.defaultHeaders()) expect(app._id).toBeDefined()
.expect("Content-Type", /json/)
.expect(200)
expect(res.body._id).toBeDefined()
expect(events.app.created).toBeCalledTimes(1) expect(events.app.created).toBeCalledTimes(1)
expect(events.app.templateImported).toBeCalledTimes(1) expect(events.app.templateImported).toBeCalledTimes(1)
}) })
it("creates app from file", async () => { it("creates app from file", async () => {
const res = await request const app = await config.api.application.create({
.post("/api/applications") name: utils.newid(),
.field("name", utils.newid()) useTemplate: "true",
.field("useTemplate", "true") templateFile: "src/api/routes/tests/data/export.txt",
.set(config.defaultHeaders()) })
.attach("templateFile", "src/api/routes/tests/data/export.txt") expect(app._id).toBeDefined()
.expect("Content-Type", /json/)
.expect(200)
expect(res.body._id).toBeDefined()
expect(events.app.created).toBeCalledTimes(1) expect(events.app.created).toBeCalledTimes(1)
expect(events.app.fileImported).toBeCalledTimes(1) expect(events.app.fileImported).toBeCalledTimes(1)
}) })
@ -84,24 +73,21 @@ describe("/applications", () => {
}) })
it("migrates navigation settings from old apps", async () => { it("migrates navigation settings from old apps", async () => {
const res = await request const app = await config.api.application.create({
.post("/api/applications") name: "Old App",
.field("name", "Old App") useTemplate: "true",
.field("useTemplate", "true") templateFile: "src/api/routes/tests/data/old-app.txt",
.set(config.defaultHeaders()) })
.attach("templateFile", "src/api/routes/tests/data/old-app.txt") expect(app._id).toBeDefined()
.expect("Content-Type", /json/) expect(app.navigation).toBeDefined()
.expect(200) expect(app.navigation!.hideLogo).toBe(true)
expect(res.body._id).toBeDefined() expect(app.navigation!.title).toBe("Custom Title")
expect(res.body.navigation).toBeDefined() expect(app.navigation!.hideLogo).toBe(true)
expect(res.body.navigation.hideLogo).toBe(true) expect(app.navigation!.navigation).toBe("Left")
expect(res.body.navigation.title).toBe("Custom Title") expect(app.navigation!.navBackground).toBe(
expect(res.body.navigation.hideLogo).toBe(true)
expect(res.body.navigation.navigation).toBe("Left")
expect(res.body.navigation.navBackground).toBe(
"var(--spectrum-global-color-blue-600)" "var(--spectrum-global-color-blue-600)"
) )
expect(res.body.navigation.navTextColor).toBe( expect(app.navigation!.navTextColor).toBe(
"var(--spectrum-global-color-gray-50)" "var(--spectrum-global-color-gray-50)"
) )
expect(events.app.created).toBeCalledTimes(1) expect(events.app.created).toBeCalledTimes(1)
@ -118,15 +104,10 @@ describe("/applications", () => {
it("lists all applications", async () => { it("lists all applications", async () => {
await config.createApp("app1") await config.createApp("app1")
await config.createApp("app2") await config.createApp("app2")
const apps = await config.api.application.fetch({ status: AppStatus.DEV })
const res = await request
.get(`/api/applications?status=${AppStatus.DEV}`)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
// two created apps + the inited app // two created apps + the inited app
expect(res.body.length).toBe(3) expect(apps.length).toBe(3)
}) })
}) })

View File

@ -1,13 +1,38 @@
import { Response } from "supertest" import { Response } from "supertest"
import { App } from "@budibase/types" import { App, type CreateAppRequest } from "@budibase/types"
import TestConfiguration from "../TestConfiguration" import TestConfiguration from "../TestConfiguration"
import { TestAPI } from "./base" import { TestAPI } from "./base"
import { AppStatus } from "../../../db/utils"
import { dbObjectAsPojo } from "oracledb"
export class ApplicationAPI extends TestAPI { export class ApplicationAPI extends TestAPI {
constructor(config: TestConfiguration) { constructor(config: TestConfiguration) {
super(config) super(config)
} }
create = async (app: CreateAppRequest): Promise<App> => {
const request = this.request
.post("/api/applications")
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
for (const key of Object.keys(app)) {
request.field(key, (app as any)[key])
}
if (app.templateFile) {
request.attach("templateFile", app.templateFile)
}
const result = await request
if (result.statusCode !== 200) {
fail(JSON.stringify(result.body))
}
return result.body as App
}
getRaw = async (appId: string): Promise<Response> => { getRaw = async (appId: string): Promise<Response> => {
const result = await this.request const result = await this.request
.get(`/api/applications/${appId}/appPackage`) .get(`/api/applications/${appId}/appPackage`)
@ -21,4 +46,18 @@ export class ApplicationAPI extends TestAPI {
const result = await this.getRaw(appId) const result = await this.getRaw(appId)
return result.body.application as App return result.body.application as App
} }
fetch = async ({ status }: { status?: AppStatus } = {}): Promise<App[]> => {
let query = []
if (status) {
query.push(`status=${status}`)
}
const result = await this.request
.get(`/api/applications${query.length ? `?${query.join("&")}` : ""}`)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
return result.body as App[]
}
} }

View File

@ -73,3 +73,15 @@ export interface AppFeatures {
export interface AutomationSettings { export interface AutomationSettings {
chainAutomations?: boolean chainAutomations?: boolean
} }
export interface CreateAppRequest {
name: string
url?: string
useTemplate?: string
templateName?: string
templateKey?: string
templateFile?: string
includeSampleData?: boolean
encryptionPassword?: string
templateString?: string
}

View File

@ -4,10 +4,10 @@ set -e
if [[ -n $CI ]] if [[ -n $CI ]]
then then
# Running in ci, where resources are limited # Running in ci, where resources are limited
echo "jest --coverage --maxWorkers=2 --forceExit --bail" echo "jest --coverage --maxWorkers=2 --forceExit --bail $@"
jest --coverage --maxWorkers=2 --forceExit --bail jest --coverage --maxWorkers=2 --forceExit --bail $@
else else
# --maxWorkers performs better in development # --maxWorkers performs better in development
echo "jest --coverage --maxWorkers=2 --forceExit" echo "jest --coverage --maxWorkers=2 --forceExit $@"
jest --coverage --maxWorkers=2 --forceExit jest --coverage --maxWorkers=2 --forceExit $@
fi fi

View File

@ -1,11 +1,10 @@
import { App } from "@budibase/types" import { App, CreateAppRequest } from "@budibase/types"
import { Response } from "node-fetch" import { Response } from "node-fetch"
import { import {
RouteConfig, RouteConfig,
AppPackageResponse, AppPackageResponse,
DeployConfig, DeployConfig,
MessageResponse, MessageResponse,
CreateAppRequest,
} from "../../../types" } from "../../../types"
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
import BaseAPI from "./BaseAPI" import BaseAPI from "./BaseAPI"

View File

@ -1,5 +1,5 @@
import { generator } from "../../shared" import { generator } from "../../shared"
import { CreateAppRequest } from "../../types" import { CreateAppRequest } from "@budibase/types"
function uniqueWord() { function uniqueWord() {
return generator.word() + generator.hash() return generator.word() + generator.hash()

View File

@ -13,17 +13,6 @@ describe("Internal API - Table Operations", () => {
await config.afterAll() await config.afterAll()
}) })
async function createAppFromTemplate() {
return config.api.apps.create({
name: generator.word(),
url: `/${generator.word()}`,
useTemplate: "true",
templateName: "Near Miss Register",
templateKey: "app/near-miss-register",
templateFile: undefined,
})
}
it("Create and delete table, columns and rows", async () => { it("Create and delete table, columns and rows", async () => {
// create the app // create the app
await config.createApp(fixtures.apps.appFromTemplate()) await config.createApp(fixtures.apps.appFromTemplate())

View File

@ -1,8 +1,8 @@
import { BudibaseInternalAPI } from "../internal-api" import { BudibaseInternalAPI } from "../internal-api"
import { AccountInternalAPI } from "../account-api" import { AccountInternalAPI } from "../account-api"
import { APIRequestOpts, CreateAppRequest, State } from "../types" import { APIRequestOpts, State } from "../types"
import * as fixtures from "../internal-api/fixtures" import * as fixtures from "../internal-api/fixtures"
import { CreateAccountRequest } from "@budibase/types" import { CreateAccountRequest, CreateAppRequest } from "@budibase/types"
export default class BudibaseTestConfiguration { export default class BudibaseTestConfiguration {
// apis // apis

View File

@ -1,10 +0,0 @@
// TODO: Integrate with budibase
export interface CreateAppRequest {
name: string
url: string
useTemplate?: string
templateName?: string
templateKey?: string
templateFile?: string
includeSampleData?: boolean
}

View File

@ -1,6 +1,5 @@
export * from "./api" export * from "./api"
export * from "./apiKeyResponse" export * from "./apiKeyResponse"
export * from "./app"
export * from "./appPackage" export * from "./appPackage"
export * from "./deploy" export * from "./deploy"
export * from "./newAccount" export * from "./newAccount"