blob: 378c255bef3dee56f377fe1255a29c477dfb46e2 [file] [log] [blame]
/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../../test/common-test-setup';
import {
TEST_ONLY,
annotateElement,
annotateWithElement,
getStringLength,
} from './gr-annotation';
import {
getSanitizeDOMValue,
setSanitizeDOMValue,
} from '@polymer/polymer/lib/utils/settings';
import {assert, fixture, html} from '@open-wc/testing';
suite('annotation', () => {
let str: string;
let parent: HTMLDivElement;
let textNode: Text;
setup(async () => {
parent = await fixture(
html`
<div>Lorem ipsum dolor sit amet, suspendisse inceptos vehicula</div>
`
);
textNode = parent.childNodes[0] as Text;
str = textNode.textContent!;
});
test('_annotateText length:0 offset:0', () => {
TEST_ONLY._annotateText(textNode, 0, 0, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'<hl class="foobar"></hl>Lorem ipsum dolor sit amet, suspendisse inceptos vehicula'
);
});
test('_annotateText length:0 offset:1', () => {
TEST_ONLY._annotateText(textNode, 1, 0, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'L<hl class="foobar"></hl>orem ipsum dolor sit amet, suspendisse inceptos vehicula'
);
});
test('_annotateText length:0 offset:str.length', () => {
TEST_ONLY._annotateText(textNode, str.length, 0, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'Lorem ipsum dolor sit amet, suspendisse inceptos vehicula<hl class="foobar"></hl>'
);
});
test('_annotateText Case 1', () => {
TEST_ONLY._annotateText(textNode, 0, str.length, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'<hl class="foobar">Lorem ipsum dolor sit amet, suspendisse inceptos vehicula</hl>'
);
});
test('_annotateText Case 2', () => {
TEST_ONLY._annotateText(textNode, 0, 12, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'<hl class="foobar">Lorem ipsum </hl>dolor sit amet, suspendisse inceptos vehicula'
);
});
test('_annotateText Case 3', () => {
TEST_ONLY._annotateText(textNode, 12, str.length - 12, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'Lorem ipsum <hl class="foobar">dolor sit amet, suspendisse inceptos vehicula</hl>'
);
});
test('_annotateText Case 4', () => {
const index = str.indexOf('dolor');
const length = 'dolor '.length;
TEST_ONLY._annotateText(textNode, index, length, 'foobar');
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'Lorem ipsum <hl class="foobar">dolor </hl>sit amet, suspendisse inceptos vehicula'
);
});
test('_annotateElement design doc example', () => {
const layers = ['amet, ', 'inceptos ', 'amet, ', 'et, suspendisse ince'];
// Apply the layers successively.
layers.forEach((layer, i) => {
annotateElement(
parent,
str.indexOf(layer),
layer.length,
`layer-${i + 1}`
);
});
assert.equal(parent.textContent, str);
assert.equal(
parent.innerHTML,
'Lorem ipsum dolor sit <hl class="layer-1"><hl class="layer-3">am<hl class="layer-4">et, </hl></hl></hl><hl class="layer-4">suspendisse </hl><hl class="layer-2"><hl class="layer-4">ince</hl>ptos </hl>vehicula'
);
});
test('splitTextNode', () => {
const helloString = 'hello';
const asciiString = 'ASCII';
const unicodeString = 'Unic💢de';
let node;
let tail;
// Non-unicode path:
node = document.createTextNode(helloString + asciiString);
tail = TEST_ONLY.splitTextNode(node, helloString.length);
assert(node.textContent, helloString);
assert(tail.textContent, asciiString);
// Unicdoe path:
node = document.createTextNode(helloString + unicodeString);
tail = TEST_ONLY.splitTextNode(node, helloString.length);
assert(node.textContent, helloString);
assert(tail.textContent, unicodeString);
});
suite('annotateWithElement', () => {
const fullText = '01234567890123456789';
let mockSanitize: sinon.SinonSpy;
let originalSanitizeDOMValue: (
value: unknown,
name: string,
type: 'property' | 'attribute',
node: Node | null | undefined
) => unknown;
setup(() => {
setSanitizeDOMValue(p0 => p0);
originalSanitizeDOMValue = getSanitizeDOMValue()!;
assert.isDefined(originalSanitizeDOMValue);
mockSanitize = sinon.spy(originalSanitizeDOMValue);
setSanitizeDOMValue(mockSanitize);
});
teardown(() => {
setSanitizeDOMValue(originalSanitizeDOMValue);
});
test('annotates when fully contained', () => {
const length = 10;
const container = document.createElement('div');
container.textContent = fullText;
annotateWithElement(container, 1, length, {
tagName: 'test-wrapper',
});
assert.equal(
container.innerHTML,
'0<test-wrapper>1234567890</test-wrapper>123456789'
);
});
test('annotates when spanning multiple nodes', () => {
const length = 10;
const container = document.createElement('div');
container.textContent = fullText;
annotateElement(container, 5, length, 'testclass');
annotateWithElement(container, 1, length, {
tagName: 'test-wrapper',
});
assert.equal(
container.innerHTML,
'0' +
'<test-wrapper>' +
'1234' +
'<hl class="testclass">567890</hl>' +
'</test-wrapper>' +
'<hl class="testclass">1234</hl>' +
'56789'
);
});
test('annotates text node', () => {
const length = 10;
const container = document.createElement('div');
container.textContent = fullText;
annotateWithElement(container.childNodes[0], 1, length, {
tagName: 'test-wrapper',
});
assert.equal(
container.innerHTML,
'0<test-wrapper>1234567890</test-wrapper>123456789'
);
});
test('handles zero-length nodes', () => {
const container = document.createElement('div');
container.appendChild(document.createTextNode('0123456789'));
container.appendChild(document.createElement('span'));
container.appendChild(document.createTextNode('0123456789'));
annotateWithElement(container, 1, 10, {
tagName: 'test-wrapper',
});
assert.equal(
container.innerHTML,
'0<test-wrapper>123456789<span></span>0</test-wrapper>123456789'
);
});
test('handles comment nodes', () => {
const container = document.createElement('div');
container.appendChild(document.createComment('comment1'));
container.appendChild(document.createTextNode('0123456789'));
container.appendChild(document.createComment('comment2'));
container.appendChild(document.createElement('span'));
container.appendChild(document.createTextNode('0123456789'));
annotateWithElement(container, 1, 10, {
tagName: 'test-wrapper',
});
assert.equal(
container.innerHTML,
'<!--comment1-->' +
'0<test-wrapper>123456789' +
'<!--comment2-->' +
'<span></span>0</test-wrapper>123456789'
);
});
test('sets sanitized attributes', () => {
const container = document.createElement('div');
container.textContent = fullText;
const attributes = {
href: 'foo',
'data-foo': 'bar',
class: 'hello world',
};
annotateWithElement(container, 1, length, {
tagName: 'test-wrapper',
attributes,
});
assert(
mockSanitize.calledWith(
'foo',
'href',
'attribute',
sinon.match.instanceOf(Element)
)
);
assert(
mockSanitize.calledWith(
'bar',
'data-foo',
'attribute',
sinon.match.instanceOf(Element)
)
);
assert(
mockSanitize.calledWith(
'hello world',
'class',
'attribute',
sinon.match.instanceOf(Element)
)
);
const el = container.querySelector('test-wrapper')!;
assert.equal(el.getAttribute('href'), 'foo');
assert.equal(el.getAttribute('data-foo'), 'bar');
assert.equal(el.getAttribute('class'), 'hello world');
});
});
suite('getStringLength', () => {
test('ASCII characters are counted correctly', () => {
assert.equal(getStringLength('ASCII'), 5);
});
test('Unicode surrogate pairs count as one symbol', () => {
assert.equal(getStringLength('Unic💢de'), 7);
assert.equal(getStringLength('💢💢'), 2);
});
test('Grapheme clusters count as multiple symbols', () => {
assert.equal(getStringLength('man\u0303ana'), 7); // mañana
assert.equal(getStringLength('q\u0307\u0323'), 3); // q̣̇
});
});
});