Commit 260f8b29 authored by Lei Shi's avatar Lei Shi Committed by Chromium LUCI CQ

Resume from the beginning of the sentence in Select To Speak

When a user resumes from a pause, STS will speak from the beginning of the current sentence. If the user paused on a sentence start, STS will continue reading from the pause.

This CL also refactors the functions in sentence_utils.js and uses staticTextNode directly to query sentenceStarts.

AX-Relnotes: N/A

Bug: 1143817
Change-Id: Iaf73a5656273db499604d3f7f4a008c37e5a4eb6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2572905
Commit-Queue: Lei Shi <leileilei@google.com>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835806}
parent 67a6625b
...@@ -632,14 +632,28 @@ class SelectToSpeak { ...@@ -632,14 +632,28 @@ class SelectToSpeak {
} }
/** /**
* Resume the TTS. Currently, we just ask the TTS engine to pick up where it * Resume the TTS from the beginning of the current sentence.
* quited last time in |this.startCurrentNodeGroup_|.
* @private * @private
*/ */
resume_() { resume_() {
if (this.isPaused_()) { // If TTS is not paused, return early.
this.startCurrentNodeGroup_(); if (!this.isPaused_()) {
return;
} }
if (!SentenceUtils.isSentenceStart(
this.currentNodeGroup_, this.navigationState_.currentCharIndex)) {
// If the current position is not a sentence start, move to the start of
// the current sentence.
const currentSentenceStart = SentenceUtils.getSentenceStart(
this.currentNodeGroup_, this.navigationState_.currentCharIndex,
constants.Dir.BACKWARD);
// Only navigate to the current sentence start if it exists, otherwise
// move to the beginning of the current nodeGroup.
// TODO(leileilei): check if the 0 char index would cause issues.
this.navigationState_.currentCharIndex =
currentSentenceStart === null ? 0 : currentSentenceStart;
}
this.startCurrentNodeGroup_();
} }
/** /**
......
...@@ -146,36 +146,109 @@ TEST_F( ...@@ -146,36 +146,109 @@ TEST_F(
}); });
}); });
TEST_F('SelectToSpeakNavigationControlTest', 'PauseAndResume', function() { TEST_F(
const bodyHtml = ` 'SelectToSpeakNavigationControlTest', 'PauseResumeWithinTheSentence',
<p id="p1">Paragraph 1</p>' function() {
const bodyHtml = `
<p id="p1">First sentence. Second sentence. Third sentence.</p>'
`; `;
this.runWithLoadedTree( this.runWithLoadedTree(
this.generateHtmlWithSelectedElement('p1', bodyHtml), () => { this.generateHtmlWithSelectedElement('p1', bodyHtml), () => {
this.triggerReadSelectedText(); this.triggerReadSelectedText();
// Speaks the first word. // Speaks until the second word of the second sentence.
this.mockTts.speakUntilCharIndex(10); this.mockTts.speakUntilCharIndex(23);
assertTrue(this.mockTts.currentlySpeaking()); assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1); assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace( this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], 'Paragraph 1'); this.mockTts.pendingUtterances()[0],
'First sentence. Second sentence. Third sentence.');
// Hitting pause will stop the current TTS.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction.PAUSE);
assertFalse(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 0);
// Hitting pause will stop the current TTS. // Hitting resume will start from the beginning of the second
selectToSpeak.onSelectToSpeakPanelAction_( // sentence.
chrome.accessibilityPrivate.SelectToSpeakPanelAction.PAUSE); selectToSpeak.onSelectToSpeakPanelAction_(
assertFalse(this.mockTts.currentlySpeaking()); chrome.accessibilityPrivate.SelectToSpeakPanelAction.RESUME);
assertEquals(this.mockTts.pendingUtterances().length, 0); assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0],
'Second sentence. Third sentence.');
});
});
// Hitting resume will start from the next position. TEST_F(
selectToSpeak.onSelectToSpeakPanelAction_( 'SelectToSpeakNavigationControlTest', 'PauseResumeAtTheBeginningOfSentence',
chrome.accessibilityPrivate.SelectToSpeakPanelAction.RESUME); function() {
assertTrue(this.mockTts.currentlySpeaking()); const bodyHtml = `
assertEquals(this.mockTts.pendingUtterances().length, 1); <p id="p1">First sentence. Second sentence. Third sentence.</p>'
this.assertEqualsCollapseWhitespace( `;
this.mockTts.pendingUtterances()[0], '1'); this.runWithLoadedTree(
}); this.generateHtmlWithSelectedElement('p1', bodyHtml), () => {
}); this.triggerReadSelectedText();
// Speaks until the third sentence.
this.mockTts.speakUntilCharIndex(33);
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0],
'First sentence. Second sentence. Third sentence.');
// Hitting pause will stop the current TTS.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction.PAUSE);
assertFalse(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 0);
// Hitting resume will start from the beginning of the third
// sentence.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction.RESUME);
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], 'Third sentence.');
});
});
TEST_F(
'SelectToSpeakNavigationControlTest',
'PauseResumeAtTheBeginningOfParagraph', function() {
const bodyHtml = `
<p id="p1">first sentence.</p>'
`;
this.runWithLoadedTree(
this.generateHtmlWithSelectedElement('p1', bodyHtml), () => {
this.triggerReadSelectedText();
// Speaks until the second word.
this.mockTts.speakUntilCharIndex(6);
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], 'first sentence.');
// Hitting pause will stop the current TTS.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction.PAUSE);
assertFalse(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 0);
// Hitting resume will start from the beginning of the paragraph.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction.RESUME);
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], 'first sentence.');
});
});
TEST_F('SelectToSpeakNavigationControlTest', 'NextSentence', function() { TEST_F('SelectToSpeakNavigationControlTest', 'NextSentence', function() {
const bodyHtml = ` const bodyHtml = `
......
...@@ -9,33 +9,40 @@ class SentenceUtils { ...@@ -9,33 +9,40 @@ class SentenceUtils {
constructor() {} constructor() {}
/** /**
* Get the next sentence start. When |direction| is set to forward, this * Gets the sentence start from the current position. When |direction| is set
* function will incrementally go over each nodeGroupItem in |nodeGroup|, * to forward, this function will incrementally go over each nodeGroupItem in
* and try to find a sentence start index after the |startCharIndex|. When * |nodeGroup|, and try to find a sentence start index after the
* |direction| is set to backward, This function will search for the preivous * |startCharIndex|. When |direction| is set to backward, This function will
* sentence. The |startCharIndex| and the found sentence start index are * search for the sentence start before the current position. The
* relative to the start of this node group. * |startCharIndex| and the found sentence start index are relative to the
* text content of this node group.
* @param {ParagraphUtils.NodeGroup} nodeGroup The node group this function * @param {ParagraphUtils.NodeGroup} nodeGroup The node group this function
* will search. * will search.
* @param {number} startCharIndex The char index that we start from. This * @param {number} startCharIndex The char index that we start from. This
* index is relative to the start of this node group and is exclusive: if * index is relative to the text content of this node group and is
* a sentence start at 0 and we search with a 0 |startCharIndex|, this * exclusive: if a sentence start at 0 and we search with a 0
* function will return the next sentence start after 0. * |startCharIndex|, this function will return the next sentence start
* @param {constants.Dir} direction Direction for the next sentence. * after 0 if we search forward.
* |constants.Dir.BACKWARD| will search for the previous sentence while * @param {constants.Dir} direction Direction to search for the next sentence
* |constants.Dir.FORWARD| will search for the next sentence. * start. |constants.Dir.BACKWARD| will search for the sentence start
* before the current position. |constants.Dir.FORWARD| will search for
* the sentence start after the current position.
* @return {?number} the next sentence start after |startCharIndex|, returns * @return {?number} the next sentence start after |startCharIndex|, returns
* null if nothing found. * null if nothing found.
*/ */
static getSentenceStart(nodeGroup, startCharIndex, direction) { static getSentenceStart(nodeGroup, startCharIndex, direction) {
if (!nodeGroup) {
return null;
}
if (nodeGroup.nodes.length === 0) { if (nodeGroup.nodes.length === 0) {
return null; return null;
} }
if (direction === constants.Dir.FORWARD) { if (direction === constants.Dir.FORWARD) {
for (let i = 0; i < nodeGroup.nodes.length; i++) { for (let i = 0; i < nodeGroup.nodes.length; i++) {
const nodeGroupItem = nodeGroup.nodes[i]; const nodeGroupItem = nodeGroup.nodes[i];
const result = SentenceUtils.getNextSentenceStartInNodeGroupItem( const result = SentenceUtils.getSentenceStartInNodeGroupItem(
nodeGroupItem, startCharIndex); nodeGroupItem, startCharIndex, constants.Dir.FORWARD);
if (result !== null) { if (result !== null) {
return result; return result;
} }
...@@ -43,8 +50,8 @@ class SentenceUtils { ...@@ -43,8 +50,8 @@ class SentenceUtils {
} else if (direction === constants.Dir.BACKWARD) { } else if (direction === constants.Dir.BACKWARD) {
for (let i = nodeGroup.nodes.length - 1; i >= 0; i--) { for (let i = nodeGroup.nodes.length - 1; i >= 0; i--) {
const nodeGroupItem = nodeGroup.nodes[i]; const nodeGroupItem = nodeGroup.nodes[i];
const result = SentenceUtils.getPrevSentenceStartInNodeGroupItem( const result = SentenceUtils.getSentenceStartInNodeGroupItem(
nodeGroupItem, startCharIndex); nodeGroupItem, startCharIndex, constants.Dir.BACKWARD);
if (result !== null) { if (result !== null) {
return result; return result;
} }
...@@ -54,82 +61,75 @@ class SentenceUtils { ...@@ -54,82 +61,75 @@ class SentenceUtils {
} }
/** /**
* Get the next sentence start within a node group item. * Gets the sentence start before or after current position within the input
* |nodeGroupItem|.
* @param {ParagraphUtils.NodeGroupItem} nodeGroupItem The node group item * @param {ParagraphUtils.NodeGroupItem} nodeGroupItem The node group item
* this function will search. * this function will search.
* @param {number} startCharIndex The char index that we start from. * @param {number} startCharIndex The char index that we start from. This is
* relative to the text content of the node group.
* @param {constants.Dir} direction Direction to search for the next sentence
* start. |constants.Dir.BACKWARD| will search for the sentence start
* before the current position. |constants.Dir.FORWARD| will search for
* the sentence start after the current position.
* @return {?number} the next sentence start after |startCharIndex|, returns * @return {?number} the next sentence start after |startCharIndex|, returns
* null if nothing found. * null if nothing found.
*/ */
static getNextSentenceStartInNodeGroupItem(nodeGroupItem, startCharIndex) { static getSentenceStartInNodeGroupItem(
const nodeGroupItemHasInlineText = nodeGroupItem, startCharIndex, direction) {
nodeGroupItem && nodeGroupItem.hasInlineText; if (!nodeGroupItem) {
if (!nodeGroupItemHasInlineText) {
return null; return null;
} }
const nodeGroupItemHasContent = nodeGroupItem.node.children.length > 0 && // Check if this nodeGroupItem has a non-empty static text node.
nodeGroupItem.node.name.length > 0; if (!nodeGroupItem.node.role === RoleType.STATIC_TEXT ||
if (!nodeGroupItemHasContent) { nodeGroupItem.node.name.length === 0) {
return null; return null;
} }
const staticTextStartChar = nodeGroupItem.startChar; const staticTextStartChar = nodeGroupItem.startChar;
const staticTextNode = nodeGroupItem.node; const staticTextNode = nodeGroupItem.node;
// If the corresponding char of |startCharIndex| is after this static text if (direction === constants.Dir.FORWARD) {
// node, skip this text node. // If the corresponding char of |startCharIndex| is after this static text
if (startCharIndex > staticTextStartChar + staticTextNode.name.length - 1) { // node, skip this text node.
return null; if (startCharIndex >
} staticTextStartChar + staticTextNode.name.length - 1) {
// If the corresponding char of |startCharIndex| is within this static text return null;
// node, we get its relative index in this static text. Otherwise, we search
// from the beginning of the static text.
const searchIndexInStaticText =
Math.max(startCharIndex - staticTextStartChar, 0);
// Find the index of the inline text node corresponding to the
// |searchIndexInStaticText|.
const inlineTextNodeIndex =
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticTextNode, searchIndexInStaticText);
// findInlineTextNodeIndexByCharacterIndex may return a negative number,
// indicating no children in the staticTextNode.
if (inlineTextNodeIndex < 0) {
return null;
}
// Incrementally iterate over each inline text nodes within this static text
// node from |inlineTextNodeIndex|.
for (let inlineTextNode, startCharInStaticText, i = inlineTextNodeIndex;
i < staticTextNode.children.length; i++) {
// If this is the first node in the for loop, use ParagraphUtils to get
// the start char. Otherwise, update the start char by adding the name
// length of previous node.
if (i === inlineTextNodeIndex) {
inlineTextNode = staticTextNode.children[i];
startCharInStaticText =
ParagraphUtils.getStartCharIndexInParent(inlineTextNode);
} else {
startCharInStaticText += inlineTextNode.name.length;
inlineTextNode = staticTextNode.children[i];
} }
// If the corresponding char of |startCharIndex| is within this static
// text node, we get its relative index in this static text. Otherwise,
// the start char is before this static text node, and any sentence start
// in this static text node is valid.
const searchIndexInStaticText =
Math.max(startCharIndex - staticTextStartChar, -1);
// Continue to the next one if the end of this inlineTextNode is still // Iterate over all sentenceStarts to find the one that is bigger than
// smaller than startCharIndex. // |searchIndexInStaticText|.
if (inlineTextNode.name.length - 1 + startCharInStaticText + for (let i = 0; i < staticTextNode.sentenceStarts.length; i++) {
staticTextStartChar <= if (staticTextNode.sentenceStarts[i] <= searchIndexInStaticText) {
startCharIndex) { continue;
continue; }
return staticTextNode.sentenceStarts[i] + staticTextStartChar;
} }
} else if (direction === constants.Dir.BACKWARD) {
// If the corresponding char of |startCharIndex| is before this static
// text node, skip this text node.
if (startCharIndex < staticTextStartChar) {
return null;
}
// If the corresponding char of |startCharIndex| is within this static
// text node, we get its relative index in this static text. Otherwise,
// the start char is after this static text node, and any sentence start
// in this static text node is valid.
const searchIndexInStaticText = Math.min(
startCharIndex - staticTextStartChar, staticTextNode.name.length);
// Iterate over all sentenceStarts to find the one that is bigger than // Iterate over all sentenceStarts to find the one that is smaller than
// startCharIndex. // |searchIndexInStaticText|.
for (let j = 0; j < inlineTextNode.sentenceStarts.length; j++) { for (let i = staticTextNode.sentenceStarts.length - 1; i >= 0; i--) {
const potentialStart = inlineTextNode.sentenceStarts[j] + if (staticTextNode.sentenceStarts[i] >= searchIndexInStaticText) {
startCharInStaticText + staticTextStartChar;
if (potentialStart <= startCharIndex) {
continue; continue;
} }
return potentialStart; return staticTextNode.sentenceStarts[i] + staticTextStartChar;
} }
} }
// We are off the edge of this static text node, return null. // We are off the edge of this static text node, return null.
...@@ -137,79 +137,53 @@ class SentenceUtils { ...@@ -137,79 +137,53 @@ class SentenceUtils {
} }
/** /**
* Get the previous sentence start within a node group item. * Checks if the current position is a sentence start.
* @param {ParagraphUtils.NodeGroupItem} nodeGroupItem The node group item * @param {ParagraphUtils.NodeGroup} nodeGroup The node group of the current
* this function will search. * position.
* @param {number} startCharIndex The char index that we start from. * @param {number} currentCharIndex The char index of the current position.
* @return {?number} the previous sentence start before |startCharIndex|, * This is relative to the text content of the node group.
* returns null if nothing found. * @return {boolean} Whether the current position is a start of a sentence.
*/ */
static getPrevSentenceStartInNodeGroupItem(nodeGroupItem, startCharIndex) { static isSentenceStart(nodeGroup, currentCharIndex) {
const nodeGroupItemHasInlineText = if (!nodeGroup) {
nodeGroupItem && nodeGroupItem.hasInlineText; return false;
if (!nodeGroupItemHasInlineText) {
return null;
}
const nodeGroupItemHasContent = nodeGroupItem.node.children.length > 0 &&
nodeGroupItem.node.name.length > 0;
if (!nodeGroupItemHasContent) {
return null;
}
const staticTextStartChar = nodeGroupItem.startChar;
const staticTextNode = nodeGroupItem.node;
// If the corresponding char of |startCharIndex| is before this static text
// node, skip this text node.
if (startCharIndex < staticTextStartChar) {
return null;
} }
// If the corresponding char of |startCharIndex| is within this static text
// node, we get its relative index in this static text. Otherwise, we search
// from the end of the static text.
const searchIndexInStaticText = Math.min(
startCharIndex - staticTextStartChar, staticTextNode.name.length - 1);
// Find the index of the inline text node corresponding to the if (nodeGroup.nodes.length === 0) {
// |searchIndexInStaticText|. return false;
const inlineTextNodeIndex =
ParagraphUtils.findInlineTextNodeIndexByCharacterIndex(
staticTextNode, searchIndexInStaticText);
// findInlineTextNodeIndexByCharacterIndex may return a negative number,
// indicating no children in the staticTextNode.
if (inlineTextNodeIndex < 0) {
return null;
} }
// Iterate backwards over each inline text nodes within this static text // Iterate over all the nodeGroupItems.
// node from |inlineTextNodeIndex|. for (let i = 0; i < nodeGroup.nodes.length; i++) {
for (let inlineTextNode, startCharInStaticText, i = inlineTextNodeIndex; const nodeGroupItem = nodeGroup.nodes[i];
i >= 0; i--) { // Check if this nodeGroupItem has a non-empty static text node.
inlineTextNode = staticTextNode.children[i]; if (!nodeGroupItem.node.role === RoleType.STATIC_TEXT ||
// If this is the first node in the for loop, use ParagraphUtils to get nodeGroupItem.node.name.length === 0) {
// the start char. Otherwise, update the start char by subtracting the continue;
// name length of the current node. }
startCharInStaticText = i === inlineTextNodeIndex ?
ParagraphUtils.getStartCharIndexInParent(inlineTextNode) :
startCharInStaticText - inlineTextNode.name.length;
// Continue to the next one if the start of this inlineTextNode is still // Check if the corresponding char of |currentCharIndex| is inside this
// bigger than startCharIndex. // static text node.
if (startCharInStaticText + staticTextStartChar >= startCharIndex) { const staticTextStartChar = nodeGroupItem.startChar;
const staticTextNode = nodeGroupItem.node;
if (currentCharIndex >
staticTextStartChar + staticTextNode.name.length - 1 ||
currentCharIndex < staticTextStartChar) {
continue; continue;
} }
// Iterate over all sentenceStarts to find the one that is smaller than const searchIndexInStaticText = currentCharIndex - staticTextStartChar;
// startCharIndex.
for (let j = inlineTextNode.sentenceStarts.length - 1; j >= 0; j--) { // Iterate over all sentenceStarts in the staticTextNode to see if we
const potentialStart = inlineTextNode.sentenceStarts[j] + // have |searchIndexInStaticText|.
startCharInStaticText + staticTextStartChar; for (let j = 0; j < staticTextNode.sentenceStarts.length; j++) {
if (potentialStart >= startCharIndex) { if (staticTextNode.sentenceStarts[j] === searchIndexInStaticText) {
continue; return true;
} }
return potentialStart;
} }
} }
// We are off the edge of this static text node, return not found.
return null; // We did not find a sentence start equal to |currentCharIndex|.
return false;
} }
} }
\ No newline at end of file
...@@ -211,79 +211,106 @@ TEST_F( ...@@ -211,79 +211,106 @@ TEST_F(
constants.Dir.BACKWARD /* direction */)); constants.Dir.BACKWARD /* direction */));
}); });
TEST_F('SelectToSpeakSentenceUtilsUnitTest', 'isSentenceStart', function() {
// The text of the test node group is "Hello. New. World."
const nodeGroup = getTestNodeGroupWithOneNode();
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 0 /* startCharIndex */));
assertEquals(
false,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 3 /* startCharIndex */));
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 7 /* startCharIndex */));
assertEquals(
false,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 11 /* startCharIndex */));
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 12 /* startCharIndex */));
});
TEST_F(
'SelectToSpeakSentenceUtilsUnitTest', 'isSentenceStartMultiNodes',
function() {
// The text of the test node group is "Hello. New. Beautiful. World." The
// char indexes of four sentence starts are 0, 7, 12, 23.
const nodeGroup = getTestNodeGroupWithMultiNodes();
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 0 /* startCharIndex */));
assertEquals(
false,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 3 /* startCharIndex */));
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 7 /* startCharIndex */));
assertEquals(
false,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 11 /* startCharIndex */));
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 12 /* startCharIndex */));
assertEquals(
false,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 15 /* startCharIndex */));
assertEquals(
true,
SentenceUtils.isSentenceStart(
nodeGroup /* nodeGroup */, 23 /* startCharIndex */));
});
function getTestNodeGroupWithOneNode() { function getTestNodeGroupWithOneNode() {
const inlineText = {sentenceStarts: [0, 7, 12], name: 'Hello. New. World.'}; const staticText = {sentenceStarts: [0, 7, 12], name: 'Hello. New. World.'};
const staticText = {children: [inlineText], name: 'Hello. New. World.'}; const node = {node: staticText, startChar: 0};
const node = {node: staticText, startChar: 0, hasInlineText: true};
return {nodes: [node], text: 'Hello. New. World.'}; return {nodes: [node], text: 'Hello. New. World.'};
} }
function getTestNodeGroupWithMultiNodes() { function getTestNodeGroupWithMultiNodes() {
const staticText1 = {name: 'Hello. New. ', role: 'staticText'}; const staticText1 = {
const inlineText1 = {
sentenceStarts: [0, 7],
name: 'Hello. New. ', name: 'Hello. New. ',
indexInParent: 0, role: 'staticText',
parent: staticText1 sentenceStarts: [0, 7]
}; };
staticText1.children = [inlineText1]; const node1 = {node: staticText1, startChar: 0};
const node1 = {node: staticText1, startChar: 0, hasInlineText: true};
const staticText2 = {name: 'Beautiful. World.', role: 'staticText'}; const staticText2 = {
const inlineText2 = { name: 'Beautiful. World.',
sentenceStarts: [0], role: 'staticText',
name: 'Beautiful. ', sentenceStarts: [0, 11]
indexInParent: 0,
parent: staticText2
};
const inlineText3 = {
sentenceStarts: [0],
name: 'World.',
indexInParent: 1,
parent: staticText2
}; };
staticText2.children = [inlineText2, inlineText3]; const node2 = {node: staticText2, startChar: 12};
const node2 = {node: staticText2, startChar: 12, hasInlineText: true};
return {nodes: [node1, node2], text: 'Hello. New. Beautiful. World.'}; return {nodes: [node1, node2], text: 'Hello. New. Beautiful. World.'};
} }
function getTestNodeGroupWithSentenceSpanningAcrossMultiNodes() { function getTestNodeGroupWithSentenceSpanningAcrossMultiNodes() {
const staticText1 = {name: 'Hello', role: 'staticText'}; const staticText1 = {name: 'Hello', role: 'staticText', sentenceStarts: [0]};
const inlineText1 = { const node1 = {node: staticText1, startChar: 0};
sentenceStarts: [0],
name: 'Hello',
indexInParent: 0,
parent: staticText1
};
staticText1.children = [inlineText1];
const node1 = {node: staticText1, startChar: 0, hasInlineText: true};
const staticText2 = {name: ' world. New', role: 'staticText'}; const staticText2 = {
const inlineText2 = { name: ' world. New',
sentenceStarts: [], role: 'staticText',
name: ' world.', sentenceStarts: [8]
indexInParent: 0,
parent: staticText2
}; };
const inlineText3 = { const node2 = {node: staticText2, startChar: 5};
sentenceStarts: [1],
name: ' New',
indexInParent: 1,
parent: staticText2
};
staticText2.children = [inlineText2, inlineText3];
const node2 = {node: staticText2, startChar: 5, hasInlineText: true};
const staticText3 = {name: ' world.', role: 'staticText'}; const staticText3 = {name: ' world.', role: 'staticText', sentenceStarts: []};
const inlineText4 = { const node3 = {node: staticText3, startChar: 16};
sentenceStarts: [],
name: ' world.',
indexInParent: 0,
parent: staticText3
};
staticText3.children = [inlineText4];
const node3 = {node: staticText3, startChar: 16, hasInlineText: true};
return {nodes: [node1, node2, node3], text: 'Hello world. New world.'}; return {nodes: [node1, node2, node3], text: 'Hello world. New world.'};
} }
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment