From e7f11d6687dc78c933ebddeda1bc91f0ba86e108 Mon Sep 17 00:00:00 2001 From: Alex <54153428+alexpietsch@users.noreply.github.com> Date: Fri, 3 May 2024 21:18:20 +0200 Subject: [PATCH] enable jasmine test runs --- package-lock.json | 13 +++ package.json | 10 +- spec/etapi/app_info.js | 12 -- spec/etapi/app_info.ts | 8 ++ spec/etapi/backup.js | 12 -- spec/etapi/backup.ts | 8 ++ spec/etapi/import.js | 24 ---- spec/etapi/import.ts | 27 +++++ spec/etapi/notes.js | 109 ------------------- spec/etapi/notes.ts | 107 ++++++++++++++++++ spec/support/etapi.js | 184 ------------------------------- spec/support/etapi.ts | 224 ++++++++++++++++++++++++++++++++++++++ spec/support/jasmine.json | 9 +- tsconfig.json | 41 +++---- 14 files changed, 413 insertions(+), 375 deletions(-) delete mode 100644 spec/etapi/app_info.js create mode 100644 spec/etapi/app_info.ts delete mode 100644 spec/etapi/backup.js create mode 100644 spec/etapi/backup.ts delete mode 100644 spec/etapi/import.js create mode 100644 spec/etapi/import.ts delete mode 100644 spec/etapi/notes.js create mode 100644 spec/etapi/notes.ts delete mode 100644 spec/support/etapi.js create mode 100644 spec/support/etapi.ts diff --git a/package-lock.json b/package-lock.json index 2dd4a639d..32a3f050d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,6 +100,7 @@ "@types/express-session": "^1.18.0", "@types/html": "^1.0.4", "@types/ini": "^4.1.0", + "@types/jasmine": "^5.1.4", "@types/jsdom": "^21.1.6", "@types/mime-types": "^2.1.4", "@types/multer": "^1.4.11", @@ -1408,6 +1409,12 @@ "integrity": "sha512-mTehMtc+xtnWBBvqizcqYCktKDBH2WChvx1GU3Sfe4PysFDXiNe+1YwtpVX1MDtCa4NQrSPw2+3HmvXHY3gt1w==", "dev": true }, + "node_modules/@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true + }, "node_modules/@types/jsdom": { "version": "21.1.6", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.6.tgz", @@ -14458,6 +14465,12 @@ "integrity": "sha512-mTehMtc+xtnWBBvqizcqYCktKDBH2WChvx1GU3Sfe4PysFDXiNe+1YwtpVX1MDtCa4NQrSPw2+3HmvXHY3gt1w==", "dev": true }, + "@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true + }, "@types/jsdom": { "version": "21.1.6", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.6.tgz", diff --git a/package.json b/package.json index cbfcd7e32..3d3d94a8e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "start-electron": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron --inspect=5858 .", "start-electron-no-dir": "cross-env TRILIUM_SAFE_MODE=1 TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 electron --inspect=5858 .", "qstart-electron": "npm run qswitch-electron && TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron --inspect=5858 .", - "start-test-server": "npm run qswitch-server; rm -rf ./data-test; cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 node src/www.js", + "start-test-server": "npm run qswitch-server; rm -rf ./data-test; cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 ts-node src/www.ts", + "rebuild": "electron-rebuild", "switch-server": "rm -rf ./node_modules/better-sqlite3 && npm install", "switch-electron": "./node_modules/.bin/electron-rebuild", "qswitch-server": "rm -rf ./node_modules/better-sqlite3/bin ; mkdir -p ./node_modules/better-sqlite3/build ; cp ./bin/better-sqlite3/linux-server-better_sqlite3.node ./node_modules/better-sqlite3/build/better_sqlite3.node", @@ -28,10 +29,10 @@ "build-frontend-docs": "rm -rf ./docs/frontend_api && ./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", "build-docs": "npm run build-backend-docs && npm run build-frontend-docs", "webpack": "webpack -c webpack.config.js", - "test-jasmine": "TRILIUM_DATA_DIR=~/trilium/data-test jasmine", + "test-jasmine": "TRILIUM_DATA_DIR=~/trilium/data-test ts-node ./node_modules/.bin/jasmine", "test-es6": "node -r esm spec-es6/attribute_parser.spec.js ", "test": "npm run test-jasmine && npm run test-es6", - "postinstall": "rimraf ./node_modules/canvas" + "postinstall": "rimraf ./node_modules/canvas && npm run rebuild" }, "dependencies": { "@braintree/sanitize-url": "6.0.4", @@ -121,6 +122,7 @@ "@types/express-session": "^1.18.0", "@types/html": "^1.0.4", "@types/ini": "^4.1.0", + "@types/jasmine": "^5.1.4", "@types/jsdom": "^21.1.6", "@types/mime-types": "^2.1.4", "@types/multer": "^1.4.11", @@ -155,4 +157,4 @@ "optionalDependencies": { "electron-installer-debian": "3.2.0" } -} \ No newline at end of file +} diff --git a/spec/etapi/app_info.js b/spec/etapi/app_info.js deleted file mode 100644 index fa82fd4dd..000000000 --- a/spec/etapi/app_info.js +++ /dev/null @@ -1,12 +0,0 @@ -const { - describeEtapi, postEtapi, - putEtapiContent -} = require('../support/etapi.js'); -const {getEtapi} = require("../support/etapi.js"); - -describeEtapi("app_info", () => { - it("get", async () => { - const appInfo = await getEtapi("app-info"); - expect(appInfo.clipperProtocolVersion).toEqual("1.0"); - }); -}); diff --git a/spec/etapi/app_info.ts b/spec/etapi/app_info.ts new file mode 100644 index 000000000..9ea99b143 --- /dev/null +++ b/spec/etapi/app_info.ts @@ -0,0 +1,8 @@ +import etapi = require("../support/etapi"); + +etapi.describeEtapi("app_info", () => { + it("get", async () => { + const appInfo = await etapi.getEtapi("app-info"); + expect(appInfo.clipperProtocolVersion).toEqual("1.0"); + }); +}); diff --git a/spec/etapi/backup.js b/spec/etapi/backup.js deleted file mode 100644 index 085d0b63e..000000000 --- a/spec/etapi/backup.js +++ /dev/null @@ -1,12 +0,0 @@ -const { - describeEtapi, postEtapi, - getEtapi, -} = require('../support/etapi.js'); -const {putEtapiContent} = require("../support/etapi.js"); - -describeEtapi("backup", () => { - it("create", async () => { - const response = await putEtapiContent("backup/etapi_test"); - expect(response.status).toEqual(204); - }); -}); diff --git a/spec/etapi/backup.ts b/spec/etapi/backup.ts new file mode 100644 index 000000000..3afda9141 --- /dev/null +++ b/spec/etapi/backup.ts @@ -0,0 +1,8 @@ +import etapi = require("../support/etapi"); + +etapi.describeEtapi("backup", () => { + it("create", async () => { + const response = await etapi.putEtapiContent("backup/etapi_test"); + expect(response.status).toEqual(204); + }); +}); diff --git a/spec/etapi/import.js b/spec/etapi/import.js deleted file mode 100644 index 82f0e001d..000000000 --- a/spec/etapi/import.js +++ /dev/null @@ -1,24 +0,0 @@ -const { - describeEtapi, postEtapi, - postEtapiContent, -} = require('../support/etapi.js'); -const fs = require("fs"); -const path = require("path"); -const {getEtapiContent} = require("../support/etapi.js"); - -describeEtapi("import", () => { - it("import", async () => { - const zipFileBuffer = fs.readFileSync(path.resolve(__dirname, 'test-export.zip')); - - const response = await postEtapiContent("notes/root/import", zipFileBuffer); - expect(response.status).toEqual(201); - - const {note, branch} = await response.json(); - - expect(note.title).toEqual("test-export"); - expect(branch.parentNoteId).toEqual("root"); - - const content = await (await getEtapiContent(`notes/${note.noteId}/content`)).text(); - expect(content).toContain("test export content"); - }); -}); diff --git a/spec/etapi/import.ts b/spec/etapi/import.ts new file mode 100644 index 000000000..a3a548d6c --- /dev/null +++ b/spec/etapi/import.ts @@ -0,0 +1,27 @@ +import etapi = require("../support/etapi"); +import fs = require("fs"); +import path = require("path"); + +etapi.describeEtapi("import", () => { + it("import", async () => { + const zipFileBuffer = fs.readFileSync( + path.resolve(__dirname, "test-export.zip") + ); + + const response = await etapi.postEtapiContent( + "notes/root/import", + zipFileBuffer + ); + expect(response.status).toEqual(201); + + const { note, branch } = await response.json(); + + expect(note.title).toEqual("test-export"); + expect(branch.parentNoteId).toEqual("root"); + + const content = await ( + await etapi.getEtapiContent(`notes/${note.noteId}/content`) + ).text(); + expect(content).toContain("test export content"); + }); +}); diff --git a/spec/etapi/notes.js b/spec/etapi/notes.js deleted file mode 100644 index c15033ca8..000000000 --- a/spec/etapi/notes.js +++ /dev/null @@ -1,109 +0,0 @@ -const crypto = require('crypto'); -const { - deleteEtapi, - getEtapiResponse, - describeEtapi, postEtapi, - getEtapi, - getEtapiContent, - patchEtapi, putEtapi, - putEtapiContent -} = require('../support/etapi.js'); - -describeEtapi("notes", () => { - it("create", async () => { - const {note, branch} = await postEtapi('create-note', { - parentNoteId: 'root', - type: 'text', - title: 'Hello World!', - content: 'Content', - prefix: 'Custom prefix' - }); - - expect(note.title).toEqual("Hello World!"); - expect(branch.parentNoteId).toEqual("root"); - expect(branch.prefix).toEqual("Custom prefix"); - - const rNote = await getEtapi(`notes/${note.noteId}`); - expect(rNote.title).toEqual("Hello World!"); - - const rContent = await (await getEtapiContent(`notes/${note.noteId}/content`)).text(); - expect(rContent).toEqual("Content"); - - const rBranch = await getEtapi(`branches/${branch.branchId}`); - expect(rBranch.parentNoteId).toEqual("root"); - expect(rBranch.prefix).toEqual("Custom prefix"); - }); - - it("patch", async () => { - const {note} = await postEtapi('create-note', { - parentNoteId: 'root', - type: 'text', - title: 'Hello World!', - content: 'Content' - }); - - await patchEtapi(`notes/${note.noteId}`, { - title: 'new title', - type: 'code', - mime: 'text/apl', - dateCreated: '2000-01-01 12:34:56.999+0200', - utcDateCreated: '2000-01-01 10:34:56.999Z', - }); - - const rNote = await getEtapi(`notes/${note.noteId}`); - expect(rNote.title).toEqual("new title"); - expect(rNote.type).toEqual("code"); - expect(rNote.mime).toEqual("text/apl"); - expect(rNote.dateCreated).toEqual("2000-01-01 12:34:56.999+0200"); - expect(rNote.utcDateCreated).toEqual("2000-01-01 10:34:56.999Z"); - }); - - it("update content", async () => { - const {note} = await postEtapi('create-note', { - parentNoteId: 'root', - type: 'text', - title: 'Hello World!', - content: 'Content' - }); - - await putEtapiContent(`notes/${note.noteId}/content`, "new content"); - - const rContent = await (await getEtapiContent(`notes/${note.noteId}/content`)).text(); - expect(rContent).toEqual("new content"); - }); - - it("create / update binary content", async () => { - const {note} = await postEtapi('create-note', { - parentNoteId: 'root', - type: 'file', - title: 'Hello World!', - content: 'ZZZ' - }); - - const updatedContent = crypto.randomBytes(16); - - await putEtapiContent(`notes/${note.noteId}/content`, updatedContent); - - const rContent = await (await getEtapiContent(`notes/${note.noteId}/content`)).arrayBuffer(); - expect(Buffer.from(new Uint8Array(rContent))).toEqual(updatedContent); - }); - - it("delete note", async () => { - const {note} = await postEtapi('create-note', { - parentNoteId: 'root', - type: 'text', - title: 'Hello World!', - content: 'Content' - }); - - await deleteEtapi(`notes/${note.noteId}`); - - const resp = await getEtapiResponse(`notes/${note.noteId}`); - expect(resp.status).toEqual(404); - - const error = await resp.json(); - expect(error.status).toEqual(404); - expect(error.code).toEqual("NOTE_NOT_FOUND"); - expect(error.message).toEqual(`Note '${note.noteId}' not found.`); - }); -}); diff --git a/spec/etapi/notes.ts b/spec/etapi/notes.ts new file mode 100644 index 000000000..208a7088d --- /dev/null +++ b/spec/etapi/notes.ts @@ -0,0 +1,107 @@ +import crypto = require("crypto"); +import etapi = require("../support/etapi"); + +etapi.describeEtapi("notes", () => { + it("create", async () => { + const { note, branch } = await etapi.postEtapi("create-note", { + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", + prefix: "Custom prefix", + }); + + expect(note.title).toEqual("Hello World!"); + expect(branch.parentNoteId).toEqual("root"); + expect(branch.prefix).toEqual("Custom prefix"); + + const rNote = await etapi.getEtapi(`notes/${note.noteId}`); + expect(rNote.title).toEqual("Hello World!"); + + const rContent = await ( + await etapi.getEtapiContent(`notes/${note.noteId}/content`) + ).text(); + expect(rContent).toEqual("Content"); + + const rBranch = await etapi.getEtapi(`branches/${branch.branchId}`); + expect(rBranch.parentNoteId).toEqual("root"); + expect(rBranch.prefix).toEqual("Custom prefix"); + }); + + it("patch", async () => { + const { note } = await etapi.postEtapi("create-note", { + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", + }); + + await etapi.patchEtapi(`notes/${note.noteId}`, { + title: "new title", + type: "code", + mime: "text/apl", + dateCreated: "2000-01-01 12:34:56.999+0200", + utcDateCreated: "2000-01-01 10:34:56.999Z", + }); + + const rNote = await etapi.getEtapi(`notes/${note.noteId}`); + expect(rNote.title).toEqual("new title"); + expect(rNote.type).toEqual("code"); + expect(rNote.mime).toEqual("text/apl"); + expect(rNote.dateCreated).toEqual("2000-01-01 12:34:56.999+0200"); + expect(rNote.utcDateCreated).toEqual("2000-01-01 10:34:56.999Z"); + }); + + it("update content", async () => { + const { note } = await etapi.postEtapi("create-note", { + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", + }); + + await etapi.putEtapiContent(`notes/${note.noteId}/content`, "new content"); + + const rContent = await ( + await etapi.getEtapiContent(`notes/${note.noteId}/content`) + ).text(); + expect(rContent).toEqual("new content"); + }); + + it("create / update binary content", async () => { + const { note } = await etapi.postEtapi("create-note", { + parentNoteId: "root", + type: "file", + title: "Hello World!", + content: "ZZZ", + }); + + const updatedContent = crypto.randomBytes(16); + + await etapi.putEtapiContent(`notes/${note.noteId}/content`, updatedContent); + + const rContent = await ( + await etapi.getEtapiContent(`notes/${note.noteId}/content`) + ).arrayBuffer(); + expect(Buffer.from(new Uint8Array(rContent))).toEqual(updatedContent); + }); + + it("delete note", async () => { + const { note } = await etapi.postEtapi("create-note", { + parentNoteId: "root", + type: "text", + title: "Hello World!", + content: "Content", + }); + + await etapi.deleteEtapi(`notes/${note.noteId}`); + + const resp = await etapi.getEtapiResponse(`notes/${note.noteId}`); + expect(resp.status).toEqual(404); + + const error = await resp.json(); + expect(error.status).toEqual(404); + expect(error.code).toEqual("NOTE_NOT_FOUND"); + expect(error.message).toEqual(`Note '${note.noteId}' not found.`); + }); +}); diff --git a/spec/support/etapi.js b/spec/support/etapi.js deleted file mode 100644 index 97bf5aee1..000000000 --- a/spec/support/etapi.js +++ /dev/null @@ -1,184 +0,0 @@ -const {spawn} = require("child_process"); -const kill = require('tree-kill'); - -let etapiAuthToken; - -const getEtapiAuthorizationHeader = () => "Basic " + Buffer.from(`etapi:${etapiAuthToken}`).toString('base64'); - -const PORT = '9999'; -const HOST = 'http://localhost:' + PORT; - -function describeEtapi(description, specDefinitions) { - describe(description, () => { - let appProcess; - - beforeAll(async () => { - appProcess = spawn('npm', ['run', 'start-test-server']); - - await new Promise(res => { - appProcess.stdout.on('data', data => { - console.log("Trilium: " + data.toString().trim()); - - if (data.toString().includes('Listening on port')) { - res(); - } - }); - }); - - await fetch(HOST + '/api/setup/new-document', { method: 'POST' }); - - const formData = new URLSearchParams(); - formData.append('password1', '1234'); - formData.append('password2', '1234'); - - await fetch(HOST + '/set-password', { method: 'POST', body: formData }); - - etapiAuthToken = (await (await fetch(HOST + '/etapi/auth/login', { - method: 'POST', - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ password: '1234' }) - })).json()).authToken; - }); - - afterAll(() => { - console.log("Attempting to kill the Trilium process as part of the cleanup..."); - kill(appProcess.pid, 'SIGKILL', () => { console.log("Trilium process killed.") }); - }); - - specDefinitions(); - }); -} - -async function getEtapiResponse(url) { - return await fetch(`${HOST}/etapi/${url}`, { - method: 'GET', - headers: { - Authorization: getEtapiAuthorizationHeader() - } - }); -} - -async function getEtapi(url) { - const response = await getEtapiResponse(url); - return await processEtapiResponse(response); -} - -async function getEtapiContent(url) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'GET', - headers: { - Authorization: getEtapiAuthorizationHeader() - } - }); - - checkStatus(response); - - return response; -} - -async function postEtapi(url, data = {}) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'POST', - headers: { - "Content-Type": "application/json", - Authorization: getEtapiAuthorizationHeader() - }, - body: JSON.stringify(data) - }); - return await processEtapiResponse(response); -} - -async function postEtapiContent(url, data) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'POST', - headers: { - "Content-Type": "application/octet-stream", - Authorization: getEtapiAuthorizationHeader() - }, - body: data - }); - - checkStatus(response); - - return response; -} - -async function putEtapi(url, data = {}) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'PUT', - headers: { - "Content-Type": "application/json", - Authorization: getEtapiAuthorizationHeader() - }, - body: JSON.stringify(data) - }); - return await processEtapiResponse(response); -} - -async function putEtapiContent(url, data) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'PUT', - headers: { - "Content-Type": "application/octet-stream", - Authorization: getEtapiAuthorizationHeader() - }, - body: data - }); - - checkStatus(response); - - return response; -} - -async function patchEtapi(url, data = {}) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'PATCH', - headers: { - "Content-Type": "application/json", - Authorization: getEtapiAuthorizationHeader() - }, - body: JSON.stringify(data) - }); - return await processEtapiResponse(response); -} - -async function deleteEtapi(url) { - const response = await fetch(`${HOST}/etapi/${url}`, { - method: 'DELETE', - headers: { - Authorization: getEtapiAuthorizationHeader() - } - }); - return await processEtapiResponse(response); -} - -async function processEtapiResponse(response) { - const text = await response.text(); - - if (response.status < 200 || response.status >= 300) { - throw new Error(`ETAPI error ${response.status}: ` + text); - } - - return text?.trim() ? JSON.parse(text) : null; -} - -function checkStatus(response) { - if (response.status < 200 || response.status >= 300) { - throw new Error(`ETAPI error ${response.status}`); - } -} - -module.exports = { - describeEtapi, - getEtapi, - getEtapiResponse, - getEtapiContent, - postEtapi, - postEtapiContent, - putEtapi, - putEtapiContent, - patchEtapi, - deleteEtapi -}; diff --git a/spec/support/etapi.ts b/spec/support/etapi.ts new file mode 100644 index 000000000..05da87bc8 --- /dev/null +++ b/spec/support/etapi.ts @@ -0,0 +1,224 @@ +import child_process = require("child_process"); +import kill = require("tree-kill"); + +let etapiAuthToken: string | undefined; + +const getEtapiAuthorizationHeader = (): string => + "Basic " + Buffer.from(`etapi:${etapiAuthToken}`).toString("base64"); + +const PORT: string = "9999"; +const HOST: string = "http://localhost:" + PORT; + +type SpecDefinitionsFunc = () => void; + +function describeEtapi( + description: string, + specDefinitions: SpecDefinitionsFunc +): void { + describe(description, () => { + let appProcess: ReturnType; + + beforeAll(async () => { + appProcess = child_process.spawn("npm", ["run", "start-test-server"]); + if (!appProcess) { + throw new Error("Failed to start the Trilium process."); + } + + await new Promise((res) => { + appProcess.stdout!.on("data", (data) => { + console.log("Trilium: " + data.toString().trim()); + + if (data.toString().includes("Listening on port")) { + res(); + } + }); + }); + + await fetch(`${HOST}/api/setup/new-document`, { method: "POST" }); + + const formData = new URLSearchParams(); + formData.append("password1", "1234"); + formData.append("password2", "1234"); + + await fetch(`${HOST}/set-password`, { method: "POST", body: formData }); + + etapiAuthToken = ( + await ( + await fetch(`${HOST}/etapi/auth/login`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ password: "1234" }), + }) + ).json() + ).authToken; + }); + + afterAll(() => { + console.log( + "Attempting to kill the Trilium process as part of the cleanup..." + ); + if (!appProcess.pid) { + console.log("Trilium process not found. Cannot kill."); + return; + } + + kill(appProcess.pid, "SIGKILL", (error) => { + if (error) { + console.error("Failed to kill the Trilium process.", error); + } + console.log("Trilium process killed."); + }); + }); + + specDefinitions(); + }); +} + +async function getEtapiResponse(url: string): Promise { + return await fetch(`${HOST}/etapi/${url}`, { + method: "GET", + headers: { + Authorization: getEtapiAuthorizationHeader(), + }, + }); +} + +async function getEtapi(url: string): Promise { + const response = await getEtapiResponse(url); + return await processEtapiResponse(response); +} + +async function getEtapiContent(url: string): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "GET", + headers: { + Authorization: getEtapiAuthorizationHeader(), + }, + }); + + checkStatus(response); + + return response; +} + +async function postEtapi( + url: string, + data: Record = {} +): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: getEtapiAuthorizationHeader(), + }, + body: JSON.stringify(data), + }); + return await processEtapiResponse(response); +} + +async function postEtapiContent( + url: string, + data: BodyInit +): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "POST", + headers: { + "Content-Type": "application/octet-stream", + Authorization: getEtapiAuthorizationHeader(), + }, + body: data, + }); + + checkStatus(response); + + return response; +} + +async function putEtapi( + url: string, + data: Record = {} +): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: getEtapiAuthorizationHeader(), + }, + body: JSON.stringify(data), + }); + return await processEtapiResponse(response); +} + +async function putEtapiContent( + url: string, + data?: BodyInit +): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "PUT", + headers: { + "Content-Type": "application/octet-stream", + Authorization: getEtapiAuthorizationHeader(), + }, + body: data, + }); + + checkStatus(response); + + return response; +} + +async function patchEtapi( + url: string, + data: Record = {} +): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: getEtapiAuthorizationHeader(), + }, + body: JSON.stringify(data), + }); + return await processEtapiResponse(response); +} + +async function deleteEtapi(url: string): Promise { + const response = await fetch(`${HOST}/etapi/${url}`, { + method: "DELETE", + headers: { + Authorization: getEtapiAuthorizationHeader(), + }, + }); + return await processEtapiResponse(response); +} + +async function processEtapiResponse(response: Response): Promise { + const text = await response.text(); + + if (response.status < 200 || response.status >= 300) { + throw new Error(`ETAPI error ${response.status}: ${text}`); + } + + return text?.trim() ? JSON.parse(text) : null; +} + +function checkStatus(response: Response): void { + if (response.status < 200 || response.status >= 300) { + throw new Error(`ETAPI error ${response.status}`); + } +} + +export { + describeEtapi, + getEtapi, + getEtapiResponse, + getEtapiContent, + postEtapi, + postEtapiContent, + putEtapi, + putEtapiContent, + patchEtapi, + deleteEtapi, +}; diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index fc531ef50..878e98751 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -1,12 +1,7 @@ { "spec_dir": "spec", - "spec_files": [ - "**/*[sS]pec.js", - "**/*[sS]pec.mjs" - ], - "helpers": [ - "helpers/**/*.js" - ], + "spec_files": ["./etapi/*.ts"], + "helpers": ["helpers/**/*.js"], "stopSpecOnExpectationFailure": false, "random": true } diff --git a/tsconfig.json b/tsconfig.json index 31aa526d2..e7a4e67c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,24 +1,19 @@ { - "compilerOptions": { - "moduleResolution": "Node", - "declaration": false, - "sourceMap": true, - "outDir": "./dist", - "strict": true, - "noImplicitAny": true, - "resolveJsonModule": true, - "lib": ["ES2022"], - "downlevelIteration": true - }, - "include": [ - "./src/**/*.js", - "./src/**/*.ts" - ], - "exclude": ["./node_modules/**/*"], - "ts-node": { - "files": true - }, - "files": [ - "src/types.d.ts" - ] - } + "compilerOptions": { + "moduleResolution": "Node", + "declaration": false, + "sourceMap": true, + "outDir": "./dist", + "strict": true, + "noImplicitAny": true, + "resolveJsonModule": true, + "lib": ["ES2022"], + "downlevelIteration": true + }, + "include": ["./src/**/*.js", "./src/**/*.ts", "./spec/**/*.ts"], + "exclude": ["./node_modules/**/*"], + "ts-node": { + "files": true + }, + "files": ["src/types.d.ts"] +}