Commit ad3a216d authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[TASK] Refactor ViewHelper tests

We have a decent test coverage of view helpers, especially
those within ext:fluid. This is an important asset and we're
sure all main functionality works.

Most of the tests rely on ViewHelperBaseTestcase from the
testing framework. This class prepares the main mocking
of view helper dependencies. Reading the code it becomes
obvious that this approach is kinda unfortunate: View
helpers are part of a bigger system - they have some
general dependencies like the rendering context, the
request and render children. This leads to a mocking
party in many unit tests, making the test goal hard to
understand and follow.
The mock preparations and assumptions of internal handling
actively block further separation of concern patches
within ext:fluid since the ViewHelperBaseTestcase breaks
all the time.

The patch refactors all unit tests that extend
ViewHelperBaseTestcase towards functional tests:
Most of them simply create a StandaloneView, feed a
template string for the specific view helper and
string compare the render result. Some FE related VH
tests additionally set up a full frontend and retrieve
a rendered fluid view as sub request.

This makes the tests much easier to read, follow and
understand. The functional tests are now good examples
to show the various features of single VH's.

Change-Id: I6c5d4eeb0c79ba66a18398a5623a591381a6d707
Resolves: #94580
Releases: master
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69857


Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Jochen's avatarJochen <rothjochen@gmail.com>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Jochen's avatarJochen <rothjochen@gmail.com>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 53bbf190
<?php
declare(strict_types=1);
/*
* 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!
*/
namespace TYPO3\CMS\Core\Tests\Functional\ViewHelpers;
use Prophecy\Argument;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class IconForRecordViewHelperTest extends FunctionalTestCase
{
/**
* @test
*/
public function renderRendersIconCallingIconFactoryAccordingToGivenArguments(): void
{
$iconProphecy = $this->prophesize(Icon::class);
$iconProphecy->render(Argument::any())->willReturn('icon html');
$iconFactoryProphecy = $this->prophesize(IconFactory::class);
$iconFactoryProphecy->getIconForRecord(Argument::cetera())->willReturn($iconProphecy->reveal());
GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
$view = new StandaloneView();
$view->setTemplateSource('<core:iconForRecord table="tt_content" row="{uid: 123}" size="large" alternativeMarkupIdentifier="inline" />');
$view->render();
$iconFactoryProphecy->getIconForRecord('tt_content', ['uid' => 123], Icon::SIZE_LARGE)->shouldHaveBeenCalled();
$iconProphecy->render('inline')->shouldHaveBeenCalled();
}
}
......@@ -15,122 +15,79 @@ declare(strict_types=1);
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Tests\Unit\ViewHelpers;
namespace TYPO3\CMS\Core\Tests\Functional\ViewHelpers;
use Prophecy\Argument;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Type\Icon\IconState;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\ViewHelpers\IconViewHelper;
use TYPO3\TestingFramework\Fluid\Unit\ViewHelpers\ViewHelperBaseTestcase;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
/**
* Test case
*/
class IconViewHelperTest extends ViewHelperBaseTestcase
class IconViewHelperTest extends FunctionalTestCase
{
/**
* @var IconViewHelper
*/
protected $viewHelper;
protected function setUp(): void
{
parent::setUp();
$this->viewHelper = $this->getAccessibleMock(IconViewHelper::class, ['renderChildren']);
$this->injectDependenciesIntoViewHelper($this->viewHelper);
$this->viewHelper->initializeArguments();
}
/**
* @test
*/
public function renderCallsIconFactoryWithDefaultSizeAndDefaultStateAndReturnsResult()
public function renderCallsIconFactoryWithDefaultSizeAndDefaultStateAndReturnsResult(): void
{
$iconFactoryProphecy = $this->prophesize(IconFactory::class);
GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
$iconProphecy = $this->prophesize(Icon::class);
$iconFactoryProphecy->getIcon('myIdentifier', Icon::SIZE_SMALL, null, IconState::cast(IconState::STATE_DEFAULT))->shouldBeCalled()->willReturn($iconProphecy->reveal());
$iconProphecy->render(null)->shouldBeCalled()->willReturn('htmlFoo');
$this->viewHelper->setArguments([
'identifier' => 'myIdentifier',
'size' => Icon::SIZE_SMALL,
'overlay' => null,
'state' => IconState::cast(IconState::STATE_DEFAULT),
'alternativeMarkupIdentifier' => null
]);
self::assertSame('htmlFoo', $this->viewHelper->render());
$view = new StandaloneView();
$view->setTemplateSource('<core:icon identifier="myIdentifier" size="small" state="default" />');
self::assertSame('htmlFoo', $view->render());
}
/**
* @test
*/
public function renderCallsIconFactoryWithGivenSizeAndReturnsResult()
public function renderCallsIconFactoryWithGivenSizeAndReturnsResult(): void
{
$iconFactoryProphecy = $this->prophesize(IconFactory::class);
GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
$iconProphecy = $this->prophesize(Icon::class);
$iconFactoryProphecy->getIcon('myIdentifier', Icon::SIZE_LARGE, null, IconState::cast(IconState::STATE_DEFAULT))->shouldBeCalled()->willReturn($iconProphecy->reveal());
$iconProphecy->render(null)->shouldBeCalled()->willReturn('htmlFoo');
$this->viewHelper->setArguments([
'identifier' => 'myIdentifier',
'size' => Icon::SIZE_LARGE,
'overlay' => null,
'state' => IconState::cast(IconState::STATE_DEFAULT),
'alternativeMarkupIdentifier' => null
]);
self::assertSame('htmlFoo', $this->viewHelper->render());
$view = new StandaloneView();
$view->setTemplateSource('<core:icon identifier="myIdentifier" size="large" state="default" />');
self::assertSame('htmlFoo', $view->render());
}
/**
* @test
*/
public function renderCallsIconFactoryWithGivenStateAndReturnsResult()
public function renderCallsIconFactoryWithGivenStateAndReturnsResult(): void
{
$iconFactoryProphecy = $this->prophesize(IconFactory::class);
GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
$iconProphecy = $this->prophesize(Icon::class);
$iconFactoryProphecy->getIcon('myIdentifier', Icon::SIZE_SMALL, null, IconState::cast(IconState::STATE_DISABLED))->shouldBeCalled()->willReturn($iconProphecy->reveal());
$iconProphecy->render(null)->shouldBeCalled()->willReturn('htmlFoo');
$this->viewHelper->setArguments([
'identifier' => 'myIdentifier',
'size' => Icon::SIZE_SMALL,
'overlay' => null,
'state' => IconState::cast(IconState::STATE_DISABLED),
'alternativeMarkupIdentifier' => null
]);
self::assertSame('htmlFoo', $this->viewHelper->render());
$view = new StandaloneView();
$view->setTemplateSource('<core:icon identifier="myIdentifier" size="small" state="disabled" />');
self::assertSame('htmlFoo', $view->render());
}
/**
* @test
*/
public function renderCallsIconFactoryWithGivenOverlayAndReturnsResult()
public function renderCallsIconFactoryWithGivenOverlayAndReturnsResult(): void
{
$iconFactoryProphecy = $this->prophesize(IconFactory::class);
GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
$iconProphecy = $this->prophesize(Icon::class);
$iconFactoryProphecy->getIcon('myIdentifier', Argument::any(), 'overlayString', IconState::cast(IconState::STATE_DEFAULT))->shouldBeCalled()->willReturn($iconProphecy->reveal());
$iconProphecy->render(null)->shouldBeCalled()->willReturn('htmlFoo');
$this->viewHelper->setArguments([
'identifier' => 'myIdentifier',
'size' => Icon::SIZE_LARGE,
'overlay' => 'overlayString',
'state' => IconState::cast(IconState::STATE_DEFAULT),
'alternativeMarkupIdentifier' => null
]);
self::assertSame('htmlFoo', $this->viewHelper->render());
$view = new StandaloneView();
$view->setTemplateSource('<core:icon identifier="myIdentifier" size="large" state="default" overlay="overlayString" />');
self::assertSame('htmlFoo', $view->render());
}
}
<?php
/*
* 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!
*/
namespace TYPO3\CMS\Core\Tests\Unit\ViewHelpers;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\ViewHelpers\IconForRecordViewHelper;
use TYPO3\CMS\Core\ViewHelpers\IconViewHelper;
use TYPO3\TestingFramework\Fluid\Unit\ViewHelpers\ViewHelperBaseTestcase;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* Test case
*/
class IconForRecordViewHelperTest extends ViewHelperBaseTestcase
{
/**
* @var Icon|ObjectProphecy
*/
protected $iconProphecy;
/**
* @var IconFactory|ObjectProphecy
*/
protected $iconFactoryProphecy;
/**
* @var IconViewHelper
*/
protected $viewHelper;
protected function setUp(): void
{
parent::setUp();
$this->iconProphecy = $this->prophesize(Icon::class);
$this->iconProphecy->render(Argument::any())->willReturn('icon html');
$this->iconFactoryProphecy = $this->prophesize(IconFactory::class);
$this->iconFactoryProphecy->getIconForRecord(Argument::cetera())->willReturn($this->iconProphecy->reveal());
GeneralUtility::addInstance(IconFactory::class, $this->iconFactoryProphecy->reveal());
}
/**
* @test
*/
public function renderRendersIconByWayOfTheIconFactoryAccordingToGivenArguments()
{
$renderingContextProphecy = $this->prophesize(RenderingContextInterface::class);
$row = ['uid' => 123];
$arguments = [
'table' => 'tt_content',
'row' => $row,
'size' => Icon::SIZE_LARGE,
'alternativeMarkupIdentifier' => 'inline'
];
$iconForRecordViewHelper = new IconForRecordViewHelper();
$iconForRecordViewHelper->setRenderingContext($renderingContextProphecy->reveal());
$iconForRecordViewHelper->setArguments($arguments);
$iconForRecordViewHelper->render();
$this->iconFactoryProphecy->getIconForRecord('tt_content', $row, Icon::SIZE_LARGE)->shouldHaveBeenCalled();
$this->iconProphecy->render('inline')->shouldHaveBeenCalled();
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -22,9 +24,7 @@ class EscapeChildrenRenderingStandaloneTest extends FunctionalTestCase
{
protected $testExtensionsToLoad = ['typo3/sysext/fluid/Tests/Functional/Fixtures/Extensions/fluid_test'];
protected $coreExtensionsToLoad = ['fluid'];
public function viewHelperTemplateSourcesDataProvider()
public function viewHelperTemplateSourcesDataProvider(): array
{
return [
'EscapeChildrenEnabledAndEscapeOutputDisabled: Tag syntax with children, properly encodes variable value' =>
......@@ -113,20 +113,15 @@ class EscapeChildrenRenderingStandaloneTest extends FunctionalTestCase
}
/**
* @param string $viewHelperTemplate
* @param string $expectedOutput
*
* @test
* @dataProvider viewHelperTemplateSourcesDataProvider
*/
public function renderingTest($viewHelperTemplate, $expectedOutput)
public function renderingTest(string $viewHelperTemplate, string $expectedOutput): void
{
$view = new StandaloneView();
$view->setTemplateSource($viewHelperTemplate);
$view->getRenderingContext()->getViewHelperResolver()->addNamespace('ft', 'TYPO3Fluid\\FluidTest\\ViewHelpers');
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource($viewHelperTemplate);
$view->assign('settings', ['test' => '<strong>Bla</strong>']);
self::assertSame($expectedOutput, $view->render());
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -22,9 +24,7 @@ class EscapeChildrenRenderingTest extends FunctionalTestCase
{
protected $testExtensionsToLoad = ['typo3/sysext/fluid/Tests/Functional/Fixtures/Extensions/fluid_test'];
protected $coreExtensionsToLoad = ['fluid'];
public function viewHelperTemplateSourcesDataProvider()
public function viewHelperTemplateSourcesDataProvider(): array
{
return [
'EscapeChildrenEnabledAndEscapeOutputDisabled: Tag syntax with children, properly encodes variable value' =>
......@@ -113,19 +113,15 @@ class EscapeChildrenRenderingTest extends FunctionalTestCase
}
/**
* @param string $viewHelperTemplate
* @param string $expectedOutput
*
* @test
* @dataProvider viewHelperTemplateSourcesDataProvider
*/
public function renderingTest($viewHelperTemplate, $expectedOutput)
public function renderingTest(string $viewHelperTemplate, string $expectedOutput)
{
$view = new TemplateView();
$view->assign('settings', ['test' => '<strong>Bla</strong>']);
$view->getRenderingContext()->getViewHelperResolver()->addNamespace('ft', 'TYPO3Fluid\\FluidTest\\ViewHelpers');
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource($viewHelperTemplate);
self::assertSame($expectedOutput, $view->render());
}
}
# Fluid Rendering Test Extension for TYPO3 [![Build Status](https://travis-ci.org/helhum/fluid_test.svg?branch=master)](https://travis-ci.org/helhum/fluid_test)
# Fluid rendering test extension, used in various fluid functional tests
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -13,13 +15,10 @@
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Form\Fixtures;
namespace TYPO3\CMS\Fluid\Tests\Functional\Fixtures\ViewHelpers;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* Class ExtendsAbstractEntity
*/
class ExtendsAbstractEntity extends AbstractEntity
{
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -13,59 +15,35 @@
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Form\Fixtures;
namespace TYPO3\CMS\Fluid\Tests\Functional\Fixtures\ViewHelpers;
/**
* Example domain class which can be used to test different view helpers, e.g. the "select" view helper.
*/
class UserDomainClass
{
protected $id;
protected $firstName;
protected $lastName;
protected int $id;
protected string $firstName;
protected string $lastName;
/**
* Constructor.
*
* @param int $id
* @param string $firstName
* @param string $lastName
*/
public function __construct($id, $firstName, $lastName)
public function __construct(int $id, string $firstName, string $lastName)
{
$this->id = $id;
$this->firstName = $firstName;
$this->lastName = $lastName;
}
/**
* Return the ID
*
* @return int ID
*/
public function getId()
public function getId(): int
{
return $this->id;
}
/**
* Return the first name
*
* @return string first name
*/
public function getFirstName()
public function getFirstName(): string
{
return $this->firstName;
}
/**
* Return the last name
*
* @return string lastname
*/
public function getLastName()
public function getLastName(): string
{
return $this->lastName;
}
......
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -13,16 +15,15 @@
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Form\Fixtures;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
namespace TYPO3\CMS\Fluid\Tests\Functional\Fixtures\ViewHelpers;
/**
* [Enter description here]
* Example domain class which can be used to test different view helpers, e.g. the "select" view helper.
*/
class EmptySyntaxTreeNode extends ViewHelperNode
class UserDomainClassToString extends UserDomainClass
{
public function __construct()
public function __toString(): string
{
return $this->firstName . 'ToString';
}
}
"pages",,,,,,,
,"uid","pid","title","sorting","deleted","perms_everybody","slug"
,1,0,"Root",128,0,15,"/"
,2,1,"Dummy 1-2",128,0,15,"/dummy-1-2"
,3,2,"Dummy 1-2-3",128,0,15,"/dummy-1-2/dummy-1-2-3"
,4,3,"Dummy 1-2-3-4",128,0,15,"/dummy-1-2/dummy-1-2-3/dummy-1-2-3-4"
,5,1,"Dummy 1-5",128,0,15,"/dummy-1-5"
,6,5,"Dummy 1-5-6",128,0,15,"/dummy-1-5/dummy-1-5-6"
,7,0,"Root 2",128,0,15,"/"
......@@ -37,13 +37,6 @@ class TemplatesPathsTest extends FunctionalTestCase
'typo3/sysext/fluid/Tests/Functional/Fixtures/Extensions/fluid_test',
];
/**
* @var array
*/
protected $coreExtensionsToLoad = [
'fluid',
];
/**
* @var array
*/
......@@ -78,7 +71,7 @@ class TemplatesPathsTest extends FunctionalTestCase
{
parent::setUp();
$this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/pages.xml');
$this->importCSVDataSet(__DIR__ . '/../Fixtures/pages.csv');
$this->writeSiteConfiguration(
'test',
$this->buildSiteConfiguration(1, 'https://website.local/'),
......
......@@ -15,30 +15,15 @@ declare(strict_types=1);
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Format;
namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers\Asset;
use Prophecy\Argument;
use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Fluid\ViewHelpers\Asset\ScriptViewHelper;
use TYPO3\TestingFramework\Fluid\Unit\ViewHelpers\ViewHelperBaseTestcase;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class ScriptViewHelperTest extends ViewHelperBaseTestcase
class ScriptViewHelperTest extends FunctionalTestCase
{
/**
* @var ScriptViewHelper
*/
protected $viewHelper;
protected function setUp(): void
{
parent::setUp();
$this->viewHelper = new ScriptViewHelper();
$this->injectDependenciesIntoViewHelper($this->viewHelper);
}
/**
* @return array
*/
public function valueDataProvider(): array
{
return [
......@@ -51,7 +36,6 @@ class ScriptViewHelperTest extends ViewHelperBaseTestcase
}
/**
* @param string $src
* @test
* @dataProvider valueDataProvider
*/
......@@ -61,11 +45,12 @@ class ScriptViewHelperTest extends ViewHelperBaseTestcase
$assetCollector
->addJavaScript('test', $src, Argument::any(), Argument::any())
->shouldBeCalled();
$this->viewHelper->injectAssetCollector($assetCollector->reveal());
$this->setArgumentsUnderTest($this->viewHelper, [