Browse Source

tests for parsing closure and custom rules

Missael H. Anda 2 years ago
parent
commit
fd920b4c49

+ 21 - 14
src/Extracting/ParsesValidationRules.php

@@ -6,6 +6,7 @@ use Illuminate\Contracts\Validation\Rule;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Facades\Validator;
 use Illuminate\Support\Str;
+use Illuminate\Validation\ClosureValidationRule;
 use Knuckles\Scribe\Exceptions\CouldntProcessValidationRule;
 use Knuckles\Scribe\Exceptions\ProblemParsingValidationRules;
 use Knuckles\Scribe\Exceptions\ScribeException;
@@ -165,30 +166,36 @@ trait ParsesValidationRules
      */
     protected function parseRule($rule, array &$parameterData, bool $independentOnly, array $allParameters = []): bool
     {
-        if ($rule instanceof Rule) {
-            if (method_exists($rule, 'docs')) {
-                $customData = call_user_func_array([$rule, 'docs'], []) ?: [];
-
-                if (isset($customData['description'])) {
-                    $parameterData['description'] .= ' ' . $customData['description'];
-                    unset($customData['description']);
-                }
-
-                $parameterData = array_merge($parameterData, $customData);
-            }
-        } elseif ($rule instanceof \Closure) {
-            $reflection = new \ReflectionFunction($rule);
+        if ($rule instanceof ClosureValidationRule || $rule instanceof \Closure) {
+            $reflection = new \ReflectionFunction($rule instanceof ClosureValidationRule ? $rule->callback : $rule);
 
             if (is_string($description = $reflection->getDocComment())) {
                 $finalDescription = '';
                 // Cleanup comment block and extract just the description
                 foreach (explode("\n", $description) as $line) {
-                    $cleaned = preg_replace(['/^\/\*+\s*/', '/^\*+\s*/', '/\*+\/$/'], '', trim($line));
+                    $cleaned = preg_replace(['/\*+\/$/', '/^\/\*+\s*/', '/^\*+\s*/'], '', trim($line));
                     if ($cleaned != '') $finalDescription .= ' ' . $cleaned;
                 }
 
                 $parameterData['description'] .= $finalDescription;
             }
+        } elseif ($rule instanceof Rule) {
+            if (method_exists($rule, 'docs')) {
+                $customData = call_user_func_array([$rule, 'docs'], []) ?: [];
+
+                if (isset($customData['description'])) {
+                    $parameterData['description'] .= ' ' . $customData['description'];
+                }
+                if (isset($customData['example'])) {
+                    $parameterData['setter'] = fn () => $customData['example'];
+                } elseif (isset($customData['setter'])) {
+                    $parameterData['setter'] = $customData['setter'];
+                }
+
+                $parameterData = array_merge($parameterData, Arr::except($customData, [
+                    'description', 'example', 'setter',
+                ]));
+            }
         } elseif (is_string($rule)) {
             try {
                 // Convert string rules into rule + arguments (eg "in:1,2" becomes ["in", ["1", "2"]])

+ 42 - 0
tests/Strategies/GetFromFormRequestTest.php

@@ -240,6 +240,27 @@ class GetFromFormRequestTest extends BaseLaravelTest
         Globals::$__instantiateFormRequestUsing = null;
     }
 
+    /** @test */
+    public function custom_rule_example_doesnt_override_form_request_example()
+    {
+        $strategy = new BodyParameters\GetFromFormRequest(new DocumentationConfig([]));
+        $parametersFromFormRequest = $strategy->getParametersFromValidationRules(
+            [
+                'dummy' => ['required', new DummyValidationRule],
+            ],
+            [
+                'dummy' => [
+                    'description' => 'New description.',
+                    'example' => 'Overrided example.',
+                ],
+            ],
+        );
+
+        $parsed = $strategy->normaliseArrayAndObjectParameters($parametersFromFormRequest);
+        $this->assertEquals('Overrided example.', $parsed['dummy']['example']);
+        $this->assertEquals('New description. This is a dummy test rule.', $parsed['dummy']['description']);
+    }
+
     protected function fetchViaBodyParams(\ReflectionMethod $method): array
     {
         $strategy = new BodyParameters\GetFromFormRequest(new DocumentationConfig([]));
@@ -254,3 +275,24 @@ class GetFromFormRequestTest extends BaseLaravelTest
         return $strategy->getParametersFromFormRequest($method, $route);
     }
 }
+
+class DummyValidationRule implements \Illuminate\Contracts\Validation\Rule
+{
+    public function passes($attribute, $value)
+    {
+        return true;
+    }
+
+    public function message()
+    {
+        return '.';
+    }
+
+    public static function docs()
+    {
+        return [
+            'description' => 'This is a dummy test rule.',
+            'example' => 'Default example, only added if none other give.',
+        ];
+    }
+}

+ 76 - 0
tests/Unit/ValidationRuleParsingTest.php

@@ -451,6 +451,61 @@ class ValidationRuleParsingTest extends BaseLaravelTest
         $this->assertCount(2, $results);
         $this->assertEquals(true, $results['array_param']['required']);
     }
+
+    /** @test */
+    public function can_parse_custom_closure_rules()
+    {
+        // Single line DocComment
+        $ruleset = [
+            'closure' => [
+                'bail', 'required',
+                /** This is a single line parsed closure rule. */
+                function ($attribute, $value, $fail) {
+                    $fail('Always fail.');
+                },
+            ],
+        ];
+
+        $results = $this->strategy->parse($ruleset);
+        $this->assertEquals(
+            'This is a single line parsed closure rule.',
+            $results['closure']['description']
+        );
+
+        // Block DocComment
+        $ruleset = [
+            'closure' => [
+                'bail', 'required',
+                /**
+                 * This is a block DocComment
+                 * parsed on a closure rule.
+                 * Extra info.
+                 */
+                function ($attribute, $value, $fail) {
+                    $fail('Always fail.');
+                },
+            ],
+        ];
+
+        $results = $this->strategy->parse($ruleset);
+        $this->assertEquals(
+            'This is a block DocComment parsed on a closure rule. Extra info.',
+            $results['closure']['description']
+        );
+    }
+
+    /** @test */
+    public function can_parse_custom_rule_classes()
+    {
+        $ruleset = [
+            // The page number. Example: 1
+            'custom_rule' => ['bail', 'required', new DummyWithDocsValidationRule],
+        ];
+
+        $results = $this->strategy->parse($ruleset);
+        $this->assertEquals(true, $results['custom_rule']['required']);
+        $this->assertEquals('This is a dummy test rule.', $results['custom_rule']['description']);
+    }
 }
 
 class DummyValidationRule implements \Illuminate\Contracts\Validation\Rule
@@ -465,3 +520,24 @@ class DummyValidationRule implements \Illuminate\Contracts\Validation\Rule
         return '.';
     }
 }
+
+class DummyWithDocsValidationRule implements \Illuminate\Contracts\Validation\Rule
+{
+    public function passes($attribute, $value)
+    {
+        return true;
+    }
+
+    public function message()
+    {
+        return '.';
+    }
+
+    public static function docs()
+    {
+        return [
+            'description' => 'This is a dummy test rule.',
+            'example' => 'Default example, only added if none other give.',
+        ];
+    }
+}