// 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.
#include "ash/ash_element_identifiers.h"
#include "ash/public/cpp/assistant/assistant_state.h"
#include "ash/public/cpp/test/assistant_test_api.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
#include "chrome/test/base/chromeos/crosier/ash_integration_test.h"
#include "chrome/test/base/chromeos/crosier/chromeos_integration_login_mixin.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_enums.h"
#include "ui/base/test/ui_controls.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
// Assistant requires Gaia login, which is only supported for branded build.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
namespace ash {
using ::ash::assistant::AssistantStatus;
// Waiter that waits for the assistant to be ready.
class AssistantReadyWaiter : private AssistantStateObserver {
public:
explicit AssistantReadyWaiter(AssistantState* state) : state_(state) {
state_->AddObserver(this);
}
~AssistantReadyWaiter() override { state_->RemoveObserver(this); }
void WaitForReady() {
if (state_->assistant_status() == AssistantStatus::READY) {
return;
}
// Wait until we're ready or we hit the timeout.
run_loop_ = std::make_unique<base::RunLoop>();
EXPECT_NO_FATAL_FAILURE(run_loop_->Run())
<< "Failed waiting for assistant to be ready. Current status is "
<< state_->assistant_status() << ". ";
run_loop_.reset();
}
private:
void OnAssistantStatusChanged(AssistantStatus status) override {
if (status == AssistantStatus::READY && run_loop_) {
run_loop_->Quit();
}
}
private:
const raw_ptr<AssistantState> state_;
std::unique_ptr<base::RunLoop> run_loop_;
};
// The suite is parameterized by which hotkey is being tested (the Assistant
// keyboard key or Search-A).
class AssistantIntegrationTest : public AshIntegrationTest,
public testing::WithParamInterface<bool> {
public:
AssistantIntegrationTest() {
set_exit_when_last_browser_closes(false);
// Allows network access for production Gaia.
SetAllowNetworkAccessToHostResolutions();
// The assistant requires Gaia login.
login_mixin().SetMode(ChromeOSIntegrationLoginMixin::Mode::kGaiaLogin);
}
};
INSTANTIATE_TEST_SUITE_P(All, AssistantIntegrationTest, testing::Bool());
IN_PROC_BROWSER_TEST_P(AssistantIntegrationTest, Hotkey) {
SetupContextWidget();
// Login and wait for the user session to start.
login_mixin().Login();
test::WaitForPrimaryUserSessionStart();
// Enable the assistant.
std::unique_ptr<AssistantTestApi> test_api = AssistantTestApi::Create();
test_api->SetAssistantEnabled(true);
test_api->DisableAnimations();
// Wait for the assistant to be ready.
AssistantReadyWaiter waiter(test_api->GetAssistantState());
waiter.WaitForReady();
RunTestSequence(
// clang-format off
Log("Pressing accelerator to open assistant"),
Do([] {
if (GetParam()) {
// Test the Assistant key.
ui_controls::SendKeyPress(/*window=*/nullptr, ui::VKEY_ASSISTANT,
/*control=*/false, /*shift=*/false,
/*alt=*/false, /*command=*/false);
} else {
// Test Search-A.
ui_controls::SendKeyPress(/*window=*/nullptr, ui::VKEY_A,
/*control=*/false, /*shift=*/false,
/*alt=*/false, /*command=*/true);
}
}),
Log("Waiting for assistant dialog plate to show"),
WaitForShow(ash::kAssistantDialogPlateElementId),
Log("Test complete")
// clang-format on
);
}
} // namespace ash
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)