1// Copyright (c) 2012 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 <string> 6 7#include "base/compiler_specific.h" 8#include "base/json/json_reader.h" 9#include "base/values.h" 10#include "chrome/test/chromedriver/chrome/browser_info.h" 11#include "chrome/test/chromedriver/chrome/navigation_tracker.h" 12#include "chrome/test/chromedriver/chrome/status.h" 13#include "chrome/test/chromedriver/chrome/stub_devtools_client.h" 14#include "testing/gtest/include/gtest/gtest.h" 15 16namespace { 17 18void AssertPendingState(NavigationTracker* tracker, 19 const std::string& frame_id, 20 bool expected_is_pending) { 21 bool is_pending = !expected_is_pending; 22 ASSERT_EQ(kOk, tracker->IsPendingNavigation(frame_id, &is_pending).code()); 23 ASSERT_EQ(expected_is_pending, is_pending); 24} 25 26void AssertTrackerExpectsSingleStopEvent(BrowserInfo* browser_info) { 27 StubDevToolsClient client; 28 NavigationTracker tracker(&client, browser_info); 29 base::DictionaryValue params; 30 params.SetString("frameId", "f"); 31 32 // pending_frames_set_.size() == 0 33 ASSERT_EQ( 34 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 35 // pending_frames_set_.size() == 1 36 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 37 ASSERT_EQ( 38 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 39 // pending_frames_set_.size() == 2 40 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 41 ASSERT_EQ( 42 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 43 // pending_frames_set_.size() == 0 44 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 45} 46 47void AssertTrackerExpectsMultipleStopEvents(BrowserInfo* browser_info) { 48 StubDevToolsClient client; 49 NavigationTracker tracker(&client, browser_info); 50 base::DictionaryValue params; 51 52 // pending_frames_set_.size() == 0 53 params.SetString("frameId", "1"); 54 ASSERT_EQ( 55 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 56 // pending_frames_set_.size() == 1 57 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "1", true)); 58 params.SetString("frameId", "2"); 59 ASSERT_EQ( 60 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 61 // pending_frames_set_.size() == 2 62 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "2", true)); 63 params.SetString("frameId", "2"); 64 ASSERT_EQ( 65 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 66 // pending_frames_set_.size() == 1 67 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "2", true)); 68 params.SetString("frameId", "1"); 69 ASSERT_EQ( 70 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 71 // pending_frames_set_.size() == 0 72 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "1", false)); 73 params.SetString("frameId", "3"); 74 ASSERT_EQ( 75 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 76 // pending_frames_set_.size() == 0 77 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "3", false)); 78 ASSERT_EQ( 79 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 80 // pending_frames_set_.size() == 1 81 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "3", true)); 82} 83 84} // namespace 85 86TEST(NavigationTracker, FrameLoadStartStop) { 87 StubDevToolsClient client; 88 BrowserInfo browser_info; 89 NavigationTracker tracker(&client, &browser_info); 90 91 base::DictionaryValue params; 92 params.SetString("frameId", "f"); 93 94 ASSERT_EQ( 95 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 96 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 97 ASSERT_EQ( 98 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 99 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 100} 101 102// When a frame fails to load due to (for example) a DNS resolution error, we 103// can sometimes see two Page.frameStartedLoading events with only a single 104// Page.frameStoppedLoading event. 105TEST(NavigationTracker, FrameLoadStartStartStop) { 106 StubDevToolsClient client; 107 BrowserInfo browser_info; 108 NavigationTracker tracker(&client, &browser_info); 109 110 base::DictionaryValue params; 111 params.SetString("frameId", "f"); 112 113 ASSERT_EQ( 114 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 115 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 116 ASSERT_EQ( 117 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 118 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 119 ASSERT_EQ( 120 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 121 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 122} 123 124// NavigationTracker::OnEvent handles the Page.frameStoppedLoading event 125// differently, depending on the browser and blink version. See the comment in 126// NavigationTracker::OnEvent for details. 127 128TEST(NavigationTracker, MultipleFramesLoadOldDevtools) { 129 BrowserInfo kOldChromeWithNewBlink("chrome", std::string(), 1916, 170248); 130 AssertTrackerExpectsSingleStopEvent(&kOldChromeWithNewBlink); 131 132 BrowserInfo kWebViewWithOldBlink("webview", std::string(), 9999, 170247); 133 AssertTrackerExpectsSingleStopEvent(&kWebViewWithOldBlink); 134} 135 136TEST(NavigationTracker, MultipleFramesLoad) { 137 BrowserInfo kNewChromeWithNewBlink("chrome", std::string(), 1917, 170248); 138 AssertTrackerExpectsMultipleStopEvents(&kNewChromeWithNewBlink); 139 140 BrowserInfo kWebViewWithNewBlink("webview", std::string(), 9999, 170248); 141 AssertTrackerExpectsMultipleStopEvents(&kWebViewWithNewBlink); 142} 143 144TEST(NavigationTracker, NavigationScheduledThenLoaded) { 145 StubDevToolsClient client; 146 BrowserInfo browser_info; 147 NavigationTracker tracker( 148 &client, NavigationTracker::kNotLoading, &browser_info); 149 base::DictionaryValue params; 150 params.SetString("frameId", "f"); 151 base::DictionaryValue params_scheduled; 152 params_scheduled.SetInteger("delay", 0); 153 params_scheduled.SetString("frameId", "f"); 154 155 ASSERT_EQ( 156 kOk, 157 tracker.OnEvent( 158 &client, "Page.frameScheduledNavigation", params_scheduled).code()); 159 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 160 ASSERT_EQ( 161 kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); 162 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 163 ASSERT_EQ( 164 kOk, 165 tracker.OnEvent(&client, "Page.frameClearedScheduledNavigation", params) 166 .code()); 167 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 168 ASSERT_EQ( 169 kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); 170 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 171} 172 173TEST(NavigationTracker, NavigationScheduledForOtherFrame) { 174 StubDevToolsClient client; 175 BrowserInfo browser_info; 176 NavigationTracker tracker( 177 &client, NavigationTracker::kNotLoading, &browser_info); 178 base::DictionaryValue params_scheduled; 179 params_scheduled.SetInteger("delay", 0); 180 params_scheduled.SetString("frameId", "other"); 181 182 ASSERT_EQ( 183 kOk, 184 tracker.OnEvent( 185 &client, "Page.frameScheduledNavigation", params_scheduled).code()); 186 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 187} 188 189TEST(NavigationTracker, NavigationScheduledThenCancelled) { 190 StubDevToolsClient client; 191 BrowserInfo browser_info; 192 NavigationTracker tracker( 193 &client, NavigationTracker::kNotLoading, &browser_info); 194 base::DictionaryValue params; 195 params.SetString("frameId", "f"); 196 base::DictionaryValue params_scheduled; 197 params_scheduled.SetInteger("delay", 0); 198 params_scheduled.SetString("frameId", "f"); 199 200 ASSERT_EQ( 201 kOk, 202 tracker.OnEvent( 203 &client, "Page.frameScheduledNavigation", params_scheduled).code()); 204 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 205 ASSERT_EQ( 206 kOk, 207 tracker.OnEvent(&client, "Page.frameClearedScheduledNavigation", params) 208 .code()); 209 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 210} 211 212TEST(NavigationTracker, NavigationScheduledTooFarAway) { 213 StubDevToolsClient client; 214 BrowserInfo browser_info; 215 NavigationTracker tracker( 216 &client, NavigationTracker::kNotLoading, &browser_info); 217 218 base::DictionaryValue params_scheduled; 219 params_scheduled.SetInteger("delay", 10); 220 params_scheduled.SetString("frameId", "f"); 221 ASSERT_EQ( 222 kOk, 223 tracker.OnEvent( 224 &client, "Page.frameScheduledNavigation", params_scheduled).code()); 225 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 226} 227 228TEST(NavigationTracker, DiscardScheduledNavigationsOnMainFrameCommit) { 229 StubDevToolsClient client; 230 BrowserInfo browser_info; 231 NavigationTracker tracker( 232 &client, NavigationTracker::kNotLoading, &browser_info); 233 234 base::DictionaryValue params_scheduled; 235 params_scheduled.SetString("frameId", "subframe"); 236 params_scheduled.SetInteger("delay", 0); 237 ASSERT_EQ( 238 kOk, 239 tracker.OnEvent( 240 &client, "Page.frameScheduledNavigation", params_scheduled).code()); 241 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "subframe", true)); 242 243 base::DictionaryValue params_navigated; 244 params_navigated.SetString("frame.parentId", "something"); 245 ASSERT_EQ( 246 kOk, 247 tracker.OnEvent(&client, "Page.frameNavigated", params_navigated).code()); 248 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "subframe", true)); 249 params_navigated.Clear(); 250 ASSERT_EQ( 251 kOk, 252 tracker.OnEvent(&client, "Page.frameNavigated", params_navigated).code()); 253 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "subframe", false)); 254} 255 256namespace { 257 258class FailToEvalScriptDevToolsClient : public StubDevToolsClient { 259 public: 260 virtual ~FailToEvalScriptDevToolsClient() {} 261 262 virtual Status SendCommandAndGetResult( 263 const std::string& method, 264 const base::DictionaryValue& params, 265 scoped_ptr<base::DictionaryValue>* result) OVERRIDE { 266 EXPECT_STREQ("Runtime.evaluate", method.c_str()); 267 return Status(kUnknownError, "failed to eval script"); 268 } 269}; 270 271} // namespace 272 273TEST(NavigationTracker, UnknownStateFailsToDetermineState) { 274 FailToEvalScriptDevToolsClient client; 275 BrowserInfo browser_info; 276 NavigationTracker tracker(&client, &browser_info); 277 bool is_pending; 278 ASSERT_EQ(kUnknownError, 279 tracker.IsPendingNavigation("f", &is_pending).code()); 280} 281 282namespace { 283 284class DeterminingLoadStateDevToolsClient : public StubDevToolsClient { 285 public: 286 DeterminingLoadStateDevToolsClient( 287 bool is_loading, 288 const std::string& send_event_first, 289 base::DictionaryValue* send_event_first_params) 290 : is_loading_(is_loading), 291 send_event_first_(send_event_first), 292 send_event_first_params_(send_event_first_params) {} 293 294 virtual ~DeterminingLoadStateDevToolsClient() {} 295 296 virtual Status SendCommandAndGetResult( 297 const std::string& method, 298 const base::DictionaryValue& params, 299 scoped_ptr<base::DictionaryValue>* result) OVERRIDE { 300 if (send_event_first_.length()) { 301 Status status = listeners_.front() 302 ->OnEvent(this, send_event_first_, *send_event_first_params_); 303 if (status.IsError()) 304 return status; 305 } 306 307 base::DictionaryValue result_dict; 308 result_dict.SetBoolean("result.value", is_loading_); 309 result->reset(result_dict.DeepCopy()); 310 return Status(kOk); 311 } 312 313 private: 314 bool is_loading_; 315 std::string send_event_first_; 316 base::DictionaryValue* send_event_first_params_; 317}; 318 319} // namespace 320 321TEST(NavigationTracker, UnknownStateForcesStart) { 322 base::DictionaryValue params; 323 DeterminingLoadStateDevToolsClient client(true, std::string(), ¶ms); 324 BrowserInfo browser_info; 325 NavigationTracker tracker(&client, &browser_info); 326 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 327} 328 329TEST(NavigationTracker, UnknownStateForcesStartReceivesStop) { 330 base::DictionaryValue params; 331 params.SetString("frameId", "f"); 332 DeterminingLoadStateDevToolsClient client( 333 true, "Page.frameStoppedLoading", ¶ms); 334 BrowserInfo browser_info; 335 NavigationTracker tracker(&client, &browser_info); 336 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 337} 338 339TEST(NavigationTracker, OnSuccessfulNavigate) { 340 base::DictionaryValue params; 341 params.SetString("frameId", "f"); 342 DeterminingLoadStateDevToolsClient client( 343 true, "Page.frameStoppedLoading", ¶ms); 344 BrowserInfo browser_info; 345 NavigationTracker tracker( 346 &client, NavigationTracker::kNotLoading, &browser_info); 347 tracker.OnCommandSuccess(&client, "Page.navigate"); 348 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); 349} 350 351TEST(NavigationTracker, OnSuccessfulNavigateStillWaiting) { 352 base::DictionaryValue params; 353 params.SetString("frameId", "f"); 354 DeterminingLoadStateDevToolsClient client(true, std::string(), ¶ms); 355 BrowserInfo browser_info; 356 NavigationTracker tracker( 357 &client, NavigationTracker::kNotLoading, &browser_info); 358 tracker.OnCommandSuccess(&client, "Page.navigate"); 359 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); 360} 361