@budibase/server
Version:
Budibase Web Server
379 lines (335 loc) • 11.6 kB
text/typescript
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
import * as setup from "./utilities"
import { events, features, roles } from "@budibase/backend-core"
import {
Screen,
Role,
BuiltinPermissionID,
SourceType,
UsageInScreensResponse,
} from "@budibase/types"
import { basicDatasourcePlus } from "../../../tests/utilities/structures"
import { SAMPLE_DATA_SCREEN_NAME } from "../../../constants/screens"
import { structures } from "@budibase/backend-core/tests"
const {
basicScreen,
createTableScreen,
createViewScreen,
createQueryScreen,
basicTable,
viewV2,
basicQuery,
} = setup.structures
describe("/screens", () => {
let screen: Screen
let config = setup.getConfig()
afterAll(setup.afterAll)
beforeAll(async () => {
await config.init()
screen = await config.createScreen()
})
function popRandomScreen(screens: Screen[]) {
const index = Math.floor(Math.random() * screens.length)
return screens.splice(index, 1)[0]
}
describe("fetch", () => {
it("should create the sample data screen", async () => {
const screens = await config.api.screen.list()
expect(screens.some(s => s.name === SAMPLE_DATA_SCREEN_NAME)).toEqual(
true
)
})
it("should be able to create a screen", async () => {
const screens = await config.api.screen.list()
expect(screens.length).toEqual(2)
expect(screens.some(s => s._id === screen._id)).toEqual(true)
})
it("should apply authorization to endpoint", async () => {
await checkBuilderEndpoint({
config,
method: "GET",
url: `/api/screens`,
})
})
})
describe("permissions", () => {
let screen1: Screen, screen2: Screen
let role1: Role, role2: Role, multiRole: Role
beforeAll(async () => {
role1 = await config.api.roles.save({
name: "role1",
inherits: roles.BUILTIN_ROLE_IDS.BASIC,
permissionId: BuiltinPermissionID.WRITE,
})
role2 = await config.api.roles.save({
name: "role2",
inherits: roles.BUILTIN_ROLE_IDS.BASIC,
permissionId: BuiltinPermissionID.WRITE,
})
multiRole = await config.api.roles.save({
name: "multiRole",
inherits: [role1._id!, role2._id!],
permissionId: BuiltinPermissionID.WRITE,
})
screen1 = await config.api.screen.save({
...basicScreen(),
routing: {
roleId: role1._id!,
route: "/foo",
homeScreen: false,
},
})
screen2 = await config.api.screen.save({
...basicScreen(),
routing: {
roleId: role2._id!,
route: "/bar",
homeScreen: false,
},
})
// get into prod app
await config.publish()
})
async function checkScreens(roleId: string, screenIds: string[]) {
await config.loginAsRole(roleId, async () => {
const res = await config.withProdApp(() =>
config.api.application.getDefinition(config.getProdAppId())
)
// Filter out sample screen
const screens = res.screens.filter(
s => s.name !== SAMPLE_DATA_SCREEN_NAME
)
expect(screens.length).toEqual(screenIds.length)
expect(screens.map(s => s._id).sort()).toEqual(screenIds.sort())
})
}
it("should be able to fetch basic and screen1 with role1", async () => {
await checkScreens(role1._id!, [screen._id!, screen1._id!])
})
it("should be able to fetch basic and screen2 with role2", async () => {
await checkScreens(role2._id!, [screen._id!, screen2._id!])
})
it("should be able to fetch basic, screen1 and screen2 with multi-inheritance role", async () => {
await checkScreens(multiRole._id!, [
screen._id!,
screen1._id!,
screen2._id!,
])
})
it("should be able to fetch basic and screen 1 with role1 in role header", async () => {
await config.withHeaders(
{
"x-budibase-role": role1._id!,
},
async () => {
const res = await config.api.application.getDefinition(
config.getAppId()
)
// Filter out sample screen
const screens = res.screens.filter(
s => s.name !== SAMPLE_DATA_SCREEN_NAME
)
const screenIds = [screen._id!, screen1._id!]
expect(screens.length).toEqual(screenIds.length)
expect(screens.map(s => s._id).sort()).toEqual(screenIds.sort())
}
)
})
})
describe("save", () => {
beforeEach(() => {
jest.clearAllMocks()
})
it("should be able to create a screen", async () => {
const screen = basicScreen()
const responseScreen = await config.api.screen.save(screen)
expect(responseScreen._rev).toBeDefined()
expect(responseScreen.name).toEqual(screen.name)
expect(events.screen.created).toHaveBeenCalledTimes(1)
})
it("should be able to update a screen", async () => {
const screen = basicScreen()
let responseScreen = await config.api.screen.save(screen)
screen._id = responseScreen._id
screen._rev = responseScreen._rev
screen.name = "edit"
jest.clearAllMocks()
responseScreen = await config.api.screen.save(screen)
expect(responseScreen._rev).toBeDefined()
expect(responseScreen.name).toEqual(screen.name)
expect(events.screen.created).not.toHaveBeenCalled()
})
it("should apply authorization to endpoint", async () => {
await checkBuilderEndpoint({
config,
method: "POST",
url: `/api/screens`,
})
})
})
describe("destroy", () => {
it("should be able to delete the screen", async () => {
const [{ _id: workspaceAppId }] = (await config.api.workspaceApp.fetch())
.workspaceApps
const screen = await config.api.screen.save({
...basicScreen(),
workspaceAppId,
})
const response = await config.api.screen.destroy(
screen._id!,
screen._rev!
)
expect(response.message).toBeDefined()
expect(events.screen.deleted).toHaveBeenCalledTimes(1)
})
it("should apply authorization to endpoint", async () => {
await checkBuilderEndpoint({
config,
method: "DELETE",
url: `/api/screens/${screen._id}/${screen._rev}`,
})
})
describe("workspace apps", () => {
let featureCleanup: () => void
beforeAll(() => {
featureCleanup = features.testutils.setFeatureFlags("*", {
WORKSPACE_APPS: true,
})
})
afterAll(() => {
featureCleanup()
})
it("should not allow deleting the last screen", async () => {
const { workspaceApp } = await config.api.workspaceApp.create(
structures.workspaceApps.createRequest()
)
const screen = await config.api.screen.save({
...basicScreen(),
workspaceAppId: workspaceApp._id,
})
await config.api.screen.destroy(screen._id!, screen._rev!, {
status: 409,
body: { message: "Cannot delete the last screen in a workspace app" },
})
})
it("should allow deleting other screens", async () => {
const { workspaceApp } = await config.api.workspaceApp.create(
structures.workspaceApps.createRequest()
)
const screens = await Promise.all(
Array.from({ length: 3 }).map(() =>
config.api.screen.save({
...basicScreen(),
workspaceAppId: workspaceApp._id,
})
)
)
let screenToDelete = popRandomScreen(screens)
await config.api.screen.destroy(
screenToDelete._id!,
screenToDelete._rev!
)
screenToDelete = popRandomScreen(screens)
await config.api.screen.destroy(
screenToDelete._id!,
screenToDelete._rev!
)
screenToDelete = popRandomScreen(screens)
await config.api.screen.destroy(
screenToDelete._id!,
screenToDelete._rev!,
{ status: 409 }
)
})
it("should allow deleting screens for other apps", async () => {
const { workspaceApp: workspaceApp1 } =
await config.api.workspaceApp.create(
structures.workspaceApps.createRequest()
)
const { workspaceApp: workspaceApp2 } =
await config.api.workspaceApp.create(
structures.workspaceApps.createRequest()
)
const app1Screens = await Promise.all(
Array.from({ length: 3 }).map(() =>
config.api.screen.save({
...basicScreen(),
workspaceAppId: workspaceApp1._id,
})
)
)
// Other screens
await Promise.all(
Array.from({ length: 2 }).map(() =>
config.api.screen.save({
...basicScreen(),
workspaceAppId: workspaceApp2._id,
})
)
)
let screenToDelete = popRandomScreen(app1Screens)
await config.api.screen.destroy(
screenToDelete._id!,
screenToDelete._rev!
)
screenToDelete = popRandomScreen(app1Screens)
await config.api.screen.destroy(
screenToDelete._id!,
screenToDelete._rev!
)
screenToDelete = popRandomScreen(app1Screens)
await config.api.screen.destroy(
screenToDelete._id!,
screenToDelete._rev!,
{ status: 409 }
)
})
})
})
describe("usage", () => {
beforeEach(async () => {
await config.init()
await config.api.screen.save(basicScreen())
})
function confirmScreen(usage: UsageInScreensResponse, screen: Screen) {
expect(usage.screens.length).toEqual(1)
expect(usage.screens[0].url).toEqual(screen.routing.route)
expect(usage.screens[0]._id).toEqual(screen._id!)
}
it("should find table usage", async () => {
const table = await config.api.table.save(basicTable())
const screen = await config.api.screen.save(
createTableScreen("BudibaseDB", table)
)
const usage = await config.api.screen.usage(table._id!)
expect(usage.sourceType).toEqual(SourceType.TABLE)
confirmScreen(usage, screen)
})
it("should find view usage", async () => {
const table = await config.api.table.save(basicTable())
const view = await config.api.viewV2.create(
viewV2.createRequest(table._id!),
{ status: 201 }
)
const screen = await config.api.screen.save(createViewScreen(view))
const usage = await config.api.screen.usage(view.id)
expect(usage.sourceType).toEqual(SourceType.VIEW)
confirmScreen(usage, screen)
})
it("should find datasource/query usage", async () => {
const datasource = await config.api.datasource.create(
basicDatasourcePlus().datasource
)
const query = await config.api.query.save(basicQuery(datasource._id!))
const screen = await config.api.screen.save(
createQueryScreen(datasource._id!, query)
)
const dsUsage = await config.api.screen.usage(datasource._id!)
expect(dsUsage.sourceType).toEqual(SourceType.DATASOURCE)
confirmScreen(dsUsage, screen)
const queryUsage = await config.api.screen.usage(query._id!)
expect(queryUsage.sourceType).toEqual(SourceType.QUERY)
confirmScreen(queryUsage, screen)
})
})
})