1 // Copyright 2018 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
5 #include <memory>
6 #include <string>
7
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/path_service.h"
11 #include "base/run_loop.h"
12 #include "base/test/bind.h"
13 #include "base/test/metrics/histogram_tester.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/time/time.h"
16 #include "base/util/values/values_util.h"
17 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
18 #include "chrome/browser/devtools/devtools_window_testing.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/hats/hats_service.h"
21 #include "chrome/browser/ui/hats/hats_service_factory.h"
22 #include "chrome/browser/ui/hats/mock_hats_service.h"
23 #include "chrome/browser/ui/test/test_browser_dialog.h"
24 #include "chrome/browser/ui/views/frame/browser_view.h"
25 #include "chrome/browser/ui/views/hats/hats_bubble_view.h"
26 #include "chrome/browser/ui/views/hats/hats_next_web_dialog.h"
27 #include "chrome/browser/ui/views/hats/hats_web_dialog.h"
28 #include "chrome/common/chrome_features.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/test/base/ui_test_utils.h"
32 #include "components/content_settings/core/browser/host_content_settings_map.h"
33 #include "components/version_info/version_info.h"
34 #include "content/public/test/browser_test.h"
35 #include "testing/gmock/include/gmock/gmock.h"
36 #include "url/gurl.h"
37
38 class HatsBubbleTest : public DialogBrowserTest {
39 public:
HatsBubbleTest()40 HatsBubbleTest() {}
41
42 // DialogBrowserTest:
ShowUi(const std::string & name)43 void ShowUi(const std::string& name) override {
44 ASSERT_TRUE(browser()->is_type_normal());
45 BrowserView::GetBrowserViewForBrowser(InProcessBrowserTest::browser())
46 ->ShowHatsBubble("test_site_id", base::DoNothing(), base::DoNothing());
47 }
48
49 private:
50 DISALLOW_COPY_AND_ASSIGN(HatsBubbleTest);
51 };
52
53 class TestHatsWebDialog : public HatsWebDialog {
54 public:
TestHatsWebDialog(Browser * browser,const base::TimeDelta & timeout,const GURL & url)55 TestHatsWebDialog(Browser* browser,
56 const base::TimeDelta& timeout,
57 const GURL& url)
58 : HatsWebDialog(browser, "fake_id_not_used"),
59 loading_timeout_(timeout),
60 content_url_(url) {}
61
62 // ui::WebDialogDelegate implementation.
GetDialogContentURL() const63 GURL GetDialogContentURL() const override {
64 if (content_url_.is_valid()) {
65 // When we have a valid overridden url, use it instead.
66 return content_url_;
67 }
68 return HatsWebDialog::GetDialogContentURL();
69 }
70
OnMainFrameResourceLoadComplete(const blink::mojom::ResourceLoadInfo & resource_load_info)71 void OnMainFrameResourceLoadComplete(
72 const blink::mojom::ResourceLoadInfo& resource_load_info) {
73 if (resource_load_info.net_error == net::Error::OK &&
74 resource_load_info.original_url == resource_url_) {
75 // The resource is loaded successfully.
76 resource_loaded_ = true;
77 }
78 }
79
set_resource_url(const GURL & url)80 void set_resource_url(const GURL& url) { resource_url_ = url; }
resource_loaded()81 bool resource_loaded() { return resource_loaded_; }
82
83 MOCK_METHOD0(OnWebContentsFinishedLoad, void());
84 MOCK_METHOD0(OnLoadTimedOut, void());
85
86 private:
ContentLoadingTimeout() const87 const base::TimeDelta ContentLoadingTimeout() const override {
88 return loading_timeout_;
89 }
90
91 base::TimeDelta loading_timeout_;
92 GURL content_url_;
93 GURL resource_url_;
94 };
95
96 class HatsWebDialogBrowserTest : public InProcessBrowserTest {
97 public:
HatsWebDialogBrowserTest()98 HatsWebDialogBrowserTest() {
99 feature_list_.InitAndDisableFeature(
100 features::kHappinessTrackingSurveysForDesktopMigration);
101 }
102
Create(Browser * browser,const base::TimeDelta & timeout,const GURL & url=GURL ())103 TestHatsWebDialog* Create(Browser* browser,
104 const base::TimeDelta& timeout,
105 const GURL& url = GURL()) {
106 auto* hats_dialog = new TestHatsWebDialog(browser, timeout, url);
107 hats_dialog->CreateWebDialog(browser);
108 return hats_dialog;
109 }
110
111 private:
112 base::test::ScopedFeatureList feature_list_;
113 };
114
115 // Test that calls ShowUi("default").
IN_PROC_BROWSER_TEST_F(HatsBubbleTest,InvokeUi_Default)116 IN_PROC_BROWSER_TEST_F(HatsBubbleTest, InvokeUi_Default) {
117 ShowAndVerifyUi();
118 }
119
120 // Test time out of preloading works.
IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest,Timeout)121 IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest, Timeout) {
122 TestHatsWebDialog* dialog = Create(browser(), base::TimeDelta());
123 EXPECT_CALL(*dialog, OnLoadTimedOut).Times(1);
124 }
125
126 // Test preloading content works.
IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest,ContentPreloading)127 IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest, ContentPreloading) {
128 base::FilePath test_data_dir;
129 base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
130 std::string contents;
131 {
132 base::ScopedAllowBlockingForTesting allow_blocking;
133 EXPECT_TRUE(base::ReadFileToString(test_data_dir.AppendASCII("simple.html"),
134 &contents));
135 }
136
137 TestHatsWebDialog* dialog =
138 Create(browser(), base::TimeDelta::FromSeconds(100),
139 GURL("data:text/html;charset=utf-8," + contents));
140 base::RunLoop run_loop;
141 EXPECT_CALL(*dialog, OnWebContentsFinishedLoad)
142 .WillOnce(testing::Invoke(&run_loop, &base::RunLoop::Quit));
143 run_loop.Run();
144 }
145
146 // Test the correct state will be set when the resource fails to load.
147 // Load with_inline_js.html which has an inline javascript that points to a
148 // nonexistent file.
IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest,LoadFailureInPreloading)149 IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest, LoadFailureInPreloading) {
150 base::FilePath test_data_dir;
151 base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
152 std::string contents;
153 {
154 base::ScopedAllowBlockingForTesting allow_blocking;
155 EXPECT_TRUE(base::ReadFileToString(
156 test_data_dir.AppendASCII("hats").AppendASCII("with_inline_js.html"),
157 &contents));
158 }
159
160 ASSERT_TRUE(embedded_test_server()->Start());
161
162 constexpr char kJSPath[] = "/hats/nonexistent.js";
163 constexpr char kSrcPlaceholder[] = "$JS_SRC";
164 GURL url = embedded_test_server()->GetURL(kJSPath);
165 size_t pos = contents.find(kSrcPlaceholder);
166 EXPECT_NE(pos, std::string::npos);
167 contents.replace(pos, strlen(kSrcPlaceholder), url.spec());
168
169 TestHatsWebDialog* dialog =
170 Create(browser(), base::TimeDelta::FromSeconds(100),
171 GURL("data:text/html;charset=utf-8," + contents));
172 dialog->set_resource_url(url);
173 base::RunLoop run_loop;
174 EXPECT_CALL(*dialog, OnWebContentsFinishedLoad)
175 .WillOnce(testing::Invoke([dialog, &run_loop]() {
176 EXPECT_FALSE(dialog->resource_loaded());
177 run_loop.Quit();
178 }));
179 run_loop.Run();
180 }
181
182 // Test cookies aren't blocked.
IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest,Cookies)183 IN_PROC_BROWSER_TEST_F(HatsWebDialogBrowserTest, Cookies) {
184 auto* settings_map =
185 HostContentSettingsMapFactory::GetForProfile(browser()->profile());
186 settings_map->SetDefaultContentSetting(ContentSettingsType::COOKIES,
187 CONTENT_SETTING_BLOCK);
188
189 TestHatsWebDialog* dialog =
190 Create(browser(), base::TimeDelta::FromSeconds(100));
191
192 settings_map = HostContentSettingsMapFactory::GetForProfile(
193 dialog->otr_profile_for_testing());
194 GURL url1("https://survey.google.com/");
195 GURL url2("https://survey.g.doubleclick.net/");
196 EXPECT_EQ(CONTENT_SETTING_ALLOW,
197 settings_map->GetContentSetting(url1, url1,
198 ContentSettingsType::COOKIES));
199 EXPECT_EQ(CONTENT_SETTING_ALLOW,
200 settings_map->GetContentSetting(url2, url2,
201 ContentSettingsType::COOKIES));
202 }
203
204 class MockHatsNextWebDialog : public HatsNextWebDialog {
205 public:
MockHatsNextWebDialog(Browser * browser,const std::string & trigger_id,const GURL & hats_survey_url,const base::TimeDelta & timeout,base::OnceClosure success_callback,base::OnceClosure failure_callback)206 MockHatsNextWebDialog(Browser* browser,
207 const std::string& trigger_id,
208 const GURL& hats_survey_url,
209 const base::TimeDelta& timeout,
210 base::OnceClosure success_callback,
211 base::OnceClosure failure_callback)
212 : HatsNextWebDialog(browser,
213 trigger_id,
214 hats_survey_url,
215 timeout,
216 std::move(success_callback),
217 std::move(failure_callback)) {}
218
219 MOCK_METHOD0(ShowWidget, void());
220 MOCK_METHOD0(CloseWidget, void());
221 MOCK_METHOD0(UpdateWidgetSize, void());
222
WaitForClose()223 void WaitForClose() {
224 base::RunLoop run_loop;
225 EXPECT_CALL(*this, CloseWidget).WillOnce([&]() {
226 widget_->Close();
227 run_loop.Quit();
228 });
229 run_loop.Run();
230 }
231
WaitForUpdateWidgetSize()232 void WaitForUpdateWidgetSize() {
233 base::RunLoop run_loop;
234 EXPECT_CALL(*this, UpdateWidgetSize).WillOnce(testing::Invoke([&run_loop] {
235 run_loop.Quit();
236 }));
237 run_loop.Run();
238 }
239 };
240
241 class HatsNextWebDialogBrowserTest : public InProcessBrowserTest {
242 public:
HatsNextWebDialogBrowserTest()243 HatsNextWebDialogBrowserTest() {
244 feature_list_.InitAndEnableFeature(
245 features::kHappinessTrackingSurveysForDesktopMigration);
246 }
247
SetUpOnMainThread()248 void SetUpOnMainThread() override {
249 hats_service_ = static_cast<MockHatsService*>(
250 HatsServiceFactory::GetInstance()->SetTestingFactoryAndUse(
251 browser()->profile(), base::BindRepeating(&BuildMockHatsService)));
252 }
253
254 // Open a blank tab in the main browser, inspect it, and return the devtools
255 // Browser for the undocked devtools window.
OpenUndockedDevToolsWindow()256 Browser* OpenUndockedDevToolsWindow() {
257 ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
258
259 const bool is_docked = false;
260 DevToolsWindow* devtools_window =
261 DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), is_docked);
262 return devtools_window->browser_;
263 }
264
hats_service()265 MockHatsService* hats_service() { return hats_service_; }
266
GetSuccessClosure()267 base::OnceClosure GetSuccessClosure() {
268 return base::BindLambdaForTesting([&]() { ++success_count; });
269 }
270
GetFailureClosure()271 base::OnceClosure GetFailureClosure() {
272 return base::BindLambdaForTesting([&]() { ++failure_count; });
273 }
274
275 int success_count = 0;
276 int failure_count = 0;
277
278 private:
279 base::test::ScopedFeatureList feature_list_;
280 MockHatsService* hats_service_;
281 };
282
283 // Test that the web dialog correctly receives change to history state that
284 // indicates a survey is ready to be shown.
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,SurveyLoaded)285 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, SurveyLoaded) {
286 ASSERT_TRUE(embedded_test_server()->Start());
287
288 // Use the preference path constants defined in hats_service.cc.
289 const std::string kLastSurveyStartedTime =
290 std::string(kHatsSurveyTriggerTesting) + ".last_survey_started_time";
291 const std::string kLastMajorVersion =
292 std::string(kHatsSurveyTriggerTesting) + ".last_major_version";
293
294 auto* dialog = new MockHatsNextWebDialog(
295 browser(), kHatsNextSurveyTriggerIDTesting,
296 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
297 base::TimeDelta::FromSeconds(100), GetSuccessClosure(),
298 GetFailureClosure());
299
300 // Check that no record of a survey being shown is present.
301 const base::DictionaryValue* pref_data =
302 browser()->profile()->GetPrefs()->GetDictionary(
303 prefs::kHatsSurveyMetadata);
304 base::Optional<base::Time> last_survey_started_time =
305 util::ValueToTime(pref_data->FindPath(kLastSurveyStartedTime));
306 base::Optional<int> last_major_version =
307 pref_data->FindIntPath(kLastMajorVersion);
308 ASSERT_FALSE(last_survey_started_time.has_value());
309 ASSERT_FALSE(last_major_version.has_value());
310
311 // The hats_next_mock.html will provide a state update to the dialog to
312 // indicate that the survey has been loaded.
313 base::RunLoop run_loop;
314 EXPECT_CALL(*dialog, ShowWidget)
315 .WillOnce(testing::Invoke([dialog, &run_loop]() {
316 EXPECT_FALSE(dialog->IsWaitingForSurveyForTesting());
317 run_loop.Quit();
318 }));
319 run_loop.Run();
320
321 EXPECT_EQ(1, success_count);
322 EXPECT_EQ(0, failure_count);
323
324 // Check that a record of the survey being shown has been recorded.
325 pref_data = browser()->profile()->GetPrefs()->GetDictionary(
326 prefs::kHatsSurveyMetadata);
327 last_survey_started_time =
328 util::ValueToTime(pref_data->FindPath(kLastSurveyStartedTime));
329 last_major_version = pref_data->FindIntPath(kLastMajorVersion);
330 ASSERT_TRUE(last_survey_started_time.has_value());
331 ASSERT_TRUE(last_major_version.has_value());
332 ASSERT_EQ(static_cast<uint32_t>(*last_major_version),
333 version_info::GetVersion().components()[0]);
334 }
335
336 // Test that the web dialog correctly receives change to history state that
337 // indicates the survey window should be closed.
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,SurveyClosed)338 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, SurveyClosed) {
339 ASSERT_TRUE(embedded_test_server()->Start());
340 base::HistogramTester histogram_tester;
341
342 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
343 auto* dialog = new MockHatsNextWebDialog(
344 browser(), "close_for_testing",
345 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
346 base::TimeDelta::FromSeconds(100), GetSuccessClosure(),
347 GetFailureClosure());
348
349 // The hats_next_mock.html will provide a state update to the dialog to
350 // indicate that the survey window should be closed.
351 dialog->WaitForClose();
352
353 EXPECT_EQ(0, success_count);
354 EXPECT_EQ(1, failure_count);
355
356 // Because no loaded state was provided, only a rejection should be recorded.
357 histogram_tester.ExpectUniqueSample(
358 kHatsShouldShowSurveyReasonHistogram,
359 HatsService::ShouldShowSurveyReasons::kNoRejectedByHatsService, 1);
360 }
361
362 // Test that a survey which first reports as loaded, then reports closure, only
363 // logs that the survey was shown.
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,SurveyLoadedThenClosed)364 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, SurveyLoadedThenClosed) {
365 ASSERT_TRUE(embedded_test_server()->Start());
366 base::HistogramTester histogram_tester;
367
368 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
369 auto* dialog = new MockHatsNextWebDialog(
370 browser(), kHatsNextSurveyTriggerIDTesting,
371 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
372 base::TimeDelta::FromSeconds(100), GetSuccessClosure(),
373 GetFailureClosure());
374 dialog->WaitForClose();
375
376 EXPECT_EQ(1, success_count);
377 EXPECT_EQ(0, failure_count);
378
379 // The only recorded sample should indicate that the survey was shown.
380 histogram_tester.ExpectUniqueSample(
381 kHatsShouldShowSurveyReasonHistogram,
382 HatsService::ShouldShowSurveyReasons::kYes, 1);
383 }
384
385 // Test that if the survey does not indicate it is ready for display before the
386 // timeout the widget is closed.
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,SurveyTimeout)387 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, SurveyTimeout) {
388 ASSERT_TRUE(embedded_test_server()->Start());
389 base::HistogramTester histogram_tester;
390
391 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
392 auto* dialog = new MockHatsNextWebDialog(
393 browser(), "invalid_test",
394 embedded_test_server()->GetURL("/hats/non_existent.html"),
395 base::TimeDelta::FromMilliseconds(1), GetSuccessClosure(),
396 GetFailureClosure());
397
398 dialog->WaitForClose();
399
400 EXPECT_EQ(0, success_count);
401 EXPECT_EQ(1, failure_count);
402 histogram_tester.ExpectUniqueSample(
403 kHatsShouldShowSurveyReasonHistogram,
404 HatsService::ShouldShowSurveyReasons::kNoSurveyUnreachable, 1);
405 }
406
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,UnknownURLFragment)407 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, UnknownURLFragment) {
408 ASSERT_TRUE(embedded_test_server()->Start());
409
410 // Check that providing an unknown URL fragment results in the dialog being
411 // closed.
412 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
413 auto* dialog = new MockHatsNextWebDialog(
414 browser(), "invalid_url_fragment_for_testing",
415 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
416 base::TimeDelta::FromSeconds(100), GetSuccessClosure(),
417 GetFailureClosure());
418
419 dialog->WaitForClose();
420 EXPECT_EQ(0, success_count);
421 EXPECT_EQ(1, failure_count);
422 }
423
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,NewWebContents)424 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, NewWebContents) {
425 ASSERT_TRUE(embedded_test_server()->Start());
426
427 auto* dialog = new MockHatsNextWebDialog(
428 browser(), "open_new_web_contents_for_testing",
429 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
430 base::TimeDelta::FromSeconds(100), base::DoNothing(), base::DoNothing());
431
432 // The mock hats dialog will push a close state after it has attempted to
433 // open another web contents.
434 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
435 dialog->WaitForClose();
436
437 // Check that a tab with http://foo.com (defined in hats_next_mock.html) has
438 // been opened in the regular browser and is active.
439 EXPECT_EQ(
440 GURL("http://foo.com"),
441 browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL());
442 }
443
444 // The devtools browser for undocked devtools has no tab strip and can't open
445 // new tabs. Instead it should open new WebContents in the main browser.
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,NewWebContentsForDevtoolsBrowser)446 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,
447 NewWebContentsForDevtoolsBrowser) {
448 ASSERT_TRUE(embedded_test_server()->Start());
449
450 Browser* devtools_browser = OpenUndockedDevToolsWindow();
451
452 auto* dialog = new MockHatsNextWebDialog(
453 devtools_browser, "open_new_web_contents_for_testing",
454 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
455 base::TimeDelta::FromSeconds(100), base::DoNothing(), base::DoNothing());
456
457 // The mock hats dialog will push a close state after it has attempted to
458 // open another web contents.
459 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
460 dialog->WaitForClose();
461
462 // Check that a tab with http://foo.com (defined in hats_next_mock.html) has
463 // been opened in the regular browser and is active.
464 EXPECT_EQ(
465 GURL("http://foo.com"),
466 browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL());
467 }
468
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,DialogResize)469 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, DialogResize) {
470 ASSERT_TRUE(embedded_test_server()->Start());
471
472 auto* dialog = new MockHatsNextWebDialog(
473 browser(), "resize_for_testing",
474 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
475 base::TimeDelta::FromSeconds(100), base::DoNothing(), base::DoNothing());
476
477 // Check that the dialog reports a preferred size the same as the size defined
478 // in hats_next_mock.html.
479 constexpr auto kTargetSize = gfx::Size(70, 300);
480
481 // Depending on renderer warm-up, an initial empty size may additionally be
482 // reported before hats_next_mock.html has had a chance to resize.
483 dialog->WaitForUpdateWidgetSize();
484 auto size = dialog->CalculatePreferredSize();
485 EXPECT_TRUE(size == kTargetSize || size == dialog->kMinSize);
486 if (size != kTargetSize) {
487 dialog->WaitForUpdateWidgetSize();
488 EXPECT_EQ(kTargetSize, dialog->CalculatePreferredSize());
489 }
490 }
491
IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest,MaximumSize)492 IN_PROC_BROWSER_TEST_F(HatsNextWebDialogBrowserTest, MaximumSize) {
493 ASSERT_TRUE(embedded_test_server()->Start());
494
495 EXPECT_CALL(*hats_service(), HatsNextDialogClosed);
496 auto* dialog = new MockHatsNextWebDialog(
497 browser(), "resize_to_large_for_testing",
498 embedded_test_server()->GetURL("/hats/hats_next_mock.html"),
499 base::TimeDelta::FromSeconds(100), base::DoNothing(), base::DoNothing());
500
501 // Check that the maximum size of the dialog is bounded appropriately by the
502 // dialogs maximum size. Depending on renderer warm-up, an initial empty size
503 // may additionally be reported before hats_next_mock.html has had a chance
504 // to resize.
505 dialog->WaitForUpdateWidgetSize();
506 auto size = dialog->CalculatePreferredSize();
507 EXPECT_TRUE(size == HatsNextWebDialog::kMaxSize || size == dialog->kMinSize);
508 if (size != HatsNextWebDialog::kMaxSize) {
509 dialog->WaitForUpdateWidgetSize();
510 EXPECT_EQ(HatsNextWebDialog::kMaxSize, dialog->CalculatePreferredSize());
511 }
512 }
513