import { DelimiterMark, DelimiterSearcher, TagPlacement } from "src/compilation"; import { Delimiters } from "src/delimiters"; import { xml, XmlNodeType } from "src/xml"; import { parseXml } from "test/testUtils"; import { describe, expect, test } from "vitest"; describe(DelimiterSearcher, () => { describe('single character delimiters', () => { test('simple paragraph', () => { const paragraph = parseXml(` {#loop}{/loop} `); const textNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 6, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: true, index: 7, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 13, xmlTextNode: textNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{', tagEnd: '}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('two different text nodes', () => { const paragraph = parseXml(` {#lo op} `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const secondTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 1, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 2, xmlTextNode: secondTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{', tagEnd: '}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('two different run nodes', () => { const paragraph = parseXml(` { tag } `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const thirdTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 2, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 0, xmlTextNode: thirdTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{', tagEnd: '}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test("inline drawing in the middle of a tag", () => { const paragraph = parseXml(` {Text Tag} `, false); const openTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const closeTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 2, 0, 0); const imagePropertiesNode = xml.query.findByPath(paragraph, XmlNodeType.General, 1, 0, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: openTextNode }, { placement: TagPlacement.Attribute, isOpen: true, index: 0, xmlNode: imagePropertiesNode, attributeName: 'descr' }, { placement: TagPlacement.Attribute, isOpen: false, index: 14, xmlNode: imagePropertiesNode, attributeName: 'descr' }, { placement: TagPlacement.TextNode, isOpen: false, index: 3, xmlTextNode: closeTextNode }, ]; const searcher = createDelimiterSearcher({ tagStart: '{', tagEnd: '}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); }); describe('multi character delimiters', () => { test('simple paragraph', () => { const paragraph = parseXml(` {{#loop}}{{/loop}} `); const textNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 7, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: true, index: 9, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 16, xmlTextNode: textNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{{', tagEnd: '}}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('two different text nodes', () => { const paragraph = parseXml(` {{#lo op}} `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const secondTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 1, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 2, xmlTextNode: secondTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{{', tagEnd: '}}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('two different run nodes', () => { const paragraph = parseXml(` {{ tag }} `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const thirdTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 2, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 0, xmlTextNode: thirdTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{{', tagEnd: '}}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('delimiters splitted across several different run nodes', () => { const paragraph = parseXml(` { {{tag} } } `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 6, xmlTextNode: firstTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{{{', tagEnd: '}}}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('delimiters splitted across several different run nodes - with attribute tags of a floating drawing in the middle', () => { const paragraph = parseXml(` { {{Text Tag} } } `, false); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const imagePropertiesNode = xml.query.findByPath(paragraph, XmlNodeType.General, 2, 0, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.Attribute, isOpen: true, index: 0, xmlNode: imagePropertiesNode, attributeName: 'descr' }, { placement: TagPlacement.Attribute, isOpen: false, index: 16, xmlNode: imagePropertiesNode, attributeName: 'descr' }, { placement: TagPlacement.TextNode, isOpen: false, index: 11, xmlTextNode: firstTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{{{', tagEnd: '}}}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters.length).toEqual(expected.length); expect(delimiters).toEqual(expected); }); }); describe('text contains multiple delimiter prefixes', () => { test('simple paragraph', () => { const paragraph = parseXml(` {{!#loop!}}{{!/loop!}} `); const textNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 1, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 8, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: true, index: 12, xmlTextNode: textNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 19, xmlTextNode: textNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{!', tagEnd: '!}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('two different text nodes', () => { const paragraph = parseXml(` {!#lo op!} `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const secondTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 1, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 2, xmlTextNode: secondTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{!', tagEnd: '!}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); test('two different run nodes', () => { const paragraph = parseXml(` {! tag !} `); const firstTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 0, 0, 0); const thirdTextNode = xml.query.findByPath(paragraph, XmlNodeType.Text, 2, 0, 0); const expected: DelimiterMark[] = [ { placement: TagPlacement.TextNode, isOpen: true, index: 0, xmlTextNode: firstTextNode }, { placement: TagPlacement.TextNode, isOpen: false, index: 0, xmlTextNode: thirdTextNode } ]; const searcher = createDelimiterSearcher({ tagStart: '{!', tagEnd: '!}' }); const delimiters = searcher.findDelimiters(paragraph); expect(delimiters).toEqual(expected); }); }); }); function createDelimiterSearcher(delimitersSetup?: Partial): DelimiterSearcher { const delimiters = new Delimiters(delimitersSetup); return new DelimiterSearcher(delimiters, 20); }