Skip to content

Commit

Permalink
TASK: Add more functional tests, add testing configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
gradinarufelix committed Dec 9, 2023
1 parent fe39b33 commit be274c1
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 14 deletions.
10 changes: 10 additions & 0 deletions Configuration/Testing/NodeTypes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'Sitegeist.LostInTranslation.Testing:NodeWithAutomaticTranslation':
superTypes:
'Neos.Neos:Node': true
properties:
inlineEditableStringProperty:
type: string
ui:
inlineEditable: true
stringProperty:
type: string
9 changes: 9 additions & 0 deletions Configuration/Testing/Policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
roles:
'Neos.Flow:Everybody':
privileges:
-
privilegeTarget: 'Neos.Neos:Backend.GeneralAccess'
permission: GRANT
-
privilegeTarget: 'Sitegeist.LostInTranslation:AccessBackendModule'
permission: GRANT
56 changes: 56 additions & 0 deletions Configuration/Testing/Settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Neos:
Flow:
persistence:
backendOptions:
dbname: 'flow_functional_testing'
i18n:
defaultLocale: de
ContentRepository:
contentDimensions:
language:
label: Languages
icon: language
# The default dimension that is applied when creating nodes without specifying a dimension
default: de
# The default preset to use if no URI segment was given when resolving languages in the router
defaultPreset: de
presets:
de:
label: Deutsch
values:
- de
uriSegment: ''
en:
label: English
values:
- en
- de
uriSegment: 'en'
options:
translationStrategy: 'sync'
it:
label: Italiano
values:
- it
- de
uriSegment: 'it'
options:
translationStrategy: 'once'
#Sitegeist:
# LostInTranslation:
# nodeTranslation:
# skipAuthorizationChecks: true
# DeepLApi:
# enableCache: true
# authenticationKey: '%env:DEEPL_AUTHENTICATION_KEY%'
# defaultOptions:
# splitting_tags: 'br'
# ignoredTerms:
# - 'Code Q'
# - 'Innsbruck Marketing GmbH'
# - 'Innsbruck Marketing'
# - 'Nordkette'
# - 'Mag.'
# - 'Dr.'
# - 'Dipl.Ing.'
# - 'Dipl.-Ing.'
10 changes: 10 additions & 0 deletions Tests/Functional/AbstractFunctionalTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Sitegeist\LostInTranslation\Tests\Functional;

use Neos\Flow\Tests\FunctionalTestCase;

abstract class AbstractFunctionalTestCase extends FunctionalTestCase
{
protected static $testablePersistenceEnabled = true;
}
213 changes: 213 additions & 0 deletions Tests/Functional/ContentRepository/NodeTranslationServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php

namespace Sitegeist\LostInTranslation\Tests\Functional\ContentRepository;

use Faker\Factory;
use Faker\Generator;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Domain\Model\NodeTemplate;
use Neos\ContentRepository\Domain\Model\NodeType;
use Neos\ContentRepository\Domain\Model\Workspace;
use Neos\ContentRepository\Domain\Repository\WorkspaceRepository;
use Neos\ContentRepository\Domain\Service\Context;
use Neos\ContentRepository\Domain\Service\ContextFactoryInterface;
use Neos\ContentRepository\Domain\Service\NodeTypeManager;
use Neos\ContentRepository\Exception\NodeException;
use Neos\Flow\Utility\Algorithms;
use PHPUnit\Framework\MockObject\MockObject;
use Sitegeist\LostInTranslation\ContentRepository\NodeTranslationService;
use Sitegeist\LostInTranslation\Infrastructure\DeepL\DeepLTranslationService;
use Sitegeist\LostInTranslation\Tests\Functional\AbstractFunctionalTestCase;

class NodeTranslationServiceTest extends AbstractFunctionalTestCase
{
/**
* @var Context
*/
protected $germanUserContext;

/**
* @var Context
*/
protected $germanLiveContext;

/**
* @var Context
*/
protected $englishLiveContext;

/**
* @var Context
*/
protected $italianLiveContext;

/**
* @var Context
*/
protected $italianUserContext;

/**
* @var ContextFactoryInterface
*/
protected $contextFactory;

/**
* @var Workspace
*/
protected $liveWorkspace;

/**
* @var Workspace
*/
protected $userWorkspace;

/**
* @var DeepLTranslationService|MockObject
*/
protected $deeplServiceMock;

/**
* @var Generator
*/
protected $germanFaker;

/**
* @var Generator
*/
protected $englishFaker;

/**
* @var Generator
*/
protected $italianFaker;

/**
* @return void
*/
public function setUp(): void
{
parent::setUp();
$workspaceRepository = $this->objectManager->get(WorkspaceRepository::class);
$this->liveWorkspace = new Workspace('live');
$workspaceRepository->add($this->liveWorkspace);
$this->userWorkspace = new Workspace('test', $this->liveWorkspace);
$workspaceRepository->add($this->userWorkspace);
$this->persistenceManager->persistAll();
$this->contextFactory = $this->objectManager->get(ContextFactoryInterface::class);
// The root live context, where our original content is live
$this->germanLiveContext = $this->contextFactory->create(['workspaceName' => 'live', 'dimensions' => ['language' => ['de']]]);
// The root user context, where the test user creates content, which is eventually published to live
$this->germanUserContext = $this->contextFactory->create(['workspaceName' => 'test', 'dimensions' => ['language' => ['de']]]);
// The English live context, where content from the German live context is synced automatically
$this->englishLiveContext = $this->contextFactory->create(['workspaceName' => 'live', 'dimensions' => ['language' => ['en']]]);
// The Italian live context, where content from the German live context is only copied once on node adoption
$this->italianLiveContext = $this->contextFactory->create(['workspaceName' => 'live', 'dimensions' => ['language' => ['it']]]);
$this->italianUserContext = $this->contextFactory->create(['workspaceName' => 'test', 'dimensions' => ['language' => ['it']]]);

// Mock
$this->deeplServiceMock = $this->getMockBuilder(DeepLTranslationService::class)->getMock();
$nodeTranslationService = $this->objectManager->get(NodeTranslationService::class);
$this->inject($nodeTranslationService, 'translationService', $this->deeplServiceMock);

// Fakers
$this->germanFaker = Factory::create('de_DE');
$this->englishFaker = Factory::create('en_GB');
$this->italianFaker = Factory::create('it_IT');
}

/**
* @return void
*/
public function tearDown(): void
{
parent::tearDown();
$this->inject($this->contextFactory, 'contextInstances', []);
}

/**
* @test
* @return void
* @throws NodeException
*/
public function newNodeInGermanIsAutomaticallyAndCorrectlySyncedToEnglish(): void
{
$germanString = $this->germanFaker->text(100);
$englishString = $this->englishFaker->text(100);
$this->deeplServiceMock->method('translate')->with(['inlineEditableStringProperty' => $germanString], 'en')->willReturn(['inlineEditableStringProperty' => $englishString]);

$nodeInGerman = $this->createTestNode(['inlineEditableStringProperty' => $germanString, 'stringProperty' => $germanString]);
$this->userWorkspace->publishNode($nodeInGerman, $this->liveWorkspace);
$nodeInEnglish = $this->englishLiveContext->getNode('/new-node');

$this->assertTrue(!is_null($nodeInEnglish), 'The node in German was automatically synced into English');
$this->assertEquals($englishString, $nodeInEnglish->getProperty('inlineEditableStringProperty'), 'The inline editable property was translated into English');
$this->assertEquals($germanString, $nodeInEnglish->getProperty('stringProperty'), 'The non-inline editable property was not translated into English');
}

/**
* @test
* @return void
* @throws NodeException
*/
public function newNodeInGermanIsNotAutomaticallySyncedToItalian(): void
{
$this->deeplServiceMock->method('translate')
->withConsecutive([['inlineEditableStringProperty' => 'Hallo Welt!'], 'en'], [['inlineEditableStringProperty' => 'Hallo Welt!'], 'it'])
->willReturnOnConsecutiveCalls(['inlineEditableStringProperty' => 'Hello World!'], ['inlineEditableStringProperty' => 'Hello World!']);

$nodeInGerman = $this->createTestNode();
$this->userWorkspace->publishNode($nodeInGerman, $this->liveWorkspace);

$nodeInItalian = $this->italianLiveContext->getNode('/new-node');
$this->assertTrue(is_null($nodeInItalian), 'The node in German was not automatically synced into Italian');
}

/**
* @test
* @return void
* @throws NodeException
*/
public function newNodeInGermanIsOnAdoptionSyncedToItalian(): void
{
$this->deeplServiceMock->method('translate')
->withConsecutive([['inlineEditableStringProperty' => 'Hallo Welt!'], 'en'], [['inlineEditableStringProperty' => 'Hallo Welt!'], 'it'])
->willReturnOnConsecutiveCalls(['inlineEditableStringProperty' => 'Hello World!'], ['inlineEditableStringProperty' => 'Ciao mondo!']);

$nodeInGerman = $this->createTestNode(['inlineEditableStringProperty' => 'Hallo Welt!', 'stringProperty' => 'Hallo Welt!']);
$this->userWorkspace->publishNode($nodeInGerman, $this->liveWorkspace);
$nodeInItalian = $this->italianUserContext->adoptNode($nodeInGerman);

$this->assertTrue(!is_null($nodeInItalian), 'The node in German was automatically synced into Italian');
$this->assertEquals('Ciao mondo!', $nodeInItalian->getProperty('inlineEditableStringProperty'), 'The inline editable property was translated into Italian');
$this->assertEquals('Hallo Welt!', $nodeInItalian->getProperty('stringProperty'), 'The non-inline editable property was not translated into Italian');
}

protected function getNodeType(string $nodeType): ?NodeType
{
$nodeTypeManager = $this->objectManager->get(NodeTypeManager::class);
return $nodeTypeManager->getNodeType($nodeType);
}

/**
* @param array $properties
* @param string $name
*
* @return NodeInterface
* @throws \Exception
*/
protected function createTestNode(array $properties = [], string $name = 'new-node'): NodeInterface
{
$identifier = Algorithms::generateUUID();
$template = new NodeTemplate();
$template->setName($name);
$template->setIdentifier($identifier);
$template->setNodeType($this->getNodeType('Sitegeist.LostInTranslation.Testing:NodeWithAutomaticTranslation'));
foreach ($properties as $propertyName => $propertyValue) {
$template->setProperty($propertyName, $propertyValue);
}

$rootNodeInSourceContext = $this->germanUserContext->getRootNode();

return $rootNodeInSourceContext->createNodeFromTemplate($template);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
use Neos\Flow\Http\Client\InfiniteRedirectionException;
use Neos\Flow\Security\Account;
use Neos\Flow\Security\Context;
use Neos\Flow\Tests\FunctionalTestCase;
use Neos\Neos\Controller\Backend\ModuleController;
use Neos\Party\Domain\Model\Person;
use Neos\Party\Domain\Model\PersonName;
use Neos\Party\Domain\Service\PartyService;
use Sitegeist\LostInTranslation\Package;
use Sitegeist\LostInTranslation\Tests\Functional\AbstractFunctionalTestCase;

class LostInTranslationModuleControllerTest extends FunctionalTestCase
class LostInTranslationModuleControllerTest extends AbstractFunctionalTestCase
{
protected StringFrontend $apiKeyCache;

Expand Down Expand Up @@ -51,7 +51,8 @@ public function storeCustomKeyActionTest(): void
$this->browser->request('http://localhost/neos/management/sitegeist_lostintranslation?moduleArguments%5B%40package%5D=sitegeist.lostintranslation&moduleArguments%5B%40controller%5D=lostintranslationmodule&moduleArguments%5B%40action%5D=storecustomkey&moduleArguments%5B%40format%5D=html', 'POST', [
"moduleArguments" => [
"key" => $fakeKey
]
],
'__csrfToken' => $this->securityContext->getCsrfProtectionToken()
]);
$this->assertEquals($fakeKey, $this->apiKeyCache->get(Package::API_KEY_CACHE_ID));
}
Expand All @@ -63,7 +64,9 @@ public function storeCustomKeyActionTest(): void
*/
public function removeCustomKeyActionTest(): void
{
$this->browser->request('http://localhost/neos/management/sitegeist_lostintranslation?moduleArguments%5B%40package%5D=sitegeist.lostintranslation&moduleArguments%5B%40controller%5D=lostintranslationmodule&moduleArguments%5B%40action%5D=removecustomkey&moduleArguments%5B%40format%5D=html&moduleArguments%5B%40subpackage%5D=', 'POST');
$this->browser->request('http://localhost/neos/management/sitegeist_lostintranslation?moduleArguments%5B%40package%5D=sitegeist.lostintranslation&moduleArguments%5B%40controller%5D=lostintranslationmodule&moduleArguments%5B%40action%5D=removecustomkey&moduleArguments%5B%40format%5D=html&moduleArguments%5B%40subpackage%5D=', 'POST', [
'__csrfToken' => $this->securityContext->getCsrfProtectionToken()
]);
$this->assertFalse($this->apiKeyCache->get(Package::API_KEY_CACHE_ID));
}
}
4 changes: 0 additions & 4 deletions Tests/FunctionalTestBootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@
shell_exec('cd ' . escapeshellarg($_SERVER['FLOW_ROOTPATH']) . ' && FLOW_CONTEXT=' . escapeshellarg($context) . ' ./flow neos.flow:cache:warmup');
}

shell_exec('cd ' . escapeshellarg($_SERVER['FLOW_ROOTPATH']) . ' && FLOW_CONTEXT=' . escapeshellarg($context) . ' ./flow doctrine:create');
shell_exec('cd ' . escapeshellarg($_SERVER['FLOW_ROOTPATH']) . ' && FLOW_CONTEXT=' . escapeshellarg($context) . ' ./flow user:create --roles Administrator admin admin admin admin');
// todo neos import

require_once($_SERVER['FLOW_ROOTPATH'] . 'Packages/Framework/Neos.Flow/Classes/Core/Bootstrap.php');
$bootstrap = new \Neos\Flow\Core\Bootstrap($context);
$bootstrap->setPreselectedRequestHandlerClassName(\Neos\Flow\Tests\FunctionalTestRequestHandler::class);
Expand Down
2 changes: 1 addition & 1 deletion Tests/FunctionalTests.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
convertWarningsToExceptions="true"
timeoutForSmallTests="0">
<testsuites>
<testsuite>
<testsuite name="Sitegeist.LostInTranslation Functional Tests">
<directory>Functional</directory>
</testsuite>
</testsuites>
Expand Down
7 changes: 2 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
"neos/neos": "^5.3 || ^7.0 || ^8.0 || dev-master",
"neos/http-factories": "^6.3 || ^7.0 || ^8.0 || dev-master"
},
"require-dev": {
"phpstan/phpstan": "0.12.99",
"squizlabs/php_codesniffer": "^3.7"
},
"autoload": {
"psr-4": {
"Sitegeist\\LostInTranslation\\": "Classes/"
Expand All @@ -26,7 +22,8 @@
"squizlabs/php_codesniffer": "^3.7",
"phpunit/phpunit": "^9",
"mockery/mockery": "@stable",
"mikey179/vfsstream": "@stable"
"mikey179/vfsstream": "@stable",
"fakerphp/faker": "^1.23.0"
},
"scripts": {
"fix:style": "phpcbf --colors --standard=PSR12 Classes",
Expand Down

0 comments on commit be274c1

Please sign in to comment.