From ada3a51b4d0bfc2df16df739990cbd51d631f5e5 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Thu, 15 May 2008 07:15:33 +0000 Subject: [PATCH] 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 --- testing/CSSContentParser.php | 57 ++++++++ testing/FunctionalTest.php | 175 +++++++++++++++++++++++++ testing/TestSession.php | 6 +- tests/testing/CSSContentParserTest.php | 42 ++++++ 4 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 testing/CSSContentParser.php create mode 100644 testing/FunctionalTest.php create mode 100644 tests/testing/CSSContentParserTest.php diff --git a/testing/CSSContentParser.php b/testing/CSSContentParser.php new file mode 100644 index 000000000..4855e4304 --- /dev/null +++ b/testing/CSSContentParser.php @@ -0,0 +1,57 @@ +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; + } + +} \ No newline at end of file diff --git a/testing/FunctionalTest.php b/testing/FunctionalTest.php new file mode 100644 index 000000000..c761eb638 --- /dev/null +++ b/testing/FunctionalTest.php @@ -0,0 +1,175 @@ + + * 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.")); + * } + * + */ +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) . "'" + ); + } + } +} \ No newline at end of file diff --git a/testing/TestSession.php b/testing/TestSession.php index 39990f725..e68c28fc1 100644 --- a/testing/TestSession.php +++ b/testing/TestSession.php @@ -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 */ diff --git a/tests/testing/CSSContentParserTest.php b/tests/testing/CSSContentParserTest.php new file mode 100644 index 000000000..cafd3d3da --- /dev/null +++ b/tests/testing/CSSContentParserTest.php @@ -0,0 +1,42 @@ +test

test

"); + + $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(<< + + test + + +
+

result

+
+

test

+ +" +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] . ''); + } +} \ No newline at end of file