Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / test / data / webui / net_internals / net_internals_test.js
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 /**
6  * @fileoverview The way these tests work is as follows:
7  * C++ in net_internals_ui_browsertest.cc does any necessary setup, and then
8  * calls the entry point for a test with RunJavascriptTest.  The called
9  * function can then use the assert/expect functions defined in test_api.js.
10  * All callbacks from the browser are wrapped in such a way that they can
11  * also use the assert/expect functions.
12  *
13  * A test ends when testDone is called.  This can be done by the test itself,
14  * but will also be done by the test framework when an assert/expect test fails
15  * or an exception is thrown.
16  */
17
18 // Include the C++ browser test class when generating *.cc files.
19 GEN('#include ' +
20     '"chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h"');
21
22 var NetInternalsTest = (function() {
23   /**
24    * A shorter poll interval is used for tests, since a few tests wait for
25    * polled values to change.
26    * @type {number}
27    * @const
28    */
29   var TESTING_POLL_INTERVAL_MS = 50;
30
31   /**
32    * Private pointer to the currently active test framework.  Needed so static
33    * functions can access some of the inner workings of the test framework.
34    * @type {NetInternalsTest}
35    */
36   var activeTest_ = null;
37
38   function NetInternalsTest() {
39     activeTest_ = this;
40   }
41
42   NetInternalsTest.prototype = {
43     __proto__: testing.Test.prototype,
44
45     /**
46      * Define the C++ fixture class and include it.
47      * @type {?string}
48      * @override
49      */
50     typedefCppFixture: 'NetInternalsTest',
51
52     /** @inheritDoc */
53     browsePreload: 'chrome://net-internals/',
54
55     /** @inheritDoc */
56     isAsync: true,
57
58     setUp: function() {
59       // Enforce accessibility auditing, but suppress some false positives.
60       this.accessibilityIssuesAreErrors = true;
61       // False positive because a unicode character is used to draw a square.
62       // If it was actual text it'd be too low-contrast, but a square is fine.
63       this.accessibilityAuditConfig.ignoreSelectors(
64           'lowContrastElements', '#timeline-view-selection-ul label');
65       // Suppress this error; the black-on-gray button is readable.
66       this.accessibilityAuditConfig.ignoreSelectors(
67           'lowContrastElements', '#export-view-save-log-file');
68       // False positive because the background color highlights and then
69       // fades out with a transition when there's an error.
70       this.accessibilityAuditConfig.ignoreSelectors(
71           'lowContrastElements', '#hsts-view-query-output span');
72       // False positives for unknown reason.
73       this.accessibilityAuditConfig.ignoreSelectors(
74           'focusableElementNotVisibleAndNotAriaHidden',
75           '#hsts-view-tab-content *');
76
77       // TODO(aboxhall): enable when this bug is fixed:
78       // https://github.com/GoogleChrome/accessibility-developer-tools/issues/69
79       this.accessibilityAuditConfig.auditRulesToIgnore.push(
80           'focusableElementNotVisibleAndNotAriaHidden');
81
82       // Wrap g_browser.receive around a test function so that assert and expect
83       // functions can be called from observers.
84       g_browser.receive =
85           this.continueTest(WhenTestDone.EXPECT,
86                             BrowserBridge.prototype.receive.bind(g_browser));
87
88       g_browser.setPollInterval(TESTING_POLL_INTERVAL_MS);
89
90       var runTest = this.deferRunTest(WhenTestDone.EXPECT);
91
92       // If we've already received the constants, start the tests.
93       if (Constants) {
94         // Stat test asynchronously, to avoid running a nested test function.
95         window.setTimeout(runTest, 0);
96         return;
97       }
98
99       // Otherwise, wait until we do.
100       console.log('Received constants late.');
101
102       /**
103        * Observer that starts the tests once we've received the constants.
104        */
105       function ConstantsObserver() {
106         this.testStarted_ = false;
107       }
108
109       ConstantsObserver.prototype.onReceivedConstants = function() {
110         if (!this.testStarted_) {
111           this.testStarted_ = true;
112           // Stat test asynchronously, to avoid running a nested test function,
113           // and so we don't call any constants observers used by individual
114           // tests.
115           window.setTimeout(runTest, 0);
116         }
117       };
118
119       g_browser.addConstantsObserver(new ConstantsObserver());
120     }
121   };
122
123   /**
124    * A callback function for use by asynchronous Tasks that need a return value
125    * from the NetInternalsTest::MessageHandler.  Must be null when no such
126    * Task is running.  Set by NetInternalsTest.setCallback.  Automatically
127    * cleared once called.  Must only be called through
128    * NetInternalsTest::MessageHandler::RunCallback, from the browser process.
129    */
130   NetInternalsTest.callback = null;
131
132   /**
133    * Sets NetInternalsTest.callback.  Any arguments will be passed to the
134    * callback function.
135    * @param {function} callbackFunction Callback function to be called from the
136    *     browser.
137    */
138   NetInternalsTest.setCallback = function(callbackFunction) {
139     // Make sure no Task has already set the callback function.
140     assertEquals(null, NetInternalsTest.callback);
141
142     // Wrap |callbackFunction| in a function that clears
143     // |NetInternalsTest.callback| before calling |callbackFunction|.
144     var callbackFunctionWrapper = function() {
145       NetInternalsTest.callback = null;
146       callbackFunction.apply(null, Array.prototype.slice.call(arguments));
147     };
148
149     // Wrap |callbackFunctionWrapper| with test framework code.
150     NetInternalsTest.callback =
151         activeTest_.continueTest(WhenTestDone.EXPECT, callbackFunctionWrapper);
152   };
153
154   /**
155    * Returns the first tbody that's a descendant of |ancestorId|. If the
156    * specified node is itself a table body node, just returns that node.
157    * Returns null if no such node is found.
158    * @param {string} ancestorId ID of an HTML element with a tbody descendant.
159    * @return {node} The tbody node, or null.
160    */
161   NetInternalsTest.getTbodyDescendent = function(ancestorId) {
162     if ($(ancestorId).nodeName == 'TBODY')
163       return $(ancestorId);
164     // The tbody element of the first styled table in |parentId|.
165     return document.querySelector('#' + ancestorId + ' tbody');
166   };
167
168   /**
169    * Finds the first tbody that's a descendant of |ancestorId|, including the
170    * |ancestorId| element itself, and returns the number of rows it has.
171    * Returns -1 if there's no such table.  Excludes hidden rows.
172    * @param {string} ancestorId ID of an HTML element with a tbody descendant.
173    * @return {number} Number of rows the style table's body has.
174    */
175   NetInternalsTest.getTbodyNumRows = function(ancestorId) {
176     // The tbody element of the first styled table in |parentId|.
177     var tbody = NetInternalsTest.getTbodyDescendent(ancestorId);
178     if (!tbody)
179       return -1;
180     var visibleChildren = 0;
181     for (var i = 0; i < tbody.children.length; ++i) {
182       if (NetInternalsTest.nodeIsVisible(tbody.children[i]))
183         ++visibleChildren;
184     }
185     return visibleChildren;
186   };
187
188   /**
189    * Finds the first tbody that's a descendant of |ancestorId|, including the
190    * |ancestorId| element itself, and checks if it has exactly |expectedRows|
191    * rows.  Does not count hidden rows.
192    * @param {string} ancestorId ID of an HTML element with a tbody descendant.
193    * @param {number} expectedRows Expected number of rows in the table.
194    */
195   NetInternalsTest.checkTbodyRows = function(ancestorId, expectedRows) {
196     expectEquals(expectedRows,
197                  NetInternalsTest.getTbodyNumRows(ancestorId),
198                  'Incorrect number of rows in ' + ancestorId);
199   };
200
201   /**
202    * Finds the tbody that's a descendant of |ancestorId|, including the
203    * |ancestorId| element itself, and returns the text of the specified cell.
204    * If the cell does not exist, throws an exception.  Skips over hidden rows.
205    * @param {string} ancestorId ID of an HTML element with a tbody descendant.
206    * @param {number} row Row of the value to retrieve.
207    * @param {number} column Column of the value to retrieve.
208    */
209   NetInternalsTest.getTbodyText = function(ancestorId, row, column) {
210     var tbody = NetInternalsTest.getTbodyDescendent(ancestorId);
211     var currentChild = tbody.children[0];
212     while (currentChild) {
213       if (NetInternalsTest.nodeIsVisible(currentChild)) {
214         if (row == 0)
215           return currentChild.children[column].innerText;
216         --row;
217       }
218       currentChild = currentChild.nextElementSibling;
219     }
220     return 'invalid row';
221   };
222
223   /**
224    * Returns the view and menu item node for the tab with given id.
225    * Asserts if the tab can't be found.
226    * @param {string}: tabId Id of the tab to lookup.
227    * @return {Object}
228    */
229   NetInternalsTest.getTab = function(tabId) {
230     var tabSwitcher = MainView.getInstance().tabSwitcher();
231     var view = tabSwitcher.getTabView(tabId);
232     var menuItem = tabSwitcher.getMenuItemNode_(tabId);
233
234     assertNotEquals(view, undefined, tabId + ' does not exist.');
235     assertNotEquals(menuItem, undefined, tabId + ' does not exist.');
236
237     return {
238       view: view,
239       menuItem: menuItem,
240     };
241   };
242
243   /**
244    * Returns true if the node is visible.
245    * @param {Node}: node Node to check the visibility state of.
246    * @return {bool} Whether or not the node is visible.
247    */
248   NetInternalsTest.nodeIsVisible = function(node) {
249     return node.style.display != 'none';
250   };
251
252   /**
253    * Returns true if the specified tab's handle is visible, false otherwise.
254    * Asserts if the handle can't be found.
255    * @param {string}: tabId Id of the tab to check.
256    * @return {bool} Whether or not the tab's handle is visible.
257    */
258   NetInternalsTest.tabHandleIsVisible = function(tabId) {
259     var tabHandleNode = NetInternalsTest.getTab(tabId).menuItem;
260     return NetInternalsTest.nodeIsVisible(tabHandleNode);
261   };
262
263   /**
264    * Returns the id of the currently active tab.
265    * @return {string} ID of the active tab.
266    */
267   NetInternalsTest.getActiveTabId = function() {
268     return MainView.getInstance().tabSwitcher().getActiveTabId();
269   };
270
271   /**
272    * Returns the tab id of a tab, given its associated URL hash value.  Asserts
273    *     if |hash| has no associated tab.
274    * @param {string}: hash Hash associated with the tab to return the id of.
275    * @return {string} String identifier of the tab with the given hash.
276    */
277   NetInternalsTest.getTabId = function(hash) {
278     /**
279      * Map of tab handle names to location hashes.  Since the text fixture
280      * must be runnable independent of net-internals, for generating the
281      * test's cc files, must be careful to only create this map while a test
282      * is running.
283      * @type {object.<string, string>}
284      */
285     var hashToTabHandleIdMap = {
286       capture: CaptureView.TAB_ID,
287       export: ExportView.TAB_ID,
288       import: ImportView.TAB_ID,
289       proxy: ProxyView.TAB_ID,
290       events: EventsView.TAB_ID,
291       waterfall: WaterfallView.TAB_ID,
292       timeline: TimelineView.TAB_ID,
293       dns: DnsView.TAB_ID,
294       sockets: SocketsView.TAB_ID,
295       spdy: SpdyView.TAB_ID,
296       quic: QuicView.TAB_ID,
297       httpCache: HttpCacheView.TAB_ID,
298       modules: ModulesView.TAB_ID,
299       tests: TestView.TAB_ID,
300       hsts: HSTSView.TAB_ID,
301       logs: LogsView.TAB_ID,
302       prerender: PrerenderView.TAB_ID,
303       bandwidth: BandwidthView.TAB_ID,
304       chromeos: CrosView.TAB_ID,
305       visualizer: CrosLogVisualizerView.TAB_ID
306     };
307
308     assertEquals(typeof hashToTabHandleIdMap[hash], 'string',
309                  'Invalid tab anchor: ' + hash);
310     var tabId = hashToTabHandleIdMap[hash];
311     assertEquals('object', typeof NetInternalsTest.getTab(tabId),
312                  'Invalid tab: ' + tabId);
313     return tabId;
314   };
315
316   /**
317    * Switches to the specified tab.
318    * @param {string}: hash Hash associated with the tab to switch to.
319    */
320   NetInternalsTest.switchToView = function(hash) {
321     var tabId = NetInternalsTest.getTabId(hash);
322
323     // Make sure the tab handle is visible, as we only simulate normal usage.
324     expectTrue(NetInternalsTest.tabHandleIsVisible(tabId),
325                tabId + ' does not have a visible tab handle.');
326     var tabHandleNode = NetInternalsTest.getTab(tabId).menuItem;
327
328     // Simulate selecting the menuitem.
329     tabHandleNode.selected = true;
330     tabHandleNode.parentNode.onchange();
331
332     // Make sure the hash changed.
333     assertEquals('#' + hash, document.location.hash);
334
335     // Make sure only the specified tab is visible.
336     var tabSwitcher = MainView.getInstance().tabSwitcher();
337     var tabIdToView = tabSwitcher.getAllTabViews();
338     for (var curTabId in tabIdToView) {
339       expectEquals(curTabId == tabId,
340                    tabSwitcher.getTabView(curTabId).isVisible(),
341                    curTabId + ': Unexpected visibility state.');
342     }
343   };
344
345   /**
346    * Checks the visibility of all tab handles against expected values.
347    * @param {object.<string, bool>}: tabVisibilityState Object with a an entry
348    *     for each tab's hash, and a bool indicating if it should be visible or
349    *     not.
350    * @param {bool+}: tourTabs True if tabs expected to be visible should should
351    *     each be navigated to as well.
352    */
353   NetInternalsTest.checkTabHandleVisibility = function(tabVisibilityState,
354                                                        tourTabs) {
355     // The currently active tab should have a handle that is visible.
356     expectTrue(NetInternalsTest.tabHandleIsVisible(
357                    NetInternalsTest.getActiveTabId()));
358
359     // Check visibility state of all tabs.
360     var tabCount = 0;
361     for (var hash in tabVisibilityState) {
362       var tabId = NetInternalsTest.getTabId(hash);
363       assertEquals('object', typeof NetInternalsTest.getTab(tabId),
364                    'Invalid tab: ' + tabId);
365       expectEquals(tabVisibilityState[hash],
366                    NetInternalsTest.tabHandleIsVisible(tabId),
367                    tabId + ' visibility state is unexpected.');
368       if (tourTabs && tabVisibilityState[hash])
369         NetInternalsTest.switchToView(hash);
370       tabCount++;
371     }
372
373     // Check that every tab was listed.
374     var tabSwitcher = MainView.getInstance().tabSwitcher();
375     var tabIdToView = tabSwitcher.getAllTabViews();
376     var expectedTabCount = 0;
377     for (tabId in tabIdToView)
378       expectedTabCount++;
379     expectEquals(tabCount, expectedTabCount);
380   };
381
382   /**
383    * This class allows multiple Tasks to be queued up to be run sequentially.
384    * A Task can wait for asynchronous callbacks from the browser before
385    * completing, at which point the next queued Task will be run.
386    * @param {bool}: endTestWhenDone True if testDone should be called when the
387    *     final task completes.
388    * @param {NetInternalsTest}: test Test fixture passed to Tasks.
389    * @constructor
390    */
391   NetInternalsTest.TaskQueue = function(endTestWhenDone) {
392     // Test fixture for passing to tasks.
393     this.tasks_ = [];
394     this.isRunning_ = false;
395     this.endTestWhenDone_ = endTestWhenDone;
396   };
397
398   NetInternalsTest.TaskQueue.prototype = {
399     /**
400      * Adds a Task to the end of the queue.  Each Task may only be added once
401      * to a single queue.  Also passes the text fixture to the Task.
402      * @param {Task}: task The Task to add.
403      */
404     addTask: function(task) {
405       this.tasks_.push(task);
406       task.setTaskQueue_(this);
407     },
408
409     /**
410      * Adds a Task to the end of the queue.  The task will call the provided
411      * function, and then complete.
412      * @param {function}: taskFunction The function the task will call.
413      */
414     addFunctionTask: function(taskFunction) {
415       this.addTask(new NetInternalsTest.CallFunctionTask(taskFunction));
416     },
417
418     /**
419      * Starts running the Tasks in the queue, passing its arguments, if any,
420      * to the first Task's start function.  May only be called once.
421      */
422     run: function() {
423       assertFalse(this.isRunning_);
424       this.isRunning_ = true;
425       this.runNextTask_(Array.prototype.slice.call(arguments));
426     },
427
428     /**
429      * If there are any Tasks in |tasks_|, removes the first one and runs it.
430      * Otherwise, sets |isRunning_| to false.  If |endTestWhenDone_| is true,
431      * calls testDone.
432      * @param {array} argArray arguments to be passed on to next Task's start
433      *     method.  May be a 0-length array.
434      */
435     runNextTask_: function(argArray) {
436       assertTrue(this.isRunning_);
437       // The last Task may have used |NetInternalsTest.callback|.  Make sure
438       // it's now null.
439       expectEquals(null, NetInternalsTest.callback);
440
441       if (this.tasks_.length > 0) {
442         var nextTask = this.tasks_.shift();
443         nextTask.start.apply(nextTask, argArray);
444       } else {
445         this.isRunning_ = false;
446         if (this.endTestWhenDone_)
447           testDone();
448       }
449     }
450   };
451
452   /**
453    * A Task that can be added to a TaskQueue.  A Task is started with a call to
454    * the start function, and must call its own onTaskDone when complete.
455    * @constructor
456    */
457   NetInternalsTest.Task = function() {
458     this.taskQueue_ = null;
459     this.isDone_ = false;
460     this.completeAsync_ = false;
461   };
462
463   NetInternalsTest.Task.prototype = {
464     /**
465      * Starts running the Task.  Only called once per Task, must be overridden.
466      * Any arguments passed to the last Task's onTaskDone, or to run (If the
467      * first task) will be passed along.
468      */
469     start: function() {
470       assertNotReached('Start function not overridden.');
471     },
472
473     /**
474      * Updates value of |completeAsync_|.  If set to true, the next Task will
475      * start asynchronously.  Useful if the Task completes on an event that
476      * the next Task may care about.
477      */
478     setCompleteAsync: function(value) {
479       this.completeAsync_ = value;
480     },
481
482     /**
483      * @return {bool} True if this task has completed by calling onTaskDone.
484      */
485     isDone: function() {
486       return this.isDone_;
487     },
488
489     /**
490      * Sets the TaskQueue used by the task in the onTaskDone function.  May only
491      * be called by the TaskQueue.
492      * @param {TaskQueue}: taskQueue The TaskQueue |this| has been added to.
493      */
494     setTaskQueue_: function(taskQueue) {
495       assertEquals(null, this.taskQueue_);
496       this.taskQueue_ = taskQueue;
497     },
498
499     /**
500      * Must be called when a task is complete, and can only be called once for a
501      * task.  Runs the next task, if any, passing along all arguments.
502      */
503     onTaskDone: function() {
504       assertFalse(this.isDone_);
505       this.isDone_ = true;
506
507       // Function to run the next task in the queue.
508       var runNextTask = this.taskQueue_.runNextTask_.bind(
509                             this.taskQueue_,
510                             Array.prototype.slice.call(arguments));
511
512       // If we need to start the next task asynchronously, we need to wrap
513       // it with the test framework code.
514       if (this.completeAsync_) {
515         window.setTimeout(activeTest_.continueTest(WhenTestDone.EXPECT,
516                                                    runNextTask),
517                           0);
518         return;
519       }
520
521       // Otherwise, just run the next task directly.
522       runNextTask();
523     },
524   };
525
526   /**
527    * A Task that can be added to a TaskQueue.  A Task is started with a call to
528    * the start function, and must call its own onTaskDone when complete.
529    * @extends {NetInternalsTest.Task}
530    * @constructor
531    */
532   NetInternalsTest.CallFunctionTask = function(taskFunction) {
533     NetInternalsTest.Task.call(this);
534     assertEquals('function', typeof taskFunction);
535     this.taskFunction_ = taskFunction;
536   };
537
538   NetInternalsTest.CallFunctionTask.prototype = {
539     __proto__: NetInternalsTest.Task.prototype,
540
541     /**
542      * Runs the function and then completes.  Passes all arguments, if any,
543      * along to the function.
544      */
545     start: function() {
546       this.taskFunction_.apply(null, Array.prototype.slice.call(arguments));
547       this.onTaskDone();
548     }
549   };
550
551   /**
552    * A Task that converts the given path into a URL served by the TestServer.
553    * The resulting URL will be passed to the next task.  Will also start the
554    * TestServer, if needed.
555    * @param {string} path Path to convert to a URL.
556    * @extends {NetInternalsTest.Task}
557    * @constructor
558    */
559   NetInternalsTest.GetTestServerURLTask = function(path) {
560     NetInternalsTest.Task.call(this);
561     assertEquals('string', typeof path);
562     this.path_ = path;
563   };
564
565   NetInternalsTest.GetTestServerURLTask.prototype = {
566     __proto__: NetInternalsTest.Task.prototype,
567
568     /**
569      * Sets |NetInternals.callback|, and sends the path to the browser process.
570      */
571     start: function() {
572       NetInternalsTest.setCallback(this.onURLReceived_.bind(this));
573       chrome.send('getTestServerURL', [this.path_]);
574     },
575
576     /**
577      * Completes the Task, passing the url on to the next Task.
578      * @param {string} url TestServer URL of the input path.
579      */
580     onURLReceived_: function(url) {
581       assertEquals('string', typeof url);
582       this.onTaskDone(url);
583     }
584   };
585
586   /**
587    * A Task that creates an incognito window and only completes once it has
588    * navigated to about:blank.  The waiting is required to avoid reentrancy
589    * issues, since the function to create the incognito browser also waits
590    * for the navigation to complete.  May not be called if there's already an
591    * incognito browser in existence.
592    * @extends {NetInternalsTest.Task}
593    * @constructor
594    */
595   NetInternalsTest.CreateIncognitoBrowserTask = function() {
596     NetInternalsTest.Task.call(this);
597   };
598
599   NetInternalsTest.CreateIncognitoBrowserTask.prototype = {
600     __proto__: NetInternalsTest.Task.prototype,
601
602     /**
603      * Tells the browser process to create an incognito browser, and sets
604      * up a callback to be called on completion.
605      */
606     start: function() {
607       // Reuse the BrowserBridge's callback mechanism, since it's already
608       // wrapped in our test harness.
609       assertEquals('undefined',
610                    typeof g_browser.onIncognitoBrowserCreatedForTest);
611       g_browser.onIncognitoBrowserCreatedForTest =
612           this.onIncognitoBrowserCreatedForTest.bind(this);
613
614       chrome.send('createIncognitoBrowser');
615     },
616
617     /**
618      * Deletes the callback function, and completes the task.
619      */
620     onIncognitoBrowserCreatedForTest: function() {
621       delete g_browser.onIncognitoBrowserCreatedForTest;
622       this.onTaskDone();
623     }
624   };
625
626   /**
627    * Returns a task that closes an incognito window created with the task
628    * above.  May only be called if there's an incognito window created by
629    * the above function that has yet to be closed.  Returns immediately.
630    * @return {Task} Task that closes incognito browser window.
631    */
632   NetInternalsTest.getCloseIncognitoBrowserTask = function() {
633     return new NetInternalsTest.CallFunctionTask(
634         function() {
635           chrome.send('closeIncognitoBrowser');
636         });
637   };
638
639   /**
640    * Returns true if a node does not have a 'display' property of 'none'.
641    * @param {node}: node The node to check.
642    */
643   NetInternalsTest.isDisplayed = function(node) {
644     var style = getComputedStyle(node);
645     return style.getPropertyValue('display') != 'none';
646   };
647
648   /**
649    * Creates a new NetLog source.  Note that the id may conflict with events
650    * received from the browser.
651    * @param {int}: type The source type.
652    * @param {int}: id The source id.
653    * @constructor
654    */
655   NetInternalsTest.Source = function(type, id) {
656     assertNotEquals(getKeyWithValue(EventSourceType, type), '?');
657     assertGE(id, 0);
658     this.type = type;
659     this.id = id;
660   };
661
662   /**
663    * Creates a new NetLog event.
664    * @param {Source}: source The source associated with the event.
665    * @param {int}: type The event id.
666    * @param {int}: time When the event occurred.
667    * @param {int}: phase The event phase.
668    * @param {object}: params The event parameters.  May be null.
669    * @constructor
670    */
671   NetInternalsTest.Event = function(source, type, time, phase, params) {
672     assertNotEquals(getKeyWithValue(EventType, type), '?');
673     assertNotEquals(getKeyWithValue(EventPhase, phase), '?');
674
675     this.source = source;
676     this.phase = phase;
677     this.type = type;
678     this.time = '' + time;
679     this.phase = phase;
680     if (params)
681       this.params = params;
682   };
683
684   /**
685    * Creates a new NetLog begin event.  Parameters are the same as Event,
686    * except there's no |phase| argument.
687    * @see Event
688    */
689   NetInternalsTest.createBeginEvent = function(source, type, time, params) {
690     return new NetInternalsTest.Event(source, type, time,
691                                       EventPhase.PHASE_BEGIN, params);
692   };
693
694   /**
695    * Creates a new NetLog end event.  Parameters are the same as Event,
696    * except there's no |phase| argument.
697    * @see Event
698    */
699   NetInternalsTest.createEndEvent = function(source, type, time, params) {
700     return new NetInternalsTest.Event(source, type, time,
701                                       EventPhase.PHASE_END, params);
702   };
703
704   /**
705    * Creates a new NetLog end event matching the given begin event.
706    * @param {Event}: beginEvent The begin event.  Returned event will have the
707    *                 same source and type.
708    * @param {int}: time When the event occurred.
709    * @param {object}: params The event parameters.  May be null.
710    * @see Event
711    */
712   NetInternalsTest.createMatchingEndEvent = function(beginEvent, time, params) {
713     return NetInternalsTest.createEndEvent(
714                beginEvent.source, beginEvent.type, time, params);
715   };
716
717   /**
718    * Checks that only the given status view node is visible.
719    * @param {string}: nodeId ID of the node that should be visible.
720    */
721   NetInternalsTest.expectStatusViewNodeVisible = function(nodeId) {
722     var allIds = [
723       CaptureStatusView.MAIN_BOX_ID,
724       LoadedStatusView.MAIN_BOX_ID,
725       HaltedStatusView.MAIN_BOX_ID
726     ];
727
728     for (var i = 0; i < allIds.length; ++i) {
729       var curId = allIds[i];
730       expectEquals(nodeId == curId, NetInternalsTest.nodeIsVisible($(curId)));
731     }
732   };
733
734   return NetInternalsTest;
735 })();