From 3ff95faaaa11e1ad92817f3290e7b1004c48e8d6 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 15 Sep 2022 01:07:15 +0100 Subject: [PATCH] Uses Collision `^7.0` --- TODO.md | 3 + bin/pest | 3 + composer.json | 3 +- overrides/Runner/TestSuiteLoader.php | 52 +++--- phpunit.xml | 17 ++ src/Concerns/Testable.php | 33 +++- src/Contracts/Plugins/HandlesArguments.php | 4 +- src/Exceptions/ShouldNotHappen.php | 2 +- src/Expectation.php | 2 + src/Expectations/HigherOrderExpectation.php | 2 +- src/Factories/TestCaseFactory.php | 40 +++-- src/Factories/TestCaseMethodFactory.php | 12 +- src/Laravel/Commands/PestDatasetCommand.php | 4 +- src/Laravel/Commands/PestInstallCommand.php | 4 +- src/Plugins/Concerns/HandleArguments.php | 14 ++ src/Plugins/Environment.php | 2 +- src/Plugins/Printer.php | 27 +++ src/Repositories/DatasetsRepository.php | 7 +- src/Support/ExceptionTrace.php | 2 +- src/Support/Printer.php | 1 + src/TestCases/IgnorableTestCase.php | 7 - src/TestSuite.php | 12 +- tests/.snapshots/success.txt | 169 +++++++++++++----- tests/Datasets/Bound.php | 16 +- tests/Features/Coverage.php | 1 - tests/Features/Datasets.php | 48 +++-- tests/Features/Exceptions.php | 4 +- tests/Features/Expect/HigherOrder/methods.php | 16 +- .../HigherOrder/methodsAndProperties.php | 12 +- .../Expect/HigherOrder/properties.php | 20 ++- tests/Features/Expect/matchExpectation.php | 86 ++++----- tests/Features/Expect/pipes.php | 67 +++---- tests/Features/Expect/sequence.php | 40 +++-- tests/Features/Expect/toBeCallable.php | 7 +- tests/Features/Expect/toHaveKey.php | 6 +- tests/Features/Expect/toMatchObject.php | 3 +- tests/Features/Expect/toThrow.php | 76 ++++++-- tests/Features/Expect/unless.php | 12 +- tests/Features/Expect/when.php | 12 +- tests/Features/HigherOrderTests.php | 32 +++- tests/Features/Skip.php | 20 ++- tests/Unit/Datasets.php | 32 ++-- tests/Unit/TestSuite.php | 10 +- tests/Visual/Help.php | 2 +- tests/Visual/JUnit.php | 2 +- tests/Visual/Success.php | 6 +- tests/Visual/TeamCity.php | 2 +- 47 files changed, 646 insertions(+), 308 deletions(-) create mode 100644 src/Plugins/Printer.php diff --git a/TODO.md b/TODO.md index b38949f8..a9cda278 100644 --- a/TODO.md +++ b/TODO.md @@ -2,3 +2,6 @@ 2. Support for `default` printer. 3. Support for `TeamCity` printer. 4. Support for `JUnit` log. +5. Plugins +6. Parallel +7. Collision's todo... diff --git a/bin/pest b/bin/pest index d6829c9a..438a88a9 100755 --- a/bin/pest +++ b/bin/pest @@ -11,6 +11,9 @@ use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; (static function () { + // Ensures Collision's Printer is registered. + $_SERVER['COLLISION_PRINTER'] = 'DefaultPrinter'; + // Used when Pest is required using composer. $vendorPath = dirname(__DIR__, 4) . '/vendor/autoload.php'; diff --git a/composer.json b/composer.json index 6ba5553b..a2c246ce 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ ], "require": { "php": "^8.1.0", - "nunomaduro/collision": "^6.3", + "nunomaduro/collision": "dev-next", "pestphp/pest-plugin": "^1.0.0", "phpunit/phpunit": "10.0.x-dev" }, @@ -86,6 +86,7 @@ "Pest\\Plugins\\Init", "Pest\\Plugins\\Environment", "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Printer", "Pest\\Plugins\\Retry", "Pest\\Plugins\\Version" ] diff --git a/overrides/Runner/TestSuiteLoader.php b/overrides/Runner/TestSuiteLoader.php index aaa894a0..8745fdea 100644 --- a/overrides/Runner/TestSuiteLoader.php +++ b/overrides/Runner/TestSuiteLoader.php @@ -44,14 +44,13 @@ use function basename; use function class_exists; use function get_declared_classes; +use Pest\Contracts\HasPrintableTestCaseName; use Pest\TestCases\IgnorableTestCase; use Pest\TestSuite; use PHPUnit\Framework\TestCase; use ReflectionClass; use ReflectionException; -use function stripos; -use function strlen; use function substr; /** @@ -83,36 +82,43 @@ final class TestSuiteLoader { $suiteClassName = $this->classNameFromFileName($suiteClassFile); - if (!class_exists($suiteClassName, false)) { - (static function () use ($suiteClassFile) { - include_once $suiteClassFile; + (static function () use ($suiteClassFile) { + include_once $suiteClassFile; - TestSuite::getInstance()->tests->makeIfNeeded($suiteClassFile); - })(); + TestSuite::getInstance()->tests->makeIfNeeded($suiteClassFile); + })(); - $loadedClasses = array_values( - array_diff( - get_declared_classes(), - array_merge( - self::$declaredClasses, - self::$loadedClasses - ) + $loadedClasses = array_values( + array_diff( + get_declared_classes(), + array_merge( + self::$declaredClasses, + self::$loadedClasses ) - ); + ) + ); - self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses); + self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses); - if (empty(self::$loadedClasses)) { - return $this->exceptionFor($suiteClassName, $suiteClassFile); + if (empty(self::$loadedClasses)) { + return $this->exceptionFor($suiteClassName, $suiteClassFile); + } + + $testCaseFound = false; + + foreach (self::$loadedClasses as $loadedClass) { + if (is_subclass_of($loadedClass, HasPrintableTestCaseName::class)) { + $suiteClassName = $loadedClass; + + $testCaseFound = true; + + break; } } - if (!class_exists($suiteClassName, false)) { - // this block will handle namespaced classes - $offset = 0 - strlen($suiteClassName); - + if (!$testCaseFound) { foreach (self::$loadedClasses as $loadedClass) { - if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0) { + if (is_subclass_of($loadedClass, TestCase::class)) { $suiteClassName = $loadedClass; break; diff --git a/phpunit.xml b/phpunit.xml index 439f1b81..2811352b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,8 +1,25 @@ diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php index e45f1956..615360b4 100644 --- a/src/Concerns/Testable.php +++ b/src/Concerns/Testable.php @@ -9,13 +9,21 @@ use Pest\Support\ChainableClosure; use Pest\Support\ExceptionTrace; use Pest\Support\Reflection; use Pest\TestSuite; +use PHPUnit\Framework\TestCase; use Throwable; /** * @internal + * + * @mixin TestCase */ trait Testable { + /** + * Test method description. + */ + private static string $__description; + /** * The Test Case "test" closure. */ @@ -118,14 +126,6 @@ trait Testable : $hook; } - /** - * Gets the Test Case filename. - */ - public static function __getFilename(): string - { - return self::$__filename; - } - /** * This method is called before the first test of this Test Case is run. */ @@ -211,6 +211,13 @@ trait Testable */ private function __resolveTestArguments(array $arguments): array { + $method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name()); + if ($this->dataName()) { + self::$__description = $method->description . ' with ' . $this->dataName(); + } else { + self::$__description = $method->description; + } + if (count($arguments) !== 1) { return $arguments; } @@ -246,8 +253,16 @@ trait Testable /** * Gets the Test Case name that should be used by printers. */ - public function getPrintableTestCaseName(): string + public static function getPrintableTestCaseName(): string { return ltrim(self::class, 'P\\'); } + + /** + * Gets the Test Case name that should be used by printers. + */ + public static function getPrintableTestCaseMethodName(): string + { + return self::$__description; + } } diff --git a/src/Contracts/Plugins/HandlesArguments.php b/src/Contracts/Plugins/HandlesArguments.php index a3c69ac5..c58c4e2d 100644 --- a/src/Contracts/Plugins/HandlesArguments.php +++ b/src/Contracts/Plugins/HandlesArguments.php @@ -12,9 +12,9 @@ interface HandlesArguments /** * Adds arguments before of the Test Suite execution. * - * @param array $argv + * @param array $arguments * * @return array */ - public function handleArguments(array $argv): array; + public function handleArguments(array $arguments): array; } diff --git a/src/Exceptions/ShouldNotHappen.php b/src/Exceptions/ShouldNotHappen.php index c2bb424b..47b764c6 100644 --- a/src/Exceptions/ShouldNotHappen.php +++ b/src/Exceptions/ShouldNotHappen.php @@ -19,7 +19,7 @@ final class ShouldNotHappen extends RuntimeException { $message = $exception->getMessage(); - parent::__construct(sprintf(<< $item) { if ($callbacks[$key] instanceof Closure) { call_user_func($callbacks[$key], new self($item), new self($keys[$key])); + continue; } @@ -220,6 +221,7 @@ final class Expectation if (is_callable($callback)) { $callback(new self($this->value)); + continue; } diff --git a/src/Expectations/HigherOrderExpectation.php b/src/Expectations/HigherOrderExpectation.php index f3aa62e7..5d194cdb 100644 --- a/src/Expectations/HigherOrderExpectation.php +++ b/src/Expectations/HigherOrderExpectation.php @@ -37,7 +37,7 @@ final class HigherOrderExpectation */ public function __construct(private Expectation $original, mixed $value) { - $this->expectation = $this->expect($value); + $this->expectation = $this->expect($value); } /** diff --git a/src/Factories/TestCaseFactory.php b/src/Factories/TestCaseFactory.php index 938b0d2b..abf75301 100644 --- a/src/Factories/TestCaseFactory.php +++ b/src/Factories/TestCaseFactory.php @@ -121,7 +121,17 @@ final class TestCaseFactory $filename = str_replace('\\\\', '\\', addslashes((string) realpath($filename))); $rootPath = TestSuite::getInstance()->rootPath; $relativePath = str_replace($rootPath . DIRECTORY_SEPARATOR, '', $filename); - $relativePath = dirname(ucfirst($relativePath)) . DIRECTORY_SEPARATOR . basename($relativePath, '.php'); + + $basename = basename($relativePath, '.php'); + + $dotPos = strpos($basename, '.'); + + if ($dotPos !== false) { + $basename = substr($basename, 0, $dotPos); + } + + $relativePath = dirname(ucfirst($relativePath)) . DIRECTORY_SEPARATOR . $basename; + $relativePath = str_replace(DIRECTORY_SEPARATOR, '\\', $relativePath); // Strip out any %-encoded octets. @@ -132,6 +142,7 @@ final class TestCaseFactory $relativePath = (string) preg_replace('/[^A-Za-z0-9\\\\]/', '', $relativePath); $classFQN = 'P\\' . $relativePath; + if (class_exists($classFQN)) { return; } @@ -174,27 +185,28 @@ final class TestCaseFactory )); $classAttributesCode = implode('', array_map( - static fn (string $attribute) => sprintf("\n %s", $attribute), + static fn (string $attribute) => sprintf("\n%s", $attribute), array_unique($classAttributes), )); try { - eval(" - namespace $namespace; + $classCode = <<proxies->proxy($this); $method->proxies->proxy($this); @@ -148,28 +147,29 @@ final class TestCaseMethodFactory } $annotations = implode('', array_map( - static fn ($annotation) => sprintf("\n * %s", $annotation), $annotations, + static fn ($annotation) => sprintf("\n * %s", $annotation), $annotations, )); $attributes = implode('', array_map( static fn ($attribute) => sprintf("\n %s", $attribute), $attributes, )); - return <<tests->get(self::\$__filename)->getMethod(\$this->name())->getClosure(\$this); + return \$this->__runTest( - \$this->__test, + \$test, ...func_get_args(), ); } - $datasetsCode - EOF; + PHP; } /** diff --git a/src/Laravel/Commands/PestDatasetCommand.php b/src/Laravel/Commands/PestDatasetCommand.php index df225ee9..67a75e4b 100644 --- a/src/Laravel/Commands/PestDatasetCommand.php +++ b/src/Laravel/Commands/PestDatasetCommand.php @@ -44,10 +44,10 @@ final class PestDatasetCommand extends Command /** @var string $name */ $name = $this->argument('name'); - $relativePath = sprintf(testDirectory('DatasetsRepository/%s.php'), ucfirst($name)); + $relativePath = sprintf(testDirectory('DatasetsRepository/%s.php'), ucfirst($name)); /* @phpstan-ignore-next-line */ - $target = base_path($relativePath); + $target = base_path($relativePath); if (File::exists($target)) { throw new InvalidConsoleArgument(sprintf('%s already exist', $target)); diff --git a/src/Laravel/Commands/PestInstallCommand.php b/src/Laravel/Commands/PestInstallCommand.php index fb6275ce..36bfc2d2 100644 --- a/src/Laravel/Commands/PestInstallCommand.php +++ b/src/Laravel/Commands/PestInstallCommand.php @@ -41,8 +41,8 @@ final class PestInstallCommand extends Command TestSuite::getInstance(base_path(), $this->option('test-directory')); /* @phpstan-ignore-next-line */ - $pest = base_path(testDirectory('Pest.php')); - $stubs = 'stubs/Laravel'; + $pest = base_path(testDirectory('Pest.php')); + $stubs = 'stubs/Laravel'; if (File::exists($pest)) { throw new InvalidConsoleArgument(sprintf('%s already exist', $pest)); diff --git a/src/Plugins/Concerns/HandleArguments.php b/src/Plugins/Concerns/HandleArguments.php index de1de7db..6ff49496 100644 --- a/src/Plugins/Concerns/HandleArguments.php +++ b/src/Plugins/Concerns/HandleArguments.php @@ -19,6 +19,20 @@ trait HandleArguments return in_array($argument, $arguments, true); } + /** + * Adds the given argument and value to the list of arguments. + * + * @param array $arguments + * + * @return array + */ + public function pushArgument(string $argument, array $arguments): array + { + $arguments[] = $argument; + + return $arguments; + } + /** * Pops the given argument from the arguments. * diff --git a/src/Plugins/Environment.php b/src/Plugins/Environment.php index 3b0e028f..aebe2343 100644 --- a/src/Plugins/Environment.php +++ b/src/Plugins/Environment.php @@ -14,7 +14,7 @@ final class Environment implements HandlesArguments /** * The continuous integration environment. */ - public const CI = 'ci'; + public const CI = 'ci'; /** * The local environment. diff --git a/src/Plugins/Printer.php b/src/Plugins/Printer.php new file mode 100644 index 00000000..b4f4bee3 --- /dev/null +++ b/src/Plugins/Printer.php @@ -0,0 +1,27 @@ +pushArgument('--no-output', $arguments); + } +} diff --git a/src/Repositories/DatasetsRepository.php b/src/Repositories/DatasetsRepository.php index 931db736..c4597fd3 100644 --- a/src/Repositories/DatasetsRepository.php +++ b/src/Repositories/DatasetsRepository.php @@ -57,6 +57,11 @@ final class DatasetsRepository self::$withs[$filename . '>>>' . $description] = $with; } + public static function has(string $filename, string $description): bool + { + return array_key_exists($filename . '>>>' . $description, self::$withs); + } + /** * @return Closure|iterable|never * @@ -107,7 +112,7 @@ final class DatasetsRepository $values = array_merge($values, $datasetCombinationElement['values']); } - $datasetDescriptions[] = $description . ' with ' . implode(' / ', $partialDescriptions); + $datasetDescriptions[] = implode(' / ', $partialDescriptions); $datasetValues[] = $values; } diff --git a/src/Support/ExceptionTrace.php b/src/Support/ExceptionTrace.php index 2e6d53f4..28c5d43b 100644 --- a/src/Support/ExceptionTrace.php +++ b/src/Support/ExceptionTrace.php @@ -56,7 +56,7 @@ final class ExceptionTrace $cleanedTrace = []; foreach ($trace as $item) { - if (key_exists('file', $item) && mb_strpos($item['file'], 'vendor/pestphp/pest/') > 0) { + if (array_key_exists('file', $item) && mb_strpos($item['file'], 'vendor/pestphp/pest/') > 0) { continue; } diff --git a/src/Support/Printer.php b/src/Support/Printer.php index f121fc19..b321391c 100644 --- a/src/Support/Printer.php +++ b/src/Support/Printer.php @@ -13,6 +13,7 @@ abstract class Printer implements \PHPUnit\Util\Printer private $stream; private bool $isPhpStream; + private bool $isOpen; private function __construct(string $out) diff --git a/src/TestCases/IgnorableTestCase.php b/src/TestCases/IgnorableTestCase.php index dfe26889..d8f94c0e 100644 --- a/src/TestCases/IgnorableTestCase.php +++ b/src/TestCases/IgnorableTestCase.php @@ -12,11 +12,4 @@ use PHPUnit\Framework\TestCase; */ class IgnorableTestCase extends TestCase { - /** - * @test - */ - public function fake(): void - { - self::markTestIncomplete(); - } } diff --git a/src/TestSuite.php b/src/TestSuite.php index 2d40cedd..7ff5c130 100644 --- a/src/TestSuite.php +++ b/src/TestSuite.php @@ -70,12 +70,12 @@ final class TestSuite string $rootPath, public string $testPath) { - $this->beforeAll = new BeforeAllRepository(); - $this->beforeEach = new BeforeEachRepository(); - $this->tests = new TestRepository(); - $this->afterEach = new AfterEachRepository(); - $this->afterAll = new AfterAllRepository(); - $this->retryTempRepository = new TempRepository('retry'); + $this->beforeAll = new BeforeAllRepository(); + $this->beforeEach = new BeforeEachRepository(); + $this->tests = new TestRepository(); + $this->afterEach = new AfterEachRepository(); + $this->afterAll = new AfterAllRepository(); + $this->retryTempRepository = new TempRepository('retry'); $this->rootPath = (string) realpath($rootPath); } diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index fe437645..1381dbb5 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -1,7 +1,4 @@ - PASS Tests\CustomTestCase\ExecutedTest - ✓ that gets executed - PASS Tests\Features\AfterAll ✓ deletes file after all @@ -23,6 +20,15 @@ ✓ it adds coverage if --min exist ✓ it generates coverage based on file input + PASS Tests\Features\Covers + ✓ it uses the correct PHPUnit attribute for class + ✓ it uses the correct PHPUnit attribute for function + ✓ it removes duplicated attributes + ✓ it guesses if the given argument is a class or function + ✓ it appends CoversNothing to method attributes + ✓ it does not append CoversNothing to other methods + ✓ it throws exception if no class nor method has been found + PASS Tests\Features\Datasets ✓ it throws exception if dataset does not exist ✓ it throws exception if dataset already exist @@ -111,16 +117,35 @@ ✓ it can correctly resolve a bound dataset that returns an array with (Closure Object (...)) ✓ it can correctly resolve a bound dataset that returns an array but wants to be spread with (Closure Object (...)) + PASS Tests\Features\Depends + ✓ first + ✓ second + ✓ depends + ✓ depends with ...params + ✓ depends with defined arguments + ✓ depends run test only once + ✓ it asserts true is true + ✓ depends works with the correct test name + + PASS Tests\Features\DependsInheritance + ✓ it is a test + ✓ it uses correct parent class + PASS Tests\Features\Exceptions ✓ it gives access the the underlying expectException ✓ it catch exceptions ✓ it catch exceptions and messages + ✓ it catch exceptions, messages and code ✓ it can just define the message + ✓ it can just define the code ✓ it not catch exceptions if given condition is false ✓ it catch exceptions if given condition is true ✓ it catch exceptions and messages if given condition is true + ✓ it catch exceptions, messages and code if given condition is true ✓ it can just define the message if given condition is true + ✓ it can just define the code if given condition is true ✓ it can just define the message if given condition is 1 + ✓ it can just define the code if given condition is 1 PASS Tests\Features\Expect\HigherOrder\methods ✓ it can access methods @@ -133,6 +158,8 @@ ✓ it can compose complex expectations ✓ it can handle nested method calls ✓ it works with higher order tests + ✓ it can use the scoped method to lock into the given level for expectations + ✓ it works consistently with the json expectation method PASS Tests\Features\Expect\HigherOrder\methodsAndProperties ✓ it can access methods and properties @@ -140,6 +167,7 @@ ✓ it works with higher order tests ✓ it can start a new higher order expectation using the and syntax ✓ it can start a new higher order expectation using the and syntax in higher order tests + ✓ it can start a new higher order expectation using the and syntax without nesting expectations PASS Tests\Features\Expect\HigherOrder\properties ✓ it allows properties to be accessed from the value @@ -161,6 +189,7 @@ ✓ chained opposite and non-opposite expectations ✓ it can add expectations via "and" ✓ it accepts callables + ✓ it passes the key of the current item to callables PASS Tests\Features\Expect\extend ✓ it macros true is true @@ -186,15 +215,16 @@ PASS Tests\Features\Expect\not ✓ not property calls - PASS Tests\Features\Expect\pipe + PASS Tests\Features\Expect\pipes ✓ pipe is applied and can stop pipeline - ✓ interceptor works with negated expectation - ✓ pipe works with negated expectation ✓ pipe is run and can let the pipeline keep going - ✓ intercept is applied - ✓ intercept stops the pipeline - ✓ interception is called only when filter is met - ✓ intercept can be filtered with a closure + ✓ pipe works with negated expectation + ✓ interceptor is applied + ✓ interceptor stops the pipeline + ✓ interceptor is called only when filter is met + ✓ interceptor can be filtered with a closure + ✓ interceptor can be filter the expected parameter as well + ✓ interceptor works with negated expectation ✓ intercept can add new parameters to the expectation PASS Tests\Features\Expect\ray @@ -405,6 +435,11 @@ ✓ not failures with multiple needles (all failing) ✓ not failures with multiple needles (some failing) + PASS Tests\Features\Expect\toContainOnlyInstancesOf + ✓ pass + ✓ failures + ✓ not failures + PASS Tests\Features\Expect\toEndWith ✓ pass ✓ failures @@ -452,8 +487,11 @@ PASS Tests\Features\Expect\toHaveKeys ✓ pass + ✓ pass with multi-dimensional arrays ✓ failures + ✓ failures with multi-dimensional arrays ✓ not failures + ✓ not failures with multi-dimensional arrays PASS Tests\Features\Expect\toHaveLength ✓ it passes with ('Fortaleza') @@ -496,6 +534,7 @@ PASS Tests\Features\Expect\toMatchObject ✓ pass + ✓ pass with class ✓ failures ✓ not failures @@ -516,6 +555,8 @@ ✓ not failures ✓ closure missing parameter ✓ closure missing type-hint + ✓ it can handle a non-defined exception + ✓ it can handle a class not found Error PASS Tests\Features\Expect\unless ✓ it pass @@ -548,9 +589,9 @@ ✓ it is capable doing multiple assertions ✓ it resolves expect callables correctly ✓ does not treat method names as callables - ✓ it can tap into the test + ✓ it can defer a method until after test setup ✓ it can pass datasets into the expect callables with (1, 2, 3) - ✓ it can pass datasets into the tap callable with (1, 2, 3) + ✓ it can pass datasets into the defer callable with (1, 2, 3) ✓ it can pass shared datasets into callables with (1) ✓ it can pass shared datasets into callables with (2) @@ -578,7 +619,7 @@ WARN Tests\Features\Skip ✓ it do not skips - - it skips with truthy + - it skips with truthy → 1 - it skips with truthy condition by default - it skips with message → skipped because bar - it skips with truthy closure condition @@ -592,6 +633,10 @@ ✓ a test ✓ higher order message test + PASS Tests\Features\ThrowsNoExceptions + ✓ it allows access to the underlying expectNotToPerformAssertions method + ✓ it allows performing no expectations without being risky + PASS Tests\Fixtures\DirectoryWithTests\ExampleTest ✓ it example 1 @@ -618,14 +663,14 @@ PASS Tests\PHPUnit\CustomAffixes\ATestWithSpaces ✓ it runs file names like `A Test With Spaces.php` - PASS Tests\PHPUnit\CustomAffixes\AdditionalFileExtensionspec + PASS Tests\PHPUnit\CustomAffixes\AdditionalFileExtension ✓ it runs file names like `AdditionalFileExtension.spec.php` PASS Tests\PHPUnit\CustomAffixes\FolderWithAn\ExampleTest ✓ custom traits can be used ✓ trait applied in this file - PASS Tests\PHPUnit\CustomAffixes\ManyExtensionsclasstest + PASS Tests\PHPUnit\CustomAffixes\ManyExtensions ✓ it runs file names like `ManyExtensions.class.test.php` PASS Tests\PHPUnit\CustomAffixes\TestCaseWithQuotes @@ -654,18 +699,14 @@ ✓ it allows global uses ✓ it allows multiple global uses registered in the same path - PASS Tests\Unit\Actions\AddsDefaults - ✓ it sets defaults - ✓ it does not override options - - PASS Tests\Unit\Actions\AddsTests - ✓ default php unit tests - ✓ it removes warnings - - PASS Tests\Unit\Actions\ValidatesConfiguration - ✓ it throws exception when configuration not found - ✓ it throws exception when `process isolation` is true - ✓ it do not throws exception when `process isolation` is false + WARN Tests\Unit\ConfigLoader + ✓ it fallbacks to default path if no phpunit file is found + - it fallbacks to default path if phpunit is not a valid XML + - it fallbacks to default path if failing to read phpunit content + - it fallbacks to default path if there is no test suites directory + - it fallbacks to default path if test suite directory has no value + - it fallbacks to default path if test suite directory does not exist + - it returns the parent folder of first test suite directory PASS Tests\Unit\Console\Help ✓ it outputs the help information when --help is used @@ -681,6 +722,9 @@ ✓ environment is set to CI when --ci option is used ✓ environment is set to Local when --ci option is not used + PASS Tests\Unit\Plugins\Retry + ✓ it retries if --retry argument is used + PASS Tests\Unit\Plugins\Version ✓ it outputs the version when --version is used ✓ it do not outputs version when --version is not used @@ -708,37 +752,66 @@ ✓ it can filter the test suite filenames to those with the only method ✓ it does not filter the test suite filenames to those with the only method when working in CI pipeline - PASS Tests\Visual\Help - ✓ visual snapshot of help command output + WARN Tests\Visual\Help + - visual snapshot of help command output → Not supported yet. - PASS Tests\Visual\JUnit - ✓ it is can successfully call all public methods + WARN Tests\Visual\JUnit + - it is can successfully call all public methods → Not supported yet. - PASS Tests\Visual\SingleTestOrDirectory + FAIL Tests\Visual\SingleTestOrDirectory ✓ allows to run a single test ✓ allows to run a directory - ✓ it has ascii chars + ⨯ it has ascii chars ✓ it disable decorating printer when colors is set to never WARN Tests\Visual\Success - visual snapshot of test suite on success - PASS Tests\Visual\TeamCity - ✓ it is can successfully call all public methods + WARN Tests\Visual\TeamCity + - it is can successfully call all public methods → Not supported yet. - PASS Tests\Features\Depends - ✓ first - ✓ second - ✓ it asserts true is true - ✓ depends - ✓ depends with ...params - ✓ depends with defined arguments - ✓ depends run test only once - ✓ depends works with the correct test name + PHPUnit\Framework\ExpectationFailedException - PASS Tests\Features\DependsInheritance - ✓ it is a test - ✓ it uses correct parent class + Failed asserting that '\n + PASS Tests\Fixtures\DirectoryWithTests\ExampleTest\n + ✓ it example 1\n + \n + Tests: 1 passed\n + Time: 0.00s\n + \n + ' contains " + PASS Tests\Fixtures\DirectoryWithTests\ExampleTest + ✓ it example 1 + + Tests: 1 passed + ". + + /Users/nunomaduro/Work/Code/pest/src/Mixins/Expectation.php:181 + /Users/nunomaduro/Work/Code/pest/src/Support/ExpectationPipeline.php:80 + /Users/nunomaduro/Work/Code/pest/src/Support/ExpectationPipeline.php:84 + /Users/nunomaduro/Work/Code/pest/src/Expectation.php:297 + /Users/nunomaduro/Work/Code/pest/tests/Visual/SingleTestOrDirectory.php:32 + /Users/nunomaduro/Work/Code/pest/src/Factories/TestCaseMethodFactory.php:101 + /Users/nunomaduro/Work/Code/pest/src/Concerns/Testable.php:251 + /Users/nunomaduro/Work/Code/pest/src/Support/ExceptionTrace.php:29 + /Users/nunomaduro/Work/Code/pest/src/Concerns/Testable.php:251 + /Users/nunomaduro/Work/Code/pest/src/Concerns/Testable.php:205 + /Users/nunomaduro/Work/Code/pest/src/Kernel.php:57 - Tests: 4 incompleted, 9 skipped, 487 passed + at src/Mixins/Expectation.php:181 + 177▕ { + 178▕ foreach ($needles as $needle) { + 179▕ if (is_string($this->value)) { + 180▕ // @phpstan-ignore-next-line + ➜ 181▕ Assert::assertStringContainsString((string) $needle, $this->value); + 182▕ } else { + 183▕ if (!is_iterable($this->value)) { + 184▕ InvalidExpectationValue::expected('iterable'); + 185▕ } + + 1 src/Mixins/Expectation.php:181 + 2 src/Support/ExpectationPipeline.php:80 + + + Tests: 1 failed, 4 incompleted, 18 skipped, 514 passed, 8 pending \ No newline at end of file diff --git a/tests/Datasets/Bound.php b/tests/Datasets/Bound.php index e442a658..f8932962 100644 --- a/tests/Datasets/Bound.php +++ b/tests/Datasets/Bound.php @@ -1,11 +1,19 @@ assertTrue(class_exists(CoveragePlugin::class)); it('adds coverage if --coverage exist', function () { $plugin = new CoveragePlugin(new ConsoleOutput()); - $testSuite = TestSuite::getInstance(); expect($plugin->coverage)->toBeFalse(); $arguments = $plugin->handleArguments([]); diff --git a/tests/Features/Datasets.php b/tests/Features/Datasets.php index 4b7f9dc3..1fc3bd43 100644 --- a/tests/Features/Datasets.php +++ b/tests/Features/Datasets.php @@ -28,23 +28,23 @@ it('sets closures', function () { yield [1]; }); - expect(DatasetsRepository::resolve('foo', ['foo']))->toBe(['foo with (1)' => [1]]); + expect(DatasetsRepository::resolve('foo', ['foo']))->toBe(['(1)' => [1]]); }); it('sets arrays', function () { DatasetsRepository::set('bar', [[2]]); - expect(DatasetsRepository::resolve('bar', ['bar']))->toBe(['bar with (2)' => [2]]); + expect(DatasetsRepository::resolve('bar', ['bar']))->toBe(['(2)' => [2]]); }); -it('gets bound to test case object', function () { +it('gets bound to test case object', function ($value) { $this->assertTrue(true); })->with([['a'], ['b']]); test('it truncates the description', function () { expect(true)->toBe(true); // it gets tested by the integration test -})->with([str_repeat('Fooo', 10000000)]); +})->with([str_repeat('Fooo', 10)]); $state = new stdClass(); $state->text = ''; @@ -233,8 +233,12 @@ test('more than two datasets did the job right', function () use ($state) { it('can resolve a dataset after the test case is available', function ($result) { expect($result)->toBe('bar'); })->with([ - function () { return $this->foo; }, - [function () { return $this->foo; }], + function () { + return $this->foo; + }, + [function () { + return $this->foo; + }], ]); it('can resolve a dataset after the test case is available with shared yield sets', function ($result) { @@ -249,38 +253,54 @@ it('resolves a potential bound dataset logically', function ($foo, $bar) { expect($foo)->toBe('foo'); expect($bar())->toBe('bar'); })->with([ - ['foo', function () { return 'bar'; }], // This should be passed as a closure because we've passed multiple arguments + ['foo', function () { + return 'bar'; + }], // This should be passed as a closure because we've passed multiple arguments ]); it('resolves a potential bound dataset logically even when the closure comes first', function ($foo, $bar) { expect($foo())->toBe('foo'); expect($bar)->toBe('bar'); })->with([ - [function () { return 'foo'; }, 'bar'], // This should be passed as a closure because we've passed multiple arguments + [function () { + return 'foo'; + }, 'bar'], // This should be passed as a closure because we've passed multiple arguments ]); it('will not resolve a closure if it is type hinted as a closure', function (Closure $data) { expect($data())->toBeString(); })->with([ - function () { return 'foo'; }, - function () { return 'bar'; }, + function () { + return 'foo'; + }, + function () { + return 'bar'; + }, ]); it('will not resolve a closure if it is type hinted as a callable', function (callable $data) { expect($data())->toBeString(); })->with([ - function () { return 'foo'; }, - function () { return 'bar'; }, + function () { + return 'foo'; + }, + function () { + return 'bar'; + }, ]); it('can correctly resolve a bound dataset that returns an array', function (array $data) { expect($data)->toBe(['foo', 'bar', 'baz']); })->with([ - function () { return ['foo', 'bar', 'baz']; }, + function () { + return ['foo', 'bar', 'baz']; + }, ]); it('can correctly resolve a bound dataset that returns an array but wants to be spread', function (string $foo, string $bar, string $baz) { expect([$foo, $bar, $baz])->toBe(['foo', 'bar', 'baz']); })->with([ - function () { return ['foo', 'bar', 'baz']; }, + function () { + return ['foo', 'bar', 'baz']; + }, ]); diff --git a/tests/Features/Exceptions.php b/tests/Features/Exceptions.php index 71482fbe..3b45b0db 100644 --- a/tests/Features/Exceptions.php +++ b/tests/Features/Exceptions.php @@ -32,7 +32,9 @@ it('not catch exceptions if given condition is false', function () { it('catch exceptions if given condition is true', function () { throw new Exception('Something bad happened'); -})->throwsIf(function () { return true; }, Exception::class); +})->throwsIf(function () { + return true; +}, Exception::class); it('catch exceptions and messages if given condition is true', function () { throw new Exception('Something bad happened'); diff --git a/tests/Features/Expect/HigherOrder/methods.php b/tests/Features/Expect/HigherOrder/methods.php index 3c03ddb5..5cac3fa9 100644 --- a/tests/Features/Expect/HigherOrder/methods.php +++ b/tests/Features/Expect/HigherOrder/methods.php @@ -40,8 +40,12 @@ it('works inside of each', function () { it('works with sequence', function () { expect(new HasMethods()) ->books()->sequence( - function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); }, - function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); }, + function ($book) { + $book->title->toEqual('Foo')->cost->toEqual(20); + }, + function ($book) { + $book->title->toEqual('Bar')->cost->toEqual(30); + }, ); }); @@ -54,8 +58,12 @@ it('can compose complex expectations', function () { ->attributes()->toBeArray() ->books()->toBeArray->each->not->toBeEmpty ->books()->sequence( - function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); }, - function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); }, + function ($book) { + $book->title->toEqual('Foo')->cost->toEqual(20); + }, + function ($book) { + $book->title->toEqual('Bar')->cost->toEqual(30); + }, ); }); diff --git a/tests/Features/Expect/HigherOrder/methodsAndProperties.php b/tests/Features/Expect/HigherOrder/methodsAndProperties.php index c9ef45aa..97e94187 100644 --- a/tests/Features/Expect/HigherOrder/methodsAndProperties.php +++ b/tests/Features/Expect/HigherOrder/methodsAndProperties.php @@ -9,8 +9,12 @@ it('can access methods and properties', function () { })->books()->toBeArray() ->posts->toBeArray->each->not->toBeEmpty ->books()->sequence( - function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); }, - function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); }, + function ($book) { + $book->title->toEqual('Foo')->cost->toEqual(20); + }, + function ($book) { + $book->title->toEqual('Bar')->cost->toEqual(30); + }, ); }); @@ -53,7 +57,9 @@ it('can start a new higher order expectation using the and syntax without nestin ->toBeInstanceOf(HasMethodsAndProperties::class) ->meta ->sequence( - function ($value, $key) { $value->toBeArray()->and($key)->toBe('foo'); }, + function ($value, $key) { + $value->toBeArray()->and($key)->toBe('foo'); + }, ); }); diff --git a/tests/Features/Expect/HigherOrder/properties.php b/tests/Features/Expect/HigherOrder/properties.php index 5595b9ab..7c15c6cc 100644 --- a/tests/Features/Expect/HigherOrder/properties.php +++ b/tests/Features/Expect/HigherOrder/properties.php @@ -35,8 +35,12 @@ it('works inside of each', function () { it('works with sequence', function () { expect(['books' => [['title' => 'Foo', 'cost' => 20], ['title' => 'Bar', 'cost' => 30]]]) ->books->sequence( - function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); }, - function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); }, + function ($book) { + $book->title->toEqual('Foo')->cost->toEqual(20); + }, + function ($book) { + $book->title->toEqual('Bar')->cost->toEqual(30); + }, ); }); @@ -51,10 +55,16 @@ it('can compose complex expectations', function () { it('works with objects', function () { expect(new HasProperties()) ->name->toEqual('foo')->not->toEqual('world') - ->posts->toHaveCount(2)->each(function ($post) { $post->is_published->toBeTrue(); }) + ->posts->toHaveCount(2)->each(function ($post) { + $post->is_published->toBeTrue(); + }) ->posts->sequence( - function ($post) { $post->title->toEqual('Foo'); }, - function ($post) { $post->title->toEqual('Bar'); }, + function ($post) { + $post->title->toEqual('Foo'); + }, + function ($post) { + $post->title->toEqual('Bar'); + }, ); }); diff --git a/tests/Features/Expect/matchExpectation.php b/tests/Features/Expect/matchExpectation.php index 2bea1be5..a5b1cba8 100644 --- a/tests/Features/Expect/matchExpectation.php +++ b/tests/Features/Expect/matchExpectation.php @@ -9,17 +9,17 @@ beforeEach(function () { it('pass', function () { expect('baz') ->match('foo', [ - 'bar' => function ($value) { - $this->matched = 'bar'; + 'bar' => function ($value) { + $this->matched = 'bar'; - return $value->toEqual('bar'); - }, - 'foo' => function ($value) { - $this->matched = 'baz'; + return $value->toEqual('bar'); + }, + 'foo' => function ($value) { + $this->matched = 'baz'; - return $value->toEqual('baz'); - }, - ] + return $value->toEqual('baz'); + }, + ] ) ->toEqual($this->matched); @@ -29,30 +29,30 @@ it('pass', function () { it('failures', function () { expect(true) ->match('foo', [ - 'bar' => function ($value) { - return $value->toBeTrue(); - }, - 'foo' => function ($value) { - return $value->toBeFalse(); - }, - ] + 'bar' => function ($value) { + return $value->toBeTrue(); + }, + 'foo' => function ($value) { + return $value->toBeFalse(); + }, + ] ); })->throws(ExpectationFailedException::class, 'true is false'); it('runs with truthy', function () { expect('foo') ->match(1, [ - 'bar' => function ($value) { - $this->matched = 'bar'; + 'bar' => function ($value) { + $this->matched = 'bar'; - return $value->toEqual('bar'); - }, - true => function ($value) { - $this->matched = 'foo'; + return $value->toEqual('bar'); + }, + true => function ($value) { + $this->matched = 'foo'; - return $value->toEqual('foo'); - }, - ] + return $value->toEqual('foo'); + }, + ] ) ->toEqual($this->matched); @@ -62,17 +62,17 @@ it('runs with truthy', function () { it('runs with falsy', function () { expect('foo') ->match(false, [ - 'bar' => function ($value) { - $this->matched = 'bar'; + 'bar' => function ($value) { + $this->matched = 'bar'; - return $value->toEqual('bar'); - }, - false => function ($value) { - $this->matched = 'foo'; + return $value->toEqual('bar'); + }, + false => function ($value) { + $this->matched = 'foo'; - return $value->toEqual('foo'); - }, - ] + return $value->toEqual('foo'); + }, + ] ) ->toEqual($this->matched); @@ -82,7 +82,9 @@ it('runs with falsy', function () { it('runs with truthy closure condition', function () { expect('foo') ->match( - function () { return '1'; }, [ + function () { + return '1'; + }, [ 'bar' => function ($value) { $this->matched = 'bar'; @@ -103,7 +105,9 @@ it('runs with truthy closure condition', function () { it('runs with falsy closure condition', function () { expect('foo') ->match( - function () { return '0'; }, [ + function () { + return '0'; + }, [ 'bar' => function ($value) { $this->matched = 'bar'; @@ -124,9 +128,9 @@ it('runs with falsy closure condition', function () { it('can be passed non-callable values', function () { expect('foo') ->match('pest', [ - 'bar' => 'foo', - 'pest' => 'baz', - ] + 'bar' => 'foo', + 'pest' => 'baz', + ] ); })->throws(ExpectationFailedException::class, 'two strings are equal'); @@ -137,7 +141,9 @@ it('fails with unhandled match', function () { it('can be used in higher order tests') ->expect(true) ->match( - function () { return true; }, [ + function () { + return true; + }, [ false => function ($value) { return $value->toBeFalse(); }, diff --git a/tests/Features/Expect/pipes.php b/tests/Features/Expect/pipes.php index 25723f68..90c00aa0 100644 --- a/tests/Features/Expect/pipes.php +++ b/tests/Features/Expect/pipes.php @@ -36,7 +36,8 @@ class Symbol class State { - public array $runCount = []; + public array $runCount = []; + public array $appliedCount = []; public function __construct() @@ -132,16 +133,16 @@ test('pipe is applied and can stop pipeline', function () use ($state) { expect($char)->toBe(new Char('A')) ->and($state) ->runCount->toMatchArray([ - 'char' => 1, - 'number' => 0, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 1, + 'number' => 0, + 'wildcard' => 0, + 'symbol' => 0, ]) ->appliedCount->toMatchArray([ - 'char' => 1, - 'number' => 0, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 1, + 'number' => 0, + 'wildcard' => 0, + 'symbol' => 0, ]); }); @@ -151,16 +152,16 @@ test('pipe is run and can let the pipeline keep going', function () use ($state) expect(3)->toBe(3) ->and($state) ->runCount->toMatchArray([ - 'char' => 1, - 'number' => 0, - 'wildcard' => 0, - 'symbol' => 1, + 'char' => 1, + 'number' => 0, + 'wildcard' => 0, + 'symbol' => 1, ]) ->appliedCount->toMatchArray([ - 'char' => 0, - 'number' => 0, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 0, + 'number' => 0, + 'wildcard' => 0, + 'symbol' => 0, ]); }); @@ -172,16 +173,16 @@ test('pipe works with negated expectation', function () use ($state) { expect($char)->not->toBe(new Char('B')) ->and($state) ->runCount->toMatchArray([ - 'char' => 1, - 'number' => 0, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 1, + 'number' => 0, + 'wildcard' => 0, + 'symbol' => 0, ]) ->appliedCount->toMatchArray([ - 'char' => 1, - 'number' => 0, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 1, + 'number' => 0, + 'wildcard' => 0, + 'symbol' => 0, ]); }); @@ -204,16 +205,16 @@ test('interceptor stops the pipeline', function () use ($state) { expect($number)->toBe(new Number(1)) ->and($state) ->runCount->toMatchArray([ - 'char' => 1, - 'number' => 1, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 1, + 'number' => 1, + 'wildcard' => 0, + 'symbol' => 0, ]) ->appliedCount->toMatchArray([ - 'char' => 0, - 'number' => 1, - 'wildcard' => 0, - 'symbol' => 0, + 'char' => 0, + 'number' => 1, + 'wildcard' => 0, + 'symbol' => 0, ]); }); diff --git a/tests/Features/Expect/sequence.php b/tests/Features/Expect/sequence.php index 4b877a5e..e145f743 100644 --- a/tests/Features/Expect/sequence.php +++ b/tests/Features/Expect/sequence.php @@ -9,9 +9,15 @@ test('an exception is thrown if the the type is not iterable', function () { test('allows for sequences of checks to be run on iterable data', function () { expect([1, 2, 3]) ->sequence( - function ($expectation) { $expectation->toBeInt()->toEqual(1); }, - function ($expectation) { $expectation->toBeInt()->toEqual(2); }, - function ($expectation) { $expectation->toBeInt()->toEqual(3); }, + function ($expectation) { + $expectation->toBeInt()->toEqual(1); + }, + function ($expectation) { + $expectation->toBeInt()->toEqual(2); + }, + function ($expectation) { + $expectation->toBeInt()->toEqual(3); + }, ); expect(static::getCount())->toBe(6); @@ -20,9 +26,15 @@ test('allows for sequences of checks to be run on iterable data', function () { test('loops back to the start if it runs out of sequence items', function () { expect([1, 2, 3, 1, 2, 3, 1, 2]) ->sequence( - function ($expectation) { $expectation->toBeInt()->toEqual(1); }, - function ($expectation) { $expectation->toBeInt()->toEqual(2); }, - function ($expectation) { $expectation->toBeInt()->toEqual(3); }, + function ($expectation) { + $expectation->toBeInt()->toEqual(1); + }, + function ($expectation) { + $expectation->toBeInt()->toEqual(2); + }, + function ($expectation) { + $expectation->toBeInt()->toEqual(3); + }, ); expect(static::getCount())->toBe(16); @@ -31,9 +43,15 @@ test('loops back to the start if it runs out of sequence items', function () { test('fails if the number of iterable items is greater than the number of expectations', function () { expect([1, 2]) ->sequence( - function ($expectation) { $expectation->toBeInt()->toEqual(1); }, - function ($expectation) { $expectation->toBeInt()->toEqual(2); }, - function ($expectation) { $expectation->toBeInt()->toEqual(3); }, + function ($expectation) { + $expectation->toBeInt()->toEqual(1); + }, + function ($expectation) { + $expectation->toBeInt()->toEqual(2); + }, + function ($expectation) { + $expectation->toBeInt()->toEqual(3); + }, ); })->throws(ExpectationFailedException::class); @@ -60,7 +78,9 @@ test('it can be passed non-callable values', function () { test('it can be passed a mixture of value types', function () { expect(['foo', 'bar', 'baz'])->sequence( 'foo', - function ($expectation) { $expectation->toEqual('bar')->toBeString(); }, + function ($expectation) { + $expectation->toEqual('bar')->toBeString(); + }, 'baz' ); diff --git a/tests/Features/Expect/toBeCallable.php b/tests/Features/Expect/toBeCallable.php index 2bd8f139..166c0019 100644 --- a/tests/Features/Expect/toBeCallable.php +++ b/tests/Features/Expect/toBeCallable.php @@ -3,7 +3,8 @@ use PHPUnit\Framework\ExpectationFailedException; test('pass', function () { - expect(function () {})->toBeCallable(); + expect(function () { + })->toBeCallable(); expect(null)->not->toBeCallable(); }); @@ -14,5 +15,7 @@ test('failures', function () { })->throws(ExpectationFailedException::class); test('not failures', function () { - expect(function () { return 42; })->not->toBeCallable(); + expect(function () { + return 42; + })->not->toBeCallable(); })->throws(ExpectationFailedException::class); diff --git a/tests/Features/Expect/toHaveKey.php b/tests/Features/Expect/toHaveKey.php index 01383c91..0d8345d6 100644 --- a/tests/Features/Expect/toHaveKey.php +++ b/tests/Features/Expect/toHaveKey.php @@ -3,10 +3,10 @@ use PHPUnit\Framework\ExpectationFailedException; $test_array = [ - 'a' => 1, + 'a' => 1, 'b', - 'c' => 'world', - 'd' => [ + 'c' => 'world', + 'd' => [ 'e' => 'hello', ], 'key.with.dots' => false, diff --git a/tests/Features/Expect/toMatchObject.php b/tests/Features/Expect/toMatchObject.php index a4394e4c..18d0e668 100644 --- a/tests/Features/Expect/toMatchObject.php +++ b/tests/Features/Expect/toMatchObject.php @@ -19,7 +19,8 @@ test('pass', function () { test('pass with class', function () { expect(new class() { - public $name = 'Nuno'; + public $name = 'Nuno'; + public $email = 'enunomaduro@gmail.com'; })->toMatchObject([ 'name' => 'Nuno', diff --git a/tests/Features/Expect/toThrow.php b/tests/Features/Expect/toThrow.php index b5c66abf..6f8b72d3 100644 --- a/tests/Features/Expect/toThrow.php +++ b/tests/Features/Expect/toThrow.php @@ -3,60 +3,98 @@ use PHPUnit\Framework\ExpectationFailedException; test('passes', function () { - expect(function () { throw new RuntimeException(); })->toThrow(RuntimeException::class); - expect(function () { throw new RuntimeException(); })->toThrow(Exception::class); - expect(function () { throw new RuntimeException(); })->toThrow(function (RuntimeException $e) {}); - expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (Exception $e) { + expect(function () { + throw new RuntimeException(); + })->toThrow(RuntimeException::class); + expect(function () { + throw new RuntimeException(); + })->toThrow(Exception::class); + expect(function () { + throw new RuntimeException(); + })->toThrow(function (RuntimeException $e) { + }); + expect(function () { + throw new RuntimeException('actual message'); + })->toThrow(function (Exception $e) { expect($e->getMessage())->toBe('actual message'); }); - expect(function () {})->not->toThrow(Exception::class); - expect(function () { throw new RuntimeException('actual message'); })->toThrow('actual message'); - expect(function () { throw new Exception(); })->not->toThrow(RuntimeException::class); - expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'actual message'); - expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (RuntimeException $e) {}, 'actual message'); + expect(function () { + })->not->toThrow(Exception::class); + expect(function () { + throw new RuntimeException('actual message'); + })->toThrow('actual message'); + expect(function () { + throw new Exception(); + })->not->toThrow(RuntimeException::class); + expect(function () { + throw new RuntimeException('actual message'); + })->toThrow(RuntimeException::class, 'actual message'); + expect(function () { + throw new RuntimeException('actual message'); + })->toThrow(function (RuntimeException $e) { + }, 'actual message'); }); test('failures 1', function () { - expect(function () {})->toThrow(RuntimeException::class); + expect(function () { + })->toThrow(RuntimeException::class); })->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.'); test('failures 2', function () { - expect(function () {})->toThrow(function (RuntimeException $e) {}); + expect(function () { + })->toThrow(function (RuntimeException $e) { + }); })->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.'); test('failures 3', function () { - expect(function () { throw new Exception(); })->toThrow(function (RuntimeException $e) {}); + expect(function () { + throw new Exception(); + })->toThrow(function (RuntimeException $e) { + }); })->throws(ExpectationFailedException::class, 'Failed asserting that Exception Object'); test('failures 4', function () { - expect(function () { throw new Exception('actual message'); }) + expect(function () { + throw new Exception('actual message'); + }) ->toThrow(function (Exception $e) { expect($e->getMessage())->toBe('expected message'); }); })->throws(ExpectationFailedException::class, 'Failed asserting that two strings are identical'); test('failures 5', function () { - expect(function () { throw new Exception('actual message'); })->toThrow('expected message'); + expect(function () { + throw new Exception('actual message'); + })->toThrow('expected message'); })->throws(ExpectationFailedException::class, 'Failed asserting that \'actual message\' contains "expected message".'); test('failures 6', function () { - expect(function () {})->toThrow('actual message'); + expect(function () { + })->toThrow('actual message'); })->throws(ExpectationFailedException::class, 'Exception with message "actual message" not thrown'); test('failures 7', function () { - expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'expected message'); + expect(function () { + throw new RuntimeException('actual message'); + })->toThrow(RuntimeException::class, 'expected message'); })->throws(ExpectationFailedException::class); test('not failures', function () { - expect(function () { throw new RuntimeException(); })->not->toThrow(RuntimeException::class); + expect(function () { + throw new RuntimeException(); + })->not->toThrow(RuntimeException::class); })->throws(ExpectationFailedException::class); test('closure missing parameter', function () { - expect(function () {})->toThrow(function () {}); + expect(function () { + })->toThrow(function () { + }); })->throws(InvalidArgumentException::class, 'The given closure must have a single parameter type-hinted as the class string.'); test('closure missing type-hint', function () { - expect(function () {})->toThrow(function ($e) {}); + expect(function () { + })->toThrow(function ($e) { + }); })->throws(InvalidArgumentException::class, 'The given closure\'s parameter must be type-hinted as the class string.'); it('can handle a non-defined exception', function () { diff --git a/tests/Features/Expect/unless.php b/tests/Features/Expect/unless.php index 0dabe2bb..92c56d2e 100644 --- a/tests/Features/Expect/unless.php +++ b/tests/Features/Expect/unless.php @@ -67,7 +67,9 @@ it('skips with falsy', function () { it('runs with truthy closure condition', function () { expect($this->unlessObject) ->unless( - function () { return '0'; }, + function () { + return '0'; + }, function ($value) { return $value->trueValue->toBeTrue(); } @@ -80,7 +82,9 @@ it('runs with truthy closure condition', function () { it('skips with falsy closure condition', function () { expect($this->unlessObject) ->unless( - function () { return '1'; }, + function () { + return '1'; + }, function ($value) { return $value->trueValue->toBeFalse(); // fails } @@ -93,7 +97,9 @@ it('skips with falsy closure condition', function () { it('can be used in higher order tests') ->expect(true) ->unless( - function () { return false; }, + function () { + return false; + }, function ($value) { return $value->toBeFalse(); } diff --git a/tests/Features/Expect/when.php b/tests/Features/Expect/when.php index 3b62e2f8..5e327b88 100644 --- a/tests/Features/Expect/when.php +++ b/tests/Features/Expect/when.php @@ -67,7 +67,9 @@ it('skips with falsy', function () { it('runs with truthy closure condition', function () { expect($this->whenObject) ->when( - function () { return '1'; }, + function () { + return '1'; + }, function ($value) { return $value->trueValue->toBeTrue(); } @@ -80,7 +82,9 @@ it('runs with truthy closure condition', function () { it('skips with falsy closure condition', function () { expect($this->whenObject) ->when( - function () { return '0'; }, + function () { + return '0'; + }, function ($value) { return $value->trueValue->toBeFalse(); // fails } @@ -93,7 +97,9 @@ it('skips with falsy closure condition', function () { it('can be used in higher order tests') ->expect(false) ->when( - function () { return true; }, + function () { + return true; + }, function ($value) { return $value->toBeTrue(); } diff --git a/tests/Features/HigherOrderTests.php b/tests/Features/HigherOrderTests.php index 7aafbaa5..a1bef96d 100644 --- a/tests/Features/HigherOrderTests.php +++ b/tests/Features/HigherOrderTests.php @@ -11,7 +11,9 @@ it('is capable doing multiple assertions') ->assertFalse(false); it('resolves expect callables correctly') - ->expect(function () { return 'foo'; }) + ->expect(function () { + return 'foo'; + }) ->toBeString() ->toBe('foo') ->and('bar') @@ -23,24 +25,38 @@ test('does not treat method names as callables') it('can defer a method until after test setup') ->expect('foo')->toBeString() - ->defer(function () { expect($this)->toBeInstanceOf(TestCase::class); }) + ->defer(function () { + expect($this)->toBeInstanceOf(TestCase::class); + }) ->toBe('foo') ->and('hello world')->toBeString(); it('can pass datasets into the expect callables') ->with([[1, 2, 3]]) - ->expect(function (...$numbers) { return $numbers; })->toBe([1, 2, 3]) - ->and(function (...$numbers) { return $numbers; })->toBe([1, 2, 3]); + ->expect(function (...$numbers) { + return $numbers; + })->toBe([1, 2, 3]) + ->and(function (...$numbers) { + return $numbers; + })->toBe([1, 2, 3]); it('can pass datasets into the defer callable') ->with([[1, 2, 3]]) - ->defer(function (...$numbers) { expect($numbers)->toBe([1, 2, 3]); }); + ->defer(function (...$numbers) { + expect($numbers)->toBe([1, 2, 3]); + }); it('can pass shared datasets into callables') ->with('numbers.closure.wrapped') - ->expect(function ($value) { return $value; }) - ->and(function ($value) { return $value; }) - ->defer(function ($value) { expect($value)->toBeInt(); }) + ->expect(function ($value) { + return $value; + }) + ->and(function ($value) { + return $value; + }) + ->defer(function ($value) { + expect($value)->toBeInt(); + }) ->toBeInt(); afterEach()->assertTrue(true); diff --git a/tests/Features/Skip.php b/tests/Features/Skip.php index f5d867ca..5ac559f9 100644 --- a/tests/Features/Skip.php +++ b/tests/Features/Skip.php @@ -21,11 +21,15 @@ it('skips with message') ->assertTrue(false); it('skips with truthy closure condition') - ->skip(function () { return '1'; }) + ->skip(function () { + return '1'; + }) ->assertTrue(false); it('do not skips with falsy closure condition') - ->skip(function () { return false; }) + ->skip(function () { + return false; + }) ->assertTrue(true); it('skips with condition and message') @@ -37,10 +41,16 @@ it('skips when skip after assertion') ->skip(); it('can use something in the test case as a condition') - ->skip(function () { return $this->shouldSkip; }, 'This test was skipped') + ->skip(function () { + return $this->shouldSkip; + }, 'This test was skipped') ->assertTrue(false); it('can user higher order callables and skip') - ->skip(function () { return $this->shouldSkip; }) - ->expect(function () { return $this->shouldSkip; }) + ->skip(function () { + return $this->shouldSkip; + }) + ->expect(function () { + return $this->shouldSkip; + }) ->toBeFalse(); diff --git a/tests/Unit/Datasets.php b/tests/Unit/Datasets.php index a93ac62e..3f1224d6 100644 --- a/tests/Unit/Datasets.php +++ b/tests/Unit/Datasets.php @@ -10,8 +10,8 @@ it('show only the names of named datasets in their description', function () { ], ])); - expect($descriptions[0])->toBe('test description with data set "one"'); - expect($descriptions[1])->toBe('test description with data set "two"'); + expect($descriptions[0])->toBe('data set "one"') + ->and($descriptions[1])->toBe('data set "two"'); }); it('show the actual dataset of non-named datasets in their description', function () { @@ -22,8 +22,8 @@ it('show the actual dataset of non-named datasets in their description', functio ], ])); - expect($descriptions[0])->toBe('test description with (1)'); - expect($descriptions[1])->toBe('test description with (array(2))'); + expect($descriptions[0])->toBe('(1)'); + expect($descriptions[1])->toBe('(array(2))'); }); it('show only the names of multiple named datasets in their description', function () { @@ -38,10 +38,10 @@ it('show only the names of multiple named datasets in their description', functi ], ])); - expect($descriptions[0])->toBe('test description with data set "one" / data set "three"'); - expect($descriptions[1])->toBe('test description with data set "one" / data set "four"'); - expect($descriptions[2])->toBe('test description with data set "two" / data set "three"'); - expect($descriptions[3])->toBe('test description with data set "two" / data set "four"'); + expect($descriptions[0])->toBe('data set "one" / data set "three"'); + expect($descriptions[1])->toBe('data set "one" / data set "four"'); + expect($descriptions[2])->toBe('data set "two" / data set "three"'); + expect($descriptions[3])->toBe('data set "two" / data set "four"'); }); it('show the actual dataset of multiple non-named datasets in their description', function () { @@ -56,10 +56,10 @@ it('show the actual dataset of multiple non-named datasets in their description' ], ])); - expect($descriptions[0])->toBe('test description with (1) / (3)'); - expect($descriptions[1])->toBe('test description with (1) / (array(4))'); - expect($descriptions[2])->toBe('test description with (array(2)) / (3)'); - expect($descriptions[3])->toBe('test description with (array(2)) / (array(4))'); + expect($descriptions[0])->toBe('(1) / (3)'); + expect($descriptions[1])->toBe('(1) / (array(4))'); + expect($descriptions[2])->toBe('(array(2)) / (3)'); + expect($descriptions[3])->toBe('(array(2)) / (array(4))'); }); it('show the correct description for mixed named and not-named datasets', function () { @@ -74,8 +74,8 @@ it('show the correct description for mixed named and not-named datasets', functi ], ])); - expect($descriptions[0])->toBe('test description with data set "one" / (3)'); - expect($descriptions[1])->toBe('test description with data set "one" / data set "four"'); - expect($descriptions[2])->toBe('test description with (array(2)) / (3)'); - expect($descriptions[3])->toBe('test description with (array(2)) / data set "four"'); + expect($descriptions[0])->toBe('data set "one" / (3)'); + expect($descriptions[1])->toBe('data set "one" / data set "four"'); + expect($descriptions[2])->toBe('(array(2)) / (3)'); + expect($descriptions[3])->toBe('(array(2)) / data set "four"'); }); diff --git a/tests/Unit/TestSuite.php b/tests/Unit/TestSuite.php index 807594be..ca7afa83 100644 --- a/tests/Unit/TestSuite.php +++ b/tests/Unit/TestSuite.php @@ -20,7 +20,8 @@ it('does not allow to add the same test description twice', function () { it('alerts users about tests with arguments but no input', function () { $testSuite = new TestSuite(getcwd(), 'tests'); - $method = new TestCaseMethodFactory('foo', 'bar', function (int $arg) {}); + $method = new TestCaseMethodFactory('foo', 'bar', function (int $arg) { + }); $testSuite->tests->set($method); })->throws( @@ -29,7 +30,7 @@ it('alerts users about tests with arguments but no input', function () { ); it('can return an array of all test suite filenames', function () { - $testSuite = TestSuite::getInstance(getcwd(), 'tests'); + $testSuite = new TestSuite(getcwd(), 'tests'); $testSuite->tests->set(new TestCaseMethodFactory('a', 'b', null)); $testSuite->tests->set(new TestCaseMethodFactory('c', 'd', null)); @@ -57,9 +58,10 @@ it('can filter the test suite filenames to those with the only method', function it('does not filter the test suite filenames to those with the only method when working in CI pipeline', function () { $previousEnvironment = Environment::name(); Environment::name(Environment::CI); - $testSuite = TestSuite::getInstance(getcwd(), 'tests'); + $testSuite = new TestSuite(getcwd(), 'tests'); - $test = function () {}; + $test = function () { + }; $testWithOnly = new TestCaseMethodFactory('a', 'b', null); $testWithOnly->only = true; diff --git a/tests/Visual/Help.php b/tests/Visual/Help.php index d4d78165..348c6852 100644 --- a/tests/Visual/Help.php +++ b/tests/Visual/Help.php @@ -24,4 +24,4 @@ test('visual snapshot of help command output', function () { }; expect($output())->toContain(file_get_contents($snapshot)); -})->skip(PHP_OS_FAMILY === 'Windows'); +})->skip(PHP_OS_FAMILY === 'Windows')->skip('Not supported yet.'); diff --git a/tests/Visual/JUnit.php b/tests/Visual/JUnit.php index a12d561f..27c6c68d 100644 --- a/tests/Visual/JUnit.php +++ b/tests/Visual/JUnit.php @@ -22,7 +22,7 @@ it('is can successfully call all public methods', function () { $junit->endTest($this, 0); $junit->endTestSuite(new TestSuite()); $this->expectNotToPerformAssertions(); -}); +})->skip('Not supported yet.'); afterEach(function () { unlink(__DIR__ . '/junit.html'); diff --git a/tests/Visual/Success.php b/tests/Visual/Success.php index d5939730..a573aaf6 100644 --- a/tests/Visual/Success.php +++ b/tests/Visual/Success.php @@ -9,7 +9,11 @@ test('visual snapshot of test suite on success', function () { ]); $output = function () use ($testsPath) { - $process = (new Symfony\Component\Process\Process(['php', 'bin/pest'], dirname($testsPath), ['EXCLUDE' => 'integration', 'REBUILD_SNAPSHOTS' => false, 'PARATEST' => 0])); + $process = (new Symfony\Component\Process\Process( + ['php', 'bin/pest'], + dirname($testsPath), + ['EXCLUDE' => 'integration', 'REBUILD_SNAPSHOTS' => false, 'PARATEST' => 0, 'COLLISION_PRINTER' => 'DefaultPrinter'], + )); $process->run(); diff --git a/tests/Visual/TeamCity.php b/tests/Visual/TeamCity.php index 1681f1ac..378b81b9 100644 --- a/tests/Visual/TeamCity.php +++ b/tests/Visual/TeamCity.php @@ -25,7 +25,7 @@ it('is can successfully call all public methods', function () { $teamCity->endTest($this, 0); $teamCity->printResult(new TestResult()); $teamCity->endTestSuite(new TestSuite()); -}); +})->skip('Not supported yet.'); afterEach(function () { unlink(__DIR__ . '/output.txt');