chromium/chrome/browser/ui/webui/ash/firmware_update_ui/integration_tests/firmware_update_interactive_uitest.cc

// 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/constants/ash_features.h"
#include "ash/webui/firmware_update_ui/url_constants.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/test/base/ash/interactive/interactive_ash_test.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "chromeos/ash/components/dbus/fwupd/fake_fwupd_client.h"
#include "chromeos/ash/components/fwupd/firmware_update_manager.h"

namespace ash {

namespace {

class FirmwareUpdateInteractiveUiTest : public InteractiveAshTest {
 public:
  FirmwareUpdateInteractiveUiTest() {
    DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirmwareUpdatesAppWebContentsId);
    webcontents_id_ = kFirmwareUpdatesAppWebContentsId;

    feature_list_.InitAndEnableFeature(features::kFirmwareUpdateUIV2);
  }

  auto LaunchFirmwareUpdatesApp() {
    return Do(
        [&]() { CreateBrowserWindow(GURL(kChromeUIFirmwareUpdateAppURL)); });
  }

  // InteractiveAshTest:
  void SetUpOnMainThread() override {
    InteractiveAshTest::SetUpOnMainThread();

    // Set up context for element tracking.
    SetupContextWidget();

    // Ensure the OS Settings system web app (SWA) is installed.
    InstallSystemApps();

    // Ensure that the Fake FwupdClient is running.
    CHECK(FwupdClient::GetFake());
    FwupdClient::GetFake()->set_defer_install_update_callback(true);
  }

  auto TriggerFwupdPropertiesChange(uint32_t percentage, FwupdStatus status) {
    return Do([this, percentage, status]() {
      DCHECK(fwupd_client());
      fwupd_client()->TriggerPropertiesChangeForTesting(
          percentage, static_cast<uint32_t>(status));
    });
  }

  auto TriggerSuccessfulUpdate() {
    return Steps(Do([this]() {
      DCHECK(fwupd_client());
      fwupd_client()->TriggerSuccessfulUpdateForTesting();
    }));
  }

  auto TriggerDeviceRequestToUnplugReplug() {
    return Steps(Do([this]() {
      DCHECK(fwupd_client());
      // A device_request_id of 1 corresponds to FWUPD_REQUEST_ID_REMOVE_REPLUG
      // in firmware_update.mojom.
      fwupd_client()->EmitDeviceRequestForTesting(/*device_request_id=*/1);
    }));
  }

  FakeFwupdClient* fwupd_client() const { return FwupdClient::GetFake(); }

 protected:
  ui::ElementIdentifier webcontents_id_;
  base::test::ScopedFeatureList feature_list_;
};

DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
                                    kFwupdClientUpdateState);

IN_PROC_BROWSER_TEST_F(FirmwareUpdateInteractiveUiTest,
                       TestFirmwareUpdateV2FullUpdate) {
  const DeepQuery kUpdateCardQuery{
      "firmware-update-app",
      "peripheral-updates-list",
      "update-card",
  };

  const DeepQuery kUpdateButtonQuery{
      "firmware-update-app",
      "peripheral-updates-list",
      "update-card",
      "#updateButton",
  };

  const DeepQuery kConfirmationDialogNextButtonQuery{
      "firmware-update-app",
      "firmware-confirmation-dialog",
      "#nextButton",
  };

  const DeepQuery kUpdateDialogDoneButtonQuery{
      "firmware-update-app",
      "firmware-update-dialog",
      "#updateDoneButton",
  };

  const DeepQuery kUpdateDialogProgressQuery{
      "firmware-update-app",
      "firmware-update-dialog",
      "#progress",
  };

  const DeepQuery kUpdateDialogBodyQuery{
      "firmware-update-app",
      "firmware-update-dialog",
      "#updateDialogBody",
  };

  RunTestSequence(
      InstrumentNextTab(webcontents_id_, AnyBrowser()),
      LaunchFirmwareUpdatesApp(),
      WaitForWebContentsReady(webcontents_id_,
                              GURL("chrome://accessory-update")),
      InAnyContext(Steps(
          Log("Verifying that the update-card element is present."),
          EnsurePresent(webcontents_id_, kUpdateCardQuery),
          Log("Clicking on the update button."),
          ClickElement(webcontents_id_, kUpdateButtonQuery),
          WaitForElementExists(webcontents_id_,
                               kConfirmationDialogNextButtonQuery),
          Log("Clicking on the start update button."),
          ClickElement(webcontents_id_, kConfirmationDialogNextButtonQuery),
          Log("Waiting for FwupdClient to register the update..."),
          PollState(kFwupdClientUpdateState,
                    [this]() { return fwupd_client()->has_update_started(); }),
          WaitForState(kFwupdClientUpdateState, true),
          Log("Triggering Fwupd properties change."),
          TriggerFwupdPropertiesChange(/*percentage=*/50,
                                       /*status=*/FwupdStatus::kDeviceWrite),
          Log("Waiting for update dialog progress to match expected value..."),
          WaitForElementTextContains(webcontents_id_,
                                     kUpdateDialogProgressQuery,
                                     "Updating (50% complete)"),
          Log("Triggering device request."),
          TriggerDeviceRequestToUnplugReplug(),
          Log("Triggering Fwupd properties change to WaitingForUser."),
          TriggerFwupdPropertiesChange(/*percentage=*/60,
                                       /*status=*/FwupdStatus::kWaitingForUser),
          Log("Waiting for update dialog progress to match expected value..."),
          WaitForElementTextContains(webcontents_id_,
                                     kUpdateDialogProgressQuery,
                                     "Paused (60% complete)"),
          Log("Waiting for update dialog body to match expected value..."),
          WaitForElementTextContains(webcontents_id_, kUpdateDialogBodyQuery,
                                     "Unplug and replug the fake_device to "
                                     "continue the update process"),
          Log("Triggering successful update."), TriggerSuccessfulUpdate(),
          Log("Verifying existence of update done button."),
          WaitForElementExists(webcontents_id_,
                               kUpdateDialogDoneButtonQuery))));
}

}  // namespace
}  // namespace ash