selenium-core-runner 0.0.3
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.
- data/Gemfile +9 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +30 -0
- data/app/controllers/selenium_core_runner/suites_controller.rb +19 -0
- data/app/views/selenium_core_runner/suites/index.html.erb +177 -0
- data/app/views/selenium_core_runner/suites/show.html.erb +0 -0
- data/config/routes.rb +6 -0
- data/lib/selenium-core-runner/engine.rb +19 -0
- data/lib/selenium-core-runner.rb +3 -0
- data/public/selenium-core-runner/Blank.html +7 -0
- data/public/selenium-core-runner/InjectedRemoteRunner.html +8 -0
- data/public/selenium-core-runner/RemoteRunner.html +101 -0
- data/public/selenium-core-runner/SeleniumLog.html +109 -0
- data/public/selenium-core-runner/TestPrompt.html +145 -0
- data/public/selenium-core-runner/TestRunner-splash.html +55 -0
- data/public/selenium-core-runner/TestRunner.hta +177 -0
- data/public/selenium-core-runner/TestRunner.html +177 -0
- data/public/selenium-core-runner/icons/all.png +0 -0
- data/public/selenium-core-runner/icons/continue.png +0 -0
- data/public/selenium-core-runner/icons/continue_disabled.png +0 -0
- data/public/selenium-core-runner/icons/pause.png +0 -0
- data/public/selenium-core-runner/icons/pause_disabled.png +0 -0
- data/public/selenium-core-runner/icons/selected.png +0 -0
- data/public/selenium-core-runner/icons/step.png +0 -0
- data/public/selenium-core-runner/icons/step_disabled.png +0 -0
- data/public/selenium-core-runner/iedoc-core.xml +1789 -0
- data/public/selenium-core-runner/iedoc.xml +1830 -0
- data/public/selenium-core-runner/lib/cssQuery/cssQuery-p.js +6 -0
- data/public/selenium-core-runner/lib/cssQuery/src/cssQuery-level2.js +142 -0
- data/public/selenium-core-runner/lib/cssQuery/src/cssQuery-level3.js +150 -0
- data/public/selenium-core-runner/lib/cssQuery/src/cssQuery-standard.js +53 -0
- data/public/selenium-core-runner/lib/cssQuery/src/cssQuery.js +356 -0
- data/public/selenium-core-runner/lib/prototype.js +2006 -0
- data/public/selenium-core-runner/lib/scriptaculous/builder.js +101 -0
- data/public/selenium-core-runner/lib/scriptaculous/controls.js +815 -0
- data/public/selenium-core-runner/lib/scriptaculous/dragdrop.js +915 -0
- data/public/selenium-core-runner/lib/scriptaculous/effects.js +958 -0
- data/public/selenium-core-runner/lib/scriptaculous/scriptaculous.js +47 -0
- data/public/selenium-core-runner/lib/scriptaculous/slider.js +283 -0
- data/public/selenium-core-runner/lib/scriptaculous/unittest.js +383 -0
- data/public/selenium-core-runner/lib/snapsie.js +91 -0
- data/public/selenium-core-runner/scripts/find_matching_child.js +69 -0
- data/public/selenium-core-runner/scripts/htmlutils.js +1623 -0
- data/public/selenium-core-runner/scripts/injection.html +72 -0
- data/public/selenium-core-runner/scripts/selenium-api.js +3240 -0
- data/public/selenium-core-runner/scripts/selenium-browserbot.js +2333 -0
- data/public/selenium-core-runner/scripts/selenium-browserdetect.js +153 -0
- data/public/selenium-core-runner/scripts/selenium-commandhandlers.js +379 -0
- data/public/selenium-core-runner/scripts/selenium-executionloop.js +175 -0
- data/public/selenium-core-runner/scripts/selenium-logging.js +148 -0
- data/public/selenium-core-runner/scripts/selenium-remoterunner.js +695 -0
- data/public/selenium-core-runner/scripts/selenium-testrunner.js +1362 -0
- data/public/selenium-core-runner/scripts/selenium-version.js +5 -0
- data/public/selenium-core-runner/scripts/ui-doc.html +803 -0
- data/public/selenium-core-runner/scripts/ui-element.js +1627 -0
- data/public/selenium-core-runner/scripts/ui-map-sample.js +979 -0
- data/public/selenium-core-runner/scripts/user-extensions.js +3 -0
- data/public/selenium-core-runner/scripts/user-extensions.js.sample +75 -0
- data/public/selenium-core-runner/scripts/xmlextras.js +153 -0
- data/public/selenium-core-runner/selenium-logo.png +0 -0
- data/public/selenium-core-runner/selenium-test.css +43 -0
- data/public/selenium-core-runner/selenium.css +316 -0
- data/public/selenium-core-runner/xpath/dom.js +566 -0
- data/public/selenium-core-runner/xpath/javascript-xpath-0.1.11.js +2816 -0
- data/public/selenium-core-runner/xpath/util.js +549 -0
- data/public/selenium-core-runner/xpath/xmltoken.js +149 -0
- data/public/selenium-core-runner/xpath/xpath.js +2481 -0
- metadata +121 -0
@@ -0,0 +1,1362 @@
|
|
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> </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> </td>\n</tr>");
|
869
|
+
}
|
870
|
+
scriptFile.WriteLine("</table><pre>");
|
871
|
+
var log = inputs["log"];
|
872
|
+
log=log.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">").replace(/"/gm,""").replace(/'/gm,"'");
|
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
|
+
|