scanning_handler_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/webui/scanning/scanning_handler.h"
  5. #include <memory>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "ash/webui/scanning/scanning_app_delegate.h"
  10. #include "base/check.h"
  11. #include "base/files/file.h"
  12. #include "base/files/file_path.h"
  13. #include "base/files/file_util.h"
  14. #include "base/files/scoped_temp_dir.h"
  15. #include "base/values.h"
  16. #include "content/public/browser/web_contents.h"
  17. #include "content/public/browser/web_ui.h"
  18. #include "content/public/test/browser_task_environment.h"
  19. #include "content/public/test/test_web_ui.h"
  20. #include "testing/gtest/include/gtest/gtest.h"
  21. #include "ui/gfx/native_widget_types.h"
  22. #include "ui/shell_dialogs/select_file_dialog.h"
  23. #include "ui/shell_dialogs/select_file_dialog_factory.h"
  24. #include "ui/shell_dialogs/select_file_policy.h"
  25. namespace ash {
  26. namespace {
  27. constexpr char kHandlerFunctionName[] = "handlerFunctionName";
  28. constexpr char kTestFilePath[] = "/test/file/path";
  29. } // namespace
  30. class TestSelectFilePolicy : public ui::SelectFilePolicy {
  31. public:
  32. TestSelectFilePolicy& operator=(const TestSelectFilePolicy&) = delete;
  33. bool CanOpenSelectFileDialog() override { return true; }
  34. void SelectFileDenied() override {}
  35. };
  36. // A test ui::SelectFileDialog.
  37. class TestSelectFileDialog : public ui::SelectFileDialog {
  38. public:
  39. TestSelectFileDialog(Listener* listener,
  40. std::unique_ptr<ui::SelectFilePolicy> policy,
  41. base::FilePath selected_path)
  42. : ui::SelectFileDialog(listener, std::move(policy)),
  43. selected_path_(selected_path) {}
  44. TestSelectFileDialog(const TestSelectFileDialog&) = delete;
  45. TestSelectFileDialog& operator=(const TestSelectFileDialog&) = delete;
  46. protected:
  47. void SelectFileImpl(Type type,
  48. const std::u16string& title,
  49. const base::FilePath& default_path,
  50. const FileTypeInfo* file_types,
  51. int file_type_index,
  52. const base::FilePath::StringType& default_extension,
  53. gfx::NativeWindow owning_window,
  54. void* params) override {
  55. if (selected_path_.empty()) {
  56. listener_->FileSelectionCanceled(params);
  57. return;
  58. }
  59. // Put the selected path on the stack so that it stays valid for the
  60. // duration of Listener::FileSelected() despite deleting the
  61. // SelectFileDialog immediately. This is in line with the default behavior
  62. // of SelectFileDialog.
  63. base::FilePath selected_path = std::move(selected_path_);
  64. listener_->FileSelected(selected_path, 0 /* index */, nullptr /* params */);
  65. }
  66. bool IsRunning(gfx::NativeWindow owning_window) const override {
  67. return true;
  68. }
  69. void ListenerDestroyed() override {}
  70. bool HasMultipleFileTypeChoicesImpl() override { return false; }
  71. private:
  72. ~TestSelectFileDialog() override = default;
  73. // The simulatd file path selected by the user.
  74. base::FilePath selected_path_;
  75. };
  76. // A factory associated with the artificial file picker.
  77. class TestSelectFileDialogFactory : public ui::SelectFileDialogFactory {
  78. public:
  79. explicit TestSelectFileDialogFactory(base::FilePath selected_path)
  80. : selected_path_(selected_path) {}
  81. ui::SelectFileDialog* Create(
  82. ui::SelectFileDialog::Listener* listener,
  83. std::unique_ptr<ui::SelectFilePolicy> policy) override {
  84. return new TestSelectFileDialog(listener, std::move(policy),
  85. selected_path_);
  86. }
  87. TestSelectFileDialogFactory(const TestSelectFileDialogFactory&) = delete;
  88. TestSelectFileDialogFactory& operator=(const TestSelectFileDialogFactory&) =
  89. delete;
  90. private:
  91. // The simulated file path selected by the user.
  92. base::FilePath selected_path_;
  93. };
  94. // A fake impl of ScanningAppDelegate.
  95. class FakeScanningAppDelegate : public ScanningAppDelegate {
  96. public:
  97. FakeScanningAppDelegate() = default;
  98. FakeScanningAppDelegate(const FakeScanningAppDelegate&) = delete;
  99. FakeScanningAppDelegate& operator=(const FakeScanningAppDelegate&) = delete;
  100. std::unique_ptr<ui::SelectFilePolicy> CreateChromeSelectFilePolicy()
  101. override {
  102. return std::make_unique<TestSelectFilePolicy>();
  103. }
  104. std::string GetBaseNameFromPath(const base::FilePath& path) override {
  105. return path.BaseName().value();
  106. }
  107. base::FilePath GetMyFilesPath() override {
  108. return base::FilePath(kTestFilePath);
  109. }
  110. bool IsFilePathSupported(const base::FilePath& path_to_file) override {
  111. return !path_to_file.ReferencesParent() &&
  112. my_files_path_.IsParent(path_to_file);
  113. }
  114. void OpenFilesInMediaApp(
  115. const std::vector<base::FilePath>& file_paths) override {
  116. DCHECK(!file_paths.empty());
  117. file_paths_ = file_paths;
  118. }
  119. void ShowFileInFilesApp(
  120. const base::FilePath& path_to_file,
  121. base::OnceCallback<void(const bool)> callback) override {
  122. std::move(callback).Run(kTestFilePath == path_to_file.value());
  123. }
  124. void SaveScanSettingsToPrefs(const std::string& scan_settings) override {
  125. scan_settings_ = scan_settings;
  126. }
  127. std::string GetScanSettingsFromPrefs() override { return scan_settings_; }
  128. // Returns the file paths saved in OpenFilesInMediaApp().
  129. const std::vector<base::FilePath>& file_paths() const { return file_paths_; }
  130. void SetMyFilesPath(base::FilePath my_files_path) {
  131. my_files_path_ = my_files_path;
  132. }
  133. private:
  134. std::vector<base::FilePath> file_paths_;
  135. std::string scan_settings_;
  136. base::FilePath my_files_path_;
  137. };
  138. class ScanningHandlerTest : public testing::Test {
  139. public:
  140. ScanningHandlerTest()
  141. : task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD),
  142. web_ui_(),
  143. scanning_handler_() {}
  144. ~ScanningHandlerTest() override = default;
  145. void SetUp() override {
  146. auto delegate = std::make_unique<FakeScanningAppDelegate>();
  147. fake_scanning_app_delegate_ = delegate.get();
  148. scanning_handler_ = std::make_unique<ScanningHandler>(std::move(delegate));
  149. scanning_handler_->SetWebUIForTest(&web_ui_);
  150. scanning_handler_->RegisterMessages();
  151. base::Value::List args;
  152. web_ui_.HandleReceivedMessage("initialize", args);
  153. EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
  154. my_files_path_ = temp_dir_.GetPath().Append("MyFiles");
  155. EXPECT_TRUE(base::CreateDirectory(my_files_path_));
  156. fake_scanning_app_delegate_->SetMyFilesPath(my_files_path_);
  157. }
  158. void TearDown() override { ui::SelectFileDialog::SetFactory(nullptr); }
  159. // Gets the call data after a ScanningHandler WebUI call and asserts the
  160. // expected response.
  161. const content::TestWebUI::CallData& GetCallData(int size_before_call) {
  162. const std::vector<std::unique_ptr<content::TestWebUI::CallData>>&
  163. call_data_list = web_ui_.call_data();
  164. EXPECT_EQ(size_before_call + 1u, call_data_list.size());
  165. const content::TestWebUI::CallData& call_data = *call_data_list.back();
  166. EXPECT_EQ("cr.webUIResponse", call_data.function_name());
  167. EXPECT_EQ(kHandlerFunctionName, call_data.arg1()->GetString());
  168. // True if ResolveJavascriptCallback and false if RejectJavascriptCallback
  169. // is called by the handler.
  170. EXPECT_TRUE(call_data.arg2()->GetBool());
  171. return call_data;
  172. }
  173. protected:
  174. content::BrowserTaskEnvironment task_environment_;
  175. content::TestWebUI web_ui_;
  176. std::unique_ptr<ScanningHandler> scanning_handler_;
  177. FakeScanningAppDelegate* fake_scanning_app_delegate_;
  178. base::ScopedTempDir temp_dir_;
  179. base::FilePath my_files_path_;
  180. };
  181. // Validates that invoking the requestScanToLocation Web UI event opens the
  182. // select dialog, and if a directory is chosen, returns the selected file path
  183. // and base name.
  184. TEST_F(ScanningHandlerTest, SelectDirectory) {
  185. const base::FilePath base_file_path("/this/is/a/test/directory/Base Name");
  186. ui::SelectFileDialog::SetFactory(
  187. new TestSelectFileDialogFactory(base_file_path));
  188. const size_t call_data_count_before_call = web_ui_.call_data().size();
  189. base::Value::List args;
  190. args.Append(kHandlerFunctionName);
  191. web_ui_.HandleReceivedMessage("requestScanToLocation", args);
  192. const content::TestWebUI::CallData& call_data =
  193. GetCallData(call_data_count_before_call);
  194. const base::DictionaryValue* selected_path_dict;
  195. EXPECT_TRUE(call_data.arg3()->GetAsDictionary(&selected_path_dict));
  196. EXPECT_EQ(base_file_path.value(),
  197. *selected_path_dict->FindStringPath("filePath"));
  198. EXPECT_EQ("Base Name", *selected_path_dict->FindStringPath("baseName"));
  199. }
  200. // Validates that invoking the requestScanToLocation Web UI event opens the
  201. // select dialog, and if the dialog is canceled, returns an empty file path and
  202. // base name.
  203. TEST_F(ScanningHandlerTest, CancelDialog) {
  204. ui::SelectFileDialog::SetFactory(
  205. new TestSelectFileDialogFactory(base::FilePath()));
  206. const size_t call_data_count_before_call = web_ui_.call_data().size();
  207. base::Value::List args;
  208. args.Append(kHandlerFunctionName);
  209. web_ui_.HandleReceivedMessage("requestScanToLocation", args);
  210. const content::TestWebUI::CallData& call_data =
  211. GetCallData(call_data_count_before_call);
  212. const base::DictionaryValue* selected_path_dict;
  213. EXPECT_TRUE(call_data.arg3()->GetAsDictionary(&selected_path_dict));
  214. EXPECT_EQ("", *selected_path_dict->FindStringPath("filePath"));
  215. EXPECT_EQ("", *selected_path_dict->FindStringPath("baseName"));
  216. }
  217. // Validates that invoking the showFileInLocation Web UI event calls the
  218. // OpenFilesAppFunction function and returns the callback with the boolean.
  219. TEST_F(ScanningHandlerTest, ShowFileInLocation) {
  220. const size_t call_data_count_before_call = web_ui_.call_data().size();
  221. base::Value::List args;
  222. args.Append(kHandlerFunctionName);
  223. args.Append(kTestFilePath);
  224. web_ui_.HandleReceivedMessage("showFileInLocation", args);
  225. const content::TestWebUI::CallData& call_data =
  226. GetCallData(call_data_count_before_call);
  227. // Expect true from call to ShowFileInFilesApp().
  228. EXPECT_TRUE(call_data.arg3()->GetBool());
  229. }
  230. // Validates that invoking the getMyFilesPath Web UI event returns the correct
  231. // path.
  232. TEST_F(ScanningHandlerTest, GetMyFilesPath) {
  233. const size_t call_data_count_before_call = web_ui_.call_data().size();
  234. base::Value::List args;
  235. args.Append(kHandlerFunctionName);
  236. web_ui_.HandleReceivedMessage("getMyFilesPath", args);
  237. const content::TestWebUI::CallData& call_data =
  238. GetCallData(call_data_count_before_call);
  239. EXPECT_EQ(base::FilePath(kTestFilePath).value(),
  240. call_data.arg3()->GetString());
  241. }
  242. // Validates that invoking the openFilesInMediaApp Web UI event calls
  243. // ChromeScanningAppDelegate.OpenFilesInMediaApp().
  244. TEST_F(ScanningHandlerTest, OpenFilesInMediaApp) {
  245. const std::string file1 = "path/to/file/file1.jpg";
  246. const std::string file2 = "path/to/file/file2.jpg";
  247. base::Value file_paths_value(base::Value::Type::LIST);
  248. file_paths_value.Append(base::Value(file1));
  249. file_paths_value.Append(base::Value(file2));
  250. base::Value::List args;
  251. args.Append(std::move(file_paths_value));
  252. web_ui_.HandleReceivedMessage("openFilesInMediaApp", args);
  253. const std::vector<base::FilePath> expected_file_paths(
  254. {base::FilePath(file1), base::FilePath(file2)});
  255. EXPECT_EQ(expected_file_paths, fake_scanning_app_delegate_->file_paths());
  256. }
  257. // Validates that calling the saveScanSettings then the getScanSettings Web UI
  258. // event invokes ChromeScanningAppDelegate.SaveScanSettingsToPrefs() and
  259. // ChromeScanningAppDelegate.GetScanSettingsFromPrefs().
  260. TEST_F(ScanningHandlerTest, ScanSettingsPrefs) {
  261. const std::string expected_sticky_settings = R"({
  262. "lastUsedScannerName": "Brother MFC-J497DW",
  263. "scanToPath": "path/to/file",
  264. "scanners": [
  265. {
  266. "name": "Brother MFC-J497DW",
  267. "lastScanDate": "2021-04-16T02:45:26.768Z",
  268. "sourceName": "ADF",
  269. "fileType": 2,
  270. "colorMode": 1,
  271. "pageSize": 2,
  272. "resolutionDpi": 100
  273. }
  274. ]
  275. })";
  276. // First, save the expected scan settings to the Pref service.
  277. base::Value::List save_args;
  278. save_args.Append(expected_sticky_settings);
  279. web_ui_.HandleReceivedMessage("saveScanSettings", save_args);
  280. // Then retrieve the expected scan settings from the Pref service.
  281. const size_t call_data_count_before_call = web_ui_.call_data().size();
  282. base::Value::List get_args;
  283. get_args.Append(kHandlerFunctionName);
  284. web_ui_.HandleReceivedMessage("getScanSettings", get_args);
  285. const content::TestWebUI::CallData& call_data =
  286. GetCallData(call_data_count_before_call);
  287. EXPECT_EQ(expected_sticky_settings, call_data.arg3()->GetString());
  288. }
  289. // Validates that invoking the ensureValidFilePath Web UI event with a valid
  290. // file path returns the expected result.
  291. TEST_F(ScanningHandlerTest, ValidFilePathExists) {
  292. const base::FilePath myScanPath = my_files_path_.Append("myScanPath");
  293. base::File(myScanPath, base::File::FLAG_CREATE | base::File::FLAG_READ);
  294. const size_t call_data_count_before_call = web_ui_.call_data().size();
  295. base::Value::List args;
  296. args.Append(kHandlerFunctionName);
  297. args.Append(myScanPath.value());
  298. web_ui_.HandleReceivedMessage("ensureValidFilePath", args);
  299. task_environment_.RunUntilIdle();
  300. const content::TestWebUI::CallData& call_data =
  301. GetCallData(call_data_count_before_call);
  302. const base::DictionaryValue* selected_path_dict;
  303. EXPECT_TRUE(call_data.arg3()->GetAsDictionary(&selected_path_dict));
  304. EXPECT_EQ(myScanPath.value(),
  305. *selected_path_dict->FindStringPath("filePath"));
  306. EXPECT_EQ("myScanPath", *selected_path_dict->FindStringPath("baseName"));
  307. }
  308. // Validates that invoking the ensureValidFilePath Web UI event with an invalid
  309. // file path returns an object with an empty file path.
  310. TEST_F(ScanningHandlerTest, InvalidFilePath) {
  311. const std::string invalidFilePath = "invalid/file/path";
  312. const size_t call_data_count_before_call = web_ui_.call_data().size();
  313. base::Value::List args;
  314. args.Append(kHandlerFunctionName);
  315. args.Append(invalidFilePath);
  316. web_ui_.HandleReceivedMessage("ensureValidFilePath", args);
  317. task_environment_.RunUntilIdle();
  318. const content::TestWebUI::CallData& call_data =
  319. GetCallData(call_data_count_before_call);
  320. const base::DictionaryValue* selected_path_dict;
  321. EXPECT_TRUE(call_data.arg3()->GetAsDictionary(&selected_path_dict));
  322. EXPECT_EQ(std::string(), *selected_path_dict->FindStringPath("filePath"));
  323. EXPECT_EQ(std::string(), *selected_path_dict->FindStringPath("baseName"));
  324. }
  325. // Validates a request for a plural string with a key missing in the plural
  326. // string map does return a value.
  327. TEST_F(ScanningHandlerTest, GetPluralStringBadKey) {
  328. base::Value::List args;
  329. args.Append(kHandlerFunctionName);
  330. args.Append(/*name=*/"incorrectKey");
  331. args.Append(/*count=*/2);
  332. web_ui_.HandleReceivedMessage("getPluralString", args);
  333. task_environment_.RunUntilIdle();
  334. const std::vector<std::unique_ptr<content::TestWebUI::CallData>>&
  335. call_data_list = web_ui_.call_data();
  336. EXPECT_EQ(0u, call_data_list.size());
  337. }
  338. } // namespace ash