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
16 namespace {
17
AssertPendingState(NavigationTracker * tracker,const std::string & frame_id,bool expected_is_pending)18 void 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
AssertTrackerExpectsSingleStopEvent(BrowserInfo * browser_info)26 void 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
AssertTrackerExpectsMultipleStopEvents(BrowserInfo * browser_info)47 void 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
TEST(NavigationTracker,FrameLoadStartStop)86 TEST(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.
TEST(NavigationTracker,FrameLoadStartStartStop)105 TEST(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
TEST(NavigationTracker,MultipleFramesLoadOldDevtools)128 TEST(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
TEST(NavigationTracker,MultipleFramesLoad)136 TEST(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
TEST(NavigationTracker,NavigationScheduledThenLoaded)144 TEST(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
TEST(NavigationTracker,NavigationScheduledForOtherFrame)173 TEST(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
TEST(NavigationTracker,NavigationScheduledThenCancelled)189 TEST(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
TEST(NavigationTracker,NavigationScheduledTooFarAway)212 TEST(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
TEST(NavigationTracker,DiscardScheduledNavigationsOnMainFrameCommit)228 TEST(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
256 namespace {
257
258 class FailToEvalScriptDevToolsClient : public StubDevToolsClient {
259 public:
~FailToEvalScriptDevToolsClient()260 virtual ~FailToEvalScriptDevToolsClient() {}
261
SendCommandAndGetResult(const std::string & method,const base::DictionaryValue & params,scoped_ptr<base::DictionaryValue> * result)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
TEST(NavigationTracker,UnknownStateFailsToDetermineState)273 TEST(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
282 namespace {
283
284 class DeterminingLoadStateDevToolsClient : public StubDevToolsClient {
285 public:
DeterminingLoadStateDevToolsClient(bool is_loading,const std::string & send_event_first,base::DictionaryValue * send_event_first_params)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
~DeterminingLoadStateDevToolsClient()294 virtual ~DeterminingLoadStateDevToolsClient() {}
295
SendCommandAndGetResult(const std::string & method,const base::DictionaryValue & params,scoped_ptr<base::DictionaryValue> * result)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
TEST(NavigationTracker,UnknownStateForcesStart)321 TEST(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
TEST(NavigationTracker,UnknownStateForcesStartReceivesStop)329 TEST(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
TEST(NavigationTracker,OnSuccessfulNavigate)339 TEST(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
TEST(NavigationTracker,OnSuccessfulNavigateStillWaiting)351 TEST(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