2008-05-15 09:15:33 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sapphire-specific testing object designed to support functional testing of your web app. It simulates get/post
|
|
|
|
* requests, form submission, and can validate resulting HTML, looking up content by CSS selector.
|
|
|
|
*
|
|
|
|
* The example below shows how it works.
|
|
|
|
*
|
|
|
|
* <code>
|
|
|
|
* function testMyForm() {
|
|
|
|
* // Visit a URL
|
|
|
|
* $this->get("your/url");
|
|
|
|
*
|
|
|
|
* // Submit a form on the page that you get in response
|
|
|
|
* $this->submitForm("MyForm_ID", array("Email" => "invalid email ^&*&^"));
|
|
|
|
*
|
|
|
|
* // Validate the content that is returned
|
|
|
|
* $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));
|
|
|
|
* }
|
|
|
|
* </code>
|
2008-06-15 15:33:53 +02:00
|
|
|
*
|
|
|
|
* @package sapphire
|
|
|
|
* @subpackage testing
|
2008-05-15 09:15:33 +02:00
|
|
|
*/
|
|
|
|
class FunctionalTest extends SapphireTest {
|
2008-05-26 08:21:30 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2008-05-15 09:15:33 +02:00
|
|
|
protected $mainSession = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CSSContentParser for the most recently requested page.
|
|
|
|
*/
|
|
|
|
protected $cssParser = null;
|
2008-05-26 08:21:30 +02:00
|
|
|
|
|
|
|
private $originalTheme = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the {@link Session} object for this test
|
|
|
|
*/
|
|
|
|
function session() {
|
|
|
|
return $this->mainSession->session();
|
|
|
|
}
|
2008-05-15 09:15:33 +02:00
|
|
|
|
|
|
|
function setUp() {
|
|
|
|
parent::setUp();
|
|
|
|
$this->mainSession = new TestSession();
|
2008-05-26 08:21:30 +02:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
2008-05-15 09:15:33 +02:00
|
|
|
}
|
2008-05-26 08:21:30 +02:00
|
|
|
|
2008-05-15 09:15:33 +02:00
|
|
|
function tearDown() {
|
|
|
|
parent::tearDown();
|
|
|
|
$this->mainSession = null;
|
2008-05-26 08:21:30 +02:00
|
|
|
|
|
|
|
// Re-enable theme, if previously disabled
|
|
|
|
if($this->stat('disable_themes')) {
|
|
|
|
SSViewer::set_theme($this->originalTheme);
|
|
|
|
}
|
2008-05-15 09:15:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submit a get request
|
|
|
|
*/
|
|
|
|
function get($url) {
|
|
|
|
$this->cssParser = null;
|
2008-05-26 08:21:30 +02:00
|
|
|
$response = $this->mainSession->get($url);
|
|
|
|
if(is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
|
|
|
|
return $response;
|
2008-05-15 09:15:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submit a post request
|
|
|
|
*/
|
|
|
|
function post($url, $data) {
|
|
|
|
$this->cssParser = null;
|
2008-05-26 08:21:30 +02:00
|
|
|
$response = $this->mainSession->post($url, $data);
|
|
|
|
if(is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
|
|
|
|
return $response;
|
2008-05-15 09:15:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submit the form with the given HTML ID, filling it out with the given data.
|
|
|
|
* Acts on the most recent response
|
|
|
|
*/
|
|
|
|
function submitForm($formID, $button = null, $data = array()) {
|
|
|
|
$this->cssParser = null;
|
2008-05-26 08:21:30 +02:00
|
|
|
$response = $this->mainSession->submitForm($formID, $button, $data);
|
|
|
|
if(is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
|
|
|
|
return $response;
|
2008-05-15 09:15:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the most recent content
|
|
|
|
*/
|
|
|
|
function content() {
|
|
|
|
return $this->mainSession->lastContent();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a CSSContentParser for the most recent content.
|
|
|
|
*/
|
|
|
|
function cssParser() {
|
|
|
|
if(!$this->cssParser) $this->cssParser = new CSSContentParser($this->mainSession->lastContent());
|
|
|
|
return $this->cssParser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
|
|
|
|
*
|
|
|
|
* The given CSS selector will be applied to the HTML of the most recent page. The content of every matching tag
|
|
|
|
* will be examined.
|
|
|
|
*
|
|
|
|
* The assertion fails if one of the expectedMatches fails to appear.
|
|
|
|
*
|
|
|
|
* Note: characters are stripped from the content; make sure that your assertions take this into account.
|
|
|
|
*/
|
|
|
|
function assertPartialMatchBySelector($selector, $expectedMatches) {
|
|
|
|
$items = $this->cssParser()->getBySelector($selector);
|
2008-05-26 08:24:06 +02:00
|
|
|
foreach($items as $item) $actuals[trim(preg_replace("/[ \n\r\t]+/", " ", $item. ''))] = true;
|
2008-05-15 09:15:33 +02:00
|
|
|
|
|
|
|
foreach($expectedMatches as $match) {
|
|
|
|
if(!isset($actuals[$match])) {
|
|
|
|
throw new PHPUnit_Framework_AssertionFailedError(
|
|
|
|
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "\n\n"
|
|
|
|
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
|
|
|
|
*
|
|
|
|
* The given CSS selector will be applied to the HTML of the most recent page. The full HTML of every matching tag
|
|
|
|
* will be examined.
|
|
|
|
*
|
|
|
|
* The assertion fails if one of the expectedMatches fails to appear.
|
|
|
|
*
|
|
|
|
* Note: characters are stripped from the content; make sure that your assertions take this into account.
|
|
|
|
*/
|
|
|
|
function assertExactMatchBySelector($selector, $expectedMatches) {
|
|
|
|
$items = $this->cssParser()->getBySelector($selector);
|
2008-05-26 08:21:30 +02:00
|
|
|
foreach($items as $item) $actuals[] = trim(preg_replace("/[ \n\r\t]+/", " ", $item. ''));
|
2008-05-15 09:15:33 +02:00
|
|
|
|
|
|
|
if($expectedMatches != $actuals) {
|
|
|
|
throw new PHPUnit_Framework_AssertionFailedError(
|
|
|
|
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "\n\n"
|
|
|
|
. "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
|
|
|
|
*
|
|
|
|
* The given CSS selector will be applied to the HTML of the most recent page. The content of every matching tag
|
|
|
|
* will be examined.
|
|
|
|
*
|
|
|
|
* The assertion fails if one of the expectedMatches fails to appear.
|
|
|
|
*
|
|
|
|
* Note: characters are stripped from the content; make sure that your assertions take this into account.
|
|
|
|
*/
|
|
|
|
function assertPartialHTMLMatchBySelector($selector, $expectedMatches) {
|
|
|
|
$items = $this->cssParser()->getBySelector($selector);
|
|
|
|
foreach($items as $item) $actuals[$item->asXML()] = true;
|
|
|
|
|
|
|
|
foreach($expectedMatches as $match) {
|
|
|
|
if(!isset($actuals[$match])) {
|
|
|
|
throw new PHPUnit_Framework_AssertionFailedError(
|
|
|
|
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "\n\n"
|
|
|
|
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
|
|
|
|
*
|
|
|
|
* The given CSS selector will be applied to the HTML of the most recent page. The full HTML of every matching tag
|
|
|
|
* will be examined.
|
|
|
|
*
|
|
|
|
* The assertion fails if one of the expectedMatches fails to appear.
|
|
|
|
*
|
|
|
|
* Note: characters are stripped from the content; make sure that your assertions take this into account.
|
|
|
|
*/
|
|
|
|
function assertExactHTMLMatchBySelector($selector, $expectedMatches) {
|
|
|
|
$items = $this->cssParser()->getBySelector($selector);
|
|
|
|
foreach($items as $item) $actuals[] = $item->asXML();
|
|
|
|
|
|
|
|
if($expectedMatches != $actuals) {
|
|
|
|
throw new PHPUnit_Framework_AssertionFailedError(
|
|
|
|
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "\n\n"
|
|
|
|
. "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2008-05-26 08:21:30 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;");
|
|
|
|
}
|
2008-05-15 09:15:33 +02:00
|
|
|
}
|