mirror of
https://github.com/pestphp/pest.git
synced 2026-03-10 01:37:21 +01:00
Merge branch '2.x' into dirty_integration
This commit is contained in:
@ -44,6 +44,30 @@ final class Backtrace
|
||||
return $current[self::FILE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current datasets file.
|
||||
*/
|
||||
public static function datasetsFile(): string
|
||||
{
|
||||
$current = null;
|
||||
|
||||
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
|
||||
assert(array_key_exists(self::FILE, $trace));
|
||||
|
||||
if (Str::endsWith($trace['file'], 'Bootstrappers/BootFiles.php') || Str::endsWith($trace[self::FILE], 'overrides/Runner/TestSuiteLoader.php')) {
|
||||
break;
|
||||
}
|
||||
|
||||
$current = $trace;
|
||||
}
|
||||
|
||||
if ($current === null) {
|
||||
throw ShouldNotHappen::fromMessage('Dataset file not found.');
|
||||
}
|
||||
|
||||
return $current[self::FILE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename that called the current function/method.
|
||||
*/
|
||||
|
||||
@ -22,8 +22,8 @@ final class ChainableClosure
|
||||
throw ShouldNotHappen::fromMessage('$this not bound to chainable closure.');
|
||||
}
|
||||
|
||||
\Pest\Support\Closure::bind($closure, $this, $this::class)(...func_get_args());
|
||||
\Pest\Support\Closure::bind($next, $this, $this::class)(...func_get_args());
|
||||
\Pest\Support\Closure::bind($closure, $this, self::class)(...func_get_args());
|
||||
\Pest\Support\Closure::bind($next, $this, self::class)(...func_get_args());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ final class Container
|
||||
private static ?Container $instance = null;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
* @var array<string, object|string>
|
||||
*/
|
||||
private array $instances = [];
|
||||
|
||||
@ -25,37 +25,30 @@ final class Container
|
||||
*/
|
||||
public static function getInstance(): self
|
||||
{
|
||||
if (static::$instance === null) {
|
||||
static::$instance = new self();
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a dependency from the container.
|
||||
*
|
||||
* @template TObject of object
|
||||
*
|
||||
* @param class-string<TObject> $id
|
||||
* @return TObject
|
||||
*/
|
||||
public function get(string $id): mixed
|
||||
public function get(string $id): object|string
|
||||
{
|
||||
if (! array_key_exists($id, $this->instances)) {
|
||||
/** @var class-string $id */
|
||||
$this->instances[$id] = $this->build($id);
|
||||
}
|
||||
|
||||
/** @var TObject $concrete */
|
||||
$concrete = $this->instances[$id];
|
||||
|
||||
return $concrete;
|
||||
return $this->instances[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given instance to the container.
|
||||
*/
|
||||
public function add(string $id, mixed $instance): void
|
||||
public function add(string $id, object|string $instance): void
|
||||
{
|
||||
$this->instances[$id] = $instance;
|
||||
}
|
||||
@ -68,7 +61,7 @@ final class Container
|
||||
* @param class-string<TObject> $id
|
||||
* @return TObject
|
||||
*/
|
||||
private function build(string $id): mixed
|
||||
private function build(string $id): object
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($id);
|
||||
|
||||
@ -77,7 +70,7 @@ final class Container
|
||||
|
||||
if ($constructor !== null) {
|
||||
$params = array_map(
|
||||
function (ReflectionParameter $param) use ($id) {
|
||||
function (ReflectionParameter $param) use ($id): object|string {
|
||||
$candidate = Reflection::getParameterClassName($param);
|
||||
|
||||
if ($candidate === null) {
|
||||
@ -90,7 +83,6 @@ final class Container
|
||||
}
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->get($candidate);
|
||||
},
|
||||
$constructor->getParameters()
|
||||
|
||||
@ -10,9 +10,9 @@ use SebastianBergmann\CodeCoverage\Node\Directory;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
use function Termwind\render;
|
||||
use function Termwind\renderUsing;
|
||||
use function Termwind\terminal;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -42,15 +42,15 @@ final class Coverage
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($runtime->hasXdebug()) {
|
||||
if (version_compare((string) phpversion('xdebug'), '3.1', '>=')) {
|
||||
if (! in_array('coverage', xdebug_info('mode'), true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (! $runtime->hasXdebug()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (! version_compare((string) phpversion('xdebug'), '3.1', '>=')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array('coverage', xdebug_info('mode'), true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,10 +83,6 @@ final class Coverage
|
||||
$codeCoverage = require $reportPath;
|
||||
unlink($reportPath);
|
||||
|
||||
$totalWidth = (new Terminal())->getWidth();
|
||||
|
||||
$dottedLineLength = $totalWidth - 6;
|
||||
|
||||
$totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines();
|
||||
|
||||
/** @var Directory<File|Directory> $report */
|
||||
@ -103,36 +99,23 @@ final class Coverage
|
||||
$dirname,
|
||||
$basename,
|
||||
]);
|
||||
$rawName = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
|
||||
$dirname,
|
||||
$basename,
|
||||
]);
|
||||
|
||||
$linesExecutedTakenSize = 0;
|
||||
|
||||
if ($file->percentageOfExecutedLines()->asString() != '0.00%') {
|
||||
$linesExecutedTakenSize = strlen($uncoveredLines = trim(implode(', ', self::getMissingCoverage($file)))) + 1;
|
||||
$name .= sprintf(' <fg=red>%s</>', $uncoveredLines);
|
||||
}
|
||||
|
||||
$percentage = $file->numberOfExecutableLines() === 0
|
||||
? '100.0'
|
||||
: number_format($file->percentageOfExecutedLines()->asFloat(), 1, '.', '');
|
||||
|
||||
$takenSize = strlen($rawName.$percentage) + 2 + $linesExecutedTakenSize; // adding 3 space and percent sign
|
||||
$color = $percentage === '100.0' ? 'green' : ($percentage === '0.0' ? 'red' : 'yellow');
|
||||
|
||||
$percentage = sprintf(
|
||||
'<fg=%s>%s</>',
|
||||
$percentage === '100.0' ? 'green' : ($percentage === '0.0' ? 'red' : 'yellow'),
|
||||
$percentage
|
||||
);
|
||||
$truncateAt = max(1, terminal()->width() - 12);
|
||||
|
||||
$output->writeln(sprintf(
|
||||
' %s <fg=gray>%s</> %s <fg=gray>%%</>',
|
||||
$name,
|
||||
str_repeat('.', max($dottedLineLength - $takenSize, 1)),
|
||||
$percentage
|
||||
));
|
||||
renderUsing($output);
|
||||
render(<<<HTML
|
||||
<div class="flex mx-2">
|
||||
<span class="truncate-{$truncateAt}">{$name}</span>
|
||||
<span class="flex-1 content-repeat-[.] text-gray mx-1"></span>
|
||||
<span class="text-{$color}">{$percentage}%</span>
|
||||
</div>
|
||||
HTML);
|
||||
}
|
||||
|
||||
$totalCoverageAsString = $totalCoverage->asFloat() === 0.0
|
||||
|
||||
38
src/Support/DatasetInfo.php
Normal file
38
src/Support/DatasetInfo.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Support;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DatasetInfo
|
||||
{
|
||||
public const DATASETS_DIR_NAME = 'Datasets';
|
||||
|
||||
public const DATASETS_FILE_NAME = 'Datasets.php';
|
||||
|
||||
public static function isInsideADatasetsDirectory(string $file): bool
|
||||
{
|
||||
return basename(dirname($file)) === self::DATASETS_DIR_NAME;
|
||||
}
|
||||
|
||||
public static function isADatasetsFile(string $file): bool
|
||||
{
|
||||
return basename($file) === self::DATASETS_FILE_NAME;
|
||||
}
|
||||
|
||||
public static function scope(string $file): string
|
||||
{
|
||||
if (self::isInsideADatasetsDirectory($file)) {
|
||||
return dirname($file, 2);
|
||||
}
|
||||
|
||||
if (self::isADatasetsFile($file)) {
|
||||
return dirname($file);
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,10 @@ use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @template TProxy
|
||||
*
|
||||
* @mixin TProxy
|
||||
*/
|
||||
final class HigherOrderTapProxy
|
||||
{
|
||||
|
||||
@ -12,6 +12,7 @@ use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
use ReflectionUnionType;
|
||||
|
||||
/**
|
||||
@ -95,7 +96,7 @@ final class Reflection
|
||||
|
||||
$reflectionProperty = null;
|
||||
|
||||
while ($reflectionProperty === null) {
|
||||
while (! $reflectionProperty instanceof ReflectionProperty) {
|
||||
try {
|
||||
/* @var ReflectionProperty $reflectionProperty */
|
||||
$reflectionProperty = $reflectionClass->getProperty($property);
|
||||
@ -127,7 +128,7 @@ final class Reflection
|
||||
|
||||
$reflectionProperty = null;
|
||||
|
||||
while ($reflectionProperty === null) {
|
||||
while (! $reflectionProperty instanceof ReflectionProperty) {
|
||||
try {
|
||||
/* @var ReflectionProperty $reflectionProperty */
|
||||
$reflectionProperty = $reflectionClass->getProperty($property);
|
||||
|
||||
@ -56,7 +56,7 @@ final class Str
|
||||
{
|
||||
$code = str_replace(' ', '_', $code);
|
||||
|
||||
return (string) preg_replace('/[^A-Z_a-z0-9\\\\]/', '', $code);
|
||||
return (string) preg_replace('/[^A-Z_a-z0-9]/', '_', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user