API CHANGE Added FunctionalTest::

API CHANGE Added ->session()
API CHANGE Added FunctionalTest::
API CHANGE Improved FunctionalTest's match by selector commands to produce less brittle output (rationalises whitespace)


git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@55134 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sam Minnee 2008-05-26 06:21:30 +00:00
parent 5562885c69
commit 5dff780a5d
7 changed files with 104 additions and 12 deletions

View File

@ -581,6 +581,11 @@ class ManifestBuilder {
die("Cannot write manifest file! Check permissions of " . MANIFEST_FILE); die("Cannot write manifest file! Check permissions of " . MANIFEST_FILE);
} }
} }
static function includeEverything() {
global $_CLASS_MANIFEST;
foreach($_CLASS_MANIFEST as $classFile) require_once($classFile);
}
} }

View File

@ -107,7 +107,7 @@ class ContentController extends Controller {
} }
// Draft/Archive security check - only CMS users should be able to look at stage/archived content // Draft/Archive security check - only CMS users should be able to look at stage/archived content
if($this->URLSegment != 'Security' && (Versioned::current_archived_date() || (Versioned::current_stage() && Versioned::current_stage() != 'Live'))) { if($this->URLSegment != 'Security' && !Session::get('unsecuredDraftSite') && (Versioned::current_archived_date() || (Versioned::current_stage() && Versioned::current_stage() != 'Live'))) {
if(!Permission::check('CMS_ACCESS_CMSMain')) { if(!Permission::check('CMS_ACCESS_CMSMain')) {
$link = $this->Link(); $link = $this->Link();
$message = _t("ContentController.DRAFT_SITE_ACCESS_RESTRICTION", "You must log in with your CMS password in order to view the draft or archived content. <a href=\"%s\">Click here to go back to the published site.</a>"); $message = _t("ContentController.DRAFT_SITE_ACCESS_RESTRICTION", "You must log in with your CMS password in order to view the draft or archived content. <a href=\"%s\">Click here to go back to the published site.</a>");

View File

@ -413,12 +413,15 @@ class Director {
static function makeRelative($url) { static function makeRelative($url) {
$base1 = self::absoluteBaseURL(); $base1 = self::absoluteBaseURL();
$base2 = self::baseFolder(); $base2 = self::baseFolder();
$base3 = self::baseURL();
// Allow for the accidental inclusion of a // in the URL // Allow for the accidental inclusion of a // in the URL
$url = ereg_replace('([^:])//','\\1/',$url); $url = ereg_replace('([^:])//','\\1/',$url);
if(substr($url,0,strlen($base1)) == $base1) return substr($url,strlen($base1)); if(substr($url,0,strlen($base1)) == $base1) return substr($url,strlen($base1));
if(substr($url,0,strlen($base2)) == $base2) return substr($url,strlen($base2)); else if(substr($url,0,strlen($base2)) == $base2) return substr($url,strlen($base2));
else if(substr($url,0,strlen($base3)) == $base3) return substr($url,strlen($base3));
return $url; return $url;
} }

View File

@ -233,7 +233,7 @@ class PDODatabase extends Database {
* @return boolean Returns true if successful * @return boolean Returns true if successful
* @todo This shouldn't take any arguments; it should take the information given in the constructor instead. * @todo This shouldn't take any arguments; it should take the information given in the constructor instead.
*/ */
public function createDatabase($connect, $username, $password, $database) { public function createDatabase() {
try { try {
$dbh = new PDO($connect, $username, $password); $dbh = new PDO($connect, $username, $password);
$stmt = $dbh->prepare("CREATE DATABASE $database"); $stmt = $dbh->prepare("CREATE DATABASE $database");

View File

@ -20,21 +20,58 @@
* </code> * </code>
*/ */
class FunctionalTest extends SapphireTest { class FunctionalTest extends SapphireTest {
/**
* Set this to true on your sub-class to disable the use of themes in this test.
* This can be handy for functional testing of modules without having to worry about whether a user has changed
* behaviour by replacing the theme.
*/
static $disable_themes = false;
/**
* Set this to true on your sub-class to use the draft site by default for every test in this class.
*/
static $use_draft_site = false;
protected $mainSession = null; protected $mainSession = null;
/** /**
* CSSContentParser for the most recently requested page. * CSSContentParser for the most recently requested page.
*/ */
protected $cssParser = null; protected $cssParser = null;
private $originalTheme = null;
/**
* Returns the {@link Session} object for this test
*/
function session() {
return $this->mainSession->session();
}
function setUp() { function setUp() {
parent::setUp(); parent::setUp();
$this->mainSession = new TestSession(); $this->mainSession = new TestSession();
// Disable theme, if necessary
if($this->stat('disable_themes')) {
$this->originalTheme = SSViewer::current_theme();
SSViewer::set_theme(null);
}
// Switch to draft site, if necessary
if($this->stat('use_draft_site')) {
$this->useDraftSite();
}
} }
function tearDown() { function tearDown() {
parent::tearDown(); parent::tearDown();
$this->mainSession = null; $this->mainSession = null;
// Re-enable theme, if previously disabled
if($this->stat('disable_themes')) {
SSViewer::set_theme($this->originalTheme);
}
} }
/** /**
@ -42,7 +79,9 @@ class FunctionalTest extends SapphireTest {
*/ */
function get($url) { function get($url) {
$this->cssParser = null; $this->cssParser = null;
return $this->mainSession->get($url); $response = $this->mainSession->get($url);
if(is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
return $response;
} }
/** /**
@ -50,7 +89,9 @@ class FunctionalTest extends SapphireTest {
*/ */
function post($url, $data) { function post($url, $data) {
$this->cssParser = null; $this->cssParser = null;
return $this->mainSession->post($url, $data); $response = $this->mainSession->post($url, $data);
if(is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
return $response;
} }
/** /**
@ -59,7 +100,9 @@ class FunctionalTest extends SapphireTest {
*/ */
function submitForm($formID, $button = null, $data = array()) { function submitForm($formID, $button = null, $data = array()) {
$this->cssParser = null; $this->cssParser = null;
return $this->mainSession->submitForm($formID, $button, $data); $response = $this->mainSession->submitForm($formID, $button, $data);
if(is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
return $response;
} }
/** /**
@ -89,7 +132,7 @@ class FunctionalTest extends SapphireTest {
*/ */
function assertPartialMatchBySelector($selector, $expectedMatches) { function assertPartialMatchBySelector($selector, $expectedMatches) {
$items = $this->cssParser()->getBySelector($selector); $items = $this->cssParser()->getBySelector($selector);
foreach($items as $item) $actuals[$item . ''] = true; foreach($items as $item) $actuals[] = trim(preg_replace("/[ \n\r\t]+/", " ", $item. ''));
foreach($expectedMatches as $match) { foreach($expectedMatches as $match) {
if(!isset($actuals[$match])) { if(!isset($actuals[$match])) {
@ -115,7 +158,7 @@ class FunctionalTest extends SapphireTest {
*/ */
function assertExactMatchBySelector($selector, $expectedMatches) { function assertExactMatchBySelector($selector, $expectedMatches) {
$items = $this->cssParser()->getBySelector($selector); $items = $this->cssParser()->getBySelector($selector);
foreach($items as $item) $actuals[] = $item . ''; foreach($items as $item) $actuals[] = trim(preg_replace("/[ \n\r\t]+/", " ", $item. ''));
if($expectedMatches != $actuals) { if($expectedMatches != $actuals) {
throw new PHPUnit_Framework_AssertionFailedError( throw new PHPUnit_Framework_AssertionFailedError(
@ -172,4 +215,22 @@ class FunctionalTest extends SapphireTest {
); );
} }
} }
/**
* Use the draft (stage) site for testing.
* This is helpful if you're not testing publication functionality and don't want "stage management" cluttering your test.
*/
function useDraftSite() {
$this->session()->inst_set('currentStage', 'Stage');
$this->session()->inst_set('unsecuredDraftSite', true);
}
/**
* Return a static variable from this class.
* Gets around PHP's lack of late static binding.
*/
function stat($varName) {
$className = get_class($this);
return eval("return {$className}::\$$varName;");
}
} }

View File

@ -60,6 +60,19 @@ class TestRunner extends Controller {
echo "Please install PHPUnit using pear"; echo "Please install PHPUnit using pear";
} }
} }
function coverage() {
if(hasPhpUnit()) {
ManifestBuilder::includeEverything();
$tests = ClassInfo::subclassesFor('SapphireTest');
array_shift($tests);
unset($tests['FunctionalTest']);
$this->runTests($tests, true);
} else {
echo "Please install PHPUnit using pear";
}
}
/** /**
* Run only a single test class * Run only a single test class
@ -75,7 +88,7 @@ class TestRunner extends Controller {
} }
function runTests($classList) { function runTests($classList, $coverage = false) {
if(!Director::is_cli()) { if(!Director::is_cli()) {
self::$default_reporter->writeHeader(); self::$default_reporter->writeHeader();
echo '<div class="info">'; echo '<div class="info">';
@ -100,7 +113,16 @@ class TestRunner extends Controller {
} }
/*, array("reportDirectory" => "/Users/sminnee/phpunit-report")*/ /*, array("reportDirectory" => "/Users/sminnee/phpunit-report")*/
$testResult = PHPUnit_TextUI_TestRunner::run($suite); if($coverage) {
$testResult = PHPUnit_TextUI_TestRunner::run($suite, array("reportDirectory" => "../assets/coverage-report"));
} else {
$testResult = PHPUnit_TextUI_TestRunner::run($suite);
}
if($coverage) {
$coverageURL = Director::absoluteURL('assets/coverage-report');
echo "<p><a href=\"$coverageURL\">Coverage report available here</a></p>";
}
if(!Director::is_cli()) echo '</div>'; if(!Director::is_cli()) echo '</div>';

View File

@ -80,7 +80,8 @@ class TestSession {
* Get the most recent response's content * Get the most recent response's content
*/ */
function lastContent() { function lastContent() {
return $this->lastResponse->getBody(); if(is_string($this->lastResponse)) return $this->lastResponse;
else return $this->lastResponse->getBody();
} }
function cssParser() { function cssParser() {