mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGE: Added Functional Test, a sub-class of SapphireTest, designed for testing end-user functionality such as page display and form submission
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@54633 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
4cb354b79a
commit
ada3a51b4d
57
testing/CSSContentParser.php
Normal file
57
testing/CSSContentParser.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CSSContentParser enables parsing & assertion running of HTML content via CSS selectors.
|
||||
*
|
||||
* It works by converting the content to XHTML using tidy, rewriting the CSS selectors as XPath queries, and executing
|
||||
* those using SimpeXML.
|
||||
*
|
||||
* It was built to facilitate testing using PHPUnit and contains a number of assert methods that will throw PHPUnit
|
||||
* assertion exception when applicable.
|
||||
*/
|
||||
class CSSContentParser extends Object {
|
||||
protected $simpleXML = null;
|
||||
|
||||
function __construct($content) {
|
||||
$CLI_content = escapeshellarg($content);
|
||||
$tidy = `echo $CLI_content | tidy -n -q -utf8 -asxhtml`;
|
||||
$tidy = str_replace('xmlns="http://www.w3.org/1999/xhtml"','',$tidy);
|
||||
$tidy = str_replace(' ','',$tidy);
|
||||
$this->simpleXML = new SimpleXMLElement($tidy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number of SimpleXML elements that match the given CSS selector.
|
||||
* Currently the selector engine only supports querying by tag, id, and classs
|
||||
*/
|
||||
function getBySelector($selector) {
|
||||
$xpath = $this->selector2xpath($selector);
|
||||
return $this->simpleXML->xpath($xpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a CSS selector into an equivalent xpath expression.
|
||||
* Currently the selector engine only supports querying by tag, id, and classs
|
||||
*/
|
||||
function selector2xpath($selector) {
|
||||
$parts = preg_split('/\\s+/', $selector);
|
||||
$xpath = "";
|
||||
foreach($parts as $part) {
|
||||
if(preg_match('/^([A-Za-z][A-Za-z0-9]*)/', $part, $matches)) {
|
||||
$xpath .= "//$matches[1]";
|
||||
} else {
|
||||
$xpath .= "//*";
|
||||
}
|
||||
$xfilters = array();
|
||||
if(preg_match('/#([^#.\[]+)/', $part, $matches)) {
|
||||
$xfilters[] = "@id='$matches[1]'";
|
||||
}
|
||||
if(preg_match('/\.([^#.\[]+)/', $part, $matches)) {
|
||||
$xfilters[] = "contains(@class,'$matches[1]')";
|
||||
}
|
||||
if($xfilters) $xpath .= '[' . implode(',', $xfilters) . ']';
|
||||
}
|
||||
return $xpath;
|
||||
}
|
||||
|
||||
}
|
175
testing/FunctionalTest.php
Normal file
175
testing/FunctionalTest.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?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>
|
||||
*/
|
||||
class FunctionalTest extends SapphireTest {
|
||||
protected $mainSession = null;
|
||||
|
||||
/**
|
||||
* CSSContentParser for the most recently requested page.
|
||||
*/
|
||||
protected $cssParser = null;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
$this->mainSession = new TestSession();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
parent::tearDown();
|
||||
$this->mainSession = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a get request
|
||||
*/
|
||||
function get($url) {
|
||||
$this->cssParser = null;
|
||||
return $this->mainSession->get($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a post request
|
||||
*/
|
||||
function post($url, $data) {
|
||||
$this->cssParser = null;
|
||||
return $this->mainSession->post($url, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return $this->mainSession->submitForm($formID, $button, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
foreach($items as $item) $actuals[$item . ''] = 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 assertExactMatchBySelector($selector, $expectedMatches) {
|
||||
$items = $this->cssParser()->getBySelector($selector);
|
||||
foreach($items as $item) $actuals[] = $item . '';
|
||||
|
||||
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) . "'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,6 @@ class TestSession {
|
||||
|
||||
$postVars = array();
|
||||
parse_str($submission->_encode(), $postVars);
|
||||
Debug::show($postVars);
|
||||
return $this->post($url, $postVars);
|
||||
}
|
||||
|
||||
@ -84,6 +83,11 @@ class TestSession {
|
||||
return $this->lastResponse->getBody();
|
||||
}
|
||||
|
||||
function cssParser() {
|
||||
return new CSSContentParser($this->lastContent());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the last response as a SimplePage object
|
||||
*/
|
||||
|
42
tests/testing/CSSContentParserTest.php
Normal file
42
tests/testing/CSSContentParserTest.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
class CSSContentParserTest extends SapphireTest {
|
||||
function testSelector2xpath() {
|
||||
$parser = new CSSContentParser("<html><head><title>test</title></head><body><p>test</p></body></html>");
|
||||
|
||||
$this->assertEquals("//div[@id='UserProfile']//label", $parser->selector2xpath("div#UserProfile label"));
|
||||
$this->assertEquals("//div", $parser->selector2xpath("div"));
|
||||
$this->assertEquals("//div[contains(@class,'test')]", $parser->selector2xpath("div.test"));
|
||||
$this->assertEquals("//*[@id='UserProfile']//div[contains(@class,'test')]//*[contains(@class,'other')]//div[@id='Item']",
|
||||
$parser->selector2xpath("#UserProfile div.test .other div#Item"));
|
||||
}
|
||||
|
||||
function testGetBySelector() {
|
||||
$parser = new CSSContentParser(<<<HTML
|
||||
<html>
|
||||
<head>
|
||||
<title>test</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="A" class="one two three">
|
||||
<p class="other">result</p>
|
||||
</div>
|
||||
<p>test</p>
|
||||
</body>
|
||||
</html>"
|
||||
HTML
|
||||
);
|
||||
|
||||
$result = $parser->getBySelector('div.one');
|
||||
$this->assertEquals("A", $result[0]['id'].'');
|
||||
$result = $parser->getBySelector('div.two');
|
||||
$this->assertEquals("A", $result[0]['id'].'');
|
||||
$result = $parser->getBySelector('div.three');
|
||||
$this->assertEquals("A", $result[0]['id'].'');
|
||||
|
||||
$result = $parser->getBySelector('div#A p.other');
|
||||
$this->assertEquals("result", $result[0] . '');
|
||||
$result = $parser->getBySelector('#A .other');
|
||||
$this->assertEquals("result", $result[0] . '');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user