createConfig(), new StoreCodeMapper($this->createRegistry(), $this->createStoreManager('https://example.com/')), $this->createRegistry(), $this->createStoreManager('https://example.com/') ); self::assertSame($expectedUrl, $converter->toLanguageUrl($url, $language)); } /** * @return mixed[] */ public static function productUrlProvider(): array { return [ 'real english US store code product url' => [ 'https://example.com/en_US/product-a', 'en', 'https://example.com/en/product-a', ], 'real german DE store code product url' => [ 'https://example.com/de_DE/product-a', 'de', 'https://example.com/de/product-a', ], 'plain product url gets language prefix' => [ 'https://example.com/product-a', 'en', 'https://example.com/en/product-a', ], 'language product url is not duplicated' => [ 'https://example.com/en/product-a', 'en', 'https://example.com/en/product-a', ], 'query and fragment survive conversion' => [ 'https://example.com/en_US/product-a?q=printer#reviews', 'en', 'https://example.com/en/product-a?q=printer#reviews', ], ]; } public function testSkipsExternalProductUrl(): void { $converter = new UrlPathConverter( $this->createConfig(), new StoreCodeMapper($this->createRegistry(), $this->createStoreManager('https://example.com/')), $this->createRegistry(), $this->createStoreManager('https://example.com/') ); self::assertSame( 'https://cdn.example.net/en_US/product-a', $converter->toLanguageUrl('https://cdn.example.net/en_US/product-a', 'en') ); } public function testSkipsStaticProductAssetPath(): void { $converter = new UrlPathConverter( $this->createConfig(), new StoreCodeMapper($this->createRegistry(), $this->createStoreManager('https://example.com/')), $this->createRegistry(), $this->createStoreManager('https://example.com/') ); self::assertSame( 'https://example.com/static/version123/frontend/product-a.jpg', $converter->toLanguageUrl('https://example.com/static/version123/frontend/product-a.jpg', 'en') ); } public function testOmitsPrefixForWebsiteDefaultLanguage(): void { $mapper = $this->createMock(StoreCodeMapper::class); $mapper->method('isRealStoreCodeInScope')->with('en_US')->willReturn(true); $mapper->method('languageFromStoreCode')->with('en_US')->willReturn('en'); $mapper->method('isLanguageAllowed')->willReturn(false); $mapper->method('isDefaultLanguage')->with('en')->willReturn(true); $converter = new UrlPathConverter( $this->createConfig(), $mapper, $this->createRegistry(), $this->createStoreManager('https://example.com/') ); self::assertSame( 'https://example.com/product-a', $converter->toLanguageUrl('https://example.com/en_US/product-a', 'en') ); } private function createConfig(): Config { $config = $this->createMock(Config::class); $config->method('shouldSkipPath')->willReturnCallback(static function (string $path): bool { $path = strtolower(trim($path, '/')); $skipPrefixes = [ 'admin', 'rest', 'graphql', 'soap', 'static', 'media', 'pub', 'customer/section/load', ]; foreach ($skipPrefixes as $prefix) { if ($path === $prefix || strpos($path, $prefix . '/') === 0) { return true; } } return false; }); return $config; } private function createRegistry(): StoreViewRegistry { $registry = $this->createMock(StoreViewRegistry::class); $registry->method('isKnownLanguage')->willReturnCallback(static function (string $language): bool { return in_array($language, ['en', 'de'], true); }); $registry->method('isKnownStoreCode')->willReturnCallback(static function (string $storeCode): bool { return in_array($storeCode, ['en_US', 'en_DE', 'de_US', 'de_DE'], true); }); $registry->method('getLanguageByStoreCode')->willReturnCallback(static function (string $storeCode): ?string { $map = [ 'en_US' => 'en', 'en_DE' => 'en', 'de_US' => 'de', 'de_DE' => 'de', ]; return $map[$storeCode] ?? null; }); $registry->method('isStoreCodeInWebsite')->willReturnCallback( static fn(string $storeCode, int $websiteId): bool => $websiteId === 1 && in_array($storeCode, ['en_US', 'en_DE', 'de_US', 'de_DE'], true) ); $registry->method('isLanguageInWebsite')->willReturnCallback( static fn(string $language, int $websiteId): bool => $websiteId === 1 && in_array($language, ['en', 'de'], true) ); return $registry; } private function createStoreManager(string $baseUrl): StoreManagerInterface { $store = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->onlyMethods(['getBaseUrl']) ->getMock(); $store->method('getBaseUrl')->willReturn($baseUrl); $storeManager = $this->createMock(StoreManagerInterface::class); $storeManager->method('getStore')->willReturn($store); $website = $this->createMock(Website::class); $website->method('getId')->willReturn(1); $website->method('getDefaultStore')->willReturn($store); $storeManager->method('getWebsite')->willReturn($website); return $storeManager; } }