Skip to content

Commit

Permalink
DI improvements + tests (#109)
Browse files Browse the repository at this point in the history
* Codestyle: don't require multiline signature

* DI: support overloading loaders and localResolvers as services

* Loader: typo

* Tests: better test structure

* Tests: skip database tests

* Tests: cover loaders as service

* Tests: fix neon loader

* Composer: upgrade dev dependencies to run on PHP 8.1

* Phpstan: errors on lowest

* CI: phpstan php 8.1
  • Loading branch information
f3l1x committed Oct 25, 2022
1 parent d24a581 commit df0b02f
Show file tree
Hide file tree
Showing 17 changed files with 330 additions and 30 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Expand Up @@ -91,7 +91,7 @@ jobs:
id: "extcache"
uses: "shivammathur/cache-extensions@v1"
with:
php-version: "8.0"
php-version: "8.1"
extensions: "${{ env.extensions }}"
key: "${{ env.cache-version }}"

Expand All @@ -105,7 +105,7 @@ jobs:
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "8.0"
php-version: "8.1"
extensions: "${{ env.extensions }}"
tools: "composer:${{ env.composer-version }}"

Expand Down
10 changes: 6 additions & 4 deletions composer.json
Expand Up @@ -19,7 +19,7 @@
"latte/latte": "^2.6|^3.0",
"nette/di": "^3.0.6",
"nette/finder": "^2.5.2",
"nette/http": "^3.0",
"nette/http": "^3.0.7",
"nette/neon": "^3.3.1",
"nette/schema": "^1.0",
"nette/routing": "^3.0",
Expand All @@ -30,10 +30,10 @@
"require-dev": {
"doctrine/orm": "^2.8",
"mockery/mockery": "^1.4",
"nette/application": "^3.0",
"nette/application": "^3.1.0",
"nette/bootstrap": "^3.0",
"nette/database": "^3.1.1",
"nette/robot-loader": " ^3.2",
"nette/robot-loader": " ^3.4.0",
"nette/tester": "^2.3.1",
"ninjify/nunjuck": "^0.3.0",
"ninjify/qa": "^0.13",
Expand All @@ -51,7 +51,9 @@
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/Tests"
"Tests\\": "tests/Tests",
"Tests\\Fixtures\\": "tests/Fixtures",
"Tests\\Toolkit\\": "tests/Toolkit"
}
},
"config": {
Expand Down
14 changes: 7 additions & 7 deletions phpstan.lowest.neon
Expand Up @@ -38,13 +38,6 @@ parameters:
"""
path: src/Latte/Filters.php

-
count: 1
message: """
#^Call to an undefined method Nette\\\\Localization\\\\ITranslator::translate\\(\\)#
"""
path: src/Latte/Filters.php

-
count: 1
message: """
Expand All @@ -69,3 +62,10 @@ parameters:
"""
path: src/Translator.php

-
message: """
#^Fetching class constant class of deprecated class Nette\\\\Bridges\\\\ApplicationLatte\\\\ILatteFactory\\:
use Nette\\\\Bridges\\\\ApplicationLatte\\\\LatteFactory$#
"""
count: 1
path: src/DI/TranslationExtension.php
3 changes: 3 additions & 0 deletions ruleset.xml
Expand Up @@ -3,6 +3,7 @@
<rule ref="./vendor/ninjify/coding-standard/contributte.xml">
<exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousSuffix" />
<exclude name="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix" />
<exclude name="SlevomatCodingStandard.Classes.RequireMultiLineMethodSignature.RequiredMultiLineSignature" />
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation">
<exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName" />
Expand All @@ -18,6 +19,8 @@
<property name="rootNamespaces" type="array">
<element key="src" value="Contributte\Translation" />
<element key="tests/Tests" value="Tests" />
<element key="tests/Fixtures" value="Tests\Fixtures" />
<element key="tests/Toolkit" value="Tests\Toolkit" />
</property>
</properties>
</rule>
Expand Down
32 changes: 32 additions & 0 deletions src/DI/Helpers.php
@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);

namespace Contributte\Translation\DI;

use Contributte\Translation\Exceptions\InvalidState;
use Nette\DI\Definitions\Definition;
use Nette\DI\Definitions\Reference;
use Nette\DI\Definitions\Statement;

class Helpers
{

/**
* @param Statement|class-string $input
* @return class-string
*/
public static function unwrapEntity(Statement|string $input): string
{
if ($input instanceof Statement) {
/** @var class-string|Reference|Definition|mixed[] $entity */
$entity = $input->getEntity();
if (is_string($entity)) {
return $entity;
}

throw new InvalidState('Only string statements allowed');
}

return $input;
}

}
5 changes: 3 additions & 2 deletions src/DI/TranslationExtension.php
Expand Up @@ -2,6 +2,7 @@

namespace Contributte\Translation\DI;

use Contributte\Translation\DI\Helpers as DIHelpers;
use Contributte\Translation\Exceptions\InvalidArgument;
use Contributte\Translation\FallbackResolver;
use Contributte\Translation\Helpers;
Expand Down Expand Up @@ -112,7 +113,7 @@ public function loadConfiguration(): void
$localeResolvers = [];

foreach ($this->config->localeResolvers as $v1) {
$reflection = new ReflectionClass($v1);
$reflection = new ReflectionClass(DIHelpers::unwrapEntity($v1));

if (!$reflection->implementsInterface(ResolverInterface::class)) {
throw new InvalidArgument('Resolver must implement interface "' . ResolverInterface::class . '".');
Expand Down Expand Up @@ -187,7 +188,7 @@ public function loadConfiguration(): void

// Loaders
foreach ($this->config->loaders as $k1 => $v1) {
$reflection = new ReflectionClass($v1);
$reflection = new ReflectionClass(DIHelpers::unwrapEntity($v1));

if (!$reflection->implementsInterface(LoaderInterface::class)) {
throw new InvalidArgument('Loader must implement interface "' . LoaderInterface::class . '".');
Expand Down
2 changes: 0 additions & 2 deletions src/Loaders/DatabaseAbstract.php
Expand Up @@ -41,8 +41,6 @@ public function load(
/** @var array<string, string> $settings */
$settings = Neon::decodeFile($resource);

$settings['table'] = $settings['table'] ?? $domain;

$config = [
'table' => $settings['table'] ?? $domain,
'id' => $settings['id'] ?? self::$defaults['id'],
Expand Down
10 changes: 10 additions & 0 deletions tests/.gitignore
@@ -0,0 +1,10 @@
# Folders - recursive
*.expected
*.actual

# Folders
/tmp

# Files
/*.log
/*.html
17 changes: 17 additions & 0 deletions tests/Fixtures/DummyLoader.php
@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);

namespace Tests\Fixtures;

use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;

class DummyLoader extends ArrayLoader implements LoaderInterface
{

public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
{
return parent::load([], $locale, $domain);
}

}
8 changes: 8 additions & 0 deletions tests/Fixtures/DummyService.php
@@ -0,0 +1,8 @@
<?php declare(strict_types = 1);

namespace Tests\Fixtures;

class DummyService
{

}
12 changes: 12 additions & 0 deletions tests/Fixtures/DummyServiceLoader.php
@@ -0,0 +1,12 @@
<?php declare(strict_types = 1);

namespace Tests\Fixtures;

class DummyServiceLoader extends DummyLoader
{

public function __construct(public DummyService $service)
{
}

}
54 changes: 54 additions & 0 deletions tests/Tests/DI/TranslationExtensionTest.phpt
Expand Up @@ -21,9 +21,12 @@ use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Tester\Assert;
use Tests\CustomTranslatorMock;
use Tests\Fixtures\DummyLoader;
use Tests\Helpers;
use Tests\PsrLoggerMock;
use Tests\TestAbstract;
use Tests\Toolkit\Container;
use Tests\Toolkit\Helpers as ToolkitHelpers;
use UnexpectedValueException;

$container = require __DIR__ . '/../../bootstrap.php';
Expand Down Expand Up @@ -258,6 +261,57 @@ final class TranslationExtensionTest extends TestAbstract
);
}

public function test10(): void
{
$container = Container::of()
->withDefaults()
->withCompiler(function (Compiler $compiler): void {
$compiler->addConfig(ToolkitHelpers::neon('
translation:
loaders:
dummy: Tests\Fixtures\DummyLoader
'));
})
->build();

Assert::notNull($container->getByType(DummyLoader::class));
}

public function test11(): void
{
$container = Container::of()
->withDefaults()
->withCompiler(function (Compiler $compiler): void {
$compiler->addConfig(ToolkitHelpers::neon('
services:
myservice: Tests\Fixtures\DummyService
translation:
loaders:
dummy: Tests\Fixtures\DummyServiceLoader(@myservice)
'));
})
->build();

Assert::notNull($container->getByType(DummyLoader::class));
}

public function test12(): void
{
Assert::exception(static function () {
Container::of()
->withDefaults()
->withCompiler(function (Compiler $compiler): void {
$compiler->addConfig(ToolkitHelpers::neon('
translation:
loaders:
dummy: Tests\Fixtures\DummyService
'));
})
->build();
}, InvalidArgument::class, 'Loader must implement interface "Symfony\Component\Translation\Loader\LoaderInterface".');
}

}

(new TranslationExtensionTest($container))->run();
10 changes: 6 additions & 4 deletions tests/Tests/Loaders/NeonTest.phpt
Expand Up @@ -18,7 +18,7 @@ final class NeonTest extends TestAbstract
{
$file = FileMock::create('
test:
for: "translate"');
for: "translate"', 'neon');

$catalogue = (new Neon())->load($file, 'en');

Expand All @@ -38,10 +38,12 @@ test:
Assert::same('missing.translate', $catalogue->get('missing.translate'));
Assert::same(['domain' => ['test.for' => 'translate']], $catalogue->all());

$catalogue = (new Neon())->load(FileMock::create(''), 'en');
$catalogue = (new Neon())->load(FileMock::create('', 'neon'), 'en');
Assert::same('en', $catalogue->getLocale());
Assert::same(['messages'], $catalogue->getDomains());
Assert::same(['messages' => []], $catalogue->all());
// Assert::same(['messages'], $catalogue->getDomains()); // 6.0
// Assert::same(['messages' => []], $catalogue->all()); // 6.0
// Assert::same([], $catalogue->getDomains()); // 6.1
// Assert::same([], $catalogue->all()); // 6.1
}

public function test02(): void
Expand Down
36 changes: 27 additions & 9 deletions tests/Tests/Loaders/NetteDatabaseTest.phpt
Expand Up @@ -3,7 +3,9 @@
namespace Tests\Loaders;

use Contributte\Translation\Loaders\NetteDatabase;
use Contributte\Translation\Translator;
use Nette\Database\Connection;
use Nette\Database\ConnectionException;
use Nette\Localization\ITranslator;
use Tester\Assert;
use Tests\Helpers;
Expand All @@ -14,15 +16,24 @@ $container = require __DIR__ . '/../../bootstrap.php';
final class NetteDatabaseTest extends TestAbstract
{

public function test01(): void
private Translator $translator;

private Connection $connection;

protected function setUp()
{
parent::setUp();

$container = Helpers::createContainerFromConfigurator(
$this->container->getParameters()['tempDir'],
[
'database' => [
'dsn' => 'mysql:host=127.0.0.1;port=13306;dbname=test',
'user' => 'root',
'password' => '1234',
'options' => [
'lazy' => true,
],
],
'translation' => [
'loaders' => [
Expand All @@ -32,19 +43,26 @@ final class NetteDatabaseTest extends TestAbstract
]
);

/** @var \Contributte\Translation\Translator $translator */
$translator = $container->getByType(ITranslator::class);
$connection = $container->getByType(Connection::class);
$this->translator = $container->getByType(ITranslator::class);
$this->connection = $container->getByType(Connection::class);

$connection->query(file_get_contents(__DIR__ . '/../../sql.sql'));
try {
$this->connection->connect();
} catch (ConnectionException $e) {
$this->skip('Database not connected');
}
}

$translator->setLocale('cs_CZ');
public function test01(): void
{
$this->connection->query(file_get_contents(__DIR__ . '/../../sql.sql'));
$this->translator->setLocale('cs_CZ');

Assert::same('Ahoj', $translator->translate('db_table.hello'));
Assert::same('Ahoj', $this->translator->translate('db_table.hello'));

$translator->setLocale('en_US');
$this->translator->setLocale('en_US');

Assert::same('Hello', $translator->translate('db_table.hello'));
Assert::same('Hello', $this->translator->translate('db_table.hello'));
}

}
Expand Down

0 comments on commit df0b02f

Please sign in to comment.