diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d85a3e..a968928 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,6 +3,25 @@ name: CI on: [push, pull_request] jobs: + phpcs: + name: Code style + runs-on: 'ubuntu-latest' + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + ini-values: memory_limit=-1, date.timezone='UTC' + tools: phpcs + + - name: Check production code style + run: phpcs src/ + + - name: Check test code style + run: phpcs tests/ --standard=tests/phpcs.xml tests: runs-on: ubuntu-latest @@ -11,7 +30,7 @@ jobs: strategy: matrix: - php: [8.0, 8.1] + php: [8.0, 8.1, 8.2] steps: - name: Checkout code diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..c38639c --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,9 @@ + + + Codeception code standard + + + + + + \ No newline at end of file diff --git a/src/Codeception/Constraint/JsonContains.php b/src/Codeception/Constraint/JsonContains.php index 28b42da..2bb0102 100644 --- a/src/Codeception/Constraint/JsonContains.php +++ b/src/Codeception/Constraint/JsonContains.php @@ -16,14 +16,8 @@ class JsonContains extends Constraint { - /** - * @var array - */ - protected $expected; - - public function __construct(array $expected) + public function __construct(protected array $expected) { - $this->expected = $expected; } /** diff --git a/src/Codeception/Constraint/JsonType.php b/src/Codeception/Constraint/JsonType.php index 5a5c950..2dbaa28 100644 --- a/src/Codeception/Constraint/JsonType.php +++ b/src/Codeception/Constraint/JsonType.php @@ -13,26 +13,17 @@ class JsonType extends Constraint { - /** - * @var array - */ - protected $jsonType; - /** - * @var bool - */ - private $match; - - public function __construct(array $jsonType, bool $match = true) - { - $this->jsonType = $jsonType; - $this->match = $match; + public function __construct( + protected array $jsonType, + private bool $match = true + ) { } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * - * @param mixed $jsonArray Value or object to evaluate. + * @param array|JsonArray $jsonArray Value or object to evaluate. */ protected function matches($jsonArray): bool { diff --git a/src/Codeception/Module/REST.php b/src/Codeception/Module/REST.php index 4062d08..de02e40 100644 --- a/src/Codeception/Module/REST.php +++ b/src/Codeception/Module/REST.php @@ -22,7 +22,10 @@ use Codeception\Util\JsonArray; use Codeception\Util\JsonType; use Codeception\Util\Soap as XmlUtils; +use Codeception\Util\XmlBuilder; use Codeception\Util\XmlStructure; +use DOMDocument; +use DOMNode; use Exception; use JsonException; use JsonSchema\Constraints\Constraint as JsonConstraint; @@ -89,7 +92,6 @@ * ## Conflicts * * Conflicts with SOAP module - * */ class REST extends Module implements DependsOnModule, PartedModule, API, ConflictsWithModule { @@ -121,17 +123,13 @@ class REST extends Module implements DependsOnModule, PartedModule, API, Conflic protected int $DEFAULT_SHORTEN_VALUE = 150; - /** - * @var HttpKernelBrowser|AbstractBrowser - */ - public $client; + public HttpKernelBrowser|AbstractBrowser|null $client; public bool $isFunctional = false; protected ?InnerBrowser $connectionModule = null; - /** @var array */ - public $params = []; + public array|string|ArrayAccess|JsonSerializable $params = []; public ?string $response = null; @@ -150,7 +148,7 @@ protected function resetVariables(): void public function _conflicts(): string { - return \Codeception\Lib\Interfaces\API::class; + return API::class; } public function _depends(): array @@ -202,7 +200,7 @@ protected function getRunningClient(): AbstractBrowser } /** - * Sets a HTTP header to be used for all subsequent requests. Use [`deleteHeader`](#deleteHeader) to unset it. + * Sets na HTTP header to be used for all subsequent requests. Use [`deleteHeader`](#deleteHeader) to unset it. * * ```php * amAWSAuthenticated(); * ``` + * * @throws ConfigurationException */ public function amAWSAuthenticated(array $additionalAWSConfig = []): void @@ -460,18 +457,20 @@ public function amAWSAuthenticated(array $additionalAWSConfig = []): void * ]]); * ``` * - * @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. * - * @see https://php.net/manual/en/features.file-upload.post-method.php - * @see codecept_data_dir() + * @see https://php.net/manual/en/features.file-upload.post-method.php + * @see codecept_data_dir() * @part json * @part xml */ - public function sendPost(string $url, $params = [], array $files = []) - { + public function sendPost( + string $url, + array|string|ArrayAccess|JsonSerializable $params = [], + array $files = [] + ): ?string { return $this->execute('POST', $url, $params, $files); } @@ -481,7 +480,7 @@ public function sendPost(string $url, $params = [], array $files = []) * @part json * @part xml */ - public function sendHead(string $url, array $params = []) + public function sendHead(string $url, array $params = []): ?string { return $this->execute('HEAD', $url, $params); } @@ -511,7 +510,7 @@ public function sendOptions(string $url, array $params = []): void * @part json * @part xml */ - public function sendGet(string $url, array $params = []) + public function sendGet(string $url, array $params = []): ?string { return $this->execute('GET', $url, $params); } @@ -524,12 +523,14 @@ public function sendGet(string $url, array $params = []) * $response = $I->sendPut('/message/1', ['subject' => 'Read this!']); * ``` * - * @param array|string|JsonSerializable $params * @part json * @part xml */ - public function sendPut(string $url, $params = [], array $files = []) - { + public function sendPut( + string $url, + array|string|ArrayAccess|JsonSerializable $params = [], + array $files = [] + ): ?string { return $this->execute('PUT', $url, $params, $files); } @@ -541,12 +542,14 @@ public function sendPut(string $url, $params = [], array $files = []) * $response = $I->sendPatch('/message/1', ['subject' => 'Read this!']); * ``` * - * @param array|string|JsonSerializable $params * @part json * @part xml */ - public function sendPatch(string $url, $params = [], array $files = []) - { + public function sendPatch( + string $url, + array|string|ArrayAccess|JsonSerializable $params = [], + array $files = [] + ): ?string { return $this->execute('PATCH', $url, $params, $files); } @@ -561,20 +564,26 @@ public function sendPatch(string $url, $params = [], array $files = []) * @part json * @part xml */ - public function sendDelete(string $url, array $params = [], array $files = []) - { + public function sendDelete( + string $url, + array|string|ArrayAccess|JsonSerializable $params = [], + array $files = [] + ): ?string { return $this->execute('DELETE', $url, $params, $files); } /** * Sends a HTTP request. * - * @param array|string|JsonSerializable $params * @part json * @part xml */ - public function send(string $method, string $url, $params = [], array $files = []) - { + public function send( + string $method, + string $url, + array|string|ArrayAccess|JsonSerializable $params = [], + array $files = [] + ): ?string { return $this->execute(strtoupper($method), $url, $params, $files); } @@ -615,8 +624,8 @@ private function setHeaderLink(array $linkEntries): void * @link https://tools.ietf.org/html/rfc2068#section-19.6.2.4 * * @author samva.ua@gmail.com - * @part json - * @part xml + * @part json + * @part xml */ public function sendLink(string $url, array $linkEntries): void { @@ -627,11 +636,11 @@ public function sendLink(string $url, array $linkEntries): void /** * Sends UNLINK request to given uri. * - * @param array $linkEntries (entry is array with keys "uri" and "link-param") - * @link https://tools.ietf.org/html/rfc2068#section-19.6.2.4 + * @param array $linkEntries (entry is array with keys "uri" and "link-param") + * @link https://tools.ietf.org/html/rfc2068#section-19.6.2.4 * @author samva.ua@gmail.com - * @part json - * @part xml + * @part json + * @part xml */ public function sendUnlink(string $url, array $linkEntries): void { @@ -640,14 +649,14 @@ public function sendUnlink(string $url, array $linkEntries): void } /** - * @param $method - * @param $url - * @param array|string|object $parameters - * @param array $files * @throws ModuleException|ExternalUrlException|JsonException */ - protected function execute($method, $url, $parameters = [], $files = []) - { + protected function execute( + string $method, + string $url, + array|string|ArrayAccess|JsonSerializable $parameters = [], + array $files = [] + ): ?string { // allow full url to be requested if (!$url) { $url = $this->config['url']; @@ -684,7 +693,8 @@ protected function execute($method, $url, $parameters = [], $files = []) $this->debugSection("Request", sprintf('%s %s', $method, $url)); $files = []; } else { - $this->debugSection("Request", + $this->debugSection( + "Request", sprintf('%s %s ', $method, $url) . json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR) ); $files = $this->formatFilesArray($files); @@ -692,6 +702,9 @@ protected function execute($method, $url, $parameters = [], $files = []) $this->response = $this->connectionModule->_request($method, $url, $parameters, $files); } else { + /** + * @var string $parameters + */ $requestData = $parameters; if ($this->isBinaryData($requestData)) { $requestData = $this->binaryToDebugString($requestData); @@ -731,7 +744,7 @@ protected function isBinaryData(string $data): bool /** * Format a binary string for debug printing * - * @param string $data the binary data string + * @param string $data the binary data string * @return string the debug string */ protected function binaryToDebugString(string $data): string @@ -739,13 +752,14 @@ protected function binaryToDebugString(string $data): string return '[binary-data length:' . strlen($data) . ' md5:' . md5($data) . ']'; } - protected function encodeApplicationJson(string $method, $parameters) - { + protected function encodeApplicationJson( + string $method, + array|string|ArrayAccess|JsonSerializable $parameters, + ): array|string { 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 | JSON_THROW_ON_ERROR); @@ -807,6 +821,7 @@ private function formatFilesArray(array $files): array } elseif (is_object($value)) { /** * do nothing, probably the user knows what he is doing + * * @issue https://github.com/Codeception/Codeception/issues/3298 */ } else { @@ -843,7 +858,6 @@ private function checkFileBeforeUpload(string $file): void /** * Extends the function Module::validateConfig for shorten messages - * */ protected function validateConfig(): void { @@ -901,15 +915,15 @@ public function dontSeeResponseContains(string $text): void * * ``` php * seeResponseContainsJson(array('name' => 'john')); + * // response: {"name": "john", "email": "john@gmail.com"} + * $I->seeResponseContainsJson(['name' => 'john']); * - * // response {user: john, profile: { email: john@gmail.com }} - * $I->seeResponseContainsJson(array('email' => 'john@gmail.com')); + * // response {"user": "john", "profile": {"email": "john@gmail.com"}} + * $I->seeResponseContainsJson(['email' => 'john@gmail.com']); * * ``` * - * This method recursively checks if one array can be found inside of another. + * This method recursively checks if one array can be found inside another. * * @part json */ @@ -977,7 +991,7 @@ public function seeResponseIsValidOnJsonSchemaString(string $schema): void * Supply schema as relative file path in your project directory or an absolute path * * @part json - * @see codecept_absolute_path() + * @see codecept_absolute_path() */ public function seeResponseIsValidOnJsonSchema(string $schemaFilename): void { @@ -992,7 +1006,7 @@ public function seeResponseIsValidOnJsonSchema(string $schemaFilename): void /** * Converts string to json and asserts that no error occurred while decoding. * - * @param string $jsonString the json encoded string + * @param string $jsonString the json encoded string * @param string $errorFormat optional string for custom sprintf format */ protected function decodeAndValidateJson(string $jsonString, string $errorFormat = "Invalid json: %s. System message: %s.") @@ -1045,7 +1059,7 @@ public function grabResponse(): string * * @return array Array of matching items * @throws Exception - * @part json + * @part json */ public function grabDataFromResponseByJsonPath(string $jsonPath): array { @@ -1088,6 +1102,7 @@ public function grabDataFromResponseByJsonPath(string $jsonPath): array * // at least one item in store has price * $I->seeResponseJsonMatchesXpath('/store//price'); * ``` + * * @part json */ public function seeResponseJsonMatchesXpath(string $xPath): void @@ -1134,6 +1149,7 @@ public function seeResponseJsonMatchesXpath(string $xPath): void * // count the number of books written by given author is 5 * $I->seeResponseJsonMatchesXpath("//author[text() = 'Nigel Rees']", 1.0); * ``` + * * @part json */ public function seeResponseJsonXpathEvaluatesTo(string $xPath, $expected): void @@ -1145,7 +1161,7 @@ public function seeResponseJsonXpathEvaluatesTo(string $xPath, $expected): void "Received JSON did not evualated XPath `{$xPath}` as expected.\nJson Response: \n" . $response ); } - + /** * Opposite to seeResponseJsonXpathEvaluatesTo * @@ -1313,13 +1329,13 @@ public function dontSeeResponseContainsJson(array $json = []): void * * ```php * 'davert@codeception.com'} + * // {"user_id": 1, "email" => "davert@codeception.com"} * $I->seeResponseMatchesJsonType([ * 'user_id' => 'string:>0:<1000', // multiple filters can be used * 'email' => 'string:regex(~\@~)' // we just check that @ char is included * ]); * - * // {'user_id': '1'} + * // {"user_id"'": "1"} * $I->seeResponseMatchesJsonType([ * 'user_id' => 'string:>0', // works with strings as well * ]); @@ -1329,7 +1345,7 @@ public function dontSeeResponseContainsJson(array $json = []): void * See [JsonType reference](https://codeception.com/docs/reference/JsonType). * * @part json - * @see JsonType + * @see JsonType */ public function seeResponseMatchesJsonType(array $jsonType, string $jsonPath = null): void { @@ -1344,9 +1360,9 @@ public function seeResponseMatchesJsonType(array $jsonType, string $jsonPath = n /** * Opposite to `seeResponseMatchesJsonType`. * - * @part json + * @part json * @param array $jsonType JsonType structure - * @see seeResponseMatchesJsonType + * @see seeResponseMatchesJsonType */ public function dontSeeResponseMatchesJsonType(array $jsonType, string $jsonPath = null): void { @@ -1486,6 +1502,7 @@ public function seeResponseIsXml(): void * seeXmlResponseMatchesXpath('//root/user[@id=1]'); * ``` + * * @part xml */ public function seeXmlResponseMatchesXpath(string $xPath): void @@ -1501,6 +1518,7 @@ public function seeXmlResponseMatchesXpath(string $xPath): void * dontSeeXmlResponseMatchesXpath('//root/user[@id=1]'); * ``` + * * @part xml */ public function dontSeeXmlResponseMatchesXpath(string $xPath): void @@ -1513,10 +1531,9 @@ public function dontSeeXmlResponseMatchesXpath(string $xPath): void * Finds and returns text contents of element. * Element is matched by either CSS or XPath * - * @param mixed $cssOrXPath * @part xml */ - public function grabTextContentFromXmlElement($cssOrXPath): string + public function grabTextContentFromXmlElement(string $cssOrXPath): string { $el = (new XmlStructure($this->connectionModule->_getResponseContent()))->matchElement($cssOrXPath); return $el->textContent; @@ -1542,12 +1559,9 @@ public function grabAttributeFromXmlElement(string $cssOrXPath, string $attribut * Checks XML response equals provided XML. * Comparison is done by canonicalizing both xml`s. * - * Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes). - * - * @param mixed $xml * @part xml */ - public function seeXmlResponseEquals($xml): void + public function seeXmlResponseEquals(DOMDocument|string $xml): void { Assert::assertXmlStringEqualsXmlString($this->connectionModule->_getResponseContent(), $xml); } @@ -1557,12 +1571,10 @@ public function seeXmlResponseEquals($xml): void * Checks XML response does not equal to provided 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 mixed $xml - * @part xml + * @part xml */ - public function dontSeeXmlResponseEquals($xml): void + public function dontSeeXmlResponseEquals(DOMDocument|string $xml): void { Assert::assertXmlStringNotEqualsXmlString( $this->connectionModule->_getResponseContent(), @@ -1582,10 +1594,9 @@ public function dontSeeXmlResponseEquals($xml): void * $I->seeXmlResponseIncludes("1"); * ``` * - * @param mixed $xml * @part xml */ - public function seeXmlResponseIncludes($xml): void + public function seeXmlResponseIncludes(DOMNode|XmlBuilder|array|string $xml): void { $this->assertStringContainsString( XmlUtils::toXml($xml)->C14N(), @@ -1599,10 +1610,9 @@ public function seeXmlResponseIncludes($xml): void * 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 mixed $xml * @part xml */ - public function dontSeeXmlResponseIncludes($xml): void + public function dontSeeXmlResponseIncludes(DOMNode|XmlBuilder|array|string $xml): void { $this->assertStringNotContainsString( XmlUtils::toXml($xml)->C14N(), @@ -1640,8 +1650,8 @@ public function dontSeeXmlResponseIncludes($xml): void * * @param string $hash the hashed data response expected * @param string $algo the hash algorithm to use. Default sha1. - * @part json - * @part xml + * @part json + * @part xml */ public function seeBinaryResponseEquals(string $hash, string $algo = 'sha1'): void { @@ -1660,8 +1670,8 @@ public function seeBinaryResponseEquals(string $hash, string $algo = 'sha1'): vo * * @param string $hash the hashed data response expected * @param string $algo the hash algorithm to use. Default md5. - * @part json - * @part xml + * @part json + * @part xml */ public function dontSeeBinaryResponseEquals(string $hash, string $algo = 'sha1'): void { diff --git a/src/Codeception/Step/AsJson.php b/src/Codeception/Step/AsJson.php index 78acdf2..1109605 100644 --- a/src/Codeception/Step/AsJson.php +++ b/src/Codeception/Step/AsJson.php @@ -3,15 +3,20 @@ namespace Codeception\Step; use Codeception\Lib\ModuleContainer; +use Codeception\Module\REST; use Codeception\Util\Template; class AsJson extends Action implements GeneratedStep { public function run(ModuleContainer $container = null) { - $container->getModule('REST')->haveHttpHeader('Content-Type', 'application/json'); + /** + * @var REST $restModule + */ + $restModule = $container->getModule('REST'); + $restModule->haveHttpHeader('Content-Type', 'application/json'); $resp = parent::run($container); - $container->getModule('REST')->seeResponseIsJson(); + $restModule->seeResponseIsJson(); return json_decode($resp, true, 512, JSON_THROW_ON_ERROR); } @@ -20,7 +25,9 @@ public static function getTemplate(Template $template): ?Template $action = $template->getVar('action'); // should only be applied to send* methods - if (!str_starts_with($action, 'send')) return null; + if (!str_starts_with($action, 'send')) { + return null; + } $conditionalDoc = "* JSON response will be automatically decoded \n " . $template->getVar('doc'); diff --git a/src/Codeception/Util/ArrayContainsComparator.php b/src/Codeception/Util/ArrayContainsComparator.php index 4d1832c..898715b 100644 --- a/src/Codeception/Util/ArrayContainsComparator.php +++ b/src/Codeception/Util/ArrayContainsComparator.php @@ -32,11 +32,9 @@ public function containsArray(array $needle): bool } /** - * @return array|bool - * @author tiger.seo@gmail.com - * @link https://www.php.net/manual/en/function.array-intersect-assoc.php#39822 - * * @author nleippe@integr8ted.com + * @author tiger.seo@gmail.com + * @link https://www.php.net/manual/en/function.array-intersect-assoc.php#39822 */ private function arrayIntersectRecursive(mixed $arr1, mixed $arr2): bool|array|null { @@ -88,9 +86,6 @@ private function sequentialArrayIntersect(array $arr1, array $arr2): array return $ret; } - /** - * @return array|bool|null - */ private function associativeArrayIntersect(array $arr1, array $arr2): bool|array|null { $commonKeys = array_intersect(array_keys($arr1), array_keys($arr2)); diff --git a/src/Codeception/Util/JsonArray.php b/src/Codeception/Util/JsonArray.php index 288d827..7608d0a 100644 --- a/src/Codeception/Util/JsonArray.php +++ b/src/Codeception/Util/JsonArray.php @@ -75,7 +75,7 @@ public function filterByXPath(string $xPath): DOMNodeList|false $path = new DOMXPath($this->toXml()); return $path->query($xPath); } - + public function evaluateXPath(string $xPath): mixed { $path = new DOMXPath($this->toXml()); @@ -126,10 +126,11 @@ private function arrayToXml(DOMDocument $doc, DOMNode $node, array $array): void } } - private function setValue($subNode, $value) { - switch(gettype($value)) { + private function setValue($subNode, $value) + { + switch (gettype($value)) { case 'boolean': - $subNode->nodeValue = $value?'true':'false'; + $subNode->nodeValue = $value ? 'true' : 'false'; $subNode->setAttribute('type', 'boolean'); break; case 'integer': diff --git a/src/Codeception/Util/JsonType.php b/src/Codeception/Util/JsonType.php index 09e4d59..764824b 100644 --- a/src/Codeception/Util/JsonType.php +++ b/src/Codeception/Util/JsonType.php @@ -26,14 +26,12 @@ * ``` * * Class JsonType + * * @package Codeception\Util */ class JsonType { - /** - * @var array|JsonArray - */ - protected $jsonArray; + protected array $jsonArray; protected static array $customFilters = []; @@ -41,10 +39,8 @@ class JsonType * 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|JsonArray */ - public function __construct($jsonArray) + public function __construct(array|JsonArray $jsonArray) { if ($jsonArray instanceof JsonArray) { $jsonArray = $jsonArray->toArray(); @@ -121,8 +117,8 @@ protected function typeComparison(array $data, array $jsonType): string|bool return sprintf("Key `%s` doesn't exist in ", $key) . json_encode($data, JSON_THROW_ON_ERROR); } - if (is_array($jsonType[$key])) { - $message = $this->typeComparison($data[$key], $jsonType[$key]); + if (is_array($type)) { + $message = $this->typeComparison($data[$key], $type); if (is_string($message)) { return $message; @@ -134,15 +130,19 @@ protected function typeComparison(array $data, array $jsonType): string|bool $regexMatcher = '/:regex\((((\()|(\{)|(\[)|(<)|(.)).*?(?(3)\)|(?(4)\}|(?(5)\]|(?(6)>|\7)))))\)/'; $regexes = []; - // Match the string ':regex(' and any characters until a ending regex delimiter followed by character ')' + // Match the string ':regex(' and any characters until an ending regex delimiter followed by character ')' // Place the 'any character' + delimiter matches in to an array. 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 (): string { - static $count = 0; - return ':regex($$' . $count++ . ')'; - }, $type); + // Do the same match as above, but replace the 'any character' + delimiter with a place holder ($${count}). + $filterType = preg_replace_callback( + $regexMatcher, + function (): string { + static $count = 0; + return ':regex($$' . $count++ . ')'; + }, + $type + ); $matchTypes = preg_split("#(?![^]\(]*\))\|#", $filterType); $matched = false; @@ -164,11 +164,15 @@ protected function typeComparison(array $data, array $jsonType): string|bool foreach ($filters as $filter) { // Fill regex pattern back into the filter. - $filter = preg_replace_callback('#\$\$\d+#', function ($m) use ($regexes) { - $pos = (int)substr($m[0], 2); - - return $regexes[1][$pos]; - }, $filter); + $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, (string)$data[$key]); } diff --git a/tests/_support/Helper/Unit.php b/tests/_support/Helper/Unit.php index 6064d37..4d27aa3 100644 --- a/tests/_support/Helper/Unit.php +++ b/tests/_support/Helper/Unit.php @@ -1,4 +1,5 @@ function() { + 'foo' => function () { if (isset($_SERVER['HTTP_FOO'])) { return 'foo: "' . $_SERVER['HTTP_FOO'] . '"'; } @@ -36,7 +36,7 @@ ]; $GLOBALS['RESTmap']['POST'] = [ - 'user' => function() { + 'user' => function () { $name = $_POST['name']; return ['name' => $name]; }, @@ -46,7 +46,7 @@ ]; $GLOBALS['RESTmap']['PUT'] = [ - 'user' => function() { + 'user' => function () { $name = $_REQUEST['name']; $user = ['name' => 'davert', 'email' => 'davert@mail.ua']; $user['name'] = $name; @@ -55,7 +55,7 @@ ]; $GLOBALS['RESTmap']['DELETE'] = [ - 'user' => function(): void { + 'user' => function (): void { header('error', false, 404); } ]; diff --git a/tests/data/rest/server.php b/tests/data/rest/server.php index 8fcd26b..635d231 100755 --- a/tests/data/rest/server.php +++ b/tests/data/rest/server.php @@ -3,15 +3,14 @@ function RESTServer(): void { // find the function/method to call - $callback = NULL; + $callback = null; if (preg_match('#rest\/([^\/]+)#i', $_SERVER['REQUEST_URI'], $m) && isset($GLOBALS['RESTmap'][$_SERVER['REQUEST_METHOD']][$m[1]])) { $callback = $GLOBALS['RESTmap'][$_SERVER['REQUEST_METHOD']][$m[1]]; } if ($callback) { - // get the request data - $data = NULL; + $data = null; if ($_SERVER['REQUEST_METHOD'] === 'GET') { $data = $_GET; } elseif ($tmp = file_get_contents('php://input')) { diff --git a/tests/phpcs.xml b/tests/phpcs.xml new file mode 100644 index 0000000..9288765 --- /dev/null +++ b/tests/phpcs.xml @@ -0,0 +1,13 @@ + + + Codeception code standard + + data/app + + + + + + + + \ No newline at end of file diff --git a/tests/unit/Codeception/Module/RestTest.php b/tests/unit/Codeception/Module/RestTest.php index d8de3e9..16de8cb 100644 --- a/tests/unit/Codeception/Module/RestTest.php +++ b/tests/unit/Codeception/Module/RestTest.php @@ -20,6 +20,7 @@ /** * Class RestTest + * * @group appveyor */ final class RestTest extends Unit @@ -39,12 +40,14 @@ protected function _setUp() $this->module->_initialize(); $this->module->_before(Stub::makeEmpty(\Codeception\Test\Test::class)); - $this->module->client->setServerParameters([ + $this->module->client->setServerParameters( + [ 'SCRIPT_FILENAME' => 'index.php', 'SCRIPT_NAME' => 'index', 'SERVER_NAME' => 'localhost', 'SERVER_PROTOCOL' => 'http' - ]); + ] + ); } public function testConflictsWithAPI() @@ -104,7 +107,6 @@ public function testPut() $this->module->dontSeeResponseContainsJson(['name' => 'john']); $this->assertNotEmpty($response); $this->assertStringContainsString('"name":"laura"', $response); - } public function testSend() @@ -217,7 +219,9 @@ public function testApplicationJsonIncludesJsonAsContent() { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->sendPOST('/', ['name' => 'john']); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertContains('application/json', $request->getServer()); $server = $request->getServer(); @@ -230,7 +234,9 @@ public function testApplicationJsonIncludesObjectSerialized() { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->sendPOST('/', new JsonSerializedItem()); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertContains('application/json', $request->getServer()); $this->assertJson($request->getContent()); @@ -243,7 +249,9 @@ public function testRequestBodyIsSentAsJsonForThisMethod(string $method) { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->send($method, '/', ['name' => 'john']); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertSame(json_encode(['name' => 'john'], JSON_THROW_ON_ERROR), $request->getContent()); } @@ -254,7 +262,9 @@ public function testRequestBodyIsSentAsJsonForThisMethod(string $method) public function testRequestBodyIsSentUrlEncodedForThisMethod(string $method) { $this->module->send($method, '/', ['name' => 'john']); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertSame(http_build_query(['name' => 'john']), $request->getContent()); } @@ -279,7 +289,9 @@ public function testJsonRequestBodyIsNotSentForThisMethod(string $method) { $this->module->haveHttpHeader('Content-Type', 'application/json'); $this->module->send($method, '/', ['name' => 'john']); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertNull($request->getContent()); $this->assertContains('john', $request->getParameters()); @@ -292,7 +304,9 @@ public function testJsonRequestBodyIsNotSentForThisMethod(string $method) public function testUrlEncodedRequestBodyIsNotSentForThisMethod(string $method) { $this->module->send($method, '/', ['name' => 'john']); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertNull($request->getContent()); $this->assertContains('john', $request->getParameters()); @@ -317,12 +331,11 @@ public function testThrowsExceptionIfParametersIsString(string $method) } /** - * @param array|object $parameters * @dataProvider invalidParameterTypes */ public function testThrowsExceptionIfParametersIsOfUnexpectedType($parameters) { - $this->expectExceptionMessage('POST parameters must be array, string or object implementing JsonSerializable interface'); + $this->expectException(TypeError::class); $this->module->sendPOST('/', $parameters); } @@ -362,17 +375,23 @@ public function testDoesntThrowExceptionIfParametersIsJsonSerializableAndContent public function testUrlIsFull() { $this->module->sendGET('/api/v1/users'); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertSame('http://localhost/api/v1/users', $request->getUri()); } public function testSeeHeaders() { - $response = new SymfonyResponse("", 200, [ + $response = new SymfonyResponse( + "", + 200, + [ 'Cache-Control' => ['no-cache', 'no-store'], 'Content_Language' => 'en-US' - ]); + ] + ); $this->module->client->mockResponse($response); $this->module->sendGET('/'); $this->module->seeHttpHeader('Cache-Control'); @@ -390,9 +409,13 @@ public function testSeeHeaders() public function testSeeHeadersOnce() { $this->shouldFail(); - $response = new SymfonyResponse("", 200, [ + $response = new SymfonyResponse( + "", + 200, + [ 'Cache-Control' => ['no-cache', 'no-store'], - ]); + ] + ); $this->module->client->mockResponse($response); $this->module->sendGET('/'); $this->module->seeHttpHeaderOnce('Cache-Control'); @@ -492,7 +515,9 @@ public function testApplicationJsonSubtypeIncludesObjectSerialized() { $this->module->haveHttpHeader('Content-Type', 'application/resource+json'); $this->module->sendPOST('/', new JsonSerializedItem()); - /** @var SymfonyRequest $request **/ + /** + * @var SymfonyRequest $request +**/ $request = $this->module->client->getRequest(); $this->assertContains('application/resource+json', $request->getServer()); $this->assertJson($request->getContent()); @@ -573,7 +598,7 @@ public function testSeeResponseJsonXpathEvaluatesToBoolean() $this->setStubResponse('{"success": 1}'); $this->module->seeResponseJsonXpathEvaluatesTo('count(//success) > 0', true); } - + public function testSeeResponseJsonXpathEvaluatesToNumber() { $this->setStubResponse('{"success": 1}'); @@ -585,7 +610,7 @@ public function testDontSeeResponseJsonXpathEvaluatesToBoolean() $this->setStubResponse('{"success": 1}'); $this->module->dontSeeResponseJsonXpathEvaluatesTo('count(//success) > 0', false); } - + public function testDontSeeResponseJsonXpathEvaluatesToNumber() { $this->setStubResponse('{"success": 1}'); @@ -688,21 +713,26 @@ public function testRestExecute(string $configUrl, string $requestUrl, string $e ->expects($this->once()) ->method('_request') ->will( - $this->returnCallback(function($method, - $uri, - $parameters, - $files, - $server, - $content - ) use ($expectedFullUrl) { - Assert::assertSame($expectedFullUrl, $uri); - return ''; - }) + $this->returnCallback( + function ( + $method, + $uri, + $parameters, + $files, + $server, + $content + ) use ($expectedFullUrl) { + Assert::assertSame($expectedFullUrl, $uri); + return ''; + } + ) ); $config = ['url' => $configUrl]; - /** @var REST */ + /** + * @var REST +*/ $module = Stub::make(REST::class); $module->_setConfig($config); $module->_inject($connectionModule); diff --git a/tests/unit/Codeception/Util/ArrayContainsComparatorTest.php b/tests/unit/Codeception/Util/ArrayContainsComparatorTest.php index 69af6aa..8b6274c 100644 --- a/tests/unit/Codeception/Util/ArrayContainsComparatorTest.php +++ b/tests/unit/Codeception/Util/ArrayContainsComparatorTest.php @@ -35,11 +35,13 @@ public function testInclusion() */ public function testContainsArrayComparesArrayWithMultipleZeroesCorrectly() { - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ 'responseCode' => 0, 'message' => 'OK', 'data' => [9, 0, 0], - ]); + ] + ); $expectedArray = [ 'responseCode' => 0, @@ -52,11 +54,13 @@ public function testContainsArrayComparesArrayWithMultipleZeroesCorrectly() public function testContainsArrayComparesArrayWithMultipleIdenticalSubArraysCorrectly() { - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ 'responseCode' => 0, 'message' => 'OK', 'data' => [[9], [0], [0]], - ]); + ] + ); $expectedArray = [ 'responseCode' => 0, @@ -95,13 +99,14 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSeco } /** - * @issue https://github.com/Codeception/Codeception/issues/2630 + * @issue https://github.com/Codeception/Codeception/issues/2630 * @codingStandardsIgnoreStart */ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSecondValueIsTheSameButOrderOfItemsIsDifferent() { // @codingStandardsIgnoreEnd - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ [ "2015-09-10", "unknown-date-1" @@ -110,7 +115,8 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSeco "2015-10-10", "unknown-date-1" ] - ]); + ] + ); $expectedArray = [ ["2015-10-10", "unknown-date-1"], ["2015-09-10", "unknown-date-1"], @@ -123,7 +129,8 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSeco */ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSecondValueIsDifferent() { - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ [ "2015-09-10", "unknown-date-1" @@ -132,7 +139,8 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSeco "2015-10-10", "unknown-date-2" ] - ]); + ] + ); $expectedArray = [ ["2015-09-10", "unknown-date-1"], ["2015-10-10", "unknown-date-2"], @@ -145,7 +153,8 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenSeco */ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenJsonHasMoreItemsThanExpectedArray() { - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ [ "2015-09-10", "unknown-date-1" @@ -158,7 +167,8 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenJson "2015-10-10", "unknown-date-2" ] - ]); + ] + ); $expectedArray = [ ["2015-09-10", "unknown-date-1"], ["2015-10-10", "unknown-date-2"], @@ -171,13 +181,15 @@ public function testContainsArrayComparesNestedSequentialArraysCorrectlyWhenJson */ public function testContainsMatchesSuperSetOfExpectedAssociativeArrayInsideSequentialArray() { - $comparator = new ArrayContainsComparator([[ + $comparator = new ArrayContainsComparator( + [[ 'id' => '1', 'title' => 'Game of Thrones', 'body' => 'You are so awesome', 'created_at' => '2015-12-16 10:42:20', 'updated_at' => '2015-12-16 10:42:20', - ]]); + ]] + ); $expectedArray = [['id' => '1']]; $this->assertTrue($comparator->containsArray($expectedArray)); } @@ -187,7 +199,8 @@ public function testContainsMatchesSuperSetOfExpectedAssociativeArrayInsideSeque */ public function testContainsArrayWithUnexpectedLevel() { - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ "level1" => [ "level2irrelevant" => [], "level2" => [ @@ -229,7 +242,8 @@ public function testContainsArrayWithUnexpectedLevel() ] ] ] - ]); + ] + ); $expectedArray = [ 'level1' => [ @@ -270,11 +284,13 @@ public function testContainsArrayComparesSequentialArraysHavingDuplicateSubArray */ public function testContainsArrayComparesAssociativeArrayIntersectCorrectlyWhenExpectedArrayKeyIsEmptyArray() { - $comparator = new ArrayContainsComparator([ + $comparator = new ArrayContainsComparator( + [ 'key' => [ 'foo' => 'bar', ], - ]); + ] + ); $expectedArray = [ 'key' => [], ]; diff --git a/tests/unit/Codeception/Util/JsonArrayTest.php b/tests/unit/Codeception/Util/JsonArrayTest.php index 5343421..889e429 100644 --- a/tests/unit/Codeception/Util/JsonArrayTest.php +++ b/tests/unit/Codeception/Util/JsonArrayTest.php @@ -42,7 +42,7 @@ public function testXPathEvaluation() $this->assertEquals(1, $this->jsonArray->evaluateXPath('count(//ticket/user/name)')); $this->assertTrue($this->jsonArray->evaluateXPath("count(//user/name[text() = 'Davert']) > 0")); } - + public function testXPathTypes() { $jsonArray = new JsonArray( @@ -58,7 +58,7 @@ public function testXPathTypes() $this->assertEquals(1, $jsonArray->evaluateXPath("count(//null[text() = ''])")); $this->assertEquals(1, $jsonArray->evaluateXPath("count(//string[@type = 'string'])")); } - + public function testXPathLocation() { $this->assertGreaterThan(0, $this->jsonArray->filterByXPath('//ticket/title')->length); diff --git a/tests/unit/Codeception/Util/JsonTypeTest.php b/tests/unit/Codeception/Util/JsonTypeTest.php index 852df14..c642f89 100644 --- a/tests/unit/Codeception/Util/JsonTypeTest.php +++ b/tests/unit/Codeception/Util/JsonTypeTest.php @@ -136,30 +136,50 @@ public function testEmailFilter() public function testNegativeFilters() { $jsonType = new JsonType(['name' => 'davert', 'id' => 1]); - $this->assertTrue($jsonType->matches([ - 'name' => 'string:!date|string:!empty', - 'id' => 'integer:!=0', - ])); + $this->assertTrue( + $jsonType->matches( + [ + 'name' => 'string:!date|string:!empty', + 'id' => 'integer:!=0', + ] + ) + ); } public function testCustomFilters() { JsonType::addCustomFilter('slug', fn($value): bool => !str_contains($value, ' ')); $jsonType = new JsonType(['title' => 'have a test', 'slug' => 'have-a-test']); - $this->assertTrue($jsonType->matches([ - 'slug' => 'string:slug' - ])); - $this->assertNotTrue($jsonType->matches([ - 'title' => 'string:slug' - ])); + $this->assertTrue( + $jsonType->matches( + [ + 'slug' => 'string:slug' + ] + ) + ); + $this->assertNotTrue( + $jsonType->matches( + [ + 'title' => 'string:slug' + ] + ) + ); JsonType::addCustomFilter('/len\((.*?)\)/', fn($value, $len): bool => strlen($value) == $len); - $this->assertTrue($jsonType->matches([ - 'slug' => 'string:len(11)' - ])); - $this->assertNotTrue($jsonType->matches([ - 'slug' => 'string:len(7)' - ])); + $this->assertTrue( + $jsonType->matches( + [ + 'slug' => 'string:len(11)' + ] + ) + ); + $this->assertNotTrue( + $jsonType->matches( + [ + 'slug' => 'string:len(7)' + ] + ) + ); } public function testArray() @@ -171,50 +191,97 @@ public function testArray() public function testNull() { - $jsonType = new JsonType(json_decode('{ + $jsonType = new JsonType( + json_decode( + '{ "id": 123456, "birthdate": null, "firstname": "John", "lastname": "Doe" - }', true, 512, JSON_THROW_ON_ERROR)); - $this->assertTrue($jsonType->matches([ - 'birthdate' => 'string|null' - ])); - $this->assertTrue($jsonType->matches([ - 'birthdate' => 'null' - ])); + }', + true, + 512, + JSON_THROW_ON_ERROR + ) + ); + $this->assertTrue( + $jsonType->matches( + [ + 'birthdate' => 'string|null' + ] + ) + ); + $this->assertTrue( + $jsonType->matches( + [ + 'birthdate' => 'null' + ] + ) + ); } public function testOR() { - $jsonType = new JsonType(json_decode('{ + $jsonType = new JsonType( + json_decode( + '{ "type": "DAY" - }', true, 512, JSON_THROW_ON_ERROR)); - $this->assertTrue($jsonType->matches([ - 'type' => 'string:=DAY|string:=WEEK' - ])); - $jsonType = new JsonType(json_decode('{ + }', + true, + 512, + JSON_THROW_ON_ERROR + ) + ); + $this->assertTrue( + $jsonType->matches( + [ + 'type' => 'string:=DAY|string:=WEEK' + ] + ) + ); + $jsonType = new JsonType( + json_decode( + '{ "type": "WEEK" - }', true, 512, JSON_THROW_ON_ERROR)); - $this->assertTrue($jsonType->matches([ - 'type' => 'string:=DAY|string:=WEEK' - ])); + }', + true, + 512, + JSON_THROW_ON_ERROR + ) + ); + $this->assertTrue( + $jsonType->matches( + [ + 'type' => 'string:=DAY|string:=WEEK' + ] + ) + ); } public function testCollection() { - $jsonType = new JsonType([ + $jsonType = new JsonType( + [ ['id' => 1], ['id' => 3], ['id' => 5] - ]); - $this->assertTrue($jsonType->matches([ - 'id' => 'integer' - ])); + ] + ); + $this->assertTrue( + $jsonType->matches( + [ + 'id' => 'integer' + ] + ) + ); - $this->assertNotTrue($res = $jsonType->matches([ - 'id' => 'integer:<3' - ])); + $this->assertNotTrue( + $res = $jsonType->matches( + [ + 'id' => 'integer:<3' + ] + ) + ); $this->assertStringContainsString('3` is of type `integer:<3', $res); $this->assertStringContainsString('5` is of type `integer:<3', $res); @@ -225,17 +292,23 @@ public function testCollection() */ public function testMatchesArrayReturnedByFetchBoth() { - $jsonType = new JsonType([ + $jsonType = new JsonType( + [ '0' => 10, 'a' => 10, '1' => 11, 'b' => 11, - ]); + ] + ); - $this->assertTrue($jsonType->matches([ - 'a' => 'integer', - 'b' => 'integer', - ])); + $this->assertTrue( + $jsonType->matches( + [ + 'a' => 'integer', + 'b' => 'integer', + ] + ) + ); } public function testRegexFilterWithPrefixedAlternatives()