diff --git a/routes/web.php b/routes/web.php index 32b5e417d..fc235bb3f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -15,6 +15,7 @@ use App\Controller\Demo\DemoDetailController; use App\Controller\Demo\ProcessDemoFormController; use App\Controller\DocumentationController; +use App\Controller\FallbackController; use App\Controller\FindRuleController; use App\Controller\HireTeamController; use App\Controller\HomepageController; @@ -76,3 +77,5 @@ Route::get('ast/{hash}', AstDetailController::class); Route::get('ast', AstController::class); Route::post('process-ast', ProcessAstFormController::class); + +Route::fallback(FallbackController::class); diff --git a/src/Controller/FallbackController.php b/src/Controller/FallbackController.php new file mode 100644 index 000000000..8ca1e5a04 --- /dev/null +++ b/src/Controller/FallbackController.php @@ -0,0 +1,31 @@ +path())->kebab()->toString(); + $ruleMetadata = $this->rectorFinder->findBySlug($slug); + + if (! $ruleMetadata instanceof RuleMetadata) { + abort(404); + } + + return redirect(status: 301)->action(RuleDetailController::class, [ + 'slug' => $ruleMetadata->getSlug(), + ]); + } +} diff --git a/src/Controller/RuleDetailController.php b/src/Controller/RuleDetailController.php index b17722931..1e60ab139 100644 --- a/src/Controller/RuleDetailController.php +++ b/src/Controller/RuleDetailController.php @@ -19,13 +19,20 @@ public function __construct( public function __invoke(string $slug): View|RedirectResponse { - $ruleMetadata = $this->rectorFinder->findBySlug($slug); + $kebab = str($slug)->kebab()->toString(); + $ruleMetadata = $this->rectorFinder->findBySlug($kebab); if (! $ruleMetadata instanceof RuleMetadata) { // nothing found, get back return redirect()->action(FindRuleController::class); } + if ($kebab !== $slug) { + return redirect(status: 301)->action(RuleDetailController::class, [ + 'slug' => $kebab, + ]); + } + return \view('homepage/rule_detail', [ 'page_title' => $ruleMetadata->getRuleShortClass(), 'ruleMetadata' => $ruleMetadata, diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php index dc93fcb42..4230d81ec 100644 --- a/tests/AbstractTestCase.php +++ b/tests/AbstractTestCase.php @@ -11,6 +11,12 @@ abstract class AbstractTestCase extends TestCase { + protected function setup(): void + { + parent::setup(); + $this->withoutVite(); + } + #[Override] public function createApplication(): Application { diff --git a/tests/Controller/FallbackControllerTest.php b/tests/Controller/FallbackControllerTest.php new file mode 100644 index 000000000..d4a73d97d --- /dev/null +++ b/tests/Controller/FallbackControllerTest.php @@ -0,0 +1,30 @@ +get('/simplify-array-search-rector'); + + $response->assertRedirect('/rule-detail/simplify-array-search-rector'); + } + + public function testReturns404ForUnknownSlug(): void + { + $response = $this->get('/unknown-rule'); + + $response->assertNotFound(); + } + + public function testRedirectsWithPascalCaseSlug(): void + { + $response = $this->get('/SimplifyArraySearchRector'); + + $response->assertRedirect('/rule-detail/simplify-array-search-rector'); + } +} diff --git a/tests/Controller/RuleDetailControllerTest.php b/tests/Controller/RuleDetailControllerTest.php new file mode 100644 index 000000000..9aff32e33 --- /dev/null +++ b/tests/Controller/RuleDetailControllerTest.php @@ -0,0 +1,38 @@ +get('/rule-detail/simplify-array-search-rector'); + + $response + ->assertOk() + ->assertViewIs('homepage.rule_detail') + ->assertViewHas('ruleMetadata') + ->assertViewHas('codeMirror', true) + ->assertViewHas('page_title', 'SimplifyArraySearchRector') + ->assertSeeText('SimplifyArraySearchRector'); + } + + public function testItRedirectsToFindRuleIfRuleNotFound(): void + { + $response = $this->get('/rule-detail/unknown-rule'); + + $response->assertRedirect('/find-rule'); + } + + public function testItRedirectsToKebabCaseSlugIfPascalCaseSlug(): void + { + $response = $this->get('/rule-detail/SimplifyArraySearchRector'); + + $response->assertRedirect('/rule-detail/simplify-array-search-rector'); + } +}