Compare commits

..

9 Commits

Author SHA1 Message Date
2d93c9c373 Bump shivammathur/setup-php in the github-actions group (#1707)
Bumps the github-actions group with 1 update: [shivammathur/setup-php](https://github.com/shivammathur/setup-php).


Updates `shivammathur/setup-php` from 2.37.0 to 2.37.1
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](accd6127cb...7c071dfe9d)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-version: 2.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 17:55:17 +01:00
e5ab3af05b ci: update dependabot config (add cooldown, single update entry) 2026-06-04 17:54:45 +01:00
40b88b62ef release: v4.7.2 2026-06-01 07:08:59 +01:00
e3361bc321 fix: don't ob_start with pao 2026-06-01 07:08:50 +01:00
92e76eb5ab ci: runs ci only against stable 2026-06-01 06:32:29 +01:00
bd22f478b8 chore: fixes issues with contracts and symfony 8.1 2026-06-01 06:24:42 +01:00
eeaac34cf6 release: v4.7.1 2026-06-01 05:42:12 +01:00
b9b07d8983 chore: bump dependencies 2026-06-01 05:42:03 +01:00
6aa7d2f891 fix: better fatal exceptions reporting 2026-06-01 05:41:58 +01:00
36 changed files with 174 additions and 169 deletions

View File

@ -4,16 +4,9 @@ updates:
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 5
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
target-branch: "5.x"
groups:
github-actions:
patterns:
- "*"
- "*"

View File

@ -2,7 +2,7 @@ name: Static Analysis
on:
push:
branches: [5.x]
branches: [4.x]
pull_request:
schedule:
- cron: '0 9 * * *'
@ -24,16 +24,16 @@ jobs:
strategy:
fail-fast: true
matrix:
dependency-version: [prefer-lowest, prefer-stable]
dependency-version: [prefer-stable]
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # v2
with:
php-version: 8.4
php-version: 8.3
tools: composer:v2
coverage: none
extensions: sockets
@ -47,10 +47,10 @@ jobs:
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: static-php-8.4-${{ matrix.dependency-version }}-composer-${{ hashFiles('**/composer.json', '**/composer.lock') }}
key: static-php-8.3-${{ matrix.dependency-version }}-composer-${{ hashFiles('**/composer.json', '**/composer.lock') }}
restore-keys: |
static-php-8.4-${{ matrix.dependency-version }}-composer-
static-php-8.4-composer-
static-php-8.3-${{ matrix.dependency-version }}-composer-
static-php-8.3-composer-
- name: Install Dependencies
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress --ansi

View File

@ -2,7 +2,7 @@ name: Tests
on:
push:
branches: [5.x]
branches: [4.x]
pull_request:
schedule:
- cron: '0 9 * * *'
@ -24,9 +24,12 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest] # windows-latest
symfony: ['8.0']
php: ['8.4', '8.5']
symfony: ['7.4', '8.0']
php: ['8.3', '8.4', '8.5']
dependency_version: [prefer-stable]
exclude:
- php: '8.3'
symfony: '8.0'
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
@ -35,7 +38,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2

View File

@ -17,20 +17,21 @@
}
],
"require": {
"php": "^8.4",
"brianium/paratest": "^7.22.3",
"php": "^8.3.0",
"brianium/paratest": "^7.20.0",
"composer/xdebug-handler": "^3.0.5",
"nunomaduro/collision": "^8.9.4",
"nunomaduro/termwind": "^2.4.0",
"pestphp/pest-plugin": "^5.0.0",
"pestphp/pest-plugin-arch": "^5.0.0",
"pestphp/pest-plugin-mutate": "^5.0.0",
"pestphp/pest-plugin-profanity": "^5.0.0",
"phpunit/phpunit": "^13.1.8",
"symfony/process": "^8.1.0"
"pestphp/pest-plugin": "^4.0.0",
"pestphp/pest-plugin-arch": "^4.0.2",
"pestphp/pest-plugin-mutate": "^4.0.1",
"pestphp/pest-plugin-profanity": "^4.2.1",
"phpunit/phpunit": "^12.5.28",
"symfony/process": "^7.4.13|^8.1.0"
},
"conflict": {
"filp/whoops": "<2.18.3",
"phpunit/phpunit": ">13.1.8",
"phpunit/phpunit": ">12.5.28",
"sebastian/exporter": "<7.0.0",
"webmozart/assert": "<1.11.0"
},
@ -59,11 +60,10 @@
},
"require-dev": {
"mrpunyapal/peststan": "^0.2.10",
"laravel/pao": "^1.0.6",
"pestphp/pest-dev-tools": "^5.0.0",
"pestphp/pest-plugin-browser": "^5.0.0",
"pestphp/pest-plugin-type-coverage": "^5.0.0",
"psy/psysh": "^0.12.22"
"pestphp/pest-dev-tools": "^4.1.0",
"pestphp/pest-plugin-browser": "^4.3.1",
"pestphp/pest-plugin-type-coverage": "^4.0.4",
"psy/psysh": "^0.12.23"
},
"minimum-stability": "dev",
"prefer-stable": true,

View File

@ -33,7 +33,7 @@ final readonly class Configuration
*/
public function in(string ...$targets): UsesCall
{
return new UsesCall($this->filename, [])->in(...$targets);
return (new UsesCall($this->filename, []))->in(...$targets);
}
/**
@ -60,7 +60,7 @@ final readonly class Configuration
*/
public function group(string ...$groups): UsesCall
{
return new UsesCall($this->filename, [])->group(...$groups);
return (new UsesCall($this->filename, []))->group(...$groups);
}
/**
@ -68,7 +68,7 @@ final readonly class Configuration
*/
public function only(): void
{
new BeforeEachCall(TestSuite::getInstance(), $this->filename)->only();
(new BeforeEachCall(TestSuite::getInstance(), $this->filename))->only();
}
/**

View File

@ -238,7 +238,7 @@ final class Expectation
if ($callbacks[$index] instanceof Closure) {
$callbacks[$index](new self($value), new self($key));
} else {
new self($value)->toEqual($callbacks[$index]);
(new self($value))->toEqual($callbacks[$index]);
}
$index = isset($callbacks[$index + 1]) ? $index + 1 : 0;
@ -915,7 +915,15 @@ final class Expectation
return Targeted::make(
$this,
fn (ObjectDescription $object): bool => array_all($interfaces, fn (string $interface): bool => isset($object->reflectionClass) && $object->reflectionClass->implementsInterface($interface)),
function (ObjectDescription $object) use ($interfaces): bool {
foreach ($interfaces as $interface) {
if (! isset($object->reflectionClass) || ! $object->reflectionClass->implementsInterface($interface)) {
return false;
}
}
return true;
},
"to implement '".implode("', '", $interfaces)."'",
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
@ -1130,8 +1138,8 @@ final class Expectation
$this,
fn (ObjectDescription $object): bool => isset($object->reflectionClass)
&& $object->reflectionClass->isEnum()
&& new ReflectionEnum($object->name)->isBacked() // @phpstan-ignore-line
&& (string) new ReflectionEnum($object->name)->getBackingType() === $backingType, // @phpstan-ignore-line
&& (new ReflectionEnum($object->name))->isBacked() // @phpstan-ignore-line
&& (string) (new ReflectionEnum($object->name))->getBackingType() === $backingType, // @phpstan-ignore-line
'to be '.$backingType.' backed enum',
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);

View File

@ -576,7 +576,15 @@ final readonly class OppositeExpectation
return Targeted::make(
$original,
fn (ObjectDescription $object): bool => array_all($traits, fn (string $trait): bool => ! (isset($object->reflectionClass) && in_array($trait, $object->reflectionClass->getTraitNames(), true))),
function (ObjectDescription $object) use ($traits): bool {
foreach ($traits as $trait) {
if (isset($object->reflectionClass) && in_array($trait, $object->reflectionClass->getTraitNames(), true)) {
return false;
}
}
return true;
},
"not to use traits '".implode("', '", $traits)."'",
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
@ -596,7 +604,15 @@ final readonly class OppositeExpectation
return Targeted::make(
$original,
fn (ObjectDescription $object): bool => array_all($interfaces, fn (string $interface): bool => ! (isset($object->reflectionClass) && $object->reflectionClass->implementsInterface($interface))),
function (ObjectDescription $object) use ($interfaces): bool {
foreach ($interfaces as $interface) {
if (isset($object->reflectionClass) && $object->reflectionClass->implementsInterface($interface)) {
return false;
}
}
return true;
},
"not to implement '".implode("', '", $interfaces)."'",
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
@ -798,11 +814,13 @@ final readonly class OppositeExpectation
$exporter = Exporter::default();
$toString = fn (mixed $argument): string => $exporter->shortenedExport($argument);
throw new ExpectationFailedException(sprintf(
'Expecting %s not %s %s.',
$exporter->shortenedExport($this->original->value),
$toString($this->original->value),
strtolower((string) preg_replace('/(?<!\ )[A-Z]/', ' $0', $name)),
implode(' ', array_map(fn (mixed $argument): string => $exporter->export($argument), $arguments)),
implode(' ', array_map(fn (mixed $argument): string => $toString($argument), $arguments)),
));
}
@ -834,8 +852,8 @@ final readonly class OppositeExpectation
$original,
fn (ObjectDescription $object): bool => isset($object->reflectionClass) === false
|| ! $object->reflectionClass->isEnum()
|| ! new \ReflectionEnum($object->name)->isBacked() // @phpstan-ignore-line
|| (string) new \ReflectionEnum($object->name)->getBackingType() !== $backingType, // @phpstan-ignore-line
|| ! (new \ReflectionEnum($object->name))->isBacked() // @phpstan-ignore-line
|| (string) (new \ReflectionEnum($object->name))->getBackingType() !== $backingType, // @phpstan-ignore-line
'not to be '.$backingType.' backed enum',
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);

View File

@ -197,7 +197,7 @@ final class TestCaseFactory
if (
$method->closure instanceof \Closure &&
new \ReflectionFunction($method->closure)->isStatic()
(new \ReflectionFunction($method->closure))->isStatic()
) {
throw new TestClosureMustNotBeStatic($method);

View File

@ -163,7 +163,7 @@ final class Kernel
$this->terminate();
if (is_array($error = error_get_last())) {
if (! in_array($error['type'], [E_ERROR, E_CORE_ERROR], true)) {
if (! in_array($error['type'], [E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR], true)) {
return;
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Pest;
use Laravel\Pao\Execution;
use Pest\Support\View;
use Symfony\Component\Console\Output\OutputInterface;
@ -28,6 +29,10 @@ final class KernelDump
*/
public function enable(): void
{
if (class_exists(Execution::class) && Execution::running()) {
return;
}
ob_start(function (string $message): string {
$this->buffer .= $message;
@ -68,6 +73,10 @@ final class KernelDump
$type = 'INFO';
if (is_array($error = error_get_last()) && in_array($error['type'], [E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR], true)) {
return;
}
if ($this->isInternalError($this->buffer)) {
$type = 'ERROR';
$this->buffer = str_replace(
@ -107,7 +116,6 @@ final class KernelDump
*/
private function isInternalError(string $output): bool
{
return str_contains($output, 'An error occurred inside PHPUnit.')
|| str_contains($output, 'Fatal error');
return str_contains($output, 'An error occurred inside PHPUnit.');
}
}

View File

@ -936,7 +936,7 @@ final class Expectation
if ($exception instanceof Closure) {
$callback = $exception;
$parameters = new ReflectionFunction($exception)->getParameters();
$parameters = (new ReflectionFunction($exception))->getParameters();
if (count($parameters) !== 1) {
throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.');

View File

@ -37,7 +37,7 @@ final readonly class HigherOrderExpectationTypeExtension implements ExpressionTy
$varType = $scope->getType($expr->var);
if (! new ObjectType(HigherOrderExpectation::class)->isSuperTypeOf($varType)->yes()) {
if (! (new ObjectType(HigherOrderExpectation::class))->isSuperTypeOf($varType)->yes()) {
return null;
}

View File

@ -53,7 +53,9 @@ final class UsesCall
$this->targets = [$filename];
}
#[\Deprecated(message: 'Use `pest()->printer()->compact()` instead.')]
/**
* @deprecated Use `pest()->printer()->compact()` instead.
*/
public function compact(): self
{
DefaultPrinter::compact(true);

View File

@ -6,7 +6,7 @@ namespace Pest;
function version(): string
{
return '5.0.0-rc.7';
return '4.7.2';
}
function testDirectory(string $file = ''): string

View File

@ -178,7 +178,13 @@ final class Parallel implements HandlesArguments
{
$arguments = new ArgvInput;
return array_any(self::UNSUPPORTED_ARGUMENTS, fn (string|array $unsupportedArgument): bool => $arguments->hasParameterOption($unsupportedArgument));
foreach (self::UNSUPPORTED_ARGUMENTS as $unsupportedArgument) {
if ($arguments->hasParameterOption($unsupportedArgument)) {
return true;
}
}
return false;
}
/**

View File

@ -7,6 +7,7 @@ namespace Pest\Plugins\Parallel\Paratest;
use const DIRECTORY_SEPARATOR;
use NunoMaduro\Collision\Adapters\Phpunit\Support\ResultReflection;
use ParaTest\Coverage\CoverageMerger;
use ParaTest\JUnit\LogMerger;
use ParaTest\JUnit\Writer;
use ParaTest\Options;
@ -24,17 +25,11 @@ use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade;
use PHPUnit\TestRunner\TestResult\TestResult;
use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry;
use PHPUnit\Util\ExcludeList;
use ReflectionProperty;
use SebastianBergmann\CodeCoverage\Node\Builder;
use SebastianBergmann\CodeCoverage\Serialization\Merger;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingSourceAnalyser;
use SebastianBergmann\Timer\Timer;
use SplFileInfo;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\PhpExecutableFinder;
use function array_filter;
use function array_merge;
use function array_merge_recursive;
use function array_shift;
@ -151,7 +146,6 @@ final class WrapperRunner implements RunnerInterface
public function run(): int
{
$directory = dirname(__DIR__);
assert($directory !== '');
ExcludeList::addDirectory($directory);
TestResultFacade::init();
EventFacade::instance()->seal();
@ -453,33 +447,10 @@ final class WrapperRunner implements RunnerInterface
return;
}
$coverageFiles = [];
foreach ($this->coverageFiles as $fileInfo) {
$realPath = $fileInfo->getRealPath();
if ($realPath !== false && $realPath !== '') {
$coverageFiles[] = $realPath;
}
$coverageMerger = new CoverageMerger($coverageManager->codeCoverage());
foreach ($this->coverageFiles as $coverageFile) {
$coverageMerger->addCoverageFromFile($coverageFile);
}
$serializedCoverage = (new Merger)->merge($coverageFiles);
$report = (new Builder(new FileAnalyser(new ParsingSourceAnalyser, false, false)))->build(
$serializedCoverage['codeCoverage'],
$serializedCoverage['testResults'],
$serializedCoverage['basePath'],
);
$codeCoverage = $coverageManager->codeCoverage();
$codeCoverage->excludeUncoveredFiles();
$mergedData = $serializedCoverage['codeCoverage'];
$basePath = $serializedCoverage['basePath'];
if ($basePath !== '') {
foreach ($mergedData->coveredFiles() as $relativePath) {
$mergedData->renameFile($relativePath, $basePath.DIRECTORY_SEPARATOR.$relativePath);
}
}
$codeCoverage->setData($mergedData);
$codeCoverage->setTests($serializedCoverage['testResults']);
(new ReflectionProperty(\SebastianBergmann\CodeCoverage\CodeCoverage::class, 'cachedReport'))->setValue($codeCoverage, $report);
$coverageManager->generateReports(
$this->printer->printer,

View File

@ -187,11 +187,11 @@ final class Shard implements AddsOutput, HandlesArguments, Terminable
*/
private function allTests(array $arguments): array
{
$output = new Process([
$output = (new Process([
'php',
...$this->removeParallelArguments($arguments),
'--list-tests',
])->setTimeout(120)->mustRun()->getOutput();
]))->setTimeout(120)->mustRun()->getOutput();
preg_match_all('/ - (?:P\\\\)?(Tests\\\\[^:]+)::/', $output, $matches);

View File

@ -624,7 +624,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
private function handleParent(array $arguments, string $projectRoot, bool $forceRebuild): array
{
$this->watchPatterns->useDefaults($projectRoot);
$this->branch = new ChangedFiles($projectRoot)->currentBranch() ?? 'main';
$this->branch = (new ChangedFiles($projectRoot))->currentBranch() ?? 'main';
$fingerprint = Fingerprint::compute($projectRoot);
$this->startFingerprint = $fingerprint;
@ -687,7 +687,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
*/
private function handleWorker(array $arguments, string $projectRoot, bool $recordingGlobal, bool $replayingGlobal): array
{
$this->branch = new ChangedFiles($projectRoot)->currentBranch() ?? 'main';
$this->branch = (new ChangedFiles($projectRoot))->currentBranch() ?? 'main';
if ($replayingGlobal) {
$this->installWorkerReplay($projectRoot);

View File

@ -1289,7 +1289,13 @@ final class Graph
/** @param array<string, array<int, string>> $edges */
private function anyTestUses(array $edges, string $component): bool
{
return array_any($edges, fn (array $components): bool => in_array($component, $components, true));
foreach ($edges as $components) {
if (in_array($component, $components, true)) {
return true;
}
}
return false;
}
public function pruneMissingTests(): void

View File

@ -386,6 +386,12 @@ final class JsModuleGraph
private static function hasViteConfig(string $projectRoot): bool
{
return array_any(self::VITE_CONFIG_NAMES, fn (string $name): bool => is_file($projectRoot.DIRECTORY_SEPARATOR.$name));
foreach (self::VITE_CONFIG_NAMES as $name) {
if (is_file($projectRoot.DIRECTORY_SEPARATOR.$name)) {
return true;
}
}
return false;
}
}

View File

@ -23,7 +23,15 @@ final class TableExtractor
}
$prefix = strtolower(substr($trimmed, 0, 6));
$matched = array_any(self::DML_PREFIXES, fn (string $dml): bool => str_starts_with($prefix, $dml));
$matched = false;
foreach (self::DML_PREFIXES as $dml) {
if (str_starts_with($prefix, $dml)) {
$matched = true;
break;
}
}
if (! $matched) {
return [];

View File

@ -94,7 +94,15 @@ final readonly class TestPaths
if (in_array($relativePath, $this->files, true)) {
return true;
}
$matchesSuffix = array_any($this->suffixes, fn (string $suffix): bool => str_ends_with($relativePath, $suffix));
$matchesSuffix = false;
foreach ($this->suffixes as $suffix) {
if (str_ends_with($relativePath, $suffix)) {
$matchesSuffix = true;
break;
}
}
if (! $matchesSuffix) {
return false;

View File

@ -253,17 +253,35 @@ final class WatchPatterns
private function patternTargetsDotfiles(string $pattern): bool
{
return array_any(explode('/', str_replace('\\', '/', $pattern)), fn (string $segment): bool => $segment !== '' && $segment[0] === '.');
foreach (explode('/', str_replace('\\', '/', $pattern)) as $segment) {
if ($segment !== '' && $segment[0] === '.') {
return true;
}
}
return false;
}
private function touchesVcs(string $file): bool
{
return array_any(explode('/', $file), fn (string $segment): bool => in_array($segment, self::VCS_DIRS, true));
foreach (explode('/', $file) as $segment) {
if (in_array($segment, self::VCS_DIRS, true)) {
return true;
}
}
return false;
}
private function touchesDotfile(string $file): bool
{
return array_any(explode('/', $file), fn (string $segment): bool => $segment !== '' && $segment[0] === '.');
foreach (explode('/', $file) as $segment) {
if ($segment !== '' && $segment[0] === '.') {
return true;
}
}
return false;
}
private function excludeMatches(string $exclude, string $file): bool

View File

@ -37,7 +37,7 @@ final class XdebugRestarter implements Restarter
return;
}
new XdebugHandler('pest')->check();
(new XdebugHandler('pest'))->check();
}
private function xdebugIsCoverageOnly(): bool

View File

@ -9,7 +9,6 @@ use Pest\Plugins\Tia\CoverageMerger;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Report\Facade;
use SebastianBergmann\Environment\Runtime;
use Symfony\Component\Console\Output\OutputInterface;
@ -96,18 +95,10 @@ final class Coverage
$codeCoverage = require $reportPath;
unlink($reportPath);
// @phpstan-ignore-next-line
if (is_array($codeCoverage)) {
$facade = Facade::fromSerializedData($codeCoverage);
$totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines();
/** @var Directory<File|Directory> $report */
$report = (fn (): Directory => $this->report)->call($facade);
} else {
/** @var Directory<File|Directory> $report */
$report = $codeCoverage->getReport();
}
$totalCoverage = $report->percentageOfExecutedLines();
/** @var Directory<File|Directory> $report */
$report = $codeCoverage->getReport();
foreach ($report->getIterator() as $file) {
if (! $file instanceof File) {

View File

@ -86,17 +86,4 @@ final readonly class Exporter
return (string) preg_replace(array_keys($map), array_values($map), $this->exporter->shortenedExport($value));
}
/**
* Exports a value into a full single-line string without truncation.
*/
public function export(mixed $value): string
{
$map = [
'#\\\n\s*#' => '',
'# Object \(\.{3}\)#' => '',
];
return (string) preg_replace(array_keys($map), array_values($map), $this->exporter->export($value));
}
}

View File

@ -50,7 +50,7 @@ final class HigherOrderMessage
}
if ($this->hasHigherOrderCallable()) {
return new HigherOrderCallables($target)->{$this->name}(...$this->arguments);
return (new HigherOrderCallables($target))->{$this->name}(...$this->arguments);
}
try {

View File

@ -31,7 +31,7 @@ final class HigherOrderMessageCollection
*/
public function addWhen(callable $condition, string $filename, int $line, string $name, ?array $arguments): void
{
$this->messages[] = new HigherOrderMessage($filename, $line, $name, $arguments)->when($condition);
$this->messages[] = (new HigherOrderMessage($filename, $line, $name, $arguments))->when($condition);
}
/**

View File

@ -38,7 +38,7 @@ final class HigherOrderTapProxy
return $this->target->{$property};
}
$className = new ReflectionClass($this->target)->getName();
$className = (new ReflectionClass($this->target))->getName();
if (str_starts_with($className, 'P\\')) {
$className = substr($className, 2);
@ -60,7 +60,7 @@ final class HigherOrderTapProxy
$filename = Backtrace::file();
$line = Backtrace::line();
return new HigherOrderMessage($filename, $line, $methodName, $arguments)
return (new HigherOrderMessage($filename, $line, $methodName, $arguments))
->call($this->target);
}
}

View File

@ -181,7 +181,7 @@ final class Reflection
*/
public static function getFunctionArguments(Closure $function): array
{
$parameters = new ReflectionFunction($function)->getParameters();
$parameters = (new ReflectionFunction($function))->getParameters();
$arguments = [];
foreach ($parameters as $parameter) {
@ -207,7 +207,7 @@ final class Reflection
public static function getFunctionVariable(Closure $function, string $key): mixed
{
return new ReflectionFunction($function)->getStaticVariables()[$key] ?? null;
return (new ReflectionFunction($function))->getStaticVariables()[$key] ?? null;
}
/**

View File

@ -1,5 +1,5 @@
Pest Testing Framework 5.0.0-rc.7.
Pest Testing Framework 4.7.2.
USAGE: pest <file> [options]
@ -45,7 +45,6 @@
--filter [pattern] ............................... Filter which tests to run
--exclude-filter [pattern] .. Exclude tests for the specified filter pattern
--test-suffix [suffixes] Only search for test in files with specified suffix(es). Default: Test.php,.phpt
--test-files-file [file] Only run test files listed in file (one file by line)
EXECUTION OPTIONS:
--parallel ........................................... Run tests in parallel
@ -126,12 +125,12 @@
LOGGING OPTIONS:
--log-junit [file] .......... Write test results in JUnit XML format to file
--log-otr [file] Write test results in Open Test Reporting XML format to file
--include-git-information Include Git information in Open Test Reporting XML logfile
--log-teamcity [file] ........ Write test results in TeamCity format to file
--testdox-html [file] .. Write test results in TestDox format (HTML) to file
--testdox-text [file] Write test results in TestDox format (plain text) to file
--log-events-text [file] ............... Stream events as plain text to file
--log-events-verbose-text [file] Stream events as plain text with extended information to file
--include-git-information ..... Include Git information in supported formats
--no-logging ....... Ignore logging configured in the XML configuration file
CODE COVERAGE OPTIONS:

View File

@ -1,3 +1,3 @@
Pest Testing Framework 5.0.0-rc.7.
Pest Testing Framework 4.7.2.

View File

@ -4,7 +4,6 @@
✓ preset → strict → ignoring ['Pest\Plugins\Tia\BaselineSync', 'usleep']
✓ preset → security → ignoring ['eval', 'str_shuffle', 'exec', …]
✓ globals
✓ contracts
PASS Tests\Environments\Windows
✓ global functions are loaded
@ -74,9 +73,9 @@
↓ is marked as todo 3
↓ shouldBeMarkedAsTodo
PASS Tests\Features\Coverage
WARN Tests\Features\Coverage
✓ it has plugin
it adds coverage if --coverage exist
- it adds coverage if --coverage exist → Coverage is not available
✓ it adds coverage if --min exist
✓ it generates coverage based on file input
@ -1697,8 +1696,6 @@
PASS Tests\Unit\Expectations\OppositeExpectation
✓ it throw expectation failed exception with string argument
✓ it throw expectation failed exception with array argument
✓ it does not truncate long string arguments in error message
✓ it does not truncate custom error message when using not()
PASS Tests\Unit\Overrides\ThrowableBuilder
✓ collision editor can be added to the stack trace
@ -1940,4 +1937,4 @@
✓ pass with dataset with ('my-datas-set-value')
✓ within describe → pass with dataset with ('my-datas-set-value')
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 34 skipped, 1332 passed (3020 assertions)
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1328 passed (3008 assertions)

View File

@ -33,13 +33,3 @@ arch('globals')
->expect(['dd', 'dump', 'ray', 'die', 'var_dump', 'sleep'])
->not->toBeUsed()
->ignoring(Expectation::class);
arch('contracts')
->expect('Pest\Contracts')
->toOnlyUse([
'NunoMaduro\Collision\Contracts',
'Pest\Factories\TestCaseMethodFactory',
'Symfony\Component\Console',
'Pest\Arch\Contracts',
'Pest\PendingCalls',
])->toBeInterfaces();

View File

@ -14,17 +14,3 @@ it('throw expectation failed exception with array argument', function (): void {
$expectation->throwExpectationFailedException('toBe', ['bar']);
})->throws(ExpectationFailedException::class, "Expecting 'foo' not to be 'bar'.");
it('does not truncate long string arguments in error message', function (): void {
$expectation = new OppositeExpectation(expect('foo'));
$longMessage = 'Very long error message. Very long error message. Very long error message.';
$expectation->throwExpectationFailedException('toBe', [$longMessage]);
})->throws(ExpectationFailedException::class, 'Very long error message. Very long error message. Very long error message.');
it('does not truncate custom error message when using not()', function (): void {
$longMessage = 'This is a very detailed custom error message that should not be truncated in the output.';
expect(true)->not()->toBeTrue($longMessage);
})->throws(ExpectationFailedException::class, 'This is a very detailed custom error message that should not be truncated in the output.');

View File

@ -24,13 +24,13 @@ test('parallel', function () use ($run) {
$file = file_get_contents(__FILE__);
$file = preg_replace(
'/\$expected = \'.*?\';/',
"\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 26 skipped, 1316 passed (2969 assertions)';",
"\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1312 passed (2957 assertions)';",
$file,
);
file_put_contents(__FILE__, $file);
}
$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 26 skipped, 1316 passed (2969 assertions)';
$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1312 passed (2957 assertions)';
expect($output)
->toContain("Tests: {$expected}")