Merge pull request #7646 from Budibase/qa/qa-core-jest
QA Core repo, and Jest API tests running end to end
This commit is contained in:
commit
c86bdffc52
36 changed files with 3841 additions and 342 deletions
6
.github/workflows/budibase_ci.yml
vendored
6
.github/workflows/budibase_ci.yml
vendored
|
@ -59,3 +59,9 @@ jobs:
|
||||||
with:
|
with:
|
||||||
install: false
|
install: false
|
||||||
command: yarn test:e2e:ci
|
command: yarn test:e2e:ci
|
||||||
|
|
||||||
|
- name: QA Core Integration Tests
|
||||||
|
run: |
|
||||||
|
cd qa-core
|
||||||
|
yarn
|
||||||
|
yarn api:test:ci
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -63,6 +63,7 @@ typings/
|
||||||
|
|
||||||
# dotenv environment variables file
|
# dotenv environment variables file
|
||||||
.env
|
.env
|
||||||
|
!qa-core/.env
|
||||||
!hosting/.env
|
!hosting/.env
|
||||||
hosting/.generated-nginx.dev.conf
|
hosting/.generated-nginx.dev.conf
|
||||||
hosting/proxy/.generated-nginx.prod.conf
|
hosting/proxy/.generated-nginx.prod.conf
|
||||||
|
|
|
@ -8,4 +8,4 @@ packages/server/client
|
||||||
packages/server/src/definitions/openapi.ts
|
packages/server/src/definitions/openapi.ts
|
||||||
packages/builder/.routify
|
packages/builder/.routify
|
||||||
packages/builder/cypress/support/queryLevelTransformerFunction.js
|
packages/builder/cypress/support/queryLevelTransformerFunction.js
|
||||||
packages/builder/cypress/support/queryLevelTransformerFunctionWithData.js
|
packages/builder/cypress/support/queryLevelTransformerFunctionWithData.js
|
|
@ -45,8 +45,8 @@
|
||||||
"lint:eslint": "eslint packages",
|
"lint:eslint": "eslint packages",
|
||||||
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"",
|
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"",
|
||||||
"lint": "yarn run lint:eslint && yarn run lint:prettier",
|
"lint": "yarn run lint:eslint && yarn run lint:prettier",
|
||||||
"lint:fix:eslint": "eslint --fix packages",
|
"lint:fix:eslint": "eslint --fix packages qa-core",
|
||||||
"lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\"",
|
"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",
|
"lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint",
|
||||||
"test:e2e": "lerna run cy:test --stream",
|
"test:e2e": "lerna run cy:test --stream",
|
||||||
"test:e2e:ci": "lerna run cy:ci --stream",
|
"test:e2e:ci": "lerna run cy:ci --stream",
|
||||||
|
|
|
@ -1659,6 +1659,117 @@
|
||||||
"data"
|
"data"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"rowSearch": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"query": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"allOr": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used."
|
||||||
|
},
|
||||||
|
"string": {
|
||||||
|
"type": "object",
|
||||||
|
"example": {
|
||||||
|
"columnName1": "value",
|
||||||
|
"columnName2": "value"
|
||||||
|
},
|
||||||
|
"description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The value to search for in the column."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fuzzy": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A fuzzy search, only supported by internal tables."
|
||||||
|
},
|
||||||
|
"range": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.",
|
||||||
|
"example": {
|
||||||
|
"columnName1": {
|
||||||
|
"low": 10,
|
||||||
|
"high": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"equal": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Searches for rows that have a column value that is exactly the value set."
|
||||||
|
},
|
||||||
|
"notEqual": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Searches for any row which does not contain the specified column value."
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.",
|
||||||
|
"example": {
|
||||||
|
"columnName1": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notEmpty": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Searches for rows which have the specified column."
|
||||||
|
},
|
||||||
|
"oneOf": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"paginate": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enables pagination, by default this is disabled."
|
||||||
|
},
|
||||||
|
"bookmark": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "If retrieving another page, the bookmark from the previous request must be supplied."
|
||||||
|
},
|
||||||
|
"limit": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The maximum number of rows to return, useful when paginating, for internal tables this will be limited to 1000, for SQL tables it will be 5000."
|
||||||
|
},
|
||||||
|
"sort": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A set of parameters describing the sort behaviour of the search.",
|
||||||
|
"properties": {
|
||||||
|
"order": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ascending",
|
||||||
|
"descending"
|
||||||
|
],
|
||||||
|
"description": "The order of the sort, by default this is ascending."
|
||||||
|
},
|
||||||
|
"column": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the column by which the rows will be sorted."
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"string",
|
||||||
|
"number"
|
||||||
|
],
|
||||||
|
"description": "Defines whether the column should be treated as a string or as numbers when sorting."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"query"
|
||||||
|
]
|
||||||
|
},
|
||||||
"nameSearch": {
|
"nameSearch": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -2129,115 +2240,7 @@
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"$ref": "#/components/schemas/rowSearch"
|
||||||
"required": [
|
|
||||||
"query"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"query": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"allOr": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used."
|
|
||||||
},
|
|
||||||
"string": {
|
|
||||||
"type": "object",
|
|
||||||
"example": {
|
|
||||||
"columnName1": "value",
|
|
||||||
"columnName2": "value"
|
|
||||||
},
|
|
||||||
"description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The value to search for in the column."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fuzzy": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "A fuzzy search, only supported by internal tables."
|
|
||||||
},
|
|
||||||
"range": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.",
|
|
||||||
"example": {
|
|
||||||
"columnName1": {
|
|
||||||
"low": 10,
|
|
||||||
"high": 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"equal": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Searches for rows that have a column value that is exactly the value set."
|
|
||||||
},
|
|
||||||
"notEqual": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Searches for any row which does not contain the specified column value."
|
|
||||||
},
|
|
||||||
"empty": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.",
|
|
||||||
"example": {
|
|
||||||
"columnName1": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notEmpty": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Searches for rows which have the specified column."
|
|
||||||
},
|
|
||||||
"oneOf": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"paginate": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Enables pagination, by default this is disabled."
|
|
||||||
},
|
|
||||||
"bookmark": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "If retrieving another page, the bookmark from the previous request must be supplied."
|
|
||||||
},
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The maximum number of rows to return, useful when paginating, for internal tables this will be limited to 1000, for SQL tables it will be 5000."
|
|
||||||
},
|
|
||||||
"sort": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "A set of parameters describing the sort behaviour of the search.",
|
|
||||||
"properties": {
|
|
||||||
"order": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"ascending",
|
|
||||||
"descending"
|
|
||||||
],
|
|
||||||
"description": "The order of the sort, by default this is ascending."
|
|
||||||
},
|
|
||||||
"column": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The name of the column by which the rows will be sorted."
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"string",
|
|
||||||
"number"
|
|
||||||
],
|
|
||||||
"description": "Defines whether the column should be treated as a string or as numbers when sorting."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1264,6 +1264,98 @@ components:
|
||||||
- _id
|
- _id
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
rowSearch:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
query:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
allOr:
|
||||||
|
type: boolean
|
||||||
|
description: Specifies that a row should be returned if it satisfies any of the
|
||||||
|
specified options, rather than requiring it to fulfill all the
|
||||||
|
search parameters. This defaults to false, meaning AND logic
|
||||||
|
will be used.
|
||||||
|
string:
|
||||||
|
type: object
|
||||||
|
example:
|
||||||
|
columnName1: value
|
||||||
|
columnName2: value
|
||||||
|
description: A map of field name to the string to search for, this will look for
|
||||||
|
rows that have a value starting with the string value.
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: The value to search for in the column.
|
||||||
|
fuzzy:
|
||||||
|
type: object
|
||||||
|
description: A fuzzy search, only supported by internal tables.
|
||||||
|
range:
|
||||||
|
type: object
|
||||||
|
description: Searches within a range, the format of this must be in the format
|
||||||
|
of an object with a "low" and "high" property.
|
||||||
|
example:
|
||||||
|
columnName1:
|
||||||
|
low: 10
|
||||||
|
high: 20
|
||||||
|
equal:
|
||||||
|
type: object
|
||||||
|
description: Searches for rows that have a column value that is exactly the
|
||||||
|
value set.
|
||||||
|
notEqual:
|
||||||
|
type: object
|
||||||
|
description: Searches for any row which does not contain the specified column
|
||||||
|
value.
|
||||||
|
empty:
|
||||||
|
type: object
|
||||||
|
description: Searches for rows which do not contain the specified column. The
|
||||||
|
object should simply contain keys of the column names, these can
|
||||||
|
map to any value.
|
||||||
|
example:
|
||||||
|
columnName1: ""
|
||||||
|
notEmpty:
|
||||||
|
type: object
|
||||||
|
description: Searches for rows which have the specified column.
|
||||||
|
oneOf:
|
||||||
|
type: object
|
||||||
|
description: Searches for rows which have a column value that is any of the
|
||||||
|
specified values. The format of this must be columnName ->
|
||||||
|
[value1, value2].
|
||||||
|
paginate:
|
||||||
|
type: boolean
|
||||||
|
description: Enables pagination, by default this is disabled.
|
||||||
|
bookmark:
|
||||||
|
oneOf:
|
||||||
|
- type: string
|
||||||
|
- type: integer
|
||||||
|
description: If retrieving another page, the bookmark from the previous request
|
||||||
|
must be supplied.
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
description: The maximum number of rows to return, useful when paginating, for
|
||||||
|
internal tables this will be limited to 1000, for SQL tables it will
|
||||||
|
be 5000.
|
||||||
|
sort:
|
||||||
|
type: object
|
||||||
|
description: A set of parameters describing the sort behaviour of the search.
|
||||||
|
properties:
|
||||||
|
order:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ascending
|
||||||
|
- descending
|
||||||
|
description: The order of the sort, by default this is ascending.
|
||||||
|
column:
|
||||||
|
type: string
|
||||||
|
description: The name of the column by which the rows will be sorted.
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- string
|
||||||
|
- number
|
||||||
|
description: Defines whether the column should be treated as a string or as
|
||||||
|
numbers when sorting.
|
||||||
|
required:
|
||||||
|
- query
|
||||||
nameSearch:
|
nameSearch:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -1544,97 +1636,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
$ref: "#/components/schemas/rowSearch"
|
||||||
required:
|
|
||||||
- query
|
|
||||||
properties:
|
|
||||||
query:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
allOr:
|
|
||||||
type: boolean
|
|
||||||
description: Specifies that a row should be returned if it satisfies any of the
|
|
||||||
specified options, rather than requiring it to fulfill
|
|
||||||
all the search parameters. This defaults to false,
|
|
||||||
meaning AND logic will be used.
|
|
||||||
string:
|
|
||||||
type: object
|
|
||||||
example:
|
|
||||||
columnName1: value
|
|
||||||
columnName2: value
|
|
||||||
description: A map of field name to the string to search for, this will look for
|
|
||||||
rows that have a value starting with the string value.
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
description: The value to search for in the column.
|
|
||||||
fuzzy:
|
|
||||||
type: object
|
|
||||||
description: A fuzzy search, only supported by internal tables.
|
|
||||||
range:
|
|
||||||
type: object
|
|
||||||
description: Searches within a range, the format of this must be in the format
|
|
||||||
of an object with a "low" and "high" property.
|
|
||||||
example:
|
|
||||||
columnName1:
|
|
||||||
low: 10
|
|
||||||
high: 20
|
|
||||||
equal:
|
|
||||||
type: object
|
|
||||||
description: Searches for rows that have a column value that is exactly the
|
|
||||||
value set.
|
|
||||||
notEqual:
|
|
||||||
type: object
|
|
||||||
description: Searches for any row which does not contain the specified column
|
|
||||||
value.
|
|
||||||
empty:
|
|
||||||
type: object
|
|
||||||
description: Searches for rows which do not contain the specified column. The
|
|
||||||
object should simply contain keys of the column names,
|
|
||||||
these can map to any value.
|
|
||||||
example:
|
|
||||||
columnName1: ""
|
|
||||||
notEmpty:
|
|
||||||
type: object
|
|
||||||
description: Searches for rows which have the specified column.
|
|
||||||
oneOf:
|
|
||||||
type: object
|
|
||||||
description: Searches for rows which have a column value that is any of the
|
|
||||||
specified values. The format of this must be columnName
|
|
||||||
-> [value1, value2].
|
|
||||||
paginate:
|
|
||||||
type: boolean
|
|
||||||
description: Enables pagination, by default this is disabled.
|
|
||||||
bookmark:
|
|
||||||
oneOf:
|
|
||||||
- type: string
|
|
||||||
- type: integer
|
|
||||||
description: If retrieving another page, the bookmark from the previous request
|
|
||||||
must be supplied.
|
|
||||||
limit:
|
|
||||||
type: integer
|
|
||||||
description: The maximum number of rows to return, useful when paginating, for
|
|
||||||
internal tables this will be limited to 1000, for SQL tables
|
|
||||||
it will be 5000.
|
|
||||||
sort:
|
|
||||||
type: object
|
|
||||||
description: A set of parameters describing the sort behaviour of the search.
|
|
||||||
properties:
|
|
||||||
order:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- ascending
|
|
||||||
- descending
|
|
||||||
description: The order of the sort, by default this is ascending.
|
|
||||||
column:
|
|
||||||
type: string
|
|
||||||
description: The name of the column by which the rows will be sorted.
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- string
|
|
||||||
- number
|
|
||||||
description: Defines whether the column should be treated as a string or as
|
|
||||||
numbers when sorting.
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The response will contain an array of rows that match the search
|
description: The response will contain an array of rows that match the search
|
||||||
|
|
|
@ -2,6 +2,122 @@ const { object } = require("./utils")
|
||||||
const Resource = require("./utils/Resource")
|
const Resource = require("./utils/Resource")
|
||||||
|
|
||||||
module.exports = new Resource().setSchemas({
|
module.exports = new Resource().setSchemas({
|
||||||
|
rowSearch: object(
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
allOr: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used.",
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
type: "object",
|
||||||
|
example: {
|
||||||
|
columnName1: "value",
|
||||||
|
columnName2: "value",
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"A map of field name to the string to search for, this will look for rows that have a value starting with the string value.",
|
||||||
|
additionalProperties: {
|
||||||
|
type: "string",
|
||||||
|
description: "The value to search for in the column.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fuzzy: {
|
||||||
|
type: "object",
|
||||||
|
description: "A fuzzy search, only supported by internal tables.",
|
||||||
|
},
|
||||||
|
range: {
|
||||||
|
type: "object",
|
||||||
|
description:
|
||||||
|
'Searches within a range, the format of this must be in the format of an object with a "low" and "high" property.',
|
||||||
|
example: {
|
||||||
|
columnName1: {
|
||||||
|
low: 10,
|
||||||
|
high: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
equal: {
|
||||||
|
type: "object",
|
||||||
|
description:
|
||||||
|
"Searches for rows that have a column value that is exactly the value set.",
|
||||||
|
},
|
||||||
|
notEqual: {
|
||||||
|
type: "object",
|
||||||
|
description:
|
||||||
|
"Searches for any row which does not contain the specified column value.",
|
||||||
|
},
|
||||||
|
empty: {
|
||||||
|
type: "object",
|
||||||
|
description:
|
||||||
|
"Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.",
|
||||||
|
example: {
|
||||||
|
columnName1: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notEmpty: {
|
||||||
|
type: "object",
|
||||||
|
description: "Searches for rows which have the specified column.",
|
||||||
|
},
|
||||||
|
oneOf: {
|
||||||
|
type: "object",
|
||||||
|
description:
|
||||||
|
"Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2].",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paginate: {
|
||||||
|
type: "boolean",
|
||||||
|
description: "Enables pagination, by default this is disabled.",
|
||||||
|
},
|
||||||
|
bookmark: {
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description:
|
||||||
|
"If retrieving another page, the bookmark from the previous request must be supplied.",
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
type: "integer",
|
||||||
|
description:
|
||||||
|
"The maximum number of rows to return, useful when paginating, for internal tables this will be limited to 1000, for SQL tables it will be 5000.",
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
type: "object",
|
||||||
|
description:
|
||||||
|
"A set of parameters describing the sort behaviour of the search.",
|
||||||
|
properties: {
|
||||||
|
order: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["ascending", "descending"],
|
||||||
|
description: "The order of the sort, by default this is ascending.",
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"The name of the column by which the rows will be sorted.",
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["string", "number"],
|
||||||
|
description:
|
||||||
|
"Defines whether the column should be treated as a string or as numbers when sorting.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: ["query"],
|
||||||
|
}
|
||||||
|
),
|
||||||
nameSearch: object({
|
nameSearch: object({
|
||||||
name: {
|
name: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|
|
@ -4,10 +4,18 @@ export type Query = components["schemas"]["query"]
|
||||||
export type ExecuteQuery = components["schemas"]["executeQueryOutput"]
|
export type ExecuteQuery = components["schemas"]["executeQueryOutput"]
|
||||||
|
|
||||||
export type Application = components["schemas"]["applicationOutput"]["data"]
|
export type Application = components["schemas"]["applicationOutput"]["data"]
|
||||||
|
export type CreateApplicationParams = components["schemas"]["application"]
|
||||||
|
|
||||||
export type Table = components["schemas"]["tableOutput"]["data"]
|
export type Table = components["schemas"]["tableOutput"]["data"]
|
||||||
|
export type CreateTableParams = components["schemas"]["table"]
|
||||||
|
|
||||||
export type Row = components["schemas"]["rowOutput"]["data"]
|
export type Row = components["schemas"]["rowOutput"]["data"]
|
||||||
export type RowSearch = components["schemas"]["searchOutput"]
|
export type RowSearch = components["schemas"]["searchOutput"]
|
||||||
|
export type CreateRowParams = components["schemas"]["row"]
|
||||||
|
|
||||||
export type User = components["schemas"]["userOutput"]["data"]
|
export type User = components["schemas"]["userOutput"]["data"]
|
||||||
|
export type CreateUserParams = components["schemas"]["user"]
|
||||||
|
|
||||||
|
export type SearchInputParams =
|
||||||
|
| components["schemas"]["nameSearch"]
|
||||||
|
| components["schemas"]["rowSearch"]
|
||||||
|
|
|
@ -143,88 +143,7 @@ read.push(new Endpoint("get", "/tables/:tableId/rows/:rowId", controller.read))
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: object
|
* $ref: '#/components/schemas/rowSearch'
|
||||||
* required:
|
|
||||||
* - query
|
|
||||||
* properties:
|
|
||||||
* query:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* allOr:
|
|
||||||
* type: boolean
|
|
||||||
* description: Specifies that a row should be returned if it satisfies
|
|
||||||
* any of the specified options, rather than requiring it to fulfill all
|
|
||||||
* the search parameters. This defaults to false, meaning AND logic will be used.
|
|
||||||
* string:
|
|
||||||
* type: object
|
|
||||||
* example:
|
|
||||||
* columnName1: value
|
|
||||||
* columnName2: value
|
|
||||||
* description: A map of field name to the string to search for,
|
|
||||||
* this will look for rows that have a value starting with the
|
|
||||||
* string value.
|
|
||||||
* additionalProperties:
|
|
||||||
* type: string
|
|
||||||
* description: The value to search for in the column.
|
|
||||||
* fuzzy:
|
|
||||||
* type: object
|
|
||||||
* description: A fuzzy search, only supported by internal tables.
|
|
||||||
* range:
|
|
||||||
* type: object
|
|
||||||
* description: Searches within a range, the format of this must be
|
|
||||||
* in the format of an object with a "low" and "high" property.
|
|
||||||
* example:
|
|
||||||
* columnName1: { low: 10, high: 20 }
|
|
||||||
* equal:
|
|
||||||
* type: object
|
|
||||||
* description: Searches for rows that have a column value that is
|
|
||||||
* exactly the value set.
|
|
||||||
* notEqual:
|
|
||||||
* type: object
|
|
||||||
* description: Searches for any row which does not contain the specified
|
|
||||||
* column value.
|
|
||||||
* empty:
|
|
||||||
* type: object
|
|
||||||
* description: Searches for rows which do not contain the specified column.
|
|
||||||
* The object should simply contain keys of the column names, these
|
|
||||||
* can map to any value.
|
|
||||||
* example:
|
|
||||||
* columnName1: ""
|
|
||||||
* notEmpty:
|
|
||||||
* type: object
|
|
||||||
* description: Searches for rows which have the specified column.
|
|
||||||
* oneOf:
|
|
||||||
* type: object
|
|
||||||
* description: Searches for rows which have a column value that is any
|
|
||||||
* of the specified values. The format of this must be columnName -> [value1, value2].
|
|
||||||
* paginate:
|
|
||||||
* type: boolean
|
|
||||||
* description: Enables pagination, by default this is disabled.
|
|
||||||
* bookmark:
|
|
||||||
* oneOf:
|
|
||||||
* - type: string
|
|
||||||
* - type: integer
|
|
||||||
* description: If retrieving another page, the bookmark from the previous request must be supplied.
|
|
||||||
* limit:
|
|
||||||
* type: integer
|
|
||||||
* description: The maximum number of rows to return, useful when paginating, for internal tables this
|
|
||||||
* will be limited to 1000, for SQL tables it will be 5000.
|
|
||||||
* sort:
|
|
||||||
* type: object
|
|
||||||
* description: A set of parameters describing the sort behaviour of the search.
|
|
||||||
* properties:
|
|
||||||
* order:
|
|
||||||
* type: string
|
|
||||||
* enum: [ascending, descending]
|
|
||||||
* description: The order of the sort, by default this is ascending.
|
|
||||||
* column:
|
|
||||||
* type: string
|
|
||||||
* description: The name of the column by which the rows will be sorted.
|
|
||||||
* type:
|
|
||||||
* type: string
|
|
||||||
* enum: [string, number]
|
|
||||||
* description: Defines whether the column should be treated as a string
|
|
||||||
* or as numbers when sorting.
|
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The response will contain an array of rows that match the search parameters.
|
* description: The response will contain an array of rows that match the search parameters.
|
||||||
|
|
|
@ -30,7 +30,11 @@ const fs = require("fs")
|
||||||
import redis from "./utilities/redis"
|
import redis from "./utilities/redis"
|
||||||
import * as migrations from "./migrations"
|
import * as migrations from "./migrations"
|
||||||
import { events, installation, tenancy } from "@budibase/backend-core"
|
import { events, installation, tenancy } from "@budibase/backend-core"
|
||||||
import { createAdminUser, getChecklist } from "./utilities/workerRequests"
|
import {
|
||||||
|
createAdminUser,
|
||||||
|
generateApiKey,
|
||||||
|
getChecklist,
|
||||||
|
} from "./utilities/workerRequests"
|
||||||
import { watch } from "./watch"
|
import { watch } from "./watch"
|
||||||
import { initialise as initialiseWebsockets } from "./websocket"
|
import { initialise as initialiseWebsockets } from "./websocket"
|
||||||
|
|
||||||
|
@ -127,11 +131,16 @@ module.exports = server.listen(env.PORT || 0, async () => {
|
||||||
if (!checklist?.adminUser?.checked) {
|
if (!checklist?.adminUser?.checked) {
|
||||||
try {
|
try {
|
||||||
const tenantId = tenancy.getTenantId()
|
const tenantId = tenancy.getTenantId()
|
||||||
await createAdminUser(
|
const user = await createAdminUser(
|
||||||
env.BB_ADMIN_USER_EMAIL,
|
env.BB_ADMIN_USER_EMAIL,
|
||||||
env.BB_ADMIN_USER_PASSWORD,
|
env.BB_ADMIN_USER_PASSWORD,
|
||||||
tenantId
|
tenantId
|
||||||
)
|
)
|
||||||
|
// Need to set up an API key for automated integration tests
|
||||||
|
if (env.isTest()) {
|
||||||
|
await generateApiKey(user._id)
|
||||||
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"Admin account automatically created for",
|
"Admin account automatically created for",
|
||||||
env.BB_ADMIN_USER_EMAIL
|
env.BB_ADMIN_USER_EMAIL
|
||||||
|
|
|
@ -278,58 +278,7 @@ export interface paths {
|
||||||
};
|
};
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": components["schemas"]["rowSearch"];
|
||||||
query: {
|
|
||||||
/** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */
|
|
||||||
allOr?: boolean;
|
|
||||||
/**
|
|
||||||
* @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value.
|
|
||||||
* @example [object Object]
|
|
||||||
*/
|
|
||||||
string?: { [key: string]: string };
|
|
||||||
/** @description A fuzzy search, only supported by internal tables. */
|
|
||||||
fuzzy?: { [key: string]: unknown };
|
|
||||||
/**
|
|
||||||
* @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property.
|
|
||||||
* @example [object Object]
|
|
||||||
*/
|
|
||||||
range?: { [key: string]: unknown };
|
|
||||||
/** @description Searches for rows that have a column value that is exactly the value set. */
|
|
||||||
equal?: { [key: string]: unknown };
|
|
||||||
/** @description Searches for any row which does not contain the specified column value. */
|
|
||||||
notEqual?: { [key: string]: unknown };
|
|
||||||
/**
|
|
||||||
* @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.
|
|
||||||
* @example [object Object]
|
|
||||||
*/
|
|
||||||
empty?: { [key: string]: unknown };
|
|
||||||
/** @description Searches for rows which have the specified column. */
|
|
||||||
notEmpty?: { [key: string]: unknown };
|
|
||||||
/** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */
|
|
||||||
oneOf?: { [key: string]: unknown };
|
|
||||||
};
|
|
||||||
/** @description Enables pagination, by default this is disabled. */
|
|
||||||
paginate?: boolean;
|
|
||||||
/** @description If retrieving another page, the bookmark from the previous request must be supplied. */
|
|
||||||
bookmark?: string | number;
|
|
||||||
/** @description The maximum number of rows to return, useful when paginating, for internal tables this will be limited to 1000, for SQL tables it will be 5000. */
|
|
||||||
limit?: number;
|
|
||||||
/** @description A set of parameters describing the sort behaviour of the search. */
|
|
||||||
sort?: {
|
|
||||||
/**
|
|
||||||
* @description The order of the sort, by default this is ascending.
|
|
||||||
* @enum {string}
|
|
||||||
*/
|
|
||||||
order?: "ascending" | "descending";
|
|
||||||
/** @description The name of the column by which the rows will be sorted. */
|
|
||||||
column?: string;
|
|
||||||
/**
|
|
||||||
* @description Defines whether the column should be treated as a string or as numbers when sorting.
|
|
||||||
* @enum {string}
|
|
||||||
*/
|
|
||||||
type?: "string" | "number";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1105,6 +1054,58 @@ export interface components {
|
||||||
_id: string;
|
_id: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
rowSearch: {
|
||||||
|
query: {
|
||||||
|
/** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */
|
||||||
|
allOr?: boolean;
|
||||||
|
/**
|
||||||
|
* @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value.
|
||||||
|
* @example [object Object]
|
||||||
|
*/
|
||||||
|
string?: { [key: string]: string };
|
||||||
|
/** @description A fuzzy search, only supported by internal tables. */
|
||||||
|
fuzzy?: { [key: string]: unknown };
|
||||||
|
/**
|
||||||
|
* @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property.
|
||||||
|
* @example [object Object]
|
||||||
|
*/
|
||||||
|
range?: { [key: string]: unknown };
|
||||||
|
/** @description Searches for rows that have a column value that is exactly the value set. */
|
||||||
|
equal?: { [key: string]: unknown };
|
||||||
|
/** @description Searches for any row which does not contain the specified column value. */
|
||||||
|
notEqual?: { [key: string]: unknown };
|
||||||
|
/**
|
||||||
|
* @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.
|
||||||
|
* @example [object Object]
|
||||||
|
*/
|
||||||
|
empty?: { [key: string]: unknown };
|
||||||
|
/** @description Searches for rows which have the specified column. */
|
||||||
|
notEmpty?: { [key: string]: unknown };
|
||||||
|
/** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */
|
||||||
|
oneOf?: { [key: string]: unknown };
|
||||||
|
};
|
||||||
|
/** @description Enables pagination, by default this is disabled. */
|
||||||
|
paginate?: boolean;
|
||||||
|
/** @description If retrieving another page, the bookmark from the previous request must be supplied. */
|
||||||
|
bookmark?: string | number;
|
||||||
|
/** @description The maximum number of rows to return, useful when paginating, for internal tables this will be limited to 1000, for SQL tables it will be 5000. */
|
||||||
|
limit?: number;
|
||||||
|
/** @description A set of parameters describing the sort behaviour of the search. */
|
||||||
|
sort?: {
|
||||||
|
/**
|
||||||
|
* @description The order of the sort, by default this is ascending.
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
order?: "ascending" | "descending";
|
||||||
|
/** @description The name of the column by which the rows will be sorted. */
|
||||||
|
column?: string;
|
||||||
|
/**
|
||||||
|
* @description Defines whether the column should be treated as a string or as numbers when sorting.
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
type?: "string" | "number";
|
||||||
|
};
|
||||||
|
};
|
||||||
nameSearch: {
|
nameSearch: {
|
||||||
/** @description The name to be used when searching - this will be used in a case insensitive starts with match. */
|
/** @description The name to be used when searching - this will be used in a case insensitive starts with match. */
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -153,3 +153,11 @@ exports.getChecklist = async () => {
|
||||||
)
|
)
|
||||||
return checkResponse(response, "get checklist")
|
return checkResponse(response, "get checklist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.generateApiKey = async userId => {
|
||||||
|
const response = await fetch(
|
||||||
|
checkSlashesInUrl(env.WORKER_URL + "/api/global/self/api_key"),
|
||||||
|
request(null, { method: "POST", body: { userId } })
|
||||||
|
)
|
||||||
|
return checkResponse(response, "generate API key")
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,11 @@ const { newid } = require("@budibase/backend-core/utils")
|
||||||
const { users } = require("../../../sdk")
|
const { users } = require("../../../sdk")
|
||||||
const { Cookies } = require("@budibase/backend-core/constants")
|
const { Cookies } = require("@budibase/backend-core/constants")
|
||||||
const { events, featureFlags } = require("@budibase/backend-core")
|
const { events, featureFlags } = require("@budibase/backend-core")
|
||||||
|
const env = require("../../../environment")
|
||||||
|
|
||||||
|
function newTestApiKey() {
|
||||||
|
return env.ENCRYPTED_TEST_PUBLIC_API_KEY
|
||||||
|
}
|
||||||
|
|
||||||
function newApiKey() {
|
function newApiKey() {
|
||||||
return encrypt(`${getTenantId()}${SEPARATOR}${newid()}`)
|
return encrypt(`${getTenantId()}${SEPARATOR}${newid()}`)
|
||||||
|
@ -29,15 +34,25 @@ function cleanupDevInfo(info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.generateAPIKey = async ctx => {
|
exports.generateAPIKey = async ctx => {
|
||||||
|
let userId
|
||||||
|
let apiKey
|
||||||
|
if (env.isTest() && ctx.request.body.userId) {
|
||||||
|
userId = ctx.request.body.userId
|
||||||
|
apiKey = newTestApiKey()
|
||||||
|
} else {
|
||||||
|
userId = ctx.user._id
|
||||||
|
apiKey = newApiKey()
|
||||||
|
}
|
||||||
|
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
const id = generateDevInfoID(ctx.user._id)
|
const id = generateDevInfoID(userId)
|
||||||
let devInfo
|
let devInfo
|
||||||
try {
|
try {
|
||||||
devInfo = await db.get(id)
|
devInfo = await db.get(id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
devInfo = { _id: id, userId: ctx.user._id }
|
devInfo = { _id: id, userId }
|
||||||
}
|
}
|
||||||
devInfo.apiKey = await newApiKey()
|
devInfo.apiKey = await apiKey
|
||||||
await db.put(devInfo)
|
await db.put(devInfo)
|
||||||
ctx.body = cleanupDevInfo(devInfo)
|
ctx.body = cleanupDevInfo(devInfo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ const env = {
|
||||||
// other
|
// other
|
||||||
CHECKLIST_CACHE_TTL: parseIntSafe(process.env.CHECKLIST_CACHE_TTL) || 3600,
|
CHECKLIST_CACHE_TTL: parseIntSafe(process.env.CHECKLIST_CACHE_TTL) || 3600,
|
||||||
SESSION_UPDATE_PERIOD: process.env.SESSION_UPDATE_PERIOD,
|
SESSION_UPDATE_PERIOD: process.env.SESSION_UPDATE_PERIOD,
|
||||||
|
ENCRYPTED_TEST_PUBLIC_API_KEY: process.env.ENCRYPTED_TEST_PUBLIC_API_KEY,
|
||||||
_set(key: any, value: any) {
|
_set(key: any, value: any) {
|
||||||
process.env[key] = value
|
process.env[key] = value
|
||||||
module.exports[key] = value
|
module.exports[key] = value
|
||||||
|
|
3
qa-core/.env
Normal file
3
qa-core/.env
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
BB_ADMIN_USER_EMAIL=qa@budibase.com
|
||||||
|
BB_ADMIN_USER_PASSWORD=budibase
|
||||||
|
ENCRYPTED_TEST_PUBLIC_API_KEY=a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f
|
4
qa-core/.gitignore
vendored
Normal file
4
qa-core/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
.env
|
||||||
|
watchtower-hook.json
|
||||||
|
dist/
|
52
qa-core/package.json
Normal file
52
qa-core/package.json
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"name": "@budibase/qa-core",
|
||||||
|
"email": "hi@budibase.com",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "index.js",
|
||||||
|
"description": "Budibase Integration Test Suite",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Budibase/budibase.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "jest --runInBand",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:debug": "DEBUG=1 jest",
|
||||||
|
"api:server:setup": "env-cmd ts-node ../packages/builder/cypress/ts/setup.ts",
|
||||||
|
"api:server:setup:ci": "env-cmd node ../packages/builder/cypress/setup.js",
|
||||||
|
"api:test:ci": "start-server-and-test api:server:setup:ci http://localhost:4100/builder test",
|
||||||
|
"api:test": "start-server-and-test api:server:setup http://localhost:4100/builder test"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "ts-jest",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"@budibase/types": "<rootDir>/../packages/types/src",
|
||||||
|
"@budibase/server": "<rootDir>/../packages/server/src"
|
||||||
|
},
|
||||||
|
"setupFiles": [
|
||||||
|
"./scripts/jestSetup.js"
|
||||||
|
],
|
||||||
|
"setupFilesAfterEnv": [
|
||||||
|
"./src/jest.extends.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@budibase/types": "1.3.4",
|
||||||
|
"@types/jest": "29.0.0",
|
||||||
|
"@types/node-fetch": "2.6.2",
|
||||||
|
"chance": "1.1.8",
|
||||||
|
"env-cmd": "^10.1.0",
|
||||||
|
"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.7.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "2"
|
||||||
|
}
|
||||||
|
}
|
17
qa-core/scripts/jestSetup.js
Normal file
17
qa-core/scripts/jestSetup.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const env = require("../src/environment")
|
||||||
|
|
||||||
|
env._set("BUDIBASE_SERVER_URL", "http://localhost:4100")
|
||||||
|
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 tk = require("timekeeper")
|
||||||
|
tk.freeze(MOCK_DATE)
|
||||||
|
|
||||||
|
if (!process.env.DEBUG) {
|
||||||
|
global.console.log = jest.fn() // console.log are ignored in tests
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
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
|
||||||
|
appId?: string
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.host = `${env.BUDIBASE_SERVER_URL}/api/public/v1`
|
||||||
|
this.apiKey = env.BUDIBASE_PUBLIC_API_KEY
|
||||||
|
this.appId = appId
|
||||||
|
}
|
||||||
|
|
||||||
|
apiCall =
|
||||||
|
(method: APIMethod) =>
|
||||||
|
async (url = "", options: ApiOptions = {}) => {
|
||||||
|
const requestOptions = {
|
||||||
|
method,
|
||||||
|
body: JSON.stringify(options.body),
|
||||||
|
headers: {
|
||||||
|
"x-budibase-api-key": this.apiKey,
|
||||||
|
"x-budibase-app-id": this.appId,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const response = await fetch(`${this.host}${url}`, requestOptions)
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.error(response)
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
post = this.apiCall("POST")
|
||||||
|
get = this.apiCall("GET")
|
||||||
|
patch = this.apiCall("PATCH")
|
||||||
|
del = this.apiCall("DELETE")
|
||||||
|
put = this.apiCall("PUT")
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublicAPIClient
|
|
@ -0,0 +1,49 @@
|
||||||
|
import PublicAPIClient from "./PublicAPIClient"
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
SearchInputParams,
|
||||||
|
CreateApplicationParams,
|
||||||
|
} from "@budibase/server/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]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
const Chance = require("chance")
|
||||||
|
|
||||||
|
export default new Chance()
|
27
qa-core/src/config/public-api/TestConfiguration/index.ts
Normal file
27
qa-core/src/config/public-api/TestConfiguration/index.ts
Normal 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>{}
|
||||||
|
}
|
||||||
|
}
|
53
qa-core/src/config/public-api/TestConfiguration/rows.ts
Normal file
53
qa-core/src/config/public-api/TestConfiguration/rows.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import PublicAPIClient from "./PublicAPIClient"
|
||||||
|
import {
|
||||||
|
CreateRowParams,
|
||||||
|
Row,
|
||||||
|
SearchInputParams,
|
||||||
|
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
import { HeadersInit, Response } from "node-fetch"
|
||||||
|
import { generateRow } from "../fixtures/tables"
|
||||||
|
|
||||||
|
export default class RowApi {
|
||||||
|
api: PublicAPIClient
|
||||||
|
headers?: HeadersInit
|
||||||
|
tableId?: string
|
||||||
|
|
||||||
|
constructor(apiClient: PublicAPIClient) {
|
||||||
|
this.api = apiClient
|
||||||
|
}
|
||||||
|
|
||||||
|
async seed(tableId: string) {
|
||||||
|
return this.create(generateRow({ tableId }))
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(body: CreateRowParams): Promise<[Response, Row]> {
|
||||||
|
const response = await this.api.post(`/tables/${this.tableId}/rows`, {
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
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}`)
|
||||||
|
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 }
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
const json = await response.json()
|
||||||
|
return [response, json.data]
|
||||||
|
}
|
||||||
|
}
|
47
qa-core/src/config/public-api/TestConfiguration/tables.ts
Normal file
47
qa-core/src/config/public-api/TestConfiguration/tables.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import PublicAPIClient from "./PublicAPIClient"
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
SearchInputParams,
|
||||||
|
CreateTableParams,
|
||||||
|
} from "@budibase/server/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())
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(body: CreateTableParams): Promise<[Response, Table]> {
|
||||||
|
const response = await this.api.post(`/tables`, {
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
const json = await response.json()
|
||||||
|
return [response, json.data]
|
||||||
|
}
|
||||||
|
|
||||||
|
async read(id: string): Promise<[Response, Table]> {
|
||||||
|
const response = await this.api.get(`/tables/${id}`)
|
||||||
|
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 })
|
||||||
|
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 })
|
||||||
|
const json = await response.json()
|
||||||
|
return [response, json.data]
|
||||||
|
}
|
||||||
|
}
|
44
qa-core/src/config/public-api/TestConfiguration/users.ts
Normal file
44
qa-core/src/config/public-api/TestConfiguration/users.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import PublicAPIClient from "./PublicAPIClient"
|
||||||
|
import {
|
||||||
|
CreateUserParams,
|
||||||
|
SearchInputParams,
|
||||||
|
User,
|
||||||
|
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
import { Response } from "node-fetch"
|
||||||
|
import generateUser from "../fixtures/users"
|
||||||
|
|
||||||
|
export default class UserApi {
|
||||||
|
api: PublicAPIClient
|
||||||
|
|
||||||
|
constructor(apiClient: PublicAPIClient) {
|
||||||
|
this.api = apiClient
|
||||||
|
}
|
||||||
|
|
||||||
|
async seed() {
|
||||||
|
return this.create(generateUser())
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
15
qa-core/src/config/public-api/fixtures/applications.ts
Normal file
15
qa-core/src/config/public-api/fixtures/applications.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import generator from "../TestConfiguration/generator"
|
||||||
|
import {
|
||||||
|
Application,
|
||||||
|
CreateApplicationParams,
|
||||||
|
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
|
||||||
|
const generate = (
|
||||||
|
overrides: Partial<Application> = {}
|
||||||
|
): CreateApplicationParams => ({
|
||||||
|
name: generator.word(),
|
||||||
|
url: `/${generator.word()}`,
|
||||||
|
...overrides,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default generate
|
60
qa-core/src/config/public-api/fixtures/tables.ts
Normal file
60
qa-core/src/config/public-api/fixtures/tables.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import {
|
||||||
|
CreateRowParams,
|
||||||
|
CreateTableParams,
|
||||||
|
Row,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
import generator from "../TestConfiguration/generator"
|
||||||
|
|
||||||
|
export const generateTable = (
|
||||||
|
overrides: Partial<Table> = {}
|
||||||
|
): CreateTableParams => ({
|
||||||
|
name: generator.word(),
|
||||||
|
primaryDisplay: "testColumn",
|
||||||
|
schema: {
|
||||||
|
"Auto ID": {
|
||||||
|
autocolumn: true,
|
||||||
|
name: "Auto ID",
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
"Created At": {
|
||||||
|
autocolumn: true,
|
||||||
|
name: "Created At",
|
||||||
|
type: "datetime",
|
||||||
|
},
|
||||||
|
"Created By": {
|
||||||
|
autocolumn: true,
|
||||||
|
fieldName: generator.word(),
|
||||||
|
name: "Created By",
|
||||||
|
relationshipType: "many-to-many",
|
||||||
|
tableId: "ta_users",
|
||||||
|
type: "link",
|
||||||
|
},
|
||||||
|
testColumn: {
|
||||||
|
name: "testColumn",
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
"Updated At": {
|
||||||
|
autocolumn: true,
|
||||||
|
name: "Updated At",
|
||||||
|
type: "datetime",
|
||||||
|
},
|
||||||
|
"Updated By": {
|
||||||
|
autocolumn: true,
|
||||||
|
fieldName: generator.word(),
|
||||||
|
name: "Updated By",
|
||||||
|
relationshipType: "many-to-many",
|
||||||
|
tableId: "ta_users",
|
||||||
|
type: "link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...overrides,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const generateRow = (overrides: Partial<Row> = {}): CreateRowParams => ({
|
||||||
|
type: "row",
|
||||||
|
tableId: "seed_table",
|
||||||
|
testColumn: generator.string({ length: 32, alpha: true, numeric: true }),
|
||||||
|
relationship: [generator.string({ length: 32, alpha: true, numeric: true })],
|
||||||
|
...overrides,
|
||||||
|
})
|
25
qa-core/src/config/public-api/fixtures/users.ts
Normal file
25
qa-core/src/config/public-api/fixtures/users.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import {
|
||||||
|
CreateUserParams,
|
||||||
|
User,
|
||||||
|
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
import generator from "../TestConfiguration/generator"
|
||||||
|
|
||||||
|
const generate = (overrides: Partial<User> = {}): 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
|
10
qa-core/src/environment.ts
Normal file
10
qa-core/src/environment.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
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
|
23
qa-core/src/jest.extends.ts
Normal file
23
qa-core/src/jest.extends.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Response } from "node-fetch"
|
||||||
|
|
||||||
|
// boilerplate to allow TS updates to the global scope
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace jest {
|
||||||
|
interface Matchers<R> {
|
||||||
|
toHaveStatusCode(code: number): R
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect extensions
|
||||||
|
expect.extend({
|
||||||
|
toHaveStatusCode(received: Response, code: number) {
|
||||||
|
const pass = received.status === code
|
||||||
|
return {
|
||||||
|
message: () => `expected ${received.status} to match status code ${code}`,
|
||||||
|
pass,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,50 @@
|
||||||
|
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 "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
|
||||||
|
describe("Public API - /applications endpoints", () => {
|
||||||
|
const api = new PublicAPIClient()
|
||||||
|
const config = new TestConfiguration<Application>(api)
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.beforeAll()
|
||||||
|
const [response, app] = await config.applications.seed()
|
||||||
|
config.context = app
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await config.afterAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Create an application", async () => {
|
||||||
|
const [response, app] = await config.applications.create(generateApp())
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(app._id).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Search applications", async () => {
|
||||||
|
const [response, apps] = await config.applications.search({
|
||||||
|
name: config.context.name,
|
||||||
|
})
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(apps[0]).toEqual(config.context)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("GET - Retrieve an application", async () => {
|
||||||
|
const [response, app] = await config.applications.read(config.context._id)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(app).toEqual(config.context)
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
expect(app.updatedAt).not.toEqual(config.context.updatedAt)
|
||||||
|
expect(app.name).toEqual(config.context.name)
|
||||||
|
})
|
||||||
|
})
|
65
qa-core/src/tests/public-api/tables/rows.spec.ts
Normal file
65
qa-core/src/tests/public-api/tables/rows.spec.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { Row } from "@budibase/server/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 = new PublicAPIClient()
|
||||||
|
|
||||||
|
const config = new TestConfiguration<Row>(api)
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.beforeAll()
|
||||||
|
const [aResp, app] = await config.applications.seed()
|
||||||
|
|
||||||
|
config.tables.api.appId = app._id
|
||||||
|
config.rows.api.appId = app._id
|
||||||
|
|
||||||
|
const [tResp, table] = await config.tables.seed()
|
||||||
|
config.rows.tableId = table._id
|
||||||
|
|
||||||
|
const [rResp, row] = await config.rows.seed(table._id)
|
||||||
|
config.context = row
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await config.afterAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Create a row", async () => {
|
||||||
|
const [response, row] = await config.rows.create(generateRow())
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(row._id).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Search rows", async () => {
|
||||||
|
const [response, rows] = await config.rows.search({
|
||||||
|
query: {
|
||||||
|
string: {
|
||||||
|
testColumn: config.context.testColumn as string,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(rows[0]._id).toEqual(config.context._id)
|
||||||
|
expect(rows[0].tableId).toEqual(config.context.tableId)
|
||||||
|
expect(rows[0].testColumn).toEqual(config.context.testColumn)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("GET - Retrieve a row", async () => {
|
||||||
|
const [response, row] = await config.rows.read(config.context._id)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(row._id).toEqual(config.context._id)
|
||||||
|
expect(row.tableId).toEqual(config.context.tableId)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("PUT - update a row", async () => {
|
||||||
|
config.context.testColumn = "UpdatedName"
|
||||||
|
const [response, row] = await config.rows.update(
|
||||||
|
config.context._id,
|
||||||
|
config.context
|
||||||
|
)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(row.testColumn).toEqual(config.context.testColumn)
|
||||||
|
})
|
||||||
|
})
|
52
qa-core/src/tests/public-api/tables/tables.spec.ts
Normal file
52
qa-core/src/tests/public-api/tables/tables.spec.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { Table } from "@budibase/server/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 = new PublicAPIClient()
|
||||||
|
const config = new TestConfiguration<Table>(api)
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.beforeAll()
|
||||||
|
const [appResp, app] = await config.applications.seed()
|
||||||
|
config.tables.api.appId = app._id
|
||||||
|
|
||||||
|
const [tableResp, table] = await config.tables.seed()
|
||||||
|
config.context = table
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await config.afterAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Create a table", async () => {
|
||||||
|
const [response, table] = await config.tables.create(generateTable())
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(table._id).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Search tables", async () => {
|
||||||
|
const [response, tables] = await config.tables.search({
|
||||||
|
name: config.context.name,
|
||||||
|
})
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(tables[0]).toEqual(config.context)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("GET - Retrieve a table", async () => {
|
||||||
|
const [response, table] = await config.tables.read(config.context._id)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(table).toEqual(config.context)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("PUT - update a table", async () => {
|
||||||
|
config.context.name = "updatedName"
|
||||||
|
const [response, table] = await config.tables.update(
|
||||||
|
config.context._id,
|
||||||
|
config.context
|
||||||
|
)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(table).toEqual(config.context)
|
||||||
|
})
|
||||||
|
})
|
49
qa-core/src/tests/public-api/users/users.spec.ts
Normal file
49
qa-core/src/tests/public-api/users/users.spec.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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 "@budibase/server/api/controllers/public/mapping/types"
|
||||||
|
|
||||||
|
describe("Public API - /users endpoints", () => {
|
||||||
|
const api = new PublicAPIClient()
|
||||||
|
const config = new TestConfiguration<User>(api)
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.beforeAll()
|
||||||
|
const [_, user] = await config.users.seed()
|
||||||
|
config.context = user
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await config.afterAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Create a user", async () => {
|
||||||
|
const [response, user] = await config.users.create(generateUser())
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(user._id).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("POST - Search users", async () => {
|
||||||
|
const [response, users] = await config.users.search({
|
||||||
|
name: config.context.email,
|
||||||
|
})
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(users[0]).toEqual(config.context)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("GET - Retrieve a user", async () => {
|
||||||
|
const [response, user] = await config.users.read(config.context._id)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(user).toEqual(config.context)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("PUT - update a user", async () => {
|
||||||
|
config.context.firstName = "Updated First Name"
|
||||||
|
const [response, user] = await config.users.update(
|
||||||
|
config.context._id,
|
||||||
|
config.context
|
||||||
|
)
|
||||||
|
expect(response).toHaveStatusCode(200)
|
||||||
|
expect(user).toEqual(config.context)
|
||||||
|
})
|
||||||
|
})
|
34
qa-core/tsconfig.json
Normal file
34
qa-core/tsconfig.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["es2020"],
|
||||||
|
"allowJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"incremental": true,
|
||||||
|
"types": ["node", "jest"],
|
||||||
|
"outDir": "dist",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"paths": {
|
||||||
|
"@budibase/types": ["../packages/types/src"],
|
||||||
|
"@budibase/server/*": ["../packages/server/src/*"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"require": ["tsconfig-paths/register"]
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{ "path": "../packages/types" },
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src/**/*",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
2670
qa-core/yarn.lock
Normal file
2670
qa-core/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue