diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 44f2d25..0409ca5 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '7.2', '7.3', '7.4', '8.0', '8.1' ] + php: [ '8.1', '8.2', '8.3' ] name: PHP ${{ matrix.php }} diff --git a/composer.json b/composer.json index 2695cf7..3d7de2b 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "php": ">=7.2", "nette/application": "^3.2", "nette/forms": "^3.2", - "nette/http": "^3.3.2", + "nette/http": "^3.3.1", "nette/security": "^3.2", "nette/tester": "~2.0" }, @@ -24,7 +24,7 @@ "latte/latte": "^3.0", "nette/bootstrap": "~3.2.0", "nextras/secured-links": "@dev", - "ninjify/qa": "^0.13", + "contributte/qa": "^0.3", "phpstan/phpstan": "^1.0", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-nette": "^1.0", @@ -38,5 +38,10 @@ }, "autoload-dev": { "classmap": ["tests/inc"] + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/phpstan.neon b/phpstan.neon index f3ff823..103da34 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,13 +6,11 @@ includes: parameters: level: 8 - checkMissingIterableValueType: false - universalObjectCratesClasses: - Nette\Http\SessionSection ignoreErrors: - - '#Property Nette\\Http\\Request::\$(.*) is not writable.#' + #- '#Property Nette\\Http\\Request::\$(.*) is not writable.#' - message: """ #^Call to deprecated method validateConfig\\(\\) of class Nette\\\\DI\\\\CompilerExtension\\: diff --git a/ruleset.xml b/ruleset.xml index 8bde8db..8b1c0d6 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,18 +1,23 @@ - + + + diff --git a/src/Bridges/Infrastructure/PresenterTesterExtension.php b/src/Bridges/Infrastructure/PresenterTesterExtension.php index c662d74..8fa56d0 100644 --- a/src/Bridges/Infrastructure/PresenterTesterExtension.php +++ b/src/Bridges/Infrastructure/PresenterTesterExtension.php @@ -10,6 +10,7 @@ use Nette\DI\Definitions\Statement; use Nette\Http\IRequest; use Nette\Http\Session; +use Nette\Routing\Router; use Nette\Security\User; use Webnazakazku\MangoTester\Infrastructure\MangoTesterExtension; use Webnazakazku\MangoTester\PresenterTester\IPresenterTesterListener; @@ -18,8 +19,8 @@ class PresenterTesterExtension extends CompilerExtension { - /** @var array */ - public $defaults = [ + /** @var array */ + public array $defaults = [ 'baseUrl' => 'https://test.dev', 'identityFactory' => null, ]; @@ -43,7 +44,7 @@ public function loadConfiguration(): void ->setType(PresenterTesterTestCaseListener::class); $this->requireService(IPresenterFactory::class); $this->requireService(User::class); - $this->requireService(IRouter::class); + $this->requireService(Router::class); $this->requireService(IRequest::class); $this->requireService(Session::class); $this->requireService(Application::class); diff --git a/src/Bridges/Infrastructure/PresenterTesterTestCaseListener.php b/src/Bridges/Infrastructure/PresenterTesterTestCaseListener.php index 9f25b3d..3399c08 100644 --- a/src/Bridges/Infrastructure/PresenterTesterTestCaseListener.php +++ b/src/Bridges/Infrastructure/PresenterTesterTestCaseListener.php @@ -11,7 +11,7 @@ class PresenterTesterTestCaseListener implements ITestCaseListener { /** @var PresenterTester|NULL */ - public $presenterTester; + public ?PresenterTester $presenterTester = null; public function setUp(TestCase $testCase): void { diff --git a/src/PresenterAssert.php b/src/PresenterAssert.php index 785d3d5..440ccc8 100644 --- a/src/PresenterAssert.php +++ b/src/PresenterAssert.php @@ -3,7 +3,7 @@ namespace Webnazakazku\MangoTester\PresenterTester; use Nette\Application\Request; -use Nette\Application\UI; +use Nette\Application\UI\Presenter; use Nette\StaticClass; use Tester\Assert; @@ -12,14 +12,18 @@ class PresenterAssert use StaticClass; + /** + * @param array|null $actual + * @throws \Tester\AssertException + */ public static function assertRequestMatch(Request $expected, ?array $actual, bool $onlyIntersectedParameters = true): void { Assert::notSame(null, $actual); assert($actual !== null); - $presenter = $actual[UI\Presenter::PRESENTER_KEY] ?? null; + $presenter = $actual[Presenter::PresenterKey] ?? null; Assert::same($expected->getPresenterName(), $presenter); - unset($actual[UI\Presenter::PRESENTER_KEY]); + unset($actual[Presenter::PresenterKey]); $expectedParameters = $expected->getParameters(); diff --git a/src/PresenterTester.php b/src/PresenterTester.php index c8240c3..61616f8 100644 --- a/src/PresenterTester.php +++ b/src/PresenterTester.php @@ -14,38 +14,33 @@ use Nette\Http\Request; use Nette\Http\Session; use Nette\Http\UrlScript; +use Nette\Routing\Router; use Nette\Security\User; use Tester\Assert; class PresenterTester { - /** @var Session */ - private $session; + private Session $session; - /** @var IPresenterFactory */ - private $presenterFactory; + private IPresenterFactory $presenterFactory; - /** @var IRouter */ - private $router; + private Router $router; - /** @var Request */ - private $httpRequest; + private Request $httpRequest; - /** @var string */ - private $baseUrl; + private string $baseUrl; - /** @var User */ - private $user; + private User $user; /** @var IPresenterTesterListener[] */ - private $listeners; + private array $listeners; /** @var callable|NULL */ private $identityFactory; /** @var TestPresenterResult[] */ - private $results = []; + private array $results = []; /** * @param IPresenterTesterListener[] $listeners @@ -54,7 +49,7 @@ public function __construct( string $baseUrl, Session $session, IPresenterFactory $presenterFactory, - IRouter $router, + Router $router, IRequest $httpRequest, User $user, array $listeners = [], @@ -93,13 +88,13 @@ public function execute(TestPresenterRequest $testRequest): TestPresenterResult $response = null; } - if ($applicationRequest->getParameter(Presenter::SIGNAL_KEY) && method_exists($presenter, 'isSignalProcessed')) { + if ($applicationRequest->getParameter(Presenter::SignalKey) && method_exists($presenter, 'isSignalProcessed')) { if (!$presenter->isSignalProcessed()) { if ($badRequestException) { $cause = 'BadRequestException with code ' . $badRequestException->getCode() . ' and message "' . $badRequestException->getMessage() . '"'; } else { assert($response !== null); - $cause = get_class($response); + $cause = $response::class; } Assert::fail('Signal has not been processed at all, received ' . $cause); @@ -176,7 +171,7 @@ protected function setupHttpRequest(TestPresenterRequest $request): void $url = new UrlScript((string) $this->router->constructUrl($appRequest->toArray(), $refUrl), '/'); - Closure::bind(function () use ($request, $url) { + Closure::bind(function () use ($request, $url): void { /** @var Request $this */ $this->headers = $request->getHeaders() + $this->headers; if ($request->isAjax()) { @@ -190,13 +185,13 @@ protected function setupHttpRequest(TestPresenterRequest $request): void $this->method = ($request->getPost() || $request->getRawBody()) ? 'POST' : 'GET'; $rawBodyCallback = [$request, 'getRawBody']; $this->rawBodyCallback = \Closure::fromCallable($rawBodyCallback); - }, $this->httpRequest, Request::class)->__invoke(); + }, $this->httpRequest, Request::class)(); } protected function setupUIPresenter(Presenter $presenter): void { $presenter->autoCanonicalize = false; - $presenter->invalidLinkMode = Presenter::INVALID_LINK_EXCEPTION; + $presenter->invalidLinkMode = Presenter::InvalidLinkException; } } diff --git a/src/TestPresenterRequest.php b/src/TestPresenterRequest.php index 70c387a..03fe8e4 100644 --- a/src/TestPresenterRequest.php +++ b/src/TestPresenterRequest.php @@ -16,41 +16,36 @@ class TestPresenterRequest use SmartObject; - /** @var string */ - private $methodName = 'GET'; + private string $methodName = 'GET'; - /** @var array */ - private $headers = []; + /** @var array */ + private array $headers = []; - /** @var string */ - private $presenterName; + private string $presenterName; - /** @var array */ - private $parameters = []; + /** @var array */ + private array $parameters = []; - /** @var array */ - private $post = []; + /** @var array */ + private array $post = []; /** @var string|NULL */ - private $rawBody; + private ?string $rawBody = null; - /** @var array */ - private $files = []; + /** @var array */ + private array $files = []; - /** @var bool */ - private $ajax = false; + private bool $ajax = false; /** @var string|NULL */ - private $componentClass; + private ?string $componentClass = null; - /** @var bool */ - private $shouldHaveIdentity = false; + private bool $shouldHaveIdentity = false; /** @var IIdentity|NULL */ - private $identity; + private ?IIdentity $identity = null; - /** @var Session */ - private $session; + private Session $session; public function __construct(string $presenterName, Session $session) { @@ -68,6 +63,9 @@ public function getMethodName(): string return $this->methodName; } + /** + * @return mixed[] + */ public function getHeaders(): array { return $this->headers; @@ -78,11 +76,17 @@ public function getPresenterName(): string return $this->presenterName; } + /** + * @return mixed[]|string[] + */ public function getParameters(): array { return $this->parameters + ['action' => 'default']; } + /** + * @return mixed[] + */ public function getPost(): array { return $this->post; @@ -93,6 +97,9 @@ public function getRawBody(): ?string return $this->rawBody; } + /** + * @return mixed[] + */ public function getFiles(): array { return $this->files; @@ -119,10 +126,8 @@ public function getIdentity(): ?IIdentity } /** - * @param string $signal - * @param array $componentParameters + * @param array $componentParameters * @param string|NULL $componentClass required for a secured signal - * @return TestPresenterRequest */ public function withSignal(string $signal, array $componentParameters = [], ?string $componentClass = null): TestPresenterRequest { @@ -139,9 +144,7 @@ public function withSignal(string $signal, array $componentParameters = [], ?str $componentClass, 'handle' . lcfirst(substr($signal, $lastDashPosition ? $lastDashPosition + 1 : 0)), [ - $componentName, array_map(function ($param) { - return is_object($param) && method_exists($param, 'getId') ? $param->getId() : $param; - }, $componentParameters)] + $componentName, array_map(fn ($param) => is_object($param) && method_exists($param, 'getId') ? $param->getId() : $param, $componentParameters)] ); $componentParameters['_sec'] = $csrfToken; } @@ -168,6 +171,10 @@ public function withMethod(string $methodName): TestPresenterRequest return $request; } + /** + * @param array $post + * @param array $files + */ public function withForm(string $formName, array $post, array $files = [], bool $withProtection = true): TestPresenterRequest { $request = $this->withSignal($formName . '-submit'); @@ -190,6 +197,9 @@ public function withRawBody(string $rawBody): TestPresenterRequest return $request; } + /** + * @param array $headers + */ public function withHeaders(array $headers): TestPresenterRequest { $request = clone $this; @@ -206,6 +216,9 @@ public function withAjax(bool $enable = true): TestPresenterRequest return $request; } + /** + * @param array $parameters + */ public function withParameters(array $parameters): TestPresenterRequest { $request = clone $this; @@ -214,6 +227,9 @@ public function withParameters(array $parameters): TestPresenterRequest return $request; } + /** + * @param array $post + */ public function withPost(array $post): TestPresenterRequest { $request = clone $this; @@ -222,6 +238,9 @@ public function withPost(array $post): TestPresenterRequest return $request; } + /** + * @param array $files + */ public function withFiles(array $files): TestPresenterRequest { $request = clone $this; diff --git a/src/TestPresenterResult.php b/src/TestPresenterResult.php index 51188c5..ae9a90a 100644 --- a/src/TestPresenterResult.php +++ b/src/TestPresenterResult.php @@ -7,42 +7,38 @@ use Nette\Application\IResponse; use Nette\Application\IRouter; use Nette\Application\Request; +use Nette\Application\Response; use Nette\Application\Responses\JsonResponse; use Nette\Application\Responses\RedirectResponse; use Nette\Application\Responses\TextResponse; use Nette\Application\UI\Presenter; use Nette\ComponentModel\Component; +use Nette\Forms\Control; use Nette\Forms\Form; use Nette\Forms\IControl; use Nette\Http\Request as HttpRequest; use Nette\Http\UrlScript; +use Nette\Routing\Router; use Tester\Assert; class TestPresenterResult { - /** @var IRouter */ - private $router; + private Router $router; - /** @var IPresenter */ - private $presenter; + private IPresenter $presenter; - /** @var Request */ - private $request; + private Request $request; - /** @var IResponse|NULL */ - private $response; + private ?Response $response = null; - /** @var string|NULL */ - private $textResponseSource; + private ?string $textResponseSource = null; - /** @var BadRequestException|NULL */ - private $badRequestException; + private ?BadRequestException $badRequestException = null; - /** @var bool */ - private $responseInspected = false; + private bool $responseInspected = false; - public function __construct(IRouter $router, Request $request, IPresenter $presenter, ?IResponse $response, ?BadRequestException $badRequestException) + public function __construct(Router $router, Request $request, IPresenter $presenter, ?Response $response, ?BadRequestException $badRequestException) { $this->presenter = $presenter; $this->response = $response; @@ -65,13 +61,15 @@ public function getUIPresenter(): Presenter { Assert::type(Presenter::class, $this->presenter); assert($this->presenter instanceof Presenter); + return $this->presenter; } - public function getResponse(): IResponse + public function getResponse(): Response { Assert::null($this->badRequestException); assert($this->response !== null); + return $this->response; } @@ -80,6 +78,7 @@ public function getRedirectResponse(): RedirectResponse $response = $this->getResponse(); Assert::type(RedirectResponse::class, $response); assert($response instanceof RedirectResponse); + return $response; } @@ -88,6 +87,7 @@ public function getTextResponse(): TextResponse $response = $this->getResponse(); Assert::type(TextResponse::class, $response); assert($response instanceof TextResponse); + return $response; } @@ -107,6 +107,7 @@ public function getJsonResponse(): JsonResponse $response = $this->getResponse(); Assert::type(JsonResponse::class, $response); assert($response instanceof JsonResponse); + return $response; } @@ -114,40 +115,41 @@ public function getBadRequestException(): BadRequestException { Assert::null($this->response); assert($this->badRequestException !== null); + return $this->badRequestException; } public function assertHasResponse(?string $type = null): self { $this->responseInspected = true; - Assert::type($type ?? IResponse::class, $this->response); + Assert::type($type ?? Response::class, $this->response); return $this; } /** - * @param string|array|NULL $match + * @param string|array|NULL $match */ - public function assertRenders($match = null): self + public function assertRenders(string|array|null $match = null): self { $this->responseInspected = true; $source = $this->getTextResponseSource(); if ($match === null) { return $this; - } elseif (is_array($match)) { $match = '%A?%' . implode('%A?%', $match) . '%A?%'; } Assert::match($match, $source); + return $this; } /** - * @param string|array $matches + * @param string|array $matches */ - public function assertNotRenders($matches): self + public function assertNotRenders(string|array $matches): self { if (is_string($matches)) { $matches = [$matches]; @@ -169,9 +171,9 @@ public function assertNotRenders($matches): self } /** - * @param array|object|NULL $expected + * @param array|object|NULL $expected */ - public function assertJson($expected = null): self + public function assertJson(array|object|null $expected = null): self { $this->responseInspected = true; $response = $this->getJsonResponse(); @@ -183,7 +185,7 @@ public function assertJson($expected = null): self } /** - * @param array $parameters optional parameters, extra parameters in a redirect request are ignored + * @param array $parameters optional parameters, extra parameters in a redirect request are ignored */ public function assertRedirects(string $presenterName, array $parameters = []): self { @@ -215,7 +217,7 @@ public function assertFormValid(string $formName): self Assert::type(Form::class, $form); assert($form instanceof Form); if ($form->hasErrors()) { - $controls = $form->getComponents(true, IControl::class); + $controls = $form->getComponents(true, Control::class); $errorsStr = []; foreach ($form->getOwnErrors() as $error) { $errorsStr[] = "\town error: " . $error; @@ -239,6 +241,9 @@ public function assertFormValid(string $formName): self return $this; } + /** + * @param array|null $formErrors + */ public function assertFormHasErrors(string $formName, ?array $formErrors = null): self { $this->responseInspected = true; diff --git a/tests/cases/PresenterTesterExtensionTest.phpt b/tests/cases/PresenterTesterExtensionTest.phpt index 9bb22cf..74bd709 100644 --- a/tests/cases/PresenterTesterExtensionTest.phpt +++ b/tests/cases/PresenterTesterExtensionTest.phpt @@ -18,11 +18,9 @@ assert($appConfigurator instanceof InfrastructureConfigurator); class PresenterTesterExtensionTest extends TestCase { - /** @var PresenterTester */ - private $presenterTester; + private PresenterTester $presenterTester; - /** @var TestPresenterTesterListener */ - private $listener; + private TestPresenterTesterListener $listener; public function __construct(PresenterTester $presenterTester, TestPresenterTesterListener $listener) { @@ -30,7 +28,7 @@ class PresenterTesterExtensionTest extends TestCase $this->listener = $listener; } - public function testExtension() + public function testExtension(): void { $rpBaseUrl = new ReflectionProperty(PresenterTester::class, 'baseUrl'); $rpBaseUrl->setAccessible(true); diff --git a/tests/cases/PresenterTesterListenerTest.phpt b/tests/cases/PresenterTesterListenerTest.phpt index 20f990e..3913065 100644 --- a/tests/cases/PresenterTesterListenerTest.phpt +++ b/tests/cases/PresenterTesterListenerTest.phpt @@ -9,21 +9,20 @@ use Webnazakazku\MangoTester\PresenterTester\PresenterTester; $factory = require __DIR__ . '/../bootstrap.php'; - /** * @testCase */ class PresenterTesterListenerTest extends TestCase { - public function testRender(PresenterTester $presenterTester, TestPresenterTesterListener $listener) + public function testRender(PresenterTester $presenterTester, TestPresenterTesterListener $listener): void { $listener->enabled = true; $request = $presenterTester->createRequest('Example'); // action is added in listener $response = $presenterTester->execute($request); - Assert::noError(function () use ($response) { + Assert::noError(function () use ($response): void { $response->assertRenders(['Hello world']); }); Assert::same($response, $listener->passedResult); diff --git a/tests/cases/PresenterTesterTest.phpt b/tests/cases/PresenterTesterTest.phpt index 7f20d26..c522867 100644 --- a/tests/cases/PresenterTesterTest.phpt +++ b/tests/cases/PresenterTesterTest.phpt @@ -9,55 +9,53 @@ use Webnazakazku\MangoTester\PresenterTester\PresenterTester; $factory = require __DIR__ . '/../bootstrap.php'; - /** * @testCase */ class PresenterTesterTest extends TestCase { - public function testRender(PresenterTester $presenterTester) + public function testRender(PresenterTester $presenterTester): void { $request = $presenterTester->createRequest('Example') ->withParameters(['action' => 'render']); $response = $presenterTester->execute($request); - Assert::noError(function () use ($response) { + Assert::noError(function () use ($response): void { $response->assertRenders(['Hello world']); }); - Assert::exception(function () use ($response) { + Assert::exception(function () use ($response): void { $response->assertRenders(['Lorem ipsum']); }, AssertException::class); } - public function testError(PresenterTester $presenterTester) + public function testError(PresenterTester $presenterTester): void { $request = $presenterTester->createRequest('Example') ->withParameters(['action' => 'error']); $response = $presenterTester->execute($request); - Assert::noError(function () use ($response) { + Assert::noError(function () use ($response): void { $response->assertBadRequest(); }); - Assert::exception(function () use ($response) { + Assert::exception(function () use ($response): void { $response->assertRenders(); }, AssertException::class); } - public function testSignal(PresenterTester $presenterTester) + public function testSignal(PresenterTester $presenterTester): void { $request = $presenterTester->createRequest('Example') ->withSignal('signal', ['value' => 'abc']); $response = $presenterTester->execute($request); - Assert::noError(function () use ($response) { + Assert::noError(function () use ($response): void { $response->assertRenders('signal processed with abc'); }); } } - PresenterTesterTest::run($factory); diff --git a/tests/inc/TestPresenterTesterListener.php b/tests/inc/TestPresenterTesterListener.php index ec335cf..cdd5a51 100644 --- a/tests/inc/TestPresenterTesterListener.php +++ b/tests/inc/TestPresenterTesterListener.php @@ -9,11 +9,9 @@ class TestPresenterTesterListener implements IPresenterTesterListener { - /** @var bool */ - public $enabled = false; + public bool $enabled = false; - /** @var TestPresenterResult|null */ - public $passedResult; + public ?TestPresenterResult $passedResult = null; public function onRequest(TestPresenterRequest $request): TestPresenterRequest {