From fa5f7cc8d2603746476dd73e2e7091100d368bbf Mon Sep 17 00:00:00 2001
From: Steve Boyd
+ * public function testMyForm() {
+ * // Visit a URL
+ * $this->get("your/url");
+ *
+ * // Submit a form on the page that you get in response
+ * $this->submitForm("MyForm_ID", "action_dologin", array("Email" => "invalid email ^&*&^"));
+ *
+ * // Validate the content that is returned
+ * $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));
+ * }
+ *
+ */
+ // Ignore multiple classes in same file
+ // @codingStandardsIgnoreStart
+ class FunctionalTest extends SapphireTest implements TestOnly
+ {
+ // @codingStandardsIgnoreEnd
+ /**
+ * Set this to true on your sub-class to disable the use of themes in this test.
+ * This can be handy for functional testing of modules without having to worry about whether a user has changed
+ * behaviour by replacing the theme.
+ *
+ * @var bool
+ */
+ protected static $disable_themes = false;
+
+ /**
+ * Set this to true on your sub-class to use the draft site by default for every test in this class.
+ *
+ * @deprecated 4.2.0:5.0.0 Use ?stage=Stage in your ->get() querystring requests instead
+ * @var bool
+ */
+ protected static $use_draft_site = false;
+
+ /**
+ * @var TestSession
+ */
+ protected $mainSession = null;
+
+ /**
+ * CSSContentParser for the most recently requested page.
+ *
+ * @var CSSContentParser
+ */
+ protected $cssParser = null;
+
+ /**
+ * If this is true, then 30x Location headers will be automatically followed.
+ * If not, then you will have to manaully call $this->mainSession->followRedirection() to follow them.
+ * However, this will let you inspect the intermediary headers
+ *
+ * @var bool
+ */
+ protected $autoFollowRedirection = true;
+
+ /**
+ * Returns the {@link Session} object for this test
+ *
+ * @return Session
+ */
+ public function session()
+ {
+ return $this->mainSession->session();
+ }
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ // Skip calling FunctionalTest directly.
+ if (static::class == __CLASS__) {
+ $this->markTestSkipped(sprintf('Skipping %s ', static::class));
+ }
+
+ $this->mainSession = new TestSession();
+
+ // Disable theme, if necessary
+ if (static::get_disable_themes()) {
+ SSViewer::config()->update('theme_enabled', false);
+ }
+
+ // Flush user
+ $this->logOut();
+
+ // Switch to draft site, if necessary
+ // If you rely on this you should be crafting stage-specific urls instead though.
+ if (static::get_use_draft_site()) {
+ $this->useDraftSite();
+ }
+
+ // Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case
+ // basis.
+ BasicAuth::protect_entire_site(false);
+
+ SecurityToken::disable();
+ }
+
+ protected function tearDown(): void
+ {
+ SecurityToken::enable();
+ unset($this->mainSession);
+ parent::tearDown();
+ }
+
+ /**
+ * Run a test while mocking the base url with the provided value
+ * @param string $url The base URL to use for this test
+ * @param callable $callback The test to run
+ */
+ protected function withBaseURL($url, $callback)
+ {
+ $oldBase = Config::inst()->get(Director::class, 'alternate_base_url');
+ Config::modify()->set(Director::class, 'alternate_base_url', $url);
+ $callback($this);
+ Config::modify()->set(Director::class, 'alternate_base_url', $oldBase);
+ }
+
+ /**
+ * Run a test while mocking the base folder with the provided value
+ * @param string $folder The base folder to use for this test
+ * @param callable $callback The test to run
+ */
+ protected function withBaseFolder($folder, $callback)
+ {
+ $oldFolder = Config::inst()->get(Director::class, 'alternate_base_folder');
+ Config::modify()->set(Director::class, 'alternate_base_folder', $folder);
+ $callback($this);
+ Config::modify()->set(Director::class, 'alternate_base_folder', $oldFolder);
+ }
+
+ /**
+ * Submit a get request
+ * @uses Director::test()
+ *
+ * @param string $url
+ * @param Session $session
+ * @param array $headers
+ * @param array $cookies
+ * @return HTTPResponse
+ */
+ public function get($url, $session = null, $headers = null, $cookies = null)
+ {
+ $this->cssParser = null;
+ $response = $this->mainSession->get($url, $session, $headers, $cookies);
+ if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
+ $response = $this->mainSession->followRedirection();
+ }
+ return $response;
+ }
+
+ /**
+ * Submit a post request
+ *
+ * @uses Director::test()
+ * @param string $url
+ * @param array $data
+ * @param array $headers
+ * @param Session $session
+ * @param string $body
+ * @param array $cookies
+ * @return HTTPResponse
+ */
+ public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null)
+ {
+ $this->cssParser = null;
+ $response = $this->mainSession->post($url, $data, $headers, $session, $body, $cookies);
+ if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
+ $response = $this->mainSession->followRedirection();
+ }
+ return $response;
+ }
+
+ /**
+ * Submit the form with the given HTML ID, filling it out with the given data.
+ * Acts on the most recent response.
+ *
+ * Any data parameters have to be present in the form, with exact form field name
+ * and values, otherwise they are removed from the submission.
+ *
+ * Caution: Parameter names have to be formatted
+ * as they are in the form submission, not as they are interpreted by PHP.
+ * Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
+ * Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
+ *
+ * @see http://www.simpletest.org/en/form_testing_documentation.html
+ *
+ * @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
+ * @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
+ * @param array $data Map of GET/POST data.
+ * @return HTTPResponse
+ */
+ public function submitForm($formID, $button = null, $data = [])
+ {
+ $this->cssParser = null;
+ $response = $this->mainSession->submitForm($formID, $button, $data);
+ if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
+ $response = $this->mainSession->followRedirection();
+ }
+ return $response;
+ }
+
+ /**
+ * Return the most recent content
+ *
+ * @return string
+ */
+ public function content()
+ {
+ return $this->mainSession->lastContent();
+ }
+
+ /**
+ * Find an attribute in a SimpleXMLElement object by name.
+ * @param SimpleXMLElement $object
+ * @param string $attribute Name of attribute to find
+ * @return SimpleXMLElement object of the attribute
+ */
+ public function findAttribute($object, $attribute)
+ {
+ $found = false;
+ foreach ($object->attributes() as $a => $b) {
+ if ($a == $attribute) {
+ $found = $b;
+ }
+ }
+ return $found;
+ }
+
+ /**
+ * Return a CSSContentParser for the most recent content.
+ *
+ * @return CSSContentParser
+ */
+ public 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.
+ *
+ * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
+ * @param array|string $expectedMatches The content of at least one of the matched tags
+ * @param string $message
+ * @throws AssertionFailedError
+ */
+ public function assertPartialMatchBySelector($selector, $expectedMatches, $message = null)
+ {
+ if (is_string($expectedMatches)) {
+ $expectedMatches = [$expectedMatches];
+ }
+
+ $items = $this->cssParser()->getBySelector($selector);
+
+ $actuals = [];
+ if ($items) {
+ foreach ($items as $item) {
+ $actuals[trim(preg_replace('/\s+/', ' ', (string)$item))] = true;
+ }
+ }
+
+ $message = $message ?:
+ "Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
+ . implode("'\n'", $expectedMatches) . "'\n\n"
+ . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'";
+
+ foreach ($expectedMatches as $match) {
+ $this->assertTrue(isset($actuals[$match]), $message);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
+ * @param array|string $expectedMatches The content of *all* matching tags as an array
+ * @param string $message
+ * @throws AssertionFailedError
+ */
+ public function assertExactMatchBySelector($selector, $expectedMatches, $message = null)
+ {
+ if (is_string($expectedMatches)) {
+ $expectedMatches = [$expectedMatches];
+ }
+
+ $items = $this->cssParser()->getBySelector($selector);
+
+ $actuals = [];
+ if ($items) {
+ foreach ($items as $item) {
+ $actuals[] = trim(preg_replace('/\s+/', ' ', (string)$item));
+ }
+ }
+
+ $message = $message ?:
+ "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) . "'";
+
+ $this->assertTrue($expectedMatches == $actuals, $message);
+ }
+
+ /**
+ * 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.
+ *
+ * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
+ * @param array|string $expectedMatches The content of at least one of the matched tags
+ * @param string $message
+ * @throws AssertionFailedError
+ */
+ public function assertPartialHTMLMatchBySelector($selector, $expectedMatches, $message = null)
+ {
+ if (is_string($expectedMatches)) {
+ $expectedMatches = [$expectedMatches];
+ }
+
+ $items = $this->cssParser()->getBySelector($selector);
+
+ $actuals = [];
+ if ($items) {
+ /** @var SimpleXMLElement $item */
+ foreach ($items as $item) {
+ $actuals[$item->asXML()] = true;
+ }
+ }
+
+ $message = $message ?:
+ "Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
+ . implode("'\n'", $expectedMatches) . "'\n\n"
+ . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'";
+
+ foreach ($expectedMatches as $match) {
+ $this->assertTrue(isset($actuals[$match]), $message);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
+ * @param array|string $expectedMatches The content of *all* matched tags as an array
+ * @param string $message
+ * @throws AssertionFailedError
+ */
+ public function assertExactHTMLMatchBySelector($selector, $expectedMatches, $message = null)
+ {
+ $items = $this->cssParser()->getBySelector($selector);
+
+ $actuals = [];
+ if ($items) {
+ /** @var SimpleXMLElement $item */
+ foreach ($items as $item) {
+ $actuals[] = $item->asXML();
+ }
+ }
+
+ $message = $message ?:
+ "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) . "'";
+
+ $this->assertTrue($expectedMatches == $actuals, $message);
+ }
+
+ /**
+ * Use the draft (stage) site for testing.
+ * This is helpful if you're not testing publication functionality and don't want "stage management" cluttering
+ * your test.
+ *
+ * @deprecated 4.2.0:5.0.0 Use ?stage=Stage querystring arguments instead of useDraftSite
+ * @param bool $enabled toggle the use of the draft site
+ */
+ public function useDraftSite($enabled = true)
+ {
+ Deprecation::notice('5.0', 'Use ?stage=Stage querystring arguments instead of useDraftSite');
+ if ($enabled) {
+ $this->session()->set('readingMode', 'Stage.Stage');
+ $this->session()->set('unsecuredDraftSite', true);
+ } else {
+ $this->session()->clear('readingMode');
+ $this->session()->clear('unsecuredDraftSite');
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ public static function get_disable_themes()
+ {
+ return static::$disable_themes;
+ }
+
+ /**
+ * @deprecated 4.2.0:5.0.0 Use ?stage=Stage in your querystring arguments instead
+ * @return bool
+ */
+ public static function get_use_draft_site()
+ {
+ return static::$use_draft_site;
+ }
+ }
+}
+
+/* -------------------------------------------------
+ *
+ * This version of FunctionalTest is for PHPUnit 5
+ * The PHPUnit 9 version is at the top of this file
+ *
+ * PHPUnit_Extensions_GroupTestSuite is a class that only exists in PHPUnit 5
+ *
+ * -------------------------------------------------
+ */
+if (!class_exists(PHPUnit_Extensions_GroupTestSuite::class)) {
+ return;
+}
+
/**
* SilverStripe-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.
@@ -31,8 +491,11 @@
* }
*
*/
+// Ignore multiple classes in same file
+// @codingStandardsIgnoreStart
class FunctionalTest extends SapphireTest implements TestOnly
{
+ // @codingStandardsIgnoreEnd
/**
* Set this to true on your sub-class to disable the use of themes in this test.
* This can be handy for functional testing of modules without having to worry about whether a user has changed
diff --git a/src/Dev/SapphireTest.php b/src/Dev/SapphireTest.php
index fb987006b55..2a0d24622f2 100644
--- a/src/Dev/SapphireTest.php
+++ b/src/Dev/SapphireTest.php
@@ -1,12 +1,21 @@
" notation
+ * between the fixtures, they act independent of each other.
+ *
+ * @var string|array
+ */
+ protected static $fixture_file = null;
+
+ /**
+ * @deprecated 4.0..5.0 Use FixtureTestState instead
+ * @var FixtureFactory
+ */
+ protected $fixtureFactory;
+
+ /**
+ * @var Boolean If set to TRUE, this will force a test database to be generated
+ * in {@link setUp()}. Note that this flag is overruled by the presence of a
+ * {@link $fixture_file}, which always forces a database build.
+ *
+ * @var bool
+ */
+ protected $usesDatabase = null;
+
+ /**
+ * This test will cleanup its state via transactions.
+ * If set to false a full schema is forced between tests, but at a performance cost.
+ *
+ * @var bool
+ */
+ protected $usesTransactions = true;
+
+ /**
+ * @var bool
+ */
+ protected static $is_running_test = false;
+
+ /**
+ * By default, setUp() does not require default records. Pass
+ * class names in here, and the require/augment default records
+ * function will be called on them.
+ *
+ * @var array
+ */
+ protected $requireDefaultRecordsFrom = [];
+
+ /**
+ * A list of extensions that can't be applied during the execution of this run. If they are
+ * applied, they will be temporarily removed and a database migration called.
+ *
+ * The keys of the are the classes that the extensions can't be applied the extensions to, and
+ * the values are an array of illegal extensions on that class.
+ *
+ * Set a class to `*` to remove all extensions (unadvised)
+ *
+ * @var array
+ */
+ protected static $illegal_extensions = [];
+
+ /**
+ * A list of extensions that must be applied during the execution of this run. If they are
+ * not applied, they will be temporarily added and a database migration called.
+ *
+ * The keys of the are the classes to apply the extensions to, and the values are an array
+ * of required extensions on that class.
+ *
+ * Example:
+ *
+ * array("MyTreeDataObject" => array("Versioned", "Hierarchy"))
+ *
+ *
+ * @var array
+ */
+ protected static $required_extensions = [];
+
+ /**
+ * By default, the test database won't contain any DataObjects that have the interface TestOnly.
+ * This variable lets you define additional TestOnly DataObjects to set up for this test.
+ * Set it to an array of DataObject subclass names.
+ *
+ * @var array
+ */
+ protected static $extra_dataobjects = [];
+
+ /**
+ * List of class names of {@see Controller} objects to register routes for
+ * Controllers must implement Link() method
+ *
+ * @var array
+ */
+ protected static $extra_controllers = [];
+
+ /**
+ * We need to disabling backing up of globals to avoid overriding
+ * the few globals SilverStripe relies on, like $lang for the i18n subsystem.
+ *
+ * @see http://sebastian-bergmann.de/archives/797-Global-Variables-and-PHPUnit.html
+ */
+ protected $backupGlobals = false;
+
+ /**
+ * State management container for SapphireTest
+ *
+ * @var SapphireTestState
+ */
+ protected static $state = null;
+
+ /**
+ * Temp database helper
+ *
+ * @var TempDatabase
+ */
+ protected static $tempDB = null;
+
+ /**
+ * @return TempDatabase
+ */
+ public static function tempDB()
+ {
+ if (!class_exists(TempDatabase::class)) {
+ return null;
+ }
+
+ if (!static::$tempDB) {
+ static::$tempDB = TempDatabase::create();
+ }
+ return static::$tempDB;
+ }
+
+ /**
+ * Gets illegal extensions for this class
+ *
+ * @return array
+ */
+ public static function getIllegalExtensions()
+ {
+ return static::$illegal_extensions;
+ }
+
+ /**
+ * Gets required extensions for this class
+ *
+ * @return array
+ */
+ public static function getRequiredExtensions()
+ {
+ return static::$required_extensions;
+ }
+
+ /**
+ * Check if test bootstrapping has been performed. Must not be relied on
+ * outside of unit tests.
+ *
+ * @return bool
+ */
+ protected static function is_running_test()
+ {
+ return self::$is_running_test;
+ }
+
+ /**
+ * Set test running state
+ *
+ * @param bool $bool
+ */
+ protected static function set_is_running_test($bool)
+ {
+ self::$is_running_test = $bool;
+ }
+
+ /**
+ * @return String
+ */
+ public static function get_fixture_file()
+ {
+ return static::$fixture_file;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getUsesDatabase()
+ {
+ return $this->usesDatabase;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getUsesTransactions()
+ {
+ return $this->usesTransactions;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRequireDefaultRecordsFrom()
+ {
+ return $this->requireDefaultRecordsFrom;
+ }
+
+ /**
+ * Setup the test.
+ * Always sets up in order:
+ * - Reset php state
+ * - Nest
+ * - Custom state helpers
+ *
+ * User code should call parent::setUp() before custom setup code
+ */
+ protected function setUp(): void
+ {
+ if (!defined('FRAMEWORK_PATH')) {
+ trigger_error(
+ 'Missing constants, did you remember to include the test bootstrap in your phpunit.xml file?',
+ E_USER_WARNING
+ );
+ }
+
+ // Call state helpers
+ static::$state->setUp($this);
+
+ // We cannot run the tests on this abstract class.
+ if (static::class == __CLASS__) {
+ $this->markTestSkipped(sprintf('Skipping %s ', static::class));
+ }
+
+ // i18n needs to be set to the defaults or tests fail
+ if (class_exists(i18n::class)) {
+ i18n::set_locale(i18n::config()->uninherited('default_locale'));
+ }
+
+ // Set default timezone consistently to avoid NZ-specific dependencies
+ date_default_timezone_set('UTC');
+
+ if (class_exists(Member::class)) {
+ Member::set_password_validator(null);
+ }
+
+ if (class_exists(Cookie::class)) {
+ Cookie::config()->update('report_errors', false);
+ }
+
+ if (class_exists(RootURLController::class)) {
+ RootURLController::reset();
+ }
+
+ if (class_exists(Security::class)) {
+ Security::clear_database_is_ready();
+ }
+
+ // Set up test routes
+ $this->setUpRoutes();
+
+ $fixtureFiles = $this->getFixturePaths();
+
+ if ($this->shouldSetupDatabaseForCurrentTest($fixtureFiles)) {
+ // Assign fixture factory to deprecated prop in case old tests use it over the getter
+ /** @var FixtureTestState $fixtureState */
+ $fixtureState = static::$state->getStateByName('fixtures');
+ $this->fixtureFactory = $fixtureState->getFixtureFactory(static::class);
+
+ $this->logInWithPermission('ADMIN');
+ }
+
+ // turn off template debugging
+ if (class_exists(SSViewer::class)) {
+ SSViewer::config()->update('source_file_comments', false);
+ }
+
+ // Set up the test mailer
+ if (class_exists(TestMailer::class)) {
+ Injector::inst()->registerService(new TestMailer(), Mailer::class);
+ }
+
+ if (class_exists(Email::class)) {
+ Email::config()->remove('send_all_emails_to');
+ Email::config()->remove('send_all_emails_from');
+ Email::config()->remove('cc_all_emails_to');
+ Email::config()->remove('bcc_all_emails_to');
+ }
+ }
+
+
+ /**
+ * Helper method to determine if the current test should enable a test database
+ *
+ * @param $fixtureFiles
+ * @return bool
+ */
+ protected function shouldSetupDatabaseForCurrentTest($fixtureFiles)
+ {
+ $databaseEnabledByDefault = $fixtureFiles || $this->usesDatabase;
+
+ return ($databaseEnabledByDefault && !$this->currentTestDisablesDatabase())
+ || $this->currentTestEnablesDatabase();
+ }
+
+ /**
+ * Helper method to check, if the current test uses the database.
+ * This can be switched on with the annotation "@useDatabase"
+ *
+ * @return bool
+ */
+ protected function currentTestEnablesDatabase()
+ {
+ $annotations = $this->getAnnotations();
+
+ return array_key_exists('useDatabase', $annotations['method'])
+ && $annotations['method']['useDatabase'][0] !== 'false';
+ }
+
+ /**
+ * Helper method to check, if the current test uses the database.
+ * This can be switched on with the annotation "@useDatabase false"
+ *
+ * @return bool
+ */
+ protected function currentTestDisablesDatabase()
+ {
+ $annotations = $this->getAnnotations();
+
+ return array_key_exists('useDatabase', $annotations['method'])
+ && $annotations['method']['useDatabase'][0] === 'false';
+ }
+
+ /**
+ * Called once per test case ({@link SapphireTest} subclass).
+ * This is different to {@link setUp()}, which gets called once
+ * per method. Useful to initialize expensive operations which
+ * don't change state for any called method inside the test,
+ * e.g. dynamically adding an extension. See {@link teardownAfterClass()}
+ * for tearing down the state again.
+ *
+ * Always sets up in order:
+ * - Reset php state
+ * - Nest
+ * - Custom state helpers
+ *
+ * User code should call parent::setUpBeforeClass() before custom setup code
+ *
+ * @throws Exception
+ */
+ public static function setUpBeforeClass(): void
+ {
+ // Start tests
+ static::start();
+
+ if (!static::$state) {
+ throw new Exception('SapphireTest failed to bootstrap!');
+ }
+
+ // Call state helpers
+ static::$state->setUpOnce(static::class);
+
+ // Build DB if we have objects
+ if (class_exists(DataObject::class) && static::getExtraDataObjects()) {
+ DataObject::reset();
+ static::resetDBSchema(true, true);
+ }
+ }
+
+ /**
+ * tearDown method that's called once per test class rather once per test method.
+ *
+ * Always sets up in order:
+ * - Custom state helpers
+ * - Unnest
+ * - Reset php state
+ *
+ * User code should call parent::tearDownAfterClass() after custom tear down code
+ */
+ public static function tearDownAfterClass(): void
+ {
+ // Call state helpers
+ static::$state->tearDownOnce(static::class);
+
+ // Reset DB schema
+ static::resetDBSchema();
+ }
+
+ /**
+ * @return FixtureFactory|false
+ * @deprecated 4.0.0:5.0.0
+ */
+ public function getFixtureFactory()
+ {
+ Deprecation::notice('5.0', __FUNCTION__ . ' is deprecated, use ' . FixtureTestState::class . ' instead');
+ /** @var FixtureTestState $state */
+ $state = static::$state->getStateByName('fixtures');
+ return $state->getFixtureFactory(static::class);
+ }
+
+ /**
+ * Sets a new fixture factory
+ * @param FixtureFactory $factory
+ * @return $this
+ * @deprecated 4.0.0:5.0.0
+ */
+ public function setFixtureFactory(FixtureFactory $factory)
+ {
+ Deprecation::notice('5.0', __FUNCTION__ . ' is deprecated, use ' . FixtureTestState::class . ' instead');
+ /** @var FixtureTestState $state */
+ $state = static::$state->getStateByName('fixtures');
+ $state->setFixtureFactory($factory, static::class);
+ $this->fixtureFactory = $factory;
+ return $this;
+ }
+
+ /**
+ * Get the ID of an object from the fixture.
+ *
+ * @param string $className The data class or table name, as specified in your fixture file. Parent classes won't work
+ * @param string $identifier The identifier string, as provided in your fixture file
+ * @return int
+ */
+ protected function idFromFixture($className, $identifier)
+ {
+ /** @var FixtureTestState $state */
+ $state = static::$state->getStateByName('fixtures');
+ $id = $state->getFixtureFactory(static::class)->getId($className, $identifier);
+
+ if (!$id) {
+ throw new InvalidArgumentException(sprintf(
+ "Couldn't find object '%s' (class: %s)",
+ $identifier,
+ $className
+ ));
+ }
+
+ return $id;
+ }
+
+ /**
+ * Return all of the IDs in the fixture of a particular class name.
+ * Will collate all IDs form all fixtures if multiple fixtures are provided.
+ *
+ * @param string $className The data class or table name, as specified in your fixture file
+ * @return array A map of fixture-identifier => object-id
+ */
+ protected function allFixtureIDs($className)
+ {
+ /** @var FixtureTestState $state */
+ $state = static::$state->getStateByName('fixtures');
+ return $state->getFixtureFactory(static::class)->getIds($className);
+ }
+
+ /**
+ * Get an object from the fixture.
+ *
+ * @param string $className The data class or table name, as specified in your fixture file. Parent classes won't work
+ * @param string $identifier The identifier string, as provided in your fixture file
+ *
+ * @return DataObject
+ */
+ protected function objFromFixture($className, $identifier)
+ {
+ /** @var FixtureTestState $state */
+ $state = static::$state->getStateByName('fixtures');
+ $obj = $state->getFixtureFactory(static::class)->get($className, $identifier);
+
+ if (!$obj) {
+ throw new InvalidArgumentException(sprintf(
+ "Couldn't find object '%s' (class: %s)",
+ $identifier,
+ $className
+ ));
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Load a YAML fixture file into the database.
+ * Once loaded, you can use idFromFixture() and objFromFixture() to get items from the fixture.
+ * Doesn't clear existing fixtures.
+ * @param string $fixtureFile The location of the .yml fixture file, relative to the site base dir
+ * @deprecated 4.0.0:5.0.0
+ *
+ */
+ public function loadFixture($fixtureFile)
+ {
+ Deprecation::notice('5.0', __FUNCTION__ . ' is deprecated, use ' . FixtureTestState::class . ' instead');
+ $fixture = Injector::inst()->create(YamlFixture::class, $fixtureFile);
+ $fixture->writeInto($this->getFixtureFactory());
+ }
+
+ /**
+ * Clear all fixtures which were previously loaded through
+ * {@link loadFixture()}
+ */
+ public function clearFixtures()
+ {
+ /** @var FixtureTestState $state */
+ $state = static::$state->getStateByName('fixtures');
+ $state->getFixtureFactory(static::class)->clear();
+ }
+
+ /**
+ * Useful for writing unit tests without hardcoding folder structures.
+ *
+ * @return string Absolute path to current class.
+ */
+ protected function getCurrentAbsolutePath()
+ {
+ $filename = ClassLoader::inst()->getItemPath(static::class);
+ if (!$filename) {
+ throw new LogicException('getItemPath returned null for ' . static::class
+ . '. Try adding flush=1 to the test run.');
+ }
+ return dirname($filename);
+ }
+
+ /**
+ * @return string File path relative to webroot
+ */
+ protected function getCurrentRelativePath()
+ {
+ $base = Director::baseFolder();
+ $path = $this->getCurrentAbsolutePath();
+ if (substr($path, 0, strlen($base)) == $base) {
+ $path = preg_replace('/^\/*/', '', substr($path, strlen($base)));
+ }
+ return $path;
+ }
+
+ /**
+ * Setup the test.
+ * Always sets up in order:
+ * - Custom state helpers
+ * - Unnest
+ * - Reset php state
+ *
+ * User code should call parent::tearDown() after custom tear down code
+ */
+ protected function tearDown(): void
+ {
+ // Reset mocked datetime
+ if (class_exists(DBDatetime::class)) {
+ DBDatetime::clear_mock_now();
+ }
+
+ // Stop the redirection that might have been requested in the test.
+ // Note: Ideally a clean Controller should be created for each test.
+ // Now all tests executed in a batch share the same controller.
+ if (class_exists(Controller::class)) {
+ $controller = Controller::has_curr() ? Controller::curr() : null;
+ if ($controller && ($response = $controller->getResponse()) && $response->getHeader('Location')) {
+ $response->setStatusCode(200);
+ $response->removeHeader('Location');
+ }
+ }
+
+ // Call state helpers
+ static::$state->tearDown($this);
+ }
+
+ /**
+ * Clear the log of emails sent
+ *
+ * @return bool True if emails cleared
+ */
+ public function clearEmails()
+ {
+ /** @var Mailer $mailer */
+ $mailer = Injector::inst()->get(Mailer::class);
+ if ($mailer instanceof TestMailer) {
+ $mailer->clearEmails();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Search for an email that was sent.
+ * All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
+ * @param string $to
+ * @param string $from
+ * @param string $subject
+ * @param string $content
+ * @return array|null Contains keys: 'Type', 'To', 'From', 'Subject', 'Content', 'PlainContent', 'AttachedFiles',
+ * 'HtmlContent'
+ */
+ public static function findEmail($to, $from = null, $subject = null, $content = null)
+ {
+ /** @var Mailer $mailer */
+ $mailer = Injector::inst()->get(Mailer::class);
+ if ($mailer instanceof TestMailer) {
+ return $mailer->findEmail($to, $from, $subject, $content);
+ }
+ return null;
+ }
+
+ /**
+ * Assert that the matching email was sent since the last call to clearEmails()
+ * All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
+ *
+ * @param string $to
+ * @param string $from
+ * @param string $subject
+ * @param string $content
+ */
+ public static function assertEmailSent($to, $from = null, $subject = null, $content = null)
+ {
+ $found = (bool)static::findEmail($to, $from, $subject, $content);
+
+ $infoParts = '';
+ $withParts = [];
+ if ($to) {
+ $infoParts .= " to '$to'";
+ }
+ if ($from) {
+ $infoParts .= " from '$from'";
+ }
+ if ($subject) {
+ $withParts[] = "subject '$subject'";
+ }
+ if ($content) {
+ $withParts[] = "content '$content'";
+ }
+ if ($withParts) {
+ $infoParts .= ' with ' . implode(' and ', $withParts);
+ }
+
+ static::assertTrue(
+ $found,
+ "Failed asserting that an email was sent$infoParts."
+ );
+ }
+
+
+ /**
+ * Assert that the given {@link SS_List} includes DataObjects matching the given key-value
+ * pairs. Each match must correspond to 1 distinct record.
+ *
+ * @param SS_List|array $matches The patterns to match. Each pattern is a map of key-value pairs. You can
+ * either pass a single pattern or an array of patterns.
+ * @param SS_List $list The {@link SS_List} to test.
+ * @param string $message
+ *
+ * Examples
+ * --------
+ * Check that $members includes an entry with Email = sam@example.com:
+ * $this->assertListContains(['Email' => '...@example.com'], $members);
+ *
+ * Check that $members includes entries with Email = sam@example.com and with
+ * Email = ingo@example.com:
+ * $this->assertListContains([
+ * ['Email' => '...@example.com'],
+ * ['Email' => 'i...@example.com'],
+ * ], $members);
+ */
+ public static function assertListContains($matches, SS_List $list, $message = '')
+ {
+ if (!is_array($matches)) {
+ throw self::createInvalidArgumentException(
+ 1,
+ 'array'
+ );
+ }
+
+ static::assertThat(
+ $list,
+ new SSListContains(
+ $matches
+ ),
+ $message
+ );
+ }
+
+ /**
+ * @param $matches
+ * @param $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListContains() instead
+ *
+ */
+ public function assertDOSContains($matches, $dataObjectSet)
+ {
+ Deprecation::notice('5.0', 'Use assertListContains() instead');
+ static::assertListContains($matches, $dataObjectSet);
+ }
+
+ /**
+ * Asserts that no items in a given list appear in the given dataobject list
+ *
+ * @param SS_List|array $matches The patterns to match. Each pattern is a map of key-value pairs. You can
+ * either pass a single pattern or an array of patterns.
+ * @param SS_List $list The {@link SS_List} to test.
+ * @param string $message
+ *
+ * Examples
+ * --------
+ * Check that $members doesn't have an entry with Email = sam@example.com:
+ * $this->assertListNotContains(['Email' => '...@example.com'], $members);
+ *
+ * Check that $members doesn't have entries with Email = sam@example.com and with
+ * Email = ingo@example.com:
+ * $this->assertListNotContains([
+ * ['Email' => '...@example.com'],
+ * ['Email' => 'i...@example.com'],
+ * ], $members);
+ */
+ public static function assertListNotContains($matches, SS_List $list, $message = '')
+ {
+ if (!is_array($matches)) {
+ throw self::createInvalidArgumentException(
+ 1,
+ 'array'
+ );
+ }
+
+ $constraint = new LogicalNot(
+ new SSListContains(
+ $matches
+ )
+ );
+
+ static::assertThat(
+ $list,
+ $constraint,
+ $message
+ );
+ }
+
+ /**
+ * @param $matches
+ * @param $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListNotContains() instead
+ *
+ */
+ public static function assertNotDOSContains($matches, $dataObjectSet)
+ {
+ Deprecation::notice('5.0', 'Use assertListNotContains() instead');
+ static::assertListNotContains($matches, $dataObjectSet);
+ }
+
+ /**
+ * Assert that the given {@link SS_List} includes only DataObjects matching the given
+ * key-value pairs. Each match must correspond to 1 distinct record.
+ *
+ * Example
+ * --------
+ * Check that *only* the entries Sam Minnee and Ingo Schommer exist in $members. Order doesn't
+ * matter:
+ * $this->assertListEquals([
+ * ['FirstName' =>'Sam', 'Surname' => 'Minnee'],
+ * ['FirstName' => 'Ingo', 'Surname' => 'Schommer'],
+ * ], $members);
+ *
+ * @param mixed $matches The patterns to match. Each pattern is a map of key-value pairs. You can
+ * either pass a single pattern or an array of patterns.
+ * @param mixed $list The {@link SS_List} to test.
+ * @param string $message
+ */
+ public static function assertListEquals($matches, SS_List $list, $message = '')
+ {
+ if (!is_array($matches)) {
+ throw self::createInvalidArgumentException(
+ 1,
+ 'array'
+ );
+ }
+
+ static::assertThat(
+ $list,
+ new SSListContainsOnly(
+ $matches
+ ),
+ $message
+ );
+ }
+
+ /**
+ * @param $matches
+ * @param SS_List $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListEquals() instead
+ *
+ */
+ public function assertDOSEquals($matches, $dataObjectSet)
+ {
+ Deprecation::notice('5.0', 'Use assertListEquals() instead');
+ static::assertListEquals($matches, $dataObjectSet);
+ }
+
+
+ /**
+ * Assert that the every record in the given {@link SS_List} matches the given key-value
+ * pairs.
+ *
+ * Example
+ * --------
+ * Check that every entry in $members has a Status of 'Active':
+ * $this->assertListAllMatch(['Status' => 'Active'], $members);
+ *
+ * @param mixed $match The pattern to match. The pattern is a map of key-value pairs.
+ * @param mixed $list The {@link SS_List} to test.
+ * @param string $message
+ */
+ public static function assertListAllMatch($match, SS_List $list, $message = '')
+ {
+ if (!is_array($match)) {
+ throw self::createInvalidArgumentException(
+ 1,
+ 'array'
+ );
+ }
+
+ static::assertThat(
+ $list,
+ new SSListContainsOnlyMatchingItems(
+ $match
+ ),
+ $message
+ );
+ }
+
+ /**
+ * @param $match
+ * @param SS_List $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListAllMatch() instead
+ *
+ */
+ public function assertDOSAllMatch($match, SS_List $dataObjectSet)
+ {
+ Deprecation::notice('5.0', 'Use assertListAllMatch() instead');
+ static::assertListAllMatch($match, $dataObjectSet);
+ }
+
+ /**
+ * Removes sequences of repeated whitespace characters from SQL queries
+ * making them suitable for string comparison
+ *
+ * @param string $sql
+ * @return string The cleaned and normalised SQL string
+ */
+ protected static function normaliseSQL($sql)
+ {
+ return trim(preg_replace('/\s+/m', ' ', $sql));
+ }
+
+ /**
+ * Asserts that two SQL queries are equivalent
+ *
+ * @param string $expectedSQL
+ * @param string $actualSQL
+ * @param string $message
+ */
+ public static function assertSQLEquals(
+ $expectedSQL,
+ $actualSQL,
+ $message = ''
+ ) {
+ // Normalise SQL queries to remove patterns of repeating whitespace
+ $expectedSQL = static::normaliseSQL($expectedSQL);
+ $actualSQL = static::normaliseSQL($actualSQL);
+
+ static::assertEquals($expectedSQL, $actualSQL, $message);
+ }
+
+ /**
+ * Asserts that a SQL query contains a SQL fragment
+ *
+ * @param string $needleSQL
+ * @param string $haystackSQL
+ * @param string $message
+ */
+ public static function assertSQLContains(
+ $needleSQL,
+ $haystackSQL,
+ $message = ''
+ ) {
+ $needleSQL = static::normaliseSQL($needleSQL);
+ $haystackSQL = static::normaliseSQL($haystackSQL);
+ if (is_iterable($haystackSQL)) {
+ /** @var iterable $iterableHaystackSQL */
+ $iterableHaystackSQL = $haystackSQL;
+ static::assertContains($needleSQL, $iterableHaystackSQL, $message);
+ } else {
+ static::assertStringContainsString($needleSQL, $haystackSQL, $message);
+ }
+ }
+
+ /**
+ * Asserts that a SQL query contains a SQL fragment
+ *
+ * @param string $needleSQL
+ * @param string $haystackSQL
+ * @param string $message
+ */
+ public static function assertSQLNotContains(
+ $needleSQL,
+ $haystackSQL,
+ $message = ''
+ ) {
+ $needleSQL = static::normaliseSQL($needleSQL);
+ $haystackSQL = static::normaliseSQL($haystackSQL);
+ if (is_iterable($haystackSQL)) {
+ /** @var iterable $iterableHaystackSQL */
+ $iterableHaystackSQL = $haystackSQL;
+ static::assertNotContains($needleSQL, $iterableHaystackSQL, $message);
+ } else {
+ static::assertStringNotContainsString($needleSQL, $haystackSQL, $message);
+ }
+ }
+
+ /**
+ * Start test environment
+ */
+ public static function start()
+ {
+ if (static::is_running_test()) {
+ return;
+ }
+
+ // Health check
+ if (InjectorLoader::inst()->countManifests()) {
+ throw new LogicException('SapphireTest::start() cannot be called within another application');
+ }
+ static::set_is_running_test(true);
+
+ // Test application
+ $kernel = new TestKernel(BASE_PATH);
+
+ if (class_exists(HTTPApplication::class)) {
+ // Mock request
+ $_SERVER['argv'] = ['vendor/bin/phpunit', '/'];
+ $request = CLIRequestBuilder::createFromEnvironment();
+
+ $app = new HTTPApplication($kernel);
+ $flush = array_key_exists('flush', $request->getVars());
+
+ // Custom application
+ $res = $app->execute($request, function (HTTPRequest $request) {
+ // Start session and execute
+ $request->getSession()->init($request);
+
+ // Invalidate classname spec since the test manifest will now pull out new subclasses for each internal class
+ // (e.g. Member will now have various subclasses of DataObjects that implement TestOnly)
+ DataObject::reset();
+
+ // Set dummy controller;
+ $controller = Controller::create();
+ $controller->setRequest($request);
+ $controller->pushCurrent();
+ $controller->doInit();
+ }, $flush);
+
+ if ($res && $res->isError()) {
+ throw new LogicException($res->getBody());
+ }
+ } else {
+ // Allow flush from the command line in the absence of HTTPApplication's special sauce
+ $flush = false;
+ foreach ($_SERVER['argv'] as $arg) {
+ if (preg_match('/^(--)?flush(=1)?$/', $arg)) {
+ $flush = true;
+ }
+ }
+ $kernel->boot($flush);
+ }
+
+ // Register state
+ static::$state = SapphireTestState::singleton();
+ // Register temp DB holder
+ static::tempDB();
+ }
+
+ /**
+ * Reset the testing database's schema, but only if it is active
+ * @param bool $includeExtraDataObjects If true, the extraDataObjects tables will also be included
+ * @param bool $forceCreate Force DB to be created if it doesn't exist
+ */
+ public static function resetDBSchema($includeExtraDataObjects = false, $forceCreate = false)
+ {
+ if (!static::$tempDB) {
+ return;
+ }
+
+ // Check if DB is active before reset
+ if (!static::$tempDB->isUsed()) {
+ if (!$forceCreate) {
+ return;
+ }
+ static::$tempDB->build();
+ }
+ $extraDataObjects = $includeExtraDataObjects ? static::getExtraDataObjects() : [];
+ static::$tempDB->resetDBSchema((array)$extraDataObjects);
+ }
+
+ /**
+ * A wrapper for automatically performing callbacks as a user with a specific permission
+ *
+ * @param string|array $permCode
+ * @param callable $callback
+ * @return mixed
+ */
+ public function actWithPermission($permCode, $callback)
+ {
+ return Member::actAs($this->createMemberWithPermission($permCode), $callback);
+ }
+
+ /**
+ * Create Member and Group objects on demand with specific permission code
+ *
+ * @param string|array $permCode
+ * @return Member
+ */
+ protected function createMemberWithPermission($permCode)
+ {
+ if (is_array($permCode)) {
+ $permArray = $permCode;
+ $permCode = implode('.', $permCode);
+ } else {
+ $permArray = [$permCode];
+ }
+
+ // Check cached member
+ if (isset($this->cache_generatedMembers[$permCode])) {
+ $member = $this->cache_generatedMembers[$permCode];
+ } else {
+ // Generate group with these permissions
+ $group = Group::create();
+ $group->Title = "$permCode group";
+ $group->write();
+
+ // Create each individual permission
+ foreach ($permArray as $permArrayItem) {
+ $permission = Permission::create();
+ $permission->Code = $permArrayItem;
+ $permission->write();
+ $group->Permissions()->add($permission);
+ }
+
+ $member = Member::get()->filter([
+ 'Email' => "$permCode@example.org",
+ ])->first();
+ if (!$member) {
+ $member = Member::create();
+ }
+
+ $member->FirstName = $permCode;
+ $member->Surname = 'User';
+ $member->Email = "$permCode@example.org";
+ $member->write();
+ $group->Members()->add($member);
+
+ $this->cache_generatedMembers[$permCode] = $member;
+ }
+ return $member;
+ }
+
+ /**
+ * Create a member and group with the given permission code, and log in with it.
+ * Returns the member ID.
+ *
+ * @param string|array $permCode Either a permission, or list of permissions
+ * @return int Member ID
+ */
+ public function logInWithPermission($permCode = 'ADMIN')
+ {
+ $member = $this->createMemberWithPermission($permCode);
+ $this->logInAs($member);
+ return $member->ID;
+ }
+
+ /**
+ * Log in as the given member
+ *
+ * @param Member|int|string $member The ID, fixture codename, or Member object of the member that you want to log in
+ */
+ public function logInAs($member)
+ {
+ if (is_numeric($member)) {
+ $member = DataObject::get_by_id(Member::class, $member);
+ } elseif (!is_object($member)) {
+ $member = $this->objFromFixture(Member::class, $member);
+ }
+ Injector::inst()->get(IdentityStore::class)->logIn($member);
+ }
+
+ /**
+ * Log out the current user
+ */
+ public function logOut()
+ {
+ /** @var IdentityStore $store */
+ $store = Injector::inst()->get(IdentityStore::class);
+ $store->logOut();
+ }
+
+ /**
+ * Cache for logInWithPermission()
+ */
+ protected $cache_generatedMembers = [];
+
+ /**
+ * Test against a theme.
+ *
+ * @param string $themeBaseDir themes directory
+ * @param string $theme Theme name
+ * @param callable $callback
+ * @throws Exception
+ */
+ protected function useTestTheme($themeBaseDir, $theme, $callback)
+ {
+ Config::nest();
+ if (strpos($themeBaseDir, BASE_PATH) === 0) {
+ $themeBaseDir = substr($themeBaseDir, strlen(BASE_PATH));
+ }
+ SSViewer::config()->update('theme_enabled', true);
+ SSViewer::set_themes([$themeBaseDir . '/themes/' . $theme, '$default']);
+
+ try {
+ $callback();
+ } finally {
+ Config::unnest();
+ }
+ }
+
+ /**
+ * Get fixture paths for this test
+ *
+ * @return array List of paths
+ */
+ protected function getFixturePaths()
+ {
+ $fixtureFile = static::get_fixture_file();
+ if (empty($fixtureFile)) {
+ return [];
+ }
+
+ $fixtureFiles = is_array($fixtureFile) ? $fixtureFile : [$fixtureFile];
+
+ return array_map(function ($fixtureFilePath) {
+ return $this->resolveFixturePath($fixtureFilePath);
+ }, $fixtureFiles);
+ }
+
+ /**
+ * Return all extra objects to scaffold for this test
+ * @return array
+ */
+ public static function getExtraDataObjects()
+ {
+ return static::$extra_dataobjects;
+ }
+
+ /**
+ * Get additional controller classes to register routes for
+ *
+ * @return array
+ */
+ public static function getExtraControllers()
+ {
+ return static::$extra_controllers;
+ }
+
+ /**
+ * Map a fixture path to a physical file
+ *
+ * @param string $fixtureFilePath
+ * @return string
+ */
+ protected function resolveFixturePath($fixtureFilePath)
+ {
+ // support loading via composer name path.
+ if (strpos($fixtureFilePath, ':') !== false) {
+ return ModuleResourceLoader::singleton()->resolvePath($fixtureFilePath);
+ }
+
+ // Support fixture paths relative to the test class, rather than relative to webroot
+ // String checking is faster than file_exists() calls.
+ $resolvedPath = realpath($this->getCurrentAbsolutePath() . '/' . $fixtureFilePath);
+ if ($resolvedPath) {
+ return $resolvedPath;
+ }
+
+ // Check if file exists relative to base dir
+ $resolvedPath = realpath(Director::baseFolder() . '/' . $fixtureFilePath);
+ if ($resolvedPath) {
+ return $resolvedPath;
+ }
+
+ return $fixtureFilePath;
+ }
+
+ protected function setUpRoutes()
+ {
+ if (!class_exists(Director::class)) {
+ return;
+ }
+
+ // Get overridden routes
+ $rules = $this->getExtraRoutes();
+
+ // Add all other routes
+ foreach (Director::config()->uninherited('rules') as $route => $rule) {
+ if (!isset($rules[$route])) {
+ $rules[$route] = $rule;
+ }
+ }
+
+ // Add default catch-all rule
+ $rules['$Controller//$Action/$ID/$OtherID'] = '*';
+
+ // Add controller-name auto-routing
+ Director::config()->set('rules', $rules);
+ }
+
+ /**
+ * Get extra routes to merge into Director.rules
+ *
+ * @return array
+ */
+ protected function getExtraRoutes()
+ {
+ $rules = [];
+ foreach ($this->getExtraControllers() as $class) {
+ $controllerInst = Controller::singleton($class);
+ $link = Director::makeRelative($controllerInst->Link());
+ $route = rtrim($link, '/') . '//$Action/$ID/$OtherID';
+ $rules[$route] = $class;
+ }
+ return $rules;
+ }
+
+ /**
+ * Reimplementation of phpunit5 PHPUnit_Util_InvalidArgumentHelper::factory()
+ *
+ * @param $argument
+ * @param $type
+ * @param $value
+ */
+ public static function createInvalidArgumentException($argument, $type, $value = null)
+ {
+ $stack = debug_backtrace(false);
+
+ return new PHPUnitFrameworkException(
+ sprintf(
+ 'Argument #%d%sof %s::%s() must be a %s',
+ $argument,
+ $value !== null ? ' (' . gettype($value) . '#' . $value . ')' : ' (No Value) ',
+ $stack[1]['class'],
+ $stack[1]['function'],
+ $type
+ )
+ );
+ }
+
+ /**
+ * Returns the annotations for this test.
+ *
+ * @return array
+ */
+ public function getAnnotations(): array
+ {
+ return TestUtil::parseTestMethodAnnotations(
+ get_class($this),
+ $this->getName(false)
+ );
+ }
+ }
+}
+
+/* -------------------------------------------------
+ *
+ * This version of SapphireTest is for phpunit 5
+ * The phpunit 9 verison is at the top of this file
+ *
+ * PHPUnit_Extensions_GroupTestSuite is a class that only exists in phpunit 5
+ *
+ * -------------------------------------------------
+ */
+if (!class_exists(PHPUnit_Extensions_GroupTestSuite::class)) {
return;
}
@@ -51,8 +1372,12 @@
* This class should not be used anywhere outside of unit tests, as phpunit may not be installed
* in production sites.
*/
+// Ignore multiple classes in same file
+// @codingStandardsIgnoreStart
class SapphireTest extends PHPUnit_Framework_TestCase implements TestOnly
{
+ // @codingStandardsIgnoreEnd
+
/**
* Path to fixture data for this test run.
* If passed as an array, multiple fixture files will be loaded.
@@ -340,7 +1665,6 @@ protected function setUp()
}
-
/**
* Helper method to determine if the current test should enable a test database
*
@@ -439,8 +1763,8 @@ public static function tearDownAfterClass()
}
/**
- * @deprecated 4.0.0:5.0.0
* @return FixtureFactory|false
+ * @deprecated 4.0.0:5.0.0
*/
public function getFixtureFactory()
{
@@ -452,9 +1776,9 @@ public function getFixtureFactory()
/**
* Sets a new fixture factory
- * @deprecated 4.0.0:5.0.0
* @param FixtureFactory $factory
* @return $this
+ * @deprecated 4.0.0:5.0.0
*/
public function setFixtureFactory(FixtureFactory $factory)
{
@@ -533,9 +1857,9 @@ protected function objFromFixture($className, $identifier)
* Load a YAML fixture file into the database.
* Once loaded, you can use idFromFixture() and objFromFixture() to get items from the fixture.
* Doesn't clear existing fixtures.
+ * @param string $fixtureFile The location of the .yml fixture file, relative to the site base dir
* @deprecated 4.0.0:5.0.0
*
- * @param string $fixtureFile The location of the .yml fixture file, relative to the site base dir
*/
public function loadFixture($fixtureFile)
{
@@ -715,7 +2039,6 @@ public static function assertEmailSent($to, $from = null, $subject = null, $cont
);
}
-
/**
* Assert that the given {@link SS_List} includes DataObjects matching the given key-value
* pairs. Each match must correspond to 1 distinct record.
@@ -756,10 +2079,10 @@ public static function assertListContains($matches, SS_List $list, $message = ''
}
/**
- * @deprecated 4.0.0:5.0.0 Use assertListContains() instead
- *
* @param $matches
* @param $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListContains() instead
+ *
*/
public function assertDOSContains($matches, $dataObjectSet)
{
@@ -796,7 +2119,7 @@ public static function assertListNotContains($matches, SS_List $list, $message =
);
}
- $constraint = new PHPUnit_Framework_Constraint_Not(
+ $constraint = new PHPUnit_Framework_Constraint_Not(
new SSListContains(
$matches
)
@@ -810,10 +2133,10 @@ public static function assertListNotContains($matches, SS_List $list, $message =
}
/**
- * @deprecated 4.0.0:5.0.0 Use assertListNotContains() instead
- *
* @param $matches
* @param $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListNotContains() instead
+ *
*/
public static function assertNotDOSContains($matches, $dataObjectSet)
{
@@ -858,10 +2181,10 @@ public static function assertListEquals($matches, SS_List $list, $message = '')
}
/**
- * @deprecated 4.0.0:5.0.0 Use assertListEquals() instead
- *
* @param $matches
* @param SS_List $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListEquals() instead
+ *
*/
public function assertDOSEquals($matches, $dataObjectSet)
{
@@ -869,7 +2192,6 @@ public function assertDOSEquals($matches, $dataObjectSet)
return static::assertListEquals($matches, $dataObjectSet);
}
-
/**
* Assert that the every record in the given {@link SS_List} matches the given key-value
* pairs.
@@ -902,10 +2224,10 @@ public static function assertListAllMatch($match, SS_List $list, $message = '')
}
/**
- * @deprecated 4.0.0:5.0.0 Use assertListAllMatch() instead
- *
* @param $match
* @param SS_List $dataObjectSet
+ * @deprecated 4.0.0:5.0.0 Use assertListAllMatch() instead
+ *
*/
public function assertDOSAllMatch($match, SS_List $dataObjectSet)
{
diff --git a/tests/behat/src/CmsFormsContext.php b/tests/behat/src/CmsFormsContext.php
index 7f495d7e53f..f4610ecb2b7 100644
--- a/tests/behat/src/CmsFormsContext.php
+++ b/tests/behat/src/CmsFormsContext.php
@@ -7,6 +7,7 @@
use Behat\Mink\Exception\ElementHtmlException;
use Behat\Gherkin\Node\TableNode;
use Behat\Mink\Session;
+use PHPUnit\Framework\Assert;
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
use SilverStripe\BehatExtension\Utility\StepHelper;
use Symfony\Component\DomCrawler\Crawler;
@@ -55,9 +56,9 @@ public function stepIShouldSeeAnEditPageForm($negative)
$form = $page->find('css', '#Form_EditForm');
if (trim($negative)) {
- assertNull($form, 'I should not see an edit page form');
+ Assert::assertNull($form, 'I should not see an edit page form');
} else {
- assertNotNull($form, 'I should see an edit page form');
+ Assert::assertNotNull($form, 'I should see an edit page form');
}
}
@@ -154,20 +155,31 @@ public function stepContentInHtmlFieldShouldHaveFormatting($text, $field, $negat
$matchedNode = $node;
}
}
- assertNotNull($matchedNode);
-
- $assertFn = $negate ? 'assertNotEquals' : 'assertEquals';
- if($formatting == 'bold') {
- call_user_func($assertFn, 'strong', $matchedNode->nodeName);
- } else if($formatting == 'left aligned') {
- if($matchedNode->getAttribute('class')) {
- call_user_func($assertFn, 'text-left', $matchedNode->getAttribute('class'));
- }
- } else if($formatting == 'right aligned') {
- call_user_func($assertFn, 'text-right', $matchedNode->getAttribute('class'));
- }
- }
- // @codingStandardsIgnoreEnd
+ Assert::assertNotNull($matchedNode);
+
+ if ($formatting == 'bold') {
+ if ($negate) {
+ Assert::assertNotEquals('strong', $matchedNode->nodeName);
+ } else {
+ Assert::assertEquals('strong', $matchedNode->nodeName);
+ }
+ } else if ($formatting == 'left aligned') {
+ if ($matchedNode->getAttribute('class')) {
+ if ($negate) {
+ Assert::assertNotEquals('text-left', $matchedNode->getAttribute('class'));
+ } else {
+ Assert::assertEquals('text-left', $matchedNode->getAttribute('class'));
+ }
+ }
+ } else if ($formatting == 'right aligned') {
+ if ($negate) {
+ Assert::assertNotEquals('text-right', $matchedNode->getAttribute('class'));
+ } else {
+ Assert::assertEquals('text-right', $matchedNode->getAttribute('class'));
+ }
+ }
+ }
+ // @codingStandardsIgnoreEnd
/**
* Selects the first textual match in the HTML editor. Does not support
@@ -226,9 +238,9 @@ public function iShouldSeeAField($negative, $text)
}
if (trim($negative)) {
- assertNull($matchedEl);
+ Assert::assertNull($matchedEl);
} else {
- assertNotNull($matchedEl);
+ Assert::assertNotNull($matchedEl);
}
}
@@ -287,19 +299,19 @@ public function iSelectValueInTreeDropdown($text, $selector)
$parentElement = null;
$this->retryThrowable(function () use (&$parentElement, &$page, $selector) {
$parentElement = $page->find('css', $selector);
- assertNotNull($parentElement, sprintf('"%s" element not found', $selector));
+ Assert::assertNotNull($parentElement, sprintf('"%s" element not found', $selector));
$page = $this->getSession()->getPage();
});
$this->retryThrowable(function () use ($parentElement, $selector) {
$dropdown = $parentElement->find('css', '.Select-arrow');
- assertNotNull($dropdown, sprintf('Unable to find the dropdown in "%s"', $selector));
+ Assert::assertNotNull($dropdown, sprintf('Unable to find the dropdown in "%s"', $selector));
$dropdown->click();
});
$this->retryThrowable(function () use ($text, $parentElement, $selector) {
$element = $parentElement->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text));
- assertNotNull($element, sprintf('"%s" not found in "%s"', $text, $selector));
+ Assert::assertNotNull($element, sprintf('"%s" not found in "%s"', $text, $selector));
$element->click();
});
}
@@ -322,7 +334,7 @@ protected function getHtmlField($locator)
$element = $this->findInputByLabelContent($locator);
}
- assertNotNull($element, sprintf('HTML field "%s" not found', $locator));
+ Assert::assertNotNull($element, sprintf('HTML field "%s" not found', $locator));
return $element;
}
@@ -335,7 +347,7 @@ protected function findInputByLabelContent($locator)
return null;
}
- assertCount(1, $label, sprintf(
+ Assert::assertCount(1, $label, sprintf(
'Found more than one element containing the phrase "%s".',
$locator
));
@@ -406,7 +418,7 @@ public function stepMySessionExpires()
public function assertIShouldSeeTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
{
$button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
- assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
+ Assert::assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
}
/**
@@ -418,7 +430,7 @@ public function assertIShouldSeeTheGridFieldButtonForRow($buttonLabel, $gridFiel
public function assertIShouldNotSeeTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
{
$button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
- assertNull($button, sprintf('Button "%s" found', $buttonLabel));
+ Assert::assertNull($button, sprintf('Button "%s" found', $buttonLabel));
}
/**
@@ -430,7 +442,7 @@ public function assertIShouldNotSeeTheGridFieldButtonForRow($buttonLabel, $gridF
public function stepIClickTheGridFieldButtonForRow($buttonLabel, $gridFieldName, $rowName)
{
$button = $this->getGridFieldButton($gridFieldName, $rowName, $buttonLabel);
- assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
+ Assert::assertNotNull($button, sprintf('Button "%s" not found', $buttonLabel));
$button->click();
}
@@ -447,7 +459,7 @@ protected function getGridFieldButton($gridFieldName, $rowName, $buttonLabel)
{
$page = $this->getSession()->getPage();
$gridField = $page->find('xpath', sprintf('//*[@data-name="%s"]', $gridFieldName));
- assertNotNull($gridField, sprintf('Gridfield "%s" not found', $gridFieldName));
+ Assert::assertNotNull($gridField, sprintf('Gridfield "%s" not found', $gridFieldName));
$name = $gridField->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $rowName));
if (!$name) {
@@ -472,12 +484,12 @@ public function stepIClickTheListBoxOption($optionLabel, $fieldName)
{
$page = $this->getSession()->getPage();
$listBox = $page->find('xpath', sprintf('//*[@name="%s[]"]', $fieldName));
- assertNotNull($listBox, sprintf('The listbox %s is not found', $fieldName));
+ Assert::assertNotNull($listBox, sprintf('The listbox %s is not found', $fieldName));
$option = $listBox->getParent()
->find('css', '.chosen-choices')
->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $optionLabel));
- assertNotNull($option, sprintf('Option %s is not found', $optionLabel));
+ Assert::assertNotNull($option, sprintf('Option %s is not found', $optionLabel));
$button = $option->getParent()->find('css', 'a');
diff --git a/tests/behat/src/CmsUiContext.php b/tests/behat/src/CmsUiContext.php
index 6e5b3f08417..b3adb6922fe 100644
--- a/tests/behat/src/CmsUiContext.php
+++ b/tests/behat/src/CmsUiContext.php
@@ -8,6 +8,7 @@
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Selector\Xpath\Escaper;
use Behat\Mink\Session;
+use PHPUnit\Framework\Assert;
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
use SilverStripe\BehatExtension\Utility\StepHelper;
@@ -72,7 +73,7 @@ public function iShouldSeeTheCms()
{
$page = $this->getSession()->getPage();
$cms_element = $page->find('css', '.cms');
- assertNotNull($cms_element, 'CMS not found');
+ Assert::assertNotNull($cms_element, 'CMS not found');
}
/**
@@ -116,9 +117,9 @@ public function stepIClickTheToastAction($action)
{
$page = $this->getMainContext()->getSession()->getPage();
$toasts = $page->find('css', '.toasts');
- assertNotNull($toasts, "We have a toast container");
+ Assert::assertNotNull($toasts, "We have a toast container");
$toastAction = $toasts->find('named', ['link_or_button', "'{$action}'"]);
- assertNotNull($toastAction, "We have a $action toast action");
+ Assert::assertNotNull($toastAction, "We have a $action toast action");
$toastAction->click();
}
@@ -145,7 +146,7 @@ protected function getCmsTabsElement()
$page = $this->getSession()->getPage();
$cms_content_header_tabs = $page->find('css', '.cms-content-header-tabs');
- assertNotNull($cms_content_header_tabs, 'CMS tabs not found');
+ Assert::assertNotNull($cms_content_header_tabs, 'CMS tabs not found');
return $cms_content_header_tabs;
}
@@ -160,7 +161,7 @@ protected function getCmsContentToolbarElement()
$page = $this->getSession()->getPage();
$cms_content_toolbar_element = $page->find('css', '.cms-content-toolbar');
- assertNotNull($cms_content_toolbar_element, 'CMS content toolbar not found');
+ Assert::assertNotNull($cms_content_toolbar_element, 'CMS content toolbar not found');
return $cms_content_toolbar_element;
}
@@ -174,7 +175,7 @@ protected function getCmsTreeElement()
$page = $this->getSession()->getPage();
$cms_tree_element = $page->find('css', '.cms-tree');
- assertNotNull($cms_tree_element, 'CMS tree not found');
+ Assert::assertNotNull($cms_tree_element, 'CMS tree not found');
return $cms_tree_element;
}
@@ -187,7 +188,7 @@ public function iShouldSeeAButtonInCmsContentToolbar($text)
$cms_content_toolbar_element = $this->getCmsContentToolbarElement();
$element = $cms_content_toolbar_element->find('named', ['link_or_button', "'$text'"]);
- assertNotNull($element, sprintf('%s button not found', $text));
+ Assert::assertNotNull($element, sprintf('%s button not found', $text));
}
/**
@@ -198,7 +199,7 @@ public function stepIShouldSeeInCmsTree($text)
// Wait until visible
$cmsTreeElement = $this->getCmsTreeElement();
$element = $cmsTreeElement->find('named', ['content', "'$text'"]);
- assertNotNull($element, sprintf('%s not found', $text));
+ Assert::assertNotNull($element, sprintf('%s not found', $text));
}
/**
@@ -209,7 +210,7 @@ public function stepIShouldNotSeeInCmsTree($text)
// Wait until not visible
$cmsTreeElement = $this->getCmsTreeElement();
$element = $cmsTreeElement->find('named', ['content', "'$text'"]);
- assertNull($element, sprintf('%s found', $text));
+ Assert::assertNull($element, sprintf('%s found', $text));
}
/**
@@ -224,14 +225,14 @@ public function stepIShouldSeeInCmsList($negate, $text)
);
$page = $this->getSession()->getPage();
$cmsListElement = $page->find('css', '.cms-list');
- assertNotNull($cmsListElement, 'CMS list not found');
+ Assert::assertNotNull($cmsListElement, 'CMS list not found');
// Check text within this element
$element = $cmsListElement->find('named', ['content', "'$text'"]);
if (strstr($negate, 'not')) {
- assertNull($element, sprintf('Unexpected %s found in cms list', $text));
+ Assert::assertNull($element, sprintf('Unexpected %s found in cms list', $text));
} else {
- assertNotNull($element, sprintf('Expected %s not found in cms list', $text));
+ Assert::assertNotNull($element, sprintf('Expected %s not found in cms list', $text));
}
}
@@ -241,7 +242,7 @@ public function stepIShouldSeeInCmsList($negate, $text)
public function stepIShouldSeeInCMSContentTabs($text)
{
// Wait until visible
- assertNotNull($this->getCmsTabElement($text), sprintf('%s content tab not found', $text));
+ Assert::assertNotNull($this->getCmsTabElement($text), sprintf('%s content tab not found', $text));
}
/**
@@ -283,7 +284,7 @@ public function stepIClickOnElementInTheContextMenu($method, $link)
"window.jQuery && window.jQuery('.jstree-apple-context').size() > 0"
);
$regionObj = $context->getRegionObj('.jstree-apple-context');
- assertNotNull($regionObj, "Context menu could not be found");
+ Assert::assertNotNull($regionObj, "Context menu could not be found");
$linkObj = $regionObj->findLink($link);
if (empty($linkObj)) {
@@ -304,7 +305,7 @@ public function stepIClickOnElementInTheTree($method, $text)
{
$treeEl = $this->getCmsTreeElement();
$treeNode = $treeEl->findLink($text);
- assertNotNull($treeNode, sprintf('%s not found', $text));
+ Assert::assertNotNull($treeNode, sprintf('%s not found', $text));
$this->interactWithElement($treeNode, $method);
}
@@ -314,7 +315,7 @@ public function stepIClickOnElementInTheTree($method, $text)
public function stepIClickOnElementInTheHeaderTabs($method, $text)
{
$tabsNode = $this->getCmsTabElement($text);
- assertNotNull($tabsNode, sprintf('%s not found', $text));
+ Assert::assertNotNull($tabsNode, sprintf('%s not found', $text));
$this->interactWithElement($tabsNode, $method);
}
@@ -324,8 +325,8 @@ public function stepIClickOnElementInTheHeaderTabs($method, $text)
public function theHeaderTabShouldBeActive($text)
{
$element = $this->getCmsTabElement($text);
- assertNotNull($element);
- assertTrue($element->hasClass('active'));
+ Assert::assertNotNull($element);
+ Assert::assertTrue($element->hasClass('active'));
}
/**
@@ -334,8 +335,8 @@ public function theHeaderTabShouldBeActive($text)
public function theHeaderTabShouldNotBeActive($text)
{
$element = $this->getCmsTabElement($text);
- assertNotNull($element);
- assertFalse($element->hasClass('active'));
+ Assert::assertNotNull($element);
+ Assert::assertFalse($element->hasClass('active'));
}
/**
@@ -354,7 +355,7 @@ public function iExpandTheCmsPanel()
//Tries to find the first visiable toggle in the page
$page = $this->getSession()->getPage();
$toggle_elements = $page->findAll('css', '.toggle-expand');
- assertNotNull($toggle_elements, 'Panel toggle not found');
+ Assert::assertNotNull($toggle_elements, 'Panel toggle not found');
/** @var NodeElement $toggle */
foreach ($toggle_elements as $toggle) {
if ($toggle->isVisible()) {
@@ -370,7 +371,7 @@ public function iExpandTheContentFilters($action)
{
$page = $this->getSession()->getPage();
$filterButton = $page->find('css', '.search-box__filter-trigger');
- assertNotNull($filterButton, sprintf('Filter button link not found'));
+ Assert::assertNotNull($filterButton, sprintf('Filter button link not found'));
$filterButtonExpanded = $filterButton->getAttribute('aria-expanded');
@@ -421,20 +422,20 @@ public function iExpandInTheTree($action, $nodeText)
//Tries to find the first visiable matched Node in the page
$treeEl = $this->getCmsTreeElement();
$treeNode = $treeEl->findLink($nodeText);
- assertNotNull($treeNode, sprintf('%s link not found', $nodeText));
+ Assert::assertNotNull($treeNode, sprintf('%s link not found', $nodeText));
$cssIcon = $treeNode->getParent()->getAttribute("class");
if ($action == "expand") {
//ensure it is collapsed
if (false === strpos($cssIcon, 'jstree-open')) {
$nodeIcon = $treeNode->getParent()->find('css', '.jstree-icon');
- assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
+ Assert::assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
$nodeIcon->click();
}
} else {
//ensure it is expanded
if (false === strpos($cssIcon, 'jstree-closed')) {
$nodeIcon = $treeNode->getParent()->find('css', '.jstree-icon');
- assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
+ Assert::assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
$nodeIcon->click();
}
}
@@ -452,7 +453,7 @@ public function iShouldSeeACmsTab($negate, $tab)
$page = $this->getSession()->getPage();
$tabsets = $page->findAll('css', '.ui-tabs-nav');
- assertNotNull($tabsets, 'CMS tabs not found');
+ Assert::assertNotNull($tabsets, 'CMS tabs not found');
$tab_element = null;
/** @var NodeElement $tabset */
@@ -463,9 +464,9 @@ public function iShouldSeeACmsTab($negate, $tab)
}
}
if ($negate) {
- assertNull($tab_element, sprintf('%s tab found', $tab));
+ Assert::assertNull($tab_element, sprintf('%s tab found', $tab));
} else {
- assertNotNull($tab_element, sprintf('%s tab not found', $tab));
+ Assert::assertNotNull($tab_element, sprintf('%s tab not found', $tab));
}
}
@@ -481,7 +482,7 @@ public function iClickTheCmsTab($tab)
$page = $this->getSession()->getPage();
$tabsets = $page->findAll('css', '.ui-tabs-nav');
- assertNotNull($tabsets, 'CMS tabs not found');
+ Assert::assertNotNull($tabsets, 'CMS tabs not found');
$tab_element = null;
/** @var NodeElement $tabset */
@@ -491,7 +492,7 @@ public function iClickTheCmsTab($tab)
}
$tab_element = $tabset->find('named', ['link_or_button', "'$tab'"]);
}
- assertNotNull($tab_element, sprintf('%s tab not found', $tab));
+ Assert::assertNotNull($tab_element, sprintf('%s tab not found', $tab));
$tab_element->click();
}
@@ -544,13 +545,13 @@ public function iWaitForThePreviewToLoad()
public function iSwitchThePreviewToMode($mode)
{
$controls = $this->getSession()->getPage()->find('css', '.cms-preview-controls');
- assertNotNull($controls, 'Preview controls not found');
+ Assert::assertNotNull($controls, 'Preview controls not found');
$label = $controls->find('xpath', sprintf(
'.//*[count(*)=0 and contains(text(), \'%s\')]',
$mode
));
- assertNotNull($label, 'Preview mode switch not found');
+ Assert::assertNotNull($label, 'Preview mode switch not found');
$label->click();
@@ -654,7 +655,7 @@ public function theIFillInTheDropdownWith($field, $value)
}
}
- assertGreaterThan(0, count($formFields), sprintf(
+ Assert::assertGreaterThan(0, count($formFields), sprintf(
'Chosen.js dropdown named "%s" not found',
$field
));
@@ -669,11 +670,11 @@ public function theIFillInTheDropdownWith($field, $value)
}
}
- assertNotNull($container, 'Chosen.js field container not found');
+ Assert::assertNotNull($container, 'Chosen.js field container not found');
// Click on newly expanded list element, indirectly setting the dropdown value
$linkEl = $container->find('xpath', './/a');
- assertNotNull($linkEl, 'Chosen.js link element not found');
+ Assert::assertNotNull($linkEl, 'Chosen.js link element not found');
$this->getSession()->wait(100); // wait for dropdown overlay to appear
$linkEl->click();
diff --git a/tests/behat/src/ConfigContext.php b/tests/behat/src/ConfigContext.php
index 7da49e94bc9..0e10db85034 100644
--- a/tests/behat/src/ConfigContext.php
+++ b/tests/behat/src/ConfigContext.php
@@ -5,6 +5,7 @@
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\AfterScenarioScope;
use InvalidArgumentException;
+use PHPUnit\Framework\Assert;
use SilverStripe\BehatExtension\Context\MainContextAwareTrait;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Kernel;
@@ -109,19 +110,19 @@ public function stepIHaveConfigFile($filename)
// Ensure site is in dev mode
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
- assertEquals(Kernel::DEV, $kernel->getEnvironment(), "Site is in dev mode");
+ Assert::assertEquals(Kernel::DEV, $kernel->getEnvironment(), "Site is in dev mode");
// Ensure file exists in specified fixture dir
$sourceDir = $this->getConfigPath();
$sourcePath = "{$sourceDir}/{$filename}";
- assertFileExists($sourcePath, "Config file {$filename} exists");
+ Assert::assertFileExists($sourcePath, "Config file {$filename} exists");
// Get destination
$project = ModuleManifest::config()->get('project') ?: 'mysite';
$mysite = ModuleLoader::getModule($project);
- assertNotNull($mysite, 'Project exists');
+ Assert::assertNotNull($mysite, 'Project exists');
$destPath = $mysite->getResource("_config/{$filename}")->getPath();
- assertFileNotExists($destPath, "Config file {$filename} hasn't aleady been loaded");
+ Assert::assertFileDoesNotExist($destPath, "Config file {$filename} hasn't aleady been loaded");
// Load
$this->activatedConfigFiles[] = $destPath;
diff --git a/tests/php/Control/ControllerTest.php b/tests/php/Control/ControllerTest.php
index 520f7ce5f13..fbdb11ba10c 100644
--- a/tests/php/Control/ControllerTest.php
+++ b/tests/php/Control/ControllerTest.php
@@ -42,7 +42,7 @@ class ControllerTest extends FunctionalTest
UnsecuredController::class,
];
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
Director::config()->update('alternate_base_url', '/');
@@ -60,7 +60,7 @@ public function testDefaultAction()
{
/* For a controller with a template, the default action will simple run that template. */
$response = $this->get("TestController/");
- $this->assertContains("This is the main template. Content is 'default content'", $response->getBody());
+ $this->assertStringContainsString("This is the main template. Content is 'default content'", $response->getBody());
}
public function testMethodActions()
@@ -68,18 +68,18 @@ public function testMethodActions()
/* The Action can refer to a method that is called on the object. If a method returns an array, then it
* will be used to customise the template data */
$response = $this->get("TestController/methodaction");
- $this->assertContains("This is the main template. Content is 'methodaction content'.", $response->getBody());
+ $this->assertStringContainsString("This is the main template. Content is 'methodaction content'.", $response->getBody());
/* If the method just returns a string, then that will be used as the response */
$response = $this->get("TestController/stringaction");
- $this->assertContains("stringaction was called.", $response->getBody());
+ $this->assertStringContainsString("stringaction was called.", $response->getBody());
}
public function testTemplateActions()
{
/* If there is no method, it can be used to point to an alternative template. */
$response = $this->get("TestController/templateaction");
- $this->assertContains(
+ $this->assertStringContainsString(
"This is the template for templateaction. Content is 'default content'.",
$response->getBody()
);
@@ -262,12 +262,10 @@ public function testAllowedActions()
$this->logOut();
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage Invalid allowed_action '*'
- */
public function testWildcardAllowedActions()
{
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage("Invalid allowed_action '*'");
$this->get('AccessWildcardSecuredController');
}
diff --git a/tests/php/Control/CookieTest.php b/tests/php/Control/CookieTest.php
index d08bb975461..8f2a8eaaa2b 100644
--- a/tests/php/Control/CookieTest.php
+++ b/tests/php/Control/CookieTest.php
@@ -10,7 +10,7 @@
class CookieTest extends SapphireTest
{
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
Injector::inst()->registerService(new CookieJar($_COOKIE), 'SilverStripe\\Control\\Cookie_Backend');
diff --git a/tests/php/Control/DirectorTest.php b/tests/php/Control/DirectorTest.php
index 115b24c9a95..e6fdc26c872 100644
--- a/tests/php/Control/DirectorTest.php
+++ b/tests/php/Control/DirectorTest.php
@@ -29,7 +29,7 @@ class DirectorTest extends SapphireTest
private $originalEnvType;
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/');
@@ -44,10 +44,10 @@ protected function setUp()
$this->expectedRedirect = null;
}
- protected function tearDown(...$args)
+ protected function tearDown(): void
{
Environment::setEnv('SS_ENVIRONMENT_TYPE', $this->originalEnvType);
- parent::tearDown(...$args);
+ parent::tearDown();
}
protected function getExtraRoutes()
diff --git a/tests/php/Control/Email/EmailTest.php b/tests/php/Control/Email/EmailTest.php
index 8857cfed616..3eee3636fb0 100644
--- a/tests/php/Control/Email/EmailTest.php
+++ b/tests/php/Control/Email/EmailTest.php
@@ -3,7 +3,7 @@
namespace SilverStripe\Control\Tests\Email;
use DateTime;
-use PHPUnit_Framework_MockObject_MockObject;
+use PHPUnit\Framework\MockObject\MockObject;
use SilverStripe\Control\Email\Email;
use SilverStripe\Control\Email\Mailer;
use SilverStripe\Control\Email\SwiftMailer;
@@ -136,7 +136,7 @@ public function testSendPlain()
public function testSend()
{
- /** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
+ /** @var Email|MockObject $email */
$email = $this->makeEmailMock('Test send HTML');
// email should not call render if a body is supplied
@@ -166,7 +166,7 @@ public function testSend()
public function testRenderedSend()
{
- /** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
+ /** @var Email|MockObject $email */
$email = $this->getMockBuilder(Email::class)
->enableProxyingToOriginalMethods()
->getMock();
@@ -193,7 +193,7 @@ public function testRenderedSendSubclass()
'$default',
]);
- /** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
+ /** @var Email|MockObject $email */
$email = $this->getMockBuilder(EmailSubClass::class)
->enableProxyingToOriginalMethods()
->getMock();
@@ -207,7 +207,7 @@ public function testRenderedSendSubclass()
$email->send();
$this->assertTrue($email->hasPlainPart());
$this->assertNotEmpty($email->getBody());
- $this->assertContains('Email Sub-class
', $email->getBody());
+ $this->assertStringContainsString('Email Sub-class
', $email->getBody());
}
public function testConsturctor()
@@ -527,7 +527,7 @@ public function testPlainTemplate()
public function testGetFailedRecipients()
{
$mailer = new SwiftMailer();
- /** @var Swift_NullTransport|PHPUnit_Framework_MockObject_MockObject $transport */
+ /** @var Swift_NullTransport|MockObject $transport */
$transport = $this->getMockBuilder(Swift_NullTransport::class)->getMock();
$transport->expects($this->once())
->method('send')
@@ -552,7 +552,7 @@ public function testRenderAgain()
'EmailContent' => 'my content',
]);
$email->render();
- $this->assertContains('my content', $email->getBody());
+ $this->assertStringContainsString('my content', $email->getBody());
$children = $email->getSwiftMessage()->getChildren();
$this->assertCount(1, $children);
$plainPart = reset($children);
@@ -570,7 +570,7 @@ public function testRerender()
'EmailContent' => 'my content',
]);
$email->render();
- $this->assertContains('my content', $email->getBody());
+ $this->assertStringContainsString('my content', $email->getBody());
$children = $email->getSwiftMessage()->getChildren();
$this->assertCount(1, $children);
$plainPart = reset($children);
@@ -581,19 +581,19 @@ public function testRerender()
'EmailContent' => 'your content'
]);
$email->render();
- $this->assertContains('your content', $email->getBody());
+ $this->assertStringContainsString('your content', $email->getBody());
// Ensure removing data causes a rerender
$email->removeData('EmailContent');
$email->render();
- $this->assertNotContains('your content', $email->getBody());
+ $this->assertStringNotContainsString('your content', $email->getBody());
// Ensure adding data causes a rerender
$email->addData([
'EmailContent' => 'their content'
]);
$email->render();
- $this->assertContains('their content', $email->getBody());
+ $this->assertStringContainsString('their content', $email->getBody());
}
public function testRenderPlainOnly()
@@ -631,8 +631,8 @@ public function testGeneratePlainPartFromBody()
$children = $email->getSwiftMessage()->getChildren();
$this->assertCount(1, $children);
$plainPart = reset($children);
- $this->assertContains('Test', $plainPart->getBody());
- $this->assertNotContains('Test
', $plainPart->getBody());
+ $this->assertStringContainsString('Test', $plainPart->getBody());
+ $this->assertStringNotContainsString('Test
', $plainPart->getBody());
}
public function testMultipleEmailSends()
@@ -644,30 +644,30 @@ public function testMultipleEmailSends()
$this->assertEmpty($email->getBody());
$this->assertEmpty($email->getSwiftMessage()->getChildren());
$email->send();
- $this->assertContains('Test', $email->getBody());
+ $this->assertStringContainsString('Test', $email->getBody());
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
$children = $email->getSwiftMessage()->getChildren();
/** @var \Swift_MimePart $plainPart */
$plainPart = reset($children);
- $this->assertContains('Test', $plainPart->getBody());
+ $this->assertStringContainsString('Test', $plainPart->getBody());
//send again
$email->send();
- $this->assertContains('Test', $email->getBody());
+ $this->assertStringContainsString('Test', $email->getBody());
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
$children = $email->getSwiftMessage()->getChildren();
/** @var \Swift_MimePart $plainPart */
$plainPart = reset($children);
- $this->assertContains('Test', $plainPart->getBody());
+ $this->assertStringContainsString('Test', $plainPart->getBody());
}
/**
- * @return PHPUnit_Framework_MockObject_MockObject|Email
+ * @return MockObject|Email
*/
protected function makeEmailMock($subject)
{
- /** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
+ /** @var Email|MockObject $email */
$email = $this->getMockBuilder(Email::class)
->enableProxyingToOriginalMethods()
->getMock();
diff --git a/tests/php/Control/Email/SwiftPluginTest.php b/tests/php/Control/Email/SwiftPluginTest.php
index da809552da8..25adb7bbea7 100644
--- a/tests/php/Control/Email/SwiftPluginTest.php
+++ b/tests/php/Control/Email/SwiftPluginTest.php
@@ -9,7 +9,7 @@
class SwiftPluginTest extends SapphireTest
{
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
diff --git a/tests/php/Control/HTTPCacheControlIntegrationTest.php b/tests/php/Control/HTTPCacheControlIntegrationTest.php
index 94b0f152793..ed2c8af8cc5 100644
--- a/tests/php/Control/HTTPCacheControlIntegrationTest.php
+++ b/tests/php/Control/HTTPCacheControlIntegrationTest.php
@@ -16,7 +16,7 @@ class HTTPCacheControlIntegrationTest extends FunctionalTest
RuleController::class,
];
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
HTTPCacheControlMiddleware::config()
@@ -31,11 +31,11 @@ public function testFormCSRF()
$response = $this->get('HTTPCacheControlIntegrationTest_SessionController/showform');
$header = $response->getHeader('Cache-Control');
$this->assertFalse($response->isError());
- $this->assertNotContains('public', $header);
- $this->assertNotContains('private', $header);
- $this->assertContains('no-cache', $header);
- $this->assertContains('no-store', $header);
- $this->assertContains('must-revalidate', $header);
+ $this->assertStringNotContainsString('public', $header);
+ $this->assertStringNotContainsString('private', $header);
+ $this->assertStringContainsString('no-cache', $header);
+ $this->assertStringContainsString('no-store', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
}
public function testPublicForm()
@@ -44,10 +44,10 @@ public function testPublicForm()
$response = $this->get('HTTPCacheControlIntegrationTest_SessionController/showpublicform');
$header = $response->getHeader('Cache-Control');
$this->assertFalse($response->isError());
- $this->assertContains('public', $header);
- $this->assertContains('must-revalidate', $header);
- $this->assertNotContains('no-cache', $response->getHeader('Cache-Control'));
- $this->assertNotContains('no-store', $response->getHeader('Cache-Control'));
+ $this->assertStringContainsString('public', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
+ $this->assertStringNotContainsString('no-cache', $response->getHeader('Cache-Control'));
+ $this->assertStringNotContainsString('no-store', $response->getHeader('Cache-Control'));
}
public function testPrivateActionsError()
@@ -56,9 +56,9 @@ public function testPrivateActionsError()
$response = $this->get('HTTPCacheControlIntegrationTest_SessionController/privateaction');
$header = $response->getHeader('Cache-Control');
$this->assertTrue($response->isError());
- $this->assertContains('no-cache', $header);
- $this->assertContains('no-store', $header);
- $this->assertContains('must-revalidate', $header);
+ $this->assertStringContainsString('no-cache', $header);
+ $this->assertStringContainsString('no-store', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
}
public function testPrivateActionsAuthenticated()
@@ -68,10 +68,10 @@ public function testPrivateActionsAuthenticated()
$response = $this->get('HTTPCacheControlIntegrationTest_SessionController/privateaction');
$header = $response->getHeader('Cache-Control');
$this->assertFalse($response->isError());
- $this->assertContains('private', $header);
- $this->assertContains('must-revalidate', $header);
- $this->assertNotContains('no-cache', $header);
- $this->assertNotContains('no-store', $header);
+ $this->assertStringContainsString('private', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
+ $this->assertStringNotContainsString('no-cache', $header);
+ $this->assertStringNotContainsString('no-store', $header);
}
public function testPrivateCache()
@@ -79,10 +79,10 @@ public function testPrivateCache()
$response = $this->get('HTTPCacheControlIntegrationTest_RuleController/privateaction');
$header = $response->getHeader('Cache-Control');
$this->assertFalse($response->isError());
- $this->assertContains('private', $header);
- $this->assertContains('must-revalidate', $header);
- $this->assertNotContains('no-cache', $header);
- $this->assertNotContains('no-store', $header);
+ $this->assertStringContainsString('private', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
+ $this->assertStringNotContainsString('no-cache', $header);
+ $this->assertStringNotContainsString('no-store', $header);
}
public function testPublicCache()
@@ -90,11 +90,11 @@ public function testPublicCache()
$response = $this->get('HTTPCacheControlIntegrationTest_RuleController/publicaction');
$header = $response->getHeader('Cache-Control');
$this->assertFalse($response->isError());
- $this->assertContains('public', $header);
- $this->assertContains('must-revalidate', $header);
- $this->assertNotContains('no-cache', $header);
- $this->assertNotContains('no-store', $header);
- $this->assertContains('max-age=9000', $header);
+ $this->assertStringContainsString('public', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
+ $this->assertStringNotContainsString('no-cache', $header);
+ $this->assertStringNotContainsString('no-store', $header);
+ $this->assertStringContainsString('max-age=9000', $header);
}
public function testDisabledCache()
@@ -102,10 +102,10 @@ public function testDisabledCache()
$response = $this->get('HTTPCacheControlIntegrationTest_RuleController/disabledaction');
$header = $response->getHeader('Cache-Control');
$this->assertFalse($response->isError());
- $this->assertNotContains('public', $header);
- $this->assertNotContains('private', $header);
- $this->assertContains('no-cache', $header);
- $this->assertContains('no-store', $header);
- $this->assertContains('must-revalidate', $header);
+ $this->assertStringNotContainsString('public', $header);
+ $this->assertStringNotContainsString('private', $header);
+ $this->assertStringContainsString('no-cache', $header);
+ $this->assertStringContainsString('no-store', $header);
+ $this->assertStringContainsString('must-revalidate', $header);
}
}
diff --git a/tests/php/Control/HTTPRequestTest.php b/tests/php/Control/HTTPRequestTest.php
index 52ce61b4923..d9778f8a4d3 100644
--- a/tests/php/Control/HTTPRequestTest.php
+++ b/tests/php/Control/HTTPRequestTest.php
@@ -52,11 +52,10 @@ public function testWildCardMatch()
/**
* This test just asserts a warning is given if there is more than one wildcard parameter. Note that this isn't an
* enforcement of an API and we an add new behaviour in the future to allow many wildcard params if we want to
- *
- * @expectedException \PHPUnit_Framework_Error_Warning
*/
public function testWildCardWithFurtherParams()
{
+ $this->expectWarning();
$request = new HTTPRequest('GET', 'admin/crm/test');
// all parameters after the first wildcard parameter are ignored
$request->match('admin/$Action/$@/$Other/$*', true);
diff --git a/tests/php/Control/HTTPTest.php b/tests/php/Control/HTTPTest.php
index 018c59ce1a8..03c37aca41f 100644
--- a/tests/php/Control/HTTPTest.php
+++ b/tests/php/Control/HTTPTest.php
@@ -19,7 +19,7 @@
*/
class HTTPTest extends FunctionalTest
{
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
// Set to disabled at null forcing level
@@ -48,9 +48,9 @@ public function testAddCacheHeaders()
HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
$response = new HTTPResponse($body, 200);
$this->addCacheHeaders($response);
- $this->assertContains('no-cache', $response->getHeader('Cache-Control'));
- $this->assertContains('no-store', $response->getHeader('Cache-Control'));
- $this->assertContains('must-revalidate', $response->getHeader('Cache-Control'));
+ $this->assertStringContainsString('no-cache', $response->getHeader('Cache-Control'));
+ $this->assertStringContainsString('no-store', $response->getHeader('Cache-Control'));
+ $this->assertStringContainsString('must-revalidate', $response->getHeader('Cache-Control'));
// Ensure max-age setting is respected in production.
HTTPCacheControlMiddleware::config()
@@ -61,8 +61,8 @@ public function testAddCacheHeaders()
HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
$response = new HTTPResponse($body, 200);
$this->addCacheHeaders($response);
- $this->assertContains('max-age=30', $response->getHeader('Cache-Control'));
- $this->assertNotContains('max-age=0', $response->getHeader('Cache-Control'));
+ $this->assertStringContainsString('max-age=30', $response->getHeader('Cache-Control'));
+ $this->assertStringNotContainsString('max-age=0', $response->getHeader('Cache-Control'));
// Still "live": Ensure header's aren't overridden if already set (using purposefully different values).
$headers = [
@@ -93,11 +93,11 @@ public function testConfigVary()
// Vary set properly
$v = $response->getHeader('Vary');
- $this->assertContains("X-Forwarded-Protocol", $v);
- $this->assertContains("X-Requested-With", $v);
- $this->assertNotContains("Cookie", $v);
- $this->assertNotContains("User-Agent", $v);
- $this->assertNotContains("Accept", $v);
+ $this->assertStringContainsString("X-Forwarded-Protocol", $v);
+ $this->assertStringContainsString("X-Requested-With", $v);
+ $this->assertStringNotContainsString("Cookie", $v);
+ $this->assertStringNotContainsString("User-Agent", $v);
+ $this->assertStringNotContainsString("Accept", $v);
// No vary
HTTPCacheControlMiddleware::singleton()
@@ -124,7 +124,7 @@ public function testDeprecatedVaryHandling()
$response = new HTTPResponse('', 200);
$this->addCacheHeaders($response);
$header = $response->getHeader('Vary');
- $this->assertContains('X-Foo', $header);
+ $this->assertStringContainsString('X-Foo', $header);
}
public function testDeprecatedCacheControlHandling()
@@ -143,8 +143,8 @@ public function testDeprecatedCacheControlHandling()
$response = new HTTPResponse('', 200);
$this->addCacheHeaders($response);
$header = $response->getHeader('Cache-Control');
- $this->assertContains('no-store', $header);
- $this->assertContains('no-cache', $header);
+ $this->assertStringContainsString('no-store', $header);
+ $this->assertStringContainsString('no-cache', $header);
}
public function testDeprecatedCacheControlHandlingOnMaxAge()
@@ -164,15 +164,13 @@ public function testDeprecatedCacheControlHandlingOnMaxAge()
$response = new HTTPResponse('', 200);
$this->addCacheHeaders($response);
$header = $response->getHeader('Cache-Control');
- $this->assertContains('max-age=99', $header);
+ $this->assertStringContainsString('max-age=99', $header);
}
- /**
- * @expectedException \LogicException
- * @expectedExceptionMessageRegExp /Found unsupported legacy directives in HTTP\.cache_control: unknown/
- */
public function testDeprecatedCacheControlHandlingThrowsWithUnknownDirectives()
{
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessageMatches('/Found unsupported legacy directives in HTTP\.cache_control: unknown/');
/** @var Config */
Config::modify()->set(
HTTP::class,
@@ -219,7 +217,7 @@ public function testGetLinksIn()
sort($result);
sort($expected);
- $this->assertInternalType('array', $result);
+ $this->assertIsArray($result);
$this->assertEquals($expected, $result, 'Test that all links within the content are found.');
}
@@ -235,7 +233,7 @@ public function testSetGetVar()
$controller->setRequest($request);
$controller->pushCurrent();
try {
- $this->assertContains(
+ $this->assertStringContainsString(
'relative/url?foo=bar',
HTTP::setGetVar('foo', 'bar'),
'Omitting a URL falls back to current URL'
@@ -263,7 +261,7 @@ public function testSetGetVar()
'Absolute URL without path and multipe existing query params, overwriting an existing parameter'
);
- $this->assertContains(
+ $this->assertStringContainsString(
'http://test.com/?foo=new',
HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=&foo=old'),
'Absolute URL and empty query param'
diff --git a/tests/php/Control/IPUtilsTest.php b/tests/php/Control/IPUtilsTest.php
index bc82835d1d4..4c4a20e0214 100644
--- a/tests/php/Control/IPUtilsTest.php
+++ b/tests/php/Control/IPUtilsTest.php
@@ -17,14 +17,14 @@
class IPUtilsTest extends SapphireTest
{
/**
- * @dataProvider testIPv4Provider
+ * @dataProvider iPv4Provider
*/
public function testIPv4($matches, $remoteAddr, $cidr)
{
$this->assertSame($matches, IPUtils::checkIP($remoteAddr, $cidr));
}
- public function testIPv4Provider()
+ public function iPv4Provider()
{
return [
[true, '192.168.1.1', '192.168.1.1'],
@@ -43,7 +43,7 @@ public function testIPv4Provider()
}
/**
- * @dataProvider testIPv6Provider
+ * @dataProvider iPv6Provider
*/
public function testIPv6($matches, $remoteAddr, $cidr)
{
@@ -54,7 +54,7 @@ public function testIPv6($matches, $remoteAddr, $cidr)
$this->assertSame($matches, IPUtils::checkIP($remoteAddr, $cidr));
}
- public function testIPv6Provider()
+ public function iPv6Provider()
{
return [
[true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'],
@@ -71,11 +71,11 @@ public function testIPv6Provider()
}
/**
- * @expectedException \RuntimeException
* @requires extension sockets
*/
public function testAnIPv6WithOptionDisabledIPv6()
{
+ $this->expectException(\RuntimeException::class);
if (defined('AF_INET6')) {
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
}
diff --git a/tests/php/Control/Middleware/CanonicalURLMiddlewareTest.php b/tests/php/Control/Middleware/CanonicalURLMiddlewareTest.php
index e4e3caa56f8..f605b0e07f9 100644
--- a/tests/php/Control/Middleware/CanonicalURLMiddlewareTest.php
+++ b/tests/php/Control/Middleware/CanonicalURLMiddlewareTest.php
@@ -16,7 +16,7 @@ class CanonicalURLMiddlewareTest extends SapphireTest
*/
protected $middleware;
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
@@ -45,7 +45,7 @@ public function testHttpsIsForcedForBasicAuth()
$this->assertNotSame($mockResponse, $result, 'New response is created and returned');
$this->assertEquals(301, $result->getStatusCode(), 'Basic auth responses are redirected');
- $this->assertContains('https://', $result->getHeader('Location'), 'HTTPS is in the redirect location');
+ $this->assertStringContainsString('https://', $result->getHeader('Location'), 'HTTPS is in the redirect location');
}
public function testMiddlewareDelegateIsReturnedWhenBasicAuthRedirectIsDisabled()
diff --git a/tests/php/Control/Middleware/HTTPCacheControlMiddlewareTest.php b/tests/php/Control/Middleware/HTTPCacheControlMiddlewareTest.php
index 1a8b3e4b24b..bd9adbfc920 100644
--- a/tests/php/Control/Middleware/HTTPCacheControlMiddlewareTest.php
+++ b/tests/php/Control/Middleware/HTTPCacheControlMiddlewareTest.php
@@ -8,7 +8,7 @@
class HTTPCacheControlMiddlewareTest extends SapphireTest
{
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
// Set to disabled at null forcing level
@@ -39,7 +39,7 @@ public function testCheckDefaultStates($state, $immutable)
$response = new HTTPResponse();
$cc->applyToResponse($response);
- $this->assertContains('must-revalidate', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('must-revalidate', $response->getHeader('cache-control'));
}
/**
@@ -62,9 +62,9 @@ public function testSetMaxAge($state, $immutable)
if ($immutable) {
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
} else {
- $this->assertContains('max-age=300', $response->getHeader('cache-control'));
- $this->assertNotContains('no-cache', $response->getHeader('cache-control'));
- $this->assertNotContains('no-store', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('max-age=300', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-cache', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-store', $response->getHeader('cache-control'));
}
}
@@ -78,9 +78,9 @@ public function testEnableCacheWithMaxAge()
$response = new HTTPResponse();
$cc->applyToResponse($response);
- $this->assertContains('max-age=300', $response->getHeader('cache-control'));
- $this->assertNotContains('no-cache', $response->getHeader('cache-control'));
- $this->assertNotContains('no-store', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('max-age=300', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-cache', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-store', $response->getHeader('cache-control'));
}
public function testEnableCacheWithMaxAgeAppliesWhenLevelDoesNot()
@@ -94,7 +94,7 @@ public function testEnableCacheWithMaxAgeAppliesWhenLevelDoesNot()
$response = new HTTPResponse();
$cc->applyToResponse($response);
- $this->assertContains('max-age=300', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('max-age=300', $response->getHeader('cache-control'));
}
public function testPublicCacheWithMaxAge()
@@ -107,10 +107,10 @@ public function testPublicCacheWithMaxAge()
$response = new HTTPResponse();
$cc->applyToResponse($response);
- $this->assertContains('max-age=300', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('max-age=300', $response->getHeader('cache-control'));
// STATE_PUBLIC doesn't contain no-cache or no-store headers to begin with,
// so can't test their removal effectively
- $this->assertNotContains('no-cache', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-cache', $response->getHeader('cache-control'));
}
public function testPublicCacheWithMaxAgeAppliesWhenLevelDoesNot()
@@ -124,7 +124,7 @@ public function testPublicCacheWithMaxAgeAppliesWhenLevelDoesNot()
$response = new HTTPResponse();
$cc->applyToResponse($response);
- $this->assertContains('max-age=300', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('max-age=300', $response->getHeader('cache-control'));
}
/**
@@ -150,9 +150,9 @@ public function testSetNoStore($state, $immutable)
if ($immutable) {
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
} else {
- $this->assertContains('no-store', $response->getHeader('cache-control'));
- $this->assertNotContains('max-age', $response->getHeader('cache-control'));
- $this->assertNotContains('s-maxage', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('no-store', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('max-age', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('s-maxage', $response->getHeader('cache-control'));
}
}
@@ -179,9 +179,9 @@ public function testSetNoCache($state, $immutable)
if ($immutable) {
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
} else {
- $this->assertContains('no-cache', $response->getHeader('cache-control'));
- $this->assertNotContains('max-age', $response->getHeader('cache-control'));
- $this->assertNotContains('s-maxage', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('no-cache', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('max-age', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('s-maxage', $response->getHeader('cache-control'));
}
}
@@ -206,9 +206,9 @@ public function testSetSharedMaxAge($state, $immutable)
if ($immutable) {
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
} else {
- $this->assertContains('s-maxage=300', $response->getHeader('cache-control'));
- $this->assertNotContains('no-cache', $response->getHeader('cache-control'));
- $this->assertNotContains('no-store', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('s-maxage=300', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-cache', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('no-store', $response->getHeader('cache-control'));
}
}
@@ -233,9 +233,9 @@ public function testSetMustRevalidate($state, $immutable)
if ($immutable) {
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
} else {
- $this->assertContains('must-revalidate', $response->getHeader('cache-control'));
- $this->assertNotContains('max-age', $response->getHeader('cache-control'));
- $this->assertNotContains('s-maxage', $response->getHeader('cache-control'));
+ $this->assertStringContainsString('must-revalidate', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('max-age', $response->getHeader('cache-control'));
+ $this->assertStringNotContainsString('s-maxage', $response->getHeader('cache-control'));
}
}
diff --git a/tests/php/Control/Middleware/RateLimitMiddlewareTest.php b/tests/php/Control/Middleware/RateLimitMiddlewareTest.php
index 23da5723914..23bda6331b7 100644
--- a/tests/php/Control/Middleware/RateLimitMiddlewareTest.php
+++ b/tests/php/Control/Middleware/RateLimitMiddlewareTest.php
@@ -17,7 +17,7 @@ class RateLimitMiddlewareTest extends FunctionalTest
TestController::class,
];
- protected function setUp()
+ protected function setUp(): void
{
parent::setUp();
DBDatetime::set_mock_now('2017-09-27 00:00:00');
diff --git a/tests/php/Control/RSS/RSSFeedTest.php b/tests/php/Control/RSS/RSSFeedTest.php
index 9e41abca07a..45d772ac54a 100644
--- a/tests/php/Control/RSS/RSSFeedTest.php
+++ b/tests/php/Control/RSS/RSSFeedTest.php
@@ -24,17 +24,17 @@ public function testRSSFeed()
$rssFeed = new RSSFeed($list, "http://www.example.com", "Test RSS Feed", "Test RSS Feed Description");
$content = $rssFeed->outputToBrowser();
- $this->assertContains('http://www.example.org/item-a/', $content);
- $this->assertContains('http://www.example.com/item-b.html', $content);
- $this->assertContains('http://www.example.com/item-c.html', $content);
+ $this->assertStringContainsString('http://www.example.org/item-a/', $content);
+ $this->assertStringContainsString('http://www.example.com/item-b.html', $content);
+ $this->assertStringContainsString('http://www.example.com/item-c.html', $content);
- $this->assertContains('