selenium-webdriver 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- };