register(LaravelLocalizationServiceProvider::class); config()->set('laravellocalization.supportedLocales', [ 'fr' => ['name' => 'French', 'script' => 'Latn', 'native' => 'Français'], 'en' => ['name' => 'English', 'script' => 'Latn', 'native' => 'English'], ]); config()->set('laravellocalization.hideDefaultLocaleInURL', false); }); afterEach(function () { Schema::dropIfExists('model_classes'); }); it('can be instantiated and resolved from container with mcamara localizer', function () { /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ // GIVEN config()->set('sitemap-generator.localizer', 'mcamara'); config()->set('sitemap-generator.output', [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ]); config()->set('sitemap-generator.supported_locales', ['fr', 'en']); // WHEN $generator = app(SitemapGenerator::class); // THEN expect($generator)->toBeInstanceOf(SitemapGenerator::class); }); it('generates an empty sitemap when no pages or cruds are configured', function () { /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ // GIVEN $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => [], 'supported_locales' => ['fr'], 'cruds' => [], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap)->toBeInstanceOf(Sitemap::class) ->and($sitemap->getTags())->toBeEmpty(); }); it('generates a sitemap with single pages in a single locale', function () { // GIVEN app('translator')->addLines([ 'routes.about' => 'a-propos', 'routes.contact' => 'contactez-nous', ], 'fr'); /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => ['about', 'contact'], 'supported_locales' => ['fr'], 'cruds' => [], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap)->toBeInstanceOf(Sitemap::class) ->and($sitemap->hasUrl('http://localhost/fr/a-propos'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/fr/contactez-nous'))->toBeTrue() ->and($sitemap->getTags())->toHaveCount(2); }); it('generates a sitemap with single pages and alternates across multiple locales', function () { // GIVEN app('translator')->addLines([ 'routes.about' => 'a-propos', 'routes.contact' => 'contactez-nous', ], 'fr'); app('translator')->addLines([ 'routes.about' => 'about-us', 'routes.contact' => 'contact-us', ], 'en'); /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => ['about', 'contact'], 'supported_locales' => ['fr', 'en'], 'cruds' => [], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap)->toBeInstanceOf(Sitemap::class) ->and($sitemap->hasUrl('http://localhost/fr/a-propos'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/en/about-us'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/fr/contactez-nous'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/en/contact-us'))->toBeTrue(); $aboutTagFr = collect($sitemap->getTags()) ->first(fn (mixed $tag): bool => $tag instanceof Url && $tag->url === 'http://localhost/fr/a-propos'); Assert::isInstanceOf($aboutTagFr, Url::class); expect($aboutTagFr->alternates)->toHaveCount(1) ->and($aboutTagFr->alternates[0]->url)->toBe('http://localhost/en/about-us') ->and($aboutTagFr->alternates[0]->locale)->toBe('en'); $aboutTagEn = collect($sitemap->getTags()) ->first(fn (mixed $tag): bool => $tag instanceof Url && $tag->url === 'http://localhost/en/about-us'); Assert::isInstanceOf($aboutTagEn, Url::class); expect($aboutTagEn->alternates)->toHaveCount(1) ->and($aboutTagEn->alternates[0]->url)->toBe('http://localhost/fr/a-propos') ->and($aboutTagEn->alternates[0]->locale)->toBe('fr'); }); it('ignores untranslated routes gracefully without producing bare locale root', function () { // GIVEN app('translator')->addLines([ 'routes.contact' => 'contactez-nous', ], 'fr'); /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => ['contact', 'missing_translation'], 'supported_locales' => ['fr'], 'cruds' => [], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap->hasUrl('http://localhost/fr/contactez-nous'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/fr'))->toBeFalse() ->and($sitemap->getTags())->toHaveCount(1); }); it('handles partially translated routes across locales', function () { // GIVEN app('translator')->addLines([ 'routes.contact' => 'contactez-nous', 'routes.french_only' => 'seulement-francais', ], 'fr'); app('translator')->addLines([ 'routes.contact' => 'contact-us', ], 'en'); /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => ['contact', 'french_only'], 'supported_locales' => ['fr', 'en'], 'cruds' => [], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap->hasUrl('http://localhost/fr/contactez-nous'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/en/contact-us'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/fr/seulement-francais'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/en'))->toBeFalse() ->and($sitemap->getTags())->toHaveCount(3); $frenchOnlyTag = collect($sitemap->getTags()) ->first(fn (mixed $tag): bool => $tag instanceof Url && $tag->url === 'http://localhost/fr/seulement-francais'); Assert::isInstanceOf($frenchOnlyTag, Url::class); expect($frenchOnlyTag->alternates)->toBeEmpty(); }); it('generates a sitemap with cruds models and alternates', function () { // GIVEN Schema::create('model_classes', function (Blueprint $table) { $table->id(); $table->timestamps(); }); ModelClass::create(); /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => [], 'supported_locales' => ['fr', 'en'], 'cruds' => [ [ 'model' => ModelClass::class, ], ], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap)->toBeInstanceOf(Sitemap::class) ->and($sitemap->hasUrl('http://localhost/fr/posts'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/en/posts'))->toBeTrue(); $crudTagFr = collect($sitemap->getTags()) ->first(fn (mixed $tag): bool => $tag instanceof Url && $tag->url === 'http://localhost/fr/posts'); Assert::isInstanceOf($crudTagFr, Url::class); expect($crudTagFr->alternates)->toHaveCount(1) ->and($crudTagFr->alternates[0]->url)->toBe('http://localhost/en/posts') ->and($crudTagFr->alternates[0]->locale)->toBe('en'); }); it('generates a combined sitemap with single pages and cruds', function () { // GIVEN app('translator')->addLines([ 'routes.home' => 'accueil', ], 'fr'); Schema::create('model_classes', function (Blueprint $table) { $table->id(); $table->timestamps(); }); ModelClass::create(); /** @var \Uwil\SitemapGenerator\Tests\TestCase $this */ $config = new SitemapConfig(new ConfigRepository([ SitemapConfig::CONFIG_KEY => [ 'single_pages' => ['home'], 'supported_locales' => ['fr'], 'cruds' => [ [ 'model' => ModelClass::class, ], ], 'schedule' => false, 'cron' => '* * * * *', 'localizer' => 'mcamara', 'output' => [ 'storage_disk' => $this->outputDisk, 'path' => $this->outputPath, ], ], ])); $translator = app()->make(Translator::class); $generator = new SitemapGenerator( $config, new SitemapHelper( $config, new McamaraRouteLocalizer($translator) ) ); // WHEN $sitemap = $generator->generate(); // THEN expect($sitemap->hasUrl('http://localhost/fr/accueil'))->toBeTrue() ->and($sitemap->hasUrl('http://localhost/fr/posts'))->toBeTrue() ->and($sitemap->getTags())->toHaveCount(2); }); });