mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00

styling fix Added enum to promoted attribute definition parser Fix for the dropdown Added enumValues attr Added support for task note type Added support for swimlanes and swimlane dashboard Some minor fixes regarding swimlanes fixed a bug with ckeditor where pasting of links was not possible restructured folders folder-restructuring some more restructuring some folder restructuring fixes for file restructuring restored removed code Sorted the swimlane lists by priority sorting swimlanes and blinking based on priority Added new func for commenting on tasks fixed the workflow end to end fixed the workflow end to end Fixed the style for comments. Added the functionality for marking tasks as Done Added a file to track feature wish-list Added status back to the task header. Also added relevant tags to the node_tree. Fixed the status update on the note tree items. Also added deadline back Adding tags to the Swimlane dashboard Updating the dashboard view Fixed the tags on the swimlane dashboard items Fixed the hide/show for swimlane headers Updated the dashboard, added the collapse and expand feature. Also added deadline to the dashboard and tasks Prevented empty lines to be added as a comment. Also cleared task_deadline on a new task created Adding swimlane props Fixed the swimlane done/deprio, also added the subtasks for the tasks that have children fixed the grouping on swimlanes, added bucket type for the main parent of tasks, added swimlane options to dashboard type, fixed deadlines, updates to how swimlanes are populated Removed the db logs Updated fancytree lib to the latest version Updated gitignore Test adding an enum label type styling fix Added enum to promoted attribute definition parser Fix for the dropdown Added enumValues attr Added support for task note type Added support for swimlanes and swimlane dashboard Some minor fixes regarding swimlanes fixed a bug with ckeditor where pasting of links was not possible restructured folders folder-restructuring some more restructuring some folder restructuring fixes for file restructuring restored removed code Sorted the swimlane lists by priority sorting swimlanes and blinking based on priority Added new func for commenting on tasks fixed the workflow end to end fixed the workflow end to end Fixed the style for comments. Added the functionality for marking tasks as Done Added a file to track feature wish-list Added status back to the task header. Also added relevant tags to the node_tree. Fixed the status update on the note tree items. Also added deadline back Adding tags to the Swimlane dashboard Updating the dashboard view Fixed the tags on the swimlane dashboard items Fixed the hide/show for swimlane headers Updated the dashboard, added the collapse and expand feature. Also added deadline to the dashboard and tasks Prevented empty lines to be added as a comment. Also cleared task_deadline on a new task created Adding swimlane props Fixed the swimlane done/deprio, also added the subtasks for the tasks that have children all the updates as of Apr 24, 2024
311 lines
12 KiB
JavaScript
311 lines
12 KiB
JavaScript
const SearchContext = require("../../src/services/search/search_context");
|
|
const parse = require('../../src/services/search/services/parse');
|
|
|
|
function tokens(toks, cur = 0) {
|
|
return toks.map(arg => {
|
|
if (Array.isArray(arg)) {
|
|
return tokens(arg, cur);
|
|
}
|
|
else {
|
|
cur += arg.length;
|
|
|
|
return {
|
|
token: arg,
|
|
inQuotes: false,
|
|
startIndex: cur - arg.length,
|
|
endIndex: cur - 1
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
function assertIsArchived(exp) {
|
|
expect(exp.constructor.name).toEqual("PropertyComparisonExp");
|
|
expect(exp.propertyName).toEqual("isArchived");
|
|
expect(exp.operator).toEqual("=");
|
|
expect(exp.comparedValue).toEqual("false");
|
|
}
|
|
|
|
describe("Parser", () => {
|
|
it("fulltext parser without content", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: tokens(["hello", "hi"]),
|
|
expressionTokens: [],
|
|
searchContext: new SearchContext({excludeArchived: true})
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
expect(rootExp.subExpressions[0].constructor.name).toEqual("PropertyComparisonExp");
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
|
|
expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("NoteFlatTextExp");
|
|
expect(rootExp.subExpressions[1].subExpressions[0].tokens).toEqual(["hello", "hi"]);
|
|
});
|
|
|
|
it("fulltext parser with content", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: tokens(["hello", "hi"]),
|
|
expressionTokens: [],
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
|
|
|
|
const subs = rootExp.subExpressions[1].subExpressions;
|
|
|
|
expect(subs[0].constructor.name).toEqual("NoteFlatTextExp");
|
|
expect(subs[0].tokens).toEqual(["hello", "hi"]);
|
|
|
|
expect(subs[1].constructor.name).toEqual("NoteContentFulltextExp");
|
|
expect(subs[1].tokens).toEqual(["hello", "hi"]);
|
|
});
|
|
|
|
it("simple label comparison", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#mylabel", "=", "text"]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("LabelComparisonExp");
|
|
expect(rootExp.subExpressions[1].attributeType).toEqual("label");
|
|
expect(rootExp.subExpressions[1].attributeName).toEqual("mylabel");
|
|
expect(rootExp.subExpressions[1].comparator).toBeTruthy();
|
|
});
|
|
|
|
it("simple attribute negation", () => {
|
|
let rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#!mylabel"]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("NotExp");
|
|
expect(rootExp.subExpressions[1].subExpression.constructor.name).toEqual("AttributeExistsExp");
|
|
expect(rootExp.subExpressions[1].subExpression.attributeType).toEqual("label");
|
|
expect(rootExp.subExpressions[1].subExpression.attributeName).toEqual("mylabel");
|
|
|
|
rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["~!myrelation"]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("NotExp");
|
|
expect(rootExp.subExpressions[1].subExpression.constructor.name).toEqual("AttributeExistsExp");
|
|
expect(rootExp.subExpressions[1].subExpression.attributeType).toEqual("relation");
|
|
expect(rootExp.subExpressions[1].subExpression.attributeName).toEqual("myrelation");
|
|
});
|
|
|
|
it("simple label AND", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "=", "text", "and", "#second", "=", "text"]),
|
|
searchContext: new SearchContext(true)
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
|
|
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
|
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(secondSub.attributeName).toEqual("second");
|
|
});
|
|
|
|
it("simple label AND without explicit AND", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "=", "text", "#second", "=", "text"]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
|
|
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
|
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(secondSub.attributeName).toEqual("second");
|
|
});
|
|
|
|
it("simple label OR", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "=", "text", "or", "#second", "=", "text"]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
|
|
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
|
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
expect(secondSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(secondSub.attributeName).toEqual("second");
|
|
});
|
|
|
|
it("fulltext and simple label", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: tokens(["hello"]),
|
|
expressionTokens: tokens(["#mylabel", "=", "text"]),
|
|
searchContext: new SearchContext({excludeArchived: true})
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
const [firstSub, secondSub, thirdSub] = rootExp.subExpressions;
|
|
|
|
expect(firstSub.constructor.name).toEqual("PropertyComparisonExp");
|
|
expect(firstSub.propertyName).toEqual('isArchived');
|
|
|
|
expect(secondSub.constructor.name).toEqual("OrExp");
|
|
expect(secondSub.subExpressions[0].constructor.name).toEqual("NoteFlatTextExp");
|
|
expect(secondSub.subExpressions[0].tokens).toEqual(["hello"]);
|
|
|
|
expect(thirdSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(thirdSub.attributeName).toEqual("mylabel");
|
|
});
|
|
|
|
it("label sub-expression", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "=", "text", "or", ["#second", "=", "text", "and", "#third", "=", "text"]]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
|
|
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
|
|
|
|
expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
expect(secondSub.constructor.name).toEqual("AndExp");
|
|
const [firstSubSub, secondSubSub] = secondSub.subExpressions;
|
|
|
|
expect(firstSubSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(firstSubSub.attributeName).toEqual("second");
|
|
|
|
expect(secondSubSub.constructor.name).toEqual("LabelComparisonExp");
|
|
expect(secondSubSub.attributeName).toEqual("third");
|
|
});
|
|
|
|
it("label sub-expression without explicit operator", () => {
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", ["#second", "or", "#third"], "#fourth"]),
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
|
|
const [firstSub, secondSub, thirdSub] = rootExp.subExpressions[1].subExpressions;
|
|
|
|
expect(firstSub.constructor.name).toEqual("AttributeExistsExp");
|
|
expect(firstSub.attributeName).toEqual("first");
|
|
|
|
expect(secondSub.constructor.name).toEqual("OrExp");
|
|
const [firstSubSub, secondSubSub] = secondSub.subExpressions;
|
|
|
|
expect(firstSubSub.constructor.name).toEqual("AttributeExistsExp");
|
|
expect(firstSubSub.attributeName).toEqual("second");
|
|
|
|
expect(secondSubSub.constructor.name).toEqual("AttributeExistsExp");
|
|
expect(secondSubSub.attributeName).toEqual("third");
|
|
|
|
expect(thirdSub.constructor.name).toEqual("AttributeExistsExp");
|
|
expect(thirdSub.attributeName).toEqual("fourth");
|
|
});
|
|
});
|
|
|
|
describe("Invalid expressions", () => {
|
|
it("incomplete comparison", () => {
|
|
const searchContext = new SearchContext();
|
|
|
|
parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "="]),
|
|
searchContext
|
|
});
|
|
|
|
expect(searchContext.error).toEqual('Misplaced or incomplete expression "="')
|
|
});
|
|
|
|
it("comparison between labels is impossible", () => {
|
|
let searchContext = new SearchContext();
|
|
searchContext.originalQuery = "#first = #second";
|
|
|
|
parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "=", "#second"]),
|
|
searchContext
|
|
});
|
|
|
|
expect(searchContext.error).toEqual(`Error near token "#second" in "#first = #second", it's possible to compare with constant only.`);
|
|
|
|
searchContext = new SearchContext();
|
|
searchContext.originalQuery = "#first = note.relations.second";
|
|
|
|
parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["#first", "=", "note", ".", "relations", "second"]),
|
|
searchContext
|
|
});
|
|
|
|
expect(searchContext.error).toEqual(`Error near token "note" in "#first = note.relations.second", it's possible to compare with constant only.`);
|
|
|
|
const rootExp = parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: [
|
|
{ token: "#first", inQuotes: false },
|
|
{ token: "=", inQuotes: false },
|
|
{ token: "#second", inQuotes: true },
|
|
],
|
|
searchContext: new SearchContext()
|
|
});
|
|
|
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
|
assertIsArchived(rootExp.subExpressions[0]);
|
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("LabelComparisonExp");
|
|
expect(rootExp.subExpressions[1].attributeType).toEqual("label");
|
|
expect(rootExp.subExpressions[1].attributeName).toEqual("first");
|
|
expect(rootExp.subExpressions[1].comparator).toBeTruthy();
|
|
});
|
|
|
|
it("searching by relation without note property", () => {
|
|
const searchContext = new SearchContext();
|
|
|
|
parse({
|
|
fulltextTokens: [],
|
|
expressionTokens: tokens(["~first", "=", "text", "-", "abc"]),
|
|
searchContext
|
|
});
|
|
|
|
expect(searchContext.error).toEqual('Relation can be compared only with property, e.g. ~relation.title=hello in ""')
|
|
});
|
|
});
|