Merge branch 'master' into datasets-scope

# Conflicts:
#	tests/.snapshots/success.txt
This commit is contained in:
Fabio Ivona
2022-11-30 14:06:05 +01:00
17 changed files with 115 additions and 85 deletions

View File

@ -26,7 +26,7 @@ We would like to extend our thanks to the following sponsors for funding Pest de
### Platinum Sponsors ### Platinum Sponsors
- **[Advent](https://advent.dev)** - **[Advent](https://advent.dev)**
- **[Localazy](https://localazy.com)** - **[Forge](https://forge.laravel.com)**
- **[Spatie](https://spatie.be)** - **[Spatie](https://spatie.be)**
- **[Worksome](https://www.worksome.com/)** - **[Worksome](https://www.worksome.com/)**
@ -36,6 +36,7 @@ We would like to extend our thanks to the following sponsors for funding Pest de
- [Auth0](https://auth0.com) - [Auth0](https://auth0.com)
- [Codecourse](https://codecourse.com/) - [Codecourse](https://codecourse.com/)
- [Laracasts](https://laracasts.com/) - [Laracasts](https://laracasts.com/)
- [Localazy](https://localazy.com)
- [Hyvor](https://hyvor.com/) - [Hyvor](https://hyvor.com/)
- [Fathom Analytics](https://usefathom.com/) - [Fathom Analytics](https://usefathom.com/)
- [Meema](https://meema.io) - [Meema](https://meema.io)

View File

@ -19,7 +19,7 @@
"require": { "require": {
"php": "^8.1.0", "php": "^8.1.0",
"nunomaduro/collision": "^7.0.0", "nunomaduro/collision": "^7.0.0",
"nunomaduro/termwind": "^1.14.0", "nunomaduro/termwind": "^1.14.2",
"pestphp/pest-plugin": "^2.0.0", "pestphp/pest-plugin": "^2.0.0",
"phpunit/phpunit": "10.0.x-dev" "phpunit/phpunit": "10.0.x-dev"
}, },

View File

@ -156,7 +156,7 @@ final class NameFilterIterator extends RecursiveFilterIterator
if ($test instanceof HasPrintableTestCaseName) { if ($test instanceof HasPrintableTestCaseName) {
return [ return [
$test::getPrintableTestCaseName(), $test::getPrintableTestCaseName(),
$test::getPrintableTestCaseMethodName(), $test->getPrintableTestCaseMethodName(),
]; ];
} }

View File

@ -12,6 +12,7 @@ parameters:
reportUnmatchedIgnoredErrors: true reportUnmatchedIgnoredErrors: true
ignoreErrors: ignoreErrors:
- '#Cannot instantiate interface PHPUnit\\Util\\Exception#'
- "#with a nullable type declaration#" - "#with a nullable type declaration#"
- "#type mixed is not subtype of native#" - "#type mixed is not subtype of native#"
- "#is not allowed to extend#" - "#is not allowed to extend#"

View File

@ -22,7 +22,12 @@ trait Testable
/** /**
* Test method description. * Test method description.
*/ */
private static string $__description; private string $__description;
/**
* Test "latest" method description.
*/
private static string $__latestDescription;
/** /**
* The Test Case "test" closure. * The Test Case "test" closure.
@ -69,7 +74,7 @@ trait Testable
if ($test->hasMethod($name)) { if ($test->hasMethod($name)) {
$method = $test->getMethod($name); $method = $test->getMethod($name);
self::$__description = $method->description; $this->__description = self::$__latestDescription = $method->description;
$this->__test = $method->getClosure($this); $this->__test = $method->getClosure($this);
} }
} }
@ -169,7 +174,7 @@ trait Testable
*/ */
protected function setUp(): void protected function setUp(): void
{ {
self::$__description = $this->name(); $this->__description = self::$__latestDescription = $this->name();
TestSuite::getInstance()->test = $this; TestSuite::getInstance()->test = $this;
@ -221,7 +226,7 @@ trait Testable
{ {
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name()); $method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
self::$__description = $this->dataName() ? $method->description.' with '.$this->dataName() : $method->description; $this->__description = self::$__latestDescription = $this->dataName() ? $method->description.' with '.$this->dataName() : $method->description;
if (count($arguments) !== 1) { if (count($arguments) !== 1) {
return $arguments; return $arguments;
@ -258,7 +263,7 @@ trait Testable
} }
/** /**
* Gets the Test Case name that should be used by printers. * The printable test case name.
*/ */
public static function getPrintableTestCaseName(): string public static function getPrintableTestCaseName(): string
{ {
@ -266,10 +271,18 @@ trait Testable
} }
/** /**
* Gets the Test Case name that should be used by printers. * The printable test case method name.
*/ */
public static function getPrintableTestCaseMethodName(): string public function getPrintableTestCaseMethodName(): string
{ {
return self::$__description; return $this->__description;
}
/**
* The latest printable test case method name.
*/
public static function getLatestPrintableTestCaseMethodName(): string
{
return self::$__latestDescription;
} }
} }

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Pest\Exceptions;
use InvalidArgumentException;
use NunoMaduro\Collision\Contracts\RenderlessEditor;
use NunoMaduro\Collision\Contracts\RenderlessTrace;
use Symfony\Component\Console\Exception\ExceptionInterface;
/**
* @internal
*/
final class TestDescriptionMissing extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
{
/**
* Creates a new Exception instance.
*/
public function __construct(string $fileName)
{
parent::__construct(sprintf('Test misses description in the filename `%s`.', $fileName));
}
}

View File

@ -161,7 +161,6 @@ final class HigherOrderExpectation
*/ */
private function getValue(): mixed private function getValue(): mixed
{ {
// @phpstan-ignore-next-line
return $this->shouldReset ? $this->original->value : $this->expectation->value; return $this->shouldReset ? $this->original->value : $this->expectation->value;
} }

View File

@ -10,6 +10,7 @@ use Pest\Contracts\HasPrintableTestCaseName;
use Pest\Exceptions\DatasetMissing; use Pest\Exceptions\DatasetMissing;
use Pest\Exceptions\ShouldNotHappen; use Pest\Exceptions\ShouldNotHappen;
use Pest\Exceptions\TestAlreadyExist; use Pest\Exceptions\TestAlreadyExist;
use Pest\Exceptions\TestDescriptionMissing;
use Pest\Factories\Concerns\HigherOrderable; use Pest\Factories\Concerns\HigherOrderable;
use Pest\Plugins\Environment; use Pest\Plugins\Environment;
use Pest\Support\Reflection; use Pest\Support\Reflection;
@ -122,6 +123,8 @@ final class TestCaseFactory
$rootPath = TestSuite::getInstance()->rootPath; $rootPath = TestSuite::getInstance()->rootPath;
$relativePath = str_replace($rootPath.DIRECTORY_SEPARATOR, '', $filename); $relativePath = str_replace($rootPath.DIRECTORY_SEPARATOR, '', $filename);
$relativePath = ltrim($relativePath, DIRECTORY_SEPARATOR);
$basename = basename($relativePath, '.php'); $basename = basename($relativePath, '.php');
$dotPos = strpos($basename, '.'); $dotPos = strpos($basename, '.');
@ -208,7 +211,11 @@ final class TestCaseFactory
eval($classCode); eval($classCode);
} catch (ParseError $caught) { } catch (ParseError $caught) {
throw new RuntimeException(sprintf('Unable to create test case for test file at %s', $filename), 1, $caught); throw new RuntimeException(sprintf(
"Unable to create test case for test file at %s. \n %s",
$filename,
$classCode
), 1, $caught);
} }
} }
@ -218,7 +225,7 @@ final class TestCaseFactory
public function addMethod(TestCaseMethodFactory $method): void public function addMethod(TestCaseMethodFactory $method): void
{ {
if ($method->description === null) { if ($method->description === null) {
throw ShouldNotHappen::fromMessage('The test description may not be empty.'); throw new TestDescriptionMissing($method->filename);
} }
if (array_key_exists($method->description, $this->methods)) { if (array_key_exists($method->description, $this->methods)) {

View File

@ -524,7 +524,6 @@ final class Expectation
{ {
Assert::assertIsString($this->value, $message); Assert::assertIsString($this->value, $message);
// @phpstan-ignore-next-line
Assert::assertJson($this->value, $message); Assert::assertJson($this->value, $message);
return $this; return $this;

View File

@ -156,7 +156,7 @@ final class DatasetsRepository
} }
if ($datasets[$index] instanceof Traversable) { if ($datasets[$index] instanceof Traversable) {
$datasets[$index] = iterator_to_array($datasets[$index]); $datasets[$index] = iterator_to_array($datasets[$index], false);
} }
// @phpstan-ignore-next-line // @phpstan-ignore-next-line

View File

@ -84,6 +84,9 @@ final class TestRepository
} }
} }
/**
* Gets the test case factory from the given filename.
*/
public function get(string $filename): TestCaseFactory public function get(string $filename): TestCaseFactory
{ {
return $this->testCases[$filename]; return $this->testCases[$filename];

View File

@ -28,7 +28,9 @@ final class Backtrace
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) { foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
assert(array_key_exists(self::FILE, $trace)); assert(array_key_exists(self::FILE, $trace));
if (Str::endsWith($trace[self::FILE], 'overrides/Runner/TestSuiteLoader.php')) { $traceFile = str_replace(DIRECTORY_SEPARATOR, '/', $trace[self::FILE]);
if (Str::endsWith($traceFile, 'overrides/Runner/TestSuiteLoader.php')) {
break; break;
} }

View File

@ -85,7 +85,7 @@ final class Coverage
$totalWidth = (new Terminal())->getWidth(); $totalWidth = (new Terminal())->getWidth();
$dottedLineLength = $totalWidth <= 70 ? $totalWidth : 70; $dottedLineLength = $totalWidth - 6;
$totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines(); $totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines();

View File

@ -60,6 +60,9 @@
--columns <n> .................... Number of columns to use for progress output --columns <n> .................... Number of columns to use for progress output
--columns max ............ Use maximum number of columns for progress output --columns max ............ Use maximum number of columns for progress output
--stderr ................................. Write to STDERR instead of STDOUT --stderr ................................. Write to STDERR instead of STDOUT
--no-progress .................... Disable output of test execution progress
--no-results ................................ Disable output of test results
--no-output ............................................. Disable all output
--display-incomplete .................. Display details for incomplete tests --display-incomplete .................. Display details for incomplete tests
--display-skipped ........................ Display details for skipped tests --display-skipped ........................ Display details for skipped tests
--display-deprecations . Display details for deprecations triggered by tests --display-deprecations . Display details for deprecations triggered by tests
@ -68,8 +71,7 @@
--display-warnings ......... Display details for warnings triggered by tests --display-warnings ......... Display details for warnings triggered by tests
--reverse-list .............................. Print defects in reverse order --reverse-list .............................. Print defects in reverse order
--teamcity ............... Report test execution progress in TeamCity format --teamcity ............... Report test execution progress in TeamCity format
--testdox ................. Report test execution progress in TestDox format --testdox ............................ Report test results in TestDox format
--no-interaction ........................ Disable TestDox progress animation
LOGGING OPTIONS: LOGGING OPTIONS:
--log-junit <file> ................ Log test execution in JUnit XML format to file --log-junit <file> ................ Log test execution in JUnit XML format to file

View File

@ -29,7 +29,7 @@
✓ it does not append CoversNothing to other methods ✓ it does not append CoversNothing to other methods
✓ it throws exception if no class nor method has been found ✓ it throws exception if no class nor method has been found
PASS Tests\Features\DatasetsTests PASS Tests\Features\Datasets
✓ it throws exception if dataset does not exist ✓ it throws exception if dataset does not exist
✓ it throws exception if dataset already exist ✓ it throws exception if dataset already exist
✓ it sets closures ✓ it sets closures
@ -102,6 +102,11 @@
✓ more than two datasets with (2) / (4) / (5) ✓ more than two datasets with (2) / (4) / (5)
✓ more than two datasets with (2) / (4) / (6) ✓ more than two datasets with (2) / (4) / (6)
✓ more than two datasets did the job right ✓ more than two datasets did the job right
✓ eager registered wrapped datasets with Generator functions with (1)
✓ eager registered wrapped datasets with Generator functions with (2)
✓ eager registered wrapped datasets with Generator functions with (3)
✓ eager registered wrapped datasets with Generator functions with (4)
✓ eager registered wrapped datasets with Generator functions did the job right
✓ it can resolve a dataset after the test case is available with (Closure Object (...)) #1 ✓ it can resolve a dataset after the test case is available with (Closure Object (...)) #1
✓ it can resolve a dataset after the test case is available with (Closure Object (...)) #2 ✓ it can resolve a dataset after the test case is available with (Closure Object (...)) #2
✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #1 ✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #1
@ -116,7 +121,6 @@
✓ it will not resolve a closure if it is type hinted as a callable with (Closure Object (...)) #2 ✓ it will not resolve a closure if it is type hinted as a callable with (Closure Object (...)) #2
✓ it can correctly resolve a bound dataset that returns an array with (Closure Object (...)) ✓ it can correctly resolve a bound dataset that returns an array with (Closure Object (...))
✓ it can correctly resolve a bound dataset that returns an array but wants to be spread with (Closure Object (...)) ✓ it can correctly resolve a bound dataset that returns an array but wants to be spread with (Closure Object (...))
↓ forbids to define tests in Datasets dirs and Datasets.php files
PASS Tests\Features\Depends PASS Tests\Features\Depends
✓ first ✓ first
@ -664,47 +668,6 @@
✓ get 'foo' → get 'bar' → expect true → toBeTrue ✓ get 'foo' → get 'bar' → expect true → toBeTrue
✓ get 'foo' → expect true → toBeTrue ✓ get 'foo' → expect true → toBeTrue
PASS Tests\Features\ScopedDatasets\Directory\NestedDirectory1\TestFileInNestedDirectoryWithDatasetsFile
✓ uses dataset with (1)
✓ uses dataset with (2)
✓ uses dataset with (3)
✓ uses dataset with (4)
✓ uses dataset with (5)
✓ uses dataset with ('ScopedDatasets/NestedDirector...ts.php')
✓ the right dataset is taken
PASS Tests\Features\ScopedDatasets\Directory\NestedDirectory2\TestFileInNestedDirectory
✓ uses dataset with (1)
✓ uses dataset with (2)
✓ uses dataset with (3)
✓ uses dataset with (4)
✓ uses dataset with (5)
✓ uses dataset with ('ScopedDatasets/Datasets/Scoped.php')
✓ the right dataset is taken
PASS Tests\Features\ScopedDatasets\Directory\TestFileWithLocallyDefinedDataset
✓ uses dataset with (1)
✓ uses dataset with (2)
✓ uses dataset with (3)
✓ uses dataset with (4)
✓ uses dataset with (5)
✓ uses dataset with ('ScopedDatasets/ScopedDatasets.php')
✓ the right dataset is taken
PASS Tests\Features\ScopedDatasets\Directory\TestFileWithScopedDataset
✓ uses dataset with (1)
✓ uses dataset with (2)
✓ uses dataset with (3)
✓ uses dataset with (4)
✓ uses dataset with (5)
✓ uses dataset with ('ScopedDatasets/Datasets/Scoped.php')
✓ the right dataset is taken
PASS Tests\Features\ScopedDatasets\TestFileOutOfScope
✓ uses dataset with (1)
✓ uses dataset with (2)
✓ the right dataset is taken
WARN Tests\Features\Skip WARN Tests\Features\Skip
✓ it do not skips ✓ it do not skips
- it skips with truthy → 1 - it skips with truthy → 1
@ -803,7 +766,7 @@
PASS Tests\Unit\Console\Help PASS Tests\Unit\Console\Help
✓ it outputs the help information when --help is used ✓ it outputs the help information when --help is used
PASS Tests\Unit\DatasetsTests PASS Tests\Unit\Datasets
✓ it show only the names of named datasets in their description ✓ it show only the names of named datasets in their description
✓ it show the actual dataset of non-named datasets in their description ✓ it show the actual dataset of non-named datasets in their description
✓ it show only the names of multiple named datasets in their description ✓ it show only the names of multiple named datasets in their description
@ -829,26 +792,6 @@
✓ it can resolve builtin value types ✓ it can resolve builtin value types
✓ it cannot resolve a parameter without type ✓ it cannot resolve a parameter without type
PASS Tests\Unit\Support\DatasetInfo
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Datase...rs.php', true)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Datasets.php', false)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Featur...rs.php', true)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Featur...rs.php', false)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Featur...ts.php', false)
✓ it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Datase...rs.php', false)
✓ it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Datasets.php', true)
✓ it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur...rs.php', false) #1
✓ it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur...rs.php', false) #2
✓ it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur...ts.php', true)
✓ it computes the dataset scope with ('/var/www/project/tests/Datase...rs.php', '/var/www/project/tests')
✓ it computes the dataset scope with ('/var/www/project/tests/Datasets.php', '/var/www/project/tests')
✓ it computes the dataset scope with ('/var/www/project/tests/Featur...rs.php', '/var/www/project/tests/Features')
✓ it computes the dataset scope with ('/var/www/project/tests/Featur...rs.php', '/var/www/project/tests/Featur...rs.php') #1
✓ it computes the dataset scope with ('/var/www/project/tests/Featur...ts.php', '/var/www/project/tests/Features')
✓ it computes the dataset scope with ('/var/www/project/tests/Featur...rs.php', '/var/www/project/tests/Featur...ollers')
✓ it computes the dataset scope with ('/var/www/project/tests/Featur...rs.php', '/var/www/project/tests/Featur...rs.php') #2
✓ it computes the dataset scope with ('/var/www/project/tests/Featur...ts.php', '/var/www/project/tests/Featur...ollers')
PASS Tests\Unit\Support\Reflection PASS Tests\Unit\Support\Reflection
✓ it gets file name from closure ✓ it gets file name from closure
✓ it gets property values ✓ it gets property values
@ -880,4 +823,4 @@
PASS Tests\Visual\Version PASS Tests\Visual\Version
✓ visual snapshot of help command output ✓ visual snapshot of help command output
Tests: 4 incomplete, 2 todos, 18 skipped, 611 passed (1521 assertions) Tests: 4 incomplete, 1 todo, 18 skipped, 567 passed (1465 assertions)

View File

@ -13,3 +13,20 @@ dataset('numbers.closure.wrapped', function () {
dataset('numbers.array', [[1], [2]]); dataset('numbers.array', [[1], [2]]);
dataset('numbers.array.wrapped', [1, 2]); dataset('numbers.array.wrapped', [1, 2]);
dataset('numbers.generators.wrapped', function () {
yield from firstSetOfNumbers();
yield from secondSetOfNumbers();
});
function firstSetOfNumbers(): Generator
{
yield 1;
yield 2;
}
function secondSetOfNumbers(): Generator
{
yield 3;
yield 4;
}

View File

@ -230,6 +230,25 @@ test('more than two datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212121212131423241314232411122122111221221112212213142324135136145146235236245246'); expect($state->text)->toBe('121212121212131423241314232411122122111221221112212213142324135136145146235236245246');
}); });
$wrapped_generator_state = new stdClass();
$wrapped_generator_state->text = '';
$wrapped_generator_function_datasets = [1, 2, 3, 4];
test(
'eager registered wrapped datasets with Generator functions',
function (int $text) use (
$wrapped_generator_state,
$wrapped_generator_function_datasets
) {
$wrapped_generator_state->text .= $text;
expect(in_array($text, $wrapped_generator_function_datasets))->toBe(true);
}
)->with('numbers.generators.wrapped');
test('eager registered wrapped datasets with Generator functions did the job right', function () use ($wrapped_generator_state) {
expect($wrapped_generator_state->text)->toBe('1234');
});
it('can resolve a dataset after the test case is available', function ($result) { it('can resolve a dataset after the test case is available', function ($result) {
expect($result)->toBe('bar'); expect($result)->toBe('bar');
})->with([ })->with([