Complex classes like SSViewerTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use SSViewerTest, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 10 | class SSViewerTest extends SapphireTest { |
||
| 11 | |||
| 12 | /** |
||
| 13 | * Backup of $_SERVER global |
||
| 14 | * |
||
| 15 | * @var array |
||
| 16 | */ |
||
| 17 | protected $oldServer = array(); |
||
| 18 | |||
| 19 | protected $extraDataObjects = array( |
||
| 20 | 'SSViewerTest_Object', |
||
| 21 | ); |
||
| 22 | |||
| 23 | public function setUp() { |
||
|
|
|||
| 24 | parent::setUp(); |
||
| 25 | Config::inst()->update('SSViewer', 'source_file_comments', false); |
||
| 26 | Config::inst()->update('SSViewer_FromString', 'cache_template', false); |
||
| 27 | AssetStoreTest_SpyStore::activate('SSViewerTest'); |
||
| 28 | $this->oldServer = $_SERVER; |
||
| 29 | } |
||
| 30 | |||
| 31 | public function tearDown() { |
||
| 32 | $_SERVER = $this->oldServer; |
||
| 33 | AssetStoreTest_SpyStore::reset(); |
||
| 34 | parent::tearDown(); |
||
| 35 | } |
||
| 36 | |||
| 37 | /** |
||
| 38 | * Tests for {@link Config::inst()->get('SSViewer', 'theme')} for different behaviour |
||
| 39 | * of user defined themes via {@link SiteConfig} and default theme |
||
| 40 | * when no user themes are defined. |
||
| 41 | */ |
||
| 42 | public function testCurrentTheme() { |
||
| 43 | //TODO: SiteConfig moved to CMS |
||
| 44 | Config::inst()->update('SSViewer', 'theme', 'mytheme'); |
||
| 45 | $this->assertEquals('mytheme', Config::inst()->get('SSViewer', 'theme'), |
||
| 46 | 'Current theme is the default - user has not defined one'); |
||
| 47 | } |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Test that a template without a <head> tag still renders. |
||
| 51 | */ |
||
| 52 | public function testTemplateWithoutHeadRenders() { |
||
| 53 | $data = new ArrayData(array( |
||
| 54 | 'Var' => 'var value' |
||
| 55 | )); |
||
| 56 | |||
| 57 | $result = $data->renderWith("SSViewerTestPartialTemplate"); |
||
| 58 | $this->assertEquals('Test partial template: var value', trim(preg_replace("/<!--.*-->/U",'',$result))); |
||
| 59 | } |
||
| 60 | |||
| 61 | public function testIncludeScopeInheritance() { |
||
| 62 | $data = $this->getScopeInheritanceTestData(); |
||
| 63 | $expected = array( |
||
| 64 | 'Item 1 - First-ODD top:Item 1', |
||
| 65 | 'Item 2 - EVEN top:Item 2', |
||
| 66 | 'Item 3 - ODD top:Item 3', |
||
| 67 | 'Item 4 - EVEN top:Item 4', |
||
| 68 | 'Item 5 - ODD top:Item 5', |
||
| 69 | 'Item 6 - Last-EVEN top:Item 6', |
||
| 70 | ); |
||
| 71 | |||
| 72 | $result = $data->renderWith('SSViewerTestIncludeScopeInheritance'); |
||
| 73 | $this->assertExpectedStrings($result, $expected); |
||
| 74 | |||
| 75 | // reset results for the tests that include arguments (the title is passed as an arg) |
||
| 76 | $expected = array( |
||
| 77 | 'Item 1 _ Item 1 - First-ODD top:Item 1', |
||
| 78 | 'Item 2 _ Item 2 - EVEN top:Item 2', |
||
| 79 | 'Item 3 _ Item 3 - ODD top:Item 3', |
||
| 80 | 'Item 4 _ Item 4 - EVEN top:Item 4', |
||
| 81 | 'Item 5 _ Item 5 - ODD top:Item 5', |
||
| 82 | 'Item 6 _ Item 6 - Last-EVEN top:Item 6', |
||
| 83 | ); |
||
| 84 | |||
| 85 | $result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs'); |
||
| 86 | $this->assertExpectedStrings($result, $expected); |
||
| 87 | } |
||
| 88 | |||
| 89 | public function testIncludeTruthyness() { |
||
| 90 | $data = new ArrayData(array( |
||
| 91 | 'Title' => 'TruthyTest', |
||
| 92 | 'Items' => new ArrayList(array( |
||
| 93 | new ArrayData(array('Title' => 'Item 1')), |
||
| 94 | new ArrayData(array('Title' => '')), |
||
| 95 | new ArrayData(array('Title' => true)), |
||
| 96 | new ArrayData(array('Title' => false)), |
||
| 97 | new ArrayData(array('Title' => null)), |
||
| 98 | new ArrayData(array('Title' => 0)), |
||
| 99 | new ArrayData(array('Title' => 7)) |
||
| 100 | )) |
||
| 101 | )); |
||
| 102 | $result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs'); |
||
| 103 | |||
| 104 | // We should not end up with empty values appearing as empty |
||
| 105 | $expected = array( |
||
| 106 | 'Item 1 _ Item 1 - First-ODD top:Item 1', |
||
| 107 | 'Untitled - EVEN top:', |
||
| 108 | '1 _ 1 - ODD top:1', |
||
| 109 | 'Untitled - EVEN top:', |
||
| 110 | 'Untitled - ODD top:', |
||
| 111 | 'Untitled - EVEN top:0', |
||
| 112 | '7 _ 7 - Last-ODD top:7' |
||
| 113 | ); |
||
| 114 | $this->assertExpectedStrings($result, $expected); |
||
| 115 | } |
||
| 116 | |||
| 117 | private function getScopeInheritanceTestData() { |
||
| 118 | return new ArrayData(array( |
||
| 119 | 'Title' => 'TopTitleValue', |
||
| 120 | 'Items' => new ArrayList(array( |
||
| 121 | new ArrayData(array('Title' => 'Item 1')), |
||
| 122 | new ArrayData(array('Title' => 'Item 2')), |
||
| 123 | new ArrayData(array('Title' => 'Item 3')), |
||
| 124 | new ArrayData(array('Title' => 'Item 4')), |
||
| 125 | new ArrayData(array('Title' => 'Item 5')), |
||
| 126 | new ArrayData(array('Title' => 'Item 6')) |
||
| 127 | )) |
||
| 128 | )); |
||
| 129 | } |
||
| 130 | |||
| 131 | private function assertExpectedStrings($result, $expected) { |
||
| 132 | foreach ($expected as $expectedStr) { |
||
| 133 | $this->assertTrue( |
||
| 134 | (boolean) preg_match("/{$expectedStr}/", $result), |
||
| 135 | "Didn't find '{$expectedStr}' in:\n{$result}" |
||
| 136 | ); |
||
| 137 | } |
||
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * Small helper to render templates from strings |
||
| 142 | */ |
||
| 143 | public function render($templateString, $data = null, $cacheTemplate = false) { |
||
| 144 | $t = SSViewer::fromString($templateString, $cacheTemplate); |
||
| 145 | if(!$data) $data = new SSViewerTestFixture(); |
||
| 146 | return trim(''.$t->process($data)); |
||
| 147 | } |
||
| 148 | |||
| 149 | public function testRequirements() { |
||
| 150 | $requirements = $this->getMock("Requirements_Backend", array("javascript", "css")); |
||
| 151 | $jsFile = FRAMEWORK_DIR . '/tests/forms/a.js'; |
||
| 152 | $cssFile = FRAMEWORK_DIR . '/tests/forms/a.js'; |
||
| 153 | |||
| 154 | $requirements->expects($this->once())->method('javascript')->with($jsFile); |
||
| 155 | $requirements->expects($this->once())->method('css')->with($cssFile); |
||
| 156 | |||
| 157 | $origReq = Requirements::backend(); |
||
| 158 | Requirements::set_backend($requirements); |
||
| 159 | $template = $this->render("<% require javascript($jsFile) %> |
||
| 160 | <% require css($cssFile) %>"); |
||
| 161 | Requirements::set_backend($origReq); |
||
| 162 | |||
| 163 | $this->assertFalse((bool)trim($template), "Should be no content in this return."); |
||
| 164 | } |
||
| 165 | |||
| 166 | public function testRequirementsCombine(){ |
||
| 167 | $testBackend = Injector::inst()->create('Requirements_Backend'); |
||
| 168 | $testBackend->setSuffixRequirements(false); |
||
| 169 | //$combinedTestFilePath = BASE_PATH . '/' . $testBackend->getCombinedFilesFolder() . '/testRequirementsCombine.js'; |
||
| 170 | |||
| 171 | $jsFile = FRAMEWORK_DIR . '/tests/view/themes/javascript/bad.js'; |
||
| 172 | $jsFileContents = file_get_contents(BASE_PATH . '/' . $jsFile); |
||
| 173 | $testBackend->combineFiles('testRequirementsCombine.js', array($jsFile)); |
||
| 174 | |||
| 175 | // first make sure that our test js file causes an exception to be thrown |
||
| 176 | try{ |
||
| 177 | require_once('thirdparty/jsmin/jsmin.php'); |
||
| 178 | JSMin::minify($jsFileContents); |
||
| 179 | $this->fail('JSMin did not throw exception on minify bad file: '); |
||
| 180 | } catch(Exception $e) { |
||
| 181 | // exception thrown... good |
||
| 182 | } |
||
| 183 | |||
| 184 | // secondly, make sure that requirements is generated, even though minification failed |
||
| 185 | $testBackend->processCombinedFiles(); |
||
| 186 | $js = array_keys($testBackend->getJavascript()); |
||
| 187 | $combinedTestFilePath = BASE_PATH . reset($js); |
||
| 188 | $this->assertContains('_combinedfiles/testRequirementsCombine-4c0e97a.js', $combinedTestFilePath); |
||
| 189 | |||
| 190 | // and make sure the combined content matches the input content, i.e. no loss of functionality |
||
| 191 | if(!file_exists($combinedTestFilePath)) { |
||
| 192 | $this->fail('No combined file was created at expected path: '.$combinedTestFilePath); |
||
| 193 | } |
||
| 194 | $combinedTestFileContents = file_get_contents($combinedTestFilePath); |
||
| 195 | $this->assertContains($jsFileContents, $combinedTestFileContents); |
||
| 196 | } |
||
| 197 | |||
| 198 | |||
| 199 | |||
| 200 | public function testComments() { |
||
| 201 | $output = $this->render(<<<SS |
||
| 202 | This is my template<%-- this is a comment --%>This is some content<%-- this is another comment --%>Final content |
||
| 203 | <%-- Alone multi |
||
| 204 | line comment --%> |
||
| 205 | Some more content |
||
| 206 | Mixing content and <%-- multi |
||
| 207 | line comment --%> Final final |
||
| 208 | content |
||
| 209 | SS |
||
| 210 | ); |
||
| 211 | $shouldbe = <<<SS |
||
| 212 | This is my templateThis is some contentFinal content |
||
| 213 | |||
| 214 | Some more content |
||
| 215 | Mixing content and Final final |
||
| 216 | content |
||
| 217 | SS; |
||
| 218 | |||
| 219 | $this->assertEquals($shouldbe, $output); |
||
| 220 | } |
||
| 221 | |||
| 222 | public function testBasicText() { |
||
| 223 | $this->assertEquals('"', $this->render('"'), 'Double-quotes are left alone'); |
||
| 224 | $this->assertEquals("'", $this->render("'"), 'Single-quotes are left alone'); |
||
| 225 | $this->assertEquals('A', $this->render('\\A'), 'Escaped characters are unescaped'); |
||
| 226 | $this->assertEquals('\\A', $this->render('\\\\A'), 'Escaped back-slashed are correctly unescaped'); |
||
| 227 | } |
||
| 228 | |||
| 229 | public function testBasicInjection() { |
||
| 230 | $this->assertEquals('[out:Test]', $this->render('$Test'), 'Basic stand-alone injection'); |
||
| 231 | $this->assertEquals('[out:Test]', $this->render('{$Test}'), 'Basic stand-alone wrapped injection'); |
||
| 232 | $this->assertEquals('A[out:Test]!', $this->render('A$Test!'), 'Basic surrounded injection'); |
||
| 233 | $this->assertEquals('A[out:Test]B', $this->render('A{$Test}B'), 'Basic surrounded wrapped injection'); |
||
| 234 | |||
| 235 | $this->assertEquals('A$B', $this->render('A\\$B'), 'No injection as $ escaped'); |
||
| 236 | $this->assertEquals('A$ B', $this->render('A$ B'), 'No injection as $ not followed by word character'); |
||
| 237 | $this->assertEquals('A{$ B', $this->render('A{$ B'), 'No injection as {$ not followed by word character'); |
||
| 238 | |||
| 239 | $this->assertEquals('{$Test}', $this->render('{\\$Test}'), 'Escapes can be used to avoid injection'); |
||
| 240 | $this->assertEquals('{\\[out:Test]}', $this->render('{\\\\$Test}'), |
||
| 241 | 'Escapes before injections are correctly unescaped'); |
||
| 242 | } |
||
| 243 | |||
| 244 | |||
| 245 | public function testGlobalVariableCalls() { |
||
| 246 | $this->assertEquals('automatic', $this->render('$SSViewerTest_GlobalAutomatic')); |
||
| 247 | $this->assertEquals('reference', $this->render('$SSViewerTest_GlobalReferencedByString')); |
||
| 248 | $this->assertEquals('reference', $this->render('$SSViewerTest_GlobalReferencedInArray')); |
||
| 249 | } |
||
| 250 | |||
| 251 | public function testGlobalVariableCallsWithArguments() { |
||
| 252 | $this->assertEquals('zz', $this->render('$SSViewerTest_GlobalThatTakesArguments')); |
||
| 253 | $this->assertEquals('zFooz', $this->render('$SSViewerTest_GlobalThatTakesArguments("Foo")')); |
||
| 254 | $this->assertEquals('zFoo:Bar:Bazz', |
||
| 255 | $this->render('$SSViewerTest_GlobalThatTakesArguments("Foo", "Bar", "Baz")')); |
||
| 256 | $this->assertEquals('zreferencez', |
||
| 257 | $this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalReferencedByString)')); |
||
| 258 | } |
||
| 259 | |||
| 260 | public function testGlobalVariablesAreEscaped() { |
||
| 261 | $this->assertEquals('<div></div>', $this->render('$SSViewerTest_GlobalHTMLFragment')); |
||
| 262 | $this->assertEquals('<div></div>', $this->render('$SSViewerTest_GlobalHTMLEscaped')); |
||
| 263 | |||
| 264 | $this->assertEquals('z<div></div>z', |
||
| 265 | $this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalHTMLFragment)')); |
||
| 266 | $this->assertEquals('z<div></div>z', |
||
| 267 | $this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalHTMLEscaped)')); |
||
| 268 | } |
||
| 269 | |||
| 270 | public function testCoreGlobalVariableCalls() { |
||
| 271 | $this->assertEquals(Director::absoluteBaseURL(), |
||
| 272 | $this->render('{$absoluteBaseURL}'), 'Director::absoluteBaseURL can be called from within template'); |
||
| 273 | $this->assertEquals(Director::absoluteBaseURL(), $this->render('{$AbsoluteBaseURL}'), |
||
| 274 | 'Upper-case %AbsoluteBaseURL can be called from within template'); |
||
| 275 | |||
| 276 | $this->assertEquals(Director::is_ajax(), $this->render('{$isAjax}'), |
||
| 277 | 'All variations of is_ajax result in the correct call'); |
||
| 278 | $this->assertEquals(Director::is_ajax(), $this->render('{$IsAjax}'), |
||
| 279 | 'All variations of is_ajax result in the correct call'); |
||
| 280 | $this->assertEquals(Director::is_ajax(), $this->render('{$is_ajax}'), |
||
| 281 | 'All variations of is_ajax result in the correct call'); |
||
| 282 | $this->assertEquals(Director::is_ajax(), $this->render('{$Is_ajax}'), |
||
| 283 | 'All variations of is_ajax result in the correct call'); |
||
| 284 | |||
| 285 | $this->assertEquals(i18n::get_locale(), $this->render('{$i18nLocale}'), |
||
| 286 | 'i18n template functions result correct result'); |
||
| 287 | $this->assertEquals(i18n::get_locale(), $this->render('{$get_locale}'), |
||
| 288 | 'i18n template functions result correct result'); |
||
| 289 | |||
| 290 | $this->assertEquals((string)Member::currentUser(), $this->render('{$CurrentMember}'), |
||
| 291 | 'Member template functions result correct result'); |
||
| 292 | $this->assertEquals((string)Member::currentUser(), $this->render('{$CurrentUser}'), |
||
| 293 | 'Member template functions result correct result'); |
||
| 294 | $this->assertEquals((string)Member::currentUser(), $this->render('{$currentMember}'), |
||
| 295 | 'Member template functions result correct result'); |
||
| 296 | $this->assertEquals((string)Member::currentUser(), $this->render('{$currentUser}'), |
||
| 297 | 'Member template functions result correct result'); |
||
| 298 | |||
| 299 | $this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$getSecurityID}'), |
||
| 300 | 'SecurityToken template functions result correct result'); |
||
| 301 | $this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$SecurityID}'), |
||
| 302 | 'SecurityToken template functions result correct result'); |
||
| 303 | |||
| 304 | $this->assertEquals(Permission::check("ADMIN"), (bool)$this->render('{$HasPerm(\'ADMIN\')}'), |
||
| 305 | 'Permissions template functions result correct result'); |
||
| 306 | $this->assertEquals(Permission::check("ADMIN"), (bool)$this->render('{$hasPerm(\'ADMIN\')}'), |
||
| 307 | 'Permissions template functions result correct result'); |
||
| 308 | } |
||
| 309 | |||
| 310 | public function testNonFieldCastingHelpersNotUsedInHasValue() { |
||
| 311 | // check if Link without $ in front of variable |
||
| 312 | $result = $this->render( |
||
| 313 | 'A<% if Link %>$Link<% end_if %>B', new SSViewerTest_Object()); |
||
| 314 | $this->assertEquals('Asome/url.htmlB', $result, 'casting helper not used for <% if Link %>'); |
||
| 315 | |||
| 316 | // check if Link with $ in front of variable |
||
| 317 | $result = $this->render( |
||
| 318 | 'A<% if $Link %>$Link<% end_if %>B', new SSViewerTest_Object()); |
||
| 319 | $this->assertEquals('Asome/url.htmlB', $result, 'casting helper not used for <% if $Link %>'); |
||
| 320 | } |
||
| 321 | |||
| 322 | public function testLocalFunctionsTakePriorityOverGlobals() { |
||
| 323 | $data = new ArrayData(array( |
||
| 324 | 'Page' => new SSViewerTest_Object() |
||
| 325 | )); |
||
| 326 | |||
| 327 | //call method with lots of arguments |
||
| 328 | $result = $this->render( |
||
| 329 | '<% with Page %>$lotsOfArguments11("a","b","c","d","e","f","g","h","i","j","k")<% end_with %>',$data); |
||
| 330 | $this->assertEquals("abcdefghijk",$result, "public function can accept up to 11 arguments"); |
||
| 331 | |||
| 332 | //call method that does not exist |
||
| 333 | $result = $this->render('<% with Page %><% if IDoNotExist %>hello<% end_if %><% end_with %>',$data); |
||
| 334 | $this->assertEquals("",$result, "Method does not exist - empty result"); |
||
| 335 | |||
| 336 | //call if that does not exist |
||
| 337 | $result = $this->render('<% with Page %>$IDoNotExist("hello")<% end_with %>',$data); |
||
| 338 | $this->assertEquals("",$result, "Method does not exist - empty result"); |
||
| 339 | |||
| 340 | //call method with same name as a global method (local call should take priority) |
||
| 341 | $result = $this->render('<% with Page %>$absoluteBaseURL<% end_with %>',$data); |
||
| 342 | $this->assertEquals("testLocalFunctionPriorityCalled",$result, |
||
| 343 | "Local Object's public function called. Did not return the actual baseURL of the current site"); |
||
| 344 | } |
||
| 345 | |||
| 346 | public function testCurrentScopeLoopWith() { |
||
| 347 | // Data to run the loop tests on - one sequence of three items, each with a subitem |
||
| 348 | $data = new ArrayData(array( |
||
| 349 | 'Foo' => new ArrayList(array( |
||
| 350 | 'Subocean' => new ArrayData(array( |
||
| 351 | 'Name' => 'Higher' |
||
| 352 | )), |
||
| 353 | new ArrayData(array( |
||
| 354 | 'Sub' => new ArrayData(array( |
||
| 355 | 'Name' => 'SubKid1' |
||
| 356 | )) |
||
| 357 | )), |
||
| 358 | new ArrayData(array( |
||
| 359 | 'Sub' => new ArrayData(array( |
||
| 360 | 'Name' => 'SubKid2' |
||
| 361 | )) |
||
| 362 | )), |
||
| 363 | new SSViewerTest_Object('Number6') |
||
| 364 | )) |
||
| 365 | )); |
||
| 366 | |||
| 367 | $result = $this->render( |
||
| 368 | '<% loop Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %>',$data); |
||
| 369 | $this->assertEquals("SubKid1SubKid2Number6",$result, "Loop works"); |
||
| 370 | |||
| 371 | $result = $this->render( |
||
| 372 | '<% loop Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %>',$data); |
||
| 373 | $this->assertEquals("SubKid1SubKid2Number6",$result, "Loop works"); |
||
| 374 | |||
| 375 | $result = $this->render('<% with Foo %>$Count<% end_with %>',$data); |
||
| 376 | $this->assertEquals("4",$result, "4 items in the DataObjectSet"); |
||
| 377 | |||
| 378 | $result = $this->render('<% with Foo %><% loop Up.Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %>' |
||
| 379 | . '<% end_if %><% end_loop %><% end_with %>',$data); |
||
| 380 | $this->assertEquals("SubKid1SubKid2Number6",$result, "Loop in with Up.Foo scope works"); |
||
| 381 | |||
| 382 | $result = $this->render('<% with Foo %><% loop %>$Number<% if Sub %><% with Sub %>$Name<% end_with %>' |
||
| 383 | . '<% end_if %><% end_loop %><% end_with %>',$data); |
||
| 384 | $this->assertEquals("SubKid1SubKid2Number6",$result, "Loop in current scope works"); |
||
| 385 | } |
||
| 386 | |||
| 387 | public function testObjectDotArguments() { |
||
| 388 | $this->assertEquals( |
||
| 389 | '[out:TestObject.methodWithOneArgument(one)] |
||
| 390 | [out:TestObject.methodWithTwoArguments(one,two)] |
||
| 391 | [out:TestMethod(Arg1,Arg2).Bar.Val] |
||
| 392 | [out:TestMethod(Arg1,Arg2).Bar] |
||
| 393 | [out:TestMethod(Arg1,Arg2)] |
||
| 394 | [out:TestMethod(Arg1).Bar.Val] |
||
| 395 | [out:TestMethod(Arg1).Bar] |
||
| 396 | [out:TestMethod(Arg1)]', |
||
| 397 | $this->render('$TestObject.methodWithOneArgument(one) |
||
| 398 | $TestObject.methodWithTwoArguments(one,two) |
||
| 399 | $TestMethod(Arg1, Arg2).Bar.Val |
||
| 400 | $TestMethod(Arg1, Arg2).Bar |
||
| 401 | $TestMethod(Arg1, Arg2) |
||
| 402 | $TestMethod(Arg1).Bar.Val |
||
| 403 | $TestMethod(Arg1).Bar |
||
| 404 | $TestMethod(Arg1)') |
||
| 405 | ); |
||
| 406 | } |
||
| 407 | |||
| 408 | public function testEscapedArguments() { |
||
| 409 | $this->assertEquals( |
||
| 410 | '[out:Foo(Arg1,Arg2).Bar.Val].Suffix |
||
| 411 | [out:Foo(Arg1,Arg2).Val]_Suffix |
||
| 412 | [out:Foo(Arg1,Arg2)]/Suffix |
||
| 413 | [out:Foo(Arg1).Bar.Val]textSuffix |
||
| 414 | [out:Foo(Arg1).Bar].Suffix |
||
| 415 | [out:Foo(Arg1)].Suffix |
||
| 416 | [out:Foo.Bar.Val].Suffix |
||
| 417 | [out:Foo.Bar].Suffix |
||
| 418 | [out:Foo].Suffix', |
||
| 419 | $this->render('{$Foo(Arg1, Arg2).Bar.Val}.Suffix |
||
| 420 | {$Foo(Arg1, Arg2).Val}_Suffix |
||
| 421 | {$Foo(Arg1, Arg2)}/Suffix |
||
| 422 | {$Foo(Arg1).Bar.Val}textSuffix |
||
| 423 | {$Foo(Arg1).Bar}.Suffix |
||
| 424 | {$Foo(Arg1)}.Suffix |
||
| 425 | {$Foo.Bar.Val}.Suffix |
||
| 426 | {$Foo.Bar}.Suffix |
||
| 427 | {$Foo}.Suffix') |
||
| 428 | ); |
||
| 429 | } |
||
| 430 | |||
| 431 | public function testLoopWhitespace() { |
||
| 432 | $this->assertEquals( |
||
| 433 | 'before[out:SingleItem.Test]after |
||
| 434 | beforeTestafter', |
||
| 435 | $this->render('before<% loop SingleItem %>$Test<% end_loop %>after |
||
| 436 | before<% loop SingleItem %>Test<% end_loop %>after') |
||
| 437 | ); |
||
| 438 | |||
| 439 | // The control tags are removed from the output, but no whitespace |
||
| 440 | // This is a quirk that could be changed, but included in the test to make the current |
||
| 441 | // behaviour explicit |
||
| 442 | $this->assertEquals( |
||
| 443 | 'before |
||
| 444 | |||
| 445 | [out:SingleItem.ItemOnItsOwnLine] |
||
| 446 | |||
| 447 | after', |
||
| 448 | $this->render('before |
||
| 449 | <% loop SingleItem %> |
||
| 450 | $ItemOnItsOwnLine |
||
| 451 | <% end_loop %> |
||
| 452 | after') |
||
| 453 | ); |
||
| 454 | |||
| 455 | // The whitespace within the control tags is preserve in a loop |
||
| 456 | // This is a quirk that could be changed, but included in the test to make the current |
||
| 457 | // behaviour explicit |
||
| 458 | $this->assertEquals( |
||
| 459 | 'before |
||
| 460 | |||
| 461 | [out:Loop3.ItemOnItsOwnLine] |
||
| 462 | |||
| 463 | [out:Loop3.ItemOnItsOwnLine] |
||
| 464 | |||
| 465 | [out:Loop3.ItemOnItsOwnLine] |
||
| 466 | |||
| 467 | after', |
||
| 468 | $this->render('before |
||
| 469 | <% loop Loop3 %> |
||
| 470 | $ItemOnItsOwnLine |
||
| 471 | <% end_loop %> |
||
| 472 | after') |
||
| 473 | ); |
||
| 474 | } |
||
| 475 | |||
| 476 | public function testControls() { |
||
| 477 | // Single item controls |
||
| 478 | $this->assertEquals( |
||
| 479 | 'a[out:Foo.Bar.Item]b |
||
| 480 | [out:Foo.Bar(Arg1).Item] |
||
| 481 | [out:Foo(Arg1).Item] |
||
| 482 | [out:Foo(Arg1,Arg2).Item] |
||
| 483 | [out:Foo(Arg1,Arg2,Arg3).Item]', |
||
| 484 | $this->render('<% with Foo.Bar %>a{$Item}b<% end_with %> |
||
| 485 | <% with Foo.Bar(Arg1) %>$Item<% end_with %> |
||
| 486 | <% with Foo(Arg1) %>$Item<% end_with %> |
||
| 487 | <% with Foo(Arg1, Arg2) %>$Item<% end_with %> |
||
| 488 | <% with Foo(Arg1, Arg2, Arg3) %>$Item<% end_with %>') |
||
| 489 | ); |
||
| 490 | |||
| 491 | // Loop controls |
||
| 492 | $this->assertEquals('a[out:Foo.Loop2.Item]ba[out:Foo.Loop2.Item]b', |
||
| 493 | $this->render('<% loop Foo.Loop2 %>a{$Item}b<% end_loop %>')); |
||
| 494 | |||
| 495 | $this->assertEquals('[out:Foo.Loop2(Arg1).Item][out:Foo.Loop2(Arg1).Item]', |
||
| 496 | $this->render('<% loop Foo.Loop2(Arg1) %>$Item<% end_loop %>')); |
||
| 497 | |||
| 498 | $this->assertEquals('[out:Loop2(Arg1).Item][out:Loop2(Arg1).Item]', |
||
| 499 | $this->render('<% loop Loop2(Arg1) %>$Item<% end_loop %>')); |
||
| 500 | |||
| 501 | $this->assertEquals('[out:Loop2(Arg1,Arg2).Item][out:Loop2(Arg1,Arg2).Item]', |
||
| 502 | $this->render('<% loop Loop2(Arg1, Arg2) %>$Item<% end_loop %>')); |
||
| 503 | |||
| 504 | $this->assertEquals('[out:Loop2(Arg1,Arg2,Arg3).Item][out:Loop2(Arg1,Arg2,Arg3).Item]', |
||
| 505 | $this->render('<% loop Loop2(Arg1, Arg2, Arg3) %>$Item<% end_loop %>')); |
||
| 506 | |||
| 507 | } |
||
| 508 | |||
| 509 | public function testIfBlocks() { |
||
| 510 | // Basic test |
||
| 511 | $this->assertEquals('AC', |
||
| 512 | $this->render('A<% if NotSet %>B$NotSet<% end_if %>C')); |
||
| 513 | |||
| 514 | // Nested test |
||
| 515 | $this->assertEquals('AB1C', |
||
| 516 | $this->render('A<% if IsSet %>B$NotSet<% if IsSet %>1<% else %>2<% end_if %><% end_if %>C')); |
||
| 517 | |||
| 518 | // else_if |
||
| 519 | $this->assertEquals('ACD', |
||
| 520 | $this->render('A<% if NotSet %>B<% else_if IsSet %>C<% end_if %>D')); |
||
| 521 | $this->assertEquals('AD', |
||
| 522 | $this->render('A<% if NotSet %>B<% else_if AlsoNotset %>C<% end_if %>D')); |
||
| 523 | $this->assertEquals('ADE', |
||
| 524 | $this->render('A<% if NotSet %>B<% else_if AlsoNotset %>C<% else_if IsSet %>D<% end_if %>E')); |
||
| 525 | |||
| 526 | $this->assertEquals('ADE', |
||
| 527 | $this->render('A<% if NotSet %>B<% else_if AlsoNotset %>C<% else_if IsSet %>D<% end_if %>E')); |
||
| 528 | |||
| 529 | // Dot syntax |
||
| 530 | $this->assertEquals('ACD', |
||
| 531 | $this->render('A<% if Foo.NotSet %>B<% else_if Foo.IsSet %>C<% end_if %>D')); |
||
| 532 | $this->assertEquals('ACD', |
||
| 533 | $this->render('A<% if Foo.Bar.NotSet %>B<% else_if Foo.Bar.IsSet %>C<% end_if %>D')); |
||
| 534 | |||
| 535 | // Params |
||
| 536 | $this->assertEquals('ACD', |
||
| 537 | $this->render('A<% if NotSet(Param) %>B<% else %>C<% end_if %>D')); |
||
| 538 | $this->assertEquals('ABD', |
||
| 539 | $this->render('A<% if IsSet(Param) %>B<% else %>C<% end_if %>D')); |
||
| 540 | |||
| 541 | // Negation |
||
| 542 | $this->assertEquals('AC', |
||
| 543 | $this->render('A<% if not IsSet %>B<% end_if %>C')); |
||
| 544 | $this->assertEquals('ABC', |
||
| 545 | $this->render('A<% if not NotSet %>B<% end_if %>C')); |
||
| 546 | |||
| 547 | // Or |
||
| 548 | $this->assertEquals('ABD', |
||
| 549 | $this->render('A<% if IsSet || NotSet %>B<% else_if A %>C<% end_if %>D')); |
||
| 550 | $this->assertEquals('ACD', |
||
| 551 | $this->render('A<% if NotSet || AlsoNotSet %>B<% else_if IsSet %>C<% end_if %>D')); |
||
| 552 | $this->assertEquals('AD', |
||
| 553 | $this->render('A<% if NotSet || AlsoNotSet %>B<% else_if NotSet3 %>C<% end_if %>D')); |
||
| 554 | $this->assertEquals('ACD', |
||
| 555 | $this->render('A<% if NotSet || AlsoNotSet %>B<% else_if IsSet || NotSet %>C<% end_if %>D')); |
||
| 556 | $this->assertEquals('AD', |
||
| 557 | $this->render('A<% if NotSet || AlsoNotSet %>B<% else_if NotSet2 || NotSet3 %>C<% end_if %>D')); |
||
| 558 | |||
| 559 | // Negated Or |
||
| 560 | $this->assertEquals('ACD', |
||
| 561 | $this->render('A<% if not IsSet || AlsoNotSet %>B<% else_if A %>C<% end_if %>D')); |
||
| 562 | $this->assertEquals('ABD', |
||
| 563 | $this->render('A<% if not NotSet || AlsoNotSet %>B<% else_if A %>C<% end_if %>D')); |
||
| 564 | $this->assertEquals('ABD', |
||
| 565 | $this->render('A<% if NotSet || not AlsoNotSet %>B<% else_if A %>C<% end_if %>D')); |
||
| 566 | |||
| 567 | // And |
||
| 568 | $this->assertEquals('ABD', |
||
| 569 | $this->render('A<% if IsSet && AlsoSet %>B<% else_if A %>C<% end_if %>D')); |
||
| 570 | $this->assertEquals('ACD', |
||
| 571 | $this->render('A<% if IsSet && NotSet %>B<% else_if IsSet %>C<% end_if %>D')); |
||
| 572 | $this->assertEquals('AD', |
||
| 573 | $this->render('A<% if NotSet && NotSet2 %>B<% else_if NotSet3 %>C<% end_if %>D')); |
||
| 574 | $this->assertEquals('ACD', |
||
| 575 | $this->render('A<% if IsSet && NotSet %>B<% else_if IsSet && AlsoSet %>C<% end_if %>D')); |
||
| 576 | $this->assertEquals('AD', |
||
| 577 | $this->render('A<% if NotSet && NotSet2 %>B<% else_if IsSet && NotSet3 %>C<% end_if %>D')); |
||
| 578 | |||
| 579 | // Equality |
||
| 580 | $this->assertEquals('ABC', |
||
| 581 | $this->render('A<% if RawVal == RawVal %>B<% end_if %>C')); |
||
| 582 | $this->assertEquals('ACD', |
||
| 583 | $this->render('A<% if Right == Wrong %>B<% else_if RawVal == RawVal %>C<% end_if %>D')); |
||
| 584 | $this->assertEquals('ABC', |
||
| 585 | $this->render('A<% if Right != Wrong %>B<% end_if %>C')); |
||
| 586 | $this->assertEquals('AD', |
||
| 587 | $this->render('A<% if Right == Wrong %>B<% else_if RawVal != RawVal %>C<% end_if %>D')); |
||
| 588 | |||
| 589 | // test inequalities with simple numbers |
||
| 590 | $this->assertEquals('ABD', $this->render('A<% if 5 > 3 %>B<% else %>C<% end_if %>D')); |
||
| 591 | $this->assertEquals('ABD', $this->render('A<% if 5 >= 3 %>B<% else %>C<% end_if %>D')); |
||
| 592 | $this->assertEquals('ACD', $this->render('A<% if 3 > 5 %>B<% else %>C<% end_if %>D')); |
||
| 593 | $this->assertEquals('ACD', $this->render('A<% if 3 >= 5 %>B<% else %>C<% end_if %>D')); |
||
| 594 | |||
| 595 | $this->assertEquals('ABD', $this->render('A<% if 3 < 5 %>B<% else %>C<% end_if %>D')); |
||
| 596 | $this->assertEquals('ABD', $this->render('A<% if 3 <= 5 %>B<% else %>C<% end_if %>D')); |
||
| 597 | $this->assertEquals('ACD', $this->render('A<% if 5 < 3 %>B<% else %>C<% end_if %>D')); |
||
| 598 | $this->assertEquals('ACD', $this->render('A<% if 5 <= 3 %>B<% else %>C<% end_if %>D')); |
||
| 599 | |||
| 600 | $this->assertEquals('ABD', $this->render('A<% if 4 <= 4 %>B<% else %>C<% end_if %>D')); |
||
| 601 | $this->assertEquals('ABD', $this->render('A<% if 4 >= 4 %>B<% else %>C<% end_if %>D')); |
||
| 602 | $this->assertEquals('ACD', $this->render('A<% if 4 > 4 %>B<% else %>C<% end_if %>D')); |
||
| 603 | $this->assertEquals('ACD', $this->render('A<% if 4 < 4 %>B<% else %>C<% end_if %>D')); |
||
| 604 | |||
| 605 | // empty else_if and else tags, if this would not be supported, |
||
| 606 | // the output would stop after A, thereby failing the assert |
||
| 607 | $this->assertEquals('AD', $this->render('A<% if IsSet %><% else %><% end_if %>D')); |
||
| 608 | $this->assertEquals('AD', |
||
| 609 | $this->render('A<% if NotSet %><% else_if IsSet %><% else %><% end_if %>D')); |
||
| 610 | $this->assertEquals('AD', |
||
| 611 | $this->render('A<% if NotSet %><% else_if AlsoNotSet %><% else %><% end_if %>D')); |
||
| 612 | |||
| 613 | // Bare words with ending space |
||
| 614 | $this->assertEquals('ABC', |
||
| 615 | $this->render('A<% if "RawVal" == RawVal %>B<% end_if %>C')); |
||
| 616 | |||
| 617 | // Else |
||
| 618 | $this->assertEquals('ADE', |
||
| 619 | $this->render('A<% if Right == Wrong %>B<% else_if RawVal != RawVal %>C<% else %>D<% end_if %>E')); |
||
| 620 | |||
| 621 | // Empty if with else |
||
| 622 | $this->assertEquals('ABC', |
||
| 623 | $this->render('A<% if NotSet %><% else %>B<% end_if %>C')); |
||
| 624 | } |
||
| 625 | |||
| 626 | public function testBaseTagGeneration() { |
||
| 627 | // XHTML wil have a closed base tag |
||
| 628 | $tmpl1 = '<?xml version="1.0" encoding="UTF-8"?> |
||
| 629 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' |
||
| 630 | . ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
||
| 631 | <html> |
||
| 632 | <head><% base_tag %></head> |
||
| 633 | <body><p>test</p><body> |
||
| 634 | </html>'; |
||
| 635 | $this->assertRegExp('/<head><base href=".*" \/><\/head>/', $this->render($tmpl1)); |
||
| 636 | |||
| 637 | // HTML4 and 5 will only have it for IE |
||
| 638 | $tmpl2 = '<!DOCTYPE html> |
||
| 639 | <html> |
||
| 640 | <head><% base_tag %></head> |
||
| 641 | <body><p>test</p><body> |
||
| 642 | </html>'; |
||
| 643 | $this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/', |
||
| 644 | $this->render($tmpl2)); |
||
| 645 | |||
| 646 | |||
| 647 | $tmpl3 = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
||
| 648 | <html> |
||
| 649 | <head><% base_tag %></head> |
||
| 650 | <body><p>test</p><body> |
||
| 651 | </html>'; |
||
| 652 | $this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/', |
||
| 653 | $this->render($tmpl3)); |
||
| 654 | |||
| 655 | // Check that the content negotiator converts to the equally legal formats |
||
| 656 | $negotiator = new ContentNegotiator(); |
||
| 657 | |||
| 658 | $response = new SS_HTTPResponse($this->render($tmpl1)); |
||
| 659 | $negotiator->html($response); |
||
| 660 | $this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/', |
||
| 661 | $response->getBody()); |
||
| 662 | |||
| 663 | $response = new SS_HTTPResponse($this->render($tmpl1)); |
||
| 664 | $negotiator->xhtml($response); |
||
| 665 | $this->assertRegExp('/<head><base href=".*" \/><\/head>/', $response->getBody()); |
||
| 666 | } |
||
| 667 | |||
| 668 | public function testIncludeWithArguments() { |
||
| 669 | $this->assertEquals( |
||
| 670 | $this->render('<% include SSViewerTestIncludeWithArguments %>'), |
||
| 671 | '<p>[out:Arg1]</p><p>[out:Arg2]</p>' |
||
| 672 | ); |
||
| 673 | |||
| 674 | $this->assertEquals( |
||
| 675 | $this->render('<% include SSViewerTestIncludeWithArguments Arg1=A %>'), |
||
| 676 | '<p>A</p><p>[out:Arg2]</p>' |
||
| 677 | ); |
||
| 678 | |||
| 679 | $this->assertEquals( |
||
| 680 | $this->render('<% include SSViewerTestIncludeWithArguments Arg1=A, Arg2=B %>'), |
||
| 681 | '<p>A</p><p>B</p>' |
||
| 682 | ); |
||
| 683 | |||
| 684 | $this->assertEquals( |
||
| 685 | $this->render('<% include SSViewerTestIncludeWithArguments Arg1=A Bare String, Arg2=B Bare String %>'), |
||
| 686 | '<p>A Bare String</p><p>B Bare String</p>' |
||
| 687 | ); |
||
| 688 | |||
| 689 | $this->assertEquals( |
||
| 690 | $this->render('<% include SSViewerTestIncludeWithArguments Arg1="A", Arg2=$B %>', |
||
| 691 | new ArrayData(array('B' => 'Bar'))), |
||
| 692 | '<p>A</p><p>Bar</p>' |
||
| 693 | ); |
||
| 694 | |||
| 695 | $this->assertEquals( |
||
| 696 | $this->render('<% include SSViewerTestIncludeWithArguments Arg1="A" %>', |
||
| 697 | new ArrayData(array('Arg1' => 'Foo', 'Arg2' => 'Bar'))), |
||
| 698 | '<p>A</p><p>Bar</p>' |
||
| 699 | ); |
||
| 700 | |||
| 701 | $this->assertEquals( |
||
| 702 | $this->render('<% include SSViewerTestIncludeScopeInheritanceWithArgsInLoop Title="SomeArg" %>', |
||
| 703 | new ArrayData(array('Items' => new ArrayList(array( |
||
| 704 | new ArrayData(array('Title' => 'Foo')), |
||
| 705 | new ArrayData(array('Title' => 'Bar')) |
||
| 706 | ))))), |
||
| 707 | 'SomeArg - Foo - Bar - SomeArg' |
||
| 708 | ); |
||
| 709 | |||
| 710 | $this->assertEquals( |
||
| 711 | $this->render('<% include SSViewerTestIncludeScopeInheritanceWithArgsInWith Title="A" %>', |
||
| 712 | new ArrayData(array('Item' => new ArrayData(array('Title' =>'B'))))), |
||
| 713 | 'A - B - A' |
||
| 714 | ); |
||
| 715 | |||
| 716 | $this->assertEquals( |
||
| 717 | $this->render('<% include SSViewerTestIncludeScopeInheritanceWithArgsInNestedWith Title="A" %>', |
||
| 718 | new ArrayData(array( |
||
| 719 | 'Item' => new ArrayData(array( |
||
| 720 | 'Title' =>'B', 'NestedItem' => new ArrayData(array('Title' => 'C')) |
||
| 721 | ))) |
||
| 722 | )), |
||
| 723 | 'A - B - C - B - A' |
||
| 724 | ); |
||
| 725 | |||
| 726 | $this->assertEquals( |
||
| 727 | $this->render('<% include SSViewerTestIncludeScopeInheritanceWithUpAndTop Title="A" %>', |
||
| 728 | new ArrayData(array( |
||
| 729 | 'Item' => new ArrayData(array( |
||
| 730 | 'Title' =>'B', 'NestedItem' => new ArrayData(array('Title' => 'C')) |
||
| 731 | ))) |
||
| 732 | )), |
||
| 733 | 'A - A - A' |
||
| 734 | ); |
||
| 735 | |||
| 736 | $data = new ArrayData(array( |
||
| 737 | 'Nested' => new ArrayData(array( |
||
| 738 | 'Object' => new ArrayData(array('Key' => 'A')) |
||
| 739 | )), |
||
| 740 | 'Object' => new ArrayData(array('Key' => 'B')) |
||
| 741 | )); |
||
| 742 | |||
| 743 | $tmpl = SSViewer::fromString('<% include SSViewerTestIncludeObjectArguments A=$Nested.Object, B=$Object %>'); |
||
| 744 | $res = $tmpl->process($data); |
||
| 745 | $this->assertEqualIgnoringWhitespace('A B', $res, 'Objects can be passed as named arguments'); |
||
| 746 | } |
||
| 747 | |||
| 748 | public function testNamespaceInclude() { |
||
| 749 | $data = new ArrayData([]); |
||
| 750 | |||
| 751 | $this->assertEquals( |
||
| 752 | "tests:( NamespaceInclude\n )", |
||
| 753 | $this->render('tests:( <% include Namespace\NamespaceInclude %> )', $data), |
||
| 754 | 'Backslashes work for namespace references in includes' |
||
| 755 | ); |
||
| 756 | |||
| 757 | $this->assertEquals( |
||
| 758 | "tests:( NamespaceInclude\n )", |
||
| 759 | $this->render('tests:( <% include Namespace/NamespaceInclude %> )', $data), |
||
| 760 | 'Forward slashes work for namespace references in includes' |
||
| 761 | ); |
||
| 762 | } |
||
| 763 | |||
| 764 | |||
| 765 | public function testRecursiveInclude() { |
||
| 766 | $view = new SSViewer(array('Includes/SSViewerTestRecursiveInclude')); |
||
| 767 | |||
| 768 | $data = new ArrayData(array( |
||
| 769 | 'Title' => 'A', |
||
| 770 | 'Children' => new ArrayList(array( |
||
| 771 | new ArrayData(array( |
||
| 772 | 'Title' => 'A1', |
||
| 773 | 'Children' => new ArrayList(array( |
||
| 774 | new ArrayData(array( 'Title' => 'A1 i', )), |
||
| 775 | new ArrayData(array( 'Title' => 'A1 ii', )), |
||
| 776 | )), |
||
| 777 | )), |
||
| 778 | new ArrayData(array( 'Title' => 'A2', )), |
||
| 779 | new ArrayData(array( 'Title' => 'A3', )), |
||
| 780 | )), |
||
| 781 | )); |
||
| 782 | |||
| 783 | $result = $view->process($data); |
||
| 784 | // We don't care about whitespace |
||
| 785 | $rationalisedResult = trim(preg_replace('/\s+/', ' ', $result)); |
||
| 786 | |||
| 787 | $this->assertEquals('A A1 A1 i A1 ii A2 A3', $rationalisedResult); |
||
| 788 | } |
||
| 789 | |||
| 790 | public function assertEqualIgnoringWhitespace($a, $b) { |
||
| 791 | $this->assertEquals(preg_replace('/\s+/', '', $a), preg_replace('/\s+/', '', $b)); |
||
| 792 | } |
||
| 793 | |||
| 794 | /** |
||
| 795 | * See {@link ViewableDataTest} for more extensive casting tests, |
||
| 796 | * this test just ensures that basic casting is correctly applied during template parsing. |
||
| 797 | */ |
||
| 798 | public function testCastingHelpers() { |
||
| 799 | $vd = new SSViewerTest_ViewableData(); |
||
| 800 | $vd->TextValue = '<b>html</b>'; |
||
| 801 | $vd->HTMLValue = '<b>html</b>'; |
||
| 802 | $vd->UncastedValue = '<b>html</b>'; |
||
| 803 | |||
| 804 | // Value casted as "Text" |
||
| 805 | $this->assertEquals( |
||
| 806 | '<b>html</b>', |
||
| 807 | $t = SSViewer::fromString('$TextValue')->process($vd) |
||
| 808 | ); |
||
| 809 | $this->assertEquals( |
||
| 810 | '<b>html</b>', |
||
| 811 | $t = SSViewer::fromString('$TextValue.RAW')->process($vd) |
||
| 812 | ); |
||
| 813 | $this->assertEquals( |
||
| 814 | '<b>html</b>', |
||
| 815 | $t = SSViewer::fromString('$TextValue.XML')->process($vd) |
||
| 816 | ); |
||
| 817 | |||
| 818 | // Value casted as "HTMLText" |
||
| 819 | $this->assertEquals( |
||
| 820 | '<b>html</b>', |
||
| 821 | $t = SSViewer::fromString('$HTMLValue')->process($vd) |
||
| 822 | ); |
||
| 823 | $this->assertEquals( |
||
| 824 | '<b>html</b>', |
||
| 825 | $t = SSViewer::fromString('$HTMLValue.RAW')->process($vd) |
||
| 826 | ); |
||
| 827 | $this->assertEquals( |
||
| 828 | '<b>html</b>', |
||
| 829 | $t = SSViewer::fromString('$HTMLValue.XML')->process($vd) |
||
| 830 | ); |
||
| 831 | |||
| 832 | // Uncasted value (falls back to ViewableData::$default_cast="Text") |
||
| 833 | $vd = new SSViewerTest_ViewableData(); |
||
| 834 | $vd->UncastedValue = '<b>html</b>'; |
||
| 835 | $this->assertEquals( |
||
| 836 | '<b>html</b>', |
||
| 837 | $t = SSViewer::fromString('$UncastedValue')->process($vd) |
||
| 838 | ); |
||
| 839 | $this->assertEquals( |
||
| 840 | '<b>html</b>', |
||
| 841 | $t = SSViewer::fromString('$UncastedValue.RAW')->process($vd) |
||
| 842 | ); |
||
| 843 | $this->assertEquals( |
||
| 844 | '<b>html</b>', |
||
| 845 | $t = SSViewer::fromString('$UncastedValue.XML')->process($vd) |
||
| 846 | ); |
||
| 847 | } |
||
| 848 | |||
| 849 | public function testSSViewerBasicIteratorSupport() { |
||
| 850 | $data = new ArrayData(array( |
||
| 851 | 'Set' => new ArrayList(array( |
||
| 852 | new SSViewerTest_Object("1"), |
||
| 853 | new SSViewerTest_Object("2"), |
||
| 854 | new SSViewerTest_Object("3"), |
||
| 855 | new SSViewerTest_Object("4"), |
||
| 856 | new SSViewerTest_Object("5"), |
||
| 857 | new SSViewerTest_Object("6"), |
||
| 858 | new SSViewerTest_Object("7"), |
||
| 859 | new SSViewerTest_Object("8"), |
||
| 860 | new SSViewerTest_Object("9"), |
||
| 861 | new SSViewerTest_Object("10"), |
||
| 862 | )) |
||
| 863 | )); |
||
| 864 | |||
| 865 | //base test |
||
| 866 | $result = $this->render('<% loop Set %>$Number<% end_loop %>',$data); |
||
| 867 | $this->assertEquals("12345678910",$result,"Numbers rendered in order"); |
||
| 868 | |||
| 869 | //test First |
||
| 870 | $result = $this->render('<% loop Set %><% if First %>$Number<% end_if %><% end_loop %>',$data); |
||
| 871 | $this->assertEquals("1",$result,"Only the first number is rendered"); |
||
| 872 | |||
| 873 | //test Last |
||
| 874 | $result = $this->render('<% loop Set %><% if Last %>$Number<% end_if %><% end_loop %>',$data); |
||
| 875 | $this->assertEquals("10",$result,"Only the last number is rendered"); |
||
| 876 | |||
| 877 | //test Even |
||
| 878 | $result = $this->render('<% loop Set %><% if Even() %>$Number<% end_if %><% end_loop %>',$data); |
||
| 879 | $this->assertEquals("246810",$result,"Even numbers rendered in order"); |
||
| 880 | |||
| 881 | //test Even with quotes |
||
| 882 | $result = $this->render('<% loop Set %><% if Even("1") %>$Number<% end_if %><% end_loop %>',$data); |
||
| 883 | $this->assertEquals("246810",$result,"Even numbers rendered in order"); |
||
| 884 | |||
| 885 | //test Even without quotes |
||
| 886 | $result = $this->render('<% loop Set %><% if Even(1) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 887 | $this->assertEquals("246810",$result,"Even numbers rendered in order"); |
||
| 888 | |||
| 889 | //test Even with zero-based start index |
||
| 890 | $result = $this->render('<% loop Set %><% if Even("0") %>$Number<% end_if %><% end_loop %>',$data); |
||
| 891 | $this->assertEquals("13579",$result,"Even (with zero-based index) numbers rendered in order"); |
||
| 892 | |||
| 893 | //test Odd |
||
| 894 | $result = $this->render('<% loop Set %><% if Odd %>$Number<% end_if %><% end_loop %>',$data); |
||
| 895 | $this->assertEquals("13579",$result,"Odd numbers rendered in order"); |
||
| 896 | |||
| 897 | //test FirstLast |
||
| 898 | $result = $this->render('<% loop Set %><% if FirstLast %>$Number$FirstLast<% end_if %><% end_loop %>',$data); |
||
| 899 | $this->assertEquals("1first10last",$result,"First and last numbers rendered in order"); |
||
| 900 | |||
| 901 | //test Middle |
||
| 902 | $result = $this->render('<% loop Set %><% if Middle %>$Number<% end_if %><% end_loop %>',$data); |
||
| 903 | $this->assertEquals("23456789",$result,"Middle numbers rendered in order"); |
||
| 904 | |||
| 905 | //test MiddleString |
||
| 906 | $result = $this->render('<% loop Set %><% if MiddleString == "middle" %>$Number$MiddleString<% end_if %>' |
||
| 907 | . '<% end_loop %>',$data); |
||
| 908 | $this->assertEquals("2middle3middle4middle5middle6middle7middle8middle9middle",$result, |
||
| 909 | "Middle numbers rendered in order"); |
||
| 910 | |||
| 911 | //test EvenOdd |
||
| 912 | $result = $this->render('<% loop Set %>$EvenOdd<% end_loop %>',$data); |
||
| 913 | $this->assertEquals("oddevenoddevenoddevenoddevenoddeven",$result, |
||
| 914 | "Even and Odd is returned in sequence numbers rendered in order"); |
||
| 915 | |||
| 916 | //test Pos |
||
| 917 | $result = $this->render('<% loop Set %>$Pos<% end_loop %>',$data); |
||
| 918 | $this->assertEquals("12345678910", $result, '$Pos is rendered in order'); |
||
| 919 | |||
| 920 | //test Pos |
||
| 921 | $result = $this->render('<% loop Set %>$Pos(0)<% end_loop %>',$data); |
||
| 922 | $this->assertEquals("0123456789", $result, '$Pos(0) is rendered in order'); |
||
| 923 | |||
| 924 | //test FromEnd |
||
| 925 | $result = $this->render('<% loop Set %>$FromEnd<% end_loop %>',$data); |
||
| 926 | $this->assertEquals("10987654321", $result, '$FromEnd is rendered in order'); |
||
| 927 | |||
| 928 | //test FromEnd |
||
| 929 | $result = $this->render('<% loop Set %>$FromEnd(0)<% end_loop %>',$data); |
||
| 930 | $this->assertEquals("9876543210", $result, '$FromEnd(0) rendered in order'); |
||
| 931 | |||
| 932 | //test Total |
||
| 933 | $result = $this->render('<% loop Set %>$TotalItems<% end_loop %>',$data); |
||
| 934 | $this->assertEquals("10101010101010101010",$result,"10 total items X 10 are returned"); |
||
| 935 | |||
| 936 | //test Modulus |
||
| 937 | $result = $this->render('<% loop Set %>$Modulus(2,1)<% end_loop %>',$data); |
||
| 938 | $this->assertEquals("1010101010",$result,"1-indexed pos modular divided by 2 rendered in order"); |
||
| 939 | |||
| 940 | //test MultipleOf 3 |
||
| 941 | $result = $this->render('<% loop Set %><% if MultipleOf(3) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 942 | $this->assertEquals("369",$result,"Only numbers that are multiples of 3 are returned"); |
||
| 943 | |||
| 944 | //test MultipleOf 4 |
||
| 945 | $result = $this->render('<% loop Set %><% if MultipleOf(4) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 946 | $this->assertEquals("48",$result,"Only numbers that are multiples of 4 are returned"); |
||
| 947 | |||
| 948 | //test MultipleOf 5 |
||
| 949 | $result = $this->render('<% loop Set %><% if MultipleOf(5) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 950 | $this->assertEquals("510",$result,"Only numbers that are multiples of 5 are returned"); |
||
| 951 | |||
| 952 | //test MultipleOf 10 |
||
| 953 | $result = $this->render('<% loop Set %><% if MultipleOf(10,1) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 954 | $this->assertEquals("10",$result,"Only numbers that are multiples of 10 (with 1-based indexing) are returned"); |
||
| 955 | |||
| 956 | //test MultipleOf 9 zero-based |
||
| 957 | $result = $this->render('<% loop Set %><% if MultipleOf(9,0) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 958 | $this->assertEquals("110",$result, |
||
| 959 | "Only numbers that are multiples of 9 with zero-based indexing are returned. (The first and last item)"); |
||
| 960 | |||
| 961 | //test MultipleOf 11 |
||
| 962 | $result = $this->render('<% loop Set %><% if MultipleOf(11) %>$Number<% end_if %><% end_loop %>',$data); |
||
| 963 | $this->assertEquals("",$result,"Only numbers that are multiples of 11 are returned. I.e. nothing returned"); |
||
| 964 | } |
||
| 965 | |||
| 966 | /** |
||
| 967 | * Test $Up works when the scope $Up refers to was entered with a "with" block |
||
| 968 | */ |
||
| 969 | public function testUpInWith() { |
||
| 970 | |||
| 971 | // Data to run the loop tests on - three levels deep |
||
| 972 | $data = new ArrayData(array( |
||
| 973 | 'Name' => 'Top', |
||
| 974 | 'Foo' => new ArrayData(array( |
||
| 975 | 'Name' => 'Foo', |
||
| 976 | 'Bar' => new ArrayData(array( |
||
| 977 | 'Name' => 'Bar', |
||
| 978 | 'Baz' => new ArrayData(array( |
||
| 979 | 'Name' => 'Baz' |
||
| 980 | )), |
||
| 981 | 'Qux' => new ArrayData(array( |
||
| 982 | 'Name' => 'Qux' |
||
| 983 | )) |
||
| 984 | )) |
||
| 985 | )) |
||
| 986 | )); |
||
| 987 | |||
| 988 | // Basic functionality |
||
| 989 | $this->assertEquals('BarFoo', |
||
| 990 | $this->render('<% with Foo %><% with Bar %>{$Name}{$Up.Name}<% end_with %><% end_with %>', $data)); |
||
| 991 | |||
| 992 | // Two level with block, up refers to internally referenced Bar |
||
| 993 | $this->assertEquals('BarFoo', |
||
| 994 | $this->render('<% with Foo.Bar %>{$Name}{$Up.Name}<% end_with %>', $data)); |
||
| 995 | |||
| 996 | // Stepping up & back down the scope tree |
||
| 997 | $this->assertEquals('BazBarQux', |
||
| 998 | $this->render('<% with Foo.Bar.Baz %>{$Name}{$Up.Name}{$Up.Qux.Name}<% end_with %>', $data)); |
||
| 999 | |||
| 1000 | // Using $Up in a with block |
||
| 1001 | $this->assertEquals('BazBarQux', |
||
| 1002 | $this->render('<% with Foo.Bar.Baz %>{$Name}<% with $Up %>{$Name}{$Qux.Name}<% end_with %>' |
||
| 1003 | .'<% end_with %>', $data)); |
||
| 1004 | |||
| 1005 | // Stepping up & back down the scope tree with with blocks |
||
| 1006 | $this->assertEquals('BazBarQuxBarBaz', |
||
| 1007 | $this->render('<% with Foo.Bar.Baz %>{$Name}<% with $Up %>{$Name}<% with Qux %>{$Name}<% end_with %>' |
||
| 1008 | . '{$Name}<% end_with %>{$Name}<% end_with %>', $data)); |
||
| 1009 | |||
| 1010 | // Using $Up.Up, where first $Up points to a previous scope entered using $Up, thereby skipping up to Foo |
||
| 1011 | $this->assertEquals('Foo', |
||
| 1012 | $this->render('<% with Foo.Bar.Baz %><% with Up %><% with Qux %>{$Up.Up.Name}<% end_with %><% end_with %>' |
||
| 1013 | . '<% end_with %>', $data)); |
||
| 1014 | |||
| 1015 | // Using $Up.Up, where first $Up points to an Up used in a local scope lookup, should still skip to Foo |
||
| 1016 | $this->assertEquals('Foo', |
||
| 1017 | $this->render('<% with Foo.Bar.Baz.Up.Qux %>{$Up.Up.Name}<% end_with %>', $data)); |
||
| 1018 | } |
||
| 1019 | |||
| 1020 | /** |
||
| 1021 | * Test $Up works when the scope $Up refers to was entered with a "loop" block |
||
| 1022 | */ |
||
| 1023 | public function testUpInLoop(){ |
||
| 1024 | |||
| 1025 | // Data to run the loop tests on - one sequence of three items, each with a subitem |
||
| 1026 | $data = new ArrayData(array( |
||
| 1027 | 'Name' => 'Top', |
||
| 1028 | 'Foo' => new ArrayList(array( |
||
| 1029 | new ArrayData(array( |
||
| 1030 | 'Name' => '1', |
||
| 1031 | 'Sub' => new ArrayData(array( |
||
| 1032 | 'Name' => 'Bar' |
||
| 1033 | )) |
||
| 1034 | )), |
||
| 1035 | new ArrayData(array( |
||
| 1036 | 'Name' => '2', |
||
| 1037 | 'Sub' => new ArrayData(array( |
||
| 1038 | 'Name' => 'Baz' |
||
| 1039 | )) |
||
| 1040 | )), |
||
| 1041 | new ArrayData(array( |
||
| 1042 | 'Name' => '3', |
||
| 1043 | 'Sub' => new ArrayData(array( |
||
| 1044 | 'Name' => 'Qux' |
||
| 1045 | )) |
||
| 1046 | )) |
||
| 1047 | )) |
||
| 1048 | )); |
||
| 1049 | |||
| 1050 | // Make sure inside a loop, $Up refers to the current item of the loop |
||
| 1051 | $this->assertEqualIgnoringWhitespace( |
||
| 1052 | '111 222 333', |
||
| 1053 | $this->render( |
||
| 1054 | '<% loop $Foo %>$Name<% with $Sub %>$Up.Name<% end_with %>$Name<% end_loop %>', |
||
| 1055 | $data |
||
| 1056 | ) |
||
| 1057 | ); |
||
| 1058 | |||
| 1059 | // Make sure inside a loop, looping over $Up uses a separate iterator, |
||
| 1060 | // and doesn't interfere with the original iterator |
||
| 1061 | $this->assertEqualIgnoringWhitespace( |
||
| 1062 | '1Bar123Bar1 2Baz123Baz2 3Qux123Qux3', |
||
| 1063 | $this->render( |
||
| 1064 | '<% loop $Foo %> |
||
| 1065 | $Name |
||
| 1066 | <% with $Sub %> |
||
| 1067 | $Name |
||
| 1068 | <% loop $Up %>$Name<% end_loop %> |
||
| 1069 | $Name |
||
| 1070 | <% end_with %> |
||
| 1071 | $Name |
||
| 1072 | <% end_loop %>', |
||
| 1073 | $data |
||
| 1074 | ) |
||
| 1075 | ); |
||
| 1076 | |||
| 1077 | // Make sure inside a loop, looping over $Up uses a separate iterator, |
||
| 1078 | // and doesn't interfere with the original iterator or local lookups |
||
| 1079 | $this->assertEqualIgnoringWhitespace( |
||
| 1080 | '1 Bar1 123 1Bar 1 2 Baz2 123 2Baz 2 3 Qux3 123 3Qux 3', |
||
| 1081 | $this->render( |
||
| 1082 | '<% loop $Foo %> |
||
| 1083 | $Name |
||
| 1084 | <% with $Sub %> |
||
| 1085 | {$Name}{$Up.Name} |
||
| 1086 | <% loop $Up %>$Name<% end_loop %> |
||
| 1087 | {$Up.Name}{$Name} |
||
| 1088 | <% end_with %> |
||
| 1089 | $Name |
||
| 1090 | <% end_loop %>', |
||
| 1091 | $data |
||
| 1092 | ) |
||
| 1093 | ); |
||
| 1094 | } |
||
| 1095 | |||
| 1096 | /** |
||
| 1097 | * Test that nested loops restore the loop variables correctly when pushing and popping states |
||
| 1098 | */ |
||
| 1099 | public function testNestedLoops(){ |
||
| 1100 | |||
| 1101 | // Data to run the loop tests on - one sequence of three items, one with child elements |
||
| 1102 | // (of a different size to the main sequence) |
||
| 1103 | $data = new ArrayData(array( |
||
| 1104 | 'Foo' => new ArrayList(array( |
||
| 1105 | new ArrayData(array( |
||
| 1106 | 'Name' => '1', |
||
| 1107 | 'Children' => new ArrayList(array( |
||
| 1108 | new ArrayData(array( |
||
| 1109 | 'Name' => 'a' |
||
| 1110 | )), |
||
| 1111 | new ArrayData(array( |
||
| 1112 | 'Name' => 'b' |
||
| 1113 | )), |
||
| 1114 | )), |
||
| 1115 | )), |
||
| 1116 | new ArrayData(array( |
||
| 1117 | 'Name' => '2', |
||
| 1118 | 'Children' => new ArrayList(), |
||
| 1119 | )), |
||
| 1120 | new ArrayData(array( |
||
| 1121 | 'Name' => '3', |
||
| 1122 | 'Children' => new ArrayList(), |
||
| 1123 | )), |
||
| 1124 | )), |
||
| 1125 | )); |
||
| 1126 | |||
| 1127 | // Make sure that including a loop inside a loop will not destroy the internal count of |
||
| 1128 | // items, checked by using "Last" |
||
| 1129 | $this->assertEqualIgnoringWhitespace( |
||
| 1130 | '1ab23last', |
||
| 1131 | $this->render('<% loop $Foo %>$Name<% loop Children %>$Name<% end_loop %><% if Last %>last<% end_if %>' |
||
| 1132 | . '<% end_loop %>', $data |
||
| 1133 | ) |
||
| 1134 | ); |
||
| 1135 | } |
||
| 1136 | |||
| 1137 | public function testLayout() { |
||
| 1138 | $self = $this; |
||
| 1139 | |||
| 1140 | $this->useTestTheme(dirname(__FILE__), 'layouttest', function() use ($self) { |
||
| 1141 | $template = new SSViewer(array('Page')); |
||
| 1142 | $self->assertEquals("Foo\n\n", $template->process(new ArrayData(array()))); |
||
| 1143 | |||
| 1144 | $template = new SSViewer(array('Shortcodes', 'Page')); |
||
| 1145 | $self->assertEquals("[file_link]\n\n", $template->process(new ArrayData(array()))); |
||
| 1146 | }); |
||
| 1147 | } |
||
| 1148 | |||
| 1149 | /** |
||
| 1150 | * @covers SSViewer::get_templates_by_class() |
||
| 1151 | */ |
||
| 1152 | public function testGetTemplatesByClass() { |
||
| 1153 | $self = $this; |
||
| 1154 | $this->useTestTheme(dirname(__FILE__), 'layouttest', function() use ($self) { |
||
| 1155 | // Test passing a string |
||
| 1156 | $templates = SSViewer::get_templates_by_class( |
||
| 1157 | 'TestNamespace\SSViewerTestModel_Controller', |
||
| 1158 | '', |
||
| 1159 | 'Controller' |
||
| 1160 | ); |
||
| 1161 | $self->assertEquals([ |
||
| 1162 | 'TestNamespace\SSViewerTestModel_Controller', |
||
| 1163 | 'Controller', |
||
| 1164 | ], $templates); |
||
| 1165 | |||
| 1166 | // Test to ensure we're stopping at the base class. |
||
| 1167 | $templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTestModel_Controller', '', 'TestNamespace\SSViewerTestModel_Controller'); |
||
| 1168 | $self->assertCount(1, $templates); |
||
| 1169 | |||
| 1170 | // Make sure we can filter our templates by suffix. |
||
| 1171 | $templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTestModel', '_Controller'); |
||
| 1172 | $self->assertCount(1, $templates); |
||
| 1173 | |||
| 1174 | // Let's throw something random in there. |
||
| 1175 | $self->setExpectedException('InvalidArgumentException'); |
||
| 1176 | $templates = SSViewer::get_templates_by_class(array()); |
||
| 1177 | }); |
||
| 1178 | } |
||
| 1179 | |||
| 1180 | public function testRewriteHashlinks() { |
||
| 1181 | $orig = Config::inst()->get('SSViewer', 'rewrite_hash_links'); |
||
| 1182 | Config::inst()->update('SSViewer', 'rewrite_hash_links', true); |
||
| 1183 | |||
| 1184 | $_SERVER['HTTP_HOST'] = 'www.mysite.com'; |
||
| 1185 | $_SERVER['REQUEST_URI'] = '//file.com?foo"onclick="alert(\'xss\')""'; |
||
| 1186 | |||
| 1187 | // Emulate SSViewer::process() |
||
| 1188 | // Note that leading double slashes have been rewritten to prevent these being mis-interepreted |
||
| 1189 | // as protocol-less absolute urls |
||
| 1190 | $base = Convert::raw2att('/file.com?foo"onclick="alert(\'xss\')""'); |
||
| 1191 | |||
| 1192 | $tmplFile = TEMP_FOLDER . '/SSViewerTest_testRewriteHashlinks_' . sha1(rand()) . '.ss'; |
||
| 1193 | |||
| 1194 | // Note: SSViewer_FromString doesn't rewrite hash links. |
||
| 1195 | file_put_contents($tmplFile, '<!DOCTYPE html> |
||
| 1196 | <html> |
||
| 1197 | <head><% base_tag %></head> |
||
| 1198 | <body> |
||
| 1199 | <a class="external-inline" href="http://google.com#anchor">ExternalInlineLink</a> |
||
| 1200 | $ExternalInsertedLink |
||
| 1201 | <a class="inline" href="#anchor">InlineLink</a> |
||
| 1202 | $InsertedLink |
||
| 1203 | <svg><use xlink:href="#sprite"></use></svg> |
||
| 1204 | <body> |
||
| 1205 | </html>'); |
||
| 1206 | $tmpl = new SSViewer($tmplFile); |
||
| 1207 | $obj = new ViewableData(); |
||
| 1208 | $obj->InsertedLink = DBField::create_field( |
||
| 1209 | 'HTMLFragment', |
||
| 1210 | '<a class="inserted" href="#anchor">InsertedLink</a>' |
||
| 1211 | ); |
||
| 1212 | $obj->ExternalInsertedLink = DBField::create_field( |
||
| 1213 | 'HTMLFragment', |
||
| 1214 | '<a class="external-inserted" href="http://google.com#anchor">ExternalInsertedLink</a>' |
||
| 1215 | ); |
||
| 1216 | $result = $tmpl->process($obj); |
||
| 1217 | $this->assertContains( |
||
| 1218 | '<a class="inserted" href="' . $base . '#anchor">InsertedLink</a>', |
||
| 1219 | $result |
||
| 1220 | ); |
||
| 1221 | $this->assertContains( |
||
| 1222 | '<a class="external-inserted" href="http://google.com#anchor">ExternalInsertedLink</a>', |
||
| 1223 | $result |
||
| 1224 | ); |
||
| 1225 | $this->assertContains( |
||
| 1226 | '<a class="inline" href="' . $base . '#anchor">InlineLink</a>', |
||
| 1227 | $result |
||
| 1228 | ); |
||
| 1229 | $this->assertContains( |
||
| 1230 | '<a class="external-inline" href="http://google.com#anchor">ExternalInlineLink</a>', |
||
| 1231 | $result |
||
| 1232 | ); |
||
| 1233 | $this->assertContains( |
||
| 1234 | '<svg><use xlink:href="#sprite"></use></svg>', |
||
| 1235 | $result, |
||
| 1236 | 'SSTemplateParser should only rewrite anchor hrefs' |
||
| 1237 | ); |
||
| 1238 | |||
| 1239 | unlink($tmplFile); |
||
| 1240 | |||
| 1241 | Config::inst()->update('SSViewer', 'rewrite_hash_links', $orig); |
||
| 1242 | } |
||
| 1243 | |||
| 1244 | public function testRewriteHashlinksInPhpMode() { |
||
| 1245 | $orig = Config::inst()->get('SSViewer', 'rewrite_hash_links'); |
||
| 1246 | Config::inst()->update('SSViewer', 'rewrite_hash_links', 'php'); |
||
| 1247 | |||
| 1248 | $tmplFile = TEMP_FOLDER . '/SSViewerTest_testRewriteHashlinksInPhpMode_' . sha1(rand()) . '.ss'; |
||
| 1249 | |||
| 1250 | // Note: SSViewer_FromString doesn't rewrite hash links. |
||
| 1251 | file_put_contents($tmplFile, '<!DOCTYPE html> |
||
| 1252 | <html> |
||
| 1253 | <head><% base_tag %></head> |
||
| 1254 | <body> |
||
| 1255 | <a class="inline" href="#anchor">InlineLink</a> |
||
| 1256 | $InsertedLink |
||
| 1257 | <svg><use xlink:href="#sprite"></use></svg> |
||
| 1258 | <body> |
||
| 1259 | </html>'); |
||
| 1260 | $tmpl = new SSViewer($tmplFile); |
||
| 1261 | $obj = new ViewableData(); |
||
| 1262 | $obj->InsertedLink = DBField::create_field( |
||
| 1263 | 'HTMLFragment', |
||
| 1264 | '<a class="inserted" href="#anchor">InsertedLink</a>' |
||
| 1265 | ); |
||
| 1266 | $result = $tmpl->process($obj); |
||
| 1267 | |||
| 1268 | $code = <<<'EOC' |
||
| 1269 | <a class="inserted" href="<?php echo Convert::raw2att(preg_replace("/^(\/)+/", "/", $_SERVER['REQUEST_URI'])); ?>#anchor">InsertedLink</a> |
||
| 1270 | EOC; |
||
| 1271 | $this->assertContains($code, $result); |
||
| 1272 | // TODO Fix inline links in PHP mode |
||
| 1273 | // $this->assertContains( |
||
| 1274 | // '<a class="inline" href="<?php echo str_replace(', |
||
| 1275 | // $result |
||
| 1276 | // ); |
||
| 1277 | $this->assertContains( |
||
| 1278 | '<svg><use xlink:href="#sprite"></use></svg>', |
||
| 1279 | $result, |
||
| 1280 | 'SSTemplateParser should only rewrite anchor hrefs' |
||
| 1281 | ); |
||
| 1282 | |||
| 1283 | unlink($tmplFile); |
||
| 1284 | |||
| 1285 | Config::inst()->update('SSViewer', 'rewrite_hash_links', $orig); |
||
| 1286 | } |
||
| 1287 | |||
| 1288 | public function testRenderWithSourceFileComments() { |
||
| 1289 | $origEnv = Config::inst()->get('Director', 'environment_type'); |
||
| 1290 | Config::inst()->update('Director', 'environment_type', 'dev'); |
||
| 1291 | Config::inst()->update('SSViewer', 'source_file_comments', true); |
||
| 1292 | $i = FRAMEWORK_PATH . '/tests/templates/Includes'; |
||
| 1293 | $f = FRAMEWORK_PATH . '/tests/templates/SSViewerTestComments'; |
||
| 1294 | $templates = array( |
||
| 1295 | array( |
||
| 1296 | 'name' => 'SSViewerTestCommentsFullSource', |
||
| 1297 | 'expected' => "" |
||
| 1298 | . "<!doctype html>" |
||
| 1299 | . "<!-- template $f/SSViewerTestCommentsFullSource.ss -->" |
||
| 1300 | . "<html>" |
||
| 1301 | . "\t<head></head>" |
||
| 1302 | . "\t<body></body>" |
||
| 1303 | . "</html>" |
||
| 1304 | . "<!-- end template $f/SSViewerTestCommentsFullSource.ss -->", |
||
| 1305 | ), |
||
| 1306 | array( |
||
| 1307 | 'name' => 'SSViewerTestCommentsFullSourceHTML4Doctype', |
||
| 1308 | 'expected' => "" |
||
| 1309 | . "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML " |
||
| 1310 | . "4.01//EN\"\t\t\"http://www.w3.org/TR/html4/strict.dtd\">" |
||
| 1311 | . "<!-- template $f/SSViewerTestCommentsFullSourceHTML4Doctype.ss -->" |
||
| 1312 | . "<html>" |
||
| 1313 | . "\t<head></head>" |
||
| 1314 | . "\t<body></body>" |
||
| 1315 | . "</html>" |
||
| 1316 | . "<!-- end template $f/SSViewerTestCommentsFullSourceHTML4Doctype.ss -->", |
||
| 1317 | ), |
||
| 1318 | array( |
||
| 1319 | 'name' => 'SSViewerTestCommentsFullSourceNoDoctype', |
||
| 1320 | 'expected' => "" |
||
| 1321 | . "<html><!-- template $f/SSViewerTestCommentsFullSourceNoDoctype.ss -->" |
||
| 1322 | . "\t<head></head>" |
||
| 1323 | . "\t<body></body>" |
||
| 1324 | . "<!-- end template $f/SSViewerTestCommentsFullSourceNoDoctype.ss --></html>", |
||
| 1325 | ), |
||
| 1326 | array( |
||
| 1327 | 'name' => 'SSViewerTestCommentsFullSourceIfIE', |
||
| 1328 | 'expected' => "" |
||
| 1329 | . "<!doctype html>" |
||
| 1330 | . "<!-- template $f/SSViewerTestCommentsFullSourceIfIE.ss -->" |
||
| 1331 | . "<!--[if lte IE 8]> <html class='old-ie'> <![endif]-->" |
||
| 1332 | . "<!--[if gt IE 8]> <html class='new-ie'> <![endif]-->" |
||
| 1333 | . "<!--[if !IE]><!--> <html class='no-ie'> <!--<![endif]-->" |
||
| 1334 | . "\t<head></head>" |
||
| 1335 | . "\t<body></body>" |
||
| 1336 | . "</html>" |
||
| 1337 | . "<!-- end template $f/SSViewerTestCommentsFullSourceIfIE.ss -->", |
||
| 1338 | ), |
||
| 1339 | array( |
||
| 1340 | 'name' => 'SSViewerTestCommentsFullSourceIfIENoDoctype', |
||
| 1341 | 'expected' => "" |
||
| 1342 | . "<!--[if lte IE 8]> <html class='old-ie'> <![endif]-->" |
||
| 1343 | . "<!--[if gt IE 8]> <html class='new-ie'> <![endif]-->" |
||
| 1344 | . "<!--[if !IE]><!--> <html class='no-ie'>" |
||
| 1345 | . "<!-- template $f/SSViewerTestCommentsFullSourceIfIENoDoctype.ss -->" |
||
| 1346 | . " <!--<![endif]-->" |
||
| 1347 | . "\t<head></head>" |
||
| 1348 | . "\t<body></body>" |
||
| 1349 | . "<!-- end template $f/SSViewerTestCommentsFullSourceIfIENoDoctype.ss --></html>", |
||
| 1350 | ), |
||
| 1351 | array( |
||
| 1352 | 'name' => 'SSViewerTestCommentsPartialSource', |
||
| 1353 | 'expected' => |
||
| 1354 | "<!-- template $f/SSViewerTestCommentsPartialSource.ss -->" |
||
| 1355 | . "<div class='typography'></div>" |
||
| 1356 | . "<!-- end template $f/SSViewerTestCommentsPartialSource.ss -->", |
||
| 1357 | ), |
||
| 1358 | array( |
||
| 1359 | 'name' => 'SSViewerTestCommentsWithInclude', |
||
| 1360 | 'expected' => |
||
| 1361 | "<!-- template $f/SSViewerTestCommentsWithInclude.ss -->" |
||
| 1362 | . "<div class='typography'>" |
||
| 1363 | . "<!-- include 'SSViewerTestCommentsInclude' -->" |
||
| 1364 | . "<!-- template $i/SSViewerTestCommentsInclude.ss -->" |
||
| 1365 | . "Included" |
||
| 1366 | . "<!-- end template $i/SSViewerTestCommentsInclude.ss -->" |
||
| 1367 | . "<!-- end include 'SSViewerTestCommentsInclude' -->" |
||
| 1368 | . "</div>" |
||
| 1369 | . "<!-- end template $f/SSViewerTestCommentsWithInclude.ss -->", |
||
| 1370 | ), |
||
| 1371 | ); |
||
| 1372 | foreach ($templates as $template) { |
||
| 1373 | $this->_renderWithSourceFileComments('SSViewerTestComments/'.$template['name'], $template['expected']); |
||
| 1374 | } |
||
| 1375 | Config::inst()->update('SSViewer', 'source_file_comments', false); |
||
| 1376 | Config::inst()->update('Director', 'environment_type', $origEnv); |
||
| 1377 | } |
||
| 1378 | private function _renderWithSourceFileComments($name, $expected) { |
||
| 1386 | |||
| 1387 | public function testLoopIteratorIterator() { |
||
| 1393 | |||
| 1394 | public function testProcessOnlyIncludesRequirementsOnce() { |
||
| 1395 | $template = new SSViewer(array('SSViewerTestProcess')); |
||
| 1396 | $basePath = dirname($this->getCurrentRelativePath()) . '/forms'; |
||
| 1397 | |||
| 1398 | $backend = Injector::inst()->create('Requirements_Backend'); |
||
| 1399 | $backend->setCombinedFilesEnabled(false); |
||
| 1400 | $backend->combineFiles( |
||
| 1401 | 'RequirementsTest_ab.css', |
||
| 1402 | array( |
||
| 1403 | $basePath . '/RequirementsTest_a.css', |
||
| 1404 | $basePath . '/RequirementsTest_b.css' |
||
| 1405 | ) |
||
| 1406 | ); |
||
| 1407 | |||
| 1408 | Requirements::set_backend($backend); |
||
| 1409 | |||
| 1410 | $this->assertEquals(1, substr_count($template->process(array()), "a.css")); |
||
| 1411 | $this->assertEquals(1, substr_count($template->process(array()), "b.css")); |
||
| 1412 | |||
| 1413 | // if we disable the requirements then we should get nothing |
||
| 1414 | $template->includeRequirements(false); |
||
| 1415 | $this->assertEquals(0, substr_count($template->process(array()), "a.css")); |
||
| 1416 | $this->assertEquals(0, substr_count($template->process(array()), "b.css")); |
||
| 1417 | } |
||
| 1418 | |||
| 1419 | public function testRequireCallInTemplateInclude() { |
||
| 1436 | |||
| 1437 | public function testCallsWithArguments() { |
||
| 1475 | |||
| 1476 | public function testClosedBlockExtension() { |
||
| 1491 | |||
| 1492 | public function testOpenBlockExtension() { |
||
| 1507 | |||
| 1508 | /** |
||
| 1509 | * Tests if caching for SSViewer_FromString is working |
||
| 1510 | */ |
||
| 1511 | public function testFromStringCaching() { |
||
| 1535 | } |
||
| 1536 | |||
| 1537 | /** |
||
| 1538 | * A test fixture that will echo back the template item |
||
| 1539 | */ |
||
| 1540 | class SSViewerTestFixture extends ViewableData { |
||
| 1541 | protected $name; |
||
| 1542 | |||
| 1543 | public function __construct($name = null) { |
||
| 1544 | $this->name = $name; |
||
| 1545 | parent::__construct(); |
||
| 1546 | } |
||
| 1547 | |||
| 1548 | |||
| 1549 | private function argedName($fieldName, $arguments) { |
||
| 1550 | $childName = $this->name ? "$this->name.$fieldName" : $fieldName; |
||
| 1551 | if($arguments) return $childName . '(' . implode(',', $arguments) . ')'; |
||
| 1552 | else return $childName; |
||
| 1553 | } |
||
| 1554 | public function obj($fieldName, $arguments=null, $cache=false, $cacheName=null) { |
||
| 1555 | $childName = $this->argedName($fieldName, $arguments); |
||
| 1556 | |||
| 1557 | // Special field name Loop### to create a list |
||
| 1558 | if(preg_match('/^Loop([0-9]+)$/', $fieldName, $matches)) { |
||
| 1559 | $output = new ArrayList(); |
||
| 1560 | for($i=0;$i<$matches[1];$i++) $output->push(new SSViewerTestFixture($childName)); |
||
| 1561 | return $output; |
||
| 1562 | |||
| 1563 | } else if(preg_match('/NotSet/i', $fieldName)) { |
||
| 1564 | return new ViewableData(); |
||
| 1565 | |||
| 1566 | } else { |
||
| 1567 | return new SSViewerTestFixture($childName); |
||
| 1568 | } |
||
| 1569 | } |
||
| 1570 | |||
| 1571 | |||
| 1572 | public function XML_val($fieldName, $arguments = null, $cache = false) { |
||
| 1573 | if(preg_match('/NotSet/i', $fieldName)) { |
||
| 1574 | return ''; |
||
| 1575 | } else if(preg_match('/Raw/i', $fieldName)) { |
||
| 1576 | return $fieldName; |
||
| 1577 | } else { |
||
| 1694 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: