<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ /** * Abstract class for the controller tests */ namespace Magento\TestFramework\TestCase; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\MessageInterface; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Theme\Controller\Result\MessagePlugin; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\Response\Http as HttpResponse; /** * @SuppressWarnings(PHPMD.NumberOfChildren) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractController extends \PHPUnit\Framework\TestCase { protected $_runCode = ''; protected $_runScope = 'store'; protected $_runOptions = []; /** * @var \Magento\Framework\App\RequestInterface */ protected $_request; /** * @var \Magento\Framework\App\ResponseInterface */ protected $_response; /** * @var \Magento\TestFramework\ObjectManager */ protected $_objectManager; /** * Whether absence of session error messages has to be asserted automatically upon a test completion * * @var bool */ protected $_assertSessionErrors = false; /** * Bootstrap instance getter * * @return \Magento\TestFramework\Helper\Bootstrap */ protected function _getBootstrap() { return \Magento\TestFramework\Helper\Bootstrap::getInstance(); } /** * Bootstrap application before any test */ protected function setUp() { $this->_assertSessionErrors = false; $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->_objectManager->removeSharedInstance(\Magento\Framework\App\ResponseInterface::class); $this->_objectManager->removeSharedInstance(\Magento\Framework\App\RequestInterface::class); } /** * @inheritDoc */ protected function tearDown() { $this->_request = null; $this->_response = null; $this->_objectManager = null; } /** * Ensure that there were no error messages displayed on the admin panel */ protected function assertPostConditions() { if ($this->_assertSessionErrors) { // equalTo() is intentionally used instead of isEmpty() to provide the informative diff $this->assertSessionMessages( $this->equalTo([]), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } } /** * Run request * * @param string $uri */ public function dispatch($uri) { /** @var HttpRequest $request */ $request = $this->getRequest(); $request->setRequestUri($uri); if ($request->isPost() && !array_key_exists('form_key', $request->getPost()) ) { /** @var FormKey $formKey */ $formKey = $this->_objectManager->get(FormKey::class); $request->setPostValue('form_key', $formKey->getFormKey()); } $this->_getBootstrap()->runApp(); } /** * Request getter * * @return \Magento\Framework\App\RequestInterface|HttpRequest */ public function getRequest() { if (!$this->_request) { $this->_request = $this->_objectManager->get(\Magento\Framework\App\RequestInterface::class); } return $this->_request; } /** * Response getter * * @return \Magento\Framework\App\ResponseInterface|HttpResponse */ public function getResponse() { if (!$this->_response) { $this->_response = $this->_objectManager->get(\Magento\Framework\App\ResponseInterface::class); } return $this->_response; } /** * Assert that response is '404 Not Found' */ public function assert404NotFound() { $this->assertEquals('noroute', $this->getRequest()->getControllerName()); $this->assertContains('404 Not Found', $this->getResponse()->getBody()); } /** * Analyze response object and look for header with specified name, and assert a regex towards its value * * @param string $headerName * @param string $valueRegex * @throws \PHPUnit\Framework\AssertionFailedError when header not found */ public function assertHeaderPcre($headerName, $valueRegex) { $headerFound = false; $headers = $this->getResponse()->getHeaders(); foreach ($headers as $header) { if ($header->getFieldName() === $headerName) { $headerFound = true; $this->assertRegExp($valueRegex, $header->getFieldValue()); } } if (!$headerFound) { $this->fail("Header '{$headerName}' was not found. Headers dump:\n" . var_export($headers, 1)); } } /** * Assert that there is a redirect to expected URL. * Omit expected URL to check that redirect to wherever has been occurred. * Examples of usage: * $this->assertRedirect($this->equalTo($expectedUrl)); * $this->assertRedirect($this->stringStartsWith($expectedUrlPrefix)); * $this->assertRedirect($this->stringEndsWith($expectedUrlSuffix)); * $this->assertRedirect($this->stringContains($expectedUrlSubstring)); * * @param \PHPUnit\Framework\Constraint\Constraint|null $urlConstraint */ public function assertRedirect(\PHPUnit\Framework\Constraint\Constraint $urlConstraint = null) { $this->assertTrue($this->getResponse()->isRedirect(), 'Redirect was expected, but none was performed.'); if ($urlConstraint) { $actualUrl = ''; foreach ($this->getResponse()->getHeaders() as $header) { if ($header->getFieldName() == 'Location') { $actualUrl = $header->getFieldValue(); break; } } $this->assertThat($actualUrl, $urlConstraint, 'Redirection URL does not match expectations'); } } /** * Assert that actual session messages meet expectations: * Usage examples: * $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); * $this->assertSessionMessages($this->equalTo(['Entity has been saved.'], * \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS); * * @param \PHPUnit\Framework\Constraint\Constraint $constraint Constraint to compare actual messages against * @param string|null $messageType Message type filter, * one of the constants \Magento\Framework\Message\MessageInterface::* * @param string $messageManagerClass Class of the session model that manages messages */ public function assertSessionMessages( \PHPUnit\Framework\Constraint\Constraint $constraint, $messageType = null, $messageManagerClass = \Magento\Framework\Message\Manager::class ) { $this->_assertSessionErrors = false; /** @var MessageInterface[]|string[] $messageObjects */ $messages = $this->getMessages($messageType, $messageManagerClass); /** @var string[] $messages */ $messagesFiltered = array_map( function ($message) { /** @var MessageInterface|string $message */ return ($message instanceof MessageInterface) ? $message->toString() : $message; }, $messages ); $this->assertThat( $messagesFiltered, $constraint, 'Session messages do not meet expectations ' . var_export($messagesFiltered, true) ); } /** * Return all stored messages * * @param string|null $messageType * @param string $messageManagerClass * @return array */ protected function getMessages( $messageType = null, $messageManagerClass = \Magento\Framework\Message\Manager::class ) { return array_merge( $this->getSessionMessages($messageType, $messageManagerClass), $this->getCookieMessages($messageType) ); } /** * Return messages stored in session * * @param string|null $messageType * @param string $messageManagerClass * @return array */ protected function getSessionMessages( $messageType = null, $messageManagerClass = \Magento\Framework\Message\Manager::class ) { /** @var $messageManager \Magento\Framework\Message\ManagerInterface */ $messageManager = $this->_objectManager->get($messageManagerClass); /** @var $messages \Magento\Framework\Message\AbstractMessage[] */ if ($messageType === null) { $messages = $messageManager->getMessages()->getItems(); } else { $messages = $messageManager->getMessages()->getItemsByType($messageType); } /** @var $messageManager InterpretationStrategyInterface */ $interpretationStrategy = $this->_objectManager->get(InterpretationStrategyInterface::class); $actualMessages = []; foreach ($messages as $message) { $actualMessages[] = $interpretationStrategy->interpret($message); } return $actualMessages; } /** * Return messages stored in cookies by type * * @param string|null $messageType * @return array */ protected function getCookieMessages($messageType = null) { /** @var $cookieManager CookieManagerInterface */ $cookieManager = $this->_objectManager->get(CookieManagerInterface::class); /** @var $jsonSerializer \Magento\Framework\Serialize\Serializer\Json */ $jsonSerializer = $this->_objectManager->get(\Magento\Framework\Serialize\Serializer\Json::class); try { $messages = $jsonSerializer->unserialize( $cookieManager->getCookie( MessagePlugin::MESSAGES_COOKIES_NAME, $jsonSerializer->serialize([]) ) ); if (!is_array($messages)) { $messages = []; } } catch (\InvalidArgumentException $e) { $messages = []; } $actualMessages = []; foreach ($messages as $message) { if ($messageType === null || $message['type'] == $messageType) { $actualMessages[] = $message['text']; } } return $actualMessages; } }