// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
GEN_INCLUDE(['../select_to_speak/select_to_speak_e2e_test_base.js']);
/**
* Test fixture for paragraph_utils.js.
*/
SelectToSpeakParagraphUnitTest = class extends SelectToSpeakE2ETest {
/** @override */
async setUpDeferred() {
await super.setUpDeferred();
await importModule('ParagraphUtils', '/common/paragraph_utils.js');
}
};
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'GetFirstBlockAncestor', function() {
const root = {role: 'rootWebArea'};
const paragraph = {role: 'paragraph', parent: root, root};
const text1 =
{role: 'staticText', parent: paragraph, display: 'block', root};
const text2 = {role: 'staticText', parent: root, root};
const text3 = {role: 'inlineTextBox', parent: text1, root};
const div =
{role: 'genericContainer', parent: paragraph, display: 'block', root};
const text4 = {role: 'staticText', parent: div, root};
assertEquals(paragraph, ParagraphUtils.getFirstBlockAncestor(text1));
assertEquals(root, ParagraphUtils.getFirstBlockAncestor(text2));
assertEquals(paragraph, ParagraphUtils.getFirstBlockAncestor(text3));
assertEquals(div, ParagraphUtils.getFirstBlockAncestor(text4));
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'SVGRootIsBlockAncestor', function() {
const root = {role: 'rootWebArea'};
const svgRoot = {role: 'svgRoot', parent: root, root};
const text1 = {role: 'staticText', parent: svgRoot, root};
const inline1 = {role: 'inlineTextBox', parent: text1, root};
const text2 = {role: 'staticText', parent: svgRoot, root};
const inline2 = {role: 'inlineTextBox', parent: text2, root};
assertEquals(svgRoot, ParagraphUtils.getFirstBlockAncestor(text1));
assertEquals(svgRoot, ParagraphUtils.getFirstBlockAncestor(inline1));
assertEquals(svgRoot, ParagraphUtils.getFirstBlockAncestor(inline2));
assertTrue(ParagraphUtils.inSameParagraph(inline1, inline2));
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'ParagraphInSVGIsBlock', function() {
// This represents how Google Docs renders Canvas accessibility as of
// October 24 2022.
const root = {role: 'rootWebArea'};
const svgRoot = {role: 'svgRoot', parent: root, root};
const group1 = {role: 'paragraph', parent: svgRoot, root};
const text1 = {role: 'graphicsSymbol', parent: group1, root};
const text2 = {role: 'graphicsSymbol', parent: group1, root};
const group2 = {role: 'paragraph', parent: svgRoot, root};
const text3 = {role: 'graphicsSymbol', parent: group2, root};
assertEquals(group1, ParagraphUtils.getFirstBlockAncestor(text1));
assertEquals(group1, ParagraphUtils.getFirstBlockAncestor(text2));
assertEquals(group2, ParagraphUtils.getFirstBlockAncestor(text3));
assertTrue(ParagraphUtils.inSameParagraph(text1, text2));
assertFalse(ParagraphUtils.inSameParagraph(text1, text3));
});
AX_TEST_F('SelectToSpeakParagraphUnitTest', 'InSameParagraph', function() {
const root = {role: 'rootWebArea'};
const paragraph1 =
{role: 'paragraph', display: 'block', parent: 'rootWebArea', root};
const text1 = {role: 'staticText', parent: paragraph1, root};
const text2 = {role: 'staticText', parent: paragraph1, root};
const paragraph2 =
{role: 'paragraph', display: 'block', parent: 'rootWebArea', root};
const text3 = {role: 'staticText', parent: paragraph2, root};
assertTrue(ParagraphUtils.inSameParagraph(text1, text2));
assertFalse(ParagraphUtils.inSameParagraph(text1, text3));
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BlockDivBreaksSameParagraph',
function() {
const root = {role: 'rootWebArea'};
const paragraph1 =
{role: 'paragraph', display: 'block', parent: 'rootWebArea', root};
const text1 = {role: 'staticText', parent: paragraph1, root};
const text2 = {role: 'image', parent: paragraph1, display: 'block', root};
const text3 =
{role: 'image', parent: paragraph1, display: 'inline', root};
const text4 = {role: 'staticText', parent: paragraph1, root};
assertFalse(ParagraphUtils.inSameParagraph(text1, text2));
assertFalse(ParagraphUtils.inSameParagraph(text2, text3));
assertTrue(ParagraphUtils.inSameParagraph(text3, text4));
});
AX_TEST_F('SelectToSpeakParagraphUnitTest', 'IsWhitespace', function() {
assertTrue(ParagraphUtils.isWhitespace(''));
assertTrue(ParagraphUtils.isWhitespace(' '));
assertTrue(ParagraphUtils.isWhitespace(' \n \t '));
assertTrue(ParagraphUtils.isWhitespace());
assertFalse(ParagraphUtils.isWhitespace('cats'));
assertFalse(ParagraphUtils.isWhitespace(' cats '));
});
AX_TEST_F('SelectToSpeakParagraphUnitTest', 'GetNodeName', function() {
assertEquals(
ParagraphUtils.getNodeName({role: 'staticText', name: 'cat'}), 'cat');
assertEquals(
ParagraphUtils.getNodeName({role: 'inlineTextBox', name: 'cat'}), 'cat');
assertEquals(ParagraphUtils.getNodeName({name: 'cat'}), 'cat');
assertEquals(
ParagraphUtils.getNodeName({role: 'radioButton', name: 'cat'}),
'cat unselected');
assertEquals(
ParagraphUtils.getNodeName({role: 'checkBox', name: 'cat'}),
'cat unchecked');
assertEquals(
ParagraphUtils.getNodeName(
{role: 'checkBox', checked: 'true', name: 'cat'}),
'cat checked');
assertEquals(ParagraphUtils.getNodeName({role: 'radioButton'}), 'unselected');
assertEquals(ParagraphUtils.getNodeName({role: 'checkBox'}), 'unchecked');
assertEquals(
ParagraphUtils.getNodeName({role: 'radioButton', checked: 'true'}),
'selected');
assertEquals(
ParagraphUtils.getNodeName({role: 'checkBox', checked: 'true'}),
'checked');
assertEquals(
ParagraphUtils.getNodeName(
{role: 'radioButton', checked: 'true', name: 'cat'}),
'cat selected');
assertEquals(
ParagraphUtils.getNodeName({role: 'checkBox', checked: 'mixed'}),
'partially checked');
assertEquals(
ParagraphUtils.getNodeName({role: 'radioButton', checked: 'mixed'}),
'partially selected');
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'GetStartCharIndexInParent', function() {
const staticText = {
role: 'staticText',
name: 'My name is Bond, James Bond',
};
const inline1 = {
role: 'inlineTextBox',
name: 'My name is ',
indexInParent: 0,
parent: staticText,
};
const inline2 = {
role: 'inlineTextBox',
name: 'Bond, ',
indexInParent: 1,
parent: staticText,
};
const inline3 = {
role: 'inlineTextBox',
name: 'James Bond',
indexInParent: 2,
parent: staticText,
};
staticText.children = [inline1, inline2, inline3];
assertEquals(ParagraphUtils.getStartCharIndexInParent(inline1), 0);
assertEquals(ParagraphUtils.getStartCharIndexInParent(inline2), 11);
assertEquals(ParagraphUtils.getStartCharIndexInParent(inline3), 17);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'FindInlineTextNodeByCharIndex',
function() {
const staticText = {
role: 'staticText',
name: 'My name is Bond, James Bond',
};
const inline1 = {role: 'inlineTextBox', name: 'My name is '};
const inline2 = {role: 'inlineTextBox', name: 'Bond, '};
const inline3 = {role: 'inlineTextBox', name: 'James Bond'};
staticText.children = [inline1, inline2, inline3];
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 0),
inline1);
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 10),
inline1);
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 11),
inline2);
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 16),
inline2);
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 17),
inline3);
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 50),
inline3);
staticText.children = [];
assertEquals(
ParagraphUtils.findInlineTextNodeByCharacterIndex(staticText, 10),
null);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'FindInlineTextNodeIndexByCharIndex',
function() {
const staticText = {
role: 'staticText',
name: 'My name is Bond, James Bond',
};
const inline1 = {role: 'inlineTextBox', name: 'My name is '};
const inline2 = {role: 'inlineTextBox', name: 'Bond, '};
const inline3 = {role: 'inlineTextBox', name: 'James Bond'};
staticText.children = [inline1, inline2, inline3];
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(staticText, 0),
0);
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticText, 10),
0);
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticText, 11),
1);
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticText, 16),
1);
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticText, 17),
2);
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticText, 50),
2);
staticText.children = [];
assertEquals(
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticText, 10),
-1);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupStopsAtNewParagraph',
function() {
const root = {role: 'rootWebArea'};
const paragraph1 =
{role: 'paragraph', display: 'block', parent: root, root};
const text1 =
{role: 'staticText', parent: paragraph1, name: 'text1', root};
const text2 =
{role: 'staticText', parent: paragraph1, name: 'text2', root};
const paragraph2 =
{role: 'paragraph', display: 'block', parent: root, root};
const text3 =
{role: 'staticText', parent: paragraph2, name: 'text3', root};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, text3], 0, {splitOnLanguage: false});
assertEquals('text1 text2 ', result.text);
assertEquals(1, result.endIndex);
assertEquals(2, result.nodes.length);
assertEquals(0, result.nodes[0].startChar);
assertEquals(text1, result.nodes[0].node);
assertEquals(6, result.nodes[1].startChar);
assertEquals(text2, result.nodes[1].node);
assertEquals(paragraph1, result.blockParent);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupAcrossParagraphs',
function() {
const root = {role: 'rootWebArea'};
const paragraph1 =
{role: 'paragraph', display: 'block', parent: root, root};
const text1 =
{role: 'staticText', parent: paragraph1, name: 'text1', root};
const text2 =
{role: 'staticText', parent: paragraph1, name: 'text2', root};
const paragraph2 =
{role: 'paragraph', display: 'block', parent: root, root};
const text3 =
{role: 'staticText', parent: paragraph2, name: 'text3', root};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, text3], 0, {splitOnParagraph: false});
assertEquals('text1 text2 text3 ', result.text);
assertEquals(2, result.endIndex);
assertEquals(3, result.nodes.length);
assertEquals(0, result.nodes[0].startChar);
assertEquals(text1, result.nodes[0].node);
assertEquals(6, result.nodes[1].startChar);
assertEquals(text2, result.nodes[1].node);
assertEquals(12, result.nodes[2].startChar);
assertEquals(text3, result.nodes[2].node);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupStopsAtLanguageBoundary',
function() {
const splitOnLanguage = true;
// When the detectedLanguage changes from en-US to fr-FR we expect to
// break the NodeGroup.
const root = {role: 'rootWebArea'};
const text1 = {
role: 'staticText',
parent: root,
name: 'text1',
root,
detectedLanguage: 'en-US',
};
const text2 = {
role: 'staticText',
parent: root,
name: 'text2',
root,
detectedLanguage: 'en-US',
};
const text3 = {
role: 'staticText',
parent: root,
name: 'text3',
root,
detectedLanguage: 'fr-FR',
};
const result1 = ParagraphUtils.buildNodeGroup(
[text1, text2, text3], 0, {splitOnLanguage});
assertEquals('text1 text2 ', result1.text);
assertEquals(1, result1.endIndex);
assertEquals(2, result1.nodes.length);
assertEquals(0, result1.nodes[0].startChar);
assertEquals(text1, result1.nodes[0].node);
assertEquals(6, result1.nodes[1].startChar);
assertEquals(text2, result1.nodes[1].node);
assertEquals('en-US', result1.detectedLanguage);
const result2 = ParagraphUtils.buildNodeGroup(
[text1, text2, text3], 2, {splitOnLanguage});
assertEquals('text3 ', result2.text);
assertEquals(2, result2.endIndex);
assertEquals(1, result2.nodes.length);
assertEquals(0, result2.nodes[0].startChar);
assertEquals(text3, result2.nodes[0].node);
assertEquals('fr-FR', result2.detectedLanguage);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest',
'BuildNodeGroupStopsAtLanguageBoundaryAllUndefined', function() {
const splitOnLanguage = true;
// If no detectedLanguage is defined then we should not split at all....
const root = {role: 'rootWebArea'};
const text1 = {role: 'staticText', parent: root, name: 'text1', root};
const text2 = {role: 'staticText', parent: root, name: 'text2', root};
const text3 = {role: 'staticText', parent: root, name: 'text3', root};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, text3], 0, {splitOnLanguage});
assertEquals('text1 text2 text3 ', result.text);
assertEquals(2, result.endIndex);
assertEquals(3, result.nodes.length);
assertEquals(0, result.nodes[0].startChar);
assertEquals(text1, result.nodes[0].node);
assertEquals(6, result.nodes[1].startChar);
assertEquals(text2, result.nodes[1].node);
assertEquals(12, result.nodes[2].startChar);
assertEquals(text3, result.nodes[2].node);
assertEquals(undefined, result.detectedLanguage);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest',
'BuildNodeGroupStopsAtLanguageBoundaryLastNode', function() {
const splitOnLanguage = true;
// our NodeGroup should get the first defined detectedLanguage
const root = {role: 'rootWebArea'};
const text1 = {role: 'staticText', parent: root, name: 'text1', root};
const text2 = {role: 'staticText', parent: root, name: 'text2', root};
const text3 = {
role: 'staticText',
parent: root,
name: 'text3',
root,
detectedLanguage: 'fr-FR',
};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, text3], 0, {splitOnLanguage});
assertEquals('text1 text2 text3 ', result.text);
assertEquals(2, result.endIndex);
assertEquals(3, result.nodes.length);
assertEquals(0, result.nodes[0].startChar);
assertEquals(text1, result.nodes[0].node);
assertEquals(6, result.nodes[1].startChar);
assertEquals(text2, result.nodes[1].node);
assertEquals(12, result.nodes[2].startChar);
assertEquals(text3, result.nodes[2].node);
assertEquals('fr-FR', result.detectedLanguage);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupSplitOnLanguageDisabled',
function() {
// Test behaviour with splitOnLanguage disabled. This is to show that we
// haven't introduced an obvious regression.
const splitOnLanguage = false;
const root = {role: 'rootWebArea'};
const text1 = {role: 'staticText', parent: root, name: 'text1', root};
const text2 = {
role: 'staticText',
parent: root,
name: 'text2',
root,
detectedLanguage: 'en-US',
};
const text3 = {role: 'staticText', parent: root, name: 'text3', root};
const text4 = {
role: 'staticText',
parent: root,
name: 'text4',
root,
detectedLanguage: 'fr-FR',
};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, text3, text4], 0, {splitOnLanguage});
assertEquals('text1 text2 text3 text4 ', result.text);
assertEquals(3, result.endIndex);
assertEquals(4, result.nodes.length);
assertEquals(text1, result.nodes[0].node);
assertEquals(text4, result.nodes[3].node);
assertEquals(undefined, result.detectedLanguage);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest',
'BuildNodeGroupStopsAtLanguageBoundarySomeUndefined', function() {
const splitOnLanguage = true;
// We never want to break up a NodeGroup based on an undefined
// detectedLanguage, instead we allow an undefined detectedLanguage to
// match any other language. The language for the NodeGroup will be
// determined by the first defined detectedLanguage.
const root = {role: 'rootWebArea'};
const text1 = {role: 'staticText', parent: root, name: 'text1', root};
const text2 = {
role: 'staticText',
parent: root,
name: 'text2',
root,
detectedLanguage: 'en-US',
};
const text3 = {role: 'staticText', parent: root, name: 'text3', root};
const text4 = {
role: 'staticText',
parent: root,
name: 'text4',
root,
detectedLanguage: 'fr-FR',
};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, text3, text4], 0, {splitOnLanguage});
assertEquals('text1 text2 text3 ', result.text);
assertEquals(2, result.endIndex);
assertEquals(3, result.nodes.length);
assertEquals(0, result.nodes[0].startChar);
assertEquals(text1, result.nodes[0].node);
assertEquals(6, result.nodes[1].startChar);
assertEquals(text2, result.nodes[1].node);
assertEquals(12, result.nodes[2].startChar);
assertEquals(text3, result.nodes[2].node);
assertEquals('en-US', result.detectedLanguage);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupIncludesLinks',
function() {
const root = {role: 'rootWebArea'};
const paragraph1 =
{role: 'paragraph', display: 'block', parent: root, root};
const text1 =
{role: 'staticText', parent: paragraph1, name: 'text1', root};
// Whitespace-only nodes should be ignored.
const text2 = {role: 'staticText', parent: paragraph1, name: '\n', root};
const link = {role: 'link', parent: paragraph1, root};
const linkText =
{role: 'staticText', parent: link, name: 'linkText', root};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2, linkText], 0, {splitOnLanguage: false});
assertEquals('text1 linkText ', result.text);
assertEquals(2, result.endIndex);
assertEquals(2, result.nodes.length);
assertEquals(0, result.nodes[0].startChar);
assertEquals(text1, result.nodes[0].node);
assertEquals(6, result.nodes[1].startChar);
assertEquals(linkText, result.nodes[1].node);
assertEquals(paragraph1, result.blockParent);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupNativeTextBox',
function() {
const root = {role: 'desktop'};
const parent = {role: 'pane', parent: root, root};
const searchBar = {
role: 'textField',
name: 'Address and search bar',
value: 'http://www.google.com',
children: [],
};
let result = ParagraphUtils.buildNodeGroup([searchBar], 0);
assertEquals('http://www.google.com ', result.text);
// If there is no value, it should use the name.
searchBar.value = '';
result = ParagraphUtils.buildNodeGroup(
[searchBar], 0, {splitOnLanguage: false});
assertEquals('Address and search bar ', result.text);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupWithSvg', function() {
const root = {role: 'rootWebArea'};
const svgRoot = {role: 'svgRoot', parent: root, root};
const text1 = {role: 'staticText', parent: svgRoot, root, name: 'Hello,'};
const inline1 =
{role: 'inlineTextBox', parent: text1, root, name: 'Hello,'};
const text2 = {role: 'staticText', parent: svgRoot, root, name: 'world!'};
const inline2 =
{role: 'inlineTextBox', parent: text2, root, name: 'world!'};
const result = ParagraphUtils.buildNodeGroup(
[inline1, inline2], 0, {splitOnLanguage: false});
assertEquals('Hello, world! ', result.text);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildNodeGroupWithAndroidClickable',
function() {
const root = {role: 'application'};
const listRoot = {role: 'list', parent: root, root};
const clickableContainer =
{role: 'genericContainer', parent: listRoot, root, clickable: true};
const text1 =
{role: 'staticText', parent: clickableContainer, root, name: 'text1'};
const text2 =
{role: 'staticText', parent: clickableContainer, root, name: 'text2'};
const result = ParagraphUtils.buildNodeGroup(
[text1, text2], 0, {splitOnLanguage: false});
assertEquals('text1 text2 ', result.text);
assertEquals(clickableContainer, result.blockParent);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest',
'BuildNodeGroupWithMultipleAndroidClickables', function() {
const root = {role: 'application'};
const container = {role: 'genericContainer', parent: root, root};
const button1 = {
role: 'button',
parent: container,
root,
clickable: true,
name: 'button1',
};
const button2 = {
role: 'button',
parent: container,
root,
clickable: true,
name: 'button2',
};
const result = ParagraphUtils.buildNodeGroup(
[button1, button2], 0, {splitOnLanguage: false});
assertEquals('button1 ', result.text);
assertEquals(button1, result.blockParent);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'findNodeFromNodeGroupByCharIndex',
function() {
// The array has four inline text nodes and one static text node.
const nodeGroup =
ParagraphUtils.buildNodeGroup(generateNodesForParagraph(), 0);
// Start index = 0
const firstInline = 'The first';
// Start index = 9
const secondInline = ' sentence.';
// Start index = 20
const thirdInline = 'The second';
// Start index = 30
const fourthInline = ' sentence is longer.';
// Start index = 51
const thirdStatic = 'No child sentence.';
let result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 0 /* charIndex */);
assertEquals(result.node.name, firstInline);
assertEquals(result.offset, 0);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 3 /* charIndex */);
assertEquals(result.node.name, firstInline);
assertEquals(result.offset, 3);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 10 /* charIndex */);
assertEquals(result.node.name, secondInline);
assertEquals(result.offset, 1);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 20 /* charIndex */);
assertEquals(result.node.name, thirdInline);
assertEquals(result.offset, 0);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 33 /* charIndex */);
assertEquals(result.node.name, fourthInline);
assertEquals(result.offset, 3);
// Pointing to the gap between the fourthInline and thirdStatic.
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 50 /* charIndex */);
assertEquals(result.node.name, thirdStatic);
assertEquals(result.offset, 0);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 51 /* charIndex */);
assertEquals(result.node.name, thirdStatic);
assertEquals(result.offset, 0);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 52 /* charIndex */);
assertEquals(result.node.name, thirdStatic);
assertEquals(result.offset, 1);
result = ParagraphUtils.findNodeFromNodeGroupByCharIndex(
nodeGroup, 100 /* charIndex */);
assertEquals(result.node, null);
});
AX_TEST_F(
'SelectToSpeakParagraphUnitTest', 'BuildSingleNodeGroupWithOffset',
function() {
// The array has four inline text nodes and one static text node.
// Their starting indexes are 0, 9, 20, 30, and 51.
const nodes = generateNodesForParagraph();
// Start index = 0
const firstInline = 'The first';
// Start index = 9
const secondInline = ' sentence.';
const firstSentence = firstInline + secondInline + ' ';
// Start index = 20
const thirdInline = 'The second';
// Start index = 30
const fourthInline = ' sentence is longer.';
const secondSentence = thirdInline + fourthInline + ' ';
// Start index = 51
const thirdStatic = 'No child sentence.';
const thirdSentence = thirdStatic + ' ';
let nodeGroup;
let startIndexInGroup;
let endIndexInGroup;
({nodeGroup, startIndexInGroup, endIndexInGroup} =
ParagraphUtils.buildSingleNodeGroupWithOffset(nodes));
assertEquals(
nodeGroup.text, firstSentence + secondSentence + thirdSentence);
assertEquals(startIndexInGroup, undefined);
assertEquals(endIndexInGroup, undefined);
({nodeGroup, startIndexInGroup, endIndexInGroup} =
ParagraphUtils.buildSingleNodeGroupWithOffset(
nodes, 5 /* startIndex */));
assertEquals(
nodeGroup.text, firstSentence + secondSentence + thirdSentence);
assertEquals(startIndexInGroup, 5);
assertEquals(endIndexInGroup, undefined);
({nodeGroup, startIndexInGroup, endIndexInGroup} =
ParagraphUtils.buildSingleNodeGroupWithOffset(
nodes.slice(1), 0 /* startIndex */));
assertEquals(
nodeGroup.text, firstSentence + secondSentence + thirdSentence);
assertEquals(startIndexInGroup, 9);
assertEquals(endIndexInGroup, undefined);
({nodeGroup, startIndexInGroup, endIndexInGroup} =
ParagraphUtils.buildSingleNodeGroupWithOffset(
nodes.slice(2, 5), 1 /* startIndex */, 1 /* endIndex */));
assertEquals(nodeGroup.text, secondSentence + thirdSentence);
assertEquals(startIndexInGroup, 1);
assertEquals(endIndexInGroup, 32);
({nodeGroup, startIndexInGroup, endIndexInGroup} =
ParagraphUtils.buildSingleNodeGroupWithOffset(
nodes.slice(4, 5), undefined, 5 /* endIndex */));
assertEquals(nodeGroup.text, thirdSentence);
assertEquals(startIndexInGroup, undefined);
assertEquals(endIndexInGroup, 5);
});
/**
* Creates an array of nodes that represents a paragraph.
* @return {Array<AutomationNode>}
*/
function generateNodesForParagraph() {
const root = {role: 'rootWebArea'};
const paragraph = {role: 'paragraph', display: 'block', parent: root, root};
const text1 = {
name: 'The first sentence.',
role: 'staticText',
parent: paragraph,
};
const inlineText1 = {
role: 'inlineTextBox',
name: 'The first',
indexInParent: 0,
parent: text1,
};
const inlineText2 = {
role: 'inlineTextBox',
name: ' sentence.',
indexInParent: 1,
parent: text1,
};
text1.children = [inlineText1, inlineText2];
const text2 = {
name: 'The second sentence is longer.',
role: 'staticText',
parent: paragraph,
};
const inlineText3 = {
role: 'inlineTextBox',
name: 'The second',
indexInParent: 0,
parent: text2,
};
const inlineText4 = {
role: 'inlineTextBox',
name: ' sentence is longer.',
indexInParent: 1,
parent: text2,
};
text2.children = [inlineText3, inlineText4];
const text3 = {
name: 'No child sentence.',
role: 'staticText',
parent: paragraph,
};
return [inlineText1, inlineText2, inlineText3, inlineText4, text3];
}