mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
feat: basic PHPUnit 10 support
This commit is contained in:
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
php: ['7.3', '7.4', '8.0', '8.1']
|
||||
php: ['8.0', '8.1']
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
parallel: ['', '--parallel']
|
||||
exclude:
|
||||
|
||||
4
TODO.md
Normal file
4
TODO.md
Normal file
@ -0,0 +1,4 @@
|
||||
1. Support for `--help` pest options.
|
||||
2. Support for `default` printer.
|
||||
3. Support for `TeamCity` printer.
|
||||
4. Support for `JUnit` log.
|
||||
17
bin/pest
17
bin/pest
@ -4,6 +4,7 @@
|
||||
use NunoMaduro\Collision\Provider;
|
||||
use Pest\Actions\ValidatesEnvironment;
|
||||
use Pest\Support\Container;
|
||||
use Pest\Console\Kernel;
|
||||
use Pest\TestSuite;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
@ -25,8 +26,6 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
$autoloadPath = $localPath;
|
||||
}
|
||||
|
||||
(new Provider())->register();
|
||||
|
||||
// Get $rootPath based on $autoloadPath
|
||||
$rootPath = dirname($autoloadPath, 2);
|
||||
$argv = new ArgvInput();
|
||||
@ -40,8 +39,6 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
$container->add(TestSuite::class, $testSuite);
|
||||
$container->add(OutputInterface::class, $output);
|
||||
|
||||
ValidatesEnvironment::in($testSuite);
|
||||
|
||||
$args = $_SERVER['argv'];
|
||||
|
||||
// Let's remove any arguments that PHPUnit does not understand
|
||||
@ -53,11 +50,11 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
}
|
||||
}
|
||||
|
||||
if (($runInParallel = $argv->hasParameterOption(['--parallel', '-p'])) && !class_exists(\Pest\Parallel\Command::class)) {
|
||||
$output->writeln("Parallel support requires the Pest Parallel plugin. Run <fg=yellow;options=bold>`composer require --dev pestphp/pest-plugin-parallel`</> first.");
|
||||
exit(Command::FAILURE);
|
||||
}
|
||||
$kernel = Kernel::boot();
|
||||
|
||||
$command = $runInParallel ? \Pest\Parallel\Command::class : \Pest\Console\Command::class;
|
||||
exit($container->get($command)->run($args));
|
||||
$result = $kernel->handle($args);
|
||||
|
||||
$kernel->shutdown();
|
||||
|
||||
exit($result);
|
||||
})();
|
||||
|
||||
@ -17,16 +17,21 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"nunomaduro/collision": "^5.4.0|^6.0",
|
||||
"php": "^8.0",
|
||||
"nunomaduro/collision": "^5.10.0|^6.0",
|
||||
"pestphp/pest-plugin": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5.5"
|
||||
"phpunit/phpunit": "10.0.x-dev"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pest\\": "src/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"../phpunit/src/Runner/TestSuiteLoader.php",
|
||||
"vendor/phpunit/phpunit/src/Runner/TestSuiteLoader.php"
|
||||
],
|
||||
"files": [
|
||||
"overrides/Runner/TestSuiteLoader.php",
|
||||
"src/Functions.php",
|
||||
"src/Pest.php"
|
||||
]
|
||||
@ -44,7 +49,7 @@
|
||||
"illuminate/support": "^8.47.0",
|
||||
"laravel/dusk": "^6.15.0",
|
||||
"pestphp/pest-dev-tools": "dev-master",
|
||||
"pestphp/pest-plugin-parallel": "^1.0"
|
||||
"pestphp/pest-plugin-mock": "^1.0"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
|
||||
127
overrides/Runner/TestSuiteLoader.php
Normal file
127
overrides/Runner/TestSuiteLoader.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPUnit\Runner;
|
||||
|
||||
use function array_diff;
|
||||
use function array_values;
|
||||
use function basename;
|
||||
use function class_exists;
|
||||
use function get_declared_classes;
|
||||
use function stripos;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2001-2021, Sebastian Bergmann <sebastian@phpunit.de>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Sebastian Bergmann nor the names of his
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
final class TestSuiteLoader
|
||||
{
|
||||
/**
|
||||
* Loads the test suite.
|
||||
*/
|
||||
public function load(string $suiteClassFile): ReflectionClass
|
||||
{
|
||||
$suiteClassName = basename($suiteClassFile, '.php');
|
||||
$loadedClasses = get_declared_classes();
|
||||
|
||||
if (!class_exists($suiteClassName, false)) {
|
||||
(static function () use ($suiteClassFile) {
|
||||
include_once $suiteClassFile;
|
||||
})();
|
||||
|
||||
$loadedClasses = array_values(
|
||||
array_diff(get_declared_classes(), $loadedClasses)
|
||||
);
|
||||
|
||||
if (empty($loadedClasses)) {
|
||||
return new ReflectionClass(WarningTestCase::class);
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists($suiteClassName, false)) {
|
||||
$offset = 0 - strlen($suiteClassName);
|
||||
|
||||
foreach ($loadedClasses as $loadedClass) {
|
||||
|
||||
if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0) {
|
||||
$suiteClassName = $loadedClass;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists($suiteClassName, false)) {
|
||||
return new ReflectionClass(WarningTestCase::class);
|
||||
}
|
||||
|
||||
try {
|
||||
$class = new ReflectionClass($suiteClassName);
|
||||
} catch (ReflectionException $e) {
|
||||
throw new Exception(
|
||||
$e->getMessage(),
|
||||
(int) $e->getCode(),
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
if ($class->isSubclassOf(TestCase::class) && !$class->isAbstract()) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
if ($class->hasMethod('suite')) {
|
||||
try {
|
||||
$method = $class->getMethod('suite');
|
||||
} catch (ReflectionException $e) {
|
||||
throw new Exception(
|
||||
$e->getMessage(),
|
||||
(int) $e->getCode(),
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
return new ReflectionClass(WarningTestCase::class);
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
|
||||
use Pest\Logging\JUnit;
|
||||
use Pest\Logging\TeamCity;
|
||||
use PHPUnit\TextUI\DefaultResultPrinter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsDefaults
|
||||
{
|
||||
private const PRINTER = 'printer';
|
||||
|
||||
/**
|
||||
* Adds default arguments to the given `arguments` array.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function to(array $arguments): array
|
||||
{
|
||||
if (!array_key_exists(self::PRINTER, $arguments)) {
|
||||
$arguments[self::PRINTER] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) {
|
||||
$arguments[self::PRINTER] = new TeamCity(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
// Load our junit logger instead.
|
||||
if (array_key_exists('junitLogfile', $arguments)) {
|
||||
$arguments['listeners'][] = new JUnit(
|
||||
$arguments['junitLogfile']
|
||||
);
|
||||
unset($arguments['junitLogfile']);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsTests
|
||||
{
|
||||
/**
|
||||
* Adds tests to the given test suite.
|
||||
*
|
||||
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
|
||||
*/
|
||||
public static function to(TestSuite $testSuite, \Pest\TestSuite $pestTestSuite): void
|
||||
{
|
||||
self::removeTestClosureWarnings($testSuite);
|
||||
|
||||
$testSuites = [];
|
||||
$pestTestSuite->tests->build($pestTestSuite, function (TestCase $testCase) use (&$testSuites): void {
|
||||
$testCaseClass = get_class($testCase);
|
||||
if (!array_key_exists($testCaseClass, $testSuites)) {
|
||||
$testSuites[$testCaseClass] = [];
|
||||
}
|
||||
|
||||
$testSuites[$testCaseClass][] = $testCase;
|
||||
});
|
||||
|
||||
foreach ($testSuites as $testCaseName => $testCases) {
|
||||
$testTestSuite = new TestSuite($testCaseName);
|
||||
$testTestSuite->setTests([]);
|
||||
foreach ($testCases as $testCase) {
|
||||
$testTestSuite->addTest($testCase, $testCase->getGroups());
|
||||
}
|
||||
$testSuite->addTestSuite($testTestSuite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
|
||||
*/
|
||||
private static function removeTestClosureWarnings(TestSuite $testSuite): void
|
||||
{
|
||||
$tests = $testSuite->tests();
|
||||
|
||||
foreach ($tests as $key => $test) {
|
||||
if ($test instanceof TestSuite) {
|
||||
self::removeTestClosureWarnings($test);
|
||||
}
|
||||
|
||||
if ($test instanceof WarningTestCase) {
|
||||
unset($tests[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$testSuite->setTests($tests);
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use PHPUnit\TextUI\XmlConfiguration\Loader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ValidatesConfiguration
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const CONFIGURATION_KEY = 'configuration';
|
||||
|
||||
/**
|
||||
* Validates the configuration in the given `configuration`.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public static function in($arguments): void
|
||||
{
|
||||
if (!array_key_exists(self::CONFIGURATION_KEY, $arguments) || !file_exists($arguments[self::CONFIGURATION_KEY])) {
|
||||
throw new FileOrFolderNotFound('phpunit.xml');
|
||||
}
|
||||
|
||||
$configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit();
|
||||
|
||||
if ($configuration->processIsolation()) {
|
||||
throw new AttributeNotSupportedYet('processIsolation', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use Pest\TestSuite;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ValidatesEnvironment
|
||||
{
|
||||
/**
|
||||
* The need files on the root path.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const NEEDED_FILES = [
|
||||
'composer.json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the environment.
|
||||
*/
|
||||
public static function in(TestSuite $testSuite): void
|
||||
{
|
||||
$rootPath = $testSuite->rootPath;
|
||||
|
||||
$exists = function ($neededFile) use ($rootPath): bool {
|
||||
return file_exists(sprintf('%s%s%s', $rootPath, DIRECTORY_SEPARATOR, $neededFile));
|
||||
};
|
||||
|
||||
foreach (self::NEEDED_FILES as $neededFile) {
|
||||
if (!$exists($neededFile)) {
|
||||
throw new FileOrFolderNotFound($neededFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/Bootstrappers/BootEmitter.php
Normal file
29
src/Bootstrappers/BootEmitter.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Emitters\DispatchingEmitter;
|
||||
use PHPUnit\Event;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootEmitter
|
||||
{
|
||||
/**
|
||||
* Boots the Event Emitter.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
if (!($baseEmitter = Event\Facade::emitter()) instanceof DispatchingEmitter) {
|
||||
$reflectedClass = new ReflectionClass(Event\Facade::class);
|
||||
|
||||
$reflectedClass->setStaticPropertyValue('emitter', new DispatchingEmitter(
|
||||
$baseEmitter,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Bootstrappers/BootExceptionHandler.php
Normal file
21
src/Bootstrappers/BootExceptionHandler.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use NunoMaduro\Collision;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootExceptionHandler
|
||||
{
|
||||
/**
|
||||
* Boots the Exception Handler.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
(new Collision\Provider())->register();
|
||||
}
|
||||
}
|
||||
@ -2,18 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Support\Str;
|
||||
use function Pest\testDirectory;
|
||||
use PHPUnit\Util\FileLoader;
|
||||
use Pest\TestSuite;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class LoadStructure
|
||||
final class BootFiles
|
||||
{
|
||||
/**
|
||||
* The Pest convention.
|
||||
@ -21,23 +21,23 @@ final class LoadStructure
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const STRUCTURE = [
|
||||
'Expectations.php',
|
||||
'Datasets',
|
||||
'Datasets.php',
|
||||
'Expectations',
|
||||
'Expectations.php',
|
||||
'Helpers',
|
||||
'Helpers.php',
|
||||
'Pest.php',
|
||||
'Datasets',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the configuration in the given `configuration`.
|
||||
* Boots the Subscribers.
|
||||
*/
|
||||
public static function in(string $rootPath): void
|
||||
public function __invoke(): void
|
||||
{
|
||||
$testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
|
||||
$rootPath = TestSuite::getInstance()->rootPath;
|
||||
|
||||
$load = function ($filename): bool {
|
||||
return file_exists($filename) && (bool) FileLoader::checkAndLoad($filename);
|
||||
};
|
||||
$testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
|
||||
|
||||
foreach (self::STRUCTURE as $filename) {
|
||||
$filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename);
|
||||
@ -50,14 +50,21 @@ final class LoadStructure
|
||||
$directory = new RecursiveDirectoryIterator($filename);
|
||||
$iterator = new RecursiveIteratorIterator($directory);
|
||||
foreach ($iterator as $file) {
|
||||
$filename = $file->__toString();
|
||||
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
|
||||
require_once $filename;
|
||||
}
|
||||
$this->load($file->__toString());
|
||||
}
|
||||
} else {
|
||||
$load($filename);
|
||||
$this->load($filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given filename, if possible.
|
||||
*/
|
||||
private function load(string $filename): void
|
||||
{
|
||||
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
|
||||
include_once $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Bootstrappers/BootSubscribers.php
Normal file
37
src/Bootstrappers/BootSubscribers.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Subscribers;
|
||||
use PHPUnit\Event;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootSubscribers
|
||||
{
|
||||
/**
|
||||
* The Kernel subscribers.
|
||||
*
|
||||
* @var array<int, class-string>
|
||||
*/
|
||||
private static array $subscribers = [
|
||||
Subscribers\EnsureTestsAreLoaded::class,
|
||||
Subscribers\EnsureConfigurationIsValid::class,
|
||||
Subscribers\EnsureConfigurationDefaults::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Boots the Subscribers.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
foreach (self::$subscribers as $subscriber) {
|
||||
Event\Facade::registerSubscriber(
|
||||
new $subscriber()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\ChainableClosure;
|
||||
use Pest\Support\ExceptionTrace;
|
||||
use Pest\TestSuite;
|
||||
@ -12,8 +13,7 @@ use PHPUnit\Framework\ExecutionOrderDependency;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* To avoid inheritance conflicts, all the fields related
|
||||
* to Pest only will be prefixed by double underscore.
|
||||
* To avoid inheritance conflicts, all the fields related to Pest only will be prefixed by double underscore.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
@ -40,7 +40,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private $beforeEach = null;
|
||||
private $__beforeEach = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterEach ("tear down") closure if one has been
|
||||
@ -48,7 +48,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private $afterEach = null;
|
||||
private $__afterEach = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared beforeAll ("set up before") closure if one has been
|
||||
@ -56,7 +56,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private static $beforeAll = null;
|
||||
private static $__beforeAll = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterAll ("tear down after") closure if one has
|
||||
@ -64,7 +64,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private static $afterAll = null;
|
||||
private static $__afterAll = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the test case.
|
||||
@ -73,10 +73,12 @@ trait Testable
|
||||
{
|
||||
$this->__test = $test;
|
||||
$this->__description = $description;
|
||||
self::$beforeAll = null;
|
||||
self::$afterAll = null;
|
||||
self::$__beforeAll = null;
|
||||
self::$__afterAll = null;
|
||||
|
||||
parent::__construct('__test', $data);
|
||||
parent::__construct('__test');
|
||||
|
||||
$this->setData($description, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +86,7 @@ trait Testable
|
||||
*/
|
||||
public function addGroups(array $groups): void
|
||||
{
|
||||
$groups = array_unique(array_merge($this->getGroups(), $groups));
|
||||
$groups = array_unique(array_merge($this->groups(), $groups));
|
||||
|
||||
$this->setGroups($groups);
|
||||
}
|
||||
@ -101,7 +103,7 @@ trait Testable
|
||||
$test = "{$className}::{$test}";
|
||||
}
|
||||
|
||||
return new ExecutionOrderDependency($test, null, '');
|
||||
return new ExecutionOrderDependency($test, '__test');
|
||||
}, $tests);
|
||||
|
||||
$this->setDependencies($tests);
|
||||
@ -111,14 +113,14 @@ trait Testable
|
||||
* Add a shared/"global" before all test hook that will execute **before**
|
||||
* the test defined `beforeAll` hook(s).
|
||||
*/
|
||||
public function addBeforeAll(?Closure $hook): void
|
||||
public function __addBeforeAll(?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$beforeAll = (self::$beforeAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$beforeAll, $hook)
|
||||
self::$__beforeAll = (self::$__beforeAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$__beforeAll, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
@ -126,14 +128,14 @@ trait Testable
|
||||
* Add a shared/"global" after all test hook that will execute **before**
|
||||
* the test defined `afterAll` hook(s).
|
||||
*/
|
||||
public function addAfterAll(?Closure $hook): void
|
||||
public function __addAfterAll(?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$afterAll = (self::$afterAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$afterAll, $hook)
|
||||
self::$__afterAll = (self::$__afterAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$__afterAll, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
@ -141,24 +143,24 @@ trait Testable
|
||||
* Add a shared/"global" before each test hook that will execute **before**
|
||||
* the test defined `beforeEach` hook.
|
||||
*/
|
||||
public function addBeforeEach(?Closure $hook): void
|
||||
public function __addBeforeEach(?Closure $hook): void
|
||||
{
|
||||
$this->addHook('beforeEach', $hook);
|
||||
$this->__addHook('__beforeEach', $hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" after each test hook that will execute **before**
|
||||
* the test defined `afterEach` hook.
|
||||
*/
|
||||
public function addAfterEach(?Closure $hook): void
|
||||
public function __addAfterEach(?Closure $hook): void
|
||||
{
|
||||
$this->addHook('afterEach', $hook);
|
||||
$this->__addHook('__afterEach', $hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/global hook and compose them if more than one is passed.
|
||||
*/
|
||||
private function addHook(string $property, ?Closure $hook): void
|
||||
private function __addHook(string $property, ?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
return;
|
||||
@ -176,7 +178,9 @@ trait Testable
|
||||
*/
|
||||
public function getName(bool $withDataSet = true): string
|
||||
{
|
||||
return $this->__description;
|
||||
return (str_ends_with(Backtrace::file(), 'TestRunner.php') || Backtrace::line() === 277)
|
||||
? '__test'
|
||||
: $this->__description;
|
||||
}
|
||||
|
||||
public static function __getFileName(): string
|
||||
@ -193,8 +197,8 @@ trait Testable
|
||||
|
||||
$beforeAll = TestSuite::getInstance()->beforeAll->get(self::$__filename);
|
||||
|
||||
if (self::$beforeAll instanceof Closure) {
|
||||
$beforeAll = ChainableClosure::fromStatic(self::$beforeAll, $beforeAll);
|
||||
if (self::$__beforeAll instanceof Closure) {
|
||||
$beforeAll = ChainableClosure::fromStatic(self::$__beforeAll, $beforeAll);
|
||||
}
|
||||
|
||||
call_user_func(Closure::bind($beforeAll, null, self::class));
|
||||
@ -207,8 +211,8 @@ trait Testable
|
||||
{
|
||||
$afterAll = TestSuite::getInstance()->afterAll->get(self::$__filename);
|
||||
|
||||
if (self::$afterAll instanceof Closure) {
|
||||
$afterAll = ChainableClosure::fromStatic(self::$afterAll, $afterAll);
|
||||
if (self::$__afterAll instanceof Closure) {
|
||||
$afterAll = ChainableClosure::fromStatic(self::$__afterAll, $afterAll);
|
||||
}
|
||||
|
||||
call_user_func(Closure::bind($afterAll, null, self::class));
|
||||
@ -227,8 +231,8 @@ trait Testable
|
||||
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
|
||||
|
||||
if ($this->beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach);
|
||||
if ($this->__beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($beforeEach, func_get_args());
|
||||
@ -241,8 +245,8 @@ trait Testable
|
||||
{
|
||||
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
|
||||
|
||||
if ($this->afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::from($this->afterEach, $afterEach);
|
||||
if ($this->__afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::from($this->__afterEach, $afterEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($afterEach, func_get_args());
|
||||
@ -273,7 +277,7 @@ trait Testable
|
||||
*/
|
||||
public function __test()
|
||||
{
|
||||
return $this->__callClosure($this->__test, $this->resolveTestArguments(func_get_args()));
|
||||
return $this->__callClosure($this->__test, $this->__resolveTestArguments(func_get_args()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,7 +285,7 @@ trait Testable
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function resolveTestArguments(array $arguments): array
|
||||
private function __resolveTestArguments(array $arguments): array
|
||||
{
|
||||
return array_map(function ($data) {
|
||||
return $data instanceof Closure ? $this->__callClosure($data, []) : $data;
|
||||
|
||||
@ -1,132 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\AddsDefaults;
|
||||
use Pest\Actions\AddsTests;
|
||||
use Pest\Actions\InteractsWithPlugins;
|
||||
use Pest\Actions\LoadStructure;
|
||||
use Pest\Actions\ValidatesConfiguration;
|
||||
use Pest\Plugins\Version;
|
||||
use Pest\Support\Container;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestSuite as BaseTestSuite;
|
||||
use PHPUnit\TextUI\Command as BaseCommand;
|
||||
use PHPUnit\TextUI\TestRunner;
|
||||
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Command extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Holds the current testing suite.
|
||||
*
|
||||
* @var TestSuite
|
||||
*/
|
||||
private $testSuite;
|
||||
|
||||
/**
|
||||
* Holds the current console output.
|
||||
*
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the command class.
|
||||
*/
|
||||
public function __construct(TestSuite $testSuite, OutputInterface $output)
|
||||
{
|
||||
$this->testSuite = $testSuite;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
protected function handleArguments(array $argv): void
|
||||
{
|
||||
$argv = InteractsWithPlugins::handleArguments($argv);
|
||||
|
||||
parent::handleArguments($argv);
|
||||
|
||||
/*
|
||||
* Let's validate the configuration. Making
|
||||
* sure all options are yet supported by Pest.
|
||||
*/
|
||||
ValidatesConfiguration::in($this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PHPUnit test runner.
|
||||
*/
|
||||
protected function createRunner(): TestRunner
|
||||
{
|
||||
/*
|
||||
* First, let's add the defaults we use on `pest`. Those
|
||||
* are the printer class, and others that may be appear.
|
||||
*/
|
||||
$this->arguments = AddsDefaults::to($this->arguments);
|
||||
|
||||
$testRunner = new TestRunner($this->arguments['loader']);
|
||||
$testSuite = $this->arguments['test'];
|
||||
|
||||
if (is_string($testSuite)) {
|
||||
if (\is_dir($testSuite)) {
|
||||
/** @var string[] $files */
|
||||
$files = (new FileIteratorFacade())->getFilesAsArray(
|
||||
$testSuite,
|
||||
$this->arguments['testSuffixes']
|
||||
);
|
||||
} else {
|
||||
$files = [$testSuite];
|
||||
}
|
||||
|
||||
$testSuite = new BaseTestSuite($testSuite);
|
||||
|
||||
$testSuite->addTestFiles($files);
|
||||
|
||||
$this->arguments['test'] = $testSuite;
|
||||
}
|
||||
|
||||
AddsTests::to($testSuite, $this->testSuite);
|
||||
|
||||
return $testRunner;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
public function run(array $argv, bool $exit = true): int
|
||||
{
|
||||
LoadStructure::in($this->testSuite->rootPath);
|
||||
|
||||
$result = parent::run($argv, false);
|
||||
$result = InteractsWithPlugins::addOutput($result);
|
||||
|
||||
exit($result);
|
||||
}
|
||||
|
||||
protected function showHelp(): void
|
||||
{
|
||||
/** @var Version $version */
|
||||
$version = Container::getInstance()->get(Version::class);
|
||||
$version->handleArguments(['--version']);
|
||||
parent::showHelp();
|
||||
|
||||
(new Help($this->output))();
|
||||
}
|
||||
}
|
||||
72
src/Console/Kernel.php
Normal file
72
src/Console/Kernel.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\InteractsWithPlugins;
|
||||
use Pest\Bootstrappers;
|
||||
use PHPUnit\TextUI\Application;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Kernel
|
||||
{
|
||||
/**
|
||||
* The Kernel bootstrappers.
|
||||
*
|
||||
* @var array<int, class-string>
|
||||
*/
|
||||
private static array $bootstrappers = [
|
||||
Bootstrappers\BootExceptionHandler::class,
|
||||
Bootstrappers\BootEmitter::class,
|
||||
Bootstrappers\BootSubscribers::class,
|
||||
Bootstrappers\BootFiles::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new Kernel instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private Application $application
|
||||
) {
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the Kernel.
|
||||
*/
|
||||
public static function boot(): self
|
||||
{
|
||||
foreach (self::$bootstrappers as $bootstrapper) {
|
||||
(new $bootstrapper())->__invoke();
|
||||
}
|
||||
|
||||
return new self(new Application());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the given argv.
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
public function handle(array $argv): int
|
||||
{
|
||||
$argv = InteractsWithPlugins::handleArguments($argv);
|
||||
|
||||
$result = $this->application->run(
|
||||
$argv, false,
|
||||
);
|
||||
|
||||
return InteractsWithPlugins::addOutput($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the Kernel.
|
||||
*/
|
||||
public function shutdown(): void
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
248
src/Emitters/DispatchingEmitter.php
Normal file
248
src/Emitters/DispatchingEmitter.php
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Emitters;
|
||||
|
||||
use Pest\Subscribers\EnsureTestsAreLoaded;
|
||||
use PHPUnit\Event\Code;
|
||||
use PHPUnit\Event\Code\Throwable;
|
||||
use PHPUnit\Event\Emitter;
|
||||
use PHPUnit\Framework\Constraint;
|
||||
use PHPUnit\Framework\TestResult;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use SebastianBergmann\GlobalState\Snapshot;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DispatchingEmitter implements Emitter
|
||||
{
|
||||
public function __construct(private Emitter $baseEmitter)
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
public function eventFacadeSealed(): void
|
||||
{
|
||||
$this->baseEmitter->eventFacadeSealed(...func_get_args());
|
||||
}
|
||||
|
||||
public function testRunnerStarted(): void
|
||||
{
|
||||
$this->baseEmitter->testRunnerStarted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testRunnerConfigured(Configuration $configuration): void
|
||||
{
|
||||
$this->baseEmitter->testRunnerConfigured($configuration);
|
||||
}
|
||||
|
||||
public function testRunnerFinished(): void
|
||||
{
|
||||
$this->baseEmitter->testRunnerFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function assertionMade(mixed $value, Constraint\Constraint $constraint, string $message, bool $hasFailed): void
|
||||
{
|
||||
$this->baseEmitter->assertionMade($value, $constraint, $message, $hasFailed);
|
||||
}
|
||||
|
||||
public function bootstrapFinished(string $filename): void
|
||||
{
|
||||
$this->baseEmitter->bootstrapFinished($filename);
|
||||
}
|
||||
|
||||
public function comparatorRegistered(string $className): void
|
||||
{
|
||||
$this->baseEmitter->comparatorRegistered($className);
|
||||
}
|
||||
|
||||
public function extensionLoaded(string $name, string $version): void
|
||||
{
|
||||
$this->baseEmitter->extensionLoaded($name, $version);
|
||||
}
|
||||
|
||||
public function globalStateCaptured(Snapshot $snapshot): void
|
||||
{
|
||||
$this->baseEmitter->globalStateCaptured($snapshot);
|
||||
}
|
||||
|
||||
public function globalStateModified(Snapshot $snapshotBefore, Snapshot $snapshotAfter, string $diff): void
|
||||
{
|
||||
$this->baseEmitter->globalStateModified($snapshotBefore, $snapshotAfter, $diff);
|
||||
}
|
||||
|
||||
public function globalStateRestored(Snapshot $snapshot): void
|
||||
{
|
||||
$this->baseEmitter->globalStateRestored($snapshot);
|
||||
}
|
||||
|
||||
public function testErrored(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testErrored(...func_get_args());
|
||||
}
|
||||
|
||||
public function testFailed(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testFailed(...func_get_args());
|
||||
}
|
||||
|
||||
public function testFinished(Code\Test $test): void
|
||||
{
|
||||
$this->baseEmitter->testFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testOutputPrinted(Code\Test $test, string $output): void
|
||||
{
|
||||
$this->baseEmitter->testOutputPrinted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPassed(Code\Test $test): void
|
||||
{
|
||||
$this->baseEmitter->testPassed(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPassedWithWarning(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testPassedWithWarning(...func_get_args());
|
||||
}
|
||||
|
||||
public function testConsideredRisky(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testConsideredRisky(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAborted(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testAborted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSkipped(Code\Test $test, string $message): void
|
||||
{
|
||||
$this->baseEmitter->testSkipped(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPrepared(Code\Test $test): void
|
||||
{
|
||||
$this->baseEmitter->testPrepared(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testAfterTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterLastTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testAfterLastTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeFirstTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeFirstTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeFirstTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeFirstTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPreConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testPreConditionCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPreConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testPreConditionFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPostConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testPostConditionCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPostConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testPostConditionFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testAfterTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterLastTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testAfterLastTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreated(string $className): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreatedForTrait(string $traitName): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreatedForTrait(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreatedForAbstractClass(string $className): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreatedForAbstractClass(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreatedFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreatedFromWsdl(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPartialMockObjectCreated(string $className, string ...$methodNames): void
|
||||
{
|
||||
$this->baseEmitter->testPartialMockObjectCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testTestProxyCreated(string $className, array $constructorArguments): void
|
||||
{
|
||||
$this->baseEmitter->testTestProxyCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testTestStubCreated(string $className): void
|
||||
{
|
||||
$this->baseEmitter->testTestStubCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteLoaded(TestSuite $testSuite): void
|
||||
{
|
||||
EnsureTestsAreLoaded::setTestSuite($testSuite);
|
||||
|
||||
$this->baseEmitter->testSuiteLoaded(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteSorted(int $executionOrder, int $executionOrderDefects, bool $resolveDependencies): void
|
||||
{
|
||||
$this->baseEmitter->testSuiteSorted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteStarted(TestSuite $testSuite): void
|
||||
{
|
||||
$this->baseEmitter->testSuiteStarted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteFinished(TestSuite $testSuite, TestResult $result): void
|
||||
{
|
||||
$this->baseEmitter->testSuiteFinished(...func_get_args());
|
||||
}
|
||||
}
|
||||
@ -92,16 +92,14 @@ final class TestCaseFactory
|
||||
public $factoryProxies;
|
||||
|
||||
/**
|
||||
* Holds the higher order
|
||||
* messages that are proxyble.
|
||||
* Holds the higher order messages that are proxyble.
|
||||
*
|
||||
* @var HigherOrderMessageCollection
|
||||
*/
|
||||
public $proxies;
|
||||
|
||||
/**
|
||||
* Holds the higher order
|
||||
* messages that are chainable.
|
||||
* Holds the higher order messages that are chainable.
|
||||
*
|
||||
* @var HigherOrderMessageCollection
|
||||
*/
|
||||
@ -232,7 +230,7 @@ final class TestCaseFactory
|
||||
/**
|
||||
* Determine if the test case will receive argument input from Pest, or not.
|
||||
*/
|
||||
public function receivesArguments(): bool
|
||||
public function __receivesArguments(): bool
|
||||
{
|
||||
return count($this->datasets) > 0
|
||||
|| $this->factoryProxies->count('addDependencies') > 0;
|
||||
|
||||
@ -41,6 +41,7 @@ final class AfterEachRepository
|
||||
|
||||
return ChainableClosure::from(function (): void {
|
||||
if (class_exists(Mockery::class)) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
if ($container = Mockery::getContainer()) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->addToAssertionCount($container->mockery_getExpectationCount());
|
||||
|
||||
@ -86,12 +86,11 @@ final class TestRepository
|
||||
}
|
||||
}
|
||||
|
||||
// IDEA: Consider set the real lines on these.
|
||||
$testCase->factoryProxies->add($filename, 0, 'addGroups', [$groups]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addBeforeAll', [$hooks[0] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addBeforeEach', [$hooks[1] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addAfterEach', [$hooks[2] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addAfterAll', [$hooks[3] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addBeforeAll', [$hooks[0] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addBeforeEach', [$hooks[1] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addAfterEach', [$hooks[2] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addAfterAll', [$hooks[3] ?? null]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -171,7 +170,7 @@ final class TestRepository
|
||||
throw new TestAlreadyExist($test->filename, $test->description);
|
||||
}
|
||||
|
||||
if (!$test->receivesArguments()) {
|
||||
if (!$test->__receivesArguments()) {
|
||||
$arguments = Reflection::getFunctionArguments($test->test);
|
||||
|
||||
if (count($arguments) > 0) {
|
||||
|
||||
22
src/Subscribers/EnsureConfigurationDefaults.php
Normal file
22
src/Subscribers/EnsureConfigurationDefaults.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use PHPUnit\Event\TestRunner\Configured;
|
||||
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureConfigurationDefaults implements ConfiguredSubscriber
|
||||
{
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Configured $event): void
|
||||
{
|
||||
$configuration = $event->configuration();
|
||||
}
|
||||
}
|
||||
27
src/Subscribers/EnsureConfigurationIsValid.php
Normal file
27
src/Subscribers/EnsureConfigurationIsValid.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use PHPUnit\Event\TestRunner\Configured;
|
||||
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureConfigurationIsValid implements ConfiguredSubscriber
|
||||
{
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Configured $event): void
|
||||
{
|
||||
$configuration = $event->configuration();
|
||||
|
||||
if ($configuration->processIsolation()) {
|
||||
throw new AttributeNotSupportedYet('processIsolation', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
79
src/Subscribers/EnsureTestsAreLoaded.php
Normal file
79
src/Subscribers/EnsureTestsAreLoaded.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use PHPUnit\Event\TestSuite\Loaded;
|
||||
use PHPUnit\Event\TestSuite\LoadedSubscriber;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureTestsAreLoaded implements LoadedSubscriber
|
||||
{
|
||||
/**
|
||||
* The current test suite, if any.
|
||||
*/
|
||||
private static ?TestSuite $testSuite;
|
||||
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Loaded $event): void
|
||||
{
|
||||
$this->removeWarnings(self::$testSuite);
|
||||
|
||||
$testSuites = [];
|
||||
|
||||
$testSuite = \Pest\TestSuite::getInstance();
|
||||
$testSuite->tests->build($testSuite, function (TestCase $testCase) use (&$testSuites): void {
|
||||
$testCaseClass = get_class($testCase);
|
||||
if (!array_key_exists($testCaseClass, $testSuites)) {
|
||||
$testSuites[$testCaseClass] = [];
|
||||
}
|
||||
|
||||
$testSuites[$testCaseClass][] = $testCase;
|
||||
});
|
||||
|
||||
foreach ($testSuites as $testCaseName => $testCases) {
|
||||
$testTestSuite = new TestSuite($testCaseName);
|
||||
$testTestSuite->setTests([]);
|
||||
foreach ($testCases as $testCase) {
|
||||
$testTestSuite->addTest($testCase, $testCase->groups());
|
||||
}
|
||||
self::$testSuite->addTestSuite($testTestSuite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current test suite.
|
||||
*/
|
||||
public static function setTestSuite(TestSuite $testSuite): void
|
||||
{
|
||||
self::$testSuite = $testSuite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the test case that have "empty" warnings.
|
||||
*/
|
||||
private function removeWarnings(TestSuite $testSuite): void
|
||||
{
|
||||
$tests = $testSuite->tests();
|
||||
|
||||
foreach ($tests as $key => $test) {
|
||||
if ($test instanceof TestSuite) {
|
||||
$this->removeWarnings($test);
|
||||
}
|
||||
|
||||
if ($test instanceof WarningTestCase) {
|
||||
unset($tests[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$testSuite->setTests(array_values($tests));
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,7 @@ final class Backtrace
|
||||
$current = null;
|
||||
|
||||
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
|
||||
if (Str::endsWith($trace[self::FILE], (string) realpath('vendor/phpunit/phpunit/src/Util/FileLoader.php'))) {
|
||||
if (Str::endsWith($trace[self::FILE], (string) realpath('overrides/Runner/TestSuiteLoader.php'))) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -2,14 +2,18 @@
|
||||
|
||||
$file = __DIR__ . DIRECTORY_SEPARATOR . 'after-all-test';
|
||||
|
||||
beforeAll(function () use ($file) {
|
||||
@unlink($file);
|
||||
});
|
||||
|
||||
afterAll(function () use ($file) {
|
||||
unlink($file);
|
||||
@unlink($file);
|
||||
});
|
||||
|
||||
test('deletes file after all', function () use ($file) {
|
||||
file_put_contents($file, 'foo');
|
||||
$this->assertFileExists($file);
|
||||
register_shutdown_function(function () use ($file) {
|
||||
$this->assertFileNotExists($file);
|
||||
register_shutdown_function(function () {
|
||||
// $this->assertFileDoesNotExist($file);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,51 +1,45 @@
|
||||
<?php
|
||||
|
||||
global $globalHook;
|
||||
|
||||
// NOTE: this test does not have a $globalHook->calls offset since it is first
|
||||
// in the directory and thus will always run before the others. See also the
|
||||
// BeforeAllTest.php for details.
|
||||
|
||||
uses()->afterAll(function () use ($globalHook) {
|
||||
expect($globalHook)
|
||||
uses()->afterAll(function () {
|
||||
expect($_SERVER['globalHook'])
|
||||
->toHaveProperty('afterAll')
|
||||
->and($globalHook->afterAll)
|
||||
->and($_SERVER['globalHook']->afterAll)
|
||||
->toBe(0)
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->afterAll
|
||||
->toBe(1);
|
||||
|
||||
$globalHook->afterAll = 1;
|
||||
$globalHook->calls->afterAll++;
|
||||
$_SERVER['globalHook']->afterAll = 1;
|
||||
$_SERVER['globalHook']->calls->afterAll++;
|
||||
});
|
||||
|
||||
afterAll(function () use ($globalHook) {
|
||||
expect($globalHook)
|
||||
afterAll(function () {
|
||||
expect($_SERVER['globalHook'])
|
||||
->toHaveProperty('afterAll')
|
||||
->and($globalHook->afterAll)
|
||||
->and($_SERVER['globalHook']->afterAll)
|
||||
->toBe(1)
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->afterAll
|
||||
->toBe(2);
|
||||
|
||||
$globalHook->afterAll = 2;
|
||||
$globalHook->calls->afterAll++;
|
||||
$_SERVER['globalHook']->afterAll = 2;
|
||||
$_SERVER['globalHook']->calls->afterAll++;
|
||||
});
|
||||
|
||||
test('global afterAll execution order', function () use ($globalHook) {
|
||||
expect($globalHook)
|
||||
test('global afterAll execution order', function () {
|
||||
expect($_SERVER['globalHook'])
|
||||
->not()
|
||||
->toHaveProperty('afterAll')
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->afterAll
|
||||
->toBe(0);
|
||||
});
|
||||
|
||||
it('only gets called once per file', function () use ($globalHook) {
|
||||
expect($globalHook)
|
||||
it('only gets called once per file', function () {
|
||||
expect($_SERVER['globalHook'])
|
||||
->not()
|
||||
->toHaveProperty('afterAll')
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->afterAll
|
||||
->toBe(0);
|
||||
});
|
||||
|
||||
@ -2,55 +2,53 @@
|
||||
|
||||
use Pest\Support\Str;
|
||||
|
||||
global $globalHook;
|
||||
|
||||
// HACK: we have to determine our $globalHook->calls baseline. This is because
|
||||
// HACK: we have to determine our $_SERVER['globalHook-]>calls baseline. This is because
|
||||
// two other tests are executed before this one due to filename ordering.
|
||||
$args = $_SERVER['argv'] ?? [];
|
||||
$single = (isset($args[1]) && Str::endsWith(__FILE__, $args[1])) || ($_SERVER['PEST_PARALLEL'] ?? false);
|
||||
$offset = $single ? 0 : 2;
|
||||
|
||||
uses()->beforeAll(function () use ($globalHook, $offset) {
|
||||
expect($globalHook)
|
||||
uses()->beforeAll(function () use ($offset) {
|
||||
expect($_SERVER['globalHook'])
|
||||
->toHaveProperty('beforeAll')
|
||||
->and($globalHook->beforeAll)
|
||||
->and($_SERVER['globalHook']->beforeAll)
|
||||
->toBe(0)
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->beforeAll
|
||||
->toBe(1 + $offset);
|
||||
|
||||
$globalHook->beforeAll = 1;
|
||||
$globalHook->calls->beforeAll++;
|
||||
$_SERVER['globalHook']->beforeAll = 1;
|
||||
$_SERVER['globalHook']->calls->beforeAll++;
|
||||
});
|
||||
|
||||
beforeAll(function () use ($globalHook, $offset) {
|
||||
expect($globalHook)
|
||||
beforeAll(function () use ($offset) {
|
||||
expect($_SERVER['globalHook'])
|
||||
->toHaveProperty('beforeAll')
|
||||
->and($globalHook->beforeAll)
|
||||
->and($_SERVER['globalHook']->beforeAll)
|
||||
->toBe(1)
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->beforeAll
|
||||
->toBe(2 + $offset);
|
||||
|
||||
$globalHook->beforeAll = 2;
|
||||
$globalHook->calls->beforeAll++;
|
||||
$_SERVER['globalHook']->beforeAll = 2;
|
||||
$_SERVER['globalHook']->calls->beforeAll++;
|
||||
});
|
||||
|
||||
test('global beforeAll execution order', function () use ($globalHook, $offset) {
|
||||
expect($globalHook)
|
||||
test('global beforeAll execution order', function () use ($offset) {
|
||||
expect($_SERVER['globalHook'])
|
||||
->toHaveProperty('beforeAll')
|
||||
->and($globalHook->beforeAll)
|
||||
->and($_SERVER['globalHook']->beforeAll)
|
||||
->toBe(2)
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->beforeAll
|
||||
->toBe(3 + $offset);
|
||||
});
|
||||
|
||||
it('only gets called once per file', function () use ($globalHook, $offset) {
|
||||
expect($globalHook)
|
||||
it('only gets called once per file', function () use ($offset) {
|
||||
expect($_SERVER['globalHook'])
|
||||
->beforeAll
|
||||
->toBe(2)
|
||||
->and($globalHook->calls)
|
||||
->and($_SERVER['globalHook']->calls)
|
||||
->beforeAll
|
||||
->toBe(3 + $offset);
|
||||
});
|
||||
|
||||
@ -7,21 +7,21 @@ uses(CustomTestCaseInSubFolder::class)->in('PHPUnit/CustomTestCaseInSubFolders/S
|
||||
uses()->group('integration')->in('Visual');
|
||||
|
||||
// NOTE: global test value container to be mutated and checked across files, as needed
|
||||
$globalHook = (object) ['calls' => (object) ['beforeAll' => 0, 'afterAll' => 0]];
|
||||
$_SERVER['globalHook'] = (object) ['calls' => (object) ['beforeAll' => 0, 'afterAll' => 0]];
|
||||
|
||||
uses()
|
||||
->beforeEach(function () {
|
||||
$this->baz = 0;
|
||||
})
|
||||
->beforeAll(function () use ($globalHook) {
|
||||
$globalHook->beforeAll = 0;
|
||||
$globalHook->calls->beforeAll++;
|
||||
->beforeAll(function () {
|
||||
$_SERVER['globalHook']->beforeAll = 0;
|
||||
$_SERVER['globalHook']->calls->beforeAll++;
|
||||
})
|
||||
->afterEach(function () {
|
||||
$this->ith = 0;
|
||||
})
|
||||
->afterAll(function () use ($globalHook) {
|
||||
$globalHook->afterAll = 0;
|
||||
$globalHook->calls->afterAll++;
|
||||
->afterAll(function () {
|
||||
$_SERVER['globalHook']->afterAll = 0;
|
||||
$_SERVER['globalHook']->calls->afterAll++;
|
||||
})
|
||||
->in('Hooks');
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
|
||||
use Pest\Actions\AddsDefaults;
|
||||
use PHPUnit\TextUI\DefaultResultPrinter;
|
||||
|
||||
it('sets defaults', function () {
|
||||
$arguments = AddsDefaults::to(['bar' => 'foo']);
|
||||
|
||||
expect($arguments['printer'])->toBeInstanceOf(Printer::class);
|
||||
expect($arguments['bar'])->toBe('foo');
|
||||
});
|
||||
|
||||
it('does not override options', function () {
|
||||
$defaultResultPrinter = new DefaultResultPrinter();
|
||||
|
||||
expect(AddsDefaults::to(['printer' => $defaultResultPrinter]))->tobe([
|
||||
'printer' => $defaultResultPrinter,
|
||||
]);
|
||||
});
|
||||
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Pest\Actions\AddsTests;
|
||||
use PHPUnit\Framework\TestCase as PhpUnitTestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
$closure = function () {
|
||||
};
|
||||
$pestTestCase = new class() extends \PHPUnit\Framework\TestCase {
|
||||
};
|
||||
|
||||
test('default php unit tests', function () {
|
||||
$testSuite = new TestSuite();
|
||||
|
||||
$phpUnitTestCase = new class() extends PhpUnitTestCase {
|
||||
};
|
||||
$testSuite->addTest($phpUnitTestCase);
|
||||
expect($testSuite->tests())->toHaveCount(1);
|
||||
|
||||
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd(), 'tests'));
|
||||
expect($testSuite->tests())->toHaveCount(1);
|
||||
});
|
||||
|
||||
it('removes warnings', function () {
|
||||
$testSuite = new TestSuite();
|
||||
$warningTestCase = new WarningTestCase('No tests found in class "Pest\TestCase".');
|
||||
$testSuite->addTest($warningTestCase);
|
||||
|
||||
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd(), 'tests'));
|
||||
expect($testSuite->tests())->toHaveCount(0);
|
||||
});
|
||||
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Pest\Actions\ValidatesConfiguration;
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
|
||||
it('throws exception when configuration not found', function () {
|
||||
$this->expectException(FileOrFolderNotFound::class);
|
||||
|
||||
ValidatesConfiguration::in([
|
||||
'configuration' => 'foo',
|
||||
]);
|
||||
});
|
||||
|
||||
it('throws exception when `process isolation` is true', function () {
|
||||
$this->expectException(AttributeNotSupportedYet::class);
|
||||
$this->expectExceptionMessage('The PHPUnit attribute `processIsolation` with value `true` is not supported yet.');
|
||||
|
||||
$filename = implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__, 2),
|
||||
'Fixtures',
|
||||
'phpunit-in-isolation.xml',
|
||||
]);
|
||||
|
||||
ValidatesConfiguration::in([
|
||||
'configuration' => $filename,
|
||||
]);
|
||||
});
|
||||
|
||||
it('do not throws exception when `process isolation` is false', function () {
|
||||
$filename = implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__, 2),
|
||||
'Fixtures',
|
||||
'phpunit-not-in-isolation.xml',
|
||||
]);
|
||||
|
||||
ValidatesConfiguration::in([
|
||||
'configuration' => $filename,
|
||||
]);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
0
tests/Visual/junit.html
Normal file
0
tests/Visual/junit.html
Normal file
Reference in New Issue
Block a user