From bf46b1238d5b36aafb7266a0cd46acdf2b9a266d Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Thu, 27 Jun 2024 01:50:50 +0300 Subject: [PATCH] Implemented mock checking --- composer.json | 1 + phpunit.xml | 9 +++ src/Contracts/Translator.php | 6 +- src/Integrations/Deepl.php | 4 +- src/Integrations/Google.php | 8 ++- src/Integrations/Integration.php | 31 ++++++--- src/Integrations/Yandex.php | 4 +- src/ServiceProvider.php | 45 ++++++------- src/Services/Translate.php | 11 +++- tests/Datasets/Translators.php | 6 +- tests/Helpers/Mocks.php | 72 ++++++++++++--------- tests/TestCase.php | 23 +++++-- tests/Unit/Can/IntegrationTest.php | 2 +- tests/Unit/Translate/IntegrationCanTest.php | 15 +++++ 14 files changed, 153 insertions(+), 84 deletions(-) diff --git a/composer.json b/composer.json index 083d29f..656f5ab 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "deeplcom/deepl-php": "^1.7", "guzzlehttp/guzzle": "^7.8", "illuminate/support": "^10.0 || ^11.0", + "laravel-lang/config": "^1.0@dev", "laravel-lang/locale-list": "^1.4", "stichoza/google-translate-php": "^5.1" }, diff --git a/phpunit.xml b/phpunit.xml index d5e0876..1f62eb0 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -19,5 +19,14 @@ + + + + + + + + + diff --git a/src/Contracts/Translator.php b/src/Contracts/Translator.php index 8383007..9ac056f 100644 --- a/src/Contracts/Translator.php +++ b/src/Contracts/Translator.php @@ -10,5 +10,9 @@ interface Translator { public function can(Locale|string $to): bool; - public function translate(array|string $text, Locale|string $to, Locale|string|null $from = null): array|string; + public function translate( + iterable|string $text, + Locale|string|null $to, + Locale|string|null $from = null + ): array|string; } diff --git a/src/Integrations/Deepl.php b/src/Integrations/Deepl.php index b165292..bd750c8 100644 --- a/src/Integrations/Deepl.php +++ b/src/Integrations/Deepl.php @@ -10,6 +10,8 @@ class Deepl extends Integration { + public static string $integration = DeeplTranslator::class; + protected array $map = [ Locale::French->value => 'fr', ]; @@ -20,6 +22,6 @@ public function __construct( protected function request(iterable|string $text, Locale|string $to, Locale|string|null $from): Collection { - return collect($this->translator->translateText($text, $this->lang($from), $this->lang($to))); + return collect($this->translator->translateText($text, $this->locale($from), $this->locale($to))); } } diff --git a/src/Integrations/Google.php b/src/Integrations/Google.php index 0a74d65..312b516 100644 --- a/src/Integrations/Google.php +++ b/src/Integrations/Google.php @@ -10,13 +10,15 @@ class Google extends Integration { + public static string $integration = GoogleTranslate::class; + protected array $map = [ Locale::French->value => 'fr', ]; public function __construct( protected GoogleTranslate $translator, - protected string|true $regex = true + protected string|bool $regex = true ) {} protected function request(iterable|string $text, Locale|string $to, Locale|string|null $from): Collection @@ -30,7 +32,7 @@ protected function translator(Locale|string $to, Locale|string|null $from): Goog { return $this->translator ->preserveParameters($this->regex) - ->setSource($this->lang($from)) - ->setTarget($this->lang($to)); + ->setSource($this->locale($from)) + ->setTarget($this->locale($to)); } } diff --git a/src/Integrations/Integration.php b/src/Integrations/Integration.php index 367dae8..132c100 100644 --- a/src/Integrations/Integration.php +++ b/src/Integrations/Integration.php @@ -10,6 +10,8 @@ abstract class Integration implements Translator { + public static string $integration; + protected array $map = []; abstract protected function request( @@ -20,35 +22,44 @@ abstract protected function request( public function can(Locale|string $to): bool { - return $this->lang($to) !== null; + return $this->locale($to) !== null; } public function translate( - array|string $text, - Locale|string $to, + iterable|string $text, + Locale|string|null $to, Locale|string|null $from = null ): array|string { - return is_array($text) + if ($this->invalidLocale($to)) { + return is_string($text) ? $text : collect($text)->all(); + } + + return is_iterable($text) ? $this->request($text, $to, $from)->all() : $this->request($text, $to, $from)->first(); } - protected function lang(Locale|string|null $lang): ?string + protected function locale(Locale|string|null $locale): ?string { - $lang = $lang?->value ?? $lang; + $locale = $locale?->value ?? $locale; - if (empty($lang)) { + if (empty($locale)) { return null; } - if ($value = $this->map[$lang] ?? false) { + if ($value = $this->map[$locale] ?? false) { return $value; } - if (in_array($lang, $this->map, true)) { - return $lang; + if (in_array($locale, $this->map, true)) { + return $locale; } return null; } + + protected function invalidLocale(Locale|string|null $locale): bool + { + return ! $this->locale($locale); + } } diff --git a/src/Integrations/Yandex.php b/src/Integrations/Yandex.php index 5378a96..5e942e3 100644 --- a/src/Integrations/Yandex.php +++ b/src/Integrations/Yandex.php @@ -10,6 +10,8 @@ class Yandex extends Integration { + public static string $integration = YandexCloud::class; + protected array $map = [ Locale::French->value => 'fr', ]; @@ -20,6 +22,6 @@ public function __construct( protected function request(iterable|string $text, Locale|string $to, Locale|string|null $from): Collection { - return collect($this->translator->translate($text, $this->lang($to), $this->lang($from))); + return collect($this->translator->translate($text, $this->locale($to), $this->locale($from))); } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 66f3a7d..849540c 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -4,39 +4,32 @@ namespace LaravelLang\Translator; -use DeepL\Translator; -use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider as BaseServiceProvider; -use LaravelLang\Translator\Integrations\Deepl; -use LaravelLang\Translator\Integrations\Google; -use LaravelLang\Translator\Integrations\Yandex; -use LaravelLang\Translator\Requests\YandexCloud; -use Stichoza\GoogleTranslate\GoogleTranslate; +use LaravelLang\Config\Data\Shared\TranslatorData; +use LaravelLang\Config\Facades\Config; +use LaravelLang\Translator\Contracts\Translator; class ServiceProvider extends BaseServiceProvider { public function boot(): void { - $this->app->singleton(Deepl::class, function (Application $app) { - return new Deepl( - translator: new Translator($app['config']->get('services.deepl.key')), - ); - }); + foreach ($this->translators() as $config) { + $this->bootTranslator($config->translator, $config->credentials); + } + } - $this->app->singleton(Google::class, function (Application $app) { - return new Google( - translator: new GoogleTranslate(), - regex : $app['config']->get('services.google_translate.regex_parameters') ?: true, - ); - }); + protected function bootTranslator(Translator|string $translator, array $credentials): void + { + $this->app->singleton($translator, fn () => new $translator( + new $translator::$integration(...$credentials) + )); + } - $this->app->singleton(Yandex::class, function (Application $app) { - return new Yandex( - new YandexCloud( - key : $app['config']->get('services.yandex_translate.key'), - folderId: $app['config']->get('services.yandex_translate.folder_id'), - ) - ); - }); + /** + * @return array + */ + protected function translators(): array + { + return Config::shared()->translators->enabled; } } diff --git a/src/Services/Translate.php b/src/Services/Translate.php index bb06988..2ac18cd 100644 --- a/src/Services/Translate.php +++ b/src/Services/Translate.php @@ -4,6 +4,8 @@ namespace LaravelLang\Translator\Services; +use LaravelLang\Config\Data\Shared\TranslatorData; +use LaravelLang\Config\Facades\Config; use LaravelLang\LocaleList\Locale; use LaravelLang\Translator\Contracts\Translator; use LaravelLang\Translator\Integrations\Deepl; @@ -14,8 +16,8 @@ class Translate { public function text(iterable|string $text, Locale|string $to, Locale|string|null $from = null): array|string { - foreach ($this->translators() as $class) { - if ($translated = $this->translate($class, $text, $to, $from)) { + foreach ($this->translators() as $service) { + if ($translated = $this->translate($service->translator, $text, $to, $from)) { return $translated; } } @@ -65,9 +67,12 @@ protected function translate( return null; } + /** + * @return array + */ protected function translators(): array { - return config('localization.translators'); + return Config::shared()->translators->enabled; } protected function initialize(string $class): Translator diff --git a/tests/Datasets/Translators.php b/tests/Datasets/Translators.php index 2cee832..a9771db 100644 --- a/tests/Datasets/Translators.php +++ b/tests/Datasets/Translators.php @@ -7,7 +7,7 @@ use LaravelLang\Translator\Integrations\Yandex; dataset('translators', fn () => [ - Deepl::class => [Deepl::class], - Google::class => [Google::class], - Yandex::class => [Yandex::class], + class_basename(Deepl::class) => [Deepl::class], + class_basename(Google::class) => [Google::class], + class_basename(Yandex::class) => [Yandex::class], ]); diff --git a/tests/Helpers/Mocks.php b/tests/Helpers/Mocks.php index 26fe2de..6bae72c 100644 --- a/tests/Helpers/Mocks.php +++ b/tests/Helpers/Mocks.php @@ -2,45 +2,57 @@ declare(strict_types=1); -use DeepL\Translator as DeeplTranslator; +use DeepL\Translator as DeeplTranslate; +use Illuminate\Support\Arr; use LaravelLang\Translator\Integrations\Deepl; use LaravelLang\Translator\Integrations\Google; use LaravelLang\Translator\Integrations\Yandex; -use LaravelLang\Translator\Requests\YandexCloud; +use LaravelLang\Translator\Requests\YandexCloud as YandexTranslate; use Stichoza\GoogleTranslate\GoogleTranslate; use Tests\Constants\Value; -function mockTranslator(string $translator, string $integration, array $methods = []): void +function mockTranslators(array|string $text = null): void { - $service = mock($integration); + mockDeeplTranslator($text); + mockGoogleTranslator($text); + mockYandexTranslator($text); +} - foreach ($methods as $key => $value) { - is_string($key) - ? $service->shouldReceive($key)->andReturn($value) - : $service->shouldReceive($value)->andReturnSelf(); - } +function mockDeeplTranslator(array|string|null $text = null): void +{ + $mock = mock(DeeplTranslate::class); - app()->forgetInstance($translator); - app()->singleton($translator, fn () => new $translator($service)); + $mock->shouldReceive('translateText')->andReturn($text ?? Value::Text1French); + + mockTranslator(Deepl::class, $mock); } -function mockTranslators( - array|string|null $deepl = null, - array|string|null $google = null, - array|string|null $yandex = null, -): void { - mockTranslator(Deepl::class, DeeplTranslator::class, [ - 'translateText' => $deepl ?? Value::Text1French, - ]); - - mockTranslator(Google::class, GoogleTranslate::class, [ - 'translate' => $google ?? Value::Text1French, - 'preserveParameters', - 'setSource', - 'setTarget', - ]); - - mockTranslator(Yandex::class, YandexCloud::class, [ - 'translate' => $yandex ?? [Value::Text1French], - ]); +function mockGoogleTranslator(array|string|null $text = null): void +{ + $mock = mock(GoogleTranslate::class); + + $mock->shouldReceive('preserveParameters', 'setSource', 'setTarget')->andReturnSelf(); + + is_array($text) + ? $mock->shouldReceive('translate')->andReturn(...$text) + : $mock->shouldReceive('translate')->andReturn($text ?? Value::Text1French); + + mockTranslator(Google::class, $mock); +} + +function mockYandexTranslator(array|string|null $text = null): void +{ + $mock = mock(YandexTranslate::class); + + $mock->shouldReceive('translate')->andReturn( + Arr::wrap($text ?? [Value::Text1French]) + ); + + mockTranslator(Yandex::class, $mock); +} + +function mockTranslator(string $translator, mixed $mock): void +{ + app()->forgetInstance($translator); + app()->singleton($translator, fn () => new $translator($mock)); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 18fc3aa..1253746 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,23 +3,36 @@ namespace Tests; use Illuminate\Config\Repository; -use LaravelLang\Translator\ServiceProvider; +use LaravelLang\Config\Enums\Name; +use LaravelLang\Config\ServiceProvider as ConfigServiceProvider; +use LaravelLang\Translator\Integrations\Deepl; +use LaravelLang\Translator\Integrations\Google; +use LaravelLang\Translator\Integrations\Yandex; +use LaravelLang\Translator\ServiceProvider as TranslatorServiceProvider; use Orchestra\Testbench\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { protected function getPackageProviders($app): array { - return [ServiceProvider::class]; + return [ + TranslatorServiceProvider::class, + ConfigServiceProvider::class, + ]; } protected function defineEnvironment($app): void { tap($app['config'], function (Repository $config) { - $config->set('services.deepl.key', 'foo'); + $config->set(Name::Shared() . '.translators.google.translator', Google::class); + $config->set(Name::Shared() . '.translators.google.credentials.key', 'foo'); - $config->set('services.yandex_translate.key', 'foo'); - $config->set('services.yandex_translate.folder_id', 'bar'); + $config->set(Name::Shared() . '.translators.deepl.translator', Deepl::class); + $config->set(Name::Shared() . '.translators.deepl.credentials.key', 'foo'); + + $config->set(Name::Shared() . '.translators.yandex.translator', Yandex::class); + $config->set(Name::Shared() . '.translators.yandex.credentials.key', 'foo'); + $config->set(Name::Shared() . '.translators.yandex.credentials.folter', '123'); }); } } diff --git a/tests/Unit/Can/IntegrationTest.php b/tests/Unit/Can/IntegrationTest.php index 3e51b7f..0be0799 100644 --- a/tests/Unit/Can/IntegrationTest.php +++ b/tests/Unit/Can/IntegrationTest.php @@ -14,5 +14,5 @@ test('cannot be translatable', function (string $translator) { $translator = translator($translator); - expect($translator->can('qwerty'))->toBeTrue(); + expect($translator->can('qwerty'))->toBeFalse(); })->with('translators'); diff --git a/tests/Unit/Translate/IntegrationCanTest.php b/tests/Unit/Translate/IntegrationCanTest.php index 77eab7e..b9de488 100644 --- a/tests/Unit/Translate/IntegrationCanTest.php +++ b/tests/Unit/Translate/IntegrationCanTest.php @@ -14,6 +14,11 @@ })->with('translators'); test('as array without keys', function (string $translator) { + mockTranslators([ + Value::Text1French, + Value::Text2French, + ]); + $translator = translator($translator); expect($translator->translate([Value::Text1English, Value::Text2English], Locale::French))->toBe([ @@ -23,6 +28,11 @@ })->with('translators'); test('as array with keys', function (string $translator) { + mockTranslators([ + 'foo' => Value::Text1French, + 'bar' => Value::Text2French, + ]); + $translator = translator($translator); expect($translator->translate([ @@ -35,6 +45,11 @@ })->with('translators'); test('as collection', function (string $translator) { + mockTranslators([ + 'foo' => Value::Text1French, + 'bar' => Value::Text2French, + ]); + $translator = translator($translator); expect($translator->translate(collect([