This is an automated email from the ASF dual-hosted git repository.

gerben pushed a commit to branch tweak-highlighter
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git

commit 992e3855909b1c665e8579c6f5286529b0ab2d0b
Author: Gerben <[email protected]>
AuthorDate: Thu Jul 23 14:16:49 2020 +0200

    Create tests for highlighter
---
 .../test/highlight-range/highlight-range.test.ts   | 199 +++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/packages/dom/test/highlight-range/highlight-range.test.ts 
b/packages/dom/test/highlight-range/highlight-range.test.ts
new file mode 100644
index 0000000..387fd7b
--- /dev/null
+++ b/packages/dom/test/highlight-range/highlight-range.test.ts
@@ -0,0 +1,199 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { assert } from 'chai';
+import { highlightRange } from '../../src/highlight-range';
+import { RangeInfo, hydrateRange, evaluateXPath } from '../utils';
+
+const domParser = new window.DOMParser();
+
+const testCases: {
+  [name: string]: {
+    inputHtml: string,
+    range: RangeInfo,
+    tagName?: string,
+    attributes?: Record<string, string>,
+    expectedHtml: string,
+  }
+} = {
+  'single text node': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 20,
+    },
+    expectedHtml: '<b>lorem ipsum <mark>dolor am</mark>et yada yada</b>',
+  },
+  'across elements': {
+    inputHtml: '<b>lorem <i>ipsum</i> dolor <u>amet</u> yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()[2]',
+      startOffset: 1,
+      endContainerXPath: '//u/text()',
+      endOffset: 2,
+    },
+    expectedHtml: '<b>lorem <i>ipsum</i> <mark>dolor 
</mark><u><mark>am</mark>et</u> yada yada</b>',
+  },
+  'collapsed range': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 12,
+    },
+    expectedHtml: '<b>lorem ipsum <mark></mark>dolor amet yada yada</b>',
+  },
+  'custom tag name': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 20,
+    },
+    tagName: 'span',
+      expectedHtml: '<b>lorem ipsum <span>dolor am</span>et yada yada</b>',
+  },
+  'custom attributes': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 20,
+    },
+    attributes: {
+        class: 'red',
+      },
+      expectedHtml: '<b>lorem ipsum <mark class="red">dolor am</mark>et yada 
yada</b>',
+  },
+  'overlapping highlight': {
+    // Starts off from the result of the 'single text node' case.
+    inputHtml: '<b>lorem ipsum <mark>dolor am</mark>et yada yada</b>',
+    range:  {
+      startContainerXPath: '//mark/text()',
+      startOffset: 6,
+      endContainerXPath: '//b/text()[2]',
+      endOffset: 7,
+    },
+    tagName: 'mark2',
+    expectedHtml: '<b>lorem ipsum <mark>dolor 
<mark2>am</mark2></mark><mark2>et yada</mark2> yada</b>',
+  },
+};
+
+describe('highlightRange', () => {
+  for (const [name, { inputHtml, range, tagName, attributes, expectedHtml }] 
of Object.entries(testCases)) {
+    it(`works for case: ${name}`, () => {
+      const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+      // Invoke highlightRange for the specified Range, and check the result.
+      const removeHighlights = highlightRange(hydrateRange(range, doc), 
tagName, attributes);
+      assert.equal(doc.body.innerHTML, expectedHtml);
+
+      // Remove the highlight again and check that we end up exactly how we 
started.
+      removeHighlights();
+      assert.equal(doc.body.innerHTML, inputHtml);
+    });
+  }
+
+  it('works on adjacent text nodes', () => {
+    const inputHtml = '<b>lorem ipsum dolor amet yada yada</b>';
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const textNode = evaluateXPath(doc, '//b/text()') as Text;
+    textNode.splitText(15); // after 'dol'
+
+    const range = doc.createRange();
+    range.setStart(evaluateXPath(doc, '//b/text()[1]'), 12); // before 'dolor 
am'
+    range.setEnd(evaluateXPath(doc, '//b/text()[2]'), 20 - 15); // after 
'dolor am'
+
+    const removeHighlights = highlightRange(range);
+    const expectedHtml = '<b>lorem ipsum <mark>dol</mark><mark>or am</mark>et 
yada yada</b>';
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('also marks empty text nodes', () => {
+    const inputHtml = '<b>lorem ipsum dolor amet yada yada</b>';
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const textNode = evaluateXPath(doc, '//b/text()') as Text;
+    textNode.splitText(15);
+    textNode.splitText(15); // Split the node twice to create an empty text 
node.
+
+    const range = doc.createRange();
+    range.setStart(evaluateXPath(doc, '//b/text()[1]'), 12); // before 'dolor 
am'
+    range.setEnd(evaluateXPath(doc, '//b/text()[3]'), 20 - 15); // after 
'dolor am'
+
+    const removeHighlights = highlightRange(range);
+    const expectedHtml = '<b>lorem ipsum <mark>dol</mark><mark></mark><mark>or 
am</mark>et yada yada</b>';
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('ignores a range that does not contain Text nodes', () => {
+    const inputHtml = `<b>Try highlighting this image: <img> — would that 
work?</b>`
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const range = document.createRange();
+    range.selectNode(evaluateXPath(doc, '//img'));
+
+    const removeHighlights = highlightRange(range);
+    assert.equal(doc.body.innerHTML, inputHtml);
+
+    removeHighlights();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('correctly removes multiple highlights (fifo order)', () => {
+    const { inputHtml, range } = testCases['single text node'];
+    const { range: range2, expectedHtml } = testCases['overlapping highlight'];
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const removeHighlights1 = highlightRange(hydrateRange(range, doc));
+    const removeHighlights2 = highlightRange(hydrateRange(range2, doc), 
'mark2');
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights1();
+    removeHighlights2();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('correctly removes multiple highlights (lifo order)', () => {
+    const { inputHtml, range } = testCases['single text node'];
+    const { range: range2, expectedHtml } = testCases['overlapping highlight'];
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const removeHighlights1 = highlightRange(hydrateRange(range, doc));
+    const removeHighlights2 = highlightRange(hydrateRange(range2, doc), 
'mark2');
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights2();
+    removeHighlights1();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+});

Reply via email to