diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
index 491cd60d..5b9b0df5 100644
--- a/.github/workflows/static.yml
+++ b/.github/workflows/static.yml
@@ -27,6 +27,7 @@ jobs:
php-version: 8.3
tools: composer:v2
coverage: none
+ extensions: sockets
- name: Install Dependencies
run: composer update --prefer-stable --no-interaction --no-progress --ansi
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index abafbbbb..1a1ea7eb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -13,7 +13,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
- symfony: ['7.1']
+ symfony: ['7.3']
php: ['8.3', '8.4']
dependency_version: [prefer-lowest, prefer-stable]
@@ -29,6 +29,7 @@ jobs:
php-version: ${{ matrix.php }}
tools: composer:v2
coverage: none
+ extensions: sockets
- name: Setup Problem Matches
run: |
diff --git a/composer.json b/composer.json
index f5718b7b..26d92d49 100644
--- a/composer.json
+++ b/composer.json
@@ -18,18 +18,19 @@
],
"require": {
"php": "^8.3.0",
- "brianium/paratest": "^7.10.2",
- "nunomaduro/collision": "^8.8.1",
+ "brianium/paratest": "^7.10.3",
+ "nunomaduro/collision": "^8.8.2",
"nunomaduro/termwind": "^2.3.1",
"pestphp/pest-plugin": "^4.0.0",
"pestphp/pest-plugin-arch": "^4.0.0",
"pestphp/pest-plugin-mutate": "^4.0.0",
"pestphp/pest-plugin-profanity": "^4.0.0",
- "phpunit/phpunit": "^12.2.1"
+ "phpunit/phpunit": "^12.2.6",
+ "symfony/process": "^7.3.0"
},
"conflict": {
- "filp/whoops": "<2.16.0",
- "phpunit/phpunit": ">12.2.1",
+ "filp/whoops": "<2.18.3",
+ "phpunit/phpunit": ">12.2.6",
"sebastian/exporter": "<7.0.0",
"webmozart/assert": "<1.11.0"
},
@@ -55,8 +56,8 @@
},
"require-dev": {
"pestphp/pest-dev-tools": "^4.0.0",
- "pestphp/pest-plugin-type-coverage": "^4.0.0",
- "symfony/process": "^7.3.0"
+ "pestphp/pest-plugin-browser": "^4.0.0",
+ "pestphp/pest-plugin-type-coverage": "^4.0.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
@@ -72,9 +73,9 @@
],
"scripts": {
"refacto": "rector",
- "lint": "pint",
+ "lint": "pint --parallel",
"test:refacto": "rector --dry-run",
- "test:lint": "pint --test",
+ "test:lint": "pint --parallel --test",
"test:profanity": "php bin/pest --profanity --compact",
"test:type:check": "phpstan analyse --ansi --memory-limit=-1 --debug",
"test:type:coverage": "php -d memory_limit=-1 bin/pest --type-coverage --min=100",
@@ -113,6 +114,7 @@
"Pest\\Plugins\\Snapshot",
"Pest\\Plugins\\Verbose",
"Pest\\Plugins\\Version",
+ "Pest\\Plugins\\Shard",
"Pest\\Plugins\\Parallel"
]
},
diff --git a/overrides/Report/PHP.php b/overrides/Report/PHP.php
new file mode 100644
index 00000000..0cd7e27b
--- /dev/null
+++ b/overrides/Report/PHP.php
@@ -0,0 +1,98 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report;
+
+use const PHP_EOL;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Util\Filesystem;
+use SebastianBergmann\CodeCoverage\WriteOperationFailedException;
+
+use function dirname;
+use function serialize;
+use function str_contains;
+
+final class PHP
+{
+ public function process(CodeCoverage $coverage, ?string $target = null): string
+ {
+ $coverage->clearCache();
+
+ $buffer = "
+
+ Using the visit() function requires the Pest Plugin Browser to be installed.
+
+ Run:
+
+
+
+ -
+ composer require pestphp/pest-plugin-browser:^4.0 --dev
+
+
+
+ -
+ npx playwright install
+
+
diff --git a/src/Bootstrappers/BootOverrides.php b/src/Bootstrappers/BootOverrides.php
index ae2fa070..a4eef19f 100644
--- a/src/Bootstrappers/BootOverrides.php
+++ b/src/Bootstrappers/BootOverrides.php
@@ -15,17 +15,18 @@ final class BootOverrides implements Bootstrapper
/**
* The list of files to be overridden.
*
- * @var array
+ * @var array
*/
public const array FILES = [
- '53c246e5f416a39817ac81124cdd64ea8403038d01d7a202e1ffa486fbdf3fa7' => 'Runner/Filter/NameFilterIterator.php',
- '77ffb7647b583bd82e37962c6fbdc4b04d3344d8a2c1ed103e625ed1ff7cb5c2' => 'Runner/ResultCache/DefaultResultCache.php',
- 'd0e81317889ad88c707db4b08a94cadee4c9010d05ff0a759f04e71af5efed89' => 'Runner/TestSuiteLoader.php',
- '3bb609b0d3bf6dee8df8d6cd62a3c8ece823c4bb941eaaae39e3cb267171b9d2' => 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
- '8abdad6413329c6fe0d7d44a8b9926e390af32c0b3123f3720bb9c5bbc6fbb7e' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
- 'b4250fc3ffad5954624cb5e682fd940b874e8d3422fa1ee298bd7225e1aa5fc2' => 'TextUI/TestSuiteFilterProcessor.php',
- '8cfcb4999af79463eca51a42058e502ea4ddc776cba5677bf2f8eb6093e21a5c' => 'Event/Value/ThrowableBuilder.php',
- '86cd9bcaa53cdd59c5b13e58f30064a015c549501e7629d93b96893d4dee1eb1' => 'Logging/JUnit/JunitXmlLogger.php',
+ 'Runner/Filter/NameFilterIterator.php',
+ 'Runner/ResultCache/DefaultResultCache.php',
+ 'Runner/TestSuiteLoader.php',
+ 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
+ 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
+ 'TextUI/TestSuiteFilterProcessor.php',
+ 'Event/Value/ThrowableBuilder.php',
+ 'Logging/JUnit/JunitXmlLogger.php',
+ 'Report/PHP.php',
];
/**
diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php
index 9918eb83..f233b6ac 100644
--- a/src/Concerns/Testable.php
+++ b/src/Concerns/Testable.php
@@ -438,15 +438,7 @@ trait Testable
return;
}
- if (count($this->__snapshotChanges) === 1) {
- $this->markTestIncomplete($this->__snapshotChanges[0]);
-
- return;
- }
-
- $messages = implode(PHP_EOL, array_map(static fn (string $message): string => '- $message', $this->__snapshotChanges));
-
- $this->markTestIncomplete($messages);
+ $this->markTestIncomplete(implode('. ', $this->__snapshotChanges));
}
/**
diff --git a/src/Configuration.php b/src/Configuration.php
index c504aa65..fb4f45a4 100644
--- a/src/Configuration.php
+++ b/src/Configuration.php
@@ -102,6 +102,14 @@ final readonly class Configuration
return Configuration\Project::getInstance();
}
+ /**
+ * Gets the browser configuration.
+ */
+ public function browser(): Browser\Configuration
+ {
+ return new Browser\Configuration;
+ }
+
/**
* Proxies calls to the uses method.
*
diff --git a/src/Functions.php b/src/Functions.php
index 1e12fe7e..1702f94a 100644
--- a/src/Functions.php
+++ b/src/Functions.php
@@ -2,11 +2,14 @@
declare(strict_types=1);
+use Pest\Browser\Api\ArrayablePendingAwaitablePage;
+use Pest\Browser\Api\PendingAwaitablePage;
use Pest\Concerns\Expectable;
use Pest\Configuration;
use Pest\Exceptions\AfterAllWithinDescribe;
use Pest\Exceptions\BeforeAllWithinDescribe;
use Pest\Expectation;
+use Pest\Installers\PluginBrowser;
use Pest\Mutate\Contracts\MutationTestRunner;
use Pest\Mutate\Repositories\ConfigurationRepository;
use Pest\PendingCalls\AfterEachCall;
@@ -278,3 +281,51 @@ if (! function_exists('mutates')) {
}
}
}
+
+if (! function_exists('fixture')) {
+ /**
+ * Returns the absolute path to a fixture file.
+ */
+ function fixture(string $file): string
+ {
+ $file = implode(DIRECTORY_SEPARATOR, [
+ TestSuite::getInstance()->rootPath,
+ TestSuite::getInstance()->testPath,
+ 'Fixtures',
+ str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $file),
+ ]);
+
+ $fileRealPath = realpath($file);
+
+ if ($fileRealPath === false) {
+ throw new InvalidArgumentException(
+ 'The fixture file ['.$file.'] does not exist.',
+ );
+ }
+
+ return $fileRealPath;
+ }
+}
+
+if (! function_exists('visit')) {
+ /**
+ * Browse to the given URL.
+ *
+ * @template TUrl of array|string
+ *
+ * @param TUrl $url
+ * @param array $options
+ * @return (TUrl is array ? ArrayablePendingAwaitablePage : PendingAwaitablePage)
+ */
+ function visit(array|string $url, array $options = []): ArrayablePendingAwaitablePage|PendingAwaitablePage
+ {
+ if (! class_exists(\Pest\Browser\Configuration::class)) {
+ PluginBrowser::install();
+
+ exit(0);
+ }
+
+ // @phpstan-ignore-next-line
+ return test()->visit($url, $options);
+ }
+}
diff --git a/src/Installers/PluginBrowser.php b/src/Installers/PluginBrowser.php
new file mode 100644
index 00000000..2d36ed3d
--- /dev/null
+++ b/src/Installers/PluginBrowser.php
@@ -0,0 +1,15 @@
+|string> $data
+ * @param Closure|iterable|string $data
*/
public function with(Closure|iterable|string ...$data): self
{
diff --git a/src/Pest.php b/src/Pest.php
index 865c18d0..7ddec711 100644
--- a/src/Pest.php
+++ b/src/Pest.php
@@ -6,7 +6,7 @@ namespace Pest;
function version(): string
{
- return '4.0.0-alpha.2';
+ return '4.0.0-alpha.5';
}
function testDirectory(string $file = ''): string
diff --git a/src/Plugins/Only.php b/src/Plugins/Only.php
index 7c2809f1..fd1001de 100644
--- a/src/Plugins/Only.php
+++ b/src/Plugins/Only.php
@@ -5,7 +5,10 @@ declare(strict_types=1);
namespace Pest\Plugins;
use Pest\Contracts\Plugins\Terminable;
+use Pest\Factories\Attribute;
+use Pest\Factories\TestCaseMethodFactory;
use Pest\PendingCalls\TestCall;
+use PHPUnit\Framework\Attributes\Group;
/**
* @internal
@@ -23,28 +26,19 @@ final class Only implements Terminable
.DIRECTORY_SEPARATOR
.'.temp';
- /**
- * {@inheritDoc}
- */
- public function terminate(): void
- {
- if (Parallel::isWorker()) {
- return;
- }
-
- $lockFile = self::TEMPORARY_FOLDER.DIRECTORY_SEPARATOR.'only.lock';
-
- if (file_exists($lockFile)) {
- unlink($lockFile);
- }
- }
-
/**
* Creates the lock file.
*/
- public static function enable(TestCall $testCall, string $group = '__pest_only'): void
+ public static function enable(TestCall|TestCaseMethodFactory $testCall, string $group = '__pest_only'): void
{
- $testCall->group($group);
+ if ($testCall instanceof TestCall) {
+ $testCall->group($group);
+ } else {
+ $testCall->attributes[] = new Attribute(
+ Group::class,
+ [$group],
+ );
+ }
if (Environment::name() === Environment::CI || Parallel::isWorker()) {
return;
@@ -88,4 +82,20 @@ final class Only implements Terminable
return file_get_contents($lockFile) ?: '__pest_only'; // @phpstan-ignore-line
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public function terminate(): void
+ {
+ if (Parallel::isWorker()) {
+ return;
+ }
+
+ $lockFile = self::TEMPORARY_FOLDER.DIRECTORY_SEPARATOR.'only.lock';
+
+ if (file_exists($lockFile)) {
+ unlink($lockFile);
+ }
+ }
}
diff --git a/src/Plugins/Shard.php b/src/Plugins/Shard.php
new file mode 100644
index 00000000..f48260bb
--- /dev/null
+++ b/src/Plugins/Shard.php
@@ -0,0 +1,177 @@
+hasArgument('--shard', $arguments)) {
+ return $arguments;
+ }
+
+ // @phpstan-ignore-next-line
+ $input = new ArgvInput($arguments);
+
+ ['index' => $index, 'total' => $total] = self::getShard($input);
+
+ $arguments = $this->popArgument("--shard=$index/$total", $this->popArgument('--shard', $this->popArgument(
+ "$index/$total",
+ $arguments,
+ )));
+
+ /** @phpstan-ignore-next-line */
+ $tests = $this->allTests($arguments);
+ $testsToRun = (array_chunk($tests, max(1, (int) ceil(count($tests) / $total))))[$index - 1] ?? [];
+
+ self::$shard = [
+ 'index' => $index,
+ 'total' => $total,
+ 'testsRan' => count($testsToRun),
+ 'testsCount' => count($tests),
+ ];
+
+ return [...$arguments, '--filter', $this->buildFilterArgument($testsToRun)];
+ }
+
+ /**
+ * Returns all tests that the test suite would run.
+ *
+ * @param list $arguments
+ * @return list
+ */
+ private function allTests(array $arguments): array
+ {
+ $output = (new Process([
+ 'php',
+ ...$this->removeParallelArguments($arguments),
+ '--list-tests',
+ ]))->mustRun()->getOutput();
+
+ preg_match_all('/ - (?:P\\\\)?(Tests\\\\[^:]+)::/', $output, $matches);
+
+ return array_values(array_unique($matches[1]));
+ }
+
+ /**
+ * @param array $arguments
+ * @return array
+ */
+ private function removeParallelArguments(array $arguments): array
+ {
+ return array_filter($arguments, fn (string $argument): bool => ! in_array($argument, ['--parallel', '-p'], strict: true));
+ }
+
+ /**
+ * Builds the filter argument for the given tests to run.
+ */
+ private function buildFilterArgument(mixed $testsToRun): string
+ {
+ return addslashes(implode('|', $testsToRun));
+ }
+
+ /**
+ * Adds output after the Test Suite execution.
+ */
+ public function addOutput(int $exitCode): int
+ {
+ if (self::$shard === null) {
+ return $exitCode;
+ }
+
+ [
+ 'index' => $index,
+ 'total' => $total,
+ 'testsRan' => $testsRan,
+ 'testsCount' => $testsCount,
+ ] = self::$shard;
+
+ $this->output->writeln(sprintf(
+ ' Shard:> %d of %d> — %d file%s ran, out of %d.',
+ $index,
+ $total,
+ $testsRan,
+ $testsRan === 1 ? '' : 's',
+ $testsCount,
+ ));
+
+ return $exitCode;
+ }
+
+ /**
+ * Returns the shard information.
+ *
+ * @return array{index: int, total: int}
+ */
+ public static function getShard(InputInterface $input): array
+ {
+ if ($input->hasParameterOption('--'.self::SHARD_OPTION)) {
+ $shard = $input->getParameterOption('--'.self::SHARD_OPTION);
+ } else {
+ $shard = null;
+ }
+
+ if (! is_string($shard) || ! preg_match('/^\d+\/\d+$/', $shard)) {
+ throw new InvalidOption('The [--shard] option must be in the format "index/total".');
+ }
+
+ [$index, $total] = explode('/', $shard);
+
+ if (! is_numeric($index) || ! is_numeric($total)) {
+ throw new InvalidOption('The [--shard] option must be in the format "index/total".');
+ }
+
+ if ($index <= 0 || $total <= 0 || $index > $total) {
+ throw new InvalidOption('The [--shard] option index must be a non-negative integer less than the total number of shards.');
+ }
+
+ $index = (int) $index;
+ $total = (int) $total;
+
+ return [
+ 'index' => $index,
+ 'total' => $total,
+ ];
+ }
+}
diff --git a/src/Result.php b/src/Result.php
index 22e1e895..4dcbe4a1 100644
--- a/src/Result.php
+++ b/src/Result.php
@@ -40,7 +40,7 @@ final class Result
*/
public static function exitCode(Configuration $configuration, TestResult $result): int
{
- if ($result->wasSuccessfulIgnoringPhpunitWarnings()) {
+ if ($result->wasSuccessful()) {
if ($configuration->failOnWarning()) {
$warnings = $result->numberOfTestsWithTestTriggeredPhpunitWarningEvents()
+ count($result->warnings())
@@ -60,7 +60,7 @@ final class Result
return self::FAILURE_EXIT;
}
- if ($result->wasSuccessfulIgnoringPhpunitWarnings()) {
+ if ($result->wasSuccessful()) {
if ($configuration->failOnRisky() && $result->hasTestConsideredRiskyEvents()) {
$returnCode = self::FAILURE_EXIT;
}
diff --git a/src/Support/Coverage.php b/src/Support/Coverage.php
index 955bbfc4..3f543790 100644
--- a/src/Support/Coverage.php
+++ b/src/Support/Coverage.php
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Pest\Support;
use Pest\Exceptions\ShouldNotHappen;
-use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\Environment\Runtime;
@@ -88,10 +87,20 @@ final class Coverage
throw ShouldNotHappen::fromMessage(sprintf('Coverage not found in path: %s.', $reportPath));
}
- /** @var CodeCoverage $codeCoverage */
- $codeCoverage = require $reportPath;
+ $handle = fopen($reportPath, 'r');
+ $code = '';
+ while (is_resource($handle) && ! feof($handle)) {
+ $code .= fread($handle, 8192);
+ }
+
+ if (is_resource($handle)) {
+ fclose($handle);
+ }
+
unlink($reportPath);
+ $codeCoverage = eval(substr($code, 5));
+
$totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines();
/** @var Directory $report */
diff --git a/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap b/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap
index b2f7a23f..8a174927 100644
--- a/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap
+++ b/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap
@@ -1,5 +1,5 @@
- Pest Testing Framework 4.0.0-alpha.2.
+ Pest Testing Framework 4.0.0-alpha.5.
USAGE: pest [options]
@@ -53,7 +53,7 @@
--disallow-test-output ................. Be strict about output during tests
--enforce-time-limit ................. Enforce time limit based on test size
--default-time-limit [sec] Timeout in seconds for tests that have no declared size
- --dont-report-useless-tests .. Do not report tests that do not test anything
+ --do-not-report-useless-tests Do not report tests that do not test anything
--stop-on-defect ... Stop after first error, failure, warning, or risky test
--stop-on-error ..................................... Stop after first error
--stop-on-failure ................................. Stop after first failure
@@ -69,10 +69,21 @@
--fail-on-deprecation Signal failure using shell exit code when a deprecation was triggered
--fail-on-phpunit-deprecation Signal failure using shell exit code when a PHPUnit deprecation was triggered
--fail-on-phpunit-notice Signal failure using shell exit code when a PHPUnit notice was triggered
+ --fail-on-phpunit-warning Signal failure using shell exit code when a PHPUnit warning was triggered
--fail-on-notice Signal failure using shell exit code when a notice was triggered
--fail-on-skipped Signal failure using shell exit code when a test was skipped
--fail-on-incomplete Signal failure using shell exit code when a test was marked incomplete
--fail-on-all-issues Signal failure using shell exit code when an issue is triggered
+ --do-not-fail-on-empty-test-suite Do not signal failure using shell exit code when no tests were run
+ --do-not-fail-on-warning Do not signal failure using shell exit code when a warning was triggered
+ --do-not-fail-on-risky Do not signal failure using shell exit code when a test was considered risky
+ --do-not-fail-on-deprecation Do not signal failure using shell exit code when a deprecation was triggered
+ --do-not-fail-on-phpunit-deprecation Do not signal failure using shell exit code when a PHPUnit deprecation was triggered
+ --do-not-fail-on-phpunit-notice Do not signal failure using shell exit code when a PHPUnit notice was triggered
+ --do-not-fail-on-phpunit-warning Do not signal failure using shell exit code when a PHPUnit warning was triggered
+ --do-not-fail-on-notice Do not signal failure using shell exit code when a notice was triggered
+ --do-not-fail-on-skipped Do not signal failure using shell exit code when a test was skipped
+ --do-not-fail-on-incomplete Do not signal failure using shell exit code when a test was marked incomplete
--cache-result ............................ Write test results to cache file
--do-not-cache-result .............. Do not write test results to cache file
--order-by [order] Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size
@@ -106,6 +117,7 @@
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
diff --git a/tests/.pest/snapshots/Visual/Version/visual_snapshot_of_help_command_output.snap b/tests/.pest/snapshots/Visual/Version/visual_snapshot_of_help_command_output.snap
index d72ea0de..f54244da 100644
--- a/tests/.pest/snapshots/Visual/Version/visual_snapshot_of_help_command_output.snap
+++ b/tests/.pest/snapshots/Visual/Version/visual_snapshot_of_help_command_output.snap
@@ -1,3 +1,3 @@
- Pest Testing Framework 4.0.0-alpha.2.
+ Pest Testing Framework 4.0.0-alpha.5.
diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt
index 5245e72c..7b78f425 100644
--- a/tests/.snapshots/success.txt
+++ b/tests/.snapshots/success.txt
@@ -1040,6 +1040,10 @@
✓ it may fail
✓ it may fail with the given message
+ PASS Tests\Features\Fixture
+ ✓ it may return a file path
+ ✓ it may throw an exception if the file does not exist
+
WARN Tests\Features\Helpers
✓ it can set/get properties on $this
! it gets null if property do not exist → Undefined property Tests\Features\Helpers::$wqdwqdqw
@@ -1708,4 +1712,4 @@
WARN Tests\Visual\Version
- visual snapshot of help command output
- Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 38 todos, 33 skipped, 1144 passed (2736 assertions)
\ No newline at end of file
+ Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 38 todos, 33 skipped, 1146 passed (2739 assertions)
\ No newline at end of file
diff --git a/tests/Arch.php b/tests/Arch.php
index d8deb460..905515d2 100644
--- a/tests/Arch.php
+++ b/tests/Arch.php
@@ -37,6 +37,7 @@ arch('dependencies')
'Termwind',
'ParaTest',
'Pest\Arch',
+ 'Pest\Browser',
'Pest\Mutate\Contracts\Configuration',
'Pest\Mutate\Decorators\TestCallDecorator',
'Pest\Mutate\Repositories\ConfigurationRepository',
diff --git a/tests/Features/Fixture.php b/tests/Features/Fixture.php
new file mode 100644
index 00000000..1ac538b8
--- /dev/null
+++ b/tests/Features/Fixture.php
@@ -0,0 +1,12 @@
+toBeString()
+ ->toBeFile();
+});
+
+it('may throw an exception if the file does not exist', function () {
+ fixture('file-that-does-not-exist.php');
+})->throws(InvalidArgumentException::class);
diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php
index 313f8208..01492414 100644
--- a/tests/Visual/Parallel.php
+++ b/tests/Visual/Parallel.php
@@ -16,7 +16,7 @@ $run = function () {
test('parallel', function () use ($run) {
expect($run('--exclude-group=integration'))
- ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 38 todos, 24 skipped, 1134 passed (2712 assertions)')
+ ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 38 todos, 24 skipped, 1136 passed (2715 assertions)')
->toContain('Parallel: 3 processes');
})->skipOnWindows();