~dongpo-deng/sahana-eden/test

« back to all changes in this revision

Viewing changes to static/selenium/core/scripts/selenium-testrunner.js

  • Committer: Deng Dongpo
  • Date: 2010-08-01 09:29:44 UTC
  • Revision ID: dongpo@dhcp-21193.iis.sinica.edu.tw-20100801092944-8t9obt4xtl7otesb
initial

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
* Copyright 2004 ThoughtWorks, Inc
 
3
*
 
4
*  Licensed under the Apache License, Version 2.0 (the "License");
 
5
*  you may not use this file except in compliance with the License.
 
6
*  You may obtain a copy of the License at
 
7
*
 
8
*      http://www.apache.org/licenses/LICENSE-2.0
 
9
*
 
10
*  Unless required by applicable law or agreed to in writing, software
 
11
*  distributed under the License is distributed on an "AS IS" BASIS,
 
12
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
*  See the License for the specific language governing permissions and
 
14
*  limitations under the License.
 
15
*
 
16
*/
 
17
 
 
18
// An object representing the current test, used external
 
19
var currentTest = null; // TODO: get rid of this global, which mirrors the htmlTestRunner.currentTest
 
20
var selenium = null;
 
21
 
 
22
var htmlTestRunner;
 
23
var HtmlTestRunner = classCreate();
 
24
objectExtend(HtmlTestRunner.prototype, {
 
25
    initialize: function() {
 
26
        this.metrics = new Metrics();
 
27
        this.controlPanel = new HtmlTestRunnerControlPanel();
 
28
        this.testFailed = false;
 
29
        this.currentTest = null;
 
30
        this.runAllTests = false;
 
31
        this.appWindow = null;
 
32
        // we use a timeout here to make sure the LOG has loaded first, so we can see _every_ error
 
33
        setTimeout(fnBind(function() {
 
34
            this.loadSuiteFrame();
 
35
        }, this), 500);
 
36
    },
 
37
 
 
38
    getTestSuite: function() {
 
39
        return suiteFrame.getCurrentTestSuite();
 
40
    },
 
41
 
 
42
    markFailed: function() {
 
43
        this.testFailed = true;
 
44
        this.getTestSuite().markFailed();
 
45
    },
 
46
 
 
47
    loadSuiteFrame: function() {
 
48
        var logLevel = this.controlPanel.getDefaultLogLevel();
 
49
        if (logLevel) {
 
50
            LOG.setLogLevelThreshold(logLevel);
 
51
        }
 
52
        if (selenium == null) {
 
53
            var appWindow = this._getApplicationWindow();
 
54
            try { appWindow.location; }
 
55
            catch (e) { 
 
56
                // when reloading, we may be pointing at an old window (Perm Denied)
 
57
                setTimeout(fnBind(function() {
 
58
                    this.loadSuiteFrame();
 
59
                }, this), 50);
 
60
                return;
 
61
            }
 
62
            selenium = Selenium.createForWindow(appWindow);
 
63
            this._registerCommandHandlers();
 
64
        }
 
65
        this.controlPanel.setHighlightOption();
 
66
        var testSuiteName = this.controlPanel.getTestSuiteName();
 
67
        var self = this;
 
68
        if (testSuiteName) {
 
69
            suiteFrame.load(testSuiteName, function() {setTimeout(fnBind(self._onloadTestSuite, self), 50)} );
 
70
            selenium.browserbot.baseUrl = absolutify(testSuiteName, window.location.href);
 
71
        }
 
72
        // DGF or should we use the old default?
 
73
        // selenium.browserbot.baseUrl = window.location.href;
 
74
        if (this.controlPanel.getBaseUrl()) {
 
75
            selenium.browserbot.baseUrl = this.controlPanel.getBaseUrl();
 
76
        }
 
77
    },
 
78
 
 
79
    _getApplicationWindow: function () {
 
80
        if (this.controlPanel.isMultiWindowMode()) {
 
81
            return this._getSeparateApplicationWindow();
 
82
        }
 
83
        return sel$('selenium_myiframe').contentWindow;
 
84
    },
 
85
 
 
86
    _getSeparateApplicationWindow: function () {
 
87
        if (this.appWindow == null) {
 
88
            this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html', this.controlPanel.isAutomatedRun());
 
89
        }
 
90
        return this.appWindow;
 
91
    },
 
92
 
 
93
    _onloadTestSuite:function () {
 
94
        suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
 
95
        if (! this.getTestSuite().isAvailable()) {
 
96
            return;
 
97
        }
 
98
        if (this.controlPanel.isAutomatedRun()) {
 
99
            this.startTestSuite();
 
100
        } else if (this.controlPanel.getAutoUrl()) {
 
101
            //todo what is the autourl doing, left to check it out
 
102
            addLoadListener(this._getApplicationWindow(), fnBind(this._startSingleTest, this));
 
103
            this._getApplicationWindow().src = this.controlPanel.getAutoUrl();
 
104
        } else {
 
105
            var testCaseLoaded = fnBind(function(){this.testCaseLoaded=true;},this);
 
106
            var testNumber = 0;
 
107
            if (this.controlPanel.getTestNumber() != null){
 
108
                var testNumber = this.controlPanel.getTestNumber() - 1; 
 
109
            }
 
110
            this.getTestSuite().getSuiteRows()[testNumber].loadTestCase(testCaseLoaded);
 
111
        }
 
112
    },
 
113
 
 
114
    _startSingleTest:function () {
 
115
        removeLoadListener(getApplicationWindow(), fnBind(this._startSingleTest, this));
 
116
        var singleTestName = this.controlPanel.getSingleTestName();
 
117
        testFrame.load(singleTestName, fnBind(this.startTest, this));
 
118
    },
 
119
 
 
120
    _registerCommandHandlers: function () {
 
121
        this.commandFactory = new CommandHandlerFactory();
 
122
        this.commandFactory.registerAll(selenium);
 
123
    },
 
124
 
 
125
    startTestSuite: function() {
 
126
        this.controlPanel.reset();
 
127
        this.metrics.resetMetrics();
 
128
        this.getTestSuite().reset();
 
129
        this.runAllTests = true;
 
130
        this.runNextTest();
 
131
    },
 
132
 
 
133
    runNextTest: function () {
 
134
        this.getTestSuite().updateSuiteWithResultOfPreviousTest();
 
135
        if (!this.runAllTests) {
 
136
            return;
 
137
        }
 
138
        this.getTestSuite().runNextTestInSuite();
 
139
    },
 
140
 
 
141
    startTest: function () {
 
142
        this.controlPanel.reset();
 
143
        testFrame.scrollToTop();
 
144
        //todo: move testFailed and storedVars to TestCase
 
145
        this.testFailed = false;
 
146
        storedVars = new Object();
 
147
        storedVars.nbsp = String.fromCharCode(160);
 
148
        storedVars.space = ' ';
 
149
        this.currentTest = new HtmlRunnerTestLoop(testFrame.getCurrentTestCase(), this.metrics, this.commandFactory);
 
150
        currentTest = this.currentTest;
 
151
        this.currentTest.start();
 
152
    },
 
153
 
 
154
    runSingleTest:function() {
 
155
        this.runAllTests = false;
 
156
        this.metrics.resetMetrics();
 
157
        this.startTest();
 
158
    }
 
159
});
 
160
 
 
161
var runInterval = 0;
 
162
 
 
163
/** SeleniumFrame encapsulates an iframe element */
 
164
var SeleniumFrame = classCreate();
 
165
objectExtend(SeleniumFrame.prototype, {
 
166
 
 
167
    initialize : function(frame) {
 
168
        this.frame = frame;
 
169
        addLoadListener(this.frame, fnBind(this._handleLoad, this));
 
170
    },
 
171
 
 
172
    getWindow : function() {
 
173
        return this.frame.contentWindow;
 
174
    },
 
175
 
 
176
    getDocument : function() {
 
177
        return this.frame.contentWindow.document;
 
178
    },
 
179
 
 
180
    _handleLoad: function() {
 
181
        this._attachStylesheet();
 
182
        this._onLoad();
 
183
        if (this.loadCallback) {
 
184
            this.loadCallback();
 
185
        }
 
186
    },
 
187
 
 
188
    _attachStylesheet: function() {
 
189
        var d = this.getDocument();
 
190
        var head = d.getElementsByTagName('head').item(0);
 
191
        var styleLink = d.createElement("link");
 
192
        styleLink.rel = "stylesheet";
 
193
        styleLink.type = "text/css";
 
194
        if (browserVersion && browserVersion.isChrome) {
 
195
            // DGF We have to play a clever trick to get the right absolute path.
 
196
            // This trick works on most browsers, (not IE), but is only needed in
 
197
            // chrome
 
198
            var tempLink = window.document.createElement("link");
 
199
            tempLink.href = "selenium-test.css"; // this will become an absolute href
 
200
            styleLink.href = tempLink.href;
 
201
        } else {
 
202
            // this works in every browser (except Firefox in chrome mode)
 
203
            var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, "selenium-test.css");
 
204
            if (browserVersion.isIE && window.location.protocol == "file:") {
 
205
                styleSheetPath = "file:///" + styleSheetPath;
 
206
            }
 
207
            styleLink.href = styleSheetPath;
 
208
        }
 
209
        // DGF You're only going to see this log message if you set defaultLogLevel=debug
 
210
        LOG.debug("styleLink.href="+styleLink.href);
 
211
        head.appendChild(styleLink);
 
212
    },
 
213
 
 
214
    _onLoad: function() {
 
215
    },
 
216
 
 
217
    scrollToTop : function() {
 
218
        this.frame.contentWindow.scrollTo(0, 0);
 
219
    },
 
220
 
 
221
    _setLocation: function(location) {
 
222
        var isChrome = browserVersion.isChrome || false;
 
223
        var isHTA = browserVersion.isHTA || false;
 
224
        // DGF TODO multiWindow
 
225
        location += (location.indexOf("?") == -1 ? "?" : "&");
 
226
        location += "thisIsChrome=" + isChrome + "&thisIsHTA=" + isHTA; 
 
227
        if (browserVersion.isSafari) {
 
228
            // safari doesn't reload the page when the location equals to current location.
 
229
            // hence, set the location to blank so that the page will reload automatically.
 
230
            this.frame.src = "about:blank";
 
231
            this.frame.src = location;
 
232
        } else {
 
233
            this.frame.contentWindow.location.replace(location);
 
234
        }
 
235
    },
 
236
 
 
237
    load: function(/* url, [callback] */) {
 
238
        if (arguments.length > 1) {
 
239
            this.loadCallback = arguments[1];
 
240
 
 
241
        }
 
242
        this._setLocation(arguments[0]);
 
243
    }
 
244
 
 
245
});
 
246
 
 
247
/** HtmlTestSuiteFrame - encapsulates the suite iframe element */
 
248
var HtmlTestSuiteFrame = classCreate();
 
249
objectExtend(HtmlTestSuiteFrame.prototype, SeleniumFrame.prototype);
 
250
objectExtend(HtmlTestSuiteFrame.prototype, {
 
251
 
 
252
    getCurrentTestSuite: function() {
 
253
        if (!this.currentTestSuite) {
 
254
            this.currentTestSuite = new HtmlTestSuite(this.getDocument());
 
255
        }
 
256
        return this.currentTestSuite;
 
257
    }
 
258
 
 
259
});
 
260
 
 
261
/** HtmlTestFrame - encapsulates the test-case iframe element */
 
262
var HtmlTestFrame = classCreate();
 
263
objectExtend(HtmlTestFrame.prototype, SeleniumFrame.prototype);
 
264
objectExtend(HtmlTestFrame.prototype, {
 
265
 
 
266
    _onLoad: function() {
 
267
        this.currentTestCase = new HtmlTestCase(this.getWindow(), htmlTestRunner.getTestSuite().getCurrentRow());
 
268
    },
 
269
 
 
270
    getCurrentTestCase: function() {
 
271
        return this.currentTestCase;
 
272
    }
 
273
 
 
274
});
 
275
 
 
276
function onSeleniumLoad() {
 
277
    suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
 
278
    testFrame = new HtmlTestFrame(getTestFrame());
 
279
    htmlTestRunner = new HtmlTestRunner();
 
280
}
 
281
 
 
282
var suiteFrame;
 
283
var testFrame;
 
284
 
 
285
function getSuiteFrame() {
 
286
    var f = sel$('testSuiteFrame');
 
287
    if (f == null) {
 
288
        f = top;
 
289
        // proxyInjection mode does not set selenium_myiframe
 
290
    }
 
291
    return f;
 
292
}
 
293
 
 
294
function getTestFrame() {
 
295
    var f = sel$('testFrame');
 
296
    if (f == null) {
 
297
        f = top;
 
298
        // proxyInjection mode does not set selenium_myiframe
 
299
    }
 
300
    return f;
 
301
}
 
302
 
 
303
var HtmlTestRunnerControlPanel = classCreate();
 
304
objectExtend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype);
 
305
objectExtend(HtmlTestRunnerControlPanel.prototype, {
 
306
    initialize: function() {
 
307
        this._acquireQueryString();
 
308
 
 
309
        this.runInterval = 0;
 
310
 
 
311
        this.highlightOption = sel$('highlightOption');
 
312
        this.pauseButton = sel$('pauseTest');
 
313
        this.stepButton = sel$('stepTest');
 
314
 
 
315
        this.highlightOption.onclick = fnBindAsEventListener((function() {
 
316
            this.setHighlightOption();
 
317
        }), this);
 
318
        this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
 
319
        this.stepButton.onclick = fnBindAsEventListener(this.stepCurrentTest, this);
 
320
 
 
321
 
 
322
        this.speedController = new Control.Slider('speedHandle', 'speedTrack', {
 
323
            range: $R(0, 1000),
 
324
            onSlide: fnBindAsEventListener(this.setRunInterval, this),
 
325
            onChange: fnBindAsEventListener(this.setRunInterval, this)
 
326
        });
 
327
 
 
328
        this._parseQueryParameter();
 
329
    },
 
330
 
 
331
    setHighlightOption: function () {
 
332
        var isHighlight = this.highlightOption.checked;
 
333
        selenium.browserbot.setShouldHighlightElement(isHighlight);
 
334
    },
 
335
 
 
336
    _parseQueryParameter: function() {
 
337
        var tempRunInterval = this._getQueryParameter("runInterval");
 
338
        if (tempRunInterval) {
 
339
            this.setRunInterval(tempRunInterval);
 
340
        }
 
341
        this.highlightOption.checked = this._getQueryParameter("highlight");
 
342
    },
 
343
 
 
344
    setRunInterval: function(runInterval) {
 
345
        this.runInterval = runInterval;
 
346
    },
 
347
 
 
348
    setToPauseAtNextCommand: function() {
 
349
        this.runInterval = -1;
 
350
    },
 
351
 
 
352
    pauseCurrentTest: function () {
 
353
        this.setToPauseAtNextCommand();
 
354
        this._switchPauseButtonToContinue();
 
355
    },
 
356
 
 
357
    continueCurrentTest: function () {
 
358
        this.reset();
 
359
        currentTest.resume();
 
360
    },
 
361
 
 
362
    reset: function() {
 
363
        this.runInterval = this.speedController.value;
 
364
        this._switchContinueButtonToPause();
 
365
    },
 
366
 
 
367
    _switchContinueButtonToPause: function() {
 
368
        this.pauseButton.className = "cssPauseTest";
 
369
        this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
 
370
    },
 
371
 
 
372
    _switchPauseButtonToContinue: function() {
 
373
        sel$('stepTest').disabled = false;
 
374
        this.pauseButton.className = "cssContinueTest";
 
375
        this.pauseButton.onclick = fnBindAsEventListener(this.continueCurrentTest, this);
 
376
    },
 
377
 
 
378
    stepCurrentTest: function () {
 
379
        this.setToPauseAtNextCommand();
 
380
        currentTest.resume();
 
381
    },
 
382
 
 
383
    isAutomatedRun: function() {
 
384
        return this._isQueryParameterTrue("auto");
 
385
    },
 
386
 
 
387
    shouldSaveResultsToFile: function() {
 
388
        return this._isQueryParameterTrue("save");
 
389
    },
 
390
 
 
391
    closeAfterTests: function() {
 
392
        return this._isQueryParameterTrue("close");
 
393
    },
 
394
 
 
395
    getTestSuiteName: function() {
 
396
        return this._getQueryParameter("test");
 
397
    },
 
398
 
 
399
    getTestNumber: function() {
 
400
        return this._getQueryParameter("testNumber");
 
401
    },
 
402
 
 
403
    getSingleTestName: function() {
 
404
        return this._getQueryParameter("singletest");
 
405
    },
 
406
 
 
407
    getAutoUrl: function() {
 
408
        return this._getQueryParameter("autoURL");
 
409
    },
 
410
    
 
411
    getDefaultLogLevel: function() {
 
412
        return this._getQueryParameter("defaultLogLevel");
 
413
    },
 
414
 
 
415
    getResultsUrl: function() {
 
416
        return this._getQueryParameter("resultsUrl");
 
417
    },
 
418
 
 
419
    _acquireQueryString: function() {
 
420
        if (this.queryString) return;
 
421
        if (browserVersion.isHTA) {
 
422
            var args = this._extractArgs();
 
423
            if (args.length < 2) return null;
 
424
            this.queryString = args[1];
 
425
        } else {
 
426
            this.queryString = location.search.substr(1);
 
427
        }
 
428
    }
 
429
 
 
430
});
 
431
 
 
432
var AbstractResultAwareRow = classCreate();
 
433
objectExtend(AbstractResultAwareRow.prototype, {
 
434
 
 
435
    initialize: function(trElement) {
 
436
        this.trElement = trElement;
 
437
    },
 
438
 
 
439
    setStatus: function(status) {
 
440
        this.unselect();
 
441
        this.trElement.className = this.trElement.className.replace(/status_[a-z]+/, "");
 
442
        if (status) {
 
443
            addClassName(this.trElement, "status_" + status);
 
444
        }
 
445
    },
 
446
 
 
447
    select: function() {
 
448
        addClassName(this.trElement, "selected");
 
449
        safeScrollIntoView(this.trElement);
 
450
    },
 
451
 
 
452
    unselect: function() {
 
453
        removeClassName(this.trElement, "selected");
 
454
    },
 
455
 
 
456
    markPassed: function() {
 
457
        this.setStatus("passed");
 
458
    },
 
459
 
 
460
    markDone: function() {
 
461
        this.setStatus("done");
 
462
    },
 
463
 
 
464
    markFailed: function() {
 
465
        this.setStatus("failed");
 
466
    }
 
467
 
 
468
});
 
469
 
 
470
var TitleRow = classCreate();
 
471
objectExtend(TitleRow.prototype, AbstractResultAwareRow.prototype);
 
472
objectExtend(TitleRow.prototype, {
 
473
 
 
474
    initialize: function(trElement) {
 
475
        this.trElement = trElement;
 
476
        trElement.className = "title";
 
477
    }
 
478
 
 
479
});
 
480
 
 
481
var HtmlTestCaseRow = classCreate();
 
482
objectExtend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype);
 
483
objectExtend(HtmlTestCaseRow.prototype, {
 
484
 
 
485
    getCommand: function () {
 
486
        return new SeleniumCommand(getText(this.trElement.cells[0]),
 
487
                getText(this.trElement.cells[1]),
 
488
                getText(this.trElement.cells[2]),
 
489
                this.isBreakpoint());
 
490
    },
 
491
 
 
492
    markFailed: function(errorMsg) {
 
493
        AbstractResultAwareRow.prototype.markFailed.call(this, errorMsg);
 
494
        this.setMessage(errorMsg);
 
495
    },
 
496
 
 
497
    setMessage: function(message) {
 
498
        setText(this.trElement.cells[2], message);
 
499
    },
 
500
 
 
501
    reset: function() {
 
502
        this.setStatus(null);
 
503
        var thirdCell = this.trElement.cells[2];
 
504
        if (thirdCell) {
 
505
            if (thirdCell.originalHTML) {
 
506
                thirdCell.innerHTML = thirdCell.originalHTML;
 
507
            } else {
 
508
                thirdCell.originalHTML = thirdCell.innerHTML;
 
509
            }
 
510
        }
 
511
    },
 
512
 
 
513
    onClick: function() {
 
514
        if (this.trElement.isBreakpoint == undefined) {
 
515
            this.trElement.isBreakpoint = true;
 
516
            addClassName(this.trElement, "breakpoint");
 
517
        } else {
 
518
            this.trElement.isBreakpoint = undefined;
 
519
            removeClassName(this.trElement, "breakpoint");
 
520
        }
 
521
    },
 
522
 
 
523
    addBreakpointSupport: function() {
 
524
        elementSetStyle(this.trElement, {"cursor" : "pointer"});
 
525
        this.trElement.onclick = fnBindAsEventListener(function() {
 
526
            this.onClick();
 
527
        }, this);
 
528
    },
 
529
 
 
530
    isBreakpoint: function() {
 
531
        if (this.trElement.isBreakpoint == undefined || this.trElement.isBreakpoint == null) {
 
532
            return false
 
533
        }
 
534
        return this.trElement.isBreakpoint;
 
535
    }
 
536
});
 
537
 
 
538
var HtmlTestSuiteRow = classCreate();
 
539
objectExtend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype);
 
540
objectExtend(HtmlTestSuiteRow.prototype, {
 
541
 
 
542
    initialize: function(trElement, testFrame, htmlTestSuite) {
 
543
        this.trElement = trElement;
 
544
        this.testFrame = testFrame;
 
545
        this.htmlTestSuite = htmlTestSuite;
 
546
        this.link = trElement.getElementsByTagName("a")[0];
 
547
        this.link.onclick = fnBindAsEventListener(this._onClick, this);
 
548
    },
 
549
 
 
550
    reset: function() {
 
551
        this.setStatus(null);
 
552
    },
 
553
 
 
554
    _onClick: function() {
 
555
        this.loadTestCase(null);
 
556
        return false;
 
557
    },
 
558
 
 
559
    loadTestCase: function(onloadFunction) {
 
560
        this.htmlTestSuite.unselectCurrentRow();
 
561
        this.select();
 
562
        this.htmlTestSuite.currentRowInSuite = this.trElement.rowIndex - 1;
 
563
        // If the row has a stored results table, use that
 
564
        var resultsFromPreviousRun = this.trElement.cells[1];
 
565
        if (resultsFromPreviousRun) {
 
566
            // todo: delegate to TestFrame, e.g.
 
567
            //   this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
 
568
            var testBody = this.testFrame.getDocument().body;
 
569
            testBody.innerHTML = resultsFromPreviousRun.innerHTML;
 
570
            this.testFrame._onLoad();
 
571
            if (onloadFunction) {
 
572
                onloadFunction();
 
573
            }
 
574
        } else {
 
575
            this.testFrame.load(this.link.href, onloadFunction);
 
576
        }
 
577
    },
 
578
 
 
579
    saveTestResults: function() {
 
580
        // todo: GLOBAL ACCESS!
 
581
        var resultHTML = this.testFrame.getDocument().body.innerHTML;
 
582
        if (!resultHTML) return;
 
583
 
 
584
        // todo: why create this div?
 
585
        var divElement = this.trElement.ownerDocument.createElement("div");
 
586
        divElement.innerHTML = resultHTML;
 
587
 
 
588
        var hiddenCell = this.trElement.ownerDocument.createElement("td");
 
589
        hiddenCell.appendChild(divElement);
 
590
        hiddenCell.style.display = "none";
 
591
 
 
592
        this.trElement.appendChild(hiddenCell);
 
593
    }
 
594
 
 
595
});
 
596
 
 
597
var HtmlTestSuite = classCreate();
 
598
objectExtend(HtmlTestSuite.prototype, {
 
599
 
 
600
    initialize: function(suiteDocument) {
 
601
        this.suiteDocument = suiteDocument;
 
602
        this.suiteRows = this._collectSuiteRows();
 
603
        var testTable = this.getTestTable();
 
604
        if (!testTable) return;
 
605
        this.titleRow = new TitleRow(testTable.rows[0]);
 
606
        this.reset();
 
607
    },
 
608
 
 
609
    reset: function() {
 
610
        this.failed = false;
 
611
        this.currentRowInSuite = -1;
 
612
        this.titleRow.setStatus(null);
 
613
        for (var i = 0; i < this.suiteRows.length; i++) {
 
614
            var row = this.suiteRows[i];
 
615
            row.reset();
 
616
        }
 
617
    },
 
618
 
 
619
    getSuiteRows: function() {
 
620
        return this.suiteRows;
 
621
    },
 
622
 
 
623
    getTestTable: function() {
 
624
        var tables = sel$A(this.suiteDocument.getElementsByTagName("table"));
 
625
        return tables[0];
 
626
    },
 
627
 
 
628
    isAvailable: function() {
 
629
        return this.getTestTable() != null;
 
630
    },
 
631
 
 
632
    _collectSuiteRows: function () {
 
633
        var result = [];
 
634
        var tables = sel$A(this.suiteDocument.getElementsByTagName("table"));
 
635
        var testTable = tables[0];
 
636
        if (!testTable) return;
 
637
        for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
 
638
            var rowElement = testTable.rows[rowNum];
 
639
            result.push(new HtmlTestSuiteRow(rowElement, testFrame, this));
 
640
        }
 
641
        
 
642
        // process the unsuited rows as well
 
643
        for (var tableNum = 1; tableNum < sel$A(this.suiteDocument.getElementsByTagName("table")).length; tableNum++) {
 
644
            testTable = tables[tableNum];
 
645
            for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
 
646
                var rowElement = testTable.rows[rowNum];
 
647
                new HtmlTestSuiteRow(rowElement, testFrame, this);
 
648
            }
 
649
        }
 
650
        return result;
 
651
    },
 
652
 
 
653
    getCurrentRow: function() {
 
654
        if (this.currentRowInSuite == -1) {
 
655
            return null;
 
656
        }
 
657
        return this.suiteRows[this.currentRowInSuite];
 
658
    },
 
659
 
 
660
    unselectCurrentRow: function() {
 
661
        var currentRow = this.getCurrentRow()
 
662
        if (currentRow) {
 
663
            currentRow.unselect();
 
664
        }
 
665
    },
 
666
 
 
667
    markFailed: function() {
 
668
        this.failed = true;
 
669
        this.titleRow.markFailed();
 
670
    },
 
671
 
 
672
    markDone: function() {
 
673
        if (!this.failed) {
 
674
            this.titleRow.markPassed();
 
675
        }
 
676
    },
 
677
 
 
678
    _startCurrentTestCase: function() {
 
679
        this.getCurrentRow().loadTestCase(fnBind(htmlTestRunner.startTest, htmlTestRunner));
 
680
    },
 
681
 
 
682
    _onTestSuiteComplete: function() {
 
683
        this.markDone();
 
684
        new SeleniumTestResult(this.failed, this.getTestTable()).post();
 
685
    },
 
686
 
 
687
    updateSuiteWithResultOfPreviousTest: function() {
 
688
        if (this.currentRowInSuite >= 0) {
 
689
            this.getCurrentRow().saveTestResults();
 
690
        }
 
691
    },
 
692
 
 
693
    runNextTestInSuite: function() {
 
694
        this.currentRowInSuite++;
 
695
 
 
696
        // If we are done with all of the tests, set the title bar as pass or fail
 
697
        if (this.currentRowInSuite >= this.suiteRows.length) {
 
698
            this._onTestSuiteComplete();
 
699
        } else {
 
700
            this._startCurrentTestCase();
 
701
        }
 
702
    }
 
703
 
 
704
 
 
705
 
 
706
});
 
707
 
 
708
var SeleniumTestResult = classCreate();
 
709
objectExtend(SeleniumTestResult.prototype, {
 
710
 
 
711
// Post the results to a servlet, CGI-script, etc.  The URL of the
 
712
// results-handler defaults to "/postResults", but an alternative location
 
713
// can be specified by providing a "resultsUrl" query parameter.
 
714
//
 
715
// Parameters passed to the results-handler are:
 
716
//      result:         passed/failed depending on whether the suite passed or failed
 
717
//      totalTime:      the total running time in seconds for the suite.
 
718
//
 
719
//      numTestPasses:  the total number of tests which passed.
 
720
//      numTestFailures: the total number of tests which failed.
 
721
//
 
722
//      numCommandPasses: the total number of commands which passed.
 
723
//      numCommandFailures: the total number of commands which failed.
 
724
//      numCommandErrors: the total number of commands which errored.
 
725
//
 
726
//      suite:      the suite table, including the hidden column of test results
 
727
//      testTable.1 to testTable.N: the individual test tables
 
728
//
 
729
    initialize: function (suiteFailed, suiteTable) {
 
730
        this.controlPanel = htmlTestRunner.controlPanel;
 
731
        this.metrics = htmlTestRunner.metrics;
 
732
        this.suiteFailed = suiteFailed;
 
733
        this.suiteTable = suiteTable;
 
734
    },
 
735
 
 
736
    post: function () {
 
737
        if (!this.controlPanel.isAutomatedRun()) {
 
738
            return;
 
739
        }
 
740
        var form = document.createElement("form");
 
741
        document.body.appendChild(form);
 
742
 
 
743
        form.id = "resultsForm";
 
744
        form.method = "post";
 
745
        form.target = "selenium_myiframe";
 
746
 
 
747
        var resultsUrl = this.controlPanel.getResultsUrl();
 
748
        if (!resultsUrl) {
 
749
            resultsUrl = "./postResults";
 
750
        }
 
751
 
 
752
        var actionAndParameters = resultsUrl.split('?', 2);
 
753
        form.action = actionAndParameters[0];
 
754
        var resultsUrlQueryString = actionAndParameters[1];
 
755
 
 
756
        form.createHiddenField = function(name, value) {
 
757
            input = document.createElement("input");
 
758
            input.type = "hidden";
 
759
            input.name = name;
 
760
            input.value = value;
 
761
            this.appendChild(input);
 
762
        };
 
763
 
 
764
        if (resultsUrlQueryString) {
 
765
            var clauses = resultsUrlQueryString.split('&');
 
766
            for (var i = 0; i < clauses.length; i++) {
 
767
                var keyValuePair = clauses[i].split('=', 2);
 
768
                var key = unescape(keyValuePair[0]);
 
769
                var value = unescape(keyValuePair[1]);
 
770
                form.createHiddenField(key, value);
 
771
            }
 
772
        }
 
773
 
 
774
        form.createHiddenField("selenium.version", Selenium.version);
 
775
        form.createHiddenField("selenium.revision", Selenium.revision);
 
776
 
 
777
        form.createHiddenField("result", this.suiteFailed ? "failed" : "passed");
 
778
 
 
779
        form.createHiddenField("totalTime", Math.floor((this.metrics.currentTime - this.metrics.startTime) / 1000));
 
780
        form.createHiddenField("numTestPasses", this.metrics.numTestPasses);
 
781
        form.createHiddenField("numTestFailures", this.metrics.numTestFailures);
 
782
        form.createHiddenField("numCommandPasses", this.metrics.numCommandPasses);
 
783
        form.createHiddenField("numCommandFailures", this.metrics.numCommandFailures);
 
784
        form.createHiddenField("numCommandErrors", this.metrics.numCommandErrors);
 
785
 
 
786
        // Create an input for each test table.  The inputs are named
 
787
        // testTable.1, testTable.2, etc.
 
788
        for (rowNum = 1; rowNum < this.suiteTable.rows.length; rowNum++) {
 
789
            // If there is a second column, then add a new input
 
790
            if (this.suiteTable.rows[rowNum].cells.length > 1) {
 
791
                var resultCell = this.suiteTable.rows[rowNum].cells[1];
 
792
                form.createHiddenField("testTable." + rowNum, resultCell.innerHTML);
 
793
                // remove the resultCell, so it's not included in the suite HTML
 
794
                resultCell.parentNode.removeChild(resultCell);
 
795
            }
 
796
        }
 
797
 
 
798
        form.createHiddenField("numTestTotal", rowNum-1);
 
799
 
 
800
        // Add HTML for the suite itself
 
801
        form.createHiddenField("suite", this.suiteTable.parentNode.innerHTML);
 
802
 
 
803
        var logMessages = [];
 
804
        while (LOG.pendingMessages.length > 0) {
 
805
            var msg = LOG.pendingMessages.shift();
 
806
            logMessages.push(msg.type);
 
807
            logMessages.push(": ");
 
808
            logMessages.push(msg.msg);
 
809
            logMessages.push('\n');
 
810
        }
 
811
        var logOutput = logMessages.join("");
 
812
        form.createHiddenField("log", logOutput);
 
813
 
 
814
        if (this.controlPanel.shouldSaveResultsToFile()) {
 
815
            this._saveToFile(resultsUrl, form);
 
816
        } else {
 
817
            form.submit();
 
818
        }
 
819
        document.body.removeChild(form);
 
820
        if (this.controlPanel.closeAfterTests()) {
 
821
            window.top.close();
 
822
        }
 
823
    },
 
824
 
 
825
    _saveToFile: function (fileName, form) {
 
826
        // This only works when run as an IE HTA
 
827
        var inputs = new Object();
 
828
        for (var i = 0; i < form.elements.length; i++) {
 
829
            inputs[form.elements[i].name] = form.elements[i].value;
 
830
        }
 
831
        
 
832
        var objFSO = new ActiveXObject("Scripting.FileSystemObject")
 
833
        
 
834
        // DGF get CSS
 
835
        var styles = "";
 
836
        try {
 
837
            var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, "selenium-test.css");
 
838
            if (window.location.protocol == "file:") {
 
839
                var stylesFile = objFSO.OpenTextFile(styleSheetPath, 1);
 
840
                styles = stylesFile.ReadAll();
 
841
            } else {
 
842
                var xhr = XmlHttp.create();
 
843
                xhr.open("GET", styleSheetPath, false);
 
844
                xhr.send("");
 
845
                styles = xhr.responseText;
 
846
            }
 
847
        } catch (e) {}
 
848
        
 
849
        var scriptFile = objFSO.CreateTextFile(fileName);
 
850
        
 
851
        
 
852
        scriptFile.WriteLine("<html><head><title>Test suite results</title><style>");
 
853
        scriptFile.WriteLine(styles);
 
854
        scriptFile.WriteLine("</style>");
 
855
        scriptFile.WriteLine("<body>\n<h1>Test suite results</h1>" +
 
856
             "\n\n<table>\n<tr>\n<td>result:</td>\n<td>" + inputs["result"] + "</td>\n" +
 
857
             "</tr>\n<tr>\n<td>totalTime:</td>\n<td>" + inputs["totalTime"] + "</td>\n</tr>\n" +
 
858
             "<tr>\n<td>numTestTotal:</td>\n<td>" + inputs["numTestTotal"] + "</td>\n</tr>\n" +
 
859
             "<tr>\n<td>numTestPasses:</td>\n<td>" + inputs["numTestPasses"] + "</td>\n</tr>\n" +
 
860
             "<tr>\n<td>numTestFailures:</td>\n<td>" + inputs["numTestFailures"] + "</td>\n</tr>\n" +
 
861
             "<tr>\n<td>numCommandPasses:</td>\n<td>" + inputs["numCommandPasses"] + "</td>\n</tr>\n" +
 
862
             "<tr>\n<td>numCommandFailures:</td>\n<td>" + inputs["numCommandFailures"] + "</td>\n</tr>\n" +
 
863
             "<tr>\n<td>numCommandErrors:</td>\n<td>" + inputs["numCommandErrors"] + "</td>\n</tr>\n" +
 
864
             "<tr>\n<td>" + inputs["suite"] + "</td>\n<td>&nbsp;</td>\n</tr></table><table>");
 
865
        var testNum = inputs["numTestTotal"];
 
866
        
 
867
        for (var rowNum = 1; rowNum <= testNum; rowNum++) {
 
868
            scriptFile.WriteLine("<tr>\n<td>" + inputs["testTable." + rowNum] + "</td>\n<td>&nbsp;</td>\n</tr>");
 
869
        }
 
870
        scriptFile.WriteLine("</table><pre>");
 
871
        var log = inputs["log"];
 
872
        log=log.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;").replace(/"/gm,"&quot;").replace(/'/gm,"&apos;");
 
873
        scriptFile.WriteLine(log);
 
874
        scriptFile.WriteLine("</pre></body></html>");
 
875
        scriptFile.Close();
 
876
    }
 
877
});
 
878
 
 
879
/** HtmlTestCase encapsulates an HTML test document */
 
880
var HtmlTestCase = classCreate();
 
881
objectExtend(HtmlTestCase.prototype, {
 
882
 
 
883
    initialize: function(testWindow, htmlTestSuiteRow) {
 
884
        if (testWindow == null) {
 
885
            throw "testWindow should not be null";
 
886
        }
 
887
        if (htmlTestSuiteRow == null) {
 
888
            throw "htmlTestSuiteRow should not be null";
 
889
        }
 
890
        this.testWindow = testWindow;
 
891
        this.testDocument = testWindow.document;
 
892
        this.pathname = "'unknown'";
 
893
        try {
 
894
            if (this.testWindow.location) {
 
895
                this.pathname = this.testWindow.location.pathname;
 
896
            }
 
897
        } catch (e) {}
 
898
            
 
899
        this.htmlTestSuiteRow = htmlTestSuiteRow;
 
900
        this.headerRow = new TitleRow(this.testDocument.getElementsByTagName("tr")[0]);
 
901
        this.commandRows = this._collectCommandRows();
 
902
        this.nextCommandRowIndex = 0;
 
903
        this._addBreakpointSupport();
 
904
    },
 
905
 
 
906
    _collectCommandRows: function () {
 
907
        var commandRows = [];
 
908
        var tables = sel$A(this.testDocument.getElementsByTagName("table"));
 
909
        var self = this;
 
910
        for (var i = 0; i < tables.length; i++) {
 
911
            var table = tables[i];
 
912
            var tableRows = sel$A(table.rows);
 
913
            for (var j = 0; j < tableRows.length; j++) {
 
914
                var candidateRow = tableRows[j];
 
915
                if (self.isCommandRow(candidateRow)) {
 
916
                    commandRows.push(new HtmlTestCaseRow(candidateRow));
 
917
                }
 
918
            }
 
919
        }
 
920
        return commandRows;
 
921
    },
 
922
 
 
923
    isCommandRow:  function (row) {
 
924
        return row.cells.length >= 3;
 
925
    },
 
926
 
 
927
    reset: function() {
 
928
        /**
 
929
         * reset the test to runnable state
 
930
         */
 
931
        this.nextCommandRowIndex = 0;
 
932
 
 
933
        this.setStatus('');
 
934
        for (var i = 0; i < this.commandRows.length; i++) {
 
935
            var row = this.commandRows[i];
 
936
            row.reset();
 
937
        }
 
938
 
 
939
        // remove any additional fake "error" row added to the end of the document
 
940
        var errorElement = this.testDocument.getElementById('error');
 
941
        if (errorElement) {
 
942
            errorElement.parentNode.removeChild(errorElement);
 
943
        }
 
944
    },
 
945
 
 
946
    getCommandRows: function () {
 
947
        return this.commandRows;
 
948
    },
 
949
 
 
950
    setStatus: function(status) {
 
951
        this.headerRow.setStatus(status);
 
952
    },
 
953
 
 
954
    markFailed: function() {
 
955
        this.setStatus("failed");
 
956
        this.htmlTestSuiteRow.markFailed();
 
957
    },
 
958
 
 
959
    markPassed: function() {
 
960
        this.setStatus("passed");
 
961
        this.htmlTestSuiteRow.markPassed();
 
962
    },
 
963
 
 
964
    addErrorMessage: function(errorMsg, currentRow) {
 
965
        errorMsg = errorMsg.replace(/ /g, String.fromCharCode(160)).replace("\n", '\\n');
 
966
        if (currentRow) {
 
967
            currentRow.markFailed(errorMsg);
 
968
        } else {
 
969
            var errorElement = this.testDocument.createElement("p");
 
970
            errorElement.id = "error";
 
971
            setText(errorElement, errorMsg);
 
972
            this.testDocument.body.appendChild(errorElement);
 
973
            errorElement.className = "status_failed";
 
974
        }
 
975
    },
 
976
 
 
977
    _addBreakpointSupport: function() {
 
978
        for (var i = 0; i < this.commandRows.length; i++) {
 
979
            var row = this.commandRows[i];
 
980
            row.addBreakpointSupport();
 
981
        }
 
982
    },
 
983
 
 
984
    hasMoreCommandRows: function() {
 
985
        return this.nextCommandRowIndex < this.commandRows.length;
 
986
    },
 
987
 
 
988
    getNextCommandRow: function() {
 
989
        if (this.hasMoreCommandRows()) {
 
990
            return this.commandRows[this.nextCommandRowIndex++];
 
991
        }
 
992
        return null;
 
993
    }
 
994
 
 
995
});
 
996
 
 
997
 
 
998
// TODO: split out an JavascriptTestCase class to handle the "sejs" stuff
 
999
 
 
1000
var get_new_rows = function() {
 
1001
    var row_array = new Array();
 
1002
    for (var i = 0; i < new_block.length; i++) {
 
1003
 
 
1004
        var new_source = (new_block[i][0].tokenizer.source.slice(new_block[i][0].start,
 
1005
                new_block[i][0].end));
 
1006
 
 
1007
        var row = '<td style="display:none;" class="js">getEval</td>' +
 
1008
                  '<td style="display:none;">currentTest.doNextCommand()</td>' +
 
1009
                  '<td style="white-space: pre;">' + new_source + '</td>' +
 
1010
                  '<td></td>'
 
1011
 
 
1012
        row_array.push(row);
 
1013
    }
 
1014
    return row_array;
 
1015
};
 
1016
 
 
1017
 
 
1018
var Metrics = classCreate();
 
1019
objectExtend(Metrics.prototype, {
 
1020
    initialize: function() {
 
1021
        // The number of tests run
 
1022
        this.numTestPasses = 0;
 
1023
        // The number of tests that have failed
 
1024
        this.numTestFailures = 0;
 
1025
        // The number of commands which have passed
 
1026
        this.numCommandPasses = 0;
 
1027
        // The number of commands which have failed
 
1028
        this.numCommandFailures = 0;
 
1029
        // The number of commands which have caused errors (element not found)
 
1030
        this.numCommandErrors = 0;
 
1031
        // The time that the test was started.
 
1032
        this.startTime = null;
 
1033
        // The current time.
 
1034
        this.currentTime = null;
 
1035
    },
 
1036
 
 
1037
    printMetrics: function() {
 
1038
        setText(sel$('commandPasses'), this.numCommandPasses);
 
1039
        setText(sel$('commandFailures'), this.numCommandFailures);
 
1040
        setText(sel$('commandErrors'), this.numCommandErrors);
 
1041
        setText(sel$('testRuns'), this.numTestPasses + this.numTestFailures);
 
1042
        setText(sel$('testFailures'), this.numTestFailures);
 
1043
 
 
1044
        this.currentTime = new Date().getTime();
 
1045
 
 
1046
        var timeDiff = this.currentTime - this.startTime;
 
1047
        var totalSecs = Math.floor(timeDiff / 1000);
 
1048
 
 
1049
        var minutes = Math.floor(totalSecs / 60);
 
1050
        var seconds = totalSecs % 60;
 
1051
 
 
1052
        setText(sel$('elapsedTime'), this._pad(minutes) + ":" + this._pad(seconds));
 
1053
    },
 
1054
 
 
1055
// Puts a leading 0 on num if it is less than 10
 
1056
    _pad: function(num) {
 
1057
        return (num > 9) ? num : "0" + num;
 
1058
    },
 
1059
 
 
1060
    resetMetrics: function() {
 
1061
        this.numTestPasses = 0;
 
1062
        this.numTestFailures = 0;
 
1063
        this.numCommandPasses = 0;
 
1064
        this.numCommandFailures = 0;
 
1065
        this.numCommandErrors = 0;
 
1066
        this.startTime = new Date().getTime();
 
1067
    }
 
1068
 
 
1069
});
 
1070
 
 
1071
var HtmlRunnerCommandFactory = classCreate();
 
1072
objectExtend(HtmlRunnerCommandFactory.prototype, {
 
1073
 
 
1074
    initialize: function(seleniumCommandFactory, testLoop) {
 
1075
        this.seleniumCommandFactory = seleniumCommandFactory;
 
1076
        this.testLoop = testLoop;
 
1077
        this.handlers = {};
 
1078
        //todo: register commands
 
1079
    },
 
1080
 
 
1081
    getCommandHandler: function(command) {
 
1082
        if (this.handlers[command]) {
 
1083
            return this.handlers[command];
 
1084
        }
 
1085
        return this.seleniumCommandFactory.getCommandHandler(command);
 
1086
    }
 
1087
 
 
1088
});
 
1089
 
 
1090
var HtmlRunnerTestLoop = classCreate();
 
1091
objectExtend(HtmlRunnerTestLoop.prototype, new TestLoop());
 
1092
objectExtend(HtmlRunnerTestLoop.prototype, {
 
1093
    initialize: function(htmlTestCase, metrics, seleniumCommandFactory) {
 
1094
 
 
1095
        this.commandFactory = new HtmlRunnerCommandFactory(seleniumCommandFactory, this);
 
1096
        this.metrics = metrics;
 
1097
 
 
1098
        this.htmlTestCase = htmlTestCase;
 
1099
        LOG.info("Starting test " + htmlTestCase.pathname);
 
1100
 
 
1101
        this.currentRow = null;
 
1102
        this.currentRowIndex = 0;
 
1103
 
 
1104
        // used for selenium tests in javascript
 
1105
        this.currentItem = null;
 
1106
        this.commandAgenda = new Array();
 
1107
        this.expectedFailure = null;
 
1108
        this.expectedFailureType = null;
 
1109
 
 
1110
        this.htmlTestCase.reset();
 
1111
    },
 
1112
 
 
1113
    _advanceToNextRow: function() {
 
1114
        if (this.htmlTestCase.hasMoreCommandRows()) {
 
1115
            this.currentRow = this.htmlTestCase.getNextCommandRow();
 
1116
            if (this.sejsElement) {
 
1117
                this.currentItem = agenda.pop();
 
1118
                this.currentRowIndex++;
 
1119
            }
 
1120
        } else {
 
1121
            this.currentRow = null;
 
1122
            this.currentItem = null;
 
1123
        }
 
1124
    },
 
1125
 
 
1126
    nextCommand : function() {
 
1127
        this._advanceToNextRow();
 
1128
        if (this.currentRow == null) {
 
1129
            return null;
 
1130
        }
 
1131
        return this.currentRow.getCommand();
 
1132
    },
 
1133
 
 
1134
    commandStarted : function() {
 
1135
        sel$('pauseTest').disabled = false;
 
1136
        this.currentRow.select();
 
1137
        this.metrics.printMetrics();
 
1138
    },
 
1139
 
 
1140
    commandComplete : function(result) {
 
1141
        this._checkExpectedFailure(result);
 
1142
        if (result.failed) {
 
1143
            this.metrics.numCommandFailures += 1;
 
1144
            this._recordFailure(result.failureMessage);
 
1145
        } else if (result.passed) {
 
1146
            this.metrics.numCommandPasses += 1;
 
1147
            this.currentRow.markPassed();
 
1148
        } else {
 
1149
            this.currentRow.markDone();
 
1150
        }
 
1151
    },
 
1152
 
 
1153
    _checkExpectedFailure : function(result) {
 
1154
        if (this.expectedFailure != null) {
 
1155
            if (this.expectedFailureJustSet) {
 
1156
                this.expectedFailureJustSet = false;
 
1157
                return;
 
1158
            }
 
1159
            if (!result.failed) {
 
1160
                result.passed = false;
 
1161
                result.failed = true;
 
1162
                result.failureMessage = "Expected " + this.expectedFailureType + " did not occur.";
 
1163
            } else {
 
1164
                if (PatternMatcher.matches(this.expectedFailure, result.failureMessage)) {
 
1165
                    var failureType = result.error ? "error" : "failure";
 
1166
                    if (failureType == this.expectedFailureType) {
 
1167
                        result.failed = false;
 
1168
                        result.passed = true;
 
1169
                    } else {
 
1170
                        result.failed = true;
 
1171
                        result.failureMessage = "Expected "+this.expectedFailureType+", but "+failureType+" occurred instead";
 
1172
                    }
 
1173
                } else {
 
1174
                    result.failed = true;
 
1175
                    result.failureMessage = "Expected " + this.expectedFailureType + " message '" + this.expectedFailure
 
1176
                                            + "' but was '" + result.failureMessage + "'";
 
1177
                }
 
1178
            }
 
1179
            this.expectedFailure = null;
 
1180
            this.expectedFailureType = null;
 
1181
        }
 
1182
    },
 
1183
 
 
1184
    commandError : function(errorMessage) {
 
1185
        var tempResult = {};
 
1186
        tempResult.passed = false;
 
1187
        tempResult.failed = true;
 
1188
        tempResult.error = true;
 
1189
        tempResult.failureMessage = errorMessage;
 
1190
        this._checkExpectedFailure(tempResult);
 
1191
        if (tempResult.passed) {
 
1192
            this.currentRow.markDone();
 
1193
            return true;
 
1194
        }
 
1195
        errorMessage = tempResult.failureMessage;
 
1196
        this.metrics.numCommandErrors += 1;
 
1197
        this._recordFailure(errorMessage);
 
1198
    },
 
1199
 
 
1200
    _recordFailure : function(errorMsg) {
 
1201
        LOG.warn("currentTest.recordFailure: " + errorMsg);
 
1202
        htmlTestRunner.markFailed();
 
1203
        this.htmlTestCase.addErrorMessage(errorMsg, this.currentRow);
 
1204
    },
 
1205
 
 
1206
    testComplete : function() {
 
1207
        sel$('pauseTest').disabled = true;
 
1208
        sel$('stepTest').disabled = true;
 
1209
        if (htmlTestRunner.testFailed) {
 
1210
            this.htmlTestCase.markFailed();
 
1211
            this.metrics.numTestFailures += 1;
 
1212
        } else {
 
1213
            this.htmlTestCase.markPassed();
 
1214
            this.metrics.numTestPasses += 1;
 
1215
        }
 
1216
 
 
1217
        this.metrics.printMetrics();
 
1218
 
 
1219
        window.setTimeout(function() {
 
1220
            htmlTestRunner.runNextTest();
 
1221
        }, 1);
 
1222
    },
 
1223
 
 
1224
    getCommandInterval : function() {
 
1225
        return htmlTestRunner.controlPanel.runInterval;
 
1226
    },
 
1227
 
 
1228
    pause : function() {
 
1229
        htmlTestRunner.controlPanel.pauseCurrentTest();
 
1230
    },
 
1231
 
 
1232
    doNextCommand: function() {
 
1233
        var _n = this.currentItem[0];
 
1234
        var _x = this.currentItem[1];
 
1235
 
 
1236
        new_block = new Array()
 
1237
        execute(_n, _x);
 
1238
        if (new_block.length > 0) {
 
1239
            var the_table = this.htmlTestCase.testDocument.getElementById("se-js-table")
 
1240
            var loc = this.currentRowIndex
 
1241
            var new_rows = get_new_rows()
 
1242
 
 
1243
            // make the new statements visible on screen...
 
1244
            for (var i = 0; i < new_rows.length; i++) {
 
1245
                the_table.insertRow(loc + 1);
 
1246
                the_table.rows[loc + 1].innerHTML = new_rows[i];
 
1247
                this.commandRows.unshift(the_table.rows[loc + 1])
 
1248
            }
 
1249
 
 
1250
        }
 
1251
    }
 
1252
 
 
1253
});
 
1254
 
 
1255
Selenium.prototype.doPause = function(waitTime) {
 
1256
    /** Wait for the specified amount of time (in milliseconds)
 
1257
     * @param waitTime the amount of time to sleep (in milliseconds)
 
1258
     */
 
1259
    // todo: should not refer to currentTest directly
 
1260
    currentTest.pauseInterval = waitTime;
 
1261
};
 
1262
 
 
1263
Selenium.prototype.doBreak = function() {
 
1264
    /** Halt the currently running test, and wait for the user to press the Continue button.
 
1265
     * This command is useful for debugging, but be careful when using it, because it will
 
1266
     * force automated tests to hang until a user intervenes manually.
 
1267
     */
 
1268
    // todo: should not refer to controlPanel directly
 
1269
    htmlTestRunner.controlPanel.setToPauseAtNextCommand();
 
1270
};
 
1271
 
 
1272
Selenium.prototype.doStore = function(expression, variableName) {
 
1273
    /** This command is a synonym for storeExpression.
 
1274
     * @param expression the value to store
 
1275
     * @param variableName the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
 
1276
     */
 
1277
    storedVars[variableName] = expression;
 
1278
}
 
1279
 
 
1280
/*
 
1281
 * Click on the located element, and attach a callback to notify
 
1282
 * when the page is reloaded.
 
1283
 */
 
1284
// DGF TODO this code has been broken for some time... what is it trying to accomplish?
 
1285
Selenium.prototype.XXXdoModalDialogTest = function(returnValue) {
 
1286
    this.browserbot.doModalDialogTest(returnValue);
 
1287
};
 
1288
 
 
1289
Selenium.prototype.doEcho = function(message) {
 
1290
    /** Prints the specified message into the third table cell in your Selenese tables.
 
1291
     * Useful for debugging.
 
1292
     * @param message the message to print
 
1293
     */
 
1294
    currentTest.currentRow.setMessage(message);
 
1295
}
 
1296
 
 
1297
/*
 
1298
 * doSetSpeed and getSpeed are already defined in selenium-api.js,
 
1299
 * so we're defining these functions in a tricky way so that doc.js doesn't
 
1300
 * try to read API doc from the function definitions here.
 
1301
 */
 
1302
Selenium.prototype._doSetSpeed = function(value) {
 
1303
    var milliseconds = parseInt(value);
 
1304
    if (milliseconds < 0) milliseconds = 0;
 
1305
    htmlTestRunner.controlPanel.speedController.setValue(milliseconds);
 
1306
    htmlTestRunner.controlPanel.setRunInterval(milliseconds);
 
1307
}
 
1308
Selenium.prototype.doSetSpeed = Selenium.prototype._doSetSpeed;
 
1309
 
 
1310
Selenium.prototype._getSpeed = function() {
 
1311
    return htmlTestRunner.controlPanel.runInterval;
 
1312
}
 
1313
Selenium.prototype.getSpeed = Selenium.prototype._getSpeed;
 
1314
 
 
1315
Selenium.prototype.assertSelected = function(selectLocator, optionLocator) {
 
1316
    /**
 
1317
     * Verifies that the selected option of a drop-down satisfies the optionSpecifier.  <i>Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.</i>
 
1318
     *
 
1319
     * <p>See the select command for more information about option locators.</p>
 
1320
     *
 
1321
     * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
 
1322
     * @param optionLocator an option locator, typically just an option label (e.g. "John Smith")
 
1323
     */
 
1324
    var element = this.page().findElement(selectLocator);
 
1325
    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
 
1326
    if (element.selectedIndex == -1)
 
1327
    {
 
1328
        Assert.fail("No option selected");
 
1329
    }
 
1330
    locator.assertSelected(element);
 
1331
};
 
1332
 
 
1333
Selenium.prototype.assertFailureOnNext = function(message) {
 
1334
    /**
 
1335
     * Tell Selenium to expect a failure on the next command execution. 
 
1336
     * @param message The failure message we should expect.  This command will fail if the wrong failure message appears.
 
1337
     */
 
1338
    if (!message) {
 
1339
        throw new SeleniumError("Message must be provided");
 
1340
    }
 
1341
 
 
1342
    currentTest.expectedFailure = message;
 
1343
    currentTest.expectedFailureType = "failure";
 
1344
    currentTest.expectedFailureJustSet = true;
 
1345
};
 
1346
 
 
1347
Selenium.prototype.assertErrorOnNext = function(message) {
 
1348
    /**
 
1349
     * Tell Selenium to expect an error on the next command execution. 
 
1350
     * @param message The error message we should expect.  This command will fail if the wrong error message appears.
 
1351
     */
 
1352
     // This command temporarily installs a CommandFactory that generates
 
1353
     // CommandHandlers that expect an error.
 
1354
    if (!message) {
 
1355
        throw new SeleniumError("Message must be provided");
 
1356
    }
 
1357
 
 
1358
    currentTest.expectedFailure = message;
 
1359
    currentTest.expectedFailureType = "error";
 
1360
    currentTest.expectedFailureJustSet = true;
 
1361
};
 
1362