mirror of
https://github.com/pestphp/pest.git
synced 2026-07-24 02:20:03 +02:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55c622972b | |||
| 333f469efb | |||
| c4ec925aa5 | |||
| 5c8cfed483 | |||
| 962510b7e3 | |||
| e9c268a584 | |||
| fa91fcf744 | |||
| c6073511a2 | |||
| b5e1b83bb4 | |||
| 1c11595504 | |||
| cd0e921158 |
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
> Pest v5 Now Available: **[Read the announcement »](https://pestphp.com/docs/pest-v5-is-here)**.
|
||||
|
||||
**Pest** is an elegant PHP testing Framework with a focus on simplicity, meticulously designed to bring back the joy of testing in PHP.
|
||||
**Pest** is an elegant testing framework for PHP developers and AI agents.
|
||||
|
||||
- Explore our docs at **[pestphp.com »](https://pestphp.com)**
|
||||
- Follow the creator Nuno Maduro:
|
||||
@@ -29,26 +29,12 @@
|
||||
|
||||
We cannot thank our sponsors enough for their incredible support in funding Pest's development. Their contributions have been instrumental in making Pest the best it can be. For those who are interested in becoming a sponsor, please visit Nuno Maduro's Sponsor page at **[github.com/sponsors/nunomaduro](https://github.com/sponsors/nunomaduro)**.
|
||||
|
||||
|
||||
### Platinum Sponsors
|
||||
|
||||
- **[CodeRabbit](https://coderabbit.ai/?ref=pestphp)**
|
||||
- **[Mailtrap](https://l.rw.rw/pestphp)**
|
||||
- **[SerpApi](https://serpapi.com/?ref=nunomaduro)**
|
||||
- **[Tighten](https://tighten.com/?ref=nunomaduro)**
|
||||
- **[Redberry](https://redberry.international/laravel-development/?utm_source=pest&utm_medium=banner&utm_campaign=pest_sponsorship)**
|
||||
|
||||
### Gold Sponsors
|
||||
|
||||
- **[CMS Max](https://cmsmax.com/?ref=pestphp)**
|
||||
|
||||
### Premium Sponsors
|
||||
|
||||
- [Zapiet](https://zapiet.com/?ref=pestphp)
|
||||
- [Load Forge](https://loadforge.com/?ref=pestphp)
|
||||
- [Route4Me](https://route4me.com/pt?ref=pestphp)
|
||||
- [Nerdify](https://getnerdify.com/?ref=pestphp)
|
||||
- [Akaunting](https://akaunting.com/?ref=pestphp)
|
||||
- [TestMu AI](https://www.testmuai.com/?utm_medium=sponsor&utm_source=pest)
|
||||
- **[PhpStorm](https://jb.gg/nuno)**
|
||||
- **[CodeRabbit](https://coderabbit.link/nunomaduro)**
|
||||
- **[SerpApi](https://serpapi.com/?ref=nunomaduro)**
|
||||
- **[Typesense](https://typesense.org/?ref=nunomaduro)**
|
||||
- **[Bento](https://bentonow.com/?ref=nunomaduro)**
|
||||
- **[Redberry](https://redberry.international/laravel-development/)**
|
||||
|
||||
Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2011 Brian Scaturro
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ParaTest\WrapperRunner;
|
||||
|
||||
use PHPUnit\TextUI\Output\Printer;
|
||||
|
||||
use function preg_match;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* This file is overridden so the "T" progress character — emitted by Pest for
|
||||
* "todo" tests — is routed to the progress file next to the regular characters,
|
||||
* instead of being treated as unexpected output.
|
||||
*/
|
||||
final readonly class ProgressPrinterOutput implements Printer
|
||||
{
|
||||
public function __construct(
|
||||
private Printer $progressPrinter,
|
||||
private Printer $outputPrinter,
|
||||
) {}
|
||||
|
||||
public function print(string $buffer): void
|
||||
{
|
||||
// Skip anything in \PHPUnit\TextUI\Output\Default\ProgressPrinter\ProgressPrinter::printProgress except $progress
|
||||
if (
|
||||
$buffer === "\n"
|
||||
|| preg_match('/^ +$/', $buffer) === 1
|
||||
|| preg_match('/^ \d+ \/ \d+ \(...%\)$/', $buffer) === 1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
match ($buffer) {
|
||||
'E', 'F', 'I', 'N', 'D', 'R', 'W', 'S', 'T', '.' => $this->progressPrinter->print($buffer),
|
||||
default => $this->outputPrinter->print($buffer),
|
||||
};
|
||||
}
|
||||
|
||||
public function flush(): void
|
||||
{
|
||||
$this->progressPrinter->flush();
|
||||
$this->outputPrinter->flush();
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -43,7 +43,7 @@ declare(strict_types=1);
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Pest\Logging\TeamCity\Subscriber;
|
||||
namespace PHPUnit\TextUI\Output\Default\ProgressPrinter;
|
||||
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\Test\SkippedSubscriber;
|
||||
@@ -51,16 +51,20 @@ use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*
|
||||
* This file is overridden so PHPUnit's progress output emits a "T" before the
|
||||
* regular "S" for "todo" tests — Pest's parallel result printer consumes the
|
||||
* "T" and swallows the "S" that follows it.
|
||||
*/
|
||||
final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber
|
||||
final readonly class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber
|
||||
{
|
||||
public function notify(Skipped $event): void
|
||||
{
|
||||
if (str_contains($event->message(), '__TODO__')) {
|
||||
if ($event->message() === '__TODO__') {
|
||||
$this->printTodoItem();
|
||||
}
|
||||
|
||||
$this->logger()->testSkipped($event);
|
||||
$this->printer()->testSkipped();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +73,7 @@ final class TestSkippedSubscriber extends Subscriber implements SkippedSubscribe
|
||||
private function printTodoItem(): void
|
||||
{
|
||||
$mirror = new ReflectionClass($this->printer());
|
||||
$printerMirror = $mirror->getMethod('printProgress');
|
||||
$printerMirror->invoke($this->printer(), 'T');
|
||||
$printProgress = $mirror->getMethod('printProgress');
|
||||
$printProgress->invoke($this->printer(), 'T');
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -23,8 +23,7 @@ return RectorConfig::configure()
|
||||
__DIR__.'/tests',
|
||||
])
|
||||
->withSets([
|
||||
PestSetList::PEST_CODE_QUALITY,
|
||||
PestSetList::PEST_CHAIN,
|
||||
PestSetList::CODING_STYLE,
|
||||
])
|
||||
->withSkip([
|
||||
__DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php',
|
||||
|
||||
@@ -18,6 +18,7 @@ final class BootOverrides implements Bootstrapper
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public const array FILES = [
|
||||
'ParaTest/WrapperRunner/ProgressPrinterOutput.php',
|
||||
'Runner/Filter/NameFilterIterator.php',
|
||||
'Runner/ResultCache/DefaultResultCache.php',
|
||||
'Runner/TestSuiteLoader.php',
|
||||
|
||||
+21
-22
@@ -15,11 +15,6 @@ use PHPUnit\Event\Test\AfterLastTestMethodErrored;
|
||||
use PHPUnit\Event\Test\AfterLastTestMethodFailed;
|
||||
use PHPUnit\Event\Test\BeforeFirstTestMethodErrored;
|
||||
use PHPUnit\Event\Test\BeforeFirstTestMethodFailed;
|
||||
use PHPUnit\Event\Test\ConsideredRisky;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\MarkedIncomplete;
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\TestSuite\TestSuite;
|
||||
use PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider;
|
||||
use PHPUnit\Framework\Exception as FrameworkException;
|
||||
@@ -254,25 +249,29 @@ final readonly class Converter
|
||||
...$result->testMarkedIncompleteEvents(),
|
||||
];
|
||||
|
||||
$numberOfNotPassedTests = count(
|
||||
array_unique(
|
||||
array_map(
|
||||
function (AfterLastTestMethodErrored|AfterLastTestMethodFailed|BeforeFirstTestMethodErrored|BeforeFirstTestMethodFailed|Errored|Failed|Skipped|ConsideredRisky|MarkedIncomplete $event): string {
|
||||
if ($event instanceof BeforeFirstTestMethodErrored
|
||||
|| $event instanceof AfterLastTestMethodErrored
|
||||
|| $event instanceof BeforeFirstTestMethodFailed
|
||||
|| $event instanceof AfterLastTestMethodFailed) {
|
||||
return $event->testClassName();
|
||||
}
|
||||
$notPassedTests = [];
|
||||
|
||||
return $this->getTestCaseLocation($event->test());
|
||||
},
|
||||
$events
|
||||
)
|
||||
)
|
||||
);
|
||||
foreach ($events as $event) {
|
||||
if ($event instanceof AfterLastTestMethodErrored) {
|
||||
// PHPUnit's collector does not count these towards `numberOfTestsRun`...
|
||||
continue;
|
||||
}
|
||||
if ($event instanceof AfterLastTestMethodFailed) {
|
||||
// PHPUnit's collector does not count these towards `numberOfTestsRun`...
|
||||
continue;
|
||||
}
|
||||
if ($event instanceof BeforeFirstTestMethodErrored || $event instanceof BeforeFirstTestMethodFailed) {
|
||||
$notPassedTests[] = $event->testClassName();
|
||||
|
||||
$numberOfPassedTests = $result->numberOfTestsRun() - $numberOfNotPassedTests;
|
||||
continue;
|
||||
}
|
||||
|
||||
$notPassedTests[] = $this->getTestCaseLocation($event->test());
|
||||
}
|
||||
|
||||
$numberOfPassedTests = $result->numberOfTestsRun()
|
||||
- count(array_unique($notPassedTests))
|
||||
- $result->numberOfTestSkippedByTestSuiteSkippedEvents();
|
||||
|
||||
return $this->stateGenerator->fromPhpUnitTestResult($numberOfPassedTests, $result);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ final class TestSkippedSubscriber extends Subscriber implements SkippedSubscribe
|
||||
{
|
||||
public function notify(Skipped $event): void
|
||||
{
|
||||
if ($event->message() === '__TODO__') {
|
||||
return; // "todo" tests are reported in the summary, not as ignored tests...
|
||||
}
|
||||
|
||||
$this->logger()->testSkipped($event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,4 +1219,104 @@ final class Expectation
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is an IP address.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function toBeIpAddress(string $message = ''): self
|
||||
{
|
||||
if (! is_string($this->value)) {
|
||||
InvalidExpectationValue::expected('string');
|
||||
}
|
||||
|
||||
Assert::assertTrue((bool) filter_var($this->value, FILTER_VALIDATE_IP), $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a MAC address.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function toBeMacAddress(string $message = ''): self
|
||||
{
|
||||
if (! is_string($this->value)) {
|
||||
InvalidExpectationValue::expected('string');
|
||||
}
|
||||
|
||||
Assert::assertTrue((bool) filter_var($this->value, FILTER_VALIDATE_MAC), $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a hostname.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function toBeHostname(string $message = ''): self
|
||||
{
|
||||
if (! is_string($this->value)) {
|
||||
InvalidExpectationValue::expected('string');
|
||||
}
|
||||
|
||||
Assert::assertTrue((bool) filter_var($this->value, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME), $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a domain name.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function toBeDomain(string $message = ''): self
|
||||
{
|
||||
if (! is_string($this->value)) {
|
||||
InvalidExpectationValue::expected('string');
|
||||
}
|
||||
|
||||
$isValid = filter_var($this->value, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false
|
||||
&& str_contains($this->value, '.');
|
||||
|
||||
Assert::assertTrue($isValid, $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a base64-encoded string.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function toBeBase64(string $message = ''): self
|
||||
{
|
||||
if (! is_string($this->value)) {
|
||||
InvalidExpectationValue::expected('string');
|
||||
}
|
||||
|
||||
$decoded = base64_decode($this->value, true);
|
||||
Assert::assertTrue($decoded !== false && base64_encode($decoded) === $this->value, $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a hexadecimal string.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function toBeHexadecimal(string $message = ''): self
|
||||
{
|
||||
if (! is_string($this->value)) {
|
||||
InvalidExpectationValue::expected('string');
|
||||
}
|
||||
|
||||
Assert::assertTrue(ctype_xdigit($this->value), $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,10 +120,6 @@ final class ResultPrinter
|
||||
|
||||
$unexpectedOutput = $this->tail($outputFile);
|
||||
if ($unexpectedOutput !== '') {
|
||||
if (preg_match('/^T+$/', $unexpectedOutput) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->write($unexpectedOutput);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use ParaTest\JUnit\LogMerger;
|
||||
use ParaTest\JUnit\Writer;
|
||||
use ParaTest\Options;
|
||||
use ParaTest\RunnerInterface;
|
||||
use ParaTest\WrapperRunner\MissingResultsException;
|
||||
use ParaTest\WrapperRunner\SuiteLoader;
|
||||
use ParaTest\WrapperRunner\WrapperWorker;
|
||||
use Pest\Result;
|
||||
@@ -42,6 +43,8 @@ use function assert;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_get_contents;
|
||||
use function filesize;
|
||||
use function is_file;
|
||||
use function max;
|
||||
use function realpath;
|
||||
use function str_starts_with;
|
||||
@@ -54,14 +57,14 @@ use function usleep;
|
||||
*/
|
||||
final class WrapperRunner implements RunnerInterface
|
||||
{
|
||||
/**
|
||||
* The time to sleep between cycles.
|
||||
*/
|
||||
/**
|
||||
* The merged test result from the parallel run.
|
||||
*/
|
||||
public static ?TestResult $result = null;
|
||||
|
||||
/**
|
||||
* The time to sleep between cycles.
|
||||
*/
|
||||
private const int CYCLE_SLEEP = 10000;
|
||||
|
||||
/**
|
||||
@@ -88,6 +91,18 @@ final class WrapperRunner implements RunnerInterface
|
||||
/** @var array<int,int> */
|
||||
private array $batches = [];
|
||||
|
||||
/** @var array<non-empty-string,true> */
|
||||
private array $requiredTestResultFiles = [];
|
||||
|
||||
/** @var array<non-empty-string,true> */
|
||||
private array $requiredCoverageFiles = [];
|
||||
|
||||
/** @var list<SplFileInfo> */
|
||||
private array $statusFiles = [];
|
||||
|
||||
/** @var list<SplFileInfo> */
|
||||
private array $progressFiles = [];
|
||||
|
||||
/** @var list<SplFileInfo> */
|
||||
private array $unexpectedOutputFiles = [];
|
||||
|
||||
@@ -217,7 +232,7 @@ final class WrapperRunner implements RunnerInterface
|
||||
|
||||
if (
|
||||
$this->exitcode > 0
|
||||
&& $this->options->configuration->stopOnFailure()
|
||||
&& $this->options->configuration->stopOnFailureThreshold() > 0
|
||||
) {
|
||||
$this->pending = [];
|
||||
} elseif (($pending = array_shift($this->pending)) !== null) {
|
||||
@@ -232,6 +247,18 @@ final class WrapperRunner implements RunnerInterface
|
||||
|
||||
private function flushWorker(WrapperWorker $worker): void
|
||||
{
|
||||
if ($worker->hasExecutedTests()) {
|
||||
$testResultFile = $worker->testResultFile->getPathname();
|
||||
|
||||
if ($testResultFile !== '') {
|
||||
$this->requiredTestResultFiles[$testResultFile] = true;
|
||||
}
|
||||
|
||||
if (isset($worker->coverageFile) && $worker->coverageFile->getPathname() !== '') {
|
||||
$this->requiredCoverageFiles[$worker->coverageFile->getPathname()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->exitcode = max($this->exitcode, $worker->getExitCode());
|
||||
$this->printer->printFeedback(
|
||||
$worker->progressFile,
|
||||
@@ -279,10 +306,15 @@ final class WrapperRunner implements RunnerInterface
|
||||
$worker->start();
|
||||
$this->batches[$token] = 0;
|
||||
|
||||
$this->unexpectedOutputFiles[] = $worker->unexpectedOutputFile;
|
||||
$this->statusFiles[] = $worker->statusFile;
|
||||
$this->progressFiles[] = $worker->progressFile;
|
||||
$this->unexpectedOutputFiles[] = $worker->unexpectedOutputFile;
|
||||
$this->testResultFiles[] = $worker->testResultFile;
|
||||
|
||||
if (isset($worker->resultCacheFile)) {
|
||||
$this->resultCacheFiles[] = $worker->resultCacheFile;
|
||||
}
|
||||
|
||||
if (isset($worker->junitFile)) {
|
||||
$this->junitFiles[] = $worker->junitFile;
|
||||
}
|
||||
@@ -315,6 +347,20 @@ final class WrapperRunner implements RunnerInterface
|
||||
|
||||
private function complete(TestResult $testResultSum): int
|
||||
{
|
||||
$missingTestResultFiles = [];
|
||||
|
||||
foreach ($this->requiredTestResultFiles as $filePath => $true) {
|
||||
if (is_file($filePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$missingTestResultFiles[] = $filePath;
|
||||
}
|
||||
|
||||
if ($missingTestResultFiles !== []) {
|
||||
throw MissingResultsException::create($missingTestResultFiles, 'test_result');
|
||||
}
|
||||
|
||||
foreach ($this->testResultFiles as $testResultFile) {
|
||||
if (! $testResultFile->isFile()) {
|
||||
continue;
|
||||
@@ -398,7 +444,7 @@ final class WrapperRunner implements RunnerInterface
|
||||
$testResultSum->testRunnerTriggeredNoticeEvents(),
|
||||
array_values(array_filter(
|
||||
$testResultSum->testRunnerTriggeredWarningEvents(),
|
||||
fn (WarningTriggered $event): bool => ! str_contains($event->message(), 'No tests found')
|
||||
fn (WarningTriggered $event): bool => ! str_contains($event->message(), 'No tests found in class')
|
||||
)),
|
||||
$testResultSum->testRunnerTriggeredIssueDeprecationEvents(),
|
||||
$testResultSum->testRunnerTriggeredIssueErrorEvents(),
|
||||
@@ -442,8 +488,11 @@ final class WrapperRunner implements RunnerInterface
|
||||
|
||||
$exitcode = Result::exitCode($this->options->configuration, $testResultSum);
|
||||
|
||||
$this->clearFiles($this->statusFiles);
|
||||
$this->clearFiles($this->progressFiles);
|
||||
$this->clearFiles($this->unexpectedOutputFiles);
|
||||
$this->clearFiles($this->testResultFiles);
|
||||
$this->clearFiles($this->resultCacheFiles);
|
||||
$this->clearFiles($this->coverageFiles);
|
||||
$this->clearFiles($this->junitFiles);
|
||||
$this->clearFiles($this->teamcityFiles);
|
||||
@@ -458,6 +507,20 @@ final class WrapperRunner implements RunnerInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$missingCoverageFiles = [];
|
||||
|
||||
foreach ($this->requiredCoverageFiles as $filePath => $true) {
|
||||
if (is_file($filePath) && filesize($filePath) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$missingCoverageFiles[] = $filePath;
|
||||
}
|
||||
|
||||
if ($missingCoverageFiles !== []) {
|
||||
throw MissingResultsException::create($missingCoverageFiles, 'coverage');
|
||||
}
|
||||
|
||||
$coverageManager = new CodeCoverage;
|
||||
$coverageManager->init(
|
||||
$this->options->configuration,
|
||||
|
||||
@@ -8,6 +8,9 @@ use Pest\Plugins\Tia;
|
||||
use Pest\Plugins\Tia\Contracts\State;
|
||||
use Pest\Support\Container;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\Selector;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\Serialization\Unserializer;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@@ -161,7 +164,60 @@ final class CoverageMerger
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value instanceof CodeCoverage ? $value : null;
|
||||
// Legacy `--coverage-php` format: a serialized `CodeCoverage` object.
|
||||
if ($value instanceof CodeCoverage) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Since phpunit/php-code-coverage 14, `--coverage-php` writes the report
|
||||
// as a serialized array (`['codeCoverage' => ..., 'testResults' => ...,
|
||||
// 'basePath' => ...]`) rather than a `CodeCoverage` object, so it has to
|
||||
// be rebuilt into one before it can be merged.
|
||||
return self::coverageFromSerializedData($reportPath);
|
||||
}
|
||||
|
||||
private static function coverageFromSerializedData(string $reportPath): ?CodeCoverage
|
||||
{
|
||||
if ($reportPath === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$serialized = new Unserializer()->unserialize($reportPath);
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $serialized['codeCoverage'];
|
||||
$basePath = $serialized['basePath'];
|
||||
|
||||
if ($basePath !== '') {
|
||||
foreach ($data->coveredFiles() as $relativePath) {
|
||||
$data->renameFile($relativePath, $basePath.DIRECTORY_SEPARATOR.$relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
$coverage = self::emptyCoverage();
|
||||
|
||||
if (! $coverage instanceof CodeCoverage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$coverage->setData($data);
|
||||
$coverage->setTests($serialized['testResults']);
|
||||
|
||||
return $coverage;
|
||||
}
|
||||
|
||||
private static function emptyCoverage(): ?CodeCoverage
|
||||
{
|
||||
try {
|
||||
$filter = new Filter;
|
||||
|
||||
return new CodeCoverage(new Selector()->forLineCoverage($filter), $filter);
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function unserializeCoverage(string $bytes): ?CodeCoverage
|
||||
|
||||
@@ -10,6 +10,10 @@ use NunoMaduro\Collision\Exceptions\TestOutcome;
|
||||
use PHPUnit\Event\Code\TestDoxBuilder;
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\Code\ThrowableBuilder;
|
||||
use PHPUnit\Event\Test\AfterLastTestMethodErrored;
|
||||
use PHPUnit\Event\Test\AfterLastTestMethodFailed;
|
||||
use PHPUnit\Event\Test\BeforeFirstTestMethodErrored;
|
||||
use PHPUnit\Event\Test\BeforeFirstTestMethodFailed;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\PhpunitDeprecationTriggered;
|
||||
@@ -35,8 +39,7 @@ final class StateGenerator
|
||||
$testResultEvent->throwable()
|
||||
));
|
||||
} else {
|
||||
// @phpstan-ignore-next-line
|
||||
$state->add(TestResult::fromBeforeFirstTestMethodErrored($testResultEvent));
|
||||
$this->addClassLevelEvent($state, $testResultEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +51,7 @@ final class StateGenerator
|
||||
$testResultEvent->throwable()
|
||||
));
|
||||
} else {
|
||||
// @phpstan-ignore-next-line
|
||||
$state->add(TestResult::fromBeforeFirstTestMethodErrored($testResultEvent));
|
||||
$this->addClassLevelEvent($state, $testResultEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +186,36 @@ final class StateGenerator
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given class-level "hook" failure to the state. Collision's
|
||||
* `fromBeforeFirstTestMethodErrored` only accepts `BeforeFirstTestMethodErrored`
|
||||
* events, so the remaining class-level events get a synthesized test method.
|
||||
*/
|
||||
private function addClassLevelEvent(State $state, AfterLastTestMethodErrored|AfterLastTestMethodFailed|BeforeFirstTestMethodErrored|BeforeFirstTestMethodFailed $event): void
|
||||
{
|
||||
if ($event instanceof BeforeFirstTestMethodErrored) {
|
||||
$state->add(TestResult::fromBeforeFirstTestMethodErrored($event));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$methodName = $event instanceof BeforeFirstTestMethodFailed ? 'beforeAll' : 'afterAll';
|
||||
|
||||
$state->add(TestResult::fromPestParallelTestCase(
|
||||
new TestMethod(
|
||||
$event->testClassName(), // @phpstan-ignore-line
|
||||
$methodName,
|
||||
'', // @phpstan-ignore-line
|
||||
1,
|
||||
TestDoxBuilder::fromClassNameAndMethodName($event->testClassName(), $methodName), // @phpstan-ignore-line
|
||||
MetadataCollection::fromArray([]),
|
||||
TestDataCollection::fromArray([])
|
||||
),
|
||||
TestResult::FAIL,
|
||||
$event->throwable()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, list<PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitNoticeTriggered|PhpunitWarningTriggered>> $testResultEvents
|
||||
*/
|
||||
|
||||
@@ -476,6 +476,16 @@
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeBase64
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with invalid padding
|
||||
✓ failures with padding only
|
||||
✓ failures with malformed input
|
||||
✓ failures with invalid type
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeBetween
|
||||
@@ -522,6 +532,14 @@
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeDomain
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with leading dot
|
||||
✓ failures with invalid type
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeEmail
|
||||
@@ -584,6 +602,24 @@
|
||||
✓ passes with strings
|
||||
✓ failures
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeHexadecimal
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with invalid characters
|
||||
✓ failures with zero-x prefix
|
||||
✓ failures with empty string
|
||||
✓ failures with invalid type
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeHostname
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with trailing hyphen
|
||||
✓ failures with invalid type
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeIn
|
||||
@@ -622,6 +658,13 @@
|
||||
✓ failure when the class is not invokable
|
||||
✓ class is not invokable
|
||||
|
||||
PASS Tests\Features\Expect\toBeIpAddress
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with invalid type
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeIterable
|
||||
✓ pass
|
||||
✓ failures
|
||||
@@ -666,6 +709,13 @@
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeMacAddress
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ failures with invalid type
|
||||
✓ failures with custom message
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toBeNAN
|
||||
@@ -2136,4 +2186,4 @@
|
||||
✓ pass with dataset with ('my-datas-set-value')
|
||||
✓ within describe → pass with dataset with ('my-datas-set-value')
|
||||
|
||||
Tests: 1 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1504 passed (3281 assertions)
|
||||
Tests: 1 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1542 passed (3375 assertions)
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Expectation;
|
||||
use Pest\Plugins\Tia\BaselineSync;
|
||||
|
||||
|
||||
+4
-2
@@ -1,12 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Plugin;
|
||||
|
||||
trait PluginTrait
|
||||
{
|
||||
public function assertPluginTraitGotRegistered(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +16,7 @@ trait SecondPluginTrait
|
||||
{
|
||||
public function assertSecondPluginTraitGotRegistered(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$file = __DIR__.DIRECTORY_SEPARATOR.'after-all-test';
|
||||
|
||||
beforeAll(function () use ($file): void {
|
||||
@@ -12,7 +14,7 @@ afterAll(function () use ($file): void {
|
||||
|
||||
test('deletes file after all', function () use ($file): void {
|
||||
file_put_contents($file, 'foo');
|
||||
$this->assertFileExists($file);
|
||||
expect($file)->toBeFile();
|
||||
register_shutdown_function(function (): void {
|
||||
// $this->assertFileDoesNotExist($file);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
beforeEach()->expect(true)->toBeTrue();
|
||||
|
||||
test('runs 1', function (): void {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
beforeEach()->skip();
|
||||
|
||||
test('does not run 1', function (): void {
|
||||
|
||||
@@ -10,17 +10,12 @@ beforeEach(function (): void {
|
||||
});
|
||||
|
||||
it('throws exception if dataset does not exist', function (): void {
|
||||
$this->expectException(DatasetDoesNotExist::class);
|
||||
$this->expectExceptionMessage("A dataset with the name `first` does not exist. You can create it using `dataset('first', ['a', 'b']);`.");
|
||||
|
||||
DatasetsRepository::resolve(['first'], __FILE__);
|
||||
expect(fn () => DatasetsRepository::resolve(['first'], __FILE__))->toThrow(DatasetDoesNotExist::class, "A dataset with the name `first` does not exist. You can create it using `dataset('first', ['a', 'b']);`.");
|
||||
});
|
||||
|
||||
it('throws exception if dataset already exist', function (): void {
|
||||
DatasetsRepository::set('second', [[]], __DIR__);
|
||||
$this->expectException(DatasetAlreadyExists::class);
|
||||
$this->expectExceptionMessage('A dataset with the name `second` already exists in scope ['.__DIR__.'].');
|
||||
DatasetsRepository::set('second', [[]], __DIR__);
|
||||
expect(fn () => DatasetsRepository::set('second', [[]], __DIR__))->toThrow(DatasetAlreadyExists::class, 'A dataset with the name `second` already exists in scope ['.__DIR__.'].');
|
||||
});
|
||||
|
||||
it('sets closures', function (): void {
|
||||
@@ -38,7 +33,7 @@ it('sets arrays', function (): void {
|
||||
});
|
||||
|
||||
it('gets bound to test case object', function ($value): void {
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
})->with([['a'], ['b']]);
|
||||
|
||||
test('it truncates the description', function (): void {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<?php
|
||||
|
||||
it('gives access the the underlying expectException', function (): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
throw new InvalidArgumentException;
|
||||
expect(fn () => throw new InvalidArgumentException)->toThrow(InvalidArgumentException::class);
|
||||
});
|
||||
|
||||
it('catch exceptions', function (): void {
|
||||
@@ -27,7 +25,7 @@ it('can just define the code', function (): void {
|
||||
})->throws(1);
|
||||
|
||||
it('not catch exceptions if given condition is false', function (): void {
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
})->throwsIf(false, Exception::class);
|
||||
|
||||
it('catch exceptions if given condition is true', function (): void {
|
||||
@@ -59,7 +57,7 @@ it('can just define the code if given condition is 1', function (): void {
|
||||
})->throwsIf(1, 1);
|
||||
|
||||
it('not catch exceptions if given condition is true', function (): void {
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
})->throwsUnless(true, Exception::class);
|
||||
|
||||
it('catch exceptions if given condition is false', function (): void {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Exceptions\InvalidExpectationValue;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function (): void {
|
||||
expect('Zm9v')->toBeBase64() // 'foo' (no padding)
|
||||
->and('Zm9vYg==')->toBeBase64() // 'foob' (with padding)
|
||||
->and('Zm9vYmE=')->toBeBase64() // 'fooba' (with padding)
|
||||
->and('Zm9vYmFy')->toBeBase64() // 'foobar' (no padding)
|
||||
->and('')->toBeBase64(); // empty string
|
||||
});
|
||||
|
||||
test('failures', function (): void {
|
||||
expect('not-base64!')->toBeBase64();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid padding', function (): void {
|
||||
expect('AAA')->toBeBase64();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with padding only', function (): void {
|
||||
expect('====')->toBeBase64();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with malformed input', function (): void {
|
||||
expect('!!invalid!!')->toBeBase64();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid type', function (): void {
|
||||
expect([])->toBeBase64();
|
||||
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||
|
||||
test('failures with custom message', function (): void {
|
||||
expect('!!invalid!!')->toBeBase64('oh no!');
|
||||
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||
|
||||
test('not failures', function (): void {
|
||||
expect('Zm9v')->not->toBeBase64();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Exceptions\InvalidExpectationValue;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function (): void {
|
||||
expect('example.com')->toBeDomain() // standard domain
|
||||
->and('sub.example.com')->toBeDomain() // subdomain
|
||||
->and('my-host.io')->toBeDomain() // with hyphen
|
||||
->and('example.co.uk')->toBeDomain(); // multi-level TLD
|
||||
});
|
||||
|
||||
test('failures', function (): void {
|
||||
expect('example')->toBeDomain();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with leading dot', function (): void {
|
||||
expect('.example.com')->toBeDomain();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid type', function (): void {
|
||||
expect([])->toBeDomain();
|
||||
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||
|
||||
test('failures with custom message', function (): void {
|
||||
expect('example')->toBeDomain('oh no!');
|
||||
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||
|
||||
test('not failures', function (): void {
|
||||
expect('example.com')->not->toBeDomain();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Exceptions\InvalidExpectationValue;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function (): void {
|
||||
expect('abcdef')->toBeHexadecimal() // lowercase
|
||||
->and('ABCDEF')->toBeHexadecimal() // uppercase
|
||||
->and('aBcDeF')->toBeHexadecimal() // mixed case
|
||||
->and('1234567890')->toBeHexadecimal() // numeric
|
||||
->and('deadbeef')->toBeHexadecimal(); // alphanumeric
|
||||
});
|
||||
|
||||
test('failures', function (): void {
|
||||
expect('xyz')->toBeHexadecimal();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid characters', function (): void {
|
||||
expect('ghijkl')->toBeHexadecimal();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with zero-x prefix', function (): void {
|
||||
expect('0x1a')->toBeHexadecimal();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with empty string', function (): void {
|
||||
expect('')->toBeHexadecimal();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid type', function (): void {
|
||||
expect([])->toBeHexadecimal();
|
||||
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||
|
||||
test('failures with custom message', function (): void {
|
||||
expect('xyz')->toBeHexadecimal('oh no!');
|
||||
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||
|
||||
test('not failures', function (): void {
|
||||
expect('abcdef')->not->toBeHexadecimal();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Exceptions\InvalidExpectationValue;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function (): void {
|
||||
expect('example')->toBeHostname() // single label
|
||||
->and('example.com')->toBeHostname() // multiple labels
|
||||
->and('sub.example.com')->toBeHostname() // subdomain
|
||||
->and('my-host')->toBeHostname(); // with hyphen
|
||||
});
|
||||
|
||||
test('failures', function (): void {
|
||||
expect('-example')->toBeHostname();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with trailing hyphen', function (): void {
|
||||
expect('example-')->toBeHostname();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid type', function (): void {
|
||||
expect([])->toBeHostname();
|
||||
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||
|
||||
test('failures with custom message', function (): void {
|
||||
expect('-example')->toBeHostname('oh no!');
|
||||
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||
|
||||
test('not failures', function (): void {
|
||||
expect('example.com')->not->toBeHostname();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
test('enum is backed by int')
|
||||
->expect('Tests\Fixtures\Arch\ToBeIntBackedEnum\HasIntBacking')
|
||||
->toBeIntBackedEnum();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Exceptions\InvalidExpectationValue;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function (): void {
|
||||
expect('192.168.1.1')->toBeIpAddress()
|
||||
->and('::1')->toBeIpAddress()
|
||||
->and('2001:db8::1')->toBeIpAddress();
|
||||
});
|
||||
|
||||
test('failures', function (): void {
|
||||
expect('not-an-ip')->toBeIpAddress();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid type', function (): void {
|
||||
expect([])->toBeIpAddress();
|
||||
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||
|
||||
test('failures with custom message', function (): void {
|
||||
expect('not-an-ip')->toBeIpAddress('oh no!');
|
||||
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||
|
||||
test('not failures', function (): void {
|
||||
expect('192.168.1.1')->not->toBeIpAddress();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Exceptions\InvalidExpectationValue;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function (): void {
|
||||
expect('00:1a:2b:3c:4d:5e')->toBeMacAddress() // colon-separated
|
||||
->and('00-1a-2b-3c-4d-5e')->toBeMacAddress() // hyphen-separated
|
||||
->and('ff:ff:ff:ff:ff:ff')->toBeMacAddress();
|
||||
});
|
||||
|
||||
test('failures', function (): void {
|
||||
expect('not-a-mac')->toBeMacAddress();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('failures with invalid type', function (): void {
|
||||
expect([])->toBeMacAddress();
|
||||
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||
|
||||
test('failures with custom message', function (): void {
|
||||
expect('not-a-mac')->toBeMacAddress('oh no!');
|
||||
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||
|
||||
test('not failures', function (): void {
|
||||
expect('00:1a:2b:3c:4d:5e')->not->toBeMacAddress();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
test('enum is backed by string')
|
||||
->expect('Tests\Fixtures\Arch\ToBeStringBackedEnum\HasStringBacking')
|
||||
->toBeStringBackedEnum();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Arch\Exceptions\ArchExpectationFailedException;
|
||||
use Tests\Fixtures\Arch\ToHaveAttribute\Attributes\AsAttribute;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
$test_array = [
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Arch\Exceptions\ArchExpectationFailedException;
|
||||
|
||||
test('missing prefix')
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Arch\Exceptions\ArchExpectationFailedException;
|
||||
|
||||
test('missing suffix')
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
|
||||
it('may fail', function (): void {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
it('may fail', function (): void {
|
||||
$this->fail();
|
||||
})->fails();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
test('a test', function (): void {
|
||||
$this->assertArrayHasKey('key', ['key' => 'foo']);
|
||||
expect(['key' => 'foo'])->toHaveKey('key');
|
||||
});
|
||||
|
||||
test('higher order message test')->expect(true)->toBeTrue();
|
||||
|
||||
@@ -11,6 +11,6 @@ class ExampleTest extends Base\ExampleTest
|
||||
#[\Override]
|
||||
public function test_example(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ class MyCustomClassTest extends TestCase
|
||||
{
|
||||
public function assertTrueIsTrue(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,6 +10,6 @@ class CustomTestCaseInSubFolder extends TestCase
|
||||
{
|
||||
public function assertCustomInSubFolderTrue(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ abstract class MyCustomClass extends TestCase
|
||||
{
|
||||
public function assertTrueIsTrue(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Plugins\Tia\ContentHash;
|
||||
|
||||
describe('of()', function (): void {
|
||||
|
||||
@@ -24,13 +24,13 @@ test('parallel', function () use ($run): void {
|
||||
$file = file_get_contents(__FILE__);
|
||||
$file = preg_replace(
|
||||
'/\$expected = \'.*?\';/',
|
||||
"\$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1487 passed (3228 assertions)';",
|
||||
"\$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1525 passed (3322 assertions)';",
|
||||
$file,
|
||||
);
|
||||
file_put_contents(__FILE__, $file);
|
||||
}
|
||||
|
||||
$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1487 passed (3228 assertions)';
|
||||
$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1525 passed (3322 assertions)';
|
||||
|
||||
expect($output)
|
||||
->toContain("Tests: {$expected}")
|
||||
|
||||
Reference in New Issue
Block a user