| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Include test fixture. |
| GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js', |
| '../../testing/assert_additions.js']); |
| |
| GEN_INCLUDE(['../../testing/mock_feedback.js']); |
| |
| /** |
| * Test fixture for editing tests. |
| * @constructor |
| * @extends {ChromeVoxNextE2ETest} |
| */ |
| function EditingTest() { |
| ChromeVoxNextE2ETest.call(this); |
| window.RoleType = chrome.automation.RoleType; |
| } |
| |
| EditingTest.prototype = { |
| __proto__: ChromeVoxNextE2ETest.prototype, |
| |
| /** |
| * @return {!MockFeedback} |
| */ |
| createMockFeedback: function() { |
| var mockFeedback = new MockFeedback(this.newCallback(), |
| this.newCallback.bind(this)); |
| mockFeedback.install(); |
| return mockFeedback; |
| }, |
| |
| }; |
| |
| var doc = function() {/*! |
| <label for='singleLine'>singleLine</label> |
| <input type='text' id='singleLine' value='Single line field'><br> |
| <label for='textarea'>textArea</label> |
| <textarea id='textarea'> |
| Line 1
 |
| line 2
 |
| line 3 |
| </textarea> |
| */}; |
| |
| TEST_F('EditingTest', 'Focus', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(doc, function(root) { |
| var singleLine = root.find({role: RoleType.TEXT_FIELD, |
| attributes: {name: 'singleLine'}}); |
| var textarea = root.find({role: RoleType.TEXT_FIELD, |
| attributes: {name: 'textArea'}}); |
| singleLine.focus(); |
| mockFeedback |
| .expectSpeech('singleLine', 'Single line field', 'Edit text') |
| .expectBraille('singleLine Single line field ed', |
| {startIndex: 11, endIndex: 11}) |
| .call(textarea.focus.bind(textarea)) |
| .expectSpeech('textArea', |
| 'Line 1\nline 2\nline 3', |
| 'Text area') |
| .expectBraille('textArea Line 1\nline 2\nline 3 mled', |
| {startIndex: 9, endIndex: 9}); |
| |
| mockFeedback.replay(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'Multiline', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(doc, function(root) { |
| var textarea = root.find({role: RoleType.TEXT_FIELD, |
| attributes: {name: 'textArea'}}); |
| textarea.focus(); |
| mockFeedback |
| .expectSpeech('textArea', |
| 'Line 1\nline 2\nline 3', |
| 'Text area') |
| .expectBraille('textArea Line 1\nline 2\nline 3 mled', |
| {startIndex: 9, endIndex: 9}) |
| .call(textarea.setSelection.bind(textarea, 1, 1)) |
| .expectSpeech('i') |
| .expectBraille('Line 1\nmled', |
| {startIndex: 1, endIndex: 1}) |
| .call(textarea.setSelection.bind(textarea, 7, 7)) |
| .expectSpeech('line 2') |
| .expectBraille('line 2\n', |
| {startIndex: 0, endIndex: 0}) |
| .call(textarea.setSelection.bind(textarea, 7, 13)) |
| .expectSpeech('line 2', 'selected') |
| .expectBraille('line 2\n', |
| {startIndex: 0, endIndex: 6}); |
| |
| mockFeedback.replay(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'TextButNoSelectionChange', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree( |
| function() {/*! |
| <h1>Test doc</h1> |
| <input type='text' id='input' value='text1'> |
| <!-- We don't seem to get an event in js when the automation |
| setSelection function is called, so poll for the actual change. --> |
| |
| <script> |
| var timer; |
| var input = document.getElementById('input'); |
| function poll(e) { |
| if (input.selectionStart == 0) |
| return; |
| console.log('TIM' + 'ER'); |
| input.value = 'text2'; |
| window.clearInterval(timer); |
| } |
| timer = window.setInterval(poll, 200); |
| </script> |
| */}, |
| function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| input.focus(); |
| mockFeedback |
| .expectSpeech('text1', 'Edit text') |
| .expectBraille('text1 ed', {startIndex: 0, endIndex: 0}) |
| .call(input.setSelection.bind(input, 5, 5)) |
| .expectBraille('text2 ed', {startIndex: 5, endIndex: 5}) |
| |
| mockFeedback.replay(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextMoveByLine', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go" role="textbox" contenteditable> |
| <h2>hello</h2> |
| <div><br></div> |
| <p>This is a <a href="#test">test</a> of rich text</p> |
| </div> |
| <script> |
| var dir = 'forward'; |
| var line = 0; |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('move', dir, 'line'); |
| if (dir == 'forward') |
| line++; |
| else |
| line--; |
| |
| if (line == 0) |
| dir = 'forward'; |
| if (line == 2) |
| dir = 'backward'; |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var moveByLine = input.doDefault.bind(input); |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(moveByLine) |
| .expectSpeech('\n') |
| .expectBraille('\n') |
| .call(moveByLine) |
| .expectSpeech('This is a ', 'test', 'Link', ' of rich text') |
| .expectBraille('This is a test of rich text') |
| .call(moveByLine) |
| .expectSpeech('\n') |
| .expectBraille('\n') |
| .call(moveByLine) |
| .expectSpeech('hello', 'Heading 2') |
| .expectBraille('hello h2 mled') |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextMoveByCharacter', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go" role="textbox" contenteditable>This <b>is</b> a test.</div> |
| |
| <script> |
| var dir = 'forward'; |
| var char = 0; |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('move', dir, 'character'); |
| if (dir == 'forward') |
| char++; |
| else |
| char--; |
| |
| if (char == 0) |
| dir = 'forward'; |
| if (line == 16) |
| dir = 'backward'; |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var moveByChar = input.doDefault.bind(input); |
| var lineText = 'This is a test. mled'; |
| |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(moveByChar) |
| .expectSpeech('h') |
| .expectBraille(lineText, { startIndex: 1, endIndex: 1 }) |
| .call(moveByChar) |
| .expectSpeech('i') |
| .expectBraille(lineText, { startIndex: 2, endIndex: 2 }) |
| .call(moveByChar) |
| .expectSpeech('s') |
| .expectBraille(lineText, { startIndex: 3, endIndex: 3 }) |
| .call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, { startIndex: 4, endIndex: 4 }) |
| |
| .call(moveByChar) |
| .expectSpeech('i') |
| .expectSpeech('Bold start') |
| .expectBraille(lineText, { startIndex: 5, endIndex: 5 }) |
| |
| .call(moveByChar) |
| .expectSpeech('s') |
| .expectSpeech('Bold end') |
| .expectBraille(lineText, { startIndex: 6, endIndex: 6 }) |
| |
| .call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, { startIndex: 7, endIndex: 7 }) |
| |
| .call(moveByChar) |
| .expectSpeech('a') |
| .expectBraille(lineText, { startIndex: 8, endIndex: 8 }) |
| |
| .call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, { startIndex: 9, endIndex: 9 }) |
| |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| // Tests specifically for cursor workarounds. |
| TEST_F('EditingTest', 'RichTextMoveByCharacterNodeWorkaround', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go" role="textbox" contenteditable>hello <b>world</b></div> |
| |
| <script> |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('move', 'forward', 'character'); |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var moveByChar = input.doDefault.bind(input) |
| var lineText = 'hello world mled'; |
| |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(moveByChar) |
| .expectSpeech('e') |
| .expectBraille(lineText, { startIndex: 1, endIndex: 1 }) |
| .call(moveByChar) |
| .expectSpeech('l') |
| .expectBraille(lineText, { startIndex: 2, endIndex: 2 }) |
| .call(moveByChar) |
| .expectSpeech('l') |
| .expectBraille(lineText, { startIndex: 3, endIndex: 3 }) |
| .call(moveByChar) |
| .expectSpeech('o') |
| .expectBraille(lineText, { startIndex: 4, endIndex: 4 }) |
| .call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, { startIndex: 5, endIndex: 5 }) |
| .call(moveByChar) |
| .expectSpeech('w') |
| .expectBraille(lineText, { startIndex: 6, endIndex: 6 }) |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextMoveByCharacterEndOfLine', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go" role="textbox" contenteditable>Test</div> |
| |
| <script> |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('move', 'forward', 'character'); |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var moveByChar = input.doDefault.bind(input); |
| var lineText = 'Test mled'; |
| |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(moveByChar) |
| .expectSpeech('e') |
| .expectBraille(lineText, { startIndex: 1, endIndex: 1 }) |
| .call(moveByChar) |
| .expectSpeech('s') |
| .expectBraille(lineText, { startIndex: 2, endIndex: 2 }) |
| .call(moveByChar) |
| .expectSpeech('t') |
| .expectBraille(lineText, { startIndex: 3, endIndex: 3 }) |
| .call(moveByChar) |
| .expectSpeech('\n') |
| .expectBraille(lineText, { startIndex: 4, endIndex: 4 }) |
| |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextLinkOutput', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go" role="textbox" contenteditable>a <a href="#">test</a></div> |
| |
| <script> |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('move', 'forward', 'character'); |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var moveByChar = input.doDefault.bind(input); |
| var lineText = 'a test mled'; |
| var lineOnLinkText = 'a test lnk mled'; |
| |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, { startIndex: 1, endIndex: 1 }) |
| .call(moveByChar) |
| .expectSpeech('t') |
| .expectSpeech('Link start') |
| .expectSpeech('Underline start') |
| .expectBraille(lineOnLinkText, { startIndex: 2, endIndex: 2 }) |
| .call(moveByChar) |
| .expectSpeech('e') |
| .expectBraille(lineOnLinkText, { startIndex: 3, endIndex: 3 }) |
| .call(moveByChar) |
| .expectSpeech('s') |
| .expectBraille(lineOnLinkText, { startIndex: 4, endIndex: 4 }) |
| .call(moveByChar) |
| .expectSpeech('t') |
| .expectSpeech('Link end') |
| .expectSpeech('Underline end') |
| .expectBraille(lineOnLinkText, { startIndex: 5, endIndex: 5 }) |
| |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextExtendByCharacter', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go" role="textbox" contenteditable>Te<br>st</div> |
| |
| <script> |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('extend', 'forward', 'character'); |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var moveByChar = input.doDefault.bind(input); |
| |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(moveByChar) |
| .expectSpeech('T', 'selected') |
| .call(moveByChar) |
| .expectSpeech('e', 'added to selection') |
| .call(moveByChar) |
| .expectSpeech('selected') |
| .call(moveByChar) |
| // This gets described by the line logic in EditableLine. |
| .expectSpeech('s', 'selected') |
| .call(moveByChar) |
| .expectSpeech('t', 'added to selection') |
| |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextImageByCharacter', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <p id="go" contenteditable> |
| <img alt="dog"></img> is a <img alt="cat"</img> test |
| </p> |
| <script> |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify('move', 'forward', 'character'); |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.PARAGRAPH}); |
| var moveByChar = input.doDefault.bind(input); |
| |
| this.listenOnce(input, 'focus', function() { |
| var lineText = 'dog is a cat test mled'; |
| var lineOnDogText = 'dog img is a cat test mled'; |
| var lineOnCatText = 'dog is a cat img test'; |
| mockFeedback |
| // This is initial output from focusing the contenteditable (which has |
| // no role). |
| .expectSpeech('dog', 'Image', ' is a ', 'cat', 'Image', ' test') |
| .expectBraille('dog img is a cat img test') |
| .clearPendingOutput() |
| .call(moveByChar) |
| .expectSpeech('dog', 'Image') |
| .expectBraille(lineOnDogText, {startIndex: 1, endIndex: 1}) |
| .call(moveByChar) |
| .expectSpeech('og ') |
| .expectBraille(lineText, {startIndex: 4, endIndex: 4}) |
| .call(moveByChar) |
| .expectSpeech('s') |
| .expectBraille(lineText, {startIndex: 5, endIndex: 5}) |
| .call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, {startIndex: 6, endIndex: 6}) |
| .call(moveByChar) |
| .expectSpeech('a') |
| .expectBraille(lineText, {startIndex: 7, endIndex: 7}) |
| .call(moveByChar) |
| .expectSpeech(' ') |
| .expectBraille(lineText, {startIndex: 8, endIndex: 8}) |
| .call(moveByChar) |
| |
| // This is technically wrong because we're actually over the entire |
| // image. This is broken because of bad node offsets. |
| .expectSpeech('c') |
| .expectBraille(lineOnCatText, {startIndex: 9, endIndex: 9}) |
| |
| // Unfortunately, the node offset being wrong here means there's no |
| // output for the next character move. Fix Once node offsets get fixed |
| // in Blink. |
| |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'RichTextSelectByLine', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <p id="go" contenteditable> |
| first line<br> |
| second line<br> |
| third line<br> |
| </p> |
| <script> |
| var commands = [ |
| ['extend', 'forward', 'character'], |
| ['extend', 'forward', 'character'], |
| |
| ['extend', 'forward', 'line'], |
| ['extend', 'forward', 'line'], |
| |
| ['extend', 'backward', 'line'], |
| ['extend', 'backward', 'line'], |
| |
| ['extend', 'forward', 'documentBoundary'], |
| |
| ['move', 'forward', 'character'], |
| ['move', 'backward', 'character'], |
| ['move', 'backward', 'character'], |
| |
| ['extend', 'backward', 'line'], |
| ['extend', 'backward', 'line'], |
| |
| ['extend', 'forward', 'line'], |
| ]; |
| document.getElementById('go').addEventListener('click', function() { |
| var sel = getSelection(); |
| sel.modify.apply(sel, commands.shift()); |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.PARAGRAPH}); |
| var move = input.doDefault.bind(input); |
| |
| this.listenOnce(input, 'focus', function() { |
| |
| // By character. |
| mockFeedback.call(move) |
| .expectSpeech('f', 'selected') |
| .expectBraille('first line\nmled', {startIndex: 0, endIndex: 1}) |
| .call(move) |
| .expectSpeech('i', 'added to selection') |
| .expectBraille('first line\nmled', {startIndex: 0, endIndex: 2}) |
| |
| // Forward selection. |
| |
| // Growing. |
| |
| // By line (notice the partial selections from the first and second |
| // lines). |
| .call(move) |
| .expectSpeech('rst line \n se', 'selected') |
| .expectBraille('second line\n', {startIndex: 0, endIndex: 2}) |
| |
| .call(move) |
| .expectSpeech('cond line \n th', 'selected') |
| .expectBraille('third line\n', {startIndex: 0, endIndex: 2}) |
| |
| // Shrinking. |
| .call(move) |
| .expectSpeech('cond line \n th', 'unselected') |
| .expectBraille('second line\n', {startIndex: 0, endIndex: 2}) |
| |
| .call(move) |
| .expectSpeech('fi', 'selected') |
| .expectBraille('first line\nmled', {startIndex: 0, endIndex: 2}) |
| |
| // Document boundary. |
| .call(move) |
| .expectSpeech('rst line \n second line \n third line \n \n', |
| 'selected') |
| .expectBraille('third line\n', {startIndex: 0, endIndex: 10}) |
| |
| // The script repositions the caret to the 'n' of the third line. |
| .call(move) |
| .expectSpeech('third line') |
| .expectBraille('third line\n', {startIndex: 10, endIndex: 10}) |
| .call(move) |
| .expectSpeech('e') |
| .expectBraille('third line\n', {startIndex: 9, endIndex: 9}) |
| .call(move) |
| .expectSpeech('n') |
| .expectBraille('third line\n', {startIndex: 8, endIndex: 8}) |
| |
| // Backward selection. |
| |
| // Growing. |
| .call(move) |
| .expectSpeech(' line \n third li', 'selected') |
| .expectBraille('second line\n', {startIndex: 6, endIndex: 12}) |
| |
| .call(move) |
| .expectSpeech('e \n second', 'selected') |
| .expectBraille('first line\n', {startIndex: 9, endIndex: 11}) |
| |
| // Shrinking. |
| .call(move) |
| .expectSpeech('e \n second', 'unselected') |
| .expectBraille('second line\n', {startIndex: 6, endIndex: 12}) |
| |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'EditableLineOneStaticText', function() { |
| this.runWithLoadedTree(function() {/*! |
| <p contenteditable style="word-spacing:100000px">this is a test</p> |
| */}, function(root) { |
| var staticText = root.find({role: RoleType.STATIC_TEXT}); |
| |
| var e = new editing.EditableLine(staticText, 0, staticText, 0); |
| assertEquals('this ', e.text); |
| |
| assertEquals(0, e.startOffset); |
| assertEquals(0, e.endOffset); |
| assertEquals(0, e.localStartOffset); |
| assertEquals(0, e.localEndOffset); |
| |
| assertEquals(0, e.containerStartOffset); |
| assertEquals(4, e.containerEndOffset); |
| |
| e = new editing.EditableLine(staticText, 1, staticText, 1); |
| assertEquals('this ', e.text); |
| |
| assertEquals(1, e.startOffset); |
| assertEquals(1, e.endOffset); |
| assertEquals(1, e.localStartOffset); |
| assertEquals(1, e.localEndOffset); |
| |
| assertEquals(0, e.containerStartOffset); |
| assertEquals(4, e.containerEndOffset); |
| |
| e = new editing.EditableLine(staticText, 5, staticText, 5); |
| assertEquals('is ', e.text); |
| |
| assertEquals(0, e.startOffset); |
| assertEquals(0, e.endOffset); |
| assertEquals(5, e.localStartOffset); |
| assertEquals(5, e.localEndOffset); |
| |
| assertEquals(0, e.containerStartOffset); |
| assertEquals(2, e.containerEndOffset); |
| |
| e = new editing.EditableLine(staticText, 7, staticText, 7); |
| assertEquals('is ', e.text); |
| |
| assertEquals(2, e.startOffset); |
| assertEquals(2, e.endOffset); |
| assertEquals(7, e.localStartOffset); |
| assertEquals(7, e.localEndOffset); |
| |
| assertEquals(0, e.containerStartOffset); |
| assertEquals(2, e.containerEndOffset); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'EditableLineTwoStaticTexts', function() { |
| this.runWithLoadedTree(function() {/*! |
| <p contenteditable>hello <b>world</b></p> |
| */}, function(root) { |
| var text = root.find({role: RoleType.STATIC_TEXT}); |
| var bold = text.nextSibling; |
| |
| var e = new editing.EditableLine(text, 0, text, 0); |
| assertEquals('hello world', e.text); |
| |
| assertEquals(0, e.startOffset); |
| assertEquals(0, e.endOffset); |
| assertEquals(0, e.localStartOffset); |
| assertEquals(0, e.localEndOffset); |
| |
| assertEquals(0, e.containerStartOffset); |
| assertEquals(5, e.containerEndOffset); |
| |
| e = new editing.EditableLine(text, 5, text, 5); |
| assertEquals('hello world', e.text); |
| |
| assertEquals(5, e.startOffset); |
| assertEquals(5, e.endOffset); |
| assertEquals(5, e.localStartOffset); |
| assertEquals(5, e.localEndOffset); |
| |
| assertEquals(0, e.containerStartOffset); |
| assertEquals(5, e.containerEndOffset); |
| |
| e = new editing.EditableLine(bold, 0, bold, 0); |
| assertEquals('hello world', e.text); |
| |
| assertEquals(6, e.startOffset); |
| assertEquals(6, e.endOffset); |
| assertEquals(0, e.localStartOffset); |
| assertEquals(0, e.localEndOffset); |
| |
| assertEquals(6, e.containerStartOffset); |
| assertEquals(10, e.containerEndOffset); |
| |
| e = new editing.EditableLine(bold, 4, bold, 4); |
| assertEquals('hello world', e.text); |
| |
| assertEquals(10, e.startOffset); |
| assertEquals(10, e.endOffset); |
| assertEquals(4, e.localStartOffset); |
| assertEquals(4, e.localEndOffset); |
| |
| assertEquals(6, e.containerStartOffset); |
| assertEquals(10, e.containerEndOffset); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'EditableLineEquality', function() { |
| this.runWithLoadedTree(function() {/*! |
| <div contenteditable role="textbox"> |
| <p style="word-spacing:100000px">this is a test</p> |
| <p>hello <b>world</b></p> |
| </div> |
| */}, function(root) { |
| var thisIsATest = root.findAll({role: RoleType.PARAGRAPH})[0].firstChild; |
| var hello = root.findAll({role: RoleType.PARAGRAPH})[1].firstChild; |
| var world = root.findAll({role: RoleType.PARAGRAPH})[1].lastChild; |
| |
| // The same position -- sanity check. |
| var e1 = new editing.EditableLine(thisIsATest, 0, thisIsATest, 0); |
| assertEquals('this ', e1.text); |
| assertTrue(e1.isSameLine(e1)); |
| |
| // Offset into the same soft line. |
| var e2 = new editing.EditableLine(thisIsATest, 1, thisIsATest, 1); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // Boundary. |
| e2 = new editing.EditableLine(thisIsATest, 4, thisIsATest, 4); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // Offsets into different soft lines. |
| e2 = new editing.EditableLine(thisIsATest, 5, thisIsATest, 5); |
| assertEquals('is ', e2.text); |
| assertFalse(e1.isSameLine(e2)); |
| |
| // Sanity check; second soft line. |
| assertTrue(e2.isSameLine(e2)); |
| |
| // Different offsets into second soft line. |
| e1 = new editing.EditableLine(thisIsATest, 6, thisIsATest, 6); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // Boundary. |
| e1 = new editing.EditableLine(thisIsATest, 7, thisIsATest, 7); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // Third line. |
| e1 = new editing.EditableLine(thisIsATest, 8, thisIsATest, 8); |
| assertEquals('a ', e1.text); |
| assertFalse(e1.isSameLine(e2)); |
| |
| // Last line. |
| e2 = new editing.EditableLine(thisIsATest, 10, thisIsATest, 10); |
| assertEquals('test', e2.text); |
| assertFalse(e1.isSameLine(e2)); |
| |
| // Boundary. |
| e1 = new editing.EditableLine(thisIsATest, 13, thisIsATest, 13); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // Cross into new paragraph. |
| e2 = new editing.EditableLine(hello, 0, hello, 0); |
| assertEquals('hello world', e2.text); |
| assertFalse(e1.isSameLine(e2)); |
| |
| // On same node, with multi-static text line. |
| e1 = new editing.EditableLine(hello, 1, hello, 1); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // On same node, with multi-static text line; boundary. |
| e1 = new editing.EditableLine(hello, 5, hello, 5); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // On different node, with multi-static text line. |
| e1 = new editing.EditableLine(world, 1, world, 1); |
| assertTrue(e1.isSameLine(e2)); |
| |
| // Another mix of lines. |
| e2 = new editing.EditableLine(thisIsATest, 9, thisIsATest, 9); |
| assertFalse(e1.isSameLine(e2)); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'EditableLineStrictEquality', function() { |
| this.runWithLoadedTree(function() {/*! |
| <div contenteditable role="textbox"> |
| <p style="word-spacing:100000px">this is a test</p> |
| <p>hello <b>world</b></p> |
| </div> |
| */}, function(root) { |
| var thisIsATest = root.findAll({role: RoleType.PARAGRAPH})[0].firstChild; |
| var hello = root.findAll({role: RoleType.PARAGRAPH})[1].firstChild; |
| var world = root.findAll({role: RoleType.PARAGRAPH})[1].lastChild; |
| |
| // The same position -- sanity check. |
| var e1 = new editing.EditableLine(thisIsATest, 0, thisIsATest, 0); |
| assertEquals('this ', e1.text); |
| assertTrue(e1.isSameLineAndSelection(e1)); |
| |
| // Offset into the same soft line. |
| var e2 = new editing.EditableLine(thisIsATest, 1, thisIsATest, 1); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // Boundary. |
| e2 = new editing.EditableLine(thisIsATest, 4, thisIsATest, 4); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // Offsets into different soft lines. |
| e2 = new editing.EditableLine(thisIsATest, 5, thisIsATest, 5); |
| assertEquals('is ', e2.text); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // Sanity check; second soft line. |
| assertTrue(e2.isSameLineAndSelection(e2)); |
| |
| // Different offsets into second soft line. |
| e1 = new editing.EditableLine(thisIsATest, 6, thisIsATest, 6); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // Boundary. |
| e1 = new editing.EditableLine(thisIsATest, 7, thisIsATest, 7); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // Cross into new paragraph. |
| e2 = new editing.EditableLine(hello, 0, hello, 0); |
| assertEquals('hello world', e2.text); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // On same node, with multi-static text line. |
| e1 = new editing.EditableLine(hello, 1, hello, 1); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| |
| // On same node, with multi-static text line; boundary. |
| e1 = new editing.EditableLine(hello, 5, hello, 5); |
| assertFalse(e1.isSameLineAndSelection(e2)); |
| }); |
| }); |
| |
| TEST_F('EditingTest', 'EditableLineBaseLineAnchorOrFocus', function() { |
| this.runWithLoadedTree(function() {/*! |
| <div contenteditable role="textbox"> |
| <p style="word-spacing:100000px">this is a test</p> |
| <p>hello <b>world</b></p> |
| </div> |
| */}, function(root) { |
| var thisIsATest = root.findAll({role: RoleType.PARAGRAPH})[0].firstChild; |
| var hello = root.findAll({role: RoleType.PARAGRAPH})[1].firstChild; |
| var world = root.findAll({role: RoleType.PARAGRAPH})[1].lastChild; |
| |
| // The same position -- sanity check. |
| var e1 = new editing.EditableLine(thisIsATest, 0, thisIsATest, 0, true); |
| assertEquals('this ', e1.text); |
| |
| // Offsets into different soft lines; base on focus (default). |
| e1 = new editing.EditableLine(thisIsATest, 0, thisIsATest, 6); |
| assertEquals('is ', e1.text); |
| // Notice that the offset is truncated at the beginning of the line. |
| assertEquals(0, e1.startOffset); |
| // Notice that the end offset is properly retained. |
| assertEquals(1, e1.endOffset); |
| |
| // Offsets into different soft lines; base on anchor. |
| e1 = new editing.EditableLine(thisIsATest, 0, thisIsATest, 6, true); |
| assertEquals('this ', e1.text); |
| assertEquals(0, e1.startOffset); |
| // Notice that the end offset is truncated up to the end of line. |
| assertEquals(5, e1.endOffset); |
| |
| // Across paragraph selection with base line on focus. |
| e1 = new editing.EditableLine(thisIsATest, 5, hello, 2); |
| assertEquals('hello world', e1.text); |
| assertEquals(0, e1.startOffset); |
| assertEquals(2, e1.endOffset); |
| |
| // Across paragraph selection with base line on anchor. |
| e1 = new editing.EditableLine(thisIsATest, 5, hello, 2, true); |
| assertEquals('is ', e1.text); |
| assertEquals(0, e1.startOffset); |
| assertEquals(3, e1.endOffset); |
| }) |
| }); |
| |
| TEST_F('EditingTest', 'IsValidLine', function() { |
| this.runWithLoadedTree(function() {/*! |
| <div contenteditable role="textbox"> |
| <p style="word-spacing:100000px">this is a test</p> |
| <p>end</p> |
| </div> |
| */}, function(root) { |
| // Each word is on its own line, but parented by a static text. |
| var text, endText; |
| [text, endText] = root.findAll({role: RoleType.STATIC_TEXT}); |
| |
| // The EditableLine object automatically adjusts to surround the line no |
| // matter what the input is. |
| var line = new editing.EditableLine(text, 0, text, 0); |
| assertTrue(line.isValidLine()); |
| |
| // During the course of editing operations, this line may become |
| // invalidted. For example, if a user starts typing into the line, the |
| // bounding nodes might change. |
| // Simulate that here by modifying private state. |
| |
| // This puts the line at offset 8 (|this is a|). |
| line.localLineStartContainerOffset_ = 0; |
| line.localLineEndContainerOffset_ = 8; |
| assertFalse(line.isValidLine()); |
| |
| // This puts us in the first line. |
| line.localLineStartContainerOffset_ = 0; |
| line.localLineEndContainerOffset_ = 4; |
| assertTrue(line.isValidLine()); |
| |
| // This is still fine (for our purposes) because the line is still intact. |
| line.localLineStartContainerOffset_ = 0; |
| line.localLineEndContainerOffset_ = 2; |
| assertTrue(line.isValidLine()); |
| |
| // The line has changed. The end has been moved for some reason. |
| line.lineEndContainer_ = endText; |
| assertFalse(line.isValidLine()); |
| }) |
| }); |
| |
| TEST_F('EditingTest', 'TelTrimsWhitespace', function() { |
| var mockFeedback = this.createMockFeedback(); |
| this.runWithLoadedTree(function() {/*! |
| <div id="go"></div> |
| <input id="input" type="tel"></input> |
| <script> |
| var data = [ |
| '6 ', |
| '60 ', |
| '601 ', |
| '60 ' |
| ]; |
| var go = document.getElementById('go'); |
| var input = document.getElementById('input'); |
| var index = 0; |
| go.addEventListener('click', function() { |
| input.value = data[index]; |
| index++; |
| input.selectionStart = index; |
| input.selectionEnd = index; |
| }, true); |
| </script> |
| */}, function(root) { |
| var input = root.find({role: RoleType.TEXT_FIELD}); |
| var go = root.find({role: RoleType.GENERIC_CONTAINER}); |
| var enterKey = go.doDefault.bind(go); |
| this.listenOnce(input, 'focus', function() { |
| mockFeedback.call(enterKey) |
| .expectSpeech('6') |
| .call(enterKey) |
| .expectSpeech('0') |
| .call(enterKey) |
| .expectSpeech('1') |
| |
| // Deletion. |
| .call(enterKey) |
| .expectSpeech('1') |
| .replay(); |
| }); |
| input.focus(); |
| }); |
| }); |