blob: fdf303264ad23d4cdb4ab4968d52fdc954e43a00 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/accessibility/chromevox_test_utils.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/accessibility/speech_monitor.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/browser/extension_registry_test_helper.h"
#include "extensions/common/constants.h"
#include "ui/accessibility/accessibility_features.h"
namespace ash {
namespace {
Profile* GetProfile() {
return AccessibilityManager::Get()->profile();
}
} // namespace
ChromeVoxTestUtils::ChromeVoxTestUtils() {
sm_ = std::make_unique<test::SpeechMonitor>();
}
ChromeVoxTestUtils::~ChromeVoxTestUtils() = default;
void ChromeVoxTestUtils::EnableChromeVox(bool check_for_intro) {
// Enable ChromeVox, disable earcons and wait for key mappings to be fetched.
ASSERT_FALSE(AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
Profile* profile = GetProfile();
console_observer_ = std::make_unique<ExtensionConsoleErrorObserver>(
profile, extension_misc::kChromeVoxExtensionId);
AccessibilityManager::Get()->EnableSpokenFeedback(true);
if (::features::IsAccessibilityManifestV3EnabledForChromeVox()) {
if (!profile->IsOffTheRecord()) {
// Watch events from an MV3 extension which runs in a service worker.
// Note: this class doesn't work with off the record profiles. For off
// the record profiles, we use the SpeechMonitor to signal that ChromeVox
// has loaded by waiting for text to speech to play.
extensions::ExtensionRegistryTestHelper observer(
extension_misc::kChromeVoxExtensionId, profile);
ASSERT_EQ(3, observer.WaitForManifestVersion());
observer.WaitForServiceWorkerStart();
}
} else {
// Watch events from an MV2 extension which runs in a background page.
extensions::ExtensionHostTestHelper host_helper(
profile, extension_misc::kChromeVoxExtensionId);
host_helper.WaitForHostCompletedFirstLoad();
}
sm()->ExpectSpeechPattern(
check_for_intro ? "ChromeVox spoken feedback is ready" : "*");
sm()->Call([this]() { GlobalizeModule("ChromeVox"); });
sm()->Call([this]() { DisableEarcons(); });
sm()->Call([this]() { WaitForReady(); });
}
void ChromeVoxTestUtils::GlobalizeModule(const std::string& name) {
std::string script =
"globalThis." + name + " = TestImportManager.getImports()." + name + ";";
script += "chrome.test.sendScriptResult('done');";
RunJS(script);
}
void ChromeVoxTestUtils::DisableEarcons() {
// Playing earcons from within a test is not only annoying if you're
// running the test locally, but seems to cause crashes
// (http://crbug.com/396507). Work around this by just telling
// ChromeVox to not ever play earcons (prerecorded sound effects).
std::string script(R"JS(
ChromeVox.earcons.playEarcon = function() {};
chrome.test.sendScriptResult('done');
)JS");
RunJS(script);
}
void ChromeVoxTestUtils::WaitForReady() {
std::string script(R"JS(
(async function() {
const imports = TestImportManager.getImports();
await imports.ChromeVoxState.ready();
chrome.test.sendScriptResult('done');
})()
)JS");
RunJS(script);
}
void ChromeVoxTestUtils::WaitForValidRange() {
std::string script(R"JS(
(async function() {
const imports = TestImportManager.getImports();
await imports.ChromeVoxState.ready();
const ChromeVoxRange = imports.ChromeVoxRange;
if (!ChromeVoxRange.current) {
await new Promise(resolve => {
new (class {
constructor() {
ChromeVoxRange.addObserver(this);
}
onCurrentRangeChanged(newRange) {
if (newRange) {
ChromeVoxRange.removeObserver(this);
resolve();
}
}
})();
});
}
chrome.test.sendScriptResult('done');
})()
)JS");
RunJS(script);
}
void ChromeVoxTestUtils::ExecuteCommandHandlerCommand(std::string command) {
GlobalizeModule("CommandHandlerInterface");
std::string script =
"CommandHandlerInterface.instance.onCommand('" + command + "');";
script += "chrome.test.sendScriptResult('done');";
RunJS(script);
}
void ChromeVoxTestUtils::RunJS(const std::string& script) {
base::Value value =
extensions::browsertest_util::ExecuteScriptInBackgroundPage(
GetProfile(), extension_misc::kChromeVoxExtensionId, script,
extensions::browsertest_util::ScriptUserActivation::kDontActivate);
ASSERT_EQ(value, "done");
}
} // namespace ash