Compare commits

...

13 Commits

Author SHA1 Message Date
fb0eef4200 release: 1.2.0 2021-05-13 00:37:55 +01:00
819da37b89 Merge pull request #292 from shuvroroy/patch-1
Remove unused import
2021-05-12 21:21:59 +01:00
8eb9c408a9 Merge pull request #296 from pestphp/feature/php-cs-fixer-3
chore: migrate to PHP-CS-Fixer 3.x
2021-05-12 15:14:06 +01:00
1f10b46402 chore: migrate to PHP-CS-Fixer 3.x 2021-05-12 11:26:09 +01:00
7bb12b73e8 Merge pull request #288 from gregorip02/master
Ignore the absence of the tests folder
2021-05-11 08:19:14 +01:00
b8103697c9 Remove unused import 2021-05-07 12:45:55 +06:00
ca3f8b5702 Merge pull request #291 from olivernybroe/feat-junit
Add Junit support
2021-05-05 17:13:58 +01:00
daa01ea44b Merge pull request #283 from faustbrian/test-directory-config
make test directory configurable
2021-05-05 11:07:10 +01:00
26d577f9c5 separate directory and path by / 2021-05-05 05:07:17 +03:00
43fb711251 Merge branch 'master' into master 2021-05-03 11:47:36 -04:00
567af55a19 make test directory configurable 2021-05-03 18:28:20 +03:00
1440637e41 Add Junit support 2021-05-02 11:22:19 +02:00
d048d60d04 Ignore the absence of the tests folder 2021-04-10 11:51:13 -04:00
23 changed files with 522 additions and 77 deletions

30
.gitattributes vendored
View File

@ -1,16 +1,14 @@
/art export-ignore /art export-ignore
/docs export-ignore /docs export-ignore
/tests export-ignore /tests export-ignore
/scripts export-ignore /scripts export-ignore
/.github export-ignore /.github export-ignore
/.php_cs export-ignore /.php-cs-fixer.dist.php export-ignore
.editorconfig export-ignore .editorconfig export-ignore
.gitattributes export-ignore .gitattributes export-ignore
.gitignore export-ignore .gitignore export-ignore
.travis.yml export-ignore phpstan.neon export-ignore
phpstan.neon export-ignore phpunit.xml export-ignore
rector.yaml export-ignore CHANGELOG.md export-ignore
phpunit.xml export-ignore CONTRIBUTING.md export-ignore
CHANGELOG.md export-ignore README.md export-ignore
CONTRIBUTING.md export-ignore
README.md export-ignore

3
.gitignore vendored
View File

@ -4,7 +4,8 @@ composer.lock
/vendor/ /vendor/
coverage.xml coverage.xml
.phpunit.result.cache .phpunit.result.cache
.php_cs.cache /.php-cs-fixer.php
.php-cs-fixer.cache
.temp/coverage.php .temp/coverage.php
*.swp *.swp
*.swo *.swo

View File

@ -6,7 +6,7 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . DIRECTORY_SEPARATOR . 'scripts') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'scripts')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')
->append(['.php_cs']); ->append(['.php-cs-fixer.dist.php']);
$rules = [ $rules = [
'@Symfony' => true, '@Symfony' => true,
@ -25,7 +25,7 @@ $rules = [
$rules['increment_style'] = ['style' => 'post']; $rules['increment_style'] = ['style' => 'post'];
return PhpCsFixer\Config::create() return (new PhpCsFixer\Config())
->setUsingCache(true) ->setUsingCache(true)
->setRules($rules) ->setRules($rules)
->setFinder($finder); ->setFinder($finder);

View File

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [v1.2.0 (2021-05-13)](https://github.com/pestphp/pest/compare/v1.1.0...v1.2.0)
### Added
- Adds JUnit / Infection support ([#291](https://github.com/pestphp/pest/pull/291))
- `--test-directory` command line option ([#283](https://github.com/pestphp/pest/pull/283))
## [v1.1.0 (2021-05-02)](https://github.com/pestphp/pest/compare/v1.0.5...v1.1.0) ## [v1.1.0 (2021-05-02)](https://github.com/pestphp/pest/compare/v1.0.5...v1.1.0)
### Added ### Added
- Possibility of "hooks" being added using the "uses" function ([#282](https://github.com/pestphp/pest/pull/282)) - Possibility of "hooks" being added using the "uses" function ([#282](https://github.com/pestphp/pest/pull/282))

View File

@ -28,11 +28,12 @@ use Symfony\Component\Console\Output\OutputInterface;
(new Provider())->register(); (new Provider())->register();
// get $rootPath based on $autoloadPath // get $rootPath based on $autoloadPath
$rootPath = dirname($autoloadPath, 2); $rootPath = dirname($autoloadPath, 2);
$argv = new ArgvInput();
$testSuite = TestSuite::getInstance($rootPath); $testSuite = TestSuite::getInstance($rootPath, $argv->getParameterOption('--test-directory', 'tests'));
$isDecorated = (new ArgvInput())->getParameterOption('--colors', 'always') !== 'never'; $isDecorated = $argv->getParameterOption('--colors', 'always') !== 'never';
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated); $output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated);
$container = Container::getInstance(); $container = Container::getInstance();
@ -41,5 +42,14 @@ use Symfony\Component\Console\Output\OutputInterface;
ValidatesEnvironment::in($testSuite); ValidatesEnvironment::in($testSuite);
// lets remove any arguments that PHPUnit does not understand
if ($argv->hasParameterOption('--test-directory')) {
foreach ($_SERVER['argv'] as $key => $value) {
if (strpos($value, '--test-directory') !== false) {
unset($_SERVER['argv'][$key]);
}
}
}
exit($container->get(Command::class)->run($_SERVER['argv'])); exit($container->get(Command::class)->run($_SERVER['argv']));
})(); })();

View File

@ -22,10 +22,13 @@ parameters:
- "#Method Pest\\\\Support\\\\Reflection::getParameterClassName\\(\\) has a nullable return type declaration.#" - "#Method Pest\\\\Support\\\\Reflection::getParameterClassName\\(\\) has a nullable return type declaration.#"
- -
message: '#Call to an undefined method PHPUnit\\Framework\\Test::getName\(\)#' message: '#Call to an undefined method PHPUnit\\Framework\\Test::getName\(\)#'
path: src/TeamCity.php path: src/Logging
- -
message: '#invalid typehint type Pest\\Concerns\\TestCase#' message: '#invalid typehint type Pest\\Concerns\\TestCase#'
path: src/TeamCity.php path: src/Logging
- -
message: '#is not subtype of native type PHPUnit\\Framework\\Test#' message: '#is not subtype of native type PHPUnit\\Framework\\Test#'
path: src/TeamCity.php path: src/Logging
-
message: '#Call to an undefined method PHPUnit\\Framework\\Test::getPrintableTestCaseName\(\)#'
path: src/Logging

View File

@ -5,7 +5,8 @@ declare(strict_types=1);
namespace Pest\Actions; namespace Pest\Actions;
use NunoMaduro\Collision\Adapters\Phpunit\Printer; use NunoMaduro\Collision\Adapters\Phpunit\Printer;
use Pest\TeamCity; use Pest\Logging\JUnit;
use Pest\Logging\TeamCity;
use PHPUnit\TextUI\DefaultResultPrinter; use PHPUnit\TextUI\DefaultResultPrinter;
/** /**
@ -32,6 +33,14 @@ final class AddsDefaults
$arguments[self::PRINTER] = new TeamCity($arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS); $arguments[self::PRINTER] = new TeamCity($arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
} }
// Load our junit logger instead.
if (array_key_exists('junitLogfile', $arguments)) {
$arguments['listeners'][] = new JUnit(
$arguments['junitLogfile']
);
unset($arguments['junitLogfile']);
}
return $arguments; return $arguments;
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Pest\Actions; namespace Pest\Actions;
use Pest\Support\Str; use Pest\Support\Str;
use PHPUnit\TextUI\Configuration\Configuration; use function Pest\testDirectory;
use PHPUnit\Util\FileLoader; use PHPUnit\Util\FileLoader;
use RecursiveDirectoryIterator; use RecursiveDirectoryIterator;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
@ -33,7 +33,7 @@ final class LoadStructure
*/ */
public static function in(string $rootPath): void public static function in(string $rootPath): void
{ {
$testsPath = $rootPath . DIRECTORY_SEPARATOR . 'tests'; $testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
$load = function ($filename): bool { $load = function ($filename): bool {
return file_exists($filename) && (bool) FileLoader::checkAndLoad($filename); return file_exists($filename) && (bool) FileLoader::checkAndLoad($filename);

View File

@ -9,7 +9,6 @@ use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace; use Pest\Support\ExceptionTrace;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\ExecutionOrderDependency; use PHPUnit\Framework\ExecutionOrderDependency;
use PHPUnit\Util\Test;
use Throwable; use Throwable;
/** /**

View File

@ -1,24 +0,0 @@
<?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 InvalidUsesPath extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
{
/**
* Creates a new instance of invalid uses path.
*/
public function __construct(string $target)
{
parent::__construct(sprintf('The path `%s` is not valid.', $target));
}
}

View File

@ -8,6 +8,7 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use function Pest\testDirectory;
/** /**
* @internal * @internal
@ -36,7 +37,7 @@ final class PestDatasetCommand extends Command
/** @var string $name */ /** @var string $name */
$name = $this->argument('name'); $name = $this->argument('name');
$relativePath = sprintf('tests/Datasets/%s.php', ucfirst($name)); $relativePath = sprintf(testDirectory('Datasets/%s.php'), ucfirst($name));
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$target = base_path($relativePath); $target = base_path($relativePath);

View File

@ -8,6 +8,7 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Pest\Console\Thanks; use Pest\Console\Thanks;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use function Pest\testDirectory;
/** /**
* @internal * @internal
@ -34,7 +35,7 @@ final class PestInstallCommand extends Command
public function handle(): void public function handle(): void
{ {
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$pest = base_path('tests/Pest.php'); $pest = base_path(testDirectory('Pest.php'));
$stubs = 'stubs/Laravel'; $stubs = 'stubs/Laravel';
if (File::exists($pest)) { if (File::exists($pest)) {

View File

@ -8,6 +8,7 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use Pest\Support\Str; use Pest\Support\Str;
use function Pest\testDirectory;
/** /**
* @internal * @internal
@ -38,7 +39,7 @@ final class PestTestCommand extends Command
$type = ((bool) $this->option('unit')) ? 'Unit' : (((bool) $this->option('dusk')) ? 'Browser' : 'Feature'); $type = ((bool) $this->option('unit')) ? 'Unit' : (((bool) $this->option('dusk')) ? 'Browser' : 'Feature');
$relativePath = sprintf('tests/%s/%s.php', $relativePath = sprintf(testDirectory('%s/%s.php'),
$type, $type,
ucfirst($name) ucfirst($name)
); );

430
src/Logging/JUnit.php Normal file
View File

@ -0,0 +1,430 @@
<?php
declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Pest\Logging;
use function class_exists;
use DOMDocument;
use DOMElement;
use Exception;
use function get_class;
use function method_exists;
use Pest\Concerns\TestCase;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestFailure;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Filter;
use PHPUnit\Util\Printer;
use PHPUnit\Util\Xml;
use ReflectionClass;
use ReflectionException;
use function sprintf;
use function str_replace;
use Throwable;
use function trim;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class JUnit extends Printer implements TestListener
{
/**
* @var DOMDocument
*/
private $document;
/**
* @var DOMElement
*/
private $root;
/**
* @var DOMElement[]
*/
private $testSuites = [];
/**
* @var int[]
*/
private $testSuiteTests = [0];
/**
* @var int[]
*/
private $testSuiteAssertions = [0];
/**
* @var int[]
*/
private $testSuiteErrors = [0];
/**
* @var int[]
*/
private $testSuiteWarnings = [0];
/**
* @var int[]
*/
private $testSuiteFailures = [0];
/**
* @var int[]
*/
private $testSuiteSkipped = [0];
/**
* @var int[]|float[]
*/
private $testSuiteTimes = [0];
/**
* @var int
*/
private $testSuiteLevel = 0;
/**
* @var DOMElement|null
*/
private $currentTestCase;
public function __construct(string $out)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('testsuites');
$this->document->appendChild($this->root);
parent::__construct($out);
}
/**
* Flush buffer and close output.
*/
public function flush(): void
{
$this->write($this->getXML());
parent::flush();
}
/**
* An error occurred.
*/
public function addError(Test $test, Throwable $t, float $time): void
{
$this->doAddFault($test, $t, 'error');
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* A warning occurred.
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->doAddFault($test, $e, 'warning');
$this->testSuiteWarnings[$this->testSuiteLevel]++;
}
/**
* A failure occurred.
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->doAddFault($test, $e, 'failure');
$this->testSuiteFailures[$this->testSuiteLevel]++;
}
/**
* Incomplete test.
*/
public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
$this->doAddSkipped();
}
/**
* Risky test.
*/
public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
}
/**
* Skipped test.
*/
public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
$this->doAddSkipped();
}
/** @phpstan-ignore-next-line */
public function startTestSuite(TestSuite $suite): void
{
$testSuite = $this->document->createElement('testsuite');
$testSuite->setAttribute('name', $suite->getName());
if (class_exists($suite->getName(), false)) {
try {
$class = new ReflectionClass($suite->getName());
if ($class->hasMethod('__getFileName')) {
$fileName = $class->getMethod('__getFileName')->invoke(null);
} else {
$fileName = $class->getFileName();
}
$testSuite->setAttribute('file', $fileName);
} catch (ReflectionException $e) {
// @ignoreException
}
}
if ($this->testSuiteLevel > 0) {
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
} else {
$this->root->appendChild($testSuite);
}
$this->testSuiteLevel++;
$this->testSuites[$this->testSuiteLevel] = $testSuite;
$this->testSuiteTests[$this->testSuiteLevel] = 0;
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
$this->testSuiteWarnings[$this->testSuiteLevel] = 0;
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
}
/** @phpstan-ignore-next-line */
public function endTestSuite(TestSuite $suite): void
{
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'tests',
(string) $this->testSuiteTests[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'assertions',
(string) $this->testSuiteAssertions[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'errors',
(string) $this->testSuiteErrors[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'warnings',
(string) $this->testSuiteWarnings[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'failures',
(string) $this->testSuiteFailures[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'skipped',
(string) $this->testSuiteSkipped[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'time',
sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
);
if ($this->testSuiteLevel > 1) {
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
$this->testSuiteWarnings[$this->testSuiteLevel - 1] += $this->testSuiteWarnings[$this->testSuiteLevel];
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
}
$this->testSuiteLevel--;
}
/**
* A test started.
*
* @param Test|TestCase $test
*/
public function startTest(Test $test): void
{
$usesDataprovider = false;
if (method_exists($test, 'usesDataProvider')) {
$usesDataprovider = $test->usesDataProvider();
}
$testCase = $this->document->createElement('testcase');
$testCase->setAttribute('name', $test->getName());
try {
$class = new ReflectionClass($test);
// @codeCoverageIgnoreStart
} catch (ReflectionException $e) {
// @phpstan-ignore-next-line
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
// @codeCoverageIgnoreEnd
$methodName = $test->getName(!$usesDataprovider);
if ($class->hasMethod($methodName)) {
try {
$method = $class->getMethod($methodName);
// @codeCoverageIgnoreStart
} catch (ReflectionException $e) {
// @phpstan-ignore-next-line
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
// @codeCoverageIgnoreEnd
$testCase->setAttribute('class', $class->getName());
$testCase->setAttribute('classname', str_replace('\\', '.', $class->getName()));
$fileName = $class->getFileName();
if ($fileName !== false) {
$testCase->setAttribute('file', $fileName);
}
$testCase->setAttribute('line', (string) $method->getStartLine());
}
if (TeamCity::isPestTest($test)) {
$testCase->setAttribute('class', $test->getPrintableTestCaseName());
$testCase->setAttribute('classname', str_replace('\\', '.', $test->getPrintableTestCaseName()));
// @phpstan-ignore-next-line
$testCase->setAttribute('file', $test->__getFileName());
}
$this->currentTestCase = $testCase;
}
/**
* A test ended.
*/
public function endTest(Test $test, float $time): void
{
$numAssertions = 0;
if (method_exists($test, 'getNumAssertions')) {
$numAssertions = $test->getNumAssertions();
}
$this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
if ($this->currentTestCase !== null) {
$this->currentTestCase->setAttribute(
'assertions',
(string) $numAssertions
);
$this->currentTestCase->setAttribute(
'time',
sprintf('%F', $time)
);
$this->testSuites[$this->testSuiteLevel]->appendChild(
$this->currentTestCase
);
}
$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
$testOutput = '';
if (method_exists($test, 'hasOutput') && method_exists($test, 'getActualOutput')) {
$testOutput = $test->hasOutput() ? $test->getActualOutput() : '';
}
if ($testOutput !== '') {
$systemOut = $this->document->createElement(
'system-out',
Xml::prepareString($testOutput)
);
if ($this->currentTestCase !== null) {
$this->currentTestCase->appendChild($systemOut);
}
}
$this->currentTestCase = null;
}
/**
* Returns the XML as a string.
*/
public function getXML(): string
{
$xml = $this->document->saveXML();
if ($xml === false) {
return '';
}
return $xml;
}
private function doAddFault(Test $test, Throwable $t, string $type): void
{
if ($this->currentTestCase === null) {
return;
}
if ($test instanceof SelfDescribing) {
$buffer = $test->toString() . "\n";
} else {
$buffer = '';
}
$buffer .= trim(
TestFailure::exceptionToString($t) . "\n" .
Filter::getFilteredStacktrace($t)
);
$fault = $this->document->createElement(
$type,
Xml::prepareString($buffer)
);
if ($t instanceof ExceptionWrapper) {
$fault->setAttribute('type', $t->getClassName());
} else {
$fault->setAttribute('type', get_class($t));
}
$this->currentTestCase->appendChild($fault);
}
private function doAddSkipped(): void
{
if ($this->currentTestCase === null) {
return;
}
$skipped = $this->document->createElement('skipped');
$this->currentTestCase->appendChild($skipped);
$this->testSuiteSkipped[$this->testSuiteLevel]++;
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Pest; namespace Pest\Logging;
use function getmypid; use function getmypid;
use Pest\Concerns\TestCase; use Pest\Concerns\TestCase;
@ -121,7 +121,7 @@ final class TeamCity extends DefaultResultPrinter
$this->printEvent('testStarted', [ $this->printEvent('testStarted', [
self::NAME => $test->getName(), self::NAME => $test->getName(),
/* @phpstan-ignore-next-line */ // @phpstan-ignore-next-line
self::LOCATION_HINT => self::PROTOCOL . $test->toString(), self::LOCATION_HINT => self::PROTOCOL . $test->toString(),
]); ]);
} }
@ -203,7 +203,7 @@ final class TeamCity extends DefaultResultPrinter
return (int) round($time * 1000); return (int) round($time * 1000);
} }
private static function isPestTest(Test $test): bool public static function isPestTest(Test $test): bool
{ {
/** @var array<string, string> $uses */ /** @var array<string, string> $uses */
$uses = class_uses($test); $uses = class_uses($test);

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Pest\PendingObjects; namespace Pest\PendingObjects;
use Closure; use Closure;
use Pest\Exceptions\InvalidUsesPath;
use Pest\TestSuite; use Pest\TestSuite;
/** /**
@ -92,14 +91,13 @@ final class UsesCall
]); ]);
}, $targets); }, $targets);
$this->targets = array_map(function ($target): string { $this->targets = array_reduce($targets, function (array $accumulator, string $target): array {
$isValid = is_dir($target) || file_exists($target); if (is_dir($target) || file_exists($target)) {
if (!$isValid) { $accumulator[] = (string) realpath($target);
throw new InvalidUsesPath($target);
} }
return (string) realpath($target); return $accumulator;
}, $targets); }, []);
} }
/** /**

View File

@ -6,5 +6,10 @@ namespace Pest;
function version(): string function version(): string
{ {
return '1.1.0'; return '1.2.0';
}
function testDirectory(string $file = ''): string
{
return TestSuite::getInstance()->testPath . '/' . $file;
} }

View File

@ -22,7 +22,7 @@ final class Plugin
public static function uses(string ...$traits): void public static function uses(string ...$traits): void
{ {
self::$callables[] = function () use ($traits): void { self::$callables[] = function () use ($traits): void {
uses(...$traits)->in(TestSuite::getInstance()->rootPath . DIRECTORY_SEPARATOR . 'tests'); uses(...$traits)->in(TestSuite::getInstance()->rootPath . DIRECTORY_SEPARATOR . testDirectory());
}; };
} }
} }

View File

@ -12,7 +12,6 @@ use ReflectionException;
use ReflectionFunction; use ReflectionFunction;
use ReflectionNamedType; use ReflectionNamedType;
use ReflectionParameter; use ReflectionParameter;
use ReflectionProperty;
/** /**
* @internal * @internal

View File

@ -66,6 +66,13 @@ final class TestSuite
*/ */
public $rootPath; public $rootPath;
/**
* Holds the test path.
*
* @var string
*/
public $testPath;
/** /**
* Holds an instance of the test suite. * Holds an instance of the test suite.
* *
@ -76,7 +83,7 @@ final class TestSuite
/** /**
* Creates a new instance of the test suite. * Creates a new instance of the test suite.
*/ */
public function __construct(string $rootPath) public function __construct(string $rootPath, string $testPath)
{ {
$this->beforeAll = new BeforeAllRepository(); $this->beforeAll = new BeforeAllRepository();
$this->beforeEach = new BeforeEachRepository(); $this->beforeEach = new BeforeEachRepository();
@ -85,15 +92,16 @@ final class TestSuite
$this->afterAll = new AfterAllRepository(); $this->afterAll = new AfterAllRepository();
$this->rootPath = (string) realpath($rootPath); $this->rootPath = (string) realpath($rootPath);
$this->testPath = $testPath;
} }
/** /**
* Returns the current instance of the test suite. * Returns the current instance of the test suite.
*/ */
public static function getInstance(string $rootPath = null): TestSuite public static function getInstance(string $rootPath = null, string $testPath = null): TestSuite
{ {
if (is_string($rootPath)) { if (is_string($rootPath) && is_string($testPath)) {
self::$instance = new TestSuite($rootPath); self::$instance = new TestSuite($rootPath, $testPath);
foreach (Plugin::$callables as $callable) { foreach (Plugin::$callables as $callable) {
$callable(); $callable();

View File

@ -18,7 +18,7 @@ test('default php unit tests', function () {
$testSuite->addTest($phpUnitTestCase); $testSuite->addTest($phpUnitTestCase);
expect($testSuite->tests())->toHaveCount(1); expect($testSuite->tests())->toHaveCount(1);
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd())); AddsTests::to($testSuite, new \Pest\TestSuite(getcwd(), 'tests'));
expect($testSuite->tests())->toHaveCount(1); expect($testSuite->tests())->toHaveCount(1);
}); });
@ -27,6 +27,6 @@ it('removes warnings', function () {
$warningTestCase = new WarningTestCase('No tests found in class "Pest\TestCase".'); $warningTestCase = new WarningTestCase('No tests found in class "Pest\TestCase".');
$testSuite->addTest($warningTestCase); $testSuite->addTest($warningTestCase);
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd())); AddsTests::to($testSuite, new \Pest\TestSuite(getcwd(), 'tests'));
expect($testSuite->tests())->toHaveCount(0); expect($testSuite->tests())->toHaveCount(0);
}); });

View File

@ -38,6 +38,7 @@ it('creates an instance and resolves also sub parameters', function () {
it('can resolve builtin value types', function () { it('can resolve builtin value types', function () {
$this->container->add('rootPath', getcwd()); $this->container->add('rootPath', getcwd());
$this->container->add('testPath', 'tests');
$instance = $this->container->get(TestSuite::class); $instance = $this->container->get(TestSuite::class);
expect($instance)->toBeInstanceOf(TestSuite::class); expect($instance)->toBeInstanceOf(TestSuite::class);

View File

@ -4,7 +4,7 @@ use Pest\Exceptions\TestAlreadyExist;
use Pest\TestSuite; use Pest\TestSuite;
it('does not allow to add the same test description twice', function () { it('does not allow to add the same test description twice', function () {
$testSuite = new TestSuite(getcwd()); $testSuite = new TestSuite(getcwd(), 'tests');
$test = function () {}; $test = function () {};
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test)); $testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test));
$this->expectException(TestAlreadyExist::class); $this->expectException(TestAlreadyExist::class);