diff --git a/core/ManifestBuilder.php b/core/ManifestBuilder.php index 44dc0a036..5ae0e2d34 100644 --- a/core/ManifestBuilder.php +++ b/core/ManifestBuilder.php @@ -581,6 +581,11 @@ class ManifestBuilder { die("Cannot write manifest file! Check permissions of " . MANIFEST_FILE); } } + + static function includeEverything() { + global $_CLASS_MANIFEST; + foreach($_CLASS_MANIFEST as $classFile) require_once($classFile); + } } diff --git a/core/control/ContentController.php b/core/control/ContentController.php index d2a4ee693..073a53c14 100644 --- a/core/control/ContentController.php +++ b/core/control/ContentController.php @@ -107,7 +107,7 @@ class ContentController extends Controller { } // 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')) { $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. Click here to go back to the published site."); diff --git a/core/control/Director.php b/core/control/Director.php index e1bcf4935..c2cfb9246 100644 --- a/core/control/Director.php +++ b/core/control/Director.php @@ -413,12 +413,15 @@ class Director { static function makeRelative($url) { $base1 = self::absoluteBaseURL(); $base2 = self::baseFolder(); + $base3 = self::baseURL(); // Allow for the accidental inclusion of a // in the URL $url = ereg_replace('([^:])//','\\1/',$url); 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; } diff --git a/core/model/PDODatabase.php b/core/model/PDODatabase.php index 4cfe24e30..0ff1c4b96 100644 --- a/core/model/PDODatabase.php +++ b/core/model/PDODatabase.php @@ -233,7 +233,7 @@ class PDODatabase extends Database { * @return boolean Returns true if successful * @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 { $dbh = new PDO($connect, $username, $password); $stmt = $dbh->prepare("CREATE DATABASE $database"); diff --git a/testing/FunctionalTest.php b/testing/FunctionalTest.php index c761eb638..17a6e3feb 100644 --- a/testing/FunctionalTest.php +++ b/testing/FunctionalTest.php @@ -20,21 +20,58 @@ * */ 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; /** * CSSContentParser for the most recently requested page. */ protected $cssParser = null; + + private $originalTheme = null; + + /** + * Returns the {@link Session} object for this test + */ + function session() { + return $this->mainSession->session(); + } function setUp() { parent::setUp(); $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() { parent::tearDown(); $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) { $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) { $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()) { $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) { $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) { if(!isset($actuals[$match])) { @@ -115,7 +158,7 @@ class FunctionalTest extends SapphireTest { */ function assertExactMatchBySelector($selector, $expectedMatches) { $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) { 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;"); + } } \ No newline at end of file diff --git a/testing/TestRunner.php b/testing/TestRunner.php index 7a7036178..f767ade1f 100644 --- a/testing/TestRunner.php +++ b/testing/TestRunner.php @@ -60,6 +60,19 @@ class TestRunner extends Controller { 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 @@ -75,7 +88,7 @@ class TestRunner extends Controller { } - function runTests($classList) { + function runTests($classList, $coverage = false) { if(!Director::is_cli()) { self::$default_reporter->writeHeader(); echo '