Compare commits

...

5 Commits

Author SHA1 Message Date
zadam
f195c7d1b6 wip 2023-11-27 00:41:46 +01:00
zadam
10f3df3ed4 make sure content is string for post-processing 2023-11-26 23:51:04 +01:00
zadam
18e2e6779b remove title to fix closing highlights list, fixes #4471 2023-11-24 23:51:12 +01:00
zadam
ed129c307b fix printing math, closes #4470 2023-11-24 00:17:20 +01:00
zadam
8742e4bfe9 make sure the promoted attributes don't take the whole screen in mobile, #4468 2023-11-24 00:04:49 +01:00
13 changed files with 202 additions and 118 deletions

View File

@@ -1,109 +0,0 @@
const crypto = require('crypto');
const {
deleteEtapi,
getEtapiResponse,
describeEtapi, postEtapi,
getEtapi,
getEtapiContent,
patchEtapi, putEtapi,
putEtapiContent
} = require("../support/etapi");
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.`);
});
});

View File

@@ -1,5 +1,108 @@
describe("Notes", () => {
it("zzz", () => {
const crypto = require('crypto');
const {
deleteEtapi,
getEtapiResponse,
describeEtapi, postEtapi,
getEtapi,
getEtapiContent,
patchEtapi, putEtapi,
putEtapiContent
} = require("../support/etapi");
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");
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.`);
});
});

43
spec/etapi/search.spec.js Normal file
View File

@@ -0,0 +1,43 @@
const crypto = require('crypto');
const {
deleteEtapi,
getEtapiResponse,
describeEtapi, postEtapi,
getEtapi,
getEtapiContent,
patchEtapi, putEtapi,
putEtapiContent
} = require("../support/etapi");
const {createTextNote} = require("../support/etapi.js");
describeEtapi("search", () => {
describe('search', () => {
let europe, america;
let austria, czechia;
let usa, canada;
beforeAll(async () => {
europe = await createTextNote(null, 'Europe');
austria = await createTextNote(europe.noteId, 'Austria');
czechia = await createTextNote(europe.noteId, 'Czechia');
america = await createTextNote(null, 'America');
usa = await createTextNote(null, 'USA');
canada = await createTextNote(null, 'Canada');
});
async function search(searchString, params) {
const keyToValues = Object.keys(params).map(key => `${key}=${params[key]}`);
const {results} = await getEtapi(`notes?search=${searchString}&${keyToValues.join('&')}`);
return results;
}
it("search", async () => {
const results = await search('Austria');
expect(results.length).toEqual(0);
});
});
});

View File

@@ -8,6 +8,8 @@ const getEtapiAuthorizationHeader = () => "Basic " + Buffer.from(`etapi:${etapiA
const PORT = '9999';
const HOST = 'http://localhost:' + PORT;
let currentTestRootNote = null;
function describeEtapi(description, specDefinitions) {
describe(description, () => {
let appProcess;
@@ -42,6 +44,10 @@ function describeEtapi(description, specDefinitions) {
})).json()).authToken;
});
beforeEach(async () => {
currentTestRootNote = await createTextNote('root', "test root");
});
afterAll(() => {
console.log("Attempting to kill the Trilium process as part of the cleanup...");
kill(appProcess.pid, 'SIGKILL', () => { console.log("Trilium process killed.") });
@@ -51,6 +57,30 @@ function describeEtapi(description, specDefinitions) {
});
}
async function createTextNote(parentNoteId = null, title = 'new note', content = '') {
if (!parentNoteId) {
parentNoteId = currentTestRootNote.noteId;
}
const {note} = await postEtapi('create-note', {
parentNoteId,
type: 'text',
title,
content
});
return note;
}
async function createLabel(noteId, name, value = '', isInheritable = false) {
return await postEtapi('attributes', {
type: 'label',
name,
value,
isInheritable
});
}
async function getEtapiResponse(url) {
return await fetch(`${HOST}/etapi/${url}`, {
method: 'GET',
@@ -172,6 +202,9 @@ function checkStatus(response) {
module.exports = {
describeEtapi,
createTextNote,
createLabel,
getCurrentTestRootNote: () => currentTestRootNote,
getEtapi,
getEtapiResponse,
getEtapiContent,

View File

@@ -56,7 +56,6 @@ export default class HighlightsListWidget extends RightPanelWidget {
.class("icon-action"),
new OnClickButtonWidget()
.icon("bx-x")
.title("Close Highlights List")
.titlePlacement("left")
.onClick(widget => widget.triggerCommand("closeHlt"))
.class("icon-action")

View File

@@ -258,10 +258,11 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
.append($("<h2>").text(this.note.title))
.append($promotedAttributes)
.prop('outerHTML'),
footer: `
<script src="${assetPath}/libraries/katex/katex.min.js"></script>
<script src="${assetPath}/libraries/katex/mhchem.min.js"></script>
<script src="${assetPath}/libraries/katex/auto-render.min.js"></script>
<script src="${assetPath}/node_modules/katex/dist/katex.min.js"></script>
<script src="${assetPath}/node_modules/katex/dist/contrib/mhchem.min.js"></script>
<script src="${assetPath}/node_modules/katex/dist/contrib/auto-render.min.js"></script>
<script>
document.body.className += ' ck-content printed-content';
@@ -273,7 +274,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
`${assetPath}/libraries/codemirror/codemirror.css`,
`${assetPath}/libraries/ckeditor/ckeditor-content.css`,
`${assetPath}/libraries/bootstrap/css/bootstrap.min.css`,
`${assetPath}/libraries/katex/katex.min.css`,
`${assetPath}/node_modules/katex/dist/katex.min.css`,
`${assetPath}/stylesheets/print.css`,
`${assetPath}/stylesheets/relation_map.css`,
`${assetPath}/stylesheets/ckeditor-theme.css`

View File

@@ -8,8 +8,14 @@ import options from "../../services/options.js";
import utils from "../../services/utils.js";
const TPL = `
<div>
<div class="promoted-attributes-widget">
<style>
body.mobile .promoted-attributes-widget {
/* https://github.com/zadam/trilium/issues/4468 */
flex-shrink: 0.4;
overflow: auto;
}
.promoted-attributes-container {
margin: auto;
display: flex;

View File

@@ -68,7 +68,6 @@ export default class TocWidget extends RightPanelWidget {
.class("icon-action"),
new OnClickButtonWidget()
.icon("bx-x")
.title("Close Table of Contents")
.titlePlacement("left")
.onClick(widget => widget.triggerCommand("closeToc"))
.class("icon-action")

View File

@@ -889,6 +889,10 @@ function scanForLinks(note, content) {
* Things which have to be executed after updating content, but asynchronously (separate transaction)
*/
async function asyncPostProcessContent(note, content) {
if (note.hasStringContent() && !utils.isString(content)) {
content = content.toString();
}
scanForLinks(note, content);
}

View File

@@ -303,6 +303,10 @@ function toMap(list, key) {
return map;
}
function isString(x) {
return Object.prototype.toString.call(x) === "[object String]";
}
module.exports = {
randomSecureToken,
randomString,
@@ -335,4 +339,5 @@ module.exports = {
normalize,
hashedBlobId,
toMap,
isString
};