complete refactor and total type safety of test suite - making use of OpenAPI types throughout

This commit is contained in:
Martin McKeaveney 2022-09-15 00:58:08 +01:00
parent 32be537023
commit fdbe429581
36 changed files with 470 additions and 557 deletions

View File

@ -45,8 +45,8 @@
"lint:eslint": "eslint packages",
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"",
"lint": "yarn run lint:eslint && yarn run lint:prettier",
"lint:fix:eslint": "eslint --fix packages",
"lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\"",
"lint:fix:eslint": "eslint --fix packages qa-core",
"lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --write \"qa-core/**/*.{js,ts,svelte}\"",
"lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint",
"test:e2e": "lerna run cy:test --stream",
"test:e2e:ci": "lerna run cy:ci --stream",

View File

@ -4,10 +4,18 @@ export type Query = components["schemas"]["query"]
export type ExecuteQuery = components["schemas"]["executeQueryOutput"]
export type Application = components["schemas"]["applicationOutput"]["data"]
export type CreateApplicationParams = components["schemas"]["application"]
export type Table = components["schemas"]["tableOutput"]["data"]
export type CreateTableParams = components["schemas"]["table"]
export type Row = components["schemas"]["rowOutput"]["data"]
export type RowSearch = components["schemas"]["searchOutput"]
export type CreateRowParams = components["schemas"]["row"]
export type User = components["schemas"]["userOutput"]["data"]
export type CreateUserParams = components["schemas"]["user"]
export type SearchInputParams =
| components["schemas"]["nameSearch"]
| components["schemas"]["querySearch"]

View File

@ -23,6 +23,7 @@ describe("/users", () => {
})
describe("fetch", () => {
it("returns a list of users from an instance db", async () => {
await config.createUser("uuidx")
await config.createUser("uuidy")

View File

@ -21,8 +21,6 @@
"preset": "ts-jest",
"testEnvironment": "node",
"moduleNameMapper": {
"@budibase/backend-core/(.*)": "<rootDir>/../packages/backend-core/$1",
"@budibase/backend-core": "<rootDir>/../packages/backend-core/src",
"@budibase/types": "<rootDir>/../packages/types/src"
},
"setupFiles": [
@ -33,18 +31,18 @@
]
},
"devDependencies": {
"@budibase/types": "^1.3.4",
"@types/jest": "^29.0.0",
"@types/node-fetch": "^2.6.2",
"chance": "^1.1.8",
"jest": "^28.0.2",
"prettier": "^2.7.1",
"start-server-and-test": "^1.14.0",
"timekeeper": "^2.2.0",
"@budibase/types": "1.3.4",
"@types/jest": "29.0.0",
"@types/node-fetch": "2.6.2",
"chance": "1.1.8",
"jest": "28.0.2",
"prettier": "2.7.1",
"start-server-and-test": "1.14.0",
"timekeeper": "2.2.0",
"ts-jest": "28.0.8",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.1.0",
"typescript": "^4.8.2"
"ts-node": "10.9.1",
"tsconfig-paths": "4.1.0",
"typescript": "4.7.3"
},
"dependencies": {
"node-fetch": "2"

View File

@ -1,12 +1,14 @@
const env = require("../src/environment")
env._set("BUDIBASE_SERVER_URL", "http://localhost:4100")
env._set("BUDIBASE_PUBLIC_API_KEY", "a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f")
env._set(
"BUDIBASE_PUBLIC_API_KEY",
"a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f"
)
// mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests
const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")
const MOCK_DATE_TIMESTAMP = 1577836800000
const tk = require("timekeeper")
tk.freeze(MOCK_DATE)

View File

@ -1,14 +1,14 @@
import env from "../../environment"
import fetch from "node-fetch"
interface HeaderOptions {
headers?: object;
body?: object;
json?: boolean;
}
import env from "../../../environment"
import fetch, { HeadersInit } from "node-fetch"
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
interface ApiOptions {
method?: APIMethod
body?: object
headers?: HeadersInit | undefined
}
class PublicAPIClient {
host: string
apiKey: string
@ -16,7 +16,9 @@ class PublicAPIClient {
constructor(appId?: string) {
if (!env.BUDIBASE_PUBLIC_API_KEY || !env.BUDIBASE_SERVER_URL) {
throw new Error("Must set BUDIBASE_PUBLIC_API_KEY and BUDIBASE_SERVER_URL env vars")
throw new Error(
"Must set BUDIBASE_PUBLIC_API_KEY and BUDIBASE_SERVER_URL env vars"
)
}
this.host = `${env.BUDIBASE_SERVER_URL}/api/public/v1`
this.apiKey = env.BUDIBASE_PUBLIC_API_KEY
@ -25,9 +27,9 @@ class PublicAPIClient {
apiCall =
(method: APIMethod) =>
async (url = "", options: HeaderOptions = {}) => {
async (url = "", options: ApiOptions = {}) => {
const requestOptions = {
method: method,
method,
body: JSON.stringify(options.body),
headers: {
"x-budibase-api-key": this.apiKey,
@ -55,4 +57,4 @@ class PublicAPIClient {
put = this.apiCall("PUT")
}
export default PublicAPIClient
export default PublicAPIClient

View File

@ -0,0 +1,49 @@
import PublicAPIClient from "./PublicAPIClient"
import {
Application,
SearchInputParams,
CreateApplicationParams,
} from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import { Response } from "node-fetch"
import generateApp from "../fixtures/applications"
export default class AppApi {
api: PublicAPIClient
constructor(apiClient: PublicAPIClient) {
this.api = apiClient
}
async seed(): Promise<[Response, Application]> {
return this.create(generateApp())
}
async create(
body: CreateApplicationParams
): Promise<[Response, Application]> {
const response = await this.api.post(`/applications`, { body })
const json = await response.json()
return [response, json.data]
}
async read(id: string): Promise<[Response, Application]> {
const response = await this.api.get(`/applications/${id}`)
const json = await response.json()
return [response, json.data]
}
async search(body: SearchInputParams): Promise<[Response, Application]> {
const response = await this.api.post(`/applications/search`, { body })
const json = await response.json()
return [response, json.data]
}
async update(
id: string,
body: Application
): Promise<[Response, Application]> {
const response = await this.api.put(`/applications/${id}`, { body })
const json = await response.json()
return [response, json.data]
}
}

View File

@ -1,3 +1,3 @@
const Chance = require("chance")
export default new Chance()
export default new Chance()

View File

@ -0,0 +1,27 @@
import PublicAPIClient from "./PublicAPIClient"
import ApplicationApi from "./applications"
import TableApi from "./tables"
import UserApi from "./users"
import RowApi from "./rows"
export default class TestConfiguration<T> {
applications: ApplicationApi
users: UserApi
tables: TableApi
rows: RowApi
context: T
constructor(apiClient: PublicAPIClient) {
this.applications = new ApplicationApi(apiClient)
this.users = new UserApi(apiClient)
this.tables = new TableApi(apiClient)
this.rows = new RowApi(apiClient)
this.context = <T>{}
}
async beforeAll() {}
async afterAll() {
this.context = <T>{}
}
}

View File

@ -0,0 +1,58 @@
import PublicAPIClient from "./PublicAPIClient"
import {
CreateRowParams,
Row,
SearchInputParams,
} from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import { HeadersInit, Response } from "node-fetch"
export default class RowApi {
api: PublicAPIClient
headers?: HeadersInit
tableId?: string
constructor(apiClient: PublicAPIClient) {
this.api = apiClient
}
set appId(appId: string) {
this.headers = {
"x-budibase-app-id": appId,
}
}
async create(body: CreateRowParams): Promise<[Response, Row]> {
const response = await this.api.post(`/tables/${this.tableId}/rows`, {
body,
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
async read(id: string): Promise<[Response, Row]> {
const response = await this.api.get(`/tables/${this.tableId}/rows/${id}`, {
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
async search(body: SearchInputParams): Promise<[Response, Row]> {
const response = await this.api.post(
`/tables/${this.tableId}/rows/search`,
{ body, headers: this.headers }
)
const json = await response.json()
return [response, json.data]
}
async update(id: string, body: Row): Promise<[Response, Row]> {
const response = await this.api.put(`/tables/${this.tableId}/rows/${id}`, {
body,
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
}

View File

@ -0,0 +1,62 @@
import PublicAPIClient from "./PublicAPIClient"
import {
Table,
SearchInputParams,
CreateTableParams,
} from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import { HeadersInit, Response } from "node-fetch"
import { generateTable } from "../fixtures/tables"
export default class TableApi {
api: PublicAPIClient
headers?: HeadersInit
constructor(apiClient: PublicAPIClient) {
this.api = apiClient
}
async seed() {
return this.create(generateTable())
}
set appId(appId: string) {
this.headers = {
"x-budibase-app-id": appId,
}
}
async create(body: CreateTableParams): Promise<[Response, Table]> {
const response = await this.api.post(`/tables`, {
body,
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
async read(id: string): Promise<[Response, Table]> {
const response = await this.api.get(`/tables/${id}`, {
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
async search(body: SearchInputParams): Promise<[Response, Table]> {
const response = await this.api.post(`/tables/search`, {
body,
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
async update(id: string, body: Table): Promise<[Response, Table]> {
const response = await this.api.put(`/tables/${id}`, {
body,
headers: this.headers,
})
const json = await response.json()
return [response, json.data]
}
}

View File

@ -0,0 +1,41 @@
import PublicAPIClient from "./PublicAPIClient"
import {
CreateUserParams,
SearchInputParams,
User,
} from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import { Response } from "node-fetch"
export default class UserApi {
api: PublicAPIClient
constructor(apiClient: PublicAPIClient) {
this.api = apiClient
}
async seed() {}
async create(body: CreateUserParams): Promise<[Response, User]> {
const response = await this.api.post(`/users`, { body })
const json = await response.json()
return [response, json.data]
}
async read(id: string): Promise<[Response, User]> {
const response = await this.api.get(`/users/${id}`)
const json = await response.json()
return [response, json.data]
}
async search(body: SearchInputParams): Promise<[Response, User]> {
const response = await this.api.post(`/users/search`, { body })
const json = await response.json()
return [response, json.data]
}
async update(id: string, body: User): Promise<[Response, User]> {
const response = await this.api.put(`/users/${id}`, { body })
const json = await response.json()
return [response, json.data]
}
}

View File

@ -0,0 +1,10 @@
import generator from "../TestConfiguration/generator"
import { CreateApplicationParams } from "../../../../../packages/server/src/api/controllers/public/mapping/types"
const generate = (overrides = {}): CreateApplicationParams => ({
name: generator.word(),
url: `/${generator.word()}`,
...overrides,
})
export default generate

View File

@ -0,0 +1,56 @@
import {
CreateRowParams,
CreateTableParams,
} from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import generator from "../TestConfiguration/generator"
export const generateTable = (overrides = {}): CreateTableParams => ({
name: generator.word(),
primaryDisplay: "sasa",
schema: {
"Auto ID": {
autocolumn: true,
name: "Auto ID",
type: "number",
},
"Created At": {
autocolumn: true,
name: "Created At",
type: "datetime",
},
"Created By": {
autocolumn: true,
fieldName: "test12-Created By",
name: "Created By",
relationshipType: "many-to-many",
tableId: "ta_users",
type: "link",
},
sasa: {
name: "sasa",
type: "string",
},
"Updated At": {
autocolumn: true,
name: "Updated At",
type: "datetime",
},
"Updated By": {
autocolumn: true,
fieldName: "test12-Updated By",
name: "Updated By",
relationshipType: "many-to-many",
tableId: "ta_users",
type: "link",
},
},
...overrides,
})
export const generateRow = (overrides = {}): CreateRowParams => ({
type: "row",
tableId: "seed_table",
sasa: "Mike",
relationship: ["ro_ta_"],
...overrides,
})

View File

@ -0,0 +1,22 @@
import { CreateUserParams } from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import generator from "../TestConfiguration/generator"
const generate = (overrides = {}): CreateUserParams => ({
email: generator.email(),
roles: {
[generator.string({ length: 32, alpha: true, numeric: true })]:
generator.word(),
},
password: generator.word(),
status: "active",
forceResetPassword: generator.bool(),
builder: {
global: generator.bool(),
},
admin: {
global: generator.bool(),
},
...overrides,
})
export default generate

View File

@ -1,8 +1,10 @@
export = {
const env = {
BUDIBASE_SERVER_URL: process.env.BUDIBASE_SERVER_URL,
BUDIBASE_PUBLIC_API_KEY: process.env.BUDIBASE_PUBLIC_API_KEY,
_set(key: any, value: any) {
process.env[key] = value
module.exports[key] = value
},
}
}
export = env

View File

@ -1,21 +1,22 @@
import { Response } from "node-fetch"
// boilerplate to allow TS updates to the global scope
export {};
export {}
declare global {
namespace jest {
interface Matchers<R> {
toHaveStatusCode(code: number): R;
toHaveStatusCode(code: number): R
}
}
}
// Expect extensions
expect.extend({
toHaveStatusCode(received, code) {
toHaveStatusCode(received: Response, code: number) {
const pass = received.status === code
return {
message: () =>
`expected ${received.status} to match status code ${code}`,
message: () => `expected ${received.status} to match status code ${code}`,
pass,
}
},

View File

@ -1,39 +0,0 @@
import PublicAPIClient from "./PublicAPIClient";
import generateApp from "./applications/fixtures/generate"
class TestConfiguration {
testContext: Record<string, any>;
apiClient: PublicAPIClient;
constructor() {
this.testContext = {}
this.apiClient = new PublicAPIClient()
}
async beforeAll() {
}
async afterAll() {
}
async seedTable(appId: string) {
const response = await this.apiClient.post("/tables", {
body: require("./tables/fixtures/seed.json"),
headers: {
"x-budibase-app-id": appId
}
})
const json = await response.json()
return json.data
}
async seedApp() {
const response = await this.apiClient.post("/applications", {
body: generateApp()
})
return response.json()
}
}
export default TestConfiguration

View File

@ -1,10 +1,11 @@
import TestConfiguration from "../TestConfiguration"
import PublicAPIClient from "../PublicAPIClient"
import generateApp from "./fixtures/generate"
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import generateApp from "../../../config/public-api/fixtures/applications"
import { Application } from "../../../../../packages/server/src/api/controllers/public/mapping/types"
describe("Public API - /applications endpoints", () => {
const api = new PublicAPIClient()
const config = new TestConfiguration()
const config = new TestConfiguration<Application>(api)
beforeAll(async () => {
await config.beforeAll()
@ -14,34 +15,30 @@ describe("Public API - /applications endpoints", () => {
await config.afterAll()
})
it("POST - Create a application", async () => {
const response = await api.post(`/applications`, {
body: generateApp()
})
const json = await response.json()
config.testContext.application = json.data
it("POST - Create an application", async () => {
const [response, app] = await config.applications.create(generateApp())
config.context = app
expect(response).toHaveStatusCode(200)
})
it("POST - Search applications", async () => {
const response = await api.post(`/applications/search`, {
body: {
name: config.testContext.application.name
}
const [response, app] = await config.applications.search({
name: config.context.name,
})
expect(response).toHaveStatusCode(200)
})
it("GET - Retrieve a application", async () => {
const response = await api.get(`/applications/${config.testContext.application._id}`)
it("GET - Retrieve an application", async () => {
const [response, app] = await config.applications.read(config.context._id)
expect(response).toHaveStatusCode(200)
})
it("PUT - update a application", async () => {
const response = await api.put(`/applications/${config.testContext.application._id}`, {
body: require("./fixtures/update_application.json")
})
it("PUT - update an application", async () => {
config.context.name = "UpdatedName"
const [response, app] = await config.applications.update(
config.context._id,
config.context
)
expect(response).toHaveStatusCode(200)
})
})

View File

@ -1,4 +0,0 @@
{
"name": "TestApp",
"url": "/testapp"
}

View File

@ -1,9 +0,0 @@
import generator from "../../generator"
const generate = (overrides = {}) => ({
name: generator.word(),
url: `/${generator.word()}`,
...overrides
})
export default generate

View File

@ -1,4 +0,0 @@
{
"name": "SeedApp",
"url": "/seedapp"
}

View File

@ -1,4 +0,0 @@
{
"name": "UpdatedTestApp",
"url": "/updatedtestapp"
}

View File

@ -1,8 +0,0 @@
{
"type": "row",
"tableId": "seed_table",
"sasa": "Mike",
"relationship": [
"ro_ta_"
]
}

View File

@ -1,94 +0,0 @@
{
"name": "test",
"primaryDisplay": "sasa",
"schema": {
"Auto ID": {
"autocolumn": true,
"constraints": {
"numericality": {
"greaterThanOrEqualTo": "",
"lessThanOrEqualTo": ""
},
"presence": false,
"type": "number"
},
"icon": "ri-magic-line",
"name": "Auto ID",
"subtype": "autoID",
"type": "number"
},
"Created At": {
"autocolumn": true,
"constraints": {
"datetime": {
"earliest": "",
"latest": ""
},
"length": {},
"presence": false,
"type": "string"
},
"icon": "ri-magic-line",
"name": "Created At",
"subtype": "createdAt",
"type": "datetime"
},
"Created By": {
"autocolumn": true,
"constraints": {
"presence": false,
"type": "array"
},
"fieldName": "test12-Created By",
"icon": "ri-magic-line",
"name": "Created By",
"relationshipType": "many-to-many",
"subtype": "createdBy",
"tableId": "ta_users",
"type": "link"
},
"sasa": {
"constraints": {
"length": {
"maximum": null
},
"presence": {
"allowEmpty": false
},
"type": "string"
},
"name": "sasa",
"type": "string"
},
"Updated At": {
"autocolumn": true,
"constraints": {
"datetime": {
"earliest": "",
"latest": ""
},
"length": {},
"presence": false,
"type": "string"
},
"icon": "ri-magic-line",
"name": "Updated At",
"subtype": "updatedAt",
"type": "datetime"
},
"Updated By": {
"autocolumn": true,
"constraints": {
"presence": false,
"type": "array"
},
"fieldName": "test12-Updated By",
"icon": "ri-magic-line",
"name": "Updated By",
"relationshipType": "many-to-many",
"subtype": "updatedBy",
"tableId": "ta_users",
"type": "link"
}
}
}

View File

@ -1,94 +0,0 @@
{
"name": "test",
"primaryDisplay": "sasa",
"schema": {
"Auto ID": {
"autocolumn": true,
"constraints": {
"numericality": {
"greaterThanOrEqualTo": "",
"lessThanOrEqualTo": ""
},
"presence": false,
"type": "number"
},
"icon": "ri-magic-line",
"name": "Auto ID",
"subtype": "autoID",
"type": "number"
},
"Created At": {
"autocolumn": true,
"constraints": {
"datetime": {
"earliest": "",
"latest": ""
},
"length": {},
"presence": false,
"type": "string"
},
"icon": "ri-magic-line",
"name": "Created At",
"subtype": "createdAt",
"type": "datetime"
},
"Created By": {
"autocolumn": true,
"constraints": {
"presence": false,
"type": "array"
},
"fieldName": "test12-Created By",
"icon": "ri-magic-line",
"name": "Created By",
"relationshipType": "many-to-many",
"subtype": "createdBy",
"tableId": "ta_users",
"type": "link"
},
"sasa": {
"constraints": {
"length": {
"maximum": null
},
"presence": {
"allowEmpty": false
},
"type": "string"
},
"name": "sasa",
"type": "string"
},
"Updated At": {
"autocolumn": true,
"constraints": {
"datetime": {
"earliest": "",
"latest": ""
},
"length": {},
"presence": false,
"type": "string"
},
"icon": "ri-magic-line",
"name": "Updated At",
"subtype": "updatedAt",
"type": "datetime"
},
"Updated By": {
"autocolumn": true,
"constraints": {
"presence": false,
"type": "array"
},
"fieldName": "test12-Updated By",
"icon": "ri-magic-line",
"name": "Updated By",
"relationshipType": "many-to-many",
"subtype": "updatedBy",
"tableId": "ta_users",
"type": "link"
}
}
}

View File

@ -1,8 +0,0 @@
{
"type": "row",
"tableId": "seed_table",
"sasa": "MikeIsTheBest",
"relationship": [
"ro_ta_..."
]
}

View File

@ -1,94 +0,0 @@
{
"name": "test123",
"primaryDisplay": "sasa",
"schema": {
"Auto ID": {
"autocolumn": true,
"constraints": {
"numericality": {
"greaterThanOrEqualTo": "",
"lessThanOrEqualTo": ""
},
"presence": false,
"type": "number"
},
"icon": "ri-magic-line",
"name": "Auto ID",
"subtype": "autoID",
"type": "number"
},
"Created At": {
"autocolumn": true,
"constraints": {
"datetime": {
"earliest": "",
"latest": ""
},
"length": {},
"presence": false,
"type": "string"
},
"icon": "ri-magic-line",
"name": "Created At",
"subtype": "createdAt",
"type": "datetime"
},
"Created By": {
"autocolumn": true,
"constraints": {
"presence": false,
"type": "array"
},
"fieldName": "test12-Created By",
"icon": "ri-magic-line",
"name": "Created By",
"relationshipType": "many-to-many",
"subtype": "createdBy",
"tableId": "ta_users",
"type": "link"
},
"sasa": {
"constraints": {
"length": {
"maximum": null
},
"presence": {
"allowEmpty": false
},
"type": "string"
},
"name": "sasa",
"type": "string"
},
"Updated At": {
"autocolumn": true,
"constraints": {
"datetime": {
"earliest": "",
"latest": ""
},
"length": {},
"presence": false,
"type": "string"
},
"icon": "ri-magic-line",
"name": "Updated At",
"subtype": "updatedAt",
"type": "datetime"
},
"Updated By": {
"autocolumn": true,
"constraints": {
"presence": false,
"type": "array"
},
"fieldName": "test12-Updated By",
"icon": "ri-magic-line",
"name": "Updated By",
"relationshipType": "many-to-many",
"subtype": "updatedBy",
"tableId": "ta_users",
"type": "link"
}
}
}

View File

@ -1,17 +1,22 @@
import TestConfiguration from "../TestConfiguration"
import PublicAPIClient from "../PublicAPIClient"
import { Row } from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import { generateRow } from "../../../config/public-api/fixtures/tables"
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
describe("Public API - /rows endpoints", () => {
let api: PublicAPIClient
let api = new PublicAPIClient()
const config = new TestConfiguration()
const config = new TestConfiguration<Row>(api)
beforeAll(async () => {
await config.beforeAll()
const app = await config.seedApp()
const [appResp, app] = await config.applications.seed()
config.testContext.table = await config.seedTable(app.data._id)
api = new PublicAPIClient(app.data._id)
config.tables.appId = app._id
const [tableResp, table] = await config.tables.seed()
config.rows.appId = app._id
config.rows.tableId = table._id
})
afterAll(async () => {
@ -19,33 +24,29 @@ describe("Public API - /rows endpoints", () => {
})
it("POST - Create a row", async () => {
const response = await api.post(`/tables/${config.testContext.table._id}/rows`, {
body: require("./fixtures/row.json")
})
const json = await response.json()
config.testContext.row = json.data
const [response, row] = await config.rows.create(generateRow())
config.context = row
expect(response).toHaveStatusCode(200)
})
it("POST - Search rows", async () => {
const response = await api.post(`/tables/${config.testContext.table._id}/rows/search`, {
body: {
name: config.testContext.row.name
}
const [response, row] = await config.rows.search({
name: config.context.name as string,
})
expect(response).toHaveStatusCode(200)
})
it("GET - Retrieve a row", async () => {
const response = await api.get(`/tables/${config.testContext.table._id}/rows/${config.testContext.row._id}`)
const [response, row] = await config.rows.read(config.context._id)
expect(response).toHaveStatusCode(200)
})
it("PUT - update a row", async () => {
const response = await api.put(`/tables/${config.testContext.table._id}/rows/${config.testContext.row._id}`, {
body: require("./fixtures/update_row.json")
})
config.context.name = "UpdatedName"
const [response, row] = await config.rows.update(
config.context._id,
config.context
)
expect(response).toHaveStatusCode(200)
})
})

View File

@ -1,14 +1,16 @@
import TestConfiguration from "../TestConfiguration"
import PublicAPIClient from "../PublicAPIClient"
import { Table } from "../../../../../packages/server/src/api/controllers/public/mapping/types"
import { generateTable } from "../../../config/public-api/fixtures/tables"
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
describe("Public API - /tables endpoints", () => {
let api: PublicAPIClient
const config = new TestConfiguration()
let api = new PublicAPIClient()
const config = new TestConfiguration<Table>(api)
beforeAll(async () => {
await config.beforeAll()
const app = await config.seedApp()
api = new PublicAPIClient(app.data._id)
const [_, app] = await config.applications.seed()
config.tables.appId = app._id
})
afterAll(async () => {
@ -16,33 +18,29 @@ describe("Public API - /tables endpoints", () => {
})
it("POST - Create a table", async () => {
const response = await api.post(`/tables`, {
body: require("./fixtures/table.json")
})
const json = await response.json()
config.testContext.table = json.data
const [response, table] = await config.tables.create(generateTable())
config.context = table
expect(response).toHaveStatusCode(200)
})
it("POST - Search tables", async () => {
const response = await api.post(`/tables/search`, {
body: {
name: config.testContext.table.name
}
const [response, table] = await config.tables.search({
name: config.context.name,
})
expect(response).toHaveStatusCode(200)
})
it("GET - Retrieve a table", async () => {
const response = await api.get(`/tables/${config.testContext.table._id}`)
const [response, table] = await config.tables.read(config.context._id)
expect(response).toHaveStatusCode(200)
})
it("PUT - update a table", async () => {
const response = await api.put(`/tables/${config.testContext.table._id}`, {
body: require("./fixtures/update_table.json")
})
config.context.name = "updatedName"
const [response, table] = await config.tables.update(
config.context._id,
config.context
)
expect(response).toHaveStatusCode(200)
})
})

View File

@ -1,22 +0,0 @@
import generator from "../../generator"
import { User } from "@budibase/types"
const generate = (overrides = {}): User => ({
tenantId: generator.word(),
email: generator.email(),
roles: {
[generator.string({ length: 32, alpha: true, numeric: true })]: generator.word(),
},
password: generator.word(),
status: "active",
forceResetPassword: generator.bool(),
builder: {
global: generator.bool()
},
admin: {
global: generator.bool()
},
...overrides
})
export default generate

View File

@ -1,18 +0,0 @@
{
"email": "test@budibase.com",
"roles": {
"sed_6d7": "sit ea amet",
"cupidatat_e16": "fugiat proident sed"
},
"password": "cupidatat Lorem ad",
"status": "active",
"firstName": "QA",
"lastName": "Updated",
"forceResetPassword": true,
"builder": {
"global": true
},
"admin": {
"global": false
}
}

View File

@ -1,18 +0,0 @@
{
"email": "test@budibase.com",
"roles": {
"sed_6d7": "sit ea amet",
"cupidatat_e16": "fugiat proident sed"
},
"password": "cupidatat Lorem ad",
"status": "active",
"firstName": "QA",
"lastName": "Test",
"forceResetPassword": true,
"builder": {
"global": true
},
"admin": {
"global": false
}
}

View File

@ -1,9 +1,11 @@
import TestConfiguration from "../TestConfiguration"
import PublicAPIClient from "../PublicAPIClient"
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import generateUser from "../../../config/public-api/fixtures/users"
import { User } from "../../../../../packages/server/src/api/controllers/public/mapping/types"
describe("Public API - /users endpoints", () => {
const api = new PublicAPIClient()
const config = new TestConfiguration()
const config = new TestConfiguration<User>(api)
beforeAll(async () => {
await config.beforeAll()
@ -14,33 +16,29 @@ describe("Public API - /users endpoints", () => {
})
it("POST - Create a user", async () => {
const response = await api.post(`/users`, {
body: require("./fixtures/user.json")
})
const json = await response.json()
config.testContext.user = json.data
const [response, user] = await config.users.create(generateUser())
config.context = user
expect(response).toHaveStatusCode(200)
})
it("POST - Search users", async () => {
const response = await api.post(`/users/search`, {
body: {
name: config.testContext.user.email
}
const [response, user] = await config.users.search({
name: config.context.email,
})
expect(response).toHaveStatusCode(200)
})
it("GET - Retrieve a user", async () => {
const response = await api.get(`/users/${config.testContext.user._id}`)
const [response, user] = await config.users.read(config.context._id)
expect(response).toHaveStatusCode(200)
})
it("PUT - update a user", async () => {
const response = await api.put(`/users/${config.testContext.user._id}`, {
body: require("./fixtures/update_user.json")
})
config.context.firstName = "Updated First Name"
const [response, user] = await config.users.update(
config.context._id,
config.context
)
expect(response).toHaveStatusCode(200)
})
})

View File

@ -14,8 +14,6 @@
"skipLibCheck": true,
"paths": {
"@budibase/types": ["../packages/types/src"],
"@budibase/backend-core": ["../packages/backend-core/src"],
"@budibase/backend-core/*": ["../packages/backend-core/*"]
}
},
"ts-node": {
@ -23,7 +21,6 @@
},
"references": [
{ "path": "../packages/types" },
{ "path": "../packages/backend-core" },
],
"include": [
"src/**/*",

View File

@ -290,7 +290,7 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/types@^1.3.4":
"@budibase/types@1.3.4":
version "1.3.4"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.3.4.tgz#25f087b024e843eb372e50c81f8f925fb39f1dfd"
integrity sha512-ndyWs8yeCS7cpZjApDB1HhY6UUM2SRBUgAMCZOZaWABG9JHeCbx7x0e/pA2SZjswdMXqS5WmnEd3br5wuvUzJw==
@ -342,7 +342,7 @@
jest-util "^28.1.3"
slash "^3.0.0"
"@jest/core@^28.1.3":
"@jest/core@^28.0.2", "@jest/core@^28.1.3":
version "28.1.3"
resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7"
integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==
@ -712,7 +712,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@^29.0.0":
"@types/jest@29.0.0":
version "29.0.0"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.0.0.tgz#bc66835bf6b09d6a47e22c21d7f5b82692e60e72"
integrity sha512-X6Zjz3WO4cT39Gkl0lZ2baFRaEMqJl5NC1OjElkwtNzAlbkr2K/WJXkBkH5VP0zx4Hgsd2TZYdOEfvp2Dxia+Q==
@ -720,7 +720,7 @@
expect "^29.0.0"
pretty-format "^29.0.0"
"@types/node-fetch@^2.6.2":
"@types/node-fetch@2.6.2":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==
@ -979,7 +979,7 @@ chalk@^4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chance@^1.1.8:
chance@1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.8.tgz#5d6c2b78c9170bf6eb9df7acdda04363085be909"
integrity sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg==
@ -1499,7 +1499,7 @@ jest-circus@^28.1.3:
slash "^3.0.0"
stack-utils "^2.0.3"
jest-cli@^28.1.3:
jest-cli@^28.0.2:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2"
integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==
@ -1866,15 +1866,14 @@ jest-worker@^28.1.3:
merge-stream "^2.0.0"
supports-color "^8.0.0"
jest@^28.0.2:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b"
integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==
jest@28.0.2:
version "28.0.2"
resolved "https://registry.yarnpkg.com/jest/-/jest-28.0.2.tgz#41385ca21d009708bb9fc65e08663110e08e2cc0"
integrity sha512-COUtjybolW4koQvO7kCfq5kgbeeU5WbSJfVZprz4zbS8AL32+RAZZTUjBEyRRdpsXqss/pHIvSL7/P+LyMYHXg==
dependencies:
"@jest/core" "^28.1.3"
"@jest/types" "^28.1.3"
"@jest/core" "^28.0.2"
import-local "^3.0.2"
jest-cli "^28.1.3"
jest-cli "^28.0.2"
joi@^17.4.0:
version "17.6.0"
@ -2163,7 +2162,7 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
prettier@^2.7.1:
prettier@2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
@ -2328,7 +2327,7 @@ stack-utils@^2.0.3:
dependencies:
escape-string-regexp "^2.0.0"
start-server-and-test@^1.14.0:
start-server-and-test@1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3"
integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw==
@ -2448,7 +2447,7 @@ through@2, through@~2.3, through@~2.3.1:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
timekeeper@^2.2.0:
timekeeper@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368"
integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==
@ -2489,7 +2488,7 @@ ts-jest@28.0.8:
semver "7.x"
yargs-parser "^21.0.1"
ts-node@^10.9.1:
ts-node@10.9.1:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
@ -2508,7 +2507,7 @@ ts-node@^10.9.1:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tsconfig-paths@^4.1.0:
tsconfig-paths@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz#f8ef7d467f08ae3a695335bf1ece088c5538d2c1"
integrity sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==
@ -2532,10 +2531,10 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typescript@^4.8.2:
version "4.8.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790"
integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==
typescript@4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
update-browserslist-db@^1.0.5:
version "1.0.7"