diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0e3c218..2015c21 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,8 +8,8 @@ jobs: strategy: matrix: - php: [5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0] - + php: [7.4, 8.0] + steps: - name: Checkout code uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index d685201..797298a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name":"codeception/module-rest", "description":"REST module for Codeception", "keywords":["codeception", "rest"], - "homepage":"http://codeception.com/", + "homepage":"https://codeception.com/", "type":"library", "license":"MIT", "authors":[ @@ -12,12 +12,15 @@ ], "minimum-stability": "RC", "require": { - "php": ">=5.6.6 <9.0", - "softcreatr/jsonpath": "^0.5 || ^0.7", + "php": "^7.4 | ^8.0", + "ext-dom": "*", + "ext-json": "*", "codeception/codeception": "^4.0", - "justinrainbow/json-schema": "~5.2.9" + "justinrainbow/json-schema": "~5.2.9", + "softcreatr/jsonpath": "^0.5 | ^0.7" }, "require-dev": { + "codeception/stub": "^3.7", "codeception/util-universalframework": "^1.0", "codeception/lib-innerbrowser": "^1.0" }, diff --git a/readme.md b/readme.md index 9d1118a..84e9168 100644 --- a/readme.md +++ b/readme.md @@ -7,6 +7,10 @@ A REST module for Codeception [![Total Downloads](https://poser.pugx.org/codeception/module-rest/downloads)](https://packagist.org/packages/codeception/module-rest) [![License](https://poser.pugx.org/codeception/module-rest/license)](/LICENSE) +## Requirements + +* `PHP 7.4` or higher. + ## Installation ``` diff --git a/src/Codeception/Module/REST.php b/src/Codeception/Module/REST.php index 3d5898d..1c715f2 100644 --- a/src/Codeception/Module/REST.php +++ b/src/Codeception/Module/REST.php @@ -1,26 +1,36 @@ + */ protected $config = [ 'url' => '', 'aws' => '' ]; - protected $dependencyMessage = <<client = &$this->connectionModule->client; $this->resetVariables(); } - protected function resetVariables() + protected function resetVariables(): void { $this->params = []; - $this->response = ""; + $this->response = ''; $this->connectionModule->headers = []; } public function _conflicts() { - return 'Codeception\Lib\Interfaces\API'; + return \Codeception\Lib\Interfaces\API::class; } public function _depends() { - return ['Codeception\Lib\InnerBrowser' => $this->dependencyMessage]; + return [InnerBrowser::class => $this->dependencyMessage]; } + /** + * @return string[] + */ public function _parts() { return ['xml', 'json']; @@ -153,31 +172,32 @@ public function _inject(InnerBrowser $connection) if ($this->connectionModule instanceof Framework) { $this->isFunctional = true; } - if ($this->connectionModule instanceof PhpBrowser) { - if (!$this->connectionModule->_getConfig('url')) { - $this->connectionModule->_setConfig(['url' => $this->config['url']]); - } + + if ($this->connectionModule instanceof PhpBrowser && !$this->connectionModule->_getConfig('url')) { + $this->connectionModule->_setConfig(['url' => $this->config['url']]); } } public function _failed(TestInterface $test, $fail) { - if (!$this->response) { + if ($this->response === '' || $this->response === '0') { return; } + $printedResponse = $this->response; if ($this->isBinaryData($printedResponse)) { $printedResponse = $this->binaryToDebugString($printedResponse); } + $test->getMetadata()->addReport('body', $printedResponse); } - - protected function getRunningClient() + protected function getRunningClient(): AbstractBrowser { if ($this->client->getInternalRequest() === null) { throw new ModuleException($this, "Response is empty. Use `\$I->sendXXX()` methods to send HTTP request"); } + return $this->client; } @@ -188,15 +208,12 @@ protected function getRunningClient() * haveHttpHeader('Content-Type', 'application/json'); * // all next requests will contain this header - * ?> * ``` * - * @param $name - * @param $value * @part json * @part xml */ - public function haveHttpHeader($name, $value) + public function haveHttpHeader(string $name, string $value): void { $this->connectionModule->haveHttpHeader($name, $value); } @@ -213,14 +230,13 @@ public function haveHttpHeader($name, $value) * // ... * $I->deleteHeader('X-Requested-With'); * $I->sendPost('some-other-page.php'); - * ?> * ``` * * @param string $name the name of the header to delete. * @part json * @part xml */ - public function deleteHeader($name) + public function deleteHeader(string $name): void { $this->connectionModule->deleteHeader($name); } @@ -229,20 +245,21 @@ public function deleteHeader($name) * Checks over the given HTTP header and (optionally) * its value, asserting that are there * - * @param $name + * @param string $name * @param $value * @part json * @part xml */ - public function seeHttpHeader($name, $value = null) + public function seeHttpHeader(string $name, $value = null): void { if ($value !== null) { - $this->assertEquals( + $this->assertSame( $value, $this->getRunningClient()->getInternalResponse()->getHeader($name) ); return; } + $this->assertNotNull($this->getRunningClient()->getInternalResponse()->getHeader($name)); } @@ -250,12 +267,12 @@ public function seeHttpHeader($name, $value = null) * Checks over the given HTTP header and (optionally) * its value, asserting that are not there * - * @param $name + * @param string $name * @param $value * @part json * @part xml */ - public function dontSeeHttpHeader($name, $value = null) + public function dontSeeHttpHeader(string $name, $value = null): void { if ($value !== null) { $this->assertNotEquals( @@ -264,6 +281,7 @@ public function dontSeeHttpHeader($name, $value = null) ); return; } + $this->assertNull($this->getRunningClient()->getInternalResponse()->getHeader($name)); } @@ -275,14 +293,13 @@ public function dontSeeHttpHeader($name, $value = null) * ``` php * seeHttpHeaderOnce('Cache-Control'); - * ?>> * ``` * - * @param $name + * @param string $name * @part json * @part xml */ - public function seeHttpHeaderOnce($name) + public function seeHttpHeaderOnce(string $name): void { $headers = $this->getRunningClient()->getInternalResponse()->getHeader($name, false); $this->assertCount(1, $headers); @@ -291,14 +308,12 @@ public function seeHttpHeaderOnce($name) /** * Returns the value of the specified header name * - * @param $name - * @param Boolean $first Whether to return the first value or all header values - * + * @param bool $first Whether to return the first value or all header values * @return string|array The first header value if $first is true, an array of values otherwise * @part json * @part xml */ - public function grabHttpHeader($name, $first = true) + public function grabHttpHeader(string $name, bool $first = true) { return $this->getRunningClient()->getInternalResponse()->getHeader($name, $first); } @@ -306,12 +321,10 @@ public function grabHttpHeader($name, $first = true) /** * Adds HTTP authentication via username/password. * - * @param $username - * @param $password * @part json * @part xml */ - public function amHttpAuthenticated($username, $password) + public function amHttpAuthenticated(string $username, string $password): void { if ($this->isFunctional) { $this->client->setServerParameter('PHP_AUTH_USER', $username); @@ -324,27 +337,25 @@ public function amHttpAuthenticated($username, $password) /** * Adds Digest authentication via username/password. * - * @param $username - * @param $password * @part json * @part xml */ - public function amDigestAuthenticated($username, $password) + public function amDigestAuthenticated(string $username, string $password): void { if ($this->isFunctional) { throw new ModuleException(__METHOD__, 'Not supported by functional modules'); } + $this->client->setAuth($username, $password, 'digest'); } /** * Adds Bearer authentication via access token. * - * @param $accessToken * @part json * @part xml */ - public function amBearerAuthenticated($accessToken) + public function amBearerAuthenticated(string $accessToken): void { $this->haveHttpHeader('Authorization', 'Bearer ' . $accessToken); } @@ -358,23 +369,22 @@ public function amBearerAuthenticated($accessToken) * ```php * amNTLMAuthenticated('jon_snow', 'targaryen'); - * ?> * ``` * - * @param $username - * @param $password * @throws ModuleException * @part json * @part xml */ - public function amNTLMAuthenticated($username, $password) + public function amNTLMAuthenticated(string $username, string $password): void { if ($this->isFunctional) { throw new ModuleException(__METHOD__, 'Not supported by functional modules'); } + if (!defined('\GuzzleHttp\Client::MAJOR_VERSION') && !defined('\GuzzleHttp\Client::VERSION')) { throw new ModuleException(__METHOD__, 'Not supported if not using a Guzzle client'); } + $this->client->setAuth($username, $password, 'ntlm'); } @@ -397,12 +407,11 @@ public function amNTLMAuthenticated($username, $password) * ```php * amAWSAuthenticated(); - * ?> * ``` * @param array $additionalAWSConfig * @throws ConfigurationException */ - public function amAWSAuthenticated($additionalAWSConfig = []) + public function amAWSAuthenticated(array $additionalAWSConfig = []): void { if (method_exists($this->client, 'setAwsAuth')) { $config = array_merge($this->config['aws'], $additionalAWSConfig); @@ -410,12 +419,15 @@ public function amAWSAuthenticated($additionalAWSConfig = []) if (!isset($config['key'])) { throw new ConfigurationException('AWS Key is not set'); } + if (!isset($config['secret'])) { throw new ConfigurationException('AWS Secret is not set'); } + if (!isset($config['service'])) { throw new ConfigurationException('AWS Service is not set'); } + if (!isset($config['region'])) { throw new ConfigurationException('AWS Region is not set'); } @@ -452,8 +464,7 @@ public function amAWSAuthenticated($additionalAWSConfig = []) * ]]); * ``` * - * @param $url - * @param array|string|\JsonSerializable $params + * @param array|string|JsonSerializable $params * @param array $files A list of filenames or "mocks" of $_FILES (each entry being an array with the following * keys: name, type, error, size, tmp_name (pointing to the real file path). Each key works * as the "name" attribute of a file input field. @@ -463,7 +474,7 @@ public function amAWSAuthenticated($additionalAWSConfig = []) * @part json * @part xml */ - public function sendPost($url, $params = [], $files = []) + public function sendPost(string $url, $params = [], array $files = []) { return $this->execute('POST', $url, $params, $files); } @@ -471,12 +482,10 @@ public function sendPost($url, $params = [], $files = []) /** * Sends a HEAD request to given uri. * - * @param $url - * @param array $params * @part json * @part xml */ - public function sendHead($url, $params = []) + public function sendHead(string $url, array $params = []) { return $this->execute('HEAD', $url, $params); } @@ -484,12 +493,10 @@ public function sendHead($url, $params = []) /** * Sends an OPTIONS request to given uri. * - * @param $url - * @param array $params * @part json * @part xml */ - public function sendOptions($url, $params = []) + public function sendOptions(string $url, array $params = []): void { $this->execute('OPTIONS', $url, $params); } @@ -505,12 +512,10 @@ public function sendOptions($url, $params = []) * $I->sendGet('/orders', ['id' => 1]) * ``` * - * @param $url - * @param array $params * @part json * @part xml */ - public function sendGet($url, $params = []) + public function sendGet(string $url, array $params = []) { return $this->execute('GET', $url, $params); } @@ -523,13 +528,11 @@ public function sendGet($url, $params = []) * $response = $I->sendPut('/message/1', ['subject' => 'Read this!']); * ``` * - * @param $url * @param array|string|\JsonSerializable $params - * @param array $files * @part json * @part xml */ - public function sendPut($url, $params = [], $files = []) + public function sendPut(string $url, $params = [], array $files = []) { return $this->execute('PUT', $url, $params, $files); } @@ -542,13 +545,11 @@ public function sendPut($url, $params = [], $files = []) * $response = $I->sendPatch('/message/1', ['subject' => 'Read this!']); * ``` * - * @param $url * @param array|string|\JsonSerializable $params - * @param array $files * @part json * @part xml */ - public function sendPatch($url, $params = [], $files = []) + public function sendPatch(string $url, $params = [], array $files = []) { return $this->execute('PATCH', $url, $params, $files); } @@ -560,15 +561,11 @@ public function sendPatch($url, $params = [], $files = []) * sendDelete('/message/1'); * ``` - * - * @param $url - * @param array $params - * @param array $files * @part json * @part xml */ - public function sendDelete($url, $params = [], $files = []) + public function sendDelete(string $url, array $params = [], array $files = []) { return $this->execute('DELETE', $url, $params, $files); } @@ -576,14 +573,11 @@ public function sendDelete($url, $params = [], $files = []) /** * Sends a HTTP request. * - * @param $method - * @param $url - * @param array|string|\JsonSerializable $params - * @param array $files + * @param array|string|JsonSerializable $params * @part json * @part xml */ - public function send($method, $url, $params = [], $files = []) + public function send(string $method, string $url, $params = [], array $files = []) { return $this->execute(strtoupper($method), $url, $params, $files); } @@ -597,16 +591,16 @@ public function send($method, $url, $params = [], $files = []) * * @author samva.ua@gmail.com */ - private function setHeaderLink(array $linkEntries) + private function setHeaderLink(array $linkEntries): void { $values = []; foreach ($linkEntries as $linkEntry) { - \PHPUnit\Framework\Assert::assertArrayHasKey( + Assert::assertArrayHasKey( 'uri', $linkEntry, 'linkEntry should contain property "uri"' ); - \PHPUnit\Framework\Assert::assertArrayHasKey( + Assert::assertArrayHasKey( 'link-param', $linkEntry, 'linkEntry should contain property "link-param"' @@ -620,7 +614,6 @@ private function setHeaderLink(array $linkEntries) /** * Sends LINK request to given uri. * - * @param $url * @param array $linkEntries (entry is array with keys "uri" and "link-param") * * @link http://tools.ietf.org/html/rfc2068#section-19.6.2.4 @@ -629,7 +622,7 @@ private function setHeaderLink(array $linkEntries) * @part json * @part xml */ - public function sendLink($url, array $linkEntries) + public function sendLink(string $url, array $linkEntries): void { $this->setHeaderLink($linkEntries); $this->execute('LINK', $url); @@ -638,19 +631,25 @@ public function sendLink($url, array $linkEntries) /** * Sends UNLINK request to given uri. * - * @param $url * @param array $linkEntries (entry is array with keys "uri" and "link-param") * @link http://tools.ietf.org/html/rfc2068#section-19.6.2.4 * @author samva.ua@gmail.com * @part json * @part xml */ - public function sendUnlink($url, array $linkEntries) + public function sendUnlink(string $url, array $linkEntries): void { $this->setHeaderLink($linkEntries); $this->execute('UNLINK', $url); } + /** + * @param $method + * @param $url + * @param array|string|object $parameters + * @param array $files + * @throws ModuleException|ExternalUrlException|JsonException + */ protected function execute($method, $url, $parameters = [], $files = []) { // allow full url to be requested @@ -682,25 +681,32 @@ protected function execute($method, $url, $parameters = [], $files = []) } else { $url .= '?'; } + $url .= http_build_query($parameters); } - $this->debugSection("Request", "$method $url"); + + $this->debugSection("Request", sprintf('%s %s', $method, $url)); $files = []; } else { - $this->debugSection("Request", "$method $url " . json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION)); + $this->debugSection("Request", + sprintf('%s %s ', $method, $url) . json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR) + ); $files = $this->formatFilesArray($files); } - $this->response = (string)$this->connectionModule->_request($method, $url, $parameters, $files); + + $this->response = $this->connectionModule->_request($method, $url, $parameters, $files); } else { $requestData = $parameters; if ($this->isBinaryData($requestData)) { $requestData = $this->binaryToDebugString($requestData); } - $this->debugSection("Request", "$method $url " . $requestData); - $this->response = (string)$this->connectionModule->_request($method, $url, [], $files, [], $parameters); + + $this->debugSection("Request", sprintf('%s %s ', $method, $url) . $requestData); + $this->response = $this->connectionModule->_request($method, $url, [], $files, [], $parameters); } + $printedResponse = $this->response; - if ($this->isBinaryData($printedResponse)) { + if ($this->isBinaryData((string) $printedResponse)) { $printedResponse = $this->binaryToDebugString($printedResponse); } @@ -720,9 +726,8 @@ protected function execute($method, $url, $parameters = [], $files = []) * Check if data has non-printable bytes and it is not a valid unicode string * * @param string $data the text or binary data string - * @return boolean */ - protected function isBinaryData($data) + protected function isBinaryData(string $data): bool { return !ctype_print($data) && false === mb_detect_encoding($data, mb_detect_order(), true); } @@ -733,29 +738,30 @@ protected function isBinaryData($data) * @param string $data the binary data string * @return string the debug string */ - protected function binaryToDebugString($data) + protected function binaryToDebugString(string $data): string { return '[binary-data length:' . strlen($data) . ' md5:' . md5($data) . ']'; } - protected function encodeApplicationJson($method, $parameters) + protected function encodeApplicationJson(string $method, $parameters) { if ( array_key_exists('Content-Type', $this->connectionModule->headers) && ($this->connectionModule->headers['Content-Type'] === 'application/json' - || preg_match('!^application/.+\+json$!', $this->connectionModule->headers['Content-Type']) + || preg_match('#^application/.+\+json$#', $this->connectionModule->headers['Content-Type']) ) ) { - if ($parameters instanceof \JsonSerializable) { - return json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION); + if ($parameters instanceof JsonSerializable) { + return json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR); } - if (is_array($parameters) || $parameters instanceof \ArrayAccess) { + + if (is_array($parameters) || $parameters instanceof ArrayAccess) { $parameters = $this->scalarizeArray($parameters); - return json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION); + return json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR); } } - if ($parameters instanceof \JsonSerializable) { + if ($parameters instanceof JsonSerializable) { throw new ModuleException(__CLASS__, $method . ' parameters is JsonSerializable object, but Content-Type header is not set to application/json'); } @@ -766,12 +772,11 @@ protected function encodeApplicationJson($method, $parameters) return $parameters; } - private function formatFilesArray(array $files) + private function formatFilesArray(array $files): array { foreach ($files as $name => $value) { if (is_string($value)) { $this->checkFileBeforeUpload($value); - $files[$name] = [ 'name' => basename($value), 'tmp_name' => $value, @@ -780,18 +785,23 @@ private function formatFilesArray(array $files) 'error' => 0, ]; continue; - } elseif (is_array($value)) { + } + + if (is_array($value)) { if (isset($value['tmp_name'])) { $this->checkFileBeforeUpload($value['tmp_name']); if (!isset($value['name'])) { $value['name'] = basename($value['tmp_name']); } + if (!isset($value['size'])) { $value['size'] = filesize($value['tmp_name']); } + if (!isset($value['type'])) { $value['type'] = $this->getFileType($value['tmp_name']); } + if (!isset($value['error'])) { $value['error'] = 0; } @@ -804,31 +814,34 @@ private function formatFilesArray(array $files) * @issue https://github.com/Codeception/Codeception/issues/3298 */ } else { - throw new ModuleException(__CLASS__, "Invalid value of key $name in files array"); + throw new ModuleException(__CLASS__, sprintf('Invalid value of key %s in files array', $name)); } } return $files; } - private function getFileType($file) + private function getFileType($file): string { if (function_exists('mime_content_type') && mime_content_type($file)) { return mime_content_type($file); } + return 'application/octet-stream'; } - private function checkFileBeforeUpload($file) + private function checkFileBeforeUpload(string $file): void { if (!file_exists($file)) { - throw new ModuleException(__CLASS__, "File $file does not exist"); + throw new ModuleException(__CLASS__, sprintf('File %s does not exist', $file)); } + if (!is_readable($file)) { - throw new ModuleException(__CLASS__, "File $file is not readable"); + throw new ModuleException(__CLASS__, sprintf('File %s is not readable', $file)); } + if (!is_file($file)) { - throw new ModuleException(__CLASS__, "File $file is not a regular file"); + throw new ModuleException(__CLASS__, sprintf('File %s is not a regular file', $file)); } } @@ -836,16 +849,14 @@ private function checkFileBeforeUpload($file) * Extends the function Module::validateConfig for shorten messages * */ - protected function validateConfig() + protected function validateConfig(): void { parent::validateConfig(); $short = $this->_getConfig('shortDebugResponse'); - if (!is_null($short)) { - if (!is_int($short) || $short < 0) { - throw new ModuleConfigException(__CLASS__, 'The value of "shortDebugMessage" should be integer and greater or equal "0".'); - } + if (!is_null($short) && (!is_int($short) || $short < 0)) { + throw new ModuleConfigException(__CLASS__, 'The value of "shortDebugMessage" should be integer and greater or equal "0".'); } } @@ -855,35 +866,33 @@ protected function validateConfig() * * @part json */ - public function seeResponseIsJson() + public function seeResponseIsJson(): void { $responseContent = $this->connectionModule->_getResponseContent(); - \PHPUnit\Framework\Assert::assertNotEquals('', $responseContent, 'response is empty'); + Assert::assertNotEquals('', $responseContent, 'response is empty'); $this->decodeAndValidateJson($responseContent); } /** * Checks whether the last response contains text. * - * @param $text * @part json * @part xml */ - public function seeResponseContains($text) + public function seeResponseContains(string $text): void { - $this->assertStringContainsString($text, $this->connectionModule->_getResponseContent(), "REST response contains"); + $this->assertStringContainsString($text, $this->connectionModule->_getResponseContent(), 'REST response contains'); } /** * Checks whether last response do not contain text. * - * @param $text * @part json * @part xml */ - public function dontSeeResponseContains($text) + public function dontSeeResponseContains(string $text): void { - $this->assertStringNotContainsString($text, $this->connectionModule->_getResponseContent(), "REST response contains"); + $this->assertStringNotContainsString($text, $this->connectionModule->_getResponseContent(), 'REST response contains'); } /** @@ -902,17 +911,15 @@ public function dontSeeResponseContains($text) * // response {user: john, profile: { email: john@gmail.com }} * $I->seeResponseContainsJson(array('email' => 'john@gmail.com')); * - * ?> * ``` * * This method recursively checks if one array can be found inside of another. * - * @param array $json * @part json */ - public function seeResponseContainsJson($json = []) + public function seeResponseContainsJson(array $json = []): void { - \PHPUnit\Framework\Assert::assertThat( + Assert::assertThat( $this->connectionModule->_getResponseContent(), new JsonContains($json) ); @@ -940,30 +947,30 @@ public function seeResponseContainsJson($json = []) * ]; * $I->seeResponseIsValidOnJsonSchemaString(json_encode($schema)); * - * ?> * ``` * - * @param string $schema * @part json */ - public function seeResponseIsValidOnJsonSchemaString($schema) + public function seeResponseIsValidOnJsonSchemaString(string $schema): void { $responseContent = $this->connectionModule->_getResponseContent(); - \PHPUnit\Framework\Assert::assertNotEquals('', $responseContent, 'response is empty'); + Assert::assertNotEquals('', $responseContent, 'response is empty'); $responseObject = $this->decodeAndValidateJson($responseContent); - \PHPUnit\Framework\Assert::assertNotEquals('', $schema, 'schema is empty'); + Assert::assertNotEquals('', $schema, 'schema is empty'); $schemaObject = $this->decodeAndValidateJson($schema, "Invalid schema json: %s. System message: %s."); $validator = new JsonSchemaValidator(); - $validator->validate($responseObject, $schemaObject, JsonContraint::CHECK_MODE_VALIDATE_SCHEMA); + $validator->validate($responseObject, $schemaObject, JsonConstraint::CHECK_MODE_VALIDATE_SCHEMA); + $outcome = $validator->isValid(); - $error = ""; + $error = ''; if (!$outcome) { $errors = $validator->getErrors(); $error = array_shift($errors)["message"]; } - \PHPUnit\Framework\Assert::assertTrue( + + Assert::assertTrue( $outcome, $error ); @@ -973,32 +980,32 @@ public function seeResponseIsValidOnJsonSchemaString($schema) * Checks whether last response matches the supplied json schema (https://json-schema.org/) * Supply schema as relative file path in your project directory or an absolute path * - * @see codecept_absolute_path() - * * @param string $schemaFilename * @part json + * @see codecept_absolute_path() */ - public function seeResponseIsValidOnJsonSchema($schemaFilename) + public function seeResponseIsValidOnJsonSchema(string $schemaFilename): void { $file = codecept_absolute_path($schemaFilename); if (!file_exists($file)) { - throw new ModuleException(__CLASS__, "File $file does not exist"); + throw new ModuleException(__CLASS__, sprintf('File %s does not exist', $file)); } + $this->seeResponseIsValidOnJsonSchemaString(file_get_contents($file)); } /** - * Converts string to json and asserts that no error occured while decoding. + * Converts string to json and asserts that no error occurred while decoding. * * @param string $jsonString the json encoded string * @param string $errorFormat optional string for custom sprintf format */ - protected function decodeAndValidateJson($jsonString, $errorFormat="Invalid json: %s. System message: %s.") + protected function decodeAndValidateJson(string $jsonString, string $errorFormat="Invalid json: %s. System message: %s.") { $json = json_decode($jsonString); $errorCode = json_last_error(); $errorMessage = json_last_error_msg(); - \PHPUnit\Framework\Assert::assertEquals( + Assert::assertSame( JSON_ERROR_NONE, $errorCode, sprintf( @@ -1019,14 +1026,12 @@ protected function decodeAndValidateJson($jsonString, $errorFormat="Invalid json * grabResponse(); * $I->sendPut('/user', array('id' => $user_id, 'name' => 'davert')); - * ?> * ``` * - * @return string * @part json * @part xml */ - public function grabResponse() + public function grabResponse(): string { return $this->connectionModule->_getResponseContent(); } @@ -1041,15 +1046,13 @@ public function grabResponse() * // match the first `user.id` in json * $firstUserId = $I->grabDataFromResponseByJsonPath('$..users[0].id'); * $I->sendPut('/user', array('id' => $firstUserId[0], 'name' => 'davert')); - * ?> * ``` * - * @param string $jsonPath * @return array Array of matching items - * @throws \Exception + * @throws Exception * @part json */ - public function grabDataFromResponseByJsonPath($jsonPath) + public function grabDataFromResponseByJsonPath(string $jsonPath): array { return (new JsonArray($this->connectionModule->_getResponseContent()))->filterByJsonPath($jsonPath); } @@ -1089,34 +1092,31 @@ public function grabDataFromResponseByJsonPath($jsonPath) * $I->seeResponseJsonMatchesXpath('//store/book[1]/author'); * // at least one item in store has price * $I->seeResponseJsonMatchesXpath('/store//price'); - * ?> * ``` - * @param string $xpath * @part json */ - public function seeResponseJsonMatchesXpath($xpath) + public function seeResponseJsonMatchesXpath(string $xPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertGreaterThan( 0, - (new JsonArray($response))->filterByXPath($xpath)->length, - "Received JSON did not match the XPath `$xpath`.\nJson Response: \n" . $response + (new JsonArray($response))->filterByXPath($xPath)->length, + "Received JSON did not match the XPath `{$xPath}`.\nJson Response: \n" . $response ); } /** * Opposite to seeResponseJsonMatchesXpath * - * @param string $xpath * @part json */ - public function dontSeeResponseJsonMatchesXpath($xpath) + public function dontSeeResponseJsonMatchesXpath(string $xPath): void { $response = $this->connectionModule->_getResponseContent(); - $this->assertEquals( + $this->assertSame( 0, - (new JsonArray($response))->filterByXPath($xpath)->length, - "Received JSON matched the XPath `$xpath`.\nJson Response: \n" . $response + (new JsonArray($response))->filterByXPath($xPath)->length, + "Received JSON matched the XPath `{$xPath}`.\nJson Response: \n" . $response ); } @@ -1154,18 +1154,16 @@ public function dontSeeResponseJsonMatchesXpath($xpath) * $I->seeResponseJsonMatchesJsonPath('$.store.book[0].author'); * // at least one item in store has price * $I->seeResponseJsonMatchesJsonPath('$.store..price'); - * ?> * ``` * - * @param string $jsonPath * @part json */ - public function seeResponseJsonMatchesJsonPath($jsonPath) + public function seeResponseJsonMatchesJsonPath(string $jsonPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertNotEmpty( (new JsonArray($response))->filterByJsonPath($jsonPath), - "Received JSON did not match the JsonPath `$jsonPath`.\nJson Response: \n" . $response + "Received JSON did not match the JsonPath `{$jsonPath}`.\nJson Response: \n" . $response ); } @@ -1173,15 +1171,14 @@ public function seeResponseJsonMatchesJsonPath($jsonPath) * See [#jsonpath](#jsonpath) for general info on JSONPath. * Opposite to [`seeResponseJsonMatchesJsonPath()`](#seeResponseJsonMatchesJsonPath) * - * @param string $jsonPath * @part json */ - public function dontSeeResponseJsonMatchesJsonPath($jsonPath) + public function dontSeeResponseJsonMatchesJsonPath(string $jsonPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertEmpty( (new JsonArray($response))->filterByJsonPath($jsonPath), - "Received JSON matched the JsonPath `$jsonPath`.\nJson Response: \n" . $response + "Received JSON matched the JsonPath `{$jsonPath}`.\nJson Response: \n" . $response ); } @@ -1189,9 +1186,8 @@ public function dontSeeResponseJsonMatchesJsonPath($jsonPath) * Opposite to seeResponseContainsJson * * @part json - * @param array $json */ - public function dontSeeResponseContainsJson($json = []) + public function dontSeeResponseContainsJson(array $json = []): void { $jsonResponseArray = new JsonArray($this->connectionModule->_getResponseContent()); $this->assertFalse( @@ -1222,7 +1218,6 @@ public function dontSeeResponseContainsJson($json = []) * // narrow down matching with JsonPath: * // {"users": [{ "name": "davert"}, {"id": 1}]} * $I->seeResponseMatchesJsonType(['name' => 'string'], '$.users[0]'); - * ?> * ``` * * You can check if the record contains fields with the data types you expect. @@ -1244,7 +1239,6 @@ public function dontSeeResponseContainsJson($json = []) * 'user_id' => 'integer|string', // multiple types * 'company' => ['name' => 'string'] * ]); - * ?> * ``` * * You can also apply filters to check values. Filter can be applied with a `:` char after the type declaration, @@ -1273,25 +1267,22 @@ public function dontSeeResponseContainsJson($json = []) * $I->seeResponseMatchesJsonType([ * 'user_id' => 'string:>0', // works with strings as well * ]); - * ?> * ``` * * You can also add custom filters by using `{@link JsonType::addCustomFilter()}`. * See [JsonType reference](http://codeception.com/docs/reference/JsonType). * * @part json - * @param array $jsonType - * @param string $jsonPath * @see JsonType */ - public function seeResponseMatchesJsonType(array $jsonType, $jsonPath = null) + public function seeResponseMatchesJsonType(array $jsonType, string $jsonPath = null): void { $jsonArray = new JsonArray($this->connectionModule->_getResponseContent()); if ($jsonPath) { $jsonArray = $jsonArray->filterByJsonPath($jsonPath); } - \PHPUnit\Framework\Assert::assertThat($jsonArray, new JsonTypeConstraint($jsonType)); + Assert::assertThat($jsonArray, new JsonTypeConstraint($jsonType)); } /** @@ -1299,17 +1290,16 @@ public function seeResponseMatchesJsonType(array $jsonType, $jsonPath = null) * * @part json * @param array $jsonType JsonType structure - * @param string $jsonPath * @see seeResponseMatchesJsonType */ - public function dontSeeResponseMatchesJsonType(array $jsonType, $jsonPath = null) + public function dontSeeResponseMatchesJsonType(array $jsonType, string $jsonPath = null): void { $jsonArray = new JsonArray($this->connectionModule->_getResponseContent()); if ($jsonPath) { $jsonArray = $jsonArray->filterByJsonPath($jsonPath); } - \PHPUnit\Framework\Assert::assertThat($jsonArray, new JsonTypeConstraint($jsonType, false)); + Assert::assertThat($jsonArray, new JsonTypeConstraint($jsonType, false)); } /** @@ -1317,11 +1307,10 @@ public function dontSeeResponseMatchesJsonType(array $jsonType, $jsonPath = null * * @part json * @part xml - * @param $response */ - public function seeResponseEquals($expected) + public function seeResponseEquals($expected): void { - $this->assertEquals($expected, $this->connectionModule->_getResponseContent()); + $this->assertSame($expected, $this->connectionModule->_getResponseContent()); } /** @@ -1337,9 +1326,8 @@ public function seeResponseEquals($expected) * * @part json * @part xml - * @param $code */ - public function seeResponseCodeIs($code) + public function seeResponseCodeIs(int $code): void { $this->connectionModule->seeResponseCodeIs($code); } @@ -1357,9 +1345,8 @@ public function seeResponseCodeIs($code) * * @part json * @part xml - * @param $code */ - public function dontSeeResponseCodeIs($code) + public function dontSeeResponseCodeIs(int $code): void { $this->connectionModule->dontSeeResponseCodeIs($code); } @@ -1370,7 +1357,7 @@ public function dontSeeResponseCodeIs($code) * @part json * @part xml */ - public function seeResponseCodeIsSuccessful() + public function seeResponseCodeIsSuccessful(): void { $this->connectionModule->seeResponseCodeIsSuccessful(); } @@ -1381,7 +1368,7 @@ public function seeResponseCodeIsSuccessful() * @part json * @part xml */ - public function seeResponseCodeIsRedirection() + public function seeResponseCodeIsRedirection(): void { $this->connectionModule->seeResponseCodeIsRedirection(); } @@ -1392,7 +1379,7 @@ public function seeResponseCodeIsRedirection() * @part json * @part xml */ - public function seeResponseCodeIsClientError() + public function seeResponseCodeIsClientError(): void { $this->connectionModule->seeResponseCodeIsClientError(); } @@ -1403,7 +1390,7 @@ public function seeResponseCodeIsClientError() * @part json * @part xml */ - public function seeResponseCodeIsServerError() + public function seeResponseCodeIsServerError(): void { $this->connectionModule->seeResponseCodeIsServerError(); } @@ -1415,23 +1402,24 @@ public function seeResponseCodeIsServerError() * * @part xml */ - public function seeResponseIsXml() + public function seeResponseIsXml(): void { libxml_use_internal_errors(true); $doc = simplexml_load_string($this->connectionModule->_getResponseContent()); - $num = ""; - $title = ""; + $num = ''; + $title = ''; if ($doc === false) { $error = libxml_get_last_error(); $num = $error->code; $title = trim($error->message); libxml_clear_errors(); } + libxml_use_internal_errors(false); - \PHPUnit\Framework\Assert::assertNotSame( + Assert::assertNotSame( false, $doc, - "xml decoding error #$num with message \"$title\", see http://www.xmlsoft.org/html/libxml-xmlerror.html" + sprintf('xml decoding error #%s with message "%s".', $num, $title) ); } @@ -1443,12 +1431,11 @@ public function seeResponseIsXml() * $I->seeXmlResponseMatchesXpath('//root/user[@id=1]'); * ``` * @part xml - * @param $xpath */ - public function seeXmlResponseMatchesXpath($xpath) + public function seeXmlResponseMatchesXpath(string $xPath): void { - $structure = new XmlStructure($this->connectionModule->_getResponseContent()); - $this->assertTrue($structure->matchesXpath($xpath), 'xpath not matched'); + $xmlStructure = new XmlStructure($this->connectionModule->_getResponseContent()); + $this->assertTrue($xmlStructure->matchesXpath($xPath), 'xpath not matched'); } /** @@ -1459,23 +1446,21 @@ public function seeXmlResponseMatchesXpath($xpath) * $I->dontSeeXmlResponseMatchesXpath('//root/user[@id=1]'); * ``` * @part xml - * @param $xpath */ - public function dontSeeXmlResponseMatchesXpath($xpath) + public function dontSeeXmlResponseMatchesXpath(string $xPath): void { $structure = new XmlStructure($this->connectionModule->_getResponseContent()); - $this->assertFalse($structure->matchesXpath($xpath), 'accidentally matched xpath'); + $this->assertFalse($structure->matchesXpath($xPath), 'accidentally matched xpath'); } /** * Finds and returns text contents of element. * Element is matched by either CSS or XPath * - * @param $cssOrXPath - * @return string + * @param mixed $cssOrXPath * @part xml */ - public function grabTextContentFromXmlElement($cssOrXPath) + public function grabTextContentFromXmlElement($cssOrXPath): string { $el = (new XmlStructure($this->connectionModule->_getResponseContent()))->matchElement($cssOrXPath); return $el->textContent; @@ -1485,17 +1470,15 @@ public function grabTextContentFromXmlElement($cssOrXPath) * Finds and returns attribute of element. * Element is matched by either CSS or XPath * - * @param $cssOrXPath - * @param $attribute - * @return string * @part xml */ - public function grabAttributeFromXmlElement($cssOrXPath, $attribute) + public function grabAttributeFromXmlElement(string $cssOrXPath, string $attribute): string { $el = (new XmlStructure($this->connectionModule->_getResponseContent()))->matchElement($cssOrXPath); if (!$el->hasAttribute($attribute)) { - $this->fail("Attribute not found in element matched by '$cssOrXPath'"); + $this->fail(sprintf("Attribute not found in element matched by '%s'", $cssOrXPath)); } + return $el->getAttribute($attribute); } @@ -1505,12 +1488,12 @@ public function grabAttributeFromXmlElement($cssOrXPath, $attribute) * * Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes). * - * @param $xml + * @param mixed $xml * @part xml */ - public function seeXmlResponseEquals($xml) + public function seeXmlResponseEquals($xml): void { - \PHPUnit\Framework\Assert::assertXmlStringEqualsXmlString($this->connectionModule->_getResponseContent(), $xml); + Assert::assertXmlStringEqualsXmlString($this->connectionModule->_getResponseContent(), $xml); } @@ -1520,12 +1503,12 @@ public function seeXmlResponseEquals($xml) * * Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes). * - * @param $xml + * @param mixed $xml * @part xml */ - public function dontSeeXmlResponseEquals($xml) + public function dontSeeXmlResponseEquals($xml): void { - \PHPUnit\Framework\Assert::assertXmlStringNotEqualsXmlString( + Assert::assertXmlStringNotEqualsXmlString( $this->connectionModule->_getResponseContent(), $xml ); @@ -1541,13 +1524,12 @@ public function dontSeeXmlResponseEquals($xml) * ``` php * seeXmlResponseIncludes("1"); - * ?> * ``` * - * @param $xml + * @param mixed $xml * @part xml */ - public function seeXmlResponseIncludes($xml) + public function seeXmlResponseIncludes($xml): void { $this->assertStringContainsString( XmlUtils::toXml($xml)->C14N(), @@ -1561,10 +1543,10 @@ public function seeXmlResponseIncludes($xml) * Comparison is done by canonicalizing both xml`s. * Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes). * - * @param $xml + * @param mixed $xml * @part xml */ - public function dontSeeXmlResponseIncludes($xml) + public function dontSeeXmlResponseIncludes($xml): void { $this->assertStringNotContainsString( XmlUtils::toXml($xml)->C14N(), @@ -1583,7 +1565,6 @@ public function dontSeeXmlResponseIncludes($xml) * ```php * seeBinaryResponseEquals("8c90748342f19b195b9c6b4eff742ded"); - * ?> * ``` * * Example: Using md5 for a file contents @@ -1592,7 +1573,6 @@ public function dontSeeXmlResponseIncludes($xml) * seeBinaryResponseEquals(md5($fileData)); - * ?> * ``` * Example: Using sha256 hash * @@ -1600,7 +1580,6 @@ public function dontSeeXmlResponseIncludes($xml) * seeBinaryResponseEquals(hash("sha256", base64_decode($fileData)), 'sha256'); - * ?> * ``` * * @param string $hash the hashed data response expected @@ -1608,10 +1587,10 @@ public function dontSeeXmlResponseIncludes($xml) * @part json * @part xml */ - public function seeBinaryResponseEquals($hash, $algo = 'md5') + public function seeBinaryResponseEquals(string $hash, string $algo = 'sha1'): void { $responseHash = hash($algo, $this->connectionModule->_getResponseContent()); - $this->assertEquals($hash, $responseHash); + $this->assertSame($hash, $responseHash); } /** @@ -1620,7 +1599,6 @@ public function seeBinaryResponseEquals($hash, $algo = 'md5') * ```php * dontSeeBinaryResponseEquals("8c90748342f19b195b9c6b4eff742ded"); - * ?> * ``` * Opposite to `seeBinaryResponseEquals` * @@ -1629,7 +1607,7 @@ public function seeBinaryResponseEquals($hash, $algo = 'md5') * @part json * @part xml */ - public function dontSeeBinaryResponseEquals($hash, $algo = 'md5') + public function dontSeeBinaryResponseEquals(string $hash, string $algo = 'sha1'): void { $responseHash = hash($algo, $this->connectionModule->_getResponseContent()); $this->assertNotEquals($hash, $responseHash); @@ -1646,7 +1624,7 @@ public function dontSeeBinaryResponseEquals($hash, $algo = 'md5') * @part xml * @part json */ - public function stopFollowingRedirects() + public function stopFollowingRedirects(): void { $this->client->followRedirects(false); } @@ -1662,7 +1640,7 @@ public function stopFollowingRedirects() * @part xml * @part json */ - public function startFollowingRedirects() + public function startFollowingRedirects(): void { $this->client->followRedirects(true); } @@ -1675,7 +1653,7 @@ public function startFollowingRedirects() * $I->setServerParameters([]); * ``` */ - public function setServerParameters(array $params) + public function setServerParameters(array $params): void { $this->client->setServerParameters($params); } @@ -1687,7 +1665,7 @@ public function setServerParameters(array $params) * $I->haveServerParameter('name', 'value'); * ``` */ - public function haveServerParameter($name, $value) + public function haveServerParameter($name, $value): void { $this->client->setServerParameter($name, $value); } diff --git a/src/Codeception/Util/JsonArray.php b/src/Codeception/Util/JsonArray.php index 57bc45b..97b783b 100644 --- a/src/Codeception/Util/JsonArray.php +++ b/src/Codeception/Util/JsonArray.php @@ -1,21 +1,22 @@ jsonXml) { return $this->jsonXml; @@ -68,39 +69,43 @@ public function toXml() return $dom; } - /** - * @return array - */ - public function toArray() + public function toArray(): array { return $this->jsonArray; } - public function filterByXPath($xpath) + /** + * @return DOMNodeList|bool + */ + public function filterByXPath(string $xPath) { - $path = new \DOMXPath($this->toXml()); - return $path->query($xpath); + $path = new DOMXPath($this->toXml()); + return $path->query($xPath); } - public function filterByJsonPath($jsonPath) + public function filterByJsonPath(string $jsonPath): array { - if (!class_exists('Flow\JSONPath\JSONPath')) { - throw new \Exception('JSONPath library not installed. Please add `softcreatr/jsonpath` to composer.json'); + if (!class_exists(JSONPath::class)) { + throw new Exception('JSONPath library not installed. Please add `softcreatr/jsonpath` to composer.json'); } + return (new JSONPath($this->jsonArray))->find($jsonPath)->getData(); } + /** + * @return string|false + */ public function getXmlString() { return $this->toXml()->saveXML(); } - public function containsArray(array $needle) + public function containsArray(array $needle): bool { return (new ArrayContainsComparator($this->jsonArray))->containsArray($needle); } - private function arrayToXml(\DOMDocument $doc, \DOMNode $node, $array) + private function arrayToXml(DOMDocument $doc, DOMNode $node, array $array): void { foreach ($array as $key => $value) { if (is_numeric($key)) { @@ -109,12 +114,14 @@ private function arrayToXml(\DOMDocument $doc, \DOMNode $node, $array) } else { try { $subNode = $doc->createElement($key); - } catch (\Exception $e) { + } catch (Exception $exception) { $key = $this->getValidTagNameForInvalidKey($key); $subNode = $doc->createElement($key); } + $node->appendChild($subNode); } + if (is_array($value)) { $this->arrayToXml($doc, $subNode, $value); } else { @@ -131,6 +138,7 @@ private function getValidTagNameForInvalidKey($key) $map[$key] = $tagName; codecept_debug($tagName . ' is "' . $key . '"'); } + return $map[$key]; } } diff --git a/src/Codeception/Util/JsonType.php b/src/Codeception/Util/JsonType.php index ee4a559..ced4134 100644 --- a/src/Codeception/Util/JsonType.php +++ b/src/Codeception/Util/JsonType.php @@ -1,5 +1,7 @@ matches([ * 'id' => 'string', * ]); // => `id: 1` is not of type string - * ?> * ``` * * Class JsonType @@ -29,22 +30,26 @@ */ class JsonType { + /** + * @var array|JsonArray + */ protected $jsonArray; - protected static $customFilters = []; + protected static array $customFilters = []; /** * Creates instance of JsonType * Pass an array or `\Codeception\Util\JsonArray` with data. * If non-associative array is passed - the very first element of it will be used for matching. * - * @param $jsonArray array|\Codeception\Util\JsonArray + * @param $jsonArray array|JsonArray */ public function __construct($jsonArray) { if ($jsonArray instanceof JsonArray) { $jsonArray = $jsonArray->toArray(); } + $this->jsonArray = $jsonArray; } @@ -67,13 +72,9 @@ public function __construct($jsonArray) * return strlen($value) == $len; * }); * // use it as 'string:len(5)' - * ?> * ``` - * - * @param $name - * @param callable $callable */ - public static function addCustomFilter($name, callable $callable) + public static function addCustomFilter(string $name, callable $callable): void { static::$customFilters[$name] = $callable; } @@ -81,7 +82,7 @@ public static function addCustomFilter($name, callable $callable) /** * Removes all custom filters */ - public static function cleanCustomFilters() + public static function cleanCustomFilters(): void { static::$customFilters = []; } @@ -91,33 +92,38 @@ public static function cleanCustomFilters() * If matching fails function returns a string with a message describing failure. * On success returns `true`. * - * @param array $jsonType - * @return bool|string + * @return string|bool */ public function matches(array $jsonType) { if (array_key_exists(0, $this->jsonArray) && is_array($this->jsonArray[0])) { // a list of items $msg = ''; - foreach ($this->jsonArray as $array) { - $res = $this->typeComparison($array, $jsonType); + foreach ($this->jsonArray as $singleJsonArray) { + $res = $this->typeComparison($singleJsonArray, $jsonType); if ($res !== true) { $msg .= "\n" . $res; } } - if ($msg) { + + if ($msg !== '') { return $msg; } + return true; } + return $this->typeComparison($this->jsonArray, $jsonType); } - protected function typeComparison($data, $jsonType) + /** + * @return string|bool + */ + protected function typeComparison(array $data, array $jsonType) { foreach ($jsonType as $key => $type) { if (!array_key_exists($key, $data)) { - return "Key `$key` doesn't exist in " . json_encode($data); + return sprintf("Key `%s` doesn't exist in ", $key) . json_encode($data, JSON_THROW_ON_ERROR); } if (is_array($jsonType[$key])) { @@ -138,7 +144,7 @@ protected function typeComparison($data, $jsonType) preg_match_all($regexMatcher, $type, $regexes); // Do the same match as above, but replace the the 'any character' + delimiter with a place holder ($${count}). - $filterType = preg_replace_callback($regexMatcher, function () { + $filterType = preg_replace_callback($regexMatcher, function (): string { static $count = 0; return ':regex($$' . $count++ . ')'; }, $type); @@ -163,13 +169,13 @@ protected function typeComparison($data, $jsonType) foreach ($filters as $filter) { // Fill regex pattern back into the filter. - $filter = preg_replace_callback('/\$\$\d+/', function ($m) use ($regexes) { + $filter = preg_replace_callback('#\$\$\d+#', function ($m) use ($regexes) { $pos = (int)substr($m[0], 2); return $regexes[1][$pos]; }, $filter); - $matched = $matched && $this->matchFilter($filter, $data[$key]); + $matched = $matched && $this->matchFilter($filter, (string) $data[$key]); } if ($matched) { @@ -181,10 +187,11 @@ protected function typeComparison($data, $jsonType) return sprintf("`$key: %s` is of type `$type`", var_export($data[$key], true)); } } + return true; } - protected function matchFilter($filter, $value) + protected function matchFilter(string $filter, string $value) { $filter = trim($filter); if (strpos($filter, '!') === 0) { @@ -193,51 +200,59 @@ protected function matchFilter($filter, $value) // apply custom filters foreach (static::$customFilters as $customFilter => $callable) { - if (strpos($customFilter, '/') === 0) { - if (preg_match($customFilter, $filter, $matches)) { - array_shift($matches); - return call_user_func_array($callable, array_merge([$value], $matches)); - } + if (strpos($customFilter, '/') === 0 && preg_match($customFilter, $filter, $matches)) { + array_shift($matches); + return call_user_func_array($callable, array_merge([$value], $matches)); } + if ($customFilter == $filter) { return $callable($value); } } if (strpos($filter, '=') === 0) { - return $value == substr($filter, 1); + return $value === substr($filter, 1); } + if ($filter === 'url') { return filter_var($value, FILTER_VALIDATE_URL); } + if ($filter === 'date') { return preg_match( - '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|(\+|-)([\d|:]*))?$/', + '#^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|(\+|-)([\d|:]*))?$#', $value ); } + if ($filter === 'email') { // from http://emailregex.com/ // @codingStandardsIgnoreStart - return preg_match('/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD', + return preg_match('#^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4]\d)|(?:1\d{2})|(?:[1-9]?\d))(?:\.(?:(?:25[0-5])|(?:2[0-4]\d)|(?:1\d{2})|(?:[1-9]?\d))){3}))\]))$#iD', $value); // @codingStandardsIgnoreEnd } + if ($filter === 'empty') { return empty($value); } - if (preg_match('~^regex\((.*?)\)$~', $filter, $matches)) { + + if (preg_match('#^regex\((.*?)\)$#', $filter, $matches)) { return preg_match($matches[1], $value); } - if (preg_match('~^>=(-?[\d\.]+)$~', $filter, $matches)) { + + if (preg_match('#^>=(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value >= (float)$matches[1]; } - if (preg_match('~^<=(-?[\d\.]+)$~', $filter, $matches)) { + + if (preg_match('#^<=(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value <= (float)$matches[1]; } - if (preg_match('~^>(-?[\d\.]+)$~', $filter, $matches)) { + + if (preg_match('#^>(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value > (float)$matches[1]; } - if (preg_match('~^<(-?[\d\.]+)$~', $filter, $matches)) { + + if (preg_match('#^<(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value < (float)$matches[1]; } diff --git a/tests/data/rest/index.php b/tests/data/rest/index.php index c04ebaa..62e950a 100755 --- a/tests/data/rest/index.php +++ b/tests/data/rest/index.php @@ -1,38 +1,35 @@ function() { - return [ - 'name' => 'davert', - 'email' => 'davert@mail.ua', - 'aliases' => [ - 'DavertMik', - 'davert.ua' - ], - 'address' => [ - 'city' => 'Kyiv', - 'country' => 'Ukraine', - ]]; - }, - 'zeroes' => function() { - return [ - 'responseCode' => 0, - 'message' => 'OK', - 'data' => [ - 9, - 0, - 0 - ], - ]; - }, + 'user' => fn(): array => [ + 'name' => 'davert', + 'email' => 'davert@mail.ua', + 'aliases' => [ + 'DavertMik', + 'davert.ua' + ], + 'address' => [ + 'city' => 'Kyiv', + 'country' => 'Ukraine', + ]], + 'zeroes' => fn(): array => [ + 'responseCode' => 0, + 'message' => 'OK', + 'data' => [ + 9, + 0, + 0 + ], + ], 'foo' => function() { if (isset($_SERVER['HTTP_FOO'])) { return 'foo: "' . $_SERVER['HTTP_FOO'] . '"'; } + return 'foo: not found'; } @@ -43,11 +40,9 @@ $name = $_POST['name']; return ['name' => $name]; }, - 'file-upload' => function() { - return [ - 'uploaded' => isset($_FILES['file']['tmp_name']) && file_exists($_FILES['file']['tmp_name']), - ]; - } + 'file-upload' => fn(): array => [ + 'uploaded' => isset($_FILES['file']['tmp_name']) && file_exists($_FILES['file']['tmp_name']), + ] ]; $GLOBALS['RESTmap']['PUT'] = [ @@ -60,7 +55,7 @@ ]; $GLOBALS['RESTmap']['DELETE'] = [ - 'user' => function() { + 'user' => function(): void { header('error', false, 404); } ]; diff --git a/tests/data/rest/server.php b/tests/data/rest/server.php index 9c862f1..8fcd26b 100755 --- a/tests/data/rest/server.php +++ b/tests/data/rest/server.php @@ -1,23 +1,21 @@ $index]); $connectionModule->_initialize(); - $this->module = Stub::make('\Codeception\Module\REST'); + + $this->module = Stub::make(\Codeception\Module\REST::class); $this->module->_inject($connectionModule); $this->module->_initialize(); - $this->module->_before(Stub::makeEmpty('\Codeception\Test\Test')); + $this->module->_before(Stub::makeEmpty(\Codeception\Test\Test::class)); + $this->module->client->setServerParameters([ 'SCRIPT_FILENAME' => 'index.php', 'SCRIPT_NAME' => 'index', @@ -38,26 +44,28 @@ public function _setUp() public function testConflictsWithAPI() { - $this->assertInstanceOf('Codeception\Lib\Interfaces\ConflictsWithModule', $this->module); - $this->assertEquals('Codeception\Lib\Interfaces\API', $this->module->_conflicts()); + $this->assertInstanceOf(\Codeception\Lib\Interfaces\ConflictsWithModule::class, $this->module); + $this->assertSame(\Codeception\Lib\Interfaces\API::class, $this->module->_conflicts()); } private function setStubResponse($response) { - $connectionModule = Stub::make('\Codeception\Module\UniversalFramework', ['_getResponseContent' => $response]); + $connectionModule = Stub::make(\Codeception\Module\UniversalFramework::class, ['_getResponseContent' => $response]); $this->module->_inject($connectionModule); $this->module->_initialize(); - $this->module->_before(Stub::makeEmpty('\Codeception\Test\Test')); + $this->module->_before(Stub::makeEmpty(\Codeception\Test\Test::class)); } public function testBeforeHookResetsVariables() { $this->module->haveHttpHeader('Origin', 'http://www.example.com'); $this->module->sendGET('/rest/user/'); + $server = $this->module->client->getInternalRequest()->getServer(); $this->assertArrayHasKey('HTTP_ORIGIN', $server); - $this->module->_before(Stub::makeEmpty('\Codeception\Test\Test')); + $this->module->_before(Stub::makeEmpty(\Codeception\Test\Test::class)); $this->module->sendGET('/rest/user/'); + $server = $this->module->client->getInternalRequest()->getServer(); $this->assertArrayNotHasKey('HTTP_ORIGIN', $server); } @@ -107,13 +115,13 @@ public function testGrabDataFromResponseByJsonPath() { $this->module->sendGET('/rest/user/'); // simple assoc array - $this->assertEquals(['davert@mail.ua'], $this->module->grabDataFromResponseByJsonPath('$.email')); + $this->assertSame(['davert@mail.ua'], $this->module->grabDataFromResponseByJsonPath('$.email')); // nested assoc array - $this->assertEquals(['Kyiv'], $this->module->grabDataFromResponseByJsonPath('$.address.city')); + $this->assertSame(['Kyiv'], $this->module->grabDataFromResponseByJsonPath('$.address.city')); // nested index array - $this->assertEquals(['DavertMik'], $this->module->grabDataFromResponseByJsonPath('$.aliases[0]')); + $this->assertSame(['DavertMik'], $this->module->grabDataFromResponseByJsonPath('$.aliases[0]')); // empty if data not found - $this->assertEquals([], $this->module->grabDataFromResponseByJsonPath('$.address.street')); + $this->assertSame([], $this->module->grabDataFromResponseByJsonPath('$.address.street')); } public function testValidJson() @@ -127,10 +135,11 @@ public function testValidJson() public function testInvalidJson() { - $this->expectException('PHPUnit\Framework\ExpectationFailedException'); + $this->expectException(ExpectationFailedException::class); $this->setStubResponse('{xxx = yyy}'); $this->module->seeResponseIsJson(); } + public function testValidXml() { $this->setStubResponse(''); @@ -149,7 +158,7 @@ public function testXmlResponseEquals() public function testInvalidXml() { - $this->expectException('PHPUnit\Framework\ExpectationFailedException'); + $this->expectException(ExpectationFailedException::class); $this->setStubResponse('John'); $this->module->seeResponseIsXml(); } @@ -203,11 +212,11 @@ public function testApplicationJsonIncludesJsonAsContent() { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->sendPOST('/', ['name' => 'john']); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); $this->assertContains('application/json', $request->getServer()); $server = $request->getServer(); - $this->assertEquals('application/json', $server['HTTP_CONTENT_TYPE']); + $this->assertSame('application/json', $server['HTTP_CONTENT_TYPE']); $this->assertJson($request->getContent()); $this->assertEmpty($request->getParameters()); } @@ -216,40 +225,39 @@ public function testApplicationJsonIncludesObjectSerialized() { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->sendPOST('/', new JsonSerializedItem()); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); $this->assertContains('application/json', $request->getServer()); $this->assertJson($request->getContent()); } /** - * @param string $method - * * @dataProvider requestBodyAwareMethods */ - public function testRequestBodyIsSentAsJsonForThisMethod($method) + public function testRequestBodyIsSentAsJsonForThisMethod(string $method) { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->send($method, '/', ['name' => 'john']); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); - $this->assertSame(json_encode(['name' => 'john']), $request->getContent()); + $this->assertSame(json_encode(['name' => 'john'], JSON_THROW_ON_ERROR), $request->getContent()); } /** - * @param string $method - * * @dataProvider requestBodyAwareMethods */ - public function testRequestBodyIsSentUrlEncodedForThisMethod($method) + public function testRequestBodyIsSentUrlEncodedForThisMethod(string $method) { $this->module->send($method, '/', ['name' => 'john']); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); $this->assertSame(http_build_query(['name' => 'john']), $request->getContent()); } - public function requestBodyAwareMethods() + /** + * @return array> + */ + public function requestBodyAwareMethods(): array { return [ 'POST' => ['POST'], @@ -260,15 +268,13 @@ public function requestBodyAwareMethods() } /** - * @param string $method - * * @dataProvider queryParamsAwareMethods */ - public function testJsonRequestBodyIsNotSentForThisMethod($method) + public function testJsonRequestBodyIsNotSentForThisMethod(string $method) { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->send($method, '/', ['name' => 'john']); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); $this->assertNull($request->getContent()); $this->assertContains('john', $request->getParameters()); @@ -276,20 +282,18 @@ public function testJsonRequestBodyIsNotSentForThisMethod($method) /** - * @param string $method - * * @dataProvider queryParamsAwareMethods */ - public function testUrlEncodedRequestBodyIsNotSentForThisMethod($method) + public function testUrlEncodedRequestBodyIsNotSentForThisMethod(string $method) { $this->module->send($method, '/', ['name' => 'john']); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); $this->assertNull($request->getContent()); $this->assertContains('john', $request->getParameters()); } - public function queryParamsAwareMethods() + public function queryParamsAwareMethods(): array { return [ 'GET' => ['GET'], @@ -301,13 +305,14 @@ public function queryParamsAwareMethods() /** * @dataProvider queryParamsAwareMethods */ - public function testThrowsExceptionIfParametersIsString($method) + public function testThrowsExceptionIfParametersIsString(string $method) { $this->expectExceptionMessage($method . ' parameters must be passed in array format'); $this->module->send($method, '/', 'string'); } /** + * @param array|object $parameters * @dataProvider invalidParameterTypes */ public function testThrowsExceptionIfParametersIsOfUnexpectedType($parameters) @@ -316,7 +321,7 @@ public function testThrowsExceptionIfParametersIsOfUnexpectedType($parameters) $this->module->sendPOST('/', $parameters); } - public function invalidParameterTypes() + public function invalidParameterTypes(): array { return [ 'boolean' => [true], @@ -329,20 +334,20 @@ public function invalidParameterTypes() public function testThrowsExceptionIfUrlIsNotString() { - $this->expectExceptionMessage('URL must be string'); + $this->expectException(TypeError::class); $this->module->sendPOST([1]); } public function testThrowsExceptionIfParametersIsJsonSerializableButContentTypeIsNotSet() { $this->expectExceptionMessage("parameters is JsonSerializable object, but Content-Type header is not set to application/json"); - $parameters = new \Codeception\Util\Maybe(['foo']); + $parameters = new Maybe(['foo']); $this->module->sendPOST('/', $parameters); } public function testDoesntThrowExceptionIfParametersIsJsonSerializableAndContentTypeIsSet() { - $parameters = new \Codeception\Util\Maybe(['foo']); + $parameters = new Maybe(['foo']); $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->sendPOST('/', $parameters); @@ -352,14 +357,14 @@ public function testDoesntThrowExceptionIfParametersIsJsonSerializableAndContent public function testUrlIsFull() { $this->module->sendGET('/api/v1/users'); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); - $this->assertEquals('http://localhost/api/v1/users', $request->getUri()); + $this->assertSame('http://localhost/api/v1/users', $request->getUri()); } public function testSeeHeaders() { - $response = new \Symfony\Component\BrowserKit\Response("", 200, [ + $response = new SymfonyResponse("", 200, [ 'Cache-Control' => ['no-cache', 'no-store'], 'Content_Language' => 'en-US' ]); @@ -371,15 +376,15 @@ public function testSeeHeaders() $this->module->dontSeeHttpHeader('Content-Language', 'en-RU'); $this->module->dontSeeHttpHeader('Content-Language1'); $this->module->seeHttpHeaderOnce('Content-Language'); - $this->assertEquals('en-US', $this->module->grabHttpHeader('Content-Language')); - $this->assertEquals('no-cache', $this->module->grabHttpHeader('Cache-Control')); - $this->assertEquals(['no-cache', 'no-store'], $this->module->grabHttpHeader('Cache-Control', false)); + $this->assertSame('en-US', $this->module->grabHttpHeader('Content-Language')); + $this->assertSame('no-cache', $this->module->grabHttpHeader('Cache-Control')); + $this->assertSame(['no-cache', 'no-store'], $this->module->grabHttpHeader('Cache-Control', false)); } public function testSeeHeadersOnce() { $this->shouldFail(); - $response = new \Symfony\Component\BrowserKit\Response("", 200, [ + $response = new SymfonyResponse("", 200, [ 'Cache-Control' => ['no-cache', 'no-store'], ]); $this->module->client->mockResponse($response); @@ -481,7 +486,7 @@ public function testApplicationJsonSubtypeIncludesObjectSerialized() { $this->module->haveHttpHeader('Content-Type', 'application/resource+json'); $this->module->sendPOST('/', new JsonSerializedItem()); - /** @var $request \Symfony\Component\BrowserKit\Request **/ + /** @var SymfonyRequest $request **/ $request = $this->module->client->getRequest(); $this->assertContains('application/resource+json', $request->getServer()); $this->assertJson($request->getContent()); @@ -508,8 +513,8 @@ public function testMatchJsonTypeFailsWithNiceMessage() try { $this->module->seeResponseMatchesJsonType(['zzz' => 'string']); $this->fail('it had to throw exception'); - } catch (PHPUnit\Framework\AssertionFailedError $e) { - $this->assertEquals('Key `zzz` doesn\'t exist in {"xxx":"yyy","user_id":1}', $e->getMessage()); + } catch (AssertionFailedError $assertionFailedError) { + $this->assertSame('Key `zzz` doesn\'t exist in {"xxx":"yyy","user_id":1}', $assertionFailedError->getMessage()); } } @@ -519,8 +524,8 @@ public function testDontMatchJsonTypeFailsWithNiceMessage() try { $this->module->dontSeeResponseMatchesJsonType(['xxx' => 'string']); $this->fail('it had to throw exception'); - } catch (PHPUnit\Framework\AssertionFailedError $e) { - $this->assertEquals('Unexpectedly response matched: {"xxx":"yyy","user_id":1}', $e->getMessage()); + } catch (AssertionFailedError $e) { + $this->assertSame('Unexpectedly response matched: {"xxx":"yyy","user_id":1}', $e->getMessage()); } } @@ -560,7 +565,7 @@ public function testSeeBinaryResponseEquals() { $data = base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k='); $this->setStubResponse($data); - $this->module->seeBinaryResponseEquals(md5($data)); + $this->module->seeBinaryResponseEquals(sha1($data)); } public function testDontSeeBinaryResponseEquals() @@ -572,7 +577,7 @@ public function testDontSeeBinaryResponseEquals() public function testAmDigestAuthenticatedThrowsExceptionWithFunctionalModules() { - $this->expectException('\Codeception\Exception\ModuleException'); + $this->expectException(\Codeception\Exception\ModuleException::class); $this->expectExceptionMessage('Not supported by functional modules'); $this->module->amDigestAuthenticated('username', 'password'); } @@ -581,11 +586,13 @@ public function testCanResetHTTPAuthenticated() { $this->module->amHttpAuthenticated('user', 'pass'); $this->module->sendGET('/rest/user/'); + $server = $this->module->client->getRequest()->getServer(); $this->assertArrayHasKey('PHP_AUTH_USER', $server); $this->assertArrayHasKey('PHP_AUTH_PW', $server); $this->module->setServerParameters([]); $this->module->sendGET('/rest/user/'); + $server = $this->module->client->getRequest()->getServer(); $this->assertArrayNotHasKey('PHP_AUTH_USER', $server); $this->assertArrayNotHasKey('PHP_AUTH_PW', $server); @@ -595,6 +602,7 @@ public function testHaveServerParameter() { $this->module->haveServerParameter('my', 'param'); $this->module->sendGET('/rest/user/'); + $server = $this->module->client->getRequest()->getServer(); $this->assertArrayHasKey('my', $server); } @@ -608,8 +616,8 @@ public function testHaveServerParameter() * @dataProvider schemaAndResponse */ - public function testSeeResponseIsValidOnJsonSchemachesJsonSchema($schema, $response, $outcome, $error) { - + public function testSeeResponseIsValidOnJsonSchemachesJsonSchema(string $schema, string $response, bool $outcome, string $error) + { $response = file_get_contents(codecept_data_dir($response)); $this->setStubResponse($response); @@ -617,6 +625,7 @@ public function testSeeResponseIsValidOnJsonSchemachesJsonSchema($schema, $respo $this->expectExceptionMessage($error); $this->shouldFail(); } + $this->module->seeResponseIsValidOnJsonSchema(codecept_data_dir($schema)); } @@ -632,17 +641,13 @@ public function testSeeResponseIsValidOnJsonSchemachesJsonSchemaString() { ] ] ]; - $this->module->seeResponseIsValidOnJsonSchemaString(json_encode($schema)); + $this->module->seeResponseIsValidOnJsonSchemaString(json_encode($schema, JSON_THROW_ON_ERROR)); } /** - * @param $configUrl - * @param $requestUrl - * @param $expectedFullUrl - * * @dataProvider configAndRequestUrls */ - public function testRestExecute($configUrl, $requestUrl, $expectedFullUrl) + public function testRestExecute(string $configUrl, string $requestUrl, string $expectedFullUrl) { $connectionModule = $this->createMock( UniversalFramework::class @@ -658,23 +663,23 @@ public function testRestExecute($configUrl, $requestUrl, $expectedFullUrl) $server, $content ) use ($expectedFullUrl) { - \PHPUnit\Framework\Assert::assertEquals($expectedFullUrl, $uri); + \PHPUnit\Framework\Assert::assertSame($expectedFullUrl, $uri); }) ); $config = ['url' => $configUrl]; /** @var REST */ - $module = Stub::make('\Codeception\Module\REST'); + $module = Stub::make(\Codeception\Module\REST::class); $module->_setConfig($config); $module->_inject($connectionModule); $module->_initialize(); - $module->_before(Stub::makeEmpty('\Codeception\Test\Test')); + $module->_before(Stub::makeEmpty(\Codeception\Test\Test::class)); $module->sendGET($requestUrl); } - public static function schemaAndResponse() + public static function schemaAndResponse(): array { return [ //schema, responsefile, valid @@ -685,7 +690,7 @@ public static function schemaAndResponse() ]; } - public static function configAndRequestUrls() + public static function configAndRequestUrls(): array { return [ //$configUrl, $requestUrl, $expectedFullUrl @@ -705,15 +710,14 @@ public static function configAndRequestUrls() protected function shouldFail() { - $this->expectException('PHPUnit\Framework\AssertionFailedError'); + $this->expectException(AssertionFailedError::class); } } class JsonSerializedItem implements JsonSerializable { - public function jsonSerialize() + public function jsonSerialize(): array { return array("hello" => "world"); } } - diff --git a/tests/unit/Codeception/Util/JsonArrayTest.php b/tests/unit/Codeception/Util/JsonArrayTest.php index 483189d..8f5daaa 100644 --- a/tests/unit/Codeception/Util/JsonArrayTest.php +++ b/tests/unit/Codeception/Util/JsonArrayTest.php @@ -1,13 +1,15 @@ assertStringContainsString('wed-dev', $jsonArray->toXml()->saveXML()); - $this->assertEquals(2, $jsonArray->filterByXPath('//user')->length); + $this->assertSame(2, $jsonArray->filterByXPath('//user')->length); } public function testXPathLocation() @@ -46,7 +48,7 @@ public function testJsonPathLocation() $this->assertNotEmpty($this->jsonArray->filterByJsonPath('$..user')); $this->assertNotEmpty($this->jsonArray->filterByJsonPath('$.ticket.user.name')); $this->assertNotEmpty($this->jsonArray->filterByJsonPath('$..user.name')); - $this->assertEquals(['Davert'], $this->jsonArray->filterByJsonPath('$.ticket.user.name')); + $this->assertSame(['Davert'], $this->jsonArray->filterByJsonPath('$.ticket.user.name')); $this->assertEmpty($this->jsonArray->filterByJsonPath('$..invalid')); } @@ -55,7 +57,7 @@ public function testJsonPathLocation() */ public function testThrowsInvalidArgumentExceptionIfJsonIsInvalid() { - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); new JsonArray('{"test":'); } @@ -65,7 +67,7 @@ public function testThrowsInvalidArgumentExceptionIfJsonIsInvalid() public function testConvertsBareJson() { $jsonArray = new JsonArray('"I am a {string}."'); - $this->assertEquals(['I am a {string}.'], $jsonArray->toArray()); + $this->assertSame(['I am a {string}.'], $jsonArray->toArray()); } /** @@ -84,7 +86,7 @@ public function testConvertsArrayHavingSingleElement() $jsonArray = new JsonArray('{"success": 1}'); $expectedXml = '' . "\n1\n"; - $this->assertEquals($expectedXml, $jsonArray->toXml()->saveXML()); + $this->assertSame($expectedXml, $jsonArray->toXml()->saveXML()); } public function testConvertsArrayHavingTwoElements() @@ -92,7 +94,7 @@ public function testConvertsArrayHavingTwoElements() $jsonArray = new JsonArray('{"success": 1, "info": "test"}'); $expectedXml = '' . "\n1test\n"; - $this->assertEquals($expectedXml, $jsonArray->toXml()->saveXML()); + $this->assertSame($expectedXml, $jsonArray->toXml()->saveXML()); } public function testConvertsArrayHavingSingleSubArray() @@ -100,6 +102,6 @@ public function testConvertsArrayHavingSingleSubArray() $jsonArray = new JsonArray('{"array": {"success": 1}}'); $expectedXml = '' . "\n1\n"; - $this->assertEquals($expectedXml, $jsonArray->toXml()->saveXML()); + $this->assertSame($expectedXml, $jsonArray->toXml()->saveXML()); } } diff --git a/tests/unit/Codeception/Util/JsonTypeTest.php b/tests/unit/Codeception/Util/JsonTypeTest.php index fe2b993..836fe4a 100644 --- a/tests/unit/Codeception/Util/JsonTypeTest.php +++ b/tests/unit/Codeception/Util/JsonTypeTest.php @@ -1,9 +1,14 @@ 'integer:>10', 'retweeted' => 'Boolean', 'in_reply_to_screen_name' => 'null|string', @@ -12,7 +17,8 @@ class JsonTypeTest extends \Codeception\Test\Unit 'url' => 'String:url' ] ]; - protected $data = [ + + protected array $data = [ 'id' => 11, 'retweeted' => false, 'in_reply_to_screen_name' => null, @@ -20,7 +26,7 @@ class JsonTypeTest extends \Codeception\Test\Unit 'user' => ['url' => 'http://davert.com'] ]; - public function _after() + protected function _after() { JsonType::cleanCustomFilters(); } @@ -138,9 +144,7 @@ public function testNegativeFilters() public function testCustomFilters() { - JsonType::addCustomFilter('slug', function ($value) { - return strpos($value, ' ') === false; - }); + JsonType::addCustomFilter('slug', fn($value): bool => strpos($value, ' ') === false); $jsonType = new JsonType(['title' => 'have a test', 'slug' => 'have-a-test']); $this->assertTrue($jsonType->matches([ 'slug' => 'string:slug' @@ -149,9 +153,7 @@ public function testCustomFilters() 'title' => 'string:slug' ])); - JsonType::addCustomFilter('/len\((.*?)\)/', function ($value, $len) { - return strlen($value) == $len; - }); + JsonType::addCustomFilter('/len\((.*?)\)/', fn($value, $len): bool => strlen($value) == $len); $this->assertTrue($jsonType->matches([ 'slug' => 'string:len(11)' ])); @@ -174,7 +176,7 @@ public function testNull() "birthdate": null, "firstname": "John", "lastname": "Doe" - }', true)); + }', true, 512, JSON_THROW_ON_ERROR)); $this->assertTrue($jsonType->matches([ 'birthdate' => 'string|null' ])); @@ -187,13 +189,13 @@ public function testOR() { $jsonType = new JsonType(json_decode('{ "type": "DAY" - }', true)); + }', true, 512, JSON_THROW_ON_ERROR)); $this->assertTrue($jsonType->matches([ 'type' => 'string:=DAY|string:=WEEK' ])); $jsonType = new JsonType(json_decode('{ "type": "WEEK" - }', true)); + }', true, 512, JSON_THROW_ON_ERROR)); $this->assertTrue($jsonType->matches([ 'type' => 'string:=DAY|string:=WEEK' ]));