blob: 3bfc3f79971563b0794afe2625ff4b3c0443fb9c [file] [log] [blame]
<!doctype html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="assert_selection.js"></script>
<script>
function checked_assert_selection(input, command, output) {
try {
assert_selection(input, command, output);
} catch (exception) {
return exception.message;
}
return 'no exception';
}
test(() => {
assert_selection('foo', 'noop', 'foo');
assert_selection('|foo', 'noop', '|foo');
assert_selection('f|oo', 'noop', 'f|oo');
assert_selection('foo|', 'noop', 'foo|');
assert_selection('^foo|', 'noop', '^foo|');
assert_selection('f^oo|', 'noop', 'f^oo|');
assert_selection('f^o|o', 'noop', 'f^o|o');
assert_selection('|foo^', 'noop', '|foo^');
assert_selection(
'|foo^',
selection => selection.modify('extend', 'forward', 'character'),
'f|oo^', 'selection.modify');
assert_selection(
'<div contenteditable>^foo|</div>',
'bold',
'<div contenteditable><b>^foo|</b></div>',
'execCommand');
}, 'markers in text');
test(() => {
assert_selection('|<img>', 'noop', '|<img>');
assert_selection('^<img>|', 'noop', '^<img>|');
assert_selection('|<img>^', 'noop', '|<img>^');
assert_selection('<a>|<br></a>', 'noop', '<a>|<br></a>');
assert_selection('<a><img>|</a>', 'noop', '<a><img>|</a>');
assert_selection('<a>^<img>|</a>', 'noop', '<a>^<img>|</a>');
assert_selection('<a>|<img>^</a>', 'noop', '<a>|<img>^</a>');
assert_selection('<a><img>|<img></a>', 'noop', '<a><img>|<img></a>');
assert_selection('<a><img>|<img>^</a>', 'noop', '<a><img>|<img>^</a>');
assert_selection('<a><img>^<img>|</a>', 'noop', '<a><img>^<img>|</a>');
}, 'markers at element');
test(() => {
assert_selection(
'|<table><tr><td>foo</td></tr></table>', 'noop',
'|<table><tbody><tr><td>foo</td></tr></tbody></table>');
assert_selection(
'<table><tr><td>foo</td></tr></table>|', 'noop',
'<table><tbody><tr><td>foo</td></tr></tbody></table>|');
assert_selection(
'^<table><tr><td>foo</td></tr></table>|', 'noop',
'^<table><tbody><tr><td>foo</td></tr></tbody></table>|');
assert_selection(
'|<table><tr><td>foo</td></tr></table>^', 'noop',
'|<table><tbody><tr><td>foo</td></tr></tbody></table>^');
}, 'markers around table');
selection_test(
'<div>foo</div>',
selection => {
let doc = selection.document;
doc.documentElement.replaceChild(
doc.createTextNode('baz'), doc.body);
},
'<html><head></head>baz</html>',
'Serialize document element instead of document.body when it is null.');
// Tests for TEXTAREA
selection_test(
'<div>|<textarea>foo</textarea></div>',
'noop',
'<div>|<textarea>foo</textarea></div>',
'We can place caret before TEXTAREA');
selection_test(
'<div><textarea>foo</textarea>|</div>',
'noop',
'<div><textarea>foo</textarea>|</div>',
'We can place caret after TEXTAREA');
selection_test(
'<div>^<textarea>foo</textarea>|</div>',
'noop',
'<div>^<textarea>foo</textarea>|</div>',
'We can select TEXTAREA');
selection_test(
'<div><textarea>f|oo</textarea></div>',
selection => {
const textarea = selection.document.querySelector('textarea');
textarea.value = 'bar';
},
'<div><textarea>bar|</textarea></div>',
'TEXTAREA is serialized with value property');
selection_test(
'<div><textarea>01234|56789</textarea></div>',
selection => {
const textarea = selection.document.querySelector('textarea');
textarea.setSelectionRange(3, 7);
},
'<div><textarea>012^3456|789</textarea></div>',
'HTMLTextArea#setSelectionRange()');
selection_test(
'<textarea>0123^456|789</textarea>',
selection => {
const textarea = selection.document.querySelector('textarea');
assert_equals(textarea.selectionStart, 4, 'selectionStart');
assert_equals(textarea.selectionEnd, 7, 'selectionEnd');
},
'<textarea>0123^456|789</textarea>',
'range selection');
selection_test(
'<textarea>0123|456^789</textarea>',
selection => {
const textarea = selection.document.querySelector('textarea');
assert_equals(textarea.selectionStart, 4, 'selectionStart');
assert_equals(textarea.selectionEnd, 7, 'selectionEnd');
assert_equals(textarea.selectionDirection, 'backward', 'selectionEnd');
},
'<textarea>0123|456^789</textarea>',
'backward range selection');
selection_test(
'<textarea>01234|56789</textarea>',
selection => {
const textarea = selection.document.querySelector('textarea');
assert_equals(textarea.selectionStart, 5, 'selectionStart');
assert_equals(textarea.selectionEnd, 5, 'selectionEnd');
},
'<textarea>01234|56789</textarea>',
'caret selection');
// Shadow DOM
selection_test(
[
'<div id="host">',
'<b id="abc">abc</b>',
'<b id="def">def</b>',
'</div>',
],
selection => {
const document = selection.document;
const host = document.getElementById('host');
const shadowRoot = host.createShadowRoot();
shadowRoot.innerHTML = [
'<span id="ghi">ghi</span>',
'<content select="#def"></content>',
'<span id="jkl">jkl</span>',
'<content select="#abc"></content>',
'<span id="mno">mno</span>',
].join('');
selection.collapse(document.getElementById('abc'), 0);
selection.extend(document.getElementById('def'), 0);
},
[
'<div id="host">',
'<span id="ghi">ghi</span>',
'<b id="def">|def</b>',
'<span id="jkl">jkl</span>',
'<b id="abc">^abc</b>',
'<span id="mno">mno</span>',
'</div>',
],
{dumpAs: 'flattree'}, 'dump flat tree for shadow DOM V0');
selection_test(
[
'<div id="host">',
'<b id="abc" slot="abc">abc</b>',
'<b id="def" slot="def">def</b>',
'</div>',
].join(''),
selection => {
const document = selection.document;
const host = document.getElementById('host');
const shadowRoot = host.attachShadow({mode: 'open'});
shadowRoot.innerHTML = [
'<span id="ghi">ghi</span>',
'<slot name="def"></slot>',
'<span id="jkl">jkl</span>',
'<slot name="abc"></slot>',
'<span id="mno">mno</span>',
].join('');
selection.collapse(document.getElementById('abc'), 0);
selection.extend(document.getElementById('def'), 0);
},
[
'<div id="host">',
'<span id="ghi">ghi</span>',
'<slot name="def">',
'<b id="def" slot="def">|def</b>',
'</slot>',
'<span id="jkl">jkl</span>',
'<slot name="abc">',
'<b id="abc" slot="abc">^abc</b>',
'</slot>',
'<span id="mno">mno</span>',
'</div>',
].join(''),
{dumpAs: 'flattree'}, 'dump flat tree for shadow DOM V1');
selection_test(
[
'<style>* { font-family: monospace; }</style>',
'<div>',
'<span id="start">ABC</span>',
'<p><a slot="a">111</a><b slot="b">222</b></p>',
'DEF',
'</div>',
],
selection => {
if (!window.eventSender)
throw 'This test requires eventSender.';
const document = selection.document;
const host = document.querySelector('p');
const shadowRoot = host.attachShadow({mode: 'open'});
shadowRoot.innerHTML = [
'ghi',
'<slot name="b"></slot>',
'<span id="end">jkl</span>',
'<slot name="a"></slot>',
'mno',
].join('');
const start = document.getElementById('start');
const end = shadowRoot.getElementById('end');
eventSender.mouseMoveTo(
selection.computeLeft(start),
selection.computeTop(start) + start.offsetHeight / 2);
eventSender.mouseDown();
eventSender.leapForward(300);
eventSender.mouseMoveTo(
selection.computeLeft(end) + end.offsetWidth - 1,
selection.computeTop(end) + end.offsetHeight / 2);
eventSender.mouseUp();
},
[
'<style>* { font-family: monospace; }</style>',
'<div>',
'<span id="start">^ABC</span>',
'<p>',
'ghi',
'<slot name="b"><b slot="b">222</b></slot>',
'<span id="end">jkl|</span>',
'<slot name="a"><a slot="a">111</a></slot>',
'mno',
'</p>',
'DEF',
'</div>',
],
{dumpAs: 'flattree'}, 'selection crossing shadow boundary');
selection_test(
'<br>',
selection => {
selection.document.documentElement.prepend('Pre-HEAD');
selection.document.body.before('Pre-BODY');
selection.document.documentElement.append('Post-BODY');
},
'<html>Pre-HEAD<head></head>Pre-BODY<body><br></body>Post-BODY</html>',
{dumpFromRoot: true},
'Serialize with dumpFromRoot option');
test(() => {
assert_equals(checked_assert_selection('fo|o', 'noop', 'fo|o'),
'no exception');
}, 'no marker in output');
test(() => {
assert_equals(checked_assert_selection('fo|o|', 'noop', 'foo'),
'You should have at least one focus marker "|" in "fo|o|".');
}, 'multiple focus markers in input');
test(() => {
assert_equals(checked_assert_selection('fo|o', 'noop', '|fo|o'),
'You should have at most one focus marker "|" in "|fo|o".');
}, 'multiple focus markers in output');
test(() => {
assert_equals(checked_assert_selection('^fo|o^', 'noop', 'foo'),
'You should have at most one anchor marker "^" in "^fo|o^".');
}, 'multiple anchor markers in input');
test(() => {
assert_equals(checked_assert_selection('fo|o', 'noop', '^fo|o^'),
'You should have at most one anchor marker "^" in "^fo|o^".');
}, 'multiple anchor markers in output');
test(() => {
assert_equals(checked_assert_selection('^foo', 'noop', 'baz'),
'You should specify caret position in "^foo".');
}, 'anchor marker only in input');
test(() => {
assert_equals(checked_assert_selection('|foo', 'noop', 'b^az'),
'You should have a focus marker "|" in "b^az".');
}, 'anchor marker only in output');
test(() => {
assert_equals(checked_assert_selection('^|foo', 'noop', 'baz'),
'You should have focus marker and should not have anchor marker if and only if selection is a caret in "^|foo".');
}, 'anchor == focus in input');
test(() => {
assert_equals(checked_assert_selection('|foo', 'noop', 'b^|az'),
'You should have focus marker and should not have anchor marker if and only if selection is a caret in "b^|az".');
}, 'anchor == focus in output');
// TODO: It's better to have more powerful diff like
// |CreateUnifiedDiff()| in gtest or "Longest common substring"
test(() => {
assert_equals(checked_assert_selection('foo', 'noop', 'foz'),
`editing/assert_selection.html:8:9)\n` +
`\t expected foz,\n` +
`\t but got foo,\n` +
`\t sameupto fo`);
}, 'Compare result');
selection_test(
'<div contenteditable><p>^test|</p></div>',
'insertHTML <span style="color: green">green</span>',
'<div contenteditable><p><span style="color: green">green|</span></p></div>',
'multiple spaces in function');
test(() => {
assert_selection(
'<div contenteditable>|</div>',
selection => {
selection.setClipboardData('<b>foo</b>');
selection.document.execCommand('paste');
},
'<div contenteditable><b>foo|</b></div>',
'set HTML fragment to clipboard and paste');
assert_selection(
'<div contenteditable>|</div>',
selection => {
selection.setClipboardData('<b>foo</b>');
selection.document.execCommand('pasteAndMatchStyle');
},
'<div contenteditable>foo|</div>',
'set HTML fragment to clipboard and pasteAndMatchStyle');
assert_selection(
'<div contenteditable>|</div>',
selection => {
selection.setClipboardData('<b>foo</b>', 'FOO');
selection.document.execCommand('pasteAndMatchStyle');
},
'<div contenteditable>FOO|</div>',
'set HTML fragment and text to clipboard and pasteAndMatchStyle');
}, 'selection.setClipboardData');
test(() => {
const sample1 = assert_selection_and_return_sample('abc', '', 'abc',
{removeSampleIfSucceeded: false});
assert_equals(sample1.iframe_.parentNode, document.body,
'removeSampleIfSucceeded: false');
const sample2 = assert_selection_and_return_sample('abc', '', 'abc',
{removeSampleIfSucceeded: true});
assert_equals(sample2.iframe_.id, Sample.playgroundId,
'removeSampleIfSucceeded: true: id');
assert_equals(sample2.iframe_.style.display, 'none',
'removeSampleIfSucceeded: true: style.display');
const sample3 = assert_selection_and_return_sample('abc', '', 'abc');
assert_equals(sample3.iframe_.id, Sample.playgroundId,
'with default options: id');
assert_equals(sample3.iframe_.style.display, 'none',
'with default options: id');
const sample4 = assert_selection_and_return_sample('abc', '', 'abc', 'description');
assert_equals(sample4.iframe_.id, Sample.playgroundId,
'with description: id');
assert_equals(sample4.iframe_.style.display, 'none',
'with description: id');
}, 'removeSampleIfSucceeded');
test(() => {
assert_own_property(window, 'internals');
assert_own_property(window, 'eventSender');
assert_selection(
'foo|bar',
() => { assert_equals(internals.textAffinity, 'Downstream'); },
'foo|bar');
assert_selection(
'<div contenteditable style="width: 25px;">foobar</div>',
selection => {
eventSender.dragMode = false;
var document = selection.document;
var div = document.querySelector('div');
eventSender.mouseMoveTo(document.offsetLeft + div.offsetLeft + 20,
document.offsetTop + div.offsetTop + 5);
eventSender.mouseDown();
eventSender.mouseUp();
assert_equals(internals.textAffinity, 'Upstream'); },
'<div contenteditable style="width: 25px;">foo|bar</div>');
}, 'Textaffinity');
test(() => {
assert_own_property(window, 'eventSender');
assert_selection(
[
'<div id="first">one <span id="start"></span>two three</div>',
'<div id="second">four <span id="end"></span>five six</div>',
].join(''),
selection => {
const start = selection.document.getElementById('start');
const end = selection.document.getElementById('end');
eventSender.mouseMoveTo(selection.computeLeft(start),
selection.computeTop(start));
eventSender.mouseDown();
eventSender.mouseMoveTo(selection.computeLeft(end),
selection.computeTop(end));
eventSender.mouseUp();
},
[
'<div id="first">one <span id="start"></span>^two three</div>',
'<div id="second">four |<span id="end"></span>five six</div>',
].join(''));
}, 'computeLeft() and computeTop()');
</script>