Merge pull request #14297 from Budibase/node-fetch-mockectomy

Remove global node-fetch mock.
This commit is contained in:
Sam Rose 2024-08-02 11:07:31 +01:00 committed by GitHub
commit d24d39afe1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 309 additions and 381 deletions

View File

@ -1,206 +0,0 @@
// @ts-ignore
import fs from "fs"
// eslint-disable-next-line @typescript-eslint/no-unused-vars
module FetchMock {
// @ts-ignore
const fetch = jest.requireActual("node-fetch")
let failCount = 0
let mockSearch = false
const func = async (url: any, opts: any) => {
const { host, pathname } = new URL(url)
function json(body: any, status = 200) {
return {
status,
headers: {
raw: () => {
return { "content-type": ["application/json"] }
},
get: (name: string) => {
if (name.toLowerCase() === "content-type") {
return ["application/json"]
}
},
},
json: async () => {
//x-www-form-encoded body is a URLSearchParams
//The call to stringify it leaves it blank
if (body?.opts?.body instanceof URLSearchParams) {
const paramArray = Array.from(body.opts.body.entries())
body.opts.body = paramArray.reduce((acc: any, pair: any) => {
acc[pair[0]] = pair[1]
return acc
}, {})
}
return body
},
}
}
if (pathname.includes("/api/global")) {
const user = {
email: "test@example.com",
_id: "us_test@example.com",
status: "active",
roles: {},
builder: {
global: false,
},
admin: {
global: false,
},
}
return pathname.endsWith("/users") && opts.method === "GET"
? json([user])
: json(user)
}
// mocked data based on url
else if (pathname.includes("api/apps")) {
return json({
app1: {
url: "/app1",
},
})
} else if (host.includes("example.com")) {
return json({
body: opts.body,
url,
method: opts.method,
})
} else if (host.includes("invalid.com")) {
return json(
{
invalid: true,
},
404
)
} else if (mockSearch && pathname.includes("_search")) {
const body = opts.body
const parts = body.split("tableId:")
let tableId
if (parts && parts[1]) {
tableId = parts[1].split('"')[0]
}
return json({
rows: [
{
doc: {
_id: "test",
tableId: tableId,
query: opts.body,
},
},
],
bookmark: "test",
})
} else if (host.includes("google.com")) {
return json({
url,
opts,
value:
'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-GB"></html>',
})
} else if (
url === "https://api.github.com/repos/my-repo/budibase-comment-box"
) {
return Promise.resolve({
json: () => {
return {
name: "budibase-comment-box",
releases_url:
"https://api.github.com/repos/my-repo/budibase-comment-box{/id}",
}
},
})
} else if (
url === "https://api.github.com/repos/my-repo/budibase-comment-box/latest"
) {
return Promise.resolve({
json: () => {
return {
assets: [
{
content_type: "application/gzip",
browser_download_url:
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz",
},
],
}
},
})
} else if (
url ===
"https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz"
) {
return Promise.resolve({
body: fs.createReadStream(
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
),
ok: true,
})
} else if (url === "https://www.npmjs.com/package/budibase-component") {
return Promise.resolve({
status: 200,
json: () => {
return {
name: "budibase-component",
"dist-tags": {
latest: "1.0.0",
},
versions: {
"1.0.0": {
dist: {
tarball:
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz",
},
},
},
}
},
})
} else if (
url ===
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.2.tgz"
) {
return Promise.resolve({
body: fs.createReadStream(
"src/api/routes/tests/data/budibase-component-1.0.2.tgz"
),
ok: true,
})
} else if (
url === "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz"
) {
return Promise.resolve({
body: fs.createReadStream(
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
),
ok: true,
})
} else if (url === "https://www.googleapis.com/oauth2/v4/token") {
// any valid response
return json({})
} else if (host.includes("failonce.com")) {
failCount++
if (failCount === 1) {
return json({ message: "error" }, 500)
} else {
return json({
fails: failCount - 1,
url,
opts,
})
}
}
return fetch(url, opts)
}
func.Headers = fetch.Headers
func.mockSearch = () => {
mockSearch = true
}
module.exports = func
}

View File

@ -1,6 +1,13 @@
import { npmUpload, urlUpload, githubUpload } from "./uploaders" import { npmUpload, urlUpload, githubUpload } from "./uploaders"
import { plugins as pluginCore } from "@budibase/backend-core" import { plugins as pluginCore } from "@budibase/backend-core"
import { PluginType, FileType, PluginSource } from "@budibase/types" import {
PluginType,
FileType,
PluginSource,
Ctx,
CreatePluginRequest,
CreatePluginResponse,
} from "@budibase/types"
import env from "../../../environment" import env from "../../../environment"
import { clientAppSocket } from "../../../websockets" import { clientAppSocket } from "../../../websockets"
import sdk from "../../../sdk" import sdk from "../../../sdk"
@ -29,7 +36,9 @@ export async function upload(ctx: any) {
} }
} }
export async function create(ctx: any) { export async function create(
ctx: Ctx<CreatePluginRequest, CreatePluginResponse>
) {
const { source, url, headers, githubToken } = ctx.request.body const { source, url, headers, githubToken } = ctx.request.body
try { try {
@ -75,14 +84,9 @@ export async function create(ctx: any) {
const doc = await pro.plugins.storePlugin(metadata, directory, source) const doc = await pro.plugins.storePlugin(metadata, directory, source)
clientAppSocket?.emit("plugins-update", { name, hash: doc.hash }) clientAppSocket?.emit("plugins-update", { name, hash: doc.hash })
ctx.body = {
message: "Plugin uploaded successfully",
plugins: [doc],
}
ctx.body = { plugin: doc } ctx.body = { plugin: doc }
} catch (err: any) { } catch (err: any) {
const errMsg = err?.message ? err?.message : err const errMsg = err?.message ? err?.message : err
ctx.throw(400, `Failed to import plugin: ${errMsg}`) ctx.throw(400, `Failed to import plugin: ${errMsg}`)
} }
} }

View File

@ -20,6 +20,7 @@ import { type App } from "@budibase/types"
import tk from "timekeeper" import tk from "timekeeper"
import * as uuid from "uuid" import * as uuid from "uuid"
import { structures } from "@budibase/backend-core/tests" import { structures } from "@budibase/backend-core/tests"
import nock from "nock"
describe("/applications", () => { describe("/applications", () => {
let config = setup.getConfig() let config = setup.getConfig()
@ -35,6 +36,7 @@ describe("/applications", () => {
throw new Error("Failed to publish app") throw new Error("Failed to publish app")
} }
jest.clearAllMocks() jest.clearAllMocks()
nock.cleanAll()
}) })
// These need to go first for the app totals to make sense // These need to go first for the app totals to make sense
@ -324,18 +326,33 @@ describe("/applications", () => {
describe("delete", () => { describe("delete", () => {
it("should delete published app and dev apps with dev app ID", async () => { it("should delete published app and dev apps with dev app ID", async () => {
const prodAppId = app.appId.replace("_dev", "")
nock("http://localhost:10000")
.delete(`/api/global/roles/${prodAppId}`)
.reply(200, {})
await config.api.application.delete(app.appId) await config.api.application.delete(app.appId)
expect(events.app.deleted).toHaveBeenCalledTimes(1) expect(events.app.deleted).toHaveBeenCalledTimes(1)
expect(events.app.unpublished).toHaveBeenCalledTimes(1) expect(events.app.unpublished).toHaveBeenCalledTimes(1)
}) })
it("should delete published app and dev app with prod app ID", async () => { it("should delete published app and dev app with prod app ID", async () => {
await config.api.application.delete(app.appId.replace("_dev", "")) const prodAppId = app.appId.replace("_dev", "")
nock("http://localhost:10000")
.delete(`/api/global/roles/${prodAppId}`)
.reply(200, {})
await config.api.application.delete(prodAppId)
expect(events.app.deleted).toHaveBeenCalledTimes(1) expect(events.app.deleted).toHaveBeenCalledTimes(1)
expect(events.app.unpublished).toHaveBeenCalledTimes(1) expect(events.app.unpublished).toHaveBeenCalledTimes(1)
}) })
it("should be able to delete an app after SQS_SEARCH_ENABLE has been set but app hasn't been migrated", async () => { it("should be able to delete an app after SQS_SEARCH_ENABLE has been set but app hasn't been migrated", async () => {
const prodAppId = app.appId.replace("_dev", "")
nock("http://localhost:10000")
.delete(`/api/global/roles/${prodAppId}`)
.reply(200, {})
await config.withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, async () => { await config.withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, async () => {
await config.api.application.delete(app.appId) await config.api.application.delete(app.appId)
}) })

View File

@ -19,6 +19,7 @@ import {
} from "@budibase/types" } from "@budibase/types"
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
import { tableForDatasource } from "../../../tests/utilities/structures" import { tableForDatasource } from "../../../tests/utilities/structures"
import nock from "nock"
describe("/datasources", () => { describe("/datasources", () => {
const config = setup.getConfig() const config = setup.getConfig()
@ -37,6 +38,7 @@ describe("/datasources", () => {
config: {}, config: {},
}) })
jest.clearAllMocks() jest.clearAllMocks()
nock.cleanAll()
}) })
describe("create", () => { describe("create", () => {
@ -71,6 +73,12 @@ describe("/datasources", () => {
describe("dynamic variables", () => { describe("dynamic variables", () => {
it("should invalidate changed or removed variables", async () => { it("should invalidate changed or removed variables", async () => {
nock("http://www.example.com/")
.get("/")
.reply(200, [{ value: "test" }])
.get("/?test=test")
.reply(200, [{ value: 1 }])
let datasource = await config.api.datasource.create({ let datasource = await config.api.datasource.create({
type: "datasource", type: "datasource",
name: "Rest", name: "Rest",
@ -81,7 +89,7 @@ describe("/datasources", () => {
const query = await config.api.query.save({ const query = await config.api.query.save({
datasourceId: datasource._id!, datasourceId: datasource._id!,
fields: { fields: {
path: "www.google.com", path: "www.example.com",
}, },
parameters: [], parameters: [],
transformer: null, transformer: null,

View File

@ -15,6 +15,8 @@ jest.mock("@budibase/backend-core", () => {
import { events, objectStore } from "@budibase/backend-core" import { events, objectStore } from "@budibase/backend-core"
import * as setup from "./utilities" import * as setup from "./utilities"
import nock from "nock"
import { PluginSource } from "@budibase/types"
const mockUploadDirectory = objectStore.uploadDirectory as jest.Mock const mockUploadDirectory = objectStore.uploadDirectory as jest.Mock
const mockDeleteFolder = objectStore.deleteFolder as jest.Mock const mockDeleteFolder = objectStore.deleteFolder as jest.Mock
@ -28,6 +30,7 @@ describe("/plugins", () => {
beforeEach(async () => { beforeEach(async () => {
await config.init() await config.init()
jest.clearAllMocks() jest.clearAllMocks()
nock.cleanAll()
}) })
const createPlugin = async (status?: number) => { const createPlugin = async (status?: number) => {
@ -112,67 +115,108 @@ describe("/plugins", () => {
}) })
describe("github", () => { describe("github", () => {
const createGithubPlugin = async (status?: number, url?: string) => { beforeEach(async () => {
return await request nock("https://api.github.com")
.post(`/api/plugin`) .get("/repos/my-repo/budibase-comment-box")
.send({ .reply(200, {
source: "Github", name: "budibase-comment-box",
url, releases_url:
githubToken: "token", "https://api.github.com/repos/my-repo/budibase-comment-box{/id}",
}) })
.set(config.defaultHeaders()) .get("/repos/my-repo/budibase-comment-box/latest")
.expect("Content-Type", /json/) .reply(200, {
.expect(status ? status : 200) assets: [
} {
it("should be able to create a plugin from github", async () => { content_type: "application/gzip",
const res = await createGithubPlugin( browser_download_url:
200, "https://github.com/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz",
"https://github.com/my-repo/budibase-comment-box.git" },
) ],
expect(res.body).toBeDefined() })
expect(res.body.plugin).toBeDefined()
expect(res.body.plugin._id).toEqual("plg_comment-box") nock("https://github.com")
.get(
"/my-repo/budibase-comment-box/releases/download/v1.0.2/comment-box-1.0.2.tar.gz"
)
.replyWithFile(
200,
"src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
)
}) })
it("should be able to create a plugin from github", async () => {
const { plugin } = await config.api.plugin.create({
source: PluginSource.GITHUB,
url: "https://github.com/my-repo/budibase-comment-box.git",
githubToken: "token",
})
expect(plugin._id).toEqual("plg_comment-box")
})
it("should fail if the url is not from github", async () => { it("should fail if the url is not from github", async () => {
const res = await createGithubPlugin( await config.api.plugin.create(
400, {
"https://notgithub.com/my-repo/budibase-comment-box" source: PluginSource.GITHUB,
) url: "https://notgithub.com/my-repo/budibase-comment-box",
expect(res.body.message).toEqual( githubToken: "token",
"Failed to import plugin: The plugin origin must be from Github" },
{
status: 400,
body: {
message:
"Failed to import plugin: The plugin origin must be from Github",
},
}
) )
}) })
}) })
describe("npm", () => { describe("npm", () => {
it("should be able to create a plugin from npm", async () => { it("should be able to create a plugin from npm", async () => {
const res = await request nock("https://registry.npmjs.org")
.post(`/api/plugin`) .get("/budibase-component")
.send({ .reply(200, {
source: "NPM", name: "budibase-component",
url: "https://www.npmjs.com/package/budibase-component", "dist-tags": {
latest: "1.0.0",
},
versions: {
"1.0.0": {
dist: {
tarball:
"https://registry.npmjs.org/budibase-component/-/budibase-component-1.0.1.tgz",
},
},
},
}) })
.set(config.defaultHeaders()) .get("/budibase-component/-/budibase-component-1.0.1.tgz")
.expect("Content-Type", /json/) .replyWithFile(
.expect(200) 200,
expect(res.body).toBeDefined() "src/api/routes/tests/data/budibase-component-1.0.1.tgz"
expect(res.body.plugin._id).toEqual("plg_budibase-component") )
const { plugin } = await config.api.plugin.create({
source: PluginSource.NPM,
url: "https://www.npmjs.com/package/budibase-component",
})
expect(plugin._id).toEqual("plg_budibase-component")
expect(events.plugin.imported).toHaveBeenCalled() expect(events.plugin.imported).toHaveBeenCalled()
}) })
}) })
describe("url", () => { describe("url", () => {
it("should be able to create a plugin from a URL", async () => { it("should be able to create a plugin from a URL", async () => {
const res = await request nock("https://www.someurl.com")
.post(`/api/plugin`) .get("/comment-box/comment-box-1.0.2.tar.gz")
.send({ .replyWithFile(
source: "URL", 200,
url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz", "src/api/routes/tests/data/comment-box-1.0.2.tar.gz"
}) )
.set(config.defaultHeaders())
.expect("Content-Type", /json/) const { plugin } = await config.api.plugin.create({
.expect(200) source: PluginSource.URL,
expect(res.body).toBeDefined() url: "https://www.someurl.com/comment-box/comment-box-1.0.2.tar.gz",
expect(res.body.plugin._id).toEqual("plg_comment-box") })
expect(plugin._id).toEqual("plg_comment-box")
expect(events.plugin.imported).toHaveBeenCalledTimes(1) expect(events.plugin.imported).toHaveBeenCalledTimes(1)
}) })
}) })

View File

@ -1,26 +0,0 @@
const setup = require("./utilities")
const fetch = require("node-fetch")
jest.mock("node-fetch")
describe("test the outgoing webhook action", () => {
let inputs
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
inputs = {
username: "joe_bloggs",
url: "http://www.example.com",
}
})
afterAll(setup.afterAll)
it("should be able to run the action", async () => {
const res = await setup.runStep(setup.actions.discord.stepId, inputs)
expect(res.response.url).toEqual("http://www.example.com")
expect(res.response.method).toEqual("post")
expect(res.success).toEqual(true)
})
})

View File

@ -0,0 +1,26 @@
import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities"
import nock from "nock"
describe("test the outgoing webhook action", () => {
let config = getConfig()
beforeAll(async () => {
await config.init()
})
afterAll(_afterAll)
beforeEach(() => {
nock.cleanAll()
})
it("should be able to run the action", async () => {
nock("http://www.example.com/").post("/").reply(200, { foo: "bar" })
const res = await runStep(actions.discord.stepId, {
url: "http://www.example.com",
username: "joe_bloggs",
})
expect(res.response.foo).toEqual("bar")
expect(res.success).toEqual(true)
})
})

View File

@ -1,4 +1,5 @@
import { getConfig, afterAll, runStep, actions } from "./utilities" import { getConfig, afterAll, runStep, actions } from "./utilities"
import nock from "nock"
describe("test the outgoing webhook action", () => { describe("test the outgoing webhook action", () => {
let config = getConfig() let config = getConfig()
@ -9,42 +10,45 @@ describe("test the outgoing webhook action", () => {
afterAll() afterAll()
beforeEach(() => {
nock.cleanAll()
})
it("should be able to run the action", async () => { it("should be able to run the action", async () => {
nock("http://www.example.com/").post("/").reply(200, { foo: "bar" })
const res = await runStep(actions.integromat.stepId, { const res = await runStep(actions.integromat.stepId, {
value1: "test",
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.response.url).toEqual("http://www.example.com") expect(res.response.foo).toEqual("bar")
expect(res.response.method).toEqual("post")
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
it("should add the payload props when a JSON string is provided", async () => { it("should add the payload props when a JSON string is provided", async () => {
const payload = `{"value1":1,"value2":2,"value3":3,"value4":4,"value5":5,"name":"Adam","age":9}` const payload = {
value1: 1,
value2: 2,
value3: 3,
value4: 4,
value5: 5,
name: "Adam",
age: 9,
}
nock("http://www.example.com/")
.post("/", payload)
.reply(200, { foo: "bar" })
const res = await runStep(actions.integromat.stepId, { const res = await runStep(actions.integromat.stepId, {
value1: "ONE", body: { value: JSON.stringify(payload) },
value2: "TWO",
value3: "THREE",
value4: "FOUR",
value5: "FIVE",
body: {
value: payload,
},
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.response.url).toEqual("http://www.example.com") expect(res.response.foo).toEqual("bar")
expect(res.response.method).toEqual("post")
expect(res.response.body).toEqual(payload)
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
it("should return a 400 if the JSON payload string is malformed", async () => { it("should return a 400 if the JSON payload string is malformed", async () => {
const payload = `{ value1 1 }`
const res = await runStep(actions.integromat.stepId, { const res = await runStep(actions.integromat.stepId, {
value1: "ONE", body: { value: "{ invalid json }" },
body: {
value: payload,
},
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.httpStatus).toEqual(400) expect(res.httpStatus).toEqual(400)

View File

@ -1,4 +1,5 @@
import { getConfig, afterAll, runStep, actions } from "./utilities" import { getConfig, afterAll, runStep, actions } from "./utilities"
import nock from "nock"
describe("test the outgoing webhook action", () => { describe("test the outgoing webhook action", () => {
let config = getConfig() let config = getConfig()
@ -9,31 +10,33 @@ describe("test the outgoing webhook action", () => {
afterAll() afterAll()
beforeEach(() => {
nock.cleanAll()
})
it("should be able to run the action and default to 'get'", async () => { it("should be able to run the action and default to 'get'", async () => {
nock("http://www.example.com/").get("/").reply(200, { foo: "bar" })
const res = await runStep(actions.n8n.stepId, { const res = await runStep(actions.n8n.stepId, {
url: "http://www.example.com", url: "http://www.example.com",
body: { body: {
test: "IGNORE_ME", test: "IGNORE_ME",
}, },
}) })
expect(res.response.url).toEqual("http://www.example.com") expect(res.response.foo).toEqual("bar")
expect(res.response.method).toEqual("GET")
expect(res.response.body).toBeUndefined()
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
it("should add the payload props when a JSON string is provided", async () => { it("should add the payload props when a JSON string is provided", async () => {
const payload = `{ "name": "Adam", "age": 9 }` nock("http://www.example.com/")
.post("/", { name: "Adam", age: 9 })
.reply(200)
const res = await runStep(actions.n8n.stepId, { const res = await runStep(actions.n8n.stepId, {
body: { body: {
value: payload, value: JSON.stringify({ name: "Adam", age: 9 }),
}, },
method: "POST", method: "POST",
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.response.url).toEqual("http://www.example.com")
expect(res.response.method).toEqual("POST")
expect(res.response.body).toEqual(`{"name":"Adam","age":9}`)
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
@ -53,6 +56,9 @@ describe("test the outgoing webhook action", () => {
}) })
it("should not append the body if the method is HEAD", async () => { it("should not append the body if the method is HEAD", async () => {
nock("http://www.example.com/")
.head("/", body => body === "")
.reply(200)
const res = await runStep(actions.n8n.stepId, { const res = await runStep(actions.n8n.stepId, {
url: "http://www.example.com", url: "http://www.example.com",
method: "HEAD", method: "HEAD",
@ -60,9 +66,6 @@ describe("test the outgoing webhook action", () => {
test: "IGNORE_ME", test: "IGNORE_ME",
}, },
}) })
expect(res.response.url).toEqual("http://www.example.com")
expect(res.response.method).toEqual("HEAD")
expect(res.response.body).toBeUndefined()
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
}) })

View File

@ -1,41 +0,0 @@
const setup = require("./utilities")
const fetch = require("node-fetch")
jest.mock("node-fetch")
describe("test the outgoing webhook action", () => {
let inputs
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
inputs = {
requestMethod: "POST",
url: "www.example.com",
requestBody: JSON.stringify({
a: 1,
}),
}
})
afterAll(setup.afterAll)
it("should be able to run the action", async () => {
const res = await setup.runStep(
setup.actions.OUTGOING_WEBHOOK.stepId,
inputs
)
expect(res.success).toEqual(true)
expect(res.response.url).toEqual("http://www.example.com")
expect(res.response.method).toEqual("POST")
expect(JSON.parse(res.response.body).a).toEqual(1)
})
it("should return an error if something goes wrong in fetch", async () => {
const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, {
requestMethod: "GET",
url: "www.invalid.com",
})
expect(res.success).toEqual(false)
})
})

View File

@ -0,0 +1,37 @@
import { getConfig, afterAll as _afterAll, runStep, actions } from "./utilities"
import nock from "nock"
describe("test the outgoing webhook action", () => {
const config = getConfig()
beforeAll(async () => {
await config.init()
})
afterAll(_afterAll)
beforeEach(() => {
nock.cleanAll()
})
it("should be able to run the action", async () => {
nock("http://www.example.com")
.post("/", { a: 1 })
.reply(200, { foo: "bar" })
const res = await runStep(actions.OUTGOING_WEBHOOK.stepId, {
requestMethod: "POST",
url: "www.example.com",
requestBody: JSON.stringify({ a: 1 }),
})
expect(res.success).toEqual(true)
expect(res.response.foo).toEqual("bar")
})
it("should return an error if something goes wrong in fetch", async () => {
const res = await runStep(actions.OUTGOING_WEBHOOK.stepId, {
requestMethod: "GET",
url: "www.invalid.com",
})
expect(res.success).toEqual(false)
})
})

View File

@ -1,4 +1,5 @@
import { getConfig, afterAll, runStep, actions } from "./utilities" import { getConfig, afterAll, runStep, actions } from "./utilities"
import nock from "nock"
describe("test the outgoing webhook action", () => { describe("test the outgoing webhook action", () => {
let config = getConfig() let config = getConfig()
@ -9,44 +10,45 @@ describe("test the outgoing webhook action", () => {
afterAll() afterAll()
beforeEach(() => {
nock.cleanAll()
})
it("should be able to run the action", async () => { it("should be able to run the action", async () => {
nock("http://www.example.com/").post("/").reply(200, { foo: "bar" })
const res = await runStep(actions.zapier.stepId, { const res = await runStep(actions.zapier.stepId, {
value1: "test",
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.response.url).toEqual("http://www.example.com") expect(res.response.foo).toEqual("bar")
expect(res.response.method).toEqual("post")
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
it("should add the payload props when a JSON string is provided", async () => { it("should add the payload props when a JSON string is provided", async () => {
const payload = `{ "value1": 1, "value2": 2, "value3": 3, "value4": 4, "value5": 5, "name": "Adam", "age": 9 }` const payload = {
value1: 1,
value2: 2,
value3: 3,
value4: 4,
value5: 5,
name: "Adam",
age: 9,
}
nock("http://www.example.com/")
.post("/", { ...payload, platform: "budibase" })
.reply(200, { foo: "bar" })
const res = await runStep(actions.zapier.stepId, { const res = await runStep(actions.zapier.stepId, {
value1: "ONE", body: { value: JSON.stringify(payload) },
value2: "TWO",
value3: "THREE",
value4: "FOUR",
value5: "FIVE",
body: {
value: payload,
},
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.response.url).toEqual("http://www.example.com") expect(res.response.foo).toEqual("bar")
expect(res.response.method).toEqual("post")
expect(res.response.body).toEqual(
`{"platform":"budibase","value1":1,"value2":2,"value3":3,"value4":4,"value5":5,"name":"Adam","age":9}`
)
expect(res.success).toEqual(true) expect(res.success).toEqual(true)
}) })
it("should return a 400 if the JSON payload string is malformed", async () => { it("should return a 400 if the JSON payload string is malformed", async () => {
const payload = `{ value1 1 }`
const res = await runStep(actions.zapier.stepId, { const res = await runStep(actions.zapier.stepId, {
value1: "ONE", body: { value: "{ invalid json }" },
body: {
value: payload,
},
url: "http://www.example.com", url: "http://www.example.com",
}) })
expect(res.httpStatus).toEqual(400) expect(res.httpStatus).toEqual(400)

View File

@ -1,4 +1,5 @@
import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet" import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet"
import nock from "nock"
jest.mock("google-auth-library") jest.mock("google-auth-library")
const { OAuth2Client } = require("google-auth-library") const { OAuth2Client } = require("google-auth-library")
@ -62,6 +63,13 @@ describe("Google Sheets Integration", () => {
await config.init() await config.init()
jest.clearAllMocks() jest.clearAllMocks()
nock.cleanAll()
nock("https://www.googleapis.com/").post("/oauth2/v4/token").reply(200, {
grant_type: "client_credentials",
client_id: "your-client-id",
client_secret: "your-client-secret",
})
}) })
function createBasicTable(name: string, columns: string[]): Table { function createBasicTable(name: string, columns: string[]): Table {

View File

@ -1,6 +1,7 @@
import TestConfiguration from "../../tests/utilities/TestConfiguration" import TestConfiguration from "../../tests/utilities/TestConfiguration"
import { startup } from "../index" import { startup } from "../index"
import { users, utils, tenancy } from "@budibase/backend-core" import { users, utils, tenancy } from "@budibase/backend-core"
import nock from "nock"
describe("check BB_ADMIN environment variables", () => { describe("check BB_ADMIN environment variables", () => {
const config = new TestConfiguration() const config = new TestConfiguration()
@ -8,7 +9,17 @@ describe("check BB_ADMIN environment variables", () => {
await config.init() await config.init()
}) })
beforeEach(() => {
nock.cleanAll()
})
it("should be able to create a user with the BB_ADMIN environment variables", async () => { it("should be able to create a user with the BB_ADMIN environment variables", async () => {
nock("http://localhost:10000")
.get("/api/global/configs/checklist")
.reply(200, {})
.get("/api/global/self/api_key")
.reply(200, {})
const EMAIL = "budibase@budibase.com", const EMAIL = "budibase@budibase.com",
PASSWORD = "budibase" PASSWORD = "budibase"
await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => {

View File

@ -1,6 +1,7 @@
import env from "../environment" import env from "../environment"
import { env as coreEnv, timers } from "@budibase/backend-core" import { env as coreEnv, timers } from "@budibase/backend-core"
import { testContainerUtils } from "@budibase/backend-core/tests" import { testContainerUtils } from "@budibase/backend-core/tests"
import nock from "nock"
if (!process.env.CI) { if (!process.env.CI) {
// set a longer timeout in dev for debugging 100 seconds // set a longer timeout in dev for debugging 100 seconds
@ -9,6 +10,15 @@ if (!process.env.CI) {
jest.setTimeout(30 * 1000) jest.setTimeout(30 * 1000)
} }
nock.disableNetConnect()
nock.enableNetConnect(host => {
return (
host.includes("localhost") ||
host.includes("127.0.0.1") ||
host.includes("::1")
)
})
testContainerUtils.setupEnv(env, coreEnv) testContainerUtils.setupEnv(env, coreEnv)
afterAll(() => { afterAll(() => {

View File

@ -15,6 +15,7 @@ import { RoleAPI } from "./role"
import { TemplateAPI } from "./template" import { TemplateAPI } from "./template"
import { RowActionAPI } from "./rowAction" import { RowActionAPI } from "./rowAction"
import { AutomationAPI } from "./automation" import { AutomationAPI } from "./automation"
import { PluginAPI } from "./plugin"
export default class API { export default class API {
table: TableAPI table: TableAPI
@ -33,6 +34,7 @@ export default class API {
templates: TemplateAPI templates: TemplateAPI
rowAction: RowActionAPI rowAction: RowActionAPI
automation: AutomationAPI automation: AutomationAPI
plugin: PluginAPI
constructor(config: TestConfiguration) { constructor(config: TestConfiguration) {
this.table = new TableAPI(config) this.table = new TableAPI(config)
@ -51,5 +53,6 @@ export default class API {
this.templates = new TemplateAPI(config) this.templates = new TemplateAPI(config)
this.rowAction = new RowActionAPI(config) this.rowAction = new RowActionAPI(config)
this.automation = new AutomationAPI(config) this.automation = new AutomationAPI(config)
this.plugin = new PluginAPI(config)
} }
} }

View File

@ -0,0 +1,11 @@
import { Expectations, TestAPI } from "./base"
import { CreatePluginRequest, CreatePluginResponse } from "@budibase/types"
export class PluginAPI extends TestAPI {
create = async (body: CreatePluginRequest, expectations?: Expectations) => {
return await this._post<CreatePluginResponse>(`/api/plugin`, {
body,
expectations,
})
}
}

View File

@ -15,3 +15,4 @@ export * from "./automation"
export * from "./layout" export * from "./layout"
export * from "./query" export * from "./query"
export * from "./role" export * from "./role"
export * from "./plugins"

View File

@ -0,0 +1,12 @@
import { PluginSource } from "../../documents"
export interface CreatePluginRequest {
source: PluginSource
url: string
githubToken?: string
headers?: { [key: string]: string }
}
export interface CreatePluginResponse {
plugin: any
}