chromium/ios/chrome/browser/web/model/choose_file/choose_file_tab_helper_unittest.mm

// 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 "ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h"

#import <memory>

#import "ios/chrome/browser/web/model/choose_file/fake_choose_file_controller.h"
#import "ios/web/public/test/fakes/fake_navigation_context.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"

// Test suite for ChooseFileTabHelper.
class ChooseFileTabHelperTest : public PlatformTest {
 public:
  void SetUp() override {
    PlatformTest::SetUp();
    web_state_ = std::make_unique<web::FakeWebState>();
    tab_helper_ = ChooseFileTabHelper::GetOrCreateForWebState(web_state_.get());
  }

 protected:
  raw_ptr<ChooseFileTabHelper> tab_helper_;
  std::unique_ptr<web::FakeWebState> web_state_;
};

// Tests that calling `StopChoosingFiles()` submits file selection and that
// `IsChoosingFiles()` returns false afterwards.
TEST_F(ChooseFileTabHelperTest, StopChoosingFiles) {
  EXPECT_FALSE(tab_helper_->IsChoosingFiles());

  __block bool selection_submitted = false;

  // Test that calling `StopChoosingFiles()` forwards its arguments to the
  // controller and ends file selection.
  auto controller = std::make_unique<FakeChooseFileController>(
      ChooseFileEvent(false, std::vector<std::string>{},
                      std::vector<std::string>{}, web_state_.get()));
  NSURL* file_url = [NSURL fileURLWithPath:@"/path/to/file"];
  NSArray<NSURL*>* file_urls = @[ file_url ];
  NSString* display_string = @"display_string";
  UIImage* icon_image = [[UIImage alloc] init];
  controller->SetSubmitSelectionCompletion(
      base::BindOnce(^(const FakeChooseFileController& control) {
        selection_submitted = true;
        EXPECT_NSEQ(file_urls, control.submitted_file_urls());
        EXPECT_NSEQ(display_string, control.submitted_display_string());
        EXPECT_NSEQ(icon_image, control.submitted_icon_image());
      }));
  tab_helper_->StartChoosingFiles(std::move(controller));
  EXPECT_FALSE(selection_submitted);
  EXPECT_TRUE(tab_helper_->IsChoosingFiles());
  tab_helper_->StopChoosingFiles(file_urls, display_string, icon_image);
  EXPECT_TRUE(selection_submitted);
  EXPECT_FALSE(tab_helper_->IsChoosingFiles());

  // Reset `selection_submitted`.
  selection_submitted = false;

  // Test that calling `StopChoosingFiles()` with no arguments forwards an empty
  // list of files to the controller and ends file selection.
  controller = std::make_unique<FakeChooseFileController>(
      ChooseFileEvent(false, std::vector<std::string>{},
                      std::vector<std::string>{}, web_state_.get()));
  controller->SetSubmitSelectionCompletion(
      base::BindOnce(^(const FakeChooseFileController& control) {
        selection_submitted = true;
        EXPECT_NSEQ(@[], control.submitted_file_urls());
        EXPECT_NSEQ(nil, control.submitted_display_string());
        EXPECT_NSEQ(nil, control.submitted_icon_image());
      }));
  tab_helper_->StartChoosingFiles(std::move(controller));
  EXPECT_FALSE(selection_submitted);
  EXPECT_TRUE(tab_helper_->IsChoosingFiles());
  tab_helper_->StopChoosingFiles();
  EXPECT_TRUE(selection_submitted);
  EXPECT_FALSE(tab_helper_->IsChoosingFiles());
}

// Tests that finishing navigation to a different document stops file selection.
TEST_F(ChooseFileTabHelperTest, DidFinishNavigation) {
  EXPECT_FALSE(tab_helper_->IsChoosingFiles());

  auto controller = std::make_unique<FakeChooseFileController>(
      ChooseFileEvent(false, std::vector<std::string>{},
                      std::vector<std::string>{}, web_state_.get()));
  tab_helper_->StartChoosingFiles(std::move(controller));
  EXPECT_TRUE(tab_helper_->IsChoosingFiles());

  auto navigation_context = std::make_unique<web::FakeNavigationContext>();

  navigation_context->SetIsSameDocument(true);
  tab_helper_->DidFinishNavigation(web_state_.get(), navigation_context.get());
  EXPECT_TRUE(tab_helper_->IsChoosingFiles());

  navigation_context->SetIsSameDocument(false);
  tab_helper_->DidFinishNavigation(web_state_.get(), navigation_context.get());
  EXPECT_FALSE(tab_helper_->IsChoosingFiles());
}

// Tests that `SetIsPresentingFilePicker()` and `IsPresentingFilePicker()` calls
// are forwarded to the controller.
TEST_F(ChooseFileTabHelperTest, SetIsPresentingFilePicker) {
  auto controller = std::make_unique<FakeChooseFileController>(
      ChooseFileEvent(false, std::vector<std::string>{},
                      std::vector<std::string>{}, web_state_.get()));
  ChooseFileController* controller_ptr = controller.get();
  tab_helper_->StartChoosingFiles(std::move(controller));
  ASSERT_TRUE(tab_helper_->IsChoosingFiles());

  tab_helper_->SetIsPresentingFilePicker(false);
  EXPECT_FALSE(tab_helper_->IsPresentingFilePicker());
  EXPECT_FALSE(controller_ptr->IsPresentingFilePicker());

  tab_helper_->SetIsPresentingFilePicker(true);
  EXPECT_TRUE(tab_helper_->IsPresentingFilePicker());
  EXPECT_TRUE(controller_ptr->IsPresentingFilePicker());
}

// Tests that `GetChooseFileEvent()` returns the event passed to the controller
// at construction.
TEST_F(ChooseFileTabHelperTest, GetChooseFileEvent) {
  ChooseFileEvent event(false, std::vector<std::string>{},
                        std::vector<std::string>{}, web_state_.get());
  auto controller = std::make_unique<FakeChooseFileController>(event);
  tab_helper_->StartChoosingFiles(std::move(controller));
  const ChooseFileEvent tab_helper_event = tab_helper_->GetChooseFileEvent();
  EXPECT_EQ(event.allow_multiple_files, tab_helper_event.allow_multiple_files);
  EXPECT_EQ(event.accept_file_extensions,
            tab_helper_event.accept_file_extensions);
  EXPECT_EQ(event.accept_mime_types, tab_helper_event.accept_mime_types);
  EXPECT_EQ(event.web_state.get(), tab_helper_event.web_state.get());
  EXPECT_EQ(event.time, tab_helper_event.time);
}