mirror of
https://github.com/pestphp/pest.git
synced 2026-03-07 00:07:22 +01:00
Merge branch 'pestphp:2.x' into 2.x
This commit is contained in:
@ -36,6 +36,5 @@ We would like to extend our thanks to the following sponsors for funding Pest de
|
||||
- [Hyvor](https://hyvor.com/)
|
||||
- [Fathom Analytics](https://usefathom.com/)
|
||||
- [Meema](https://meema.io)
|
||||
- [Scout APM](https://scoutapm.com)
|
||||
|
||||
Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
||||
|
||||
8
bin/pest
8
bin/pest
@ -22,12 +22,12 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
$todo = false;
|
||||
|
||||
foreach ($args as $key => $value) {
|
||||
if (str_contains($value, '--compact')) {
|
||||
if ($value === '--compact') {
|
||||
$_SERVER['COLLISION_PRINTER_COMPACT'] = 'true';
|
||||
unset($args[$key]);
|
||||
}
|
||||
|
||||
if (str_contains($value, '--profile')) {
|
||||
if ($value === '--profile') {
|
||||
$_SERVER['COLLISION_PRINTER_PROFILE'] = 'true';
|
||||
unset($args[$key]);
|
||||
}
|
||||
@ -36,12 +36,12 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
unset($args[$key]);
|
||||
}
|
||||
|
||||
if (str_contains($value, '--dirty')) {
|
||||
if ($value === '--dirty') {
|
||||
$dirty = true;
|
||||
unset($args[$key]);
|
||||
}
|
||||
|
||||
if (str_contains($value, '--todo')) {
|
||||
if ($value === '--todo') {
|
||||
$todo = true;
|
||||
unset($args[$key]);
|
||||
}
|
||||
|
||||
@ -18,10 +18,10 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.1.0",
|
||||
"nunomaduro/collision": "^7.0.5",
|
||||
"nunomaduro/collision": "^7.1.0",
|
||||
"nunomaduro/termwind": "^1.15.1",
|
||||
"pestphp/pest-plugin": "^2.0.0",
|
||||
"phpunit/phpunit": "^10.0.12"
|
||||
"phpunit/phpunit": "^10.0.15"
|
||||
},
|
||||
"conflict": {
|
||||
"brianium/paratest": "<7.0.6"
|
||||
@ -46,10 +46,10 @@
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^7.0.6",
|
||||
"brianium/paratest": "^7.1.0",
|
||||
"pestphp/pest-dev-tools": "^2.4.0",
|
||||
"pestphp/pest-plugin-arch": "^2.0.0",
|
||||
"symfony/process": "^6.2.5"
|
||||
"symfony/process": "^6.2.7"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
|
||||
@ -128,6 +128,27 @@ final class TestSuiteLoader
|
||||
}
|
||||
}
|
||||
|
||||
if (! $testCaseFound) {
|
||||
foreach (array_reverse($loadedClasses) as $loadedClass) {
|
||||
$offset = 0 - strlen($suiteClassName);
|
||||
|
||||
if (stripos(substr($loadedClass, $offset - 1), '\\'.$suiteClassName) === 0 ||
|
||||
stripos(substr($loadedClass, $offset - 1), '_'.$suiteClassName) === 0) {
|
||||
try {
|
||||
$class = new ReflectionClass($loadedClass);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (ReflectionException) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$suiteClassName = $loadedClass;
|
||||
$testCaseFound = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $testCaseFound) {
|
||||
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||
}
|
||||
|
||||
104
overrides/TextUI/Command/WarmCodeCoverageCacheCommand.php
Normal file
104
overrides/TextUI/Command/WarmCodeCoverageCacheCommand.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* 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 HOLDER 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\TextUI\Command;
|
||||
|
||||
use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use PHPUnit\TextUI\Configuration\NoCoverageCacheDirectoryException;
|
||||
use SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer;
|
||||
use SebastianBergmann\Timer\NoActiveTimerException;
|
||||
use SebastianBergmann\Timer\Timer;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class WarmCodeCoverageCacheCommand implements Command
|
||||
{
|
||||
private readonly Configuration $configuration;
|
||||
|
||||
private readonly CodeCoverageFilterRegistry $codeCoverageFilterRegistry;
|
||||
|
||||
public function __construct(Configuration $configuration, CodeCoverageFilterRegistry $codeCoverageFilterRegistry)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->codeCoverageFilterRegistry = $codeCoverageFilterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoActiveTimerException
|
||||
* @throws NoCoverageCacheDirectoryException
|
||||
*/
|
||||
public function execute(): Result
|
||||
{
|
||||
if (! $this->configuration->hasCoverageCacheDirectory()) {
|
||||
return Result::from(
|
||||
'Cache for static analysis has not been configured'.PHP_EOL,
|
||||
Result::FAILURE
|
||||
);
|
||||
}
|
||||
|
||||
$this->codeCoverageFilterRegistry->init($this->configuration);
|
||||
|
||||
if (! $this->codeCoverageFilterRegistry->configured()) {
|
||||
return Result::from(
|
||||
'Filter for code coverage has not been configured'.PHP_EOL,
|
||||
Result::FAILURE
|
||||
);
|
||||
}
|
||||
|
||||
$timer = new Timer;
|
||||
$timer->start();
|
||||
|
||||
(new CacheWarmer)->warmCache(
|
||||
$this->configuration->coverageCacheDirectory(),
|
||||
! $this->configuration->disableCodeCoverageIgnore(),
|
||||
$this->configuration->ignoreDeprecatedCodeUnitsFromCodeCoverage(),
|
||||
$this->codeCoverageFilterRegistry->get()
|
||||
);
|
||||
|
||||
return Result::from();
|
||||
}
|
||||
}
|
||||
@ -61,9 +61,11 @@ final class TestSkippedSubscriber extends Subscriber implements SkippedSubscribe
|
||||
*/
|
||||
public function notify(Skipped $event): void
|
||||
{
|
||||
str_contains($event->message(), '__TODO__')
|
||||
? $this->printTodoItem()
|
||||
: $this->printer()->testSkipped();
|
||||
if (str_contains($event->message(), '__TODO__')) {
|
||||
$this->printTodoItem();
|
||||
}
|
||||
|
||||
$this->printer()->testSkipped();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -7,17 +7,11 @@
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
failOnWarning="false"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
backupStaticProperties="false"
|
||||
displayDetailsOnIncompleteTests="true"
|
||||
displayDetailsOnSkippedTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerErrors="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use NunoMaduro\Collision;
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootExceptionHandler implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Boots the "Collision" exception handler.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$handler = new Collision\Provider();
|
||||
|
||||
$handler->register();
|
||||
}
|
||||
}
|
||||
@ -69,11 +69,9 @@ final class BootFiles implements Bootstrapper
|
||||
if (! Str::endsWith($filename, '.php')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! file_exists($filename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
include_once $filename;
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ final class BootOverrides implements Bootstrapper
|
||||
'Runner/Filter/NameFilterIterator.php',
|
||||
'Runner/ResultCache/DefaultResultCache.php',
|
||||
'Runner/TestSuiteLoader.php',
|
||||
'TextUI/Command/WarmCodeCoverageCacheCommand.php',
|
||||
'TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php',
|
||||
];
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@ final class Kernel
|
||||
*/
|
||||
private const BOOTSTRAPPERS = [
|
||||
Bootstrappers\BootOverrides::class,
|
||||
Bootstrappers\BootExceptionHandler::class,
|
||||
Bootstrappers\BootSubscribers::class,
|
||||
Bootstrappers\BootFiles::class,
|
||||
Bootstrappers\BootView::class,
|
||||
|
||||
@ -180,6 +180,13 @@ final class Converter
|
||||
*/
|
||||
public function getStateFromResult(PhpUnitTestResult $result): State
|
||||
{
|
||||
return $this->stateGenerator->fromPhpUnitTestResult($result);
|
||||
$numberOfPassedTests = $result->numberOfTestsRun()
|
||||
- $result->numberOfTestErroredEvents()
|
||||
- $result->numberOfTestFailedEvents()
|
||||
- $result->numberOfTestSkippedEvents()
|
||||
- $result->numberOfTestsWithTestConsideredRiskyEvents()
|
||||
- $result->numberOfTestMarkedIncompleteEvents();
|
||||
|
||||
return $this->stateGenerator->fromPhpUnitTestResult($numberOfPassedTests, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,6 @@ final class TeamCityLogger
|
||||
|
||||
public function testMarkedIncomplete(MarkedIncomplete $event): never
|
||||
{
|
||||
// TODO: when does this trigger?
|
||||
throw ShouldNotHappen::fromMessage('testMarkedIncomplete not implemented.');
|
||||
}
|
||||
|
||||
|
||||
@ -30,13 +30,13 @@ final class Cache implements HandlesArguments
|
||||
*/
|
||||
public function handleArguments(array $arguments): array
|
||||
{
|
||||
if (! $this->hasArgument('--parallel', $arguments)) {
|
||||
$arguments = $this->pushArgument(
|
||||
sprintf('--cache-directory=%s', realpath(self::TEMPORARY_FOLDER)),
|
||||
$arguments
|
||||
);
|
||||
$arguments = $this->pushArgument(
|
||||
sprintf('--cache-directory=%s', realpath(self::TEMPORARY_FOLDER)),
|
||||
$arguments
|
||||
);
|
||||
|
||||
$arguments = $this->pushArgument('--cache-result', $arguments);
|
||||
if (! $this->hasArgument('--parallel', $arguments)) {
|
||||
return $this->pushArgument('--cache-result', $arguments);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
|
||||
@ -12,6 +12,7 @@ use function fread;
|
||||
use function fseek;
|
||||
use function ftell;
|
||||
use function fwrite;
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\State;
|
||||
use ParaTest\Options;
|
||||
use Pest\Plugins\Parallel\Support\CompactPrinter;
|
||||
use Pest\Support\StateGenerator;
|
||||
@ -27,11 +28,21 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
/** @internal */
|
||||
final class ResultPrinter
|
||||
{
|
||||
/**
|
||||
* If the test should be marked as todo.
|
||||
*/
|
||||
public bool $lastWasTodo = false;
|
||||
|
||||
/**
|
||||
* The "native" printer.
|
||||
*/
|
||||
public readonly Printer $printer;
|
||||
|
||||
/**
|
||||
* The state.
|
||||
*/
|
||||
public int $passedTests = 0;
|
||||
|
||||
/**
|
||||
* The "compact" printer.
|
||||
*/
|
||||
@ -56,6 +67,13 @@ final class ResultPrinter
|
||||
|
||||
public function print(string $buffer): void
|
||||
{
|
||||
$buffer = OutputFormatter::escape($buffer);
|
||||
if (str_starts_with($buffer, "\nGenerating code coverage report")) {
|
||||
return;
|
||||
}
|
||||
if (str_starts_with($buffer, 'done [')) {
|
||||
return;
|
||||
}
|
||||
$this->output->write(OutputFormatter::escape($buffer));
|
||||
}
|
||||
|
||||
@ -140,7 +158,7 @@ final class ResultPrinter
|
||||
return;
|
||||
}
|
||||
|
||||
$state = (new StateGenerator())->fromPhpUnitTestResult($testResult);
|
||||
$state = (new StateGenerator())->fromPhpUnitTestResult($this->passedTests, $testResult);
|
||||
|
||||
$this->compactPrinter->errors($state);
|
||||
$this->compactPrinter->recap($state, $testResult, $duration, $this->options);
|
||||
@ -148,6 +166,20 @@ final class ResultPrinter
|
||||
|
||||
private function printFeedbackItem(string $item): void
|
||||
{
|
||||
if ($this->lastWasTodo) {
|
||||
$this->lastWasTodo = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($item === 'T') {
|
||||
$this->lastWasTodo = true;
|
||||
}
|
||||
|
||||
if ($item === '.') {
|
||||
$this->passedTests++;
|
||||
}
|
||||
|
||||
$this->compactPrinter->descriptionItem($item);
|
||||
}
|
||||
|
||||
|
||||
@ -36,8 +36,9 @@ final class CompactPrinter
|
||||
'.' => ['gray', '.'],
|
||||
'S' => ['yellow', 's'],
|
||||
'T' => ['cyan', 't'],
|
||||
'I' => ['yellow', 'i'],
|
||||
'N' => ['yellow', 'i'],
|
||||
'I' => ['yellow', '!'],
|
||||
'N' => ['yellow', '!'],
|
||||
'D' => ['yellow', '!'],
|
||||
'R' => ['yellow', '!'],
|
||||
'W' => ['yellow', '!'],
|
||||
'E' => ['red', '⨯'],
|
||||
@ -105,7 +106,9 @@ final class CompactPrinter
|
||||
*/
|
||||
public function errors(State $state): void
|
||||
{
|
||||
$this->style->writeErrorsSummary($state, false);
|
||||
$this->output->writeln('');
|
||||
|
||||
$this->style->writeErrorsSummary($state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -6,15 +6,12 @@ namespace Pest\Support;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class HigherOrderTapProxy
|
||||
{
|
||||
private const UNDEFINED_PROPERTY = 'Undefined property: P\\'; // @phpstan-ignore-line
|
||||
|
||||
/**
|
||||
* Create a new tap proxy instance.
|
||||
*/
|
||||
@ -39,20 +36,19 @@ final class HigherOrderTapProxy
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
try {
|
||||
if (property_exists($this->target, $property)) {
|
||||
return $this->target->{$property}; // @phpstan-ignore-line
|
||||
} catch (Throwable $throwable) { // @phpstan-ignore-line
|
||||
Reflection::setPropertyValue($throwable, 'file', Backtrace::file());
|
||||
Reflection::setPropertyValue($throwable, 'line', Backtrace::line());
|
||||
|
||||
if (Str::startsWith($message = $throwable->getMessage(), self::UNDEFINED_PROPERTY)) {
|
||||
/** @var ReflectionClass $reflection */
|
||||
$reflection = (new ReflectionClass($this->target))->getParentClass();
|
||||
Reflection::setPropertyValue($throwable, 'message', sprintf('Undefined property %s::$%s', $reflection->getName(), $property));
|
||||
}
|
||||
|
||||
throw $throwable;
|
||||
}
|
||||
|
||||
$className = (new ReflectionClass($this->target))->getName();
|
||||
|
||||
if (str_starts_with($className, 'P\\')) {
|
||||
$className = substr($className, 2);
|
||||
}
|
||||
|
||||
trigger_error(sprintf('Undefined property %s::$%s', $className, $property), E_USER_WARNING);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -6,19 +6,19 @@ namespace Pest\Support;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\State;
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\TestResult;
|
||||
use NunoMaduro\Collision\Exceptions\TestOutcome;
|
||||
use PHPUnit\Event\Code\TestDox;
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\Code\Throwable;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\TestData\TestDataCollection;
|
||||
use PHPUnit\Framework\IncompleteTestError;
|
||||
use PHPUnit\Framework\SkippedWithMessageException;
|
||||
use PHPUnit\Metadata\MetadataCollection;
|
||||
use PHPUnit\TestRunner\TestResult\TestResult as PHPUnitTestResult;
|
||||
|
||||
final class StateGenerator
|
||||
{
|
||||
public function fromPhpUnitTestResult(PHPUnitTestResult $testResult): State
|
||||
public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testResult): State
|
||||
{
|
||||
$state = new State();
|
||||
|
||||
@ -55,7 +55,7 @@ final class StateGenerator
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$riskyEvent->test(),
|
||||
TestResult::RISKY,
|
||||
Throwable::from(new IncompleteTestError($riskyEvent->message()))
|
||||
Throwable::from(new TestOutcome($riskyEvent->message()))
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -74,16 +74,69 @@ final class StateGenerator
|
||||
));
|
||||
}
|
||||
|
||||
$numberOfPassedTests = $testResult->numberOfTestsRun()
|
||||
- $testResult->numberOfTestErroredEvents()
|
||||
- $testResult->numberOfTestFailedEvents()
|
||||
- $testResult->numberOfTestSkippedEvents()
|
||||
- $testResult->numberOfTestsWithTestConsideredRiskyEvents()
|
||||
- $testResult->numberOfTestMarkedIncompleteEvents();
|
||||
foreach ($testResult->testTriggeredDeprecationEvents() as $testResultEvent) {
|
||||
$testResultEvent = $testResultEvent[0];
|
||||
|
||||
for ($i = 0; $i < $numberOfPassedTests; $i++) {
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$testResultEvent->test(),
|
||||
TestResult::DEPRECATED,
|
||||
Throwable::from(new TestOutcome($testResultEvent->message()))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($testResult->testTriggeredPhpDeprecationEvents() as $testResultEvent) {
|
||||
$testResultEvent = $testResultEvent[0];
|
||||
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$testResultEvent->test(),
|
||||
TestResult::DEPRECATED,
|
||||
Throwable::from(new TestOutcome($testResultEvent->message()))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($testResult->testTriggeredNoticeEvents() as $testResultEvent) {
|
||||
$testResultEvent = $testResultEvent[0];
|
||||
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$testResultEvent->test(),
|
||||
TestResult::NOTICE,
|
||||
Throwable::from(new TestOutcome($testResultEvent->message()))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($testResult->testTriggeredPhpNoticeEvents() as $testResultEvent) {
|
||||
$testResultEvent = $testResultEvent[0];
|
||||
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$testResultEvent->test(),
|
||||
TestResult::NOTICE,
|
||||
Throwable::from(new TestOutcome($testResultEvent->message()))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($testResult->testTriggeredWarningEvents() as $testResultEvent) {
|
||||
$testResultEvent = $testResultEvent[0];
|
||||
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$testResultEvent->test(),
|
||||
TestResult::WARN,
|
||||
Throwable::from(new TestOutcome($testResultEvent->message()))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($testResult->testTriggeredPhpWarningEvents() as $testResultEvent) {
|
||||
$testResultEvent = $testResultEvent[0];
|
||||
|
||||
$state->add(TestResult::fromTestCase(
|
||||
$testResultEvent->test(),
|
||||
TestResult::WARN,
|
||||
Throwable::from(new TestOutcome($testResultEvent->message()))
|
||||
));
|
||||
}
|
||||
|
||||
// for each test that passed, we need to add it to the state
|
||||
for ($i = 0; $i < $passedTests; $i++) {
|
||||
$state->add(TestResult::fromTestCase(
|
||||
new TestMethod(
|
||||
/** @phpstan-ignore-next-line */
|
||||
"$i",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
##teamcity[testSuiteStarted name='Tests/tests/Failure' locationHint='file://tests/.tests/Failure.php' flowId='1234']
|
||||
##teamcity[testStarted name='it can fail with comparison' locationHint='pest_qn://tests/.tests/Failure.php::it can fail with comparison' flowId='1234']
|
||||
##teamcity[testFailed name='it can fail with comparison' message='Failed asserting that true matches expected false.' details='at src/Mixins/Expectation.php:342|nat src/Support/ExpectationPipeline.php:75|nat src/Support/ExpectationPipeline.php:79|nat src/Expectation.php:300|nat tests/.tests/Failure.php:6|nat src/Factories/TestCaseMethodFactory.php:100|nat src/Concerns/Testable.php:262|nat src/Support/ExceptionTrace.php:28|nat src/Concerns/Testable.php:262|nat src/Concerns/Testable.php:217|nat src/Kernel.php:91' type='comparisonFailure' actual='true' expected='false' flowId='1234']
|
||||
##teamcity[testFailed name='it can fail with comparison' message='Failed asserting that true matches expected false.' details='at src/Mixins/Expectation.php:342|nat src/Support/ExpectationPipeline.php:75|nat src/Support/ExpectationPipeline.php:79|nat src/Expectation.php:300|nat tests/.tests/Failure.php:6|nat src/Factories/TestCaseMethodFactory.php:100|nat src/Concerns/Testable.php:262|nat src/Support/ExceptionTrace.php:28|nat src/Concerns/Testable.php:262|nat src/Concerns/Testable.php:217|nat src/Kernel.php:90' type='comparisonFailure' actual='true' expected='false' flowId='1234']
|
||||
##teamcity[testFinished name='it can fail with comparison' duration='100000' flowId='1234']
|
||||
##teamcity[testStarted name='it can be ignored because of no assertions' locationHint='pest_qn://tests/.tests/Failure.php::it can be ignored because of no assertions' flowId='1234']
|
||||
##teamcity[testIgnored name='it can be ignored because of no assertions' message='This test did not perform any assertions' details='' flowId='1234']
|
||||
@ -9,7 +9,7 @@
|
||||
##teamcity[testIgnored name='it can be ignored because it is skipped' message='This test was ignored.' details='' flowId='1234']
|
||||
##teamcity[testFinished name='it can be ignored because it is skipped' duration='100000' flowId='1234']
|
||||
##teamcity[testStarted name='it can fail' locationHint='pest_qn://tests/.tests/Failure.php::it can fail' flowId='1234']
|
||||
##teamcity[testFailed name='it can fail' message='oh noo' details='at tests/.tests/Failure.php:18|nat src/Factories/TestCaseMethodFactory.php:100|nat src/Concerns/Testable.php:262|nat src/Support/ExceptionTrace.php:28|nat src/Concerns/Testable.php:262|nat src/Concerns/Testable.php:217|nat src/Kernel.php:91' flowId='1234']
|
||||
##teamcity[testFailed name='it can fail' message='oh noo' details='at tests/.tests/Failure.php:18|nat src/Factories/TestCaseMethodFactory.php:100|nat src/Concerns/Testable.php:262|nat src/Support/ExceptionTrace.php:28|nat src/Concerns/Testable.php:262|nat src/Concerns/Testable.php:217|nat src/Kernel.php:90' flowId='1234']
|
||||
##teamcity[testFinished name='it can fail' duration='100000' flowId='1234']
|
||||
##teamcity[testStarted name='it is not done yet' locationHint='pest_qn://tests/.tests/Failure.php::it is not done yet' flowId='1234']
|
||||
##teamcity[testIgnored name='it is not done yet' message='This test was ignored.' details='' flowId='1234']
|
||||
|
||||
@ -139,6 +139,10 @@
|
||||
✓ it is a test
|
||||
✓ it uses correct parent class
|
||||
|
||||
DEPR Tests\Features\Deprecated
|
||||
! deprecated → str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated
|
||||
! user deprecated → Since foo 1.0: This is a deprecation description
|
||||
|
||||
PASS Tests\Features\Exceptions
|
||||
✓ it gives access the the underlying expectException
|
||||
✓ it catch exceptions
|
||||
@ -646,9 +650,9 @@
|
||||
✓ it skips with falsy closure condition
|
||||
✓ it can be used in higher order tests
|
||||
|
||||
PASS Tests\Features\Helpers
|
||||
WARN Tests\Features\Helpers
|
||||
✓ it can set/get properties on $this
|
||||
✓ it throws error if property do not exist
|
||||
! it gets null if property do not exist → Undefined property Tests\Features\Helpers::$wqdwqdqw
|
||||
✓ it allows to call underlying protected/private methods
|
||||
✓ it throws error if method do not exist
|
||||
✓ it can forward unexpected calls to any global function
|
||||
@ -678,6 +682,9 @@
|
||||
✓ it is a test
|
||||
✓ it is a higher order message test
|
||||
|
||||
NOTI Tests\Features\Notices
|
||||
! notice → This is a notice description
|
||||
|
||||
PASS Tests\Features\PendingHigherOrderTests
|
||||
✓ get 'foo'
|
||||
✓ get 'foo' → get 'bar' → expect true → toBeTrue
|
||||
@ -750,6 +757,10 @@
|
||||
↓ something todo later chained and with function body
|
||||
✓ it does something within a file with a todo
|
||||
|
||||
WARN Tests\Features\Warnings
|
||||
! warning → Undefined property: P\Tests\Features\Warnings::$fooqwdfwqdfqw
|
||||
! user warning → This is a warning description
|
||||
|
||||
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
|
||||
✓ it example 1
|
||||
|
||||
@ -913,8 +924,9 @@
|
||||
|
||||
PASS Tests\Visual\Todo
|
||||
✓ todo
|
||||
✓ todo in parallel
|
||||
|
||||
PASS Tests\Visual\Version
|
||||
✓ visual snapshot of help command output
|
||||
|
||||
Tests: 4 incomplete, 4 todos, 18 skipped, 634 passed (1560 assertions)
|
||||
Tests: 2 deprecated, 3 warnings, 4 incomplete, 1 notice, 4 todos, 18 skipped, 634 passed (1567 assertions)
|
||||
@ -1,9 +1,5 @@
|
||||
<?php
|
||||
|
||||
if (class_exists(NunoMaduro\Collision\Provider::class)) {
|
||||
(new NunoMaduro\Collision\Provider())->register();
|
||||
}
|
||||
|
||||
trait PluginTrait
|
||||
{
|
||||
public function assertPluginTraitGotRegistered(): void
|
||||
|
||||
15
tests/Features/Deprecated.php
Normal file
15
tests/Features/Deprecated.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
test('deprecated', function () {
|
||||
str_contains(null, null);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('user deprecated', function () {
|
||||
trigger_deprecation('foo', '1.0', 'This is a deprecation description');
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@ -10,9 +10,9 @@ it('can set/get properties on $this', function () {
|
||||
expect($this->user)->toBe('nuno');
|
||||
});
|
||||
|
||||
it('throws error if property do not exist', function () {
|
||||
test()->user;
|
||||
})->throws(\Whoops\Exception\ErrorException::class, 'Undefined property PHPUnit\Framework\TestCase::$user');
|
||||
it('gets null if property do not exist', function () {
|
||||
expect(test()->wqdwqdqw)->toBe(null);
|
||||
});
|
||||
|
||||
class User
|
||||
{
|
||||
|
||||
7
tests/Features/Notices.php
Normal file
7
tests/Features/Notices.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
test('notice', function () {
|
||||
trigger_error('This is a notice description', E_USER_NOTICE);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
13
tests/Features/Warnings.php
Normal file
13
tests/Features/Warnings.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
test('warning', function () {
|
||||
$this->fooqwdfwqdfqw;
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('user warning', function () {
|
||||
trigger_error('This is a warning description', E_USER_WARNING);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@ -15,6 +15,6 @@ $run = function () {
|
||||
};
|
||||
|
||||
test('parallel', function () use ($run) {
|
||||
expect($run())->toContain('Tests: 4 incomplete, 4 todos, 15 skipped, 626 passed (1550 assertions)')
|
||||
expect($run())->toContain('Tests: 2 deprecated, 3 warnings, 4 incomplete, 1 notice, 4 todos, 15 skipped, 626 passed (1555 assertions)')
|
||||
->toContain('Parallel: 3 processes');
|
||||
});
|
||||
|
||||
@ -2,14 +2,16 @@
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
$run = function (string $target, $decorated = false) {
|
||||
$process = new Process(['php', 'bin/pest', $target, '--colors=always'], dirname(__DIR__, 2),
|
||||
$run = function (string $target, bool $parallel) {
|
||||
$process = new Process(['php', 'bin/pest', $target, $parallel ? '--parallel' : '', '--colors=always'], dirname(__DIR__, 2),
|
||||
['COLLISION_PRINTER' => 'DefaultPrinter', 'COLLISION_IGNORE_DURATION' => 'true'],
|
||||
);
|
||||
|
||||
$process->run();
|
||||
|
||||
return $decorated ? $process->getOutput() : preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput());
|
||||
expect($process->getExitCode())->toBe(0);
|
||||
|
||||
return preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput());
|
||||
};
|
||||
|
||||
$snapshot = function ($name) {
|
||||
@ -23,5 +25,9 @@ $snapshot = function ($name) {
|
||||
};
|
||||
|
||||
test('todo', function () use ($run, $snapshot) {
|
||||
expect($run('--todo'))->toContain($snapshot('todo'));
|
||||
expect($run('--todo', false))->toContain($snapshot('todo'));
|
||||
})->skip(PHP_OS_FAMILY === 'Windows');
|
||||
|
||||
test('todo in parallel', function () use ($run, $snapshot) {
|
||||
expect($run('--todo', true))->toContain($snapshot('todo'));
|
||||
})->skip(PHP_OS_FAMILY === 'Windows');
|
||||
|
||||
Reference in New Issue
Block a user