assertEquals(['mytheme', '$default'], SSViewer::get_themes()); // Ensure add_themes() prepends SSViewer::add_themes(['my_more_important_theme']); $this->assertEquals(['my_more_important_theme', 'mytheme', '$default'], SSViewer::get_themes()); // Ensure add_themes() on theme already in cascade promotes it to the top SSViewer::add_themes(['mytheme']); $this->assertEquals(['mytheme', 'my_more_important_theme', '$default'], SSViewer::get_themes()); } public function testRequirementsInjected() { Requirements::clear(); try { Requirements::customCSS('pretend this is real css'); $viewer = new SSViewer([], new DummyTemplateEngine()); $result1 = $viewer->process('pretend this is a model')->getValue(); // if we disable the requirements then we should get nothing $viewer->includeRequirements(false); $result2 = $viewer->process('pretend this is a model')->getValue(); } finally { Requirements::restore(); } $this->assertEqualIgnoringWhitespace( '', $result1 ); $this->assertEqualIgnoringWhitespace( '', $result2 ); } public function testGetTemplatesByClass() { $this->useTestTheme( __DIR__ . '/SSViewerTest', 'layouttest', function () { // Test passing a string $templates = SSViewer::get_templates_by_class( SSViewerTestModelController::class, '', Controller::class ); $this->assertEquals( [ SSViewerTestModelController::class, [ 'type' => 'Includes', SSViewerTestModelController::class, ], SSViewerTestModel::class, Controller::class, [ 'type' => 'Includes', Controller::class, ], ], $templates ); // Test to ensure we're stopping at the base class. $templates = SSViewer::get_templates_by_class( SSViewerTestModelController::class, '', SSViewerTestModelController::class ); $this->assertEquals( [ SSViewerTestModelController::class, [ 'type' => 'Includes', SSViewerTestModelController::class, ], SSViewerTestModel::class, ], $templates ); // Make sure we can search templates by suffix. $templates = SSViewer::get_templates_by_class( SSViewerTestModel::class, 'Controller', DataObject::class ); $this->assertEquals( [ SSViewerTestModelController::class, [ 'type' => 'Includes', SSViewerTestModelController::class, ], DataObject::class . 'Controller', [ 'type' => 'Includes', DataObject::class . 'Controller', ], ], $templates ); // Let's throw something random in there. $this->expectException(\InvalidArgumentException::class); SSViewer::get_templates_by_class('no-class'); } ); } public function testRewriteHashlinks() { SSViewer::setRewriteHashLinksDefault(true); $oldServerVars = $_SERVER; try { $_SERVER['REQUEST_URI'] = '//file.com?foo"onclick="alert(\'xss\')""'; // Note that leading double slashes have been rewritten to prevent these being mis-interepreted // as protocol-less absolute urls $base = Convert::raw2att('/file.com?foo"onclick="alert(\'xss\')""'); $engine = new DummyTemplateEngine(); $engine->setOutput( ' ExternalInlineLink ExternalInsertedLink InlineLink InsertedLink ' ); $tmpl = new SSViewer([], $engine); $result = $tmpl->process('pretend this is a model'); $this->assertStringContainsString( 'InsertedLink', $result ); $this->assertStringContainsString( 'ExternalInsertedLink', $result ); $this->assertStringContainsString( 'InlineLink', $result ); $this->assertStringContainsString( 'ExternalInlineLink', $result ); $this->assertStringContainsString( '', $result, 'SSTemplateParser should only rewrite anchor hrefs' ); } finally { $_SERVER = $oldServerVars; } } public function testRewriteHashlinksInPhpMode() { SSViewer::setRewriteHashLinksDefault('php'); $engine = new DummyTemplateEngine(); $engine->setOutput( ' InlineLink InsertedLink ' ); $tmpl = new SSViewer([], $engine); $result = $tmpl->process('pretend this is a model'); $code = <<<'EOC' #anchor">InsertedLink EOC; $this->assertStringContainsString($code, $result); $this->assertStringContainsString( '', $result, 'SSTemplateParser should only rewrite anchor hrefs' ); } private function assertEqualIgnoringWhitespace(string $a, string $b, string $message = ''): void { $this->assertEquals(preg_replace('/\s+/', '', $a ), preg_replace('/\s+/', '', $b), $message); } }