// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
import {BrowserProxy} from '//resources/cr_components/color_change_listener/browser_proxy.js';
import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
import {PauseActionSource} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
import {suppressInnocuousErrors, waitForPlayFromSelection} from './common.js';
import {TestColorUpdaterBrowserProxy} from './test_color_updater_browser_proxy.js';
suite('ReadAloud_UpdateContentSelectionPDF', () => {
let app: AppElement;
let testBrowserProxy: TestColorUpdaterBrowserProxy;
// Similar to read_aloud_update_content_selection, but this tree uses
// negative values, as some node ids, such as those for PDFs, may be negative.
// root htmlTag='#document' id=1
// ++paragraph htmlTag='p' id=-2
// ++++staticText name='Hello' id=-3
// ++paragraph htmlTag='p' id=-4
// ++++staticText name='World' id=-5
// ++paragraph htmlTag='p' id=-6
// ++++staticText name='Friend' id=-7
// ++++staticText name='!' id=-8
const axTree = {
rootId: 1,
nodes: [
{
id: 1,
role: 'rootWebArea',
htmlTag: '#document',
childIds: [-2, -4, -6],
},
{
id: -2,
role: 'paragraph',
htmlTag: 'p',
childIds: [-3],
},
{
id: -3,
role: 'staticText',
name: 'Hello',
},
{
id: -4,
role: 'paragraph',
htmlTag: 'p',
childIds: [-5],
},
{
id: -5,
role: 'staticText',
name: 'World',
},
{
id: -6,
role: 'paragraph',
htmlTag: 'p',
childIds: [-7, -8],
},
{
id: -7,
role: 'staticText',
name: 'Friend',
},
{
id: -8,
role: 'staticText',
name: '!',
},
],
selection: {
anchor_object_id: -5,
focus_object_id: -7,
anchor_offset: 1,
focus_offset: 2,
is_backward: false,
},
};
setup(() => {
suppressInnocuousErrors();
testBrowserProxy = new TestColorUpdaterBrowserProxy();
BrowserProxy.setInstance(testBrowserProxy);
document.body.innerHTML = window.trustedTypes!.emptyHTML;
// Do not call the real `onConnected()`. As defined in
// ReadAnythingAppController, onConnected creates mojo pipes to connect to
// the rest of the Read Anything feature, which we are not testing here.
chrome.readingMode.onConnected = () => {};
app = document.createElement('read-anything-app');
document.body.appendChild(app);
document.onselectionchange = () => {};
chrome.readingMode.setContentForTesting(axTree, []);
});
suite('Before speech started', () => {
test('inner html of container matches expected html', () => {
assertFalse(app.speechPlayingState.isSpeechActive);
assertFalse(app.speechPlayingState.hasSpeechBeenTriggered);
// isSpeechTreeInitialized set in updateContent
assertTrue(app.speechPlayingState.isSpeechTreeInitialized);
// The expected HTML before any highlights are added.
const expected = '<div><p>World</p><p>Friend!</p></div>';
const innerHTML = app.$.container.innerHTML;
assertEquals(expected, innerHTML);
});
test('selection in reading mode panel correct', () => {
// Calling shadowRoot.getSelection directly is not supported in TS tests,
// so use a helper to get the selection from the app instead.
const selection = document.getSelection()!;
assertTrue(selection != null);
assertEquals('World', selection.anchorNode!.textContent);
assertEquals('Friend', selection.focusNode!.textContent);
assertEquals(1, selection.anchorOffset);
assertEquals(2, selection.focusOffset);
});
test('container class correct', () => {
assertEquals(
app.$.container.className,
'user-select-disabled-when-speech-active-false');
assertEquals('auto', window.getComputedStyle(app.$.container).userSelect);
});
});
suite('While Read Aloud playing', () => {
setup(async () => {
app.playSpeech();
await waitForPlayFromSelection();
});
test('inner html of container matches expected html', () => {
assertTrue(app.speechPlayingState.isSpeechActive);
assertTrue(app.speechPlayingState.isSpeechTreeInitialized);
// The expected HTML with the current highlights.
const expected = '<div><p><span class="parent-of-highlight">' +
'<span class="current-read-highlight">World</span>' +
'</span></p><p><span class="parent-of-highlight">' +
'<span class="current-read-highlight">Friend' +
'</span></span><span class="parent-of-highlight">' +
'<span class="current-read-highlight">!</span>' +
'</span></p></div>';
const innerHTML = app.$.container.innerHTML;
assertEquals(expected, innerHTML);
});
test('selection in reading mode panel cleared', () => {
const selection = document.getSelection()!;
assertEquals('', selection.toString());
});
test('container class correct', () => {
assertEquals(
app.$.container.className,
'user-select-disabled-when-speech-active-true');
assertEquals('none', window.getComputedStyle(app.$.container).userSelect);
});
});
suite('While Read Aloud paused', () => {
setup(async () => {
app.playSpeech();
await waitForPlayFromSelection();
app.stopSpeech(PauseActionSource.BUTTON_CLICK);
});
test('inner html of container matches expected html', () => {
assertFalse(app.speechPlayingState.isSpeechActive);
assertTrue(app.speechPlayingState.isSpeechTreeInitialized);
// The expected HTML with the current highlights.
const expected = '<div><p><span class="parent-of-highlight">' +
'<span class="current-read-highlight">World</span>' +
'</span></p><p><span class="parent-of-highlight">' +
'<span class="current-read-highlight">Friend' +
'</span></span><span class="parent-of-highlight">' +
'<span class="current-read-highlight">!</span>' +
'</span></p></div>';
const innerHTML = app.$.container.innerHTML;
assertEquals(expected, innerHTML);
});
test('selection in reading mode panel cleared', () => {
const selection = document.getSelection()!;
assertEquals('', selection.toString());
});
test('container class correct', () => {
assertEquals(
app.$.container.className,
'user-select-disabled-when-speech-active-false');
assertEquals('auto', window.getComputedStyle(app.$.container).userSelect);
});
});
});