Skip to content

Commit

Permalink
[FEATURE] Frontend functional tests as sub request (#219)
Browse files Browse the repository at this point in the history
Add a core v11 compatible method to execute frontend
functional tests as sub request instead of a PHP sub
process. Deprecate the old way.
  • Loading branch information
lolli42 committed Dec 1, 2020
1 parent f866461 commit d62c872
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 4 deletions.
136 changes: 136 additions & 0 deletions Classes/Core/Functional/Framework/FrameworkState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace TYPO3\TestingFramework\Core\Functional\Framework;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

use TYPO3\CMS\Core\Database\ReferenceIndex;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;

/**
* This is an ugly helper class to manage some TYPO3 framework state.
*
* It is used in functional tests that fire a frontend request (->executeFrontendSubRequest()).
*
* Since TYPO3 still has a couple of static properties and globals that are 'tainted' after a
* request has been handled, this class is a helper to get rid of this state again.
*
* This class should not be needed. It is a manifest of technical core debt.
* It should shrink over time and vanish altogether in the end.
*/
class FrameworkState
{
protected static $state = [];

/**
* Push current state to stack
*/
public static function push()
{
$state = [];
$state['globals-server'] = $GLOBALS['_SERVER'];
$state['globals-beUser'] = $GLOBALS['BE_USER'] ?: null;
// InternalRequestContext->withGlobalSettings() may override globals, especially TYPO3_CONF_VARS
$state['globals-typo3-conf-vars'] = $GLOBALS['TYPO3_CONF_VARS'] ?: null;

// Backing up TCA *should* not be needed: TCA is (hopefully) never changed after bootstrap and identical in FE and BE.
// Some tests change TCA on the fly (eg. core DataHandling/Regular/Modify localizeContentWithEmptyTcaIntegrityColumns).
// A FE call then resets this TCA change since it initializes the global again. Then, after the FE call, the TCA is
// different. And code that runs after that within the test scope may fail (eg. the referenceIndex check in tearDown() that
// relies on TCA). So we backup TCA for now before executing frontend tests.
$state['globals-tca'] = $GLOBALS['TCA'];

// Can be dropped when GeneralUtility::getIndpEnv() is abandoned
$generalUtilityReflection = new \ReflectionClass(GeneralUtility::class);
$generalUtilityIndpEnvCache = $generalUtilityReflection->getProperty('indpEnvCache');
$generalUtilityIndpEnvCache->setAccessible(true);
$state['generalUtilityIndpEnvCache'] = $generalUtilityIndpEnvCache->getValue();

$state['generalUtilitySingletonInstances'] = GeneralUtility::getSingletonInstances();

// Infamous RootlineUtility carries various static state ...
$rootlineUtilityReflection = new \ReflectionClass(RootlineUtility::class);
$rootlineUtilityLocalCache = $rootlineUtilityReflection->getProperty('localCache');
$rootlineUtilityLocalCache->setAccessible(true);
$state['rootlineUtilityLocalCache'] = $rootlineUtilityLocalCache->getValue();
$rootlineUtilityRootlineFields = $rootlineUtilityReflection->getProperty('rootlineFields');
$rootlineUtilityRootlineFields->setAccessible(true);
$state['rootlineUtilityRootlineFields'] = $rootlineUtilityRootlineFields->getValue();
$rootlineUtilityPageRecordCache = $rootlineUtilityReflection->getProperty('pageRecordCache');
$rootlineUtilityPageRecordCache->setAccessible(true);
$state['rootlineUtilityPageRecordCache'] = $rootlineUtilityPageRecordCache->getValue();

self::$state[] = $state;
}

/**
* Reset some state before functional frontend tests are executed
*/
public static function reset()
{
unset($GLOBALS['BE_USER']);

$generalUtilityReflection = new \ReflectionClass(GeneralUtility::class);
$generalUtilityIndpEnvCache = $generalUtilityReflection->getProperty('indpEnvCache');
$generalUtilityIndpEnvCache->setAccessible(true);
$generalUtilityIndpEnvCache = $generalUtilityIndpEnvCache->setValue([]);

GeneralUtility::resetSingletonInstances([]);

RootlineUtility::purgeCaches();
$rootlineUtilityReflection = new \ReflectionClass(RootlineUtility::class);
$rootlineFieldsDefault = $rootlineUtilityReflection->getDefaultProperties();
$rootlineFieldsDefault = $rootlineFieldsDefault['rootlineFields'];
$rootlineUtilityRootlineFields = $rootlineUtilityReflection->getProperty('rootlineFields');
$rootlineUtilityRootlineFields->setAccessible(true);
$state['rootlineUtilityRootlineFields'] = $rootlineFieldsDefault;
}

/**
* Pop state from stash and apply again to set state back to 'before frontend call'
*/
public static function pop()
{
$state = array_pop(self::$state);

$GLOBALS['_SERVER'] = $state['globals-server'];
if ($state['globals-beUser'] !== null) {
$GLOBALS['BE_USER'] = $state['globals-beUser'];
}
if ($state['globals-typo3-conf-vars'] !== null) {
$GLOBALS['TYPO3_CONF_VARS'] = $state['globals-typo3-conf-vars'];
}

$GLOBALS['TCA'] = $state['globals-tca'];

$generalUtilityReflection = new \ReflectionClass(GeneralUtility::class);
$generalUtilityIndpEnvCache = $generalUtilityReflection->getProperty('indpEnvCache');
$generalUtilityIndpEnvCache->setAccessible(true);
$generalUtilityIndpEnvCache->setValue($state['generalUtilityIndpEnvCache']);

GeneralUtility::resetSingletonInstances($state['generalUtilitySingletonInstances']);

$rootlineUtilityReflection = new \ReflectionClass(RootlineUtility::class);
$rootlineUtilityLocalCache = $rootlineUtilityReflection->getProperty('localCache');
$rootlineUtilityLocalCache->setAccessible(true);
$rootlineUtilityLocalCache->setValue($state['rootlineUtilityLocalCache']);
$rootlineUtilityRootlineFields = $rootlineUtilityReflection->getProperty('rootlineFields');
$rootlineUtilityRootlineFields->setAccessible(true);
$rootlineUtilityRootlineFields->setValue($state['rootlineUtilityRootlineFields']);
$rootlineUtilityPageRecordCache = $rootlineUtilityReflection->getProperty('pageRecordCache');
$rootlineUtilityPageRecordCache->setAccessible(true);
$rootlineUtilityPageRecordCache->setValue($state['rootlineUtilityPageRecordCache']);
}
}
10 changes: 9 additions & 1 deletion Classes/Core/Functional/Framework/Frontend/InternalRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\AbstractInstruction;

/**
* Model of internal frontend request context
* Model of internal frontend request context.
*
* This is a helper class in testing-framework context used to execute frontend requests.
* It provides some convenient helper methods like ->withPageId() to easily set up a frontend request.
* It is later turned into a request implementing PSR-7 ServerRequestInterface.
*/
class InternalRequest extends Request implements \JsonSerializable
{
Expand All @@ -34,6 +38,8 @@ class InternalRequest extends Request implements \JsonSerializable
/**
* @param array $data
* @return InternalRequest
* @internal
* @deprecated Can be removed when retrieveFrontendRequestResult() is dropped
*/
public static function fromArray(array $data): InternalRequest
{
Expand Down Expand Up @@ -72,6 +78,8 @@ public function __construct($uri = null)

/**
* @return array
* @internal
* @deprecated Can be removed when retrieveFrontendRequestResult() is dropped, also drop 'implements JsonSerializable'
*/
public function jsonSerialize(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
use TYPO3\TestingFramework\Core\Functional\Framework\AssignablePropertyTrait;

/**
* Model of internal frontend request context
* Model of internal frontend request context.
*
* Helper class for frontend requests to hand over details like 'a backend user should be logged in'.
* This is used by testing-framework extension ext:json_response in its middlewares.
*/
class InternalRequestContext implements \JsonSerializable
{
Expand Down Expand Up @@ -46,6 +49,8 @@ class InternalRequestContext implements \JsonSerializable
/**
* @param array $data
* @return InternalRequestContext
* @internal
* @deprecated Can be removed when retrieveFrontendRequestResult() is dropped
*/
public static function fromArray(array $data)
{
Expand All @@ -54,6 +59,8 @@ public static function fromArray(array $data)

/**
* @return array
* @internal
* @deprecated Can be removed when retrieveFrontendRequestResult() is dropped, also drop 'implements JsonSerializable'
*/
public function jsonSerialize(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

/**
* Bootstrap for direct CLI Request
*
* @internal
* @deprecated This class should be dropped or heavily reduced when retrieveFrontendRequestResult() is dropped.
*/
class RequestBootstrap
{
Expand Down

0 comments on commit d62c872

Please sign in to comment.