selenium-webdriver 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,598 +0,0 @@
1
- /** @license
2
- Copyright 2007-2009 WebDriver committers
3
- Copyright 2007-2009 Google Inc.
4
-
5
- Licensed under the Apache License, Version 2.0 (the "License");
6
- you may not use this file except in compliance with the License.
7
- You may obtain a copy of the License at
8
-
9
- http://www.apache.org/licenses/LICENSE-2.0
10
-
11
- Unless required by applicable law or agreed to in writing, software
12
- distributed under the License is distributed on an "AS IS" BASIS,
13
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- See the License for the specific language governing permissions and
15
- limitations under the License.
16
- */
17
-
18
- /**
19
- * @fileoverview A xUnit framework for writing unit tests using the WebDriver
20
- * JavaScript API.
21
- * Example Usage:
22
- * <code>
23
- * goog.require('webdriver.TestRunner');
24
- * goog.require('webdriver.factory');
25
- *
26
- * function testGoogleSearch(driver) {
27
- * driver.get('http://www.google.com');
28
- * driver.findElement({name: 'q'}).sendKeys('webdriver');
29
- * driver.findElement({name: 'btnG'}).click();
30
- * }
31
- *
32
- * window.onload = function() {
33
- * new webdriver.TestRunner(webdriver.factory.createLocalWebDriver).
34
- * go();
35
- * };
36
- * </code>
37
- *
38
- * @author jmleyba@gmail.com (Jason Leyba)
39
- */
40
-
41
- goog.provide('webdriver.TestCase');
42
- goog.provide('webdriver.TestResult');
43
- goog.provide('webdriver.TestRunner');
44
-
45
- goog.require('goog.Uri');
46
- goog.require('goog.dom');
47
- goog.require('goog.style');
48
- goog.require('webdriver.WebDriver.EventType');
49
- goog.require('webdriver.factory');
50
- goog.require('webdriver.logging');
51
- goog.require('webdriver.timing');
52
-
53
-
54
- /**
55
- * Represents a single test function to be executed by the
56
- * {@code webdriver.TestRunner}.
57
- * @param {string} name The name of the test function.
58
- * @param {function} testFn The test function that will be executed.
59
- * @constructor
60
- */
61
- webdriver.TestCase = function(name, testFn) {
62
- this.name = name;
63
- this.testFn = testFn;
64
- };
65
-
66
-
67
- /**
68
- * Stores the result of a {@code webdriver.TestCase}.
69
- * @param {webdriver.TestCase} testCase The test this is a result for.
70
- * @param {boolean} passed Whether the test passed.
71
- * @param {string} opt_errMsg The error message describing the test failure.
72
- * @constructor
73
- */
74
- webdriver.TestResult = function(testCase, passed, opt_errMsg) {
75
- this.testCase = testCase;
76
- this.passed = passed;
77
- this.errMsg = opt_errMsg || '';
78
- };
79
-
80
-
81
- /**
82
- * @return {string} A summary of this result.
83
- */
84
- webdriver.TestResult.prototype.getSummary = function() {
85
- if (this.passed) {
86
- return this.testCase.name + ' [PASSED]';
87
- } else {
88
- return this.testCase.name + ' [FAILED]\n ' +
89
- this.errMsg.replace(/\n/g, '\n ');
90
- }
91
- };
92
-
93
-
94
- /**
95
- * The actual test runner. When created, scans the global scope for all test
96
- * functions (those functions whose name is prefixed with "test"). Once
97
- * started, the TestRunner will execute each test case and pass a new
98
- * {@code webdriver.WebDriver} instance to the test for it to issue commands to.
99
- * The driver will be paused while commands are collected from the test
100
- * function. Once resumed, the TestRunner will listen for the driver's
101
- * {@code IDLE} and {@code ERROR} events to determine when the test is done and
102
- * what its result was.
103
- * @param {function} opt_driverFactoryFn The factory function to call for
104
- * creating new {@code webdriver.WebDriver} instances. A new instance will
105
- * be created for each test case; defaults to
106
- * {@code webdriver.factory.createLocalWebDriver}.
107
- * @param {goog.dom.DomHelper} opt_dom A DomHelper for the content window to
108
- * scan for test functions.
109
- * @constructor
110
- */
111
- webdriver.TestRunner = function(opt_driverFactoryFn, opt_dom) {
112
-
113
- /**
114
- * Factory function to call for creating new instances of
115
- * {@code webdriver.WebDriver}.
116
- * @type {function}
117
- * @private
118
- */
119
- this.driverFactoryFn_ =
120
- opt_driverFactoryFn || webdriver.factory.createLocalWebDriver;
121
-
122
- /**
123
- * DomHelper for the content window to scan for test functions.
124
- * @type {goog.dom.DomHelper}
125
- * @private
126
- */
127
- this.dom_ = opt_dom || goog.dom.getDomHelper();
128
-
129
- /**
130
- * Whether this instance has started executing tests.
131
- * @type {boolean}
132
- * @private
133
- */
134
- this.started_ = false;
135
-
136
- /**
137
- * Whether this instance has finished executing tests.
138
- * @type {boolean}
139
- * @private
140
- */
141
- this.finished_ = false;
142
-
143
- /**
144
- * The tests discovered on the page that will be executed.
145
- * @type {Array.<webdriver.TestCase>}
146
- * @private
147
- */
148
- this.tests_ = [];
149
-
150
- /**
151
- * A hash of all known test case names.
152
- * @type {Object}
153
- * @private
154
- */
155
- this.testNames_ = {};
156
-
157
- /**
158
- * The index of the test currently running.
159
- * @type {number}
160
- * @private
161
- */
162
- this.currentTest_ = -1;
163
-
164
- /**
165
- * Results for the executed tests.
166
- * @type {Array.<webdriver.TestResult>}
167
- * @private
168
- */
169
- this.results_ = [];
170
-
171
- /**
172
- * The {@code setUp} function, if any, to call before each test is executed.
173
- * @type {function}
174
- * @private
175
- */
176
- this.setUpFn_ = null;
177
-
178
- /**
179
- * The {@code tearDown} function, if any, to call after each test is executed.
180
- * @type {function}
181
- * @private
182
- */
183
- this.tearDownFn_ = null;
184
-
185
- /**
186
- * The number of tests that have passed.
187
- * @type {number}
188
- * @private
189
- */
190
- this.numPassing_ = 0;
191
-
192
- /**
193
- * DOM element to log results to; lazily initialized in
194
- * {@code initResultsSection_}.
195
- * @type {Element}
196
- * @private
197
- */
198
- this.resultsDiv_ = null;
199
-
200
- /**
201
- * DOM element that logs the current progress of the TestRunner; lazily
202
- * initialized in {@code initResultsSection_}.
203
- * @type {Element}
204
- * @private
205
- */
206
- this.headerDiv_ = null;
207
-
208
- /**
209
- * Element in the progress message showing the number of tests that have
210
- * passed; lazily initialized in {@code initResultsSection_}.
211
- * @type {Element}
212
- * @private
213
- */
214
- this.numPassedSpan_ = null;
215
-
216
- /**
217
- * Element in the progress message showing the number of tests that have yet
218
- * to be executed; lazily initialized in {@code initResultsSection_}.
219
- * @type {Element}
220
- * @private
221
- */
222
- this.numPendingSpan_ = null;
223
-
224
- this.findTestFunctions_();
225
- this.initResultsSection_();
226
- };
227
-
228
-
229
- webdriver.TestRunner.SINGLETON = null;
230
-
231
-
232
- webdriver.TestRunner.start = function(factoryFn) {
233
- if (!webdriver.TestRunner.SINGLETON) {
234
- webdriver.TestRunner.SINGLETON = new webdriver.TestRunner(factoryFn);
235
- webdriver.TestRunner.SINGLETON.go();
236
- }
237
- return webdriver.TestRunner.SINGLETON;
238
- };
239
- goog.exportSymbol('WD_getTestRunner', webdriver.TestRunner.start);
240
-
241
-
242
- /**
243
- * Scans this instance's test window for any global test functions and for the
244
- * setUp and tearDown functions.
245
- * @private
246
- */
247
- webdriver.TestRunner.prototype.findTestFunctions_ = function() {
248
- webdriver.logging.info('Locating test functions');
249
-
250
- var uri = new goog.Uri(this.dom_.getWindow().location.href);
251
- var testName = uri.getParameterValue('test');
252
- var matchFn;
253
- if (testName) {
254
- webdriver.logging.info('...searching for test matching "' + testName +'"');
255
- matchFn = function(prop) {
256
- return testName == prop;
257
- };
258
- } else {
259
- var testRegex = /^test\w+$/;
260
- webdriver.logging.info('...searching for tests matching ' + testRegex);
261
- matchFn = function(prop) {
262
- return testRegex.test(prop);
263
- };
264
- }
265
-
266
-
267
- // This won't work on IE. There's a different way of querying for global
268
- // functions in IE (of course).
269
- // TODO(jmleyba): Look it up and make it so.
270
- var win = this.dom_.getWindow();
271
- for (var prop in win) {
272
- if (matchFn(prop) && goog.isFunction(win[prop])) {
273
- this.tests_.push(new webdriver.TestCase(prop, win[prop]));
274
- if (prop in this.testNames_) {
275
- webdriver.logging.error('Duplicate test name found: ' + prop);
276
- } else {
277
- this.testNames_[prop] = true;
278
- }
279
- }
280
- }
281
- webdriver.logging.info('...found ' + this.tests_.length + ' test(s)');
282
-
283
- function getGlobal(name) {
284
- var fn = goog.global[name];
285
- return goog.isFunction(fn) ? fn : goog.nullFunction;
286
- }
287
- this.setUpPageFn_ = getGlobal('setUpPage');
288
- this.setUpFn_ = getGlobal('setUp');
289
- this.tearDownFn_ = getGlobal('tearDown');
290
- this.tearDownPageFn_ = getGlobal('tearDownPage');
291
- };
292
-
293
-
294
- /**
295
- * Initializes the result DOM for reporting results at the top of the page.
296
- * @private
297
- */
298
- webdriver.TestRunner.prototype.initResultsSection_ = function() {
299
- this.resultsDiv_ = this.dom_.createDom('DIV');
300
- var doc = this.dom_.getDocument();
301
- if (doc.body.firstChild) {
302
- goog.dom.insertSiblingBefore(this.resultsDiv_, doc.body.firstChild);
303
- } else {
304
- goog.dom.appendChild(doc.body, this.resultsDiv_);
305
- }
306
-
307
- this.headerDiv_ = this.dom_.createDom('DIV');
308
- goog.style.setStyle(this.headerDiv_, 'fontFamily', 'Courier;');
309
- goog.style.setStyle(this.headerDiv_, 'fontSize', '10pt;');
310
- goog.style.setStyle(this.headerDiv_, 'fontWeight', 'bold');
311
- goog.dom.appendChild(this.resultsDiv_, this.headerDiv_);
312
-
313
- if (this.tests_.length) {
314
- this.numPassedSpan_ = this.dom_.createDom('SPAN');
315
- goog.dom.setTextContent(this.numPassedSpan_, '0');
316
- goog.dom.appendChild(this.headerDiv_, this.numPassedSpan_);
317
- goog.dom.appendChild(this.headerDiv_,
318
- this.dom_.createTextNode('/' + this.tests_.length + ' tests passed ('));
319
-
320
- this.numPendingSpan_ = this.dom_.createDom('SPAN');
321
- goog.dom.setTextContent(this.numPendingSpan_, this.tests_.length);
322
- goog.dom.appendChild(this.headerDiv_, this.numPendingSpan_);
323
- goog.dom.appendChild(this.headerDiv_,
324
- this.dom_.createTextNode(' tests pending)'));
325
- } else {
326
- goog.dom.setTextContent(this.headerDiv_, 'No tests to run');
327
- }
328
- };
329
-
330
-
331
- /**
332
- * Reports a result of a test on the page.
333
- * @private
334
- */
335
- webdriver.TestRunner.prototype.reportResult_ = function(result, driver) {
336
- if (driver) {
337
- driver.dispose();
338
- }
339
- this.errorListener_ = null;
340
- // TODO(jmleyba): Should quit the driver for remote driver instances.
341
-
342
- this.results_.push(result);
343
-
344
- var resultDiv = this.dom_.createDom('DIV');
345
- goog.style.setStyle(resultDiv, 'fontFamily', 'Courier');
346
- goog.style.setStyle(resultDiv, 'fontSize', '9pt');
347
- if (result.passed) {
348
- goog.dom.appendChild(this.resultsDiv_, resultDiv);
349
- if (!this.firstPassing_) {
350
- this.firstPassing_ = resultDiv;
351
- }
352
- this.numPassing_ += 1;
353
- goog.dom.setTextContent(resultDiv, result.testCase.name + ' [PASSED]');
354
- goog.style.setStyle(resultDiv, 'color', 'green');
355
- } else {
356
- if (this.firstPassing_) {
357
- goog.dom.insertSiblingBefore(resultDiv, this.firstPassing_);
358
- } else {
359
- goog.dom.appendChild(this.resultsDiv_, resultDiv);
360
- }
361
- goog.dom.setTextContent(resultDiv, result.testCase.name + ' [FAILED]');
362
- goog.style.setStyle(resultDiv, 'color', 'red');
363
-
364
- var uri = new goog.Uri(this.dom_.getWindow().location.href);
365
- uri.getQueryData().clear();
366
- uri.getQueryData().add('test', result.testCase.name);
367
- var link = this.dom_.createDom('A', {
368
- 'href': uri.toString()
369
- });
370
- goog.dom.setTextContent(link, '(run individually)');
371
- goog.dom.appendChild(resultDiv, link);
372
-
373
-
374
- var reason = this.dom_.createDom('DIV');
375
- goog.style.setStyle(reason, 'color', 'black');
376
- goog.dom.appendChild(resultDiv, reason);
377
- reason.innerHTML = webdriver.logging.jsStringToHtml(result.errMsg);
378
- webdriver.logging.warn(result.errMsg);
379
- }
380
-
381
- goog.dom.setTextContent(this.numPassedSpan_, this.numPassing_);
382
- goog.dom.setTextContent(this.numPendingSpan_,
383
- (this.tests_.length - this.currentTest_ - 1));
384
- webdriver.logging.info('scheduling next test');
385
- webdriver.timing.setTimeout(goog.bind(this.executeNextTest_, this), 0);
386
- };
387
-
388
-
389
- /**
390
- * Event handler for when a test pauses the command processing. Adds a button
391
- * to the DOM that resumes the driver when clicked.
392
- * @param {goog.event.Event} e The pause event to handle. The target of this
393
- * event will be the paused {@code webdriver.WebDriver} instance.
394
- * @private
395
- */
396
- webdriver.TestRunner.prototype.onPause_ = function(e) {
397
- this.pausedDriver = e.target;
398
- if (!this.pausedButton_) {
399
- this.pausedButton_ = this.dom_.createDom('INPUT', {
400
- type: 'button',
401
- style: 'margin-left: 10px',
402
- value: 'Click to resume'
403
- });
404
- this.pausedDiv_ = this.dom_.createDom('DIV', null,
405
- this.dom_.createTextNode('WebDriver paused'),
406
- this.pausedButton_);
407
- goog.style.setStyle(this.pausedDiv_, 'width', '100%');
408
- goog.style.setStyle(this.pausedDiv_, 'backgroundColor', 'yellow');
409
- goog.style.setStyle(this.pausedDiv_, 'fontFamily', 'Courier;');
410
- goog.style.setStyle(this.pausedDiv_, 'fontSize', '10pt;');
411
- goog.style.setStyle(this.pausedDiv_, 'fontWeight', 'bold');
412
- goog.dom.insertSiblingAfter(this.pausedDiv_, this.headerDiv_);
413
- }
414
-
415
- goog.style.setStyle(this.pausedDiv_, 'display', 'block');
416
- this.pausedButton_.disabled = false;
417
- goog.events.listenOnce(this.pausedButton_, goog.events.EventType.CLICK,
418
- function() {
419
- this.pausedButton_.disabled = true;
420
- goog.style.setStyle(this.pausedDiv_, 'display', 'none');
421
- this.pausedDriver.resume();
422
- }, false, this);
423
- };
424
-
425
-
426
- /**
427
- * Kicks off the execution of the tests gathered by this instance. This is a
428
- * no-op if this instance has already started.
429
- */
430
- webdriver.TestRunner.prototype.go = function() {
431
- if (this.started_) {
432
- return;
433
- }
434
- this.started_ = true;
435
- this.setUpPageFn_();
436
- this.executeNextTest_();
437
- };
438
-
439
-
440
- /**
441
- * @return {boolean} Whether this instance has finished executing tests.
442
- */
443
- webdriver.TestRunner.prototype.isFinished = function() {
444
- return this.finished_;
445
- };
446
-
447
-
448
- /**
449
- * @return {number} The current number of passing tests executed by this
450
- * runner.
451
- */
452
- webdriver.TestRunner.prototype.getNumPassed = function() {
453
- return this.numPassing_;
454
- };
455
-
456
-
457
- /**
458
- * @return {number} The number of tests executed by this runner.
459
- */
460
- webdriver.TestRunner.prototype.getNumTests = function() {
461
- return this.results_.length;
462
- };
463
-
464
-
465
- /**
466
- * @return {string} A summary of all tests that have been completed by this
467
- * runner.
468
- */
469
- webdriver.TestRunner.prototype.getReport = function() {
470
- if (!this.isFinished()) {
471
- return null;
472
- }
473
- return goog.array.map(this.results_, function(result) {
474
- return result.getSummary();
475
- }).join('\n') + '\n';
476
- };
477
-
478
-
479
- /**
480
- * Executes the next test.
481
- * @private
482
- */
483
- webdriver.TestRunner.prototype.executeNextTest_ = function() {
484
- this.currentTest_ += 1;
485
- if (this.currentTest_ >= this.tests_.length) {
486
- webdriver.logging.info('No more tests');
487
- this.tearDownPageFn_();
488
- this.finished_ = true;
489
- return;
490
- }
491
-
492
- var test = this.tests_[this.currentTest_];
493
- var result = new webdriver.TestResult(test, true);
494
-
495
- webdriver.logging.info('>>>>>> Starting ' + test.name);
496
-
497
- var driver;
498
- try {
499
- driver = this.driverFactoryFn_();
500
- var driverError = goog.bind(this.handleDriverError_, this, result);
501
- goog.events.listen(driver,
502
- webdriver.WebDriver.EventType.ERROR, driverError);
503
-
504
- driver.newSession(true);
505
- webdriver.timing.setTimeout(
506
- goog.bind(this.setUp_, this, result, driver), 0);
507
- } catch (ex) {
508
- result.passed = false;
509
- result.errMsg = ex.message + (ex.stack ? ('\n' + ex.stack) : '');
510
- this.reportResult_(result, driver);
511
- }
512
- };
513
-
514
-
515
- /**
516
- * Internal method for collecting and executing driver commands before calling
517
- * the next test phase.
518
- * @param {webdriver.TestResult} result Result object for the current test.
519
- * @param {webdriver.WebDriver} driver The WebDriver instance to pass to the
520
- * test function.
521
- * @param {function} commandFn The function to collect driver commands from.
522
- * The function should take a single {@code webdriver.WebDriver} argument.
523
- * @param {function} nextPhase The next phase in the test (e.g. setUp, test,
524
- * tearDown).
525
- * @private
526
- */
527
- webdriver.TestRunner.prototype.collectAndRunDriverCommands_ = function(
528
- result, driver, commandFn, nextPhase) {
529
- try {
530
- commandFn.apply(result.testCase, [driver]);
531
- driver.callFunction(nextPhase, this, result, driver);
532
- } catch (ex) {
533
- result.passed = false;
534
- result.errMsg = ex.message + (ex.stack ? ('\n' + ex.stack) : '');
535
- this.reportResult_(result, driver);
536
- }
537
- };
538
-
539
-
540
- /**
541
- * Executes {@code setUp} if one was found in the global scope.
542
- * @param {webdriver.TestResult} result Result object for the current test.
543
- * @param {webdriver.WebDriver} driver The WebDriver instance to pass to the
544
- * test function.
545
- * @private
546
- */
547
- webdriver.TestRunner.prototype.setUp_ = function(result, driver) {
548
- this.collectAndRunDriverCommands_(
549
- result, driver, this.setUpFn_, this.runTest_);
550
- };
551
-
552
-
553
- /**
554
- * Executes a test function.
555
- * @param {webdriver.TestResult} result Result object for the current test.
556
- * @param {webdriver.WebDriver} driver The WebDriver instance to pass to the
557
- * test function.
558
- * @private
559
- */
560
- webdriver.TestRunner.prototype.runTest_ = function(result, driver) {
561
- this.collectAndRunDriverCommands_(
562
- result, driver, result.testCase.testFn, this.tearDown_);
563
- };
564
-
565
-
566
- /**
567
- * Executes {@code tearDown} if one was found in the global scope.
568
- * @param {webdriver.TestResult} result Result object for the current test.
569
- * @param {webdriver.WebDriver} driver The WebDriver instance to pass to the
570
- * test function.
571
- * @private
572
- */
573
- webdriver.TestRunner.prototype.tearDown_ = function(result, driver) {
574
- this.collectAndRunDriverCommands_(
575
- result, driver, this.tearDownFn_, this.reportResult_);
576
- };
577
-
578
-
579
- /**
580
- * Event handler that fails the current test if {@code webdriver.WebDriver}
581
- * dispatches an {@code webdriver.WebDriver.EventType.ERROR} event.
582
- * @param {webdriver.TestResult} result Result object for the current test.
583
- * @param {goog.events.Event} e The error event whose target should be a
584
- * {@code webdriver.WebDriver} instance.
585
- * @private
586
- */
587
- webdriver.TestRunner.prototype.handleDriverError_ = function(result, e) {
588
- result.passed = false;
589
- var failingCommand = e.target;
590
- var response = failingCommand.getResponse();
591
- if (response) {
592
- result.errMsg = response.getErrorMessage();
593
- } else {
594
- // Should never happen, but just in case.
595
- result.errMsg = 'Unknown error!';
596
- }
597
- this.reportResult_(result, failingCommand.getDriver());
598
- };