BehavioursTest.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php /** @noinspection NonAsciiCharacters */
  2. namespace Knuckles\Scribe\Tests\GenerateDocumentation;
  3. use Illuminate\Support\Facades\File as FileFacade;
  4. use Illuminate\Support\Facades\Route as RouteFacade;
  5. use Illuminate\Support\Facades\Storage;
  6. use Knuckles\Scribe\Commands\GenerateDocumentation;
  7. use Knuckles\Scribe\Scribe;
  8. use Knuckles\Scribe\Tests\BaseLaravelTest;
  9. use Knuckles\Scribe\Tests\Fixtures\TestController;
  10. use Knuckles\Scribe\Tests\Fixtures\TestGroupController;
  11. use Knuckles\Scribe\Tests\Fixtures\TestIgnoreThisController;
  12. use Knuckles\Scribe\Tests\Fixtures\TestPartialResourceController;
  13. use Knuckles\Scribe\Tests\Fixtures\TestResourceController;
  14. use Knuckles\Scribe\Tests\Fixtures\TestUser;
  15. use Knuckles\Scribe\Tests\TestHelpers;
  16. use Knuckles\Scribe\Tools\Utils;
  17. class BehavioursTest extends BaseLaravelTest
  18. {
  19. use TestHelpers;
  20. protected function setUp(): void
  21. {
  22. parent::setUp();
  23. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  24. $factory->define(TestUser::class, function () {
  25. return [
  26. 'id' => 4,
  27. 'first_name' => 'Tested',
  28. 'last_name' => 'Again',
  29. 'email' => 'a@b.com',
  30. ];
  31. });
  32. }
  33. public function tearDown(): void
  34. {
  35. Utils::deleteDirectoryAndContents('public/docs');
  36. Utils::deleteDirectoryAndContents('.scribe');
  37. }
  38. /** @test */
  39. public function can_process_traditional_laravel_route_syntax_and_callable_tuple_syntax()
  40. {
  41. RouteFacade::get('/api/test', [TestController::class, 'withEndpointDescription']);
  42. RouteFacade::get('/api/array/test', [TestController::class, 'withEndpointDescription']);
  43. $this->generateAndExpectConsoleOutput(expected: [
  44. 'Processed route: [GET] api/test',
  45. 'Processed route: [GET] api/array/test'
  46. ]);
  47. }
  48. /** @test */
  49. public function processes_head_routes_as_head_not_get()
  50. {
  51. RouteFacade::addRoute('HEAD', '/api/test', [TestController::class, 'withEndpointDescription']);
  52. $this->generateAndExpectConsoleOutput(expected: ['Processed route: [HEAD] api/test']);
  53. }
  54. /**
  55. * @test
  56. * @see https://github.com/knuckleswtf/scribe/issues/53
  57. */
  58. public function can_process_closure_routes()
  59. {
  60. RouteFacade::get('/api/closure', fn() => 'hi');
  61. $this->generateAndExpectConsoleOutput(expected: ['Processed route: [GET] api/closure']);
  62. }
  63. /** @test */
  64. public function calls_afterGenerating_hook_with_correct_paths()
  65. {
  66. $paths = [];
  67. Scribe::afterGenerating(function (array $outputPaths) use (&$paths) {
  68. $paths = $outputPaths;
  69. });
  70. RouteFacade::get('/api/test', [TestController::class, 'withEndpointDescription']);
  71. $this->setConfig([
  72. 'type' => 'laravel',
  73. 'laravel.add_routes' => true,
  74. 'laravel.docs_url' => '/apidocs',
  75. 'postman.enabled' => true,
  76. 'openapi.enabled' => true,
  77. ]);
  78. $this->generate();
  79. $ノ = DIRECTORY_SEPARATOR; // Cross-platform
  80. $this->assertEquals([
  81. 'html' => null,
  82. 'blade' => resource_path("views{$ノ}scribe{$ノ}index.blade.php"),
  83. 'postman' => Storage::disk('local')->path("scribe{$ノ}collection.json"),
  84. 'openapi' => Storage::disk('local')->path("scribe{$ノ}openapi.yaml"),
  85. 'assets' => [
  86. 'js' => public_path("vendor{$ノ}scribe{$ノ}js"),
  87. 'css' => public_path("vendor{$ノ}scribe{$ノ}css"),
  88. 'images' => public_path("vendor{$ノ}scribe{$ノ}images"),
  89. ],
  90. ], $paths);
  91. $this->setConfig([
  92. 'type' => 'static',
  93. 'static.output_path' => 'public/docs',
  94. 'postman.enabled' => false,
  95. 'openapi.enabled' => false,
  96. ]);
  97. $this->generate();
  98. $this->assertEquals([
  99. 'html' => realpath("public{$ノ}docs{$ノ}index.html"),
  100. 'blade' => null,
  101. 'postman' => null,
  102. 'openapi' => null,
  103. 'assets' => [
  104. 'js' => realpath("public{$ノ}docs{$ノ}js"),
  105. 'css' => realpath("public{$ノ}docs{$ノ}css"),
  106. 'images' => realpath("public{$ノ}docs{$ノ}images"),
  107. ],
  108. ], $paths);
  109. Scribe::afterGenerating(fn() => null);
  110. }
  111. /** @test */
  112. public function calls_bootstrap_hook()
  113. {
  114. $commandInstance = null;
  115. Scribe::bootstrap(function (GenerateDocumentation $command) use (&$commandInstance) {
  116. $commandInstance = $command;
  117. });
  118. RouteFacade::get('/api/test', [TestController::class, 'withEndpointDescription']);
  119. $this->generate();
  120. $this->assertTrue($commandInstance instanceof GenerateDocumentation);
  121. Scribe::bootstrap(fn() => null);
  122. }
  123. /** @test */
  124. public function skips_methods_and_classes_with_hidefromapidocumentation_tag()
  125. {
  126. RouteFacade::get('/api/skip', [TestController::class, 'skip']);
  127. RouteFacade::get('/api/skipClass', TestIgnoreThisController::class . '@dummy');
  128. RouteFacade::get('/api/test', [TestController::class, 'withEndpointDescription']);
  129. $this->generateAndExpectConsoleOutput(expected: [
  130. 'Skipping route: [GET] api/skip',
  131. 'Skipping route: [GET] api/skipClass',
  132. 'Processed route: [GET] api/test'
  133. ]);
  134. }
  135. /** @test */
  136. public function warns_of_nonexistent_response_files()
  137. {
  138. RouteFacade::get('/api/non-existent', [TestController::class, 'withNonExistentResponseFile']);
  139. $this->generateAndExpectConsoleOutput(expected: ['@responseFile i-do-not-exist.json does not exist']);
  140. }
  141. /** @test */
  142. public function can_parse_resource_routes()
  143. {
  144. RouteFacade::resource('/api/users', TestResourceController::class)->only(['index', 'store']);
  145. $this->generateAndExpectConsoleOutput(
  146. expected: [
  147. 'Processed route: [GET] api/users',
  148. 'Processed route: [POST] api/users'
  149. ],
  150. notExpected: [
  151. 'Processed route: [PUT,PATCH] api/users/{user}',
  152. 'Processed route: [DELETE] api/users/{user}',]
  153. );
  154. }
  155. /** @test */
  156. public function supports_partial_resource_controller()
  157. {
  158. RouteFacade::resource('/api/users', TestPartialResourceController::class);
  159. $this->generateAndExpectConsoleOutput(expected: [
  160. 'Processed route: [GET] api/users',
  161. 'Processed route: [PUT,PATCH] api/users/{user}'
  162. ]);
  163. }
  164. /** @test */
  165. public function can_customise_static_output_path()
  166. {
  167. RouteFacade::get('/api/action1', TestGroupController::class . '@action1');
  168. $this->setConfig(['type' => 'static', 'static.output_path' => 'static/docs']);
  169. $this->assertFileDoesNotExist('static/docs/index.html');
  170. $this->generate();
  171. $this->assertFileExists('static/docs/index.html');
  172. Utils::deleteDirectoryAndContents('static/');
  173. }
  174. /** @test */
  175. public function can_generate_with_apiresource_tag_but_without_apiresourcemodel_tag()
  176. {
  177. RouteFacade::get('/api/test', [TestController::class, 'withEmptyApiResource']);
  178. $this->generateAndExpectConsoleOutput(expected: [
  179. "Couldn't detect an Eloquent API resource model",
  180. 'Processed route: [GET] api/test'
  181. ]);
  182. }
  183. }