Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * Selenium PHP driver. Saves the test command in a "out" queue (in session),
4
 * and for each selenese request, remove the first comand from the "out" queue
5
 * and push the results into the "in" queque (also in session). When "out" queque
6
 * is empty, write the results to disk.
7
 *
8
 * Usage: See ../../example.php
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the BSD License.
12
 *
13
 * Copyright(c) 2004 by Wei Zhuo. All rights reserved.
14
 *
15
 * To contact the author write to {@link mailto:weizhuo[at]gmail[dot]com Wei Zhuo}
16
 * The latest version of PRADO can be obtained from:
17
 * {@link http://prado.sourceforge.net/}
18
 *
19
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
20
 * @version $Id: selenium.php 1771 2007-03-26 00:27:59Z xue $
21
 * @package Prado.tests
22
 */
23
 
24
/**
25
 * Selenium automatic client runner,
26
 *
27
 * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
28
 * @version $Id: selenium.php 1771 2007-03-26 00:27:59Z xue $
29
 * @package Prado.tests
30
 */
31
 
32
require_once(dirname(__FILE__).'/results.php');
33
 
34
class SeleniumTestRunner
35
{
36
	protected $driver;
37
	protected $base_dir = '';
38
 
39
	public function __construct($driver=null, $base_dir='../javascript/')
40
	{
41
		if(is_null($driver) && !(php_sapi_name() == 'cli'))
42
			$driver = $_SERVER['SCRIPT_NAME'];
43
		$this->driver = $driver;
44
		$this->base_dir = $base_dir;
45
	}
46
 
47
	public function render()
48
	{
49
		if((php_sapi_name() == 'cli')) return;
50
		$file = dirname(__FILE__).'/TestRunner.php';
51
		$driver = $this->driver;
52
 
53
		//$base_dir = $this->base_dir;
54
		$base_dir = $driver.'?sr=';
55
		include($file);
56
	}
57
 
58
	public function getDriver()
59
	{
60
		return $this->driver;
61
	}
62
}
63
 
64
class SeleniumTestStorage
65
{
66
	protected $outputs = array();
67
	protected $tests = array();
68
	protected $options=array();
69
 
70
	public function getTests()
71
	{
72
		return $this->tests;
73
	}
74
 
75
	public function getOptions()
76
	{
77
		return $this->options;
78
	}
79
 
80
	public function addOption($test_name, $option)
81
	{
82
		$this->options[$test_name] = $option;
83
	}
84
 
85
	public function addCommand($test_case_id, $command)
86
	{
87
		$data = array($test_case_id, $command);
88
		array_push($this->outputs, $data);
89
	}
90
 
91
	public function getCommand()
92
	{
93
		return array_shift($this->outputs);
94
	}
95
 
96
	public function addTestCase($command, $trace_details, $test_name, $test_suite)
97
	{
98
		$data = array(0, 0, $command, "", $trace_details, $test_name, $test_suite);
99
		array_push($this->tests, $data);
100
	}
101
}
102
 
103
class SeleneseInterpreter
104
{
105
	protected $storage;
106
	protected $tracer;
107
 
108
	public function __construct($storage, $tracer)
109
	{
110
		$this->storage = $storage;
111
		$this->tracer = $tracer;
112
	}
113
 
114
	public function getTests()
115
	{
116
		return $this->storage->getTests();
117
	}
118
 
119
	public function getOptions()
120
	{
121
		return $this->storage->getOptions();
122
	}
123
 
124
	public function getCommand()
125
	{
126
		$command = $this->storage->getCommand();
127
		return empty($command) ? "|testComplete|||" : "{$command[1]}<{$command[0]}>";
128
	}
129
 
130
	public function __call($func, $args)
131
	{
132
		if($func{0} == '_') return;
133
 
134
		$trace = debug_backtrace();
135
		if($this->isTestOptionFunction($func,$args,$trace))
136
			return;
137
 
138
		$ID = isset($args[0]) ? $args[0] : "";
139
		$value = isset($args[1]) ? $args[1] : "";
140
		if(strpos(strtolower($func),'htmlpresent') || strpos(strtolower($func),'htmlnotpresent'))
141
			$ID = htmlspecialchars($ID);
142
		$command = array($func, $ID, $value);
143
 
144
		if(is_int(strpos(strtolower($func), 'visible')))
145
			$this->addCommand(array('pause','250',''),$trace);
146
 
147
		return $this->addCommand($command, $trace);
148
	}
149
 
150
	protected function isTestOptionFunction($func,$args,$trace)
151
	{
152
		if(strtolower($func)==='skipcondition')
153
		{
154
			list($trace, $test, $suite) = $this->tracer->getTrace($trace);
155
			$this->storage->addOption($test,$args[0]);
156
			return true;
157
		}
158
		return false;
159
	}
160
 
161
	protected function addCommand($command, $trace)
162
	{
163
		list($trace, $test, $suite) = $this->tracer->getTrace($trace);
164
		$test_id = $this->storage->addTestCase($command, $trace, $test, $suite);
165
		$this->storage->addCommand($test_id, $command);
166
	}
167
}
168
 
169
class SeleniumTestTrace
170
{
171
	protected $root;
172
 
173
	public function __construct($root)
174
	{
175
		$this->root = $root;
176
	}
177
 
178
	public function getTrace($trace)
179
	{
180
		$group = array_pop($trace);
181
		$info = $trace[3];
182
		$test = $group['args'][0]->getTestList();
183
		$i = count($test);
184
		$name = $test[$i-2].'::'.$test[$i-1];
185
		$suite = $test[0];
186
		unset($info['object']);
187
		/*
188
		for($i = 0; $i < count($info['args']); $i++)
189
		{
190
			if($info['args'][$i] instanceof TControl)
191
				$info['args'][$i] = $info['args'][$i]->ClientID;
192
		}*/
193
		$file = str_replace($this->root, '', $info['file']);
194
		$info['file'] = substr($file, 1);
195
 		return array($info, $name, $suite);
196
	}
197
}
198
 
199
class SimpleSeleniumProxyServer
200
{
201
	protected $runner;
202
	protected $int;
203
	protected $result_file;
204
 
205
	public function __construct($runner, $int, $result_file)
206
	{
207
		$this->int = $int;
208
		$this->runner = $runner;
209
		$this->result_file = $result_file;
210
	}
211
 
212
	public function proxy()
213
	{
214
		return $this->int;
215
	}
216
 
217
 
218
	public static function getInstance($root='/', $result_file='results.dat', $base_dir='selenium/')
219
	{
220
		static $instance;
221
		if(!isset($instance))
222
		{
223
			$storage = new SeleniumTestStorage();
224
			$tracer = new SeleniumTestTrace($root);
225
			$interpreter = new SeleneseInterpreter($storage, $tracer);
226
			$runner = new SeleniumTestRunner(null, $base_dir);
227
			$instance = new SimpleSeleniumProxyServer($runner, $interpreter, $result_file);
228
		}
229
		$instance->serveResults();
230
		return $instance;
231
	}
232
 
233
	public function handleRequest()
234
	{
235
		$client = new SeleniumTestRunnerServer($this->int, $this->runner);
236
		$client->serve();
237
		return true;
238
	}
239
 
240
	public function serveResults()
241
	{
242
		if(isset($_POST['result']))
243
		{
244
			$result = new SeleniumTestResult();
245
			$reporter = new SeleniumHtmlReporter($result);
246
			$reporter->render();
247
			exit();
248
		}
249
	}
250
 
251
}
252
 
253
class SeleniumTestSuiteWriter
254
{
255
	protected $suites;
256
	protected $name;
257
	protected $runner;
258
 
259
	function __construct($suites, $name, $runner)
260
	{
261
		$this->suites = $suites;
262
		$this->name = $name;
263
		$this->runner = $runner;
264
 
265
	}
266
 
267
	protected function renderHeader()
268
	{
269
		$base_dir = $this->runner->getDriver().'?sr=';
270
 
271
		include(dirname(__FILE__).'/TestSuiteHeader.php');
272
 
273
		$contents = <<<EOD
274
            <tr><td><b>{$this->name}</b></td></tr>
275
EOD;
276
		echo $contents;
277
	}
278
 
279
	public function render()
280
	{
281
		$this->renderHeader();
282
		foreach($this->suites as $name => $suite)
283
		{
284
			$file = $suite[0]['trace']['file'];
285
			$file = strtr($file,'\\','/');
286
			$option = $suite[0]['option']===null?'':' unless="'.$suite[0]['option'].'" ';
287
			$url = $this->runner->getDriver()."?case={$name}&file={$file}";
288
			echo "<tr{$option}>\n";
289
            echo "<td><a href=\"{$url}\">{$name}</a></td>\n";
290
            echo "</tr>\n";
291
		}
292
		echo $this->renderFooter();
293
	}
294
 
295
	protected function getJsTraceInfo()
296
	{
297
		$contents = "var prado_trace = {};\n";
298
		foreach($this->suites as $name => $suite)
299
		{
300
			$name = $name;
301
			$contents .= "prado_trace['{$name}'] = [";
302
			$cases = array();
303
			foreach($suite as $testcase)
304
				$cases[] = "'".addslashes(htmlspecialchars(serialize($testcase['trace'])))."'";
305
			$contents .= implode(",\n", $cases)."];\n\n";
306
		}
307
		return $contents;
308
	}
309
 
310
	protected function renderFooter()
311
	{
312
		$trace = '';//$this->getJsTraceInfo();
313
		$contents = <<<EOD
314
        </tbody>
315
    </table>
316
 
317
    <br />
318
    <em>Not supported in this browser</em>
319
    <table id="skippedTests" cellpadding="1"
320
           cellspacing="1"
321
           border="1"
322
           class="selenium">
323
        <tbody>
324
            <tr><td><b>Skipped Tests</b></td></tr>
325
        </tbody>
326
    </table>
327
	<script type="text/javascript">
328
	/*<![CDATA[*/
329
		$trace
330
	/*]]>*/
331
	</script>
332
</body>
333
</html>
334
EOD;
335
		return $contents;
336
	}
337
}
338
 
339
class SeleniumTestCaseWriter
340
{
341
	protected $case;
342
	protected $tests;
343
 
344
	function __construct($case, $tests)
345
	{
346
		$this->case = $case;
347
		$this->tests = $tests;
348
	}
349
 
350
	protected function renderHeader()
351
	{
352
		$contents = <<<EOD
353
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
354
<html>
355
<head>
356
<title>{$this->case}</title>
357
  <meta content="text/html; charset=UTF-8" http-equiv="content-type">
358
</head>
359
<body>
360
<table cellpadding="1" cellspacing="1" border="1" id=TABLE1>
361
  <tbody>
362
    <tr>
363
      <td rowspan="1" colspan="3"><strong>{$this->case}</strong></td>
364
    </tr>
365
EOD;
366
		return $contents;
367
	}
368
 
369
	public function render()
370
	{
371
		echo $this->renderHeader();
372
		foreach($this->tests as $test)
373
		{
374
			$t = $test['test'];
375
			if($t[0] == "open")
376
				$t[1] = "<a href=\"{$t[1]}\" target=\"_blank\">{$t[1]}</a>";
377
			echo "<tr>\n";
378
			echo "<td>{$t[0]}</td>\n";
379
			echo "<td>{$t[1]}</td>\n";
380
			echo "<td>{$t[2]}</td>\n";
381
			echo "</tr>\n";
382
		}
383
		echo $this->renderFooter();
384
	}
385
 
386
	protected function renderFooter()
387
	{
388
		$contents = <<<EOD
389
  </tbody>
390
</table>
391
</body>
392
</html>
393
EOD;
394
		return $contents;
395
	}
396
}
397
 
398
class SeleniumTestRunnerServer
399
{
400
	protected $cases = array();
401
	protected $trace = array();
402
	protected $name;
403
	protected $runner;
404
 
405
	public function __construct($int, $runner)
406
	{
407
		$this->runner = $runner;
408
		$this->initialize($int);
409
	}
410
 
411
	protected function initialize($int)
412
	{
413
		$options = $int->getOptions();
414
		foreach($int->getTests() as $command)
415
		{
416
			$case = $command[5];
417
			$option=isset($options[$case])?$options[$case]:null;
418
			$this->cases[$case][] =
419
				array('test' => $command[2],
420
					'trace' => $command[4], 'option'=>$option);
421
			if(is_null($this->name))
422
				$this->name = $command[6];
423
		}
424
	}
425
 
426
	function serve()
427
	{
428
		if($this->isTestSuiteRequest())
429
		{
430
			$testSuite = new SeleniumTestSuiteWriter($this->cases,
431
								$this->name, $this->runner);
432
			$testSuite->render();
433
		}
434
		else if($this->isTestCaseRequest())
435
		{
436
			if(($case = $this->getTestCaseRequest()) !== false)
437
			{
438
 
439
				$testCase = new SeleniumTestCaseWriter($case, $this->getTestCase());
440
				$testCase->render();
441
			}
442
		}
443
		else
444
		{
445
			$this->runner->render();
446
		}
447
	}
448
 
449
	protected function isTestSuiteRequest()
450
	{
451
		return isset($_GET['testSuites']);
452
	}
453
 
454
	protected function isTestCaseRequest()
455
	{
456
		return isset($_GET['case']);
457
	}
458
 
459
	public function isClientRequest()
460
	{
461
		return !$this->isTestSuiteRequest() && !$this->isTestCaseRequest();
462
	}
463
 
464
	protected function getTestCaseRequest()
465
	{
466
		$case = $_GET['case'];
467
		if(isset($this->cases[$case]))
468
			return $case;
469
		else return false;
470
	}
471
 
472
	protected function getTestCase()
473
	{
474
		$case = $_GET['case'];
475
		if(isset($this->cases[$case]))
476
			return $this->cases[$case];
477
		else
478
			return array();
479
	}
480
}
481
 
482
 
483
class SeleniumTestCase extends UnitTestCase
484
{
485
	protected $selenium;
486
	protected $Page;
487
 
488
	const KONQUEROR='browserVersion.isKonqueror';
489
	const OPERA='browserVersion.isOpera';
490
	const CHROME='browserVersion.isChrome';
491
	const INTERNET_EXPLORER='browserVersion.isIE';
492
	const SAFARI='browserVersion.isSafari';
493
	const KHTML='browserVersion.khtml';
494
	const FIREFOX='browserVersion.isFirefox';
495
	const MOZILLA='browserVersion.isMozilla';
496
	const GECKO='browserVersion.isGecko';
497
 
498
	protected $options=array();
499
 
500
	function __construct()
501
	{
502
		$server = SimpleSeleniumProxyServer::getInstance();
503
		if(!is_null($server))
504
			$this->selenium = $server->proxy();
505
		parent::__construct();
506
	}
507
 
508
	function getPage($class)
509
	{
510
		$info = new ReflectionClass($class);
511
		return Prado::getApplication()->getTestPage($info->getFileName());
512
	}
513
 
514
	function __call($func, $args)
515
	{
516
		if(count($args) == 0)
517
			return $this->selenium->{$func}();
518
		else if	(count($args) == 1)
519
			return $this->selenium->{$func}($args[0]);
520
		else if (count($args) == 2)
521
			return $this->selenium->{$func}($args[0], $args[1]);
522
	}
523
 
524
	function disabled()
525
	{
526
		$this->selenium->skipCondition('DISABLED');
527
	}
528
 
529
	function skipBrowsers()
530
	{
531
		$conditions = $this->getBrowserOptions(func_get_args());
532
		$this->selenium->skipCondition($conditions);
533
	}
534
 
535
	protected function getBrowserOptions($arg_list)
536
	{
537
		$browsers=array();
538
		foreach($arg_list as $arg)
539
		{
540
			if(is_array($arg))
541
				$browsers[] = '('.implode(' && ', $arg).')';
542
			else
543
				$browsers[] = $arg;
544
		}
545
		return implode(' || ', $browsers);
546
	}
547
 
548
	function targetBrowsers()
549
	{
550
		$conditions = $this->getBrowserOptions(func_get_args());
551
		$this->selenium->skipCondition("!(".$conditions.")");
552
	}
553
}
554
 
555
?>