<?php /** * Selenium PHP driver. Saves the test command in a "out" queue (in session), * and for each selenese request, remove the first comand from the "out" queue * and push the results into the "in" queque (also in session). When "out" queque * is empty, write the results to disk. * * Usage: See ../../example.php * * This program is free software; you can redistribute it and/or modify * it under the terms of the BSD License. * * Copyright(c) 2004 by Wei Zhuo. All rights reserved. * * To contact the author write to {@link mailto:weizhuo[at]gmail[dot]com Wei Zhuo} * The latest version of PRADO can be obtained from: * {@link http://prado.sourceforge.net/} * * @author Wei Zhuo <weizhuo[at]gmail[dot]com> * @version $Id$ * @package Prado.tests */ /** * Selenium automatic client runner, * * @author Wei Zhuo<weizhuo[at]gmail[dot]com> * @version $Id$ * @package Prado.tests */ require_once(dirname(__FILE__).'/results.php'); class SeleniumTestRunner { protected $driver; protected $base_dir = ''; public function __construct($driver=null, $base_dir='../javascript/') { if(is_null($driver) && !(php_sapi_name() == 'cli')) $driver = $_SERVER['SCRIPT_NAME']; $this->driver = $driver; $this->base_dir = $base_dir; } public function render() { if((php_sapi_name() == 'cli')) return; $file = dirname(__FILE__).'/TestRunner.php'; $driver = $this->driver; //$base_dir = $this->base_dir; $base_dir = $driver.'?sr='; include($file); } public function getDriver() { return $this->driver; } } class SeleniumTestStorage { protected $outputs = array(); protected $tests = array(); protected $options=array(); public function getTests() { return $this->tests; } public function getOptions() { return $this->options; } public function addOption($test_name, $option) { $this->options[$test_name] = $option; } public function addCommand($test_case_id, $command) { $data = array($test_case_id, $command); array_push($this->outputs, $data); } public function getCommand() { return array_shift($this->outputs); } public function addTestCase($command, $trace_details, $test_name, $test_suite) { $data = array(0, 0, $command, "", $trace_details, $test_name, $test_suite); array_push($this->tests, $data); } } class SeleneseInterpreter { protected $storage; protected $tracer; public function __construct($storage, $tracer) { $this->storage = $storage; $this->tracer = $tracer; } public function getTests() { return $this->storage->getTests(); } public function getOptions() { return $this->storage->getOptions(); } public function getCommand() { $command = $this->storage->getCommand(); return empty($command) ? "|testComplete|||" : "{$command[1]}<{$command[0]}>"; } public function __call($func, $args) { if($func{0} == '_') return; $trace = debug_backtrace(); if($this->isTestOptionFunction($func,$args,$trace)) return; $ID = isset($args[0]) ? $args[0] : ""; $value = isset($args[1]) ? $args[1] : ""; if(strpos(strtolower($func),'htmlpresent') || strpos(strtolower($func),'htmlnotpresent')) $ID = htmlspecialchars($ID); $command = array($func, $ID, $value); if(is_int(strpos(strtolower($func), 'visible'))) $this->addCommand(array('pause','250',''),$trace); return $this->addCommand($command, $trace); } protected function isTestOptionFunction($func,$args,$trace) { if(strtolower($func)==='skipcondition') { list($trace, $test, $suite) = $this->tracer->getTrace($trace); $this->storage->addOption($test,$args[0]); return true; } return false; } protected function addCommand($command, $trace) { list($trace, $test, $suite) = $this->tracer->getTrace($trace); $test_id = $this->storage->addTestCase($command, $trace, $test, $suite); $this->storage->addCommand($test_id, $command); } } class SeleniumTestTrace { protected $root; public function __construct($root) { $this->root = $root; } public function getTrace($trace) { $group = array_pop($trace); $info = $trace[3]; $test = $group['args'][0]->getTestList(); $i = count($test); $name = $test[$i-2].'::'.$test[$i-1]; $suite = $test[0]; unset($info['object']); /* for($i = 0; $i < count($info['args']); $i++) { if($info['args'][$i] instanceof TControl) $info['args'][$i] = $info['args'][$i]->ClientID; }*/ $file = str_replace($this->root, '', $info['file']); $info['file'] = substr($file, 1); return array($info, $name, $suite); } } class SimpleSeleniumProxyServer { protected $runner; protected $int; protected $result_file; public function __construct($runner, $int, $result_file) { $this->int = $int; $this->runner = $runner; $this->result_file = $result_file; } public function proxy() { return $this->int; } public static function getInstance($root='/', $result_file='results.dat', $base_dir='selenium/') { static $instance; if(!isset($instance)) { $storage = new SeleniumTestStorage(); $tracer = new SeleniumTestTrace($root); $interpreter = new SeleneseInterpreter($storage, $tracer); $runner = new SeleniumTestRunner(null, $base_dir); $instance = new SimpleSeleniumProxyServer($runner, $interpreter, $result_file); } $instance->serveResults(); return $instance; } public function handleRequest() { $client = new SeleniumTestRunnerServer($this->int, $this->runner); $client->serve(); return true; } public function serveResults() { if(isset($_POST['result'])) { $result = new SeleniumTestResult(); $reporter = new SeleniumHtmlReporter($result); $reporter->render(); exit(); } } } class SeleniumTestSuiteWriter { protected $suites; protected $name; protected $runner; function __construct($suites, $name, $runner) { $this->suites = $suites; $this->name = $name; $this->runner = $runner; } protected function renderHeader() { $base_dir = $this->runner->getDriver().'?sr='; include(dirname(__FILE__).'/TestSuiteHeader.php'); $contents = <<<EOD <tr><td><b>{$this->name}</b></td></tr> EOD; echo $contents; } public function render() { $this->renderHeader(); foreach($this->suites as $name => $suite) { $file = $suite[0]['trace']['file']; $file = strtr($file,'\\','/'); $option = $suite[0]['option']===null?'':' unless="'.$suite[0]['option'].'" '; $url = $this->runner->getDriver()."?case={$name}&file={$file}"; echo "<tr{$option}>\n"; echo "<td><a href=\"{$url}\">{$name}</a></td>\n"; echo "</tr>\n"; } echo $this->renderFooter(); } protected function getJsTraceInfo() { $contents = "var prado_trace = {};\n"; foreach($this->suites as $name => $suite) { $name = $name; $contents .= "prado_trace['{$name}'] = ["; $cases = array(); foreach($suite as $testcase) $cases[] = "'".addslashes(htmlspecialchars(serialize($testcase['trace'])))."'"; $contents .= implode(",\n", $cases)."];\n\n"; } return $contents; } protected function renderFooter() { $trace = '';//$this->getJsTraceInfo(); $contents = <<<EOD </tbody> </table> <br /> <em>Not supported in this browser</em> <table id="skippedTests" cellpadding="1" cellspacing="1" border="1" class="selenium"> <tbody> <tr><td><b>Skipped Tests</b></td></tr> </tbody> </table> <script type="text/javascript"> /*<![CDATA[*/ $trace /*]]>*/ </script> </body> </html> EOD; return $contents; } } class SeleniumTestCaseWriter { protected $case; protected $tests; function __construct($case, $tests) { $this->case = $case; $this->tests = $tests; } protected function renderHeader() { $contents = <<<EOD <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>{$this->case}</title> <meta content="text/html; charset=UTF-8" http-equiv="content-type"> </head> <body> <table cellpadding="1" cellspacing="1" border="1" id=TABLE1> <tbody> <tr> <td rowspan="1" colspan="3"><strong>{$this->case}</strong></td> </tr> EOD; return $contents; } public function render() { echo $this->renderHeader(); foreach($this->tests as $test) { $t = $test['test']; if($t[0] == "open") $t[1] = "<a href=\"{$t[1]}\" target=\"_blank\">{$t[1]}</a>"; echo "<tr>\n"; echo "<td>{$t[0]}</td>\n"; echo "<td>{$t[1]}</td>\n"; echo "<td>{$t[2]}</td>\n"; echo "</tr>\n"; } echo $this->renderFooter(); } protected function renderFooter() { $contents = <<<EOD </tbody> </table> </body> </html> EOD; return $contents; } } class SeleniumTestRunnerServer { protected $cases = array(); protected $trace = array(); protected $name; protected $runner; public function __construct($int, $runner) { $this->runner = $runner; $this->initialize($int); } protected function initialize($int) { $options = $int->getOptions(); foreach($int->getTests() as $command) { $case = $command[5]; $option=isset($options[$case])?$options[$case]:null; $this->cases[$case][] = array('test' => $command[2], 'trace' => $command[4], 'option'=>$option); if(is_null($this->name)) $this->name = $command[6]; } } function serve() { if($this->isTestSuiteRequest()) { $testSuite = new SeleniumTestSuiteWriter($this->cases, $this->name, $this->runner); $testSuite->render(); } else if($this->isTestCaseRequest()) { if(($case = $this->getTestCaseRequest()) !== false) { $testCase = new SeleniumTestCaseWriter($case, $this->getTestCase()); $testCase->render(); } } else { $this->runner->render(); } } protected function isTestSuiteRequest() { return isset($_GET['testSuites']); } protected function isTestCaseRequest() { return isset($_GET['case']); } public function isClientRequest() { return !$this->isTestSuiteRequest() && !$this->isTestCaseRequest(); } protected function getTestCaseRequest() { $case = $_GET['case']; if(isset($this->cases[$case])) return $case; else return false; } protected function getTestCase() { $case = $_GET['case']; if(isset($this->cases[$case])) return $this->cases[$case]; else return array(); } } class SeleniumTestCase extends UnitTestCase { protected $selenium; protected $Page; const KONQUEROR='browserVersion.isKonqueror'; const OPERA='browserVersion.isOpera'; const CHROME='browserVersion.isChrome'; const INTERNET_EXPLORER='browserVersion.isIE'; const SAFARI='browserVersion.isSafari'; const KHTML='browserVersion.khtml'; const FIREFOX='browserVersion.isFirefox'; const MOZILLA='browserVersion.isMozilla'; const GECKO='browserVersion.isGecko'; protected $options=array(); function __construct() { $server = SimpleSeleniumProxyServer::getInstance(); if(!is_null($server)) $this->selenium = $server->proxy(); parent::__construct(); } function getPage($class) { $info = new ReflectionClass($class); return Prado::getApplication()->getTestPage($info->getFileName()); } function __call($func, $args) { if(count($args) == 0) return $this->selenium->{$func}(); else if (count($args) == 1) return $this->selenium->{$func}($args[0]); else if (count($args) == 2) return $this->selenium->{$func}($args[0], $args[1]); } function disabled() { $this->selenium->skipCondition('DISABLED'); } function skipBrowsers() { $conditions = $this->getBrowserOptions(func_get_args()); $this->selenium->skipCondition($conditions); } protected function getBrowserOptions($arg_list) { $browsers=array(); foreach($arg_list as $arg) { if(is_array($arg)) $browsers[] = '('.implode(' && ', $arg).')'; else $browsers[] = $arg; } return implode(' || ', $browsers); } function targetBrowsers() { $conditions = $this->getBrowserOptions(func_get_args()); $this->selenium->skipCondition("!(".$conditions.")"); } } ?>