silverstripe-cms/tests/php/Model/RedirectorPageTest.php

231 lines
8.6 KiB
PHP

<?php
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\RedirectorPageController;
use SilverStripe\Control\Director;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Dev\TestAssetStore;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
class RedirectorPageTest extends FunctionalTest
{
protected static $fixture_file = 'RedirectorPageTest.yml';
protected $autoFollowRedirection = false;
protected function setUp(): void
{
parent::setUp();
// Set backend root to /ImageTest
TestAssetStore::activate('FileTest');
// Create a test files for each of the fixture references
$fileIDs = $this->allFixtureIDs(File::class);
foreach ($fileIDs as $fileID) {
/** @var File $file */
$file = File::get()->byId($fileID);
$file->setFromString(str_repeat('x', 1000000), $file->getFilename());
$file->publishSingle();
}
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
// Ensure all pages are published
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
$page->publishSingle();
}
}
public function testGoodRedirectors()
{
// For good redirectors, the final destination URL will be returned
$this->assertEquals(
"http://www.google.com",
$this->objFromFixture(RedirectorPage::class, 'goodexternal')->Link()
);
$this->assertEquals(
"/redirection-dest",
$this->objFromFixture(RedirectorPage::class, 'goodinternal')->redirectionLink()
);
$this->assertEquals(
"/redirection-dest",
$this->objFromFixture(RedirectorPage::class, 'goodinternal')->Link()
);
}
public function provideEmptyRedirectors()
{
return [
'use 200' => [
'use404' => false,
],
'use 404' => [
'use404' => true,
],
];
}
/**
* @dataProvider provideEmptyRedirectors
*/
public function testEmptyRedirectors(bool $use404)
{
Config::modify()->set(RedirectorPageController::class, 'missing_redirect_is_404', $use404);
// If a redirector page is misconfigured, then its link method will just return the usual
// URLSegment-generated value
$page1 = $this->objFromFixture(RedirectorPage::class, 'badexternal');
$this->assertEquals('/bad-external', $page1->Link());
$response = $this->get($page1->Link());
$this->assertEquals(200, $response->getStatusCode());
// An error message will be shown if you visit it
$content = $this->get(Director::makeRelative($page1->Link()))->getBody();
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
// This also applies for internal links
$page2 = $this->objFromFixture(RedirectorPage::class, 'badinternal');
$this->assertEquals('/bad-internal', $page2->Link());
$response = $this->get(Director::makeRelative($page2->Link()));
$content = $response->getBody();
if ($use404) {
$this->assertNull($response->getBody());
} else {
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
}
}
public function testReflexiveAndTransitiveInternalRedirectors()
{
// Reflexive redirectors are those that point to themselves.
// They should behave the same as an empty redirector
$page = $this->objFromFixture(RedirectorPage::class, 'reflexive');
$this->assertEquals('/reflexive', $page->Link());
$content = $this->get(Director::makeRelative($page->Link()))->getBody();
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
// Transitive redirectors are those that point to another redirector page.
// They should send people to the URLSegment of the destination page - the middle-stop, so to speak.
// That should redirect to the final destination
$page = $this->objFromFixture(RedirectorPage::class, 'transitive');
$this->assertEquals('/good-internal', $page->Link());
$this->autoFollowRedirection = false;
$response = $this->get(Director::makeRelative($page->Link()));
$this->assertEquals(Director::absoluteURL('/redirection-dest'), $response->getHeader("Location"));
}
public function testExternalURLGetsPrefixIfNotSet()
{
$page = $this->objFromFixture(RedirectorPage::class, 'externalnoprefix');
$this->assertEquals($page->ExternalURL, 'http://google.com', 'onBeforeWrite has prefixed with http');
$page->write();
$this->assertEquals(
$page->ExternalURL,
'http://google.com',
'onBeforeWrite will not double prefix if written again!'
);
}
public function testAllowsProtocolRelative()
{
$noProtocol = new RedirectorPage(['ExternalURL' => 'mydomain.com']);
$noProtocol->write();
$this->assertEquals('http://mydomain.com', $noProtocol->ExternalURL);
$protocolAbsolute = new RedirectorPage(['ExternalURL' => 'http://mydomain.com']);
$protocolAbsolute->write();
$this->assertEquals('http://mydomain.com', $protocolAbsolute->ExternalURL);
$protocolRelative = new RedirectorPage(['ExternalURL' => '//mydomain.com']);
$protocolRelative->write();
$this->assertEquals('//mydomain.com', $protocolRelative->ExternalURL);
}
/**
* Test that we can trigger a redirection before RedirectorPageController::init() is called
*/
public function testRedirectRespectsFinishedResponse()
{
$page = $this->objFromFixture(RedirectorPage::class, 'goodinternal');
RedirectorPageController::add_extension(RedirectorPageTest_RedirectExtension::class);
$response = $this->get($page->regularLink());
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals('http://www.mysite.com/foo', $response->getHeader('Location'));
RedirectorPageController::remove_extension(RedirectorPageTest_RedirectExtension::class);
}
public function testNoJSLinksAllowed()
{
$page = new RedirectorPage();
$js = 'javascript:alert("hello world")';
$page->ExternalURL = $js;
$this->assertEquals($js, $page->ExternalURL);
$page->write();
$this->assertEmpty($page->ExternalURL);
}
public function testFileRedirector()
{
$page = $this->objFromFixture(RedirectorPage::class, 'file');
$this->assertStringContainsString("FileTest.txt", $page->Link());
}
public function provideUnpublishedTarget()
{
return [
'use 200 with sitetree' => [
'use404' => false,
'isFile' => false,
],
'use 404 with sitetree' => [
'use404' => true,
'isFile' => false,
],
'use 200 with file' => [
'use404' => false,
'isFile' => true,
],
'use 404 with file' => [
'use404' => true,
'isFile' => true,
],
];
}
/**
* @dataProvider provideUnpublishedTarget
*/
public function testUnpublishedTarget(bool $use404, bool $isFile)
{
Config::modify()->set(RedirectorPageController::class, 'missing_redirect_is_404', $use404);
$redirectorPage = $this->objFromFixture(RedirectorPage::class, $isFile ? 'file' : 'goodinternal');
$targetModel = $isFile ? $redirectorPage->LinkToFile() : $redirectorPage->LinkTo();
$targetModel->publishSingle();
$redirectorPage->publishSingle();
$this->assertEquals(Controller::normaliseTrailingSlash($isFile ? '/filedirector' : '/good-internal'), $redirectorPage->regularLink());
$redirectorPageLink = Director::makeRelative($redirectorPage->regularLink());
// redirector page should give 301 (redirection) status code
$response = $this->get($redirectorPageLink);
$this->assertEquals(301, $response->getStatusCode());
// Unpublish the target model of this redirector page.
$targetModel->doUnpublish();
// redirector page should give a 404 or a 200 based on config when there's no page to redirect to
$response = $this->get($redirectorPageLink);
$this->assertEquals($use404 ? 404 : 200, $response->getStatusCode());
}
}