mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a49cf7edc5 | |||
| b0f6a74cb6 | |||
| aaa226f6a6 | |||
| 69cb752d02 | |||
| cf00e58b7d | |||
| 9fcbca69d4 | |||
| 3a4329ddc7 | |||
| dd01229d7b | |||
| c7e4efcea4 | |||
| df3205e814 | |||
| bc57a84e77 | |||
| bc39830d8a | |||
| 3a566b100e | |||
| 9fe61e0e56 | |||
| e86bec3e68 | |||
| 58b8f3cc5d | |||
| c157b661f2 | |||
| be90610f17 | |||
| 1701a306c3 | |||
| 064ab3fc2e | |||
| 44e315df98 | |||
| 62694c14b9 | |||
| 7c43c1c583 | |||
| 6a96aed654 | |||
| b1c997a869 | |||
| b4172e2c2e | |||
| ae419afd36 | |||
| 27aa305897 | |||
| f5820bd670 | |||
| 41fd831153 | |||
| 51340439e8 | |||
| 1a39826935 | |||
| 00990efc97 | |||
| 477d20a54f | |||
| b7b16096db | |||
| 4105e33c39 | |||
| 08b09f2e98 | |||
| b0fab7e437 | |||
| 8e3444e1db | |||
| fc7a4182b5 | |||
| b7406938ac | |||
| 314caabd1d | |||
| 65cabf91b1 | |||
| f91c6c1e1e | |||
| 843dbbf18a | |||
| 47fb1d7763 | |||
| 639df4cb43 |
23
.github/workflows/static.yml
vendored
23
.github/workflows/static.yml
vendored
@ -2,10 +2,15 @@ name: Static Analysis
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches: [4.x]
|
||||||
pull_request:
|
pull_request:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: static-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
static:
|
static:
|
||||||
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
|
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
|
||||||
@ -19,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
@ -29,8 +34,22 @@ jobs:
|
|||||||
coverage: none
|
coverage: none
|
||||||
extensions: sockets
|
extensions: sockets
|
||||||
|
|
||||||
|
- name: Get Composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
shell: bash
|
||||||
|
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache Composer dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: static-php-8.3-${{ matrix.dependency-version }}-composer-${{ hashFiles('**/composer.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
static-php-8.3-${{ matrix.dependency-version }}-composer-
|
||||||
|
static-php-8.3-composer-
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: composer update --prefer-stable --no-interaction --no-progress --ansi
|
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress --ansi
|
||||||
|
|
||||||
- name: Profanity Check
|
- name: Profanity Check
|
||||||
run: composer test:profanity
|
run: composer test:profanity
|
||||||
|
|||||||
28
.github/workflows/tests.yml
vendored
28
.github/workflows/tests.yml
vendored
@ -2,8 +2,13 @@ name: Tests
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches: [4.x]
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
|
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
|
||||||
@ -13,15 +18,18 @@ jobs:
|
|||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest] # windows-latest
|
os: [ubuntu-latest, macos-latest] # windows-latest
|
||||||
symfony: ['7.3']
|
symfony: ['7.4', '8.0']
|
||||||
php: ['8.3', '8.4']
|
php: ['8.3', '8.4', '8.5']
|
||||||
dependency_version: [prefer-stable]
|
dependency_version: [prefer-stable]
|
||||||
|
exclude:
|
||||||
|
- php: '8.3'
|
||||||
|
symfony: '8.0'
|
||||||
|
|
||||||
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
|
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
@ -31,6 +39,20 @@ jobs:
|
|||||||
coverage: none
|
coverage: none
|
||||||
extensions: sockets
|
extensions: sockets
|
||||||
|
|
||||||
|
- name: Get Composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
shell: bash
|
||||||
|
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache Composer dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: ${{ matrix.os }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles('**/composer.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ matrix.os }}-php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-
|
||||||
|
${{ matrix.os }}-php-${{ matrix.php }}-composer-
|
||||||
|
|
||||||
- name: Setup Problem Matches
|
- name: Setup Problem Matches
|
||||||
run: |
|
run: |
|
||||||
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||||
|
|||||||
25
README.md
25
README.md
@ -5,6 +5,7 @@
|
|||||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/pestphp/pest"></a>
|
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/pestphp/pest"></a>
|
||||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Latest Version" src="https://img.shields.io/packagist/v/pestphp/pest"></a>
|
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Latest Version" src="https://img.shields.io/packagist/v/pestphp/pest"></a>
|
||||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
|
<a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
|
||||||
|
<a href="https://whyphp.dev"><img src="https://img.shields.io/badge/Why_PHP-in_2026-7A86E8?style=flat-square&labelColor=18181b" alt="Why PHP in 2026"></a>
|
||||||
</p>
|
</p>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -16,8 +17,8 @@
|
|||||||
|
|
||||||
- Explore our docs at **[pestphp.com »](https://pestphp.com)**
|
- Explore our docs at **[pestphp.com »](https://pestphp.com)**
|
||||||
- Follow the creator Nuno Maduro:
|
- Follow the creator Nuno Maduro:
|
||||||
- YouTube: **[youtube.com/@nunomaduro](https://www.youtube.com/@nunomaduro)** — Videos every weekday
|
- YouTube: **[youtube.com/@nunomaduro](https://youtube.com/@nunomaduro)** — Videos every week
|
||||||
- Twitch: **[twitch.tv/enunomaduro](https://www.twitch.tv/enunomaduro)** — Streams (almost) every weekday
|
- Twitch: **[twitch.tv/nunomaduro](https://twitch.tv/nunomaduro)** — Live coding on Mondays, Wednesdays, and Fridays at 9PM UTC
|
||||||
- Twitter / X: **[x.com/enunomaduro](https://x.com/enunomaduro)**
|
- Twitter / X: **[x.com/enunomaduro](https://x.com/enunomaduro)**
|
||||||
- LinkedIn: **[linkedin.com/in/nunomaduro](https://www.linkedin.com/in/nunomaduro)**
|
- LinkedIn: **[linkedin.com/in/nunomaduro](https://www.linkedin.com/in/nunomaduro)**
|
||||||
- Instagram: **[instagram.com/enunomaduro](https://www.instagram.com/enunomaduro)**
|
- Instagram: **[instagram.com/enunomaduro](https://www.instagram.com/enunomaduro)**
|
||||||
@ -30,23 +31,23 @@ We cannot thank our sponsors enough for their incredible support in funding Pest
|
|||||||
|
|
||||||
### Platinum Sponsors
|
### Platinum Sponsors
|
||||||
|
|
||||||
- **[Laracasts](https://laracasts.com/?ref=pestphp)**
|
- **[CodeRabbit](https://coderabbit.ai/?ref=pestphp)**
|
||||||
|
- **[Devin](https://devin.ai/?ref=nunomaduro)**
|
||||||
|
- **[Mailtrap](https://l.rw.rw/pestphp)**
|
||||||
|
- **[Tighten](https://tighten.com/?ref=nunomaduro)**
|
||||||
|
- **[Redberry](https://redberry.international/laravel-development/?utm_source=pest&utm_medium=banner&utm_campaign=pest_sponsorship)**
|
||||||
|
|
||||||
### Gold Sponsors
|
### Gold Sponsors
|
||||||
|
|
||||||
- **[CodeRabbit](https://coderabbit.ai/?ref=pestphp)**
|
|
||||||
- **[NativePHP](https://nativephp.com/mobile?ref=pestphp.com)**
|
|
||||||
- **[CMS Max](https://cmsmax.com/?ref=pestphp)**
|
- **[CMS Max](https://cmsmax.com/?ref=pestphp)**
|
||||||
|
|
||||||
### Premium Sponsors
|
### Premium Sponsors
|
||||||
|
|
||||||
|
- [Zapiet](https://zapiet.com/?ref=pestphp)
|
||||||
|
- [Load Forge](https://loadforge.com/?ref=pestphp)
|
||||||
|
- [Route4Me](https://route4me.com/pt?ref=pestphp)
|
||||||
|
- [Nerdify](https://getnerdify.com/?ref=pestphp)
|
||||||
- [Akaunting](https://akaunting.com/?ref=pestphp)
|
- [Akaunting](https://akaunting.com/?ref=pestphp)
|
||||||
- [DocuWriter.ai](https://www.docuwriter.ai/?ref=pestphp)
|
- [TestMu AI](https://www.testmuai.com/?utm_medium=sponsor&utm_source=pest)
|
||||||
- [Localazy](https://localazy.com/?ref=pestphp)
|
|
||||||
- [Forge](https://forge.laravel.com/?ref=pestphp)
|
|
||||||
- [Route4Me](https://www.route4me.com/?ref=pestphp)
|
|
||||||
- [Spatie](https://spatie.be/?ref=pestphp)
|
|
||||||
- [Worksome](https://www.worksome.com/?ref=pestphp)
|
|
||||||
- [Zapiet](https://www.zapiet.com/?ref=pestphp)
|
|
||||||
|
|
||||||
Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
||||||
|
|||||||
@ -86,7 +86,7 @@ $bootPest = (static function (): void {
|
|||||||
$getopt['teamcity-file'] ?? null,
|
$getopt['teamcity-file'] ?? null,
|
||||||
$getopt['testdox-file'] ?? null,
|
$getopt['testdox-file'] ?? null,
|
||||||
isset($getopt['testdox-color']),
|
isset($getopt['testdox-color']),
|
||||||
(int) $getopt['testdox-columns'] ?? null,
|
(int) ($getopt['testdox-columns'] ?? null),
|
||||||
);
|
);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@ -18,19 +18,19 @@
|
|||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3.0",
|
"php": "^8.3.0",
|
||||||
"brianium/paratest": "^7.11.2",
|
"brianium/paratest": "^7.19.0",
|
||||||
"nunomaduro/collision": "^8.8.2",
|
"nunomaduro/collision": "^8.9.0",
|
||||||
"nunomaduro/termwind": "^2.3.1",
|
"nunomaduro/termwind": "^2.4.0",
|
||||||
"pestphp/pest-plugin": "^4.0.0",
|
"pestphp/pest-plugin": "^4.0.0",
|
||||||
"pestphp/pest-plugin-arch": "^4.0.0",
|
"pestphp/pest-plugin-arch": "^4.0.0",
|
||||||
"pestphp/pest-plugin-mutate": "^4.0.1",
|
"pestphp/pest-plugin-mutate": "^4.0.1",
|
||||||
"pestphp/pest-plugin-profanity": "^4.0.1",
|
"pestphp/pest-plugin-profanity": "^4.2.1",
|
||||||
"phpunit/phpunit": "^12.3.6",
|
"phpunit/phpunit": "^12.5.12",
|
||||||
"symfony/process": "^7.3.0"
|
"symfony/process": "^7.4.5|^8.0.5"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"filp/whoops": "<2.18.3",
|
"filp/whoops": "<2.18.3",
|
||||||
"phpunit/phpunit": ">12.3.6",
|
"phpunit/phpunit": ">12.5.12",
|
||||||
"sebastian/exporter": "<7.0.0",
|
"sebastian/exporter": "<7.0.0",
|
||||||
"webmozart/assert": "<1.11.0"
|
"webmozart/assert": "<1.11.0"
|
||||||
},
|
},
|
||||||
@ -55,10 +55,10 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"pestphp/pest-dev-tools": "^4.0.0",
|
"pestphp/pest-dev-tools": "^4.1.0",
|
||||||
"pestphp/pest-plugin-browser": "^4.0.2",
|
"pestphp/pest-plugin-browser": "^4.3.0",
|
||||||
"pestphp/pest-plugin-type-coverage": "^4.0.2",
|
"pestphp/pest-plugin-type-coverage": "^4.0.3",
|
||||||
"psy/psysh": "^0.12.10"
|
"psy/psysh": "^0.12.20"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Rector\CodingStyle\Rector\ArrowFunction\ArrowFunctionDelegatingCallToFirstClassCallableRector;
|
||||||
use Rector\Config\RectorConfig;
|
use Rector\Config\RectorConfig;
|
||||||
|
use Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingConstructorRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector;
|
||||||
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
|
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
|
||||||
|
|
||||||
return RectorConfig::configure()
|
return RectorConfig::configure()
|
||||||
@ -12,6 +15,9 @@ return RectorConfig::configure()
|
|||||||
->withSkip([
|
->withSkip([
|
||||||
__DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php',
|
__DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php',
|
||||||
ReturnNeverTypeRector::class,
|
ReturnNeverTypeRector::class,
|
||||||
|
ArrowFunctionDelegatingCallToFirstClassCallableRector::class,
|
||||||
|
NarrowObjectReturnTypeRector::class,
|
||||||
|
RemoveParentDelegatingConstructorRector::class,
|
||||||
])
|
])
|
||||||
->withPreparedSets(
|
->withPreparedSets(
|
||||||
deadCode: true,
|
deadCode: true,
|
||||||
|
|||||||
@ -4,9 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Pest\ArchPresets;
|
namespace Pest\ArchPresets;
|
||||||
|
|
||||||
use Pest\Arch\Contracts\ArchExpectation;
|
|
||||||
use Pest\Expectation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@ -92,9 +89,5 @@ final class Php extends AbstractPreset
|
|||||||
'xdebug_var_dump',
|
'xdebug_var_dump',
|
||||||
'trap',
|
'trap',
|
||||||
])->not->toBeUsed();
|
])->not->toBeUsed();
|
||||||
|
|
||||||
$this->eachUserNamespace(
|
|
||||||
fn (Expectation $namespace): ArchExpectation => $namespace->not->toHaveSuspiciousCharacters(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,7 +83,7 @@ final class BootFiles implements Bootstrapper
|
|||||||
|
|
||||||
private function bootDatasets(string $testsPath): void
|
private function bootDatasets(string $testsPath): void
|
||||||
{
|
{
|
||||||
assert(strlen($testsPath) > 0);
|
assert($testsPath !== '');
|
||||||
|
|
||||||
$files = (new PhpUnitFileIterator)->getFilesAsArray($testsPath, '.php');
|
$files = (new PhpUnitFileIterator)->getFilesAsArray($testsPath, '.php');
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,8 @@ use Closure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
*
|
||||||
|
* @template T of object
|
||||||
*/
|
*/
|
||||||
trait Extendable
|
trait Extendable
|
||||||
{
|
{
|
||||||
@ -20,6 +22,8 @@ trait Extendable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a new extend.
|
* Register a new extend.
|
||||||
|
*
|
||||||
|
* @param-closure-this T $extend
|
||||||
*/
|
*/
|
||||||
public function extend(string $name, Closure $extend): void
|
public function extend(string $name, Closure $extend): void
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Pest;
|
namespace Pest;
|
||||||
|
|
||||||
|
use Pest\PendingCalls\BeforeEachCall;
|
||||||
use Pest\PendingCalls\UsesCall;
|
use Pest\PendingCalls\UsesCall;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,6 +63,14 @@ final readonly class Configuration
|
|||||||
return (new UsesCall($this->filename, []))->group(...$groups);
|
return (new UsesCall($this->filename, []))->group(...$groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all tests in the current file to be run exclusively.
|
||||||
|
*/
|
||||||
|
public function only(): void
|
||||||
|
{
|
||||||
|
(new BeforeEachCall(TestSuite::getInstance(), $this->filename))->only();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depending on where is called, it will extend the given classes and traits globally or locally.
|
* Depending on where is called, it will extend the given classes and traits globally or locally.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -25,8 +25,8 @@ final readonly class Thanks
|
|||||||
private const array FUNDING_MESSAGES = [
|
private const array FUNDING_MESSAGES = [
|
||||||
'Star' => 'https://github.com/pestphp/pest',
|
'Star' => 'https://github.com/pestphp/pest',
|
||||||
'YouTube' => 'https://youtube.com/@nunomaduro',
|
'YouTube' => 'https://youtube.com/@nunomaduro',
|
||||||
'TikTok' => 'https://tiktok.com/@nunomaduro',
|
'TikTok' => 'https://tiktok.com/@enunomaduro',
|
||||||
'Twitch' => 'https://twitch.tv/enunomaduro',
|
'Twitch' => 'https://twitch.tv/nunomaduro',
|
||||||
'LinkedIn' => 'https://linkedin.com/in/nunomaduro',
|
'LinkedIn' => 'https://linkedin.com/in/nunomaduro',
|
||||||
'Instagram' => 'https://instagram.com/enunomaduro',
|
'Instagram' => 'https://instagram.com/enunomaduro',
|
||||||
'X' => 'https://x.com/enunomaduro',
|
'X' => 'https://x.com/enunomaduro',
|
||||||
|
|||||||
@ -52,7 +52,9 @@ use ReflectionProperty;
|
|||||||
*/
|
*/
|
||||||
final class Expectation
|
final class Expectation
|
||||||
{
|
{
|
||||||
|
/** @use Extendable<self<TValue>> */
|
||||||
use Extendable;
|
use Extendable;
|
||||||
|
|
||||||
use Pipeable;
|
use Pipeable;
|
||||||
use Retrievable;
|
use Retrievable;
|
||||||
|
|
||||||
@ -397,7 +399,7 @@ final class Expectation
|
|||||||
*
|
*
|
||||||
* @return Expectation<TValue>|OppositeExpectation<TValue>|EachExpectation<TValue>|HigherOrderExpectation<Expectation<TValue>, TValue|null>|TValue
|
* @return Expectation<TValue>|OppositeExpectation<TValue>|EachExpectation<TValue>|HigherOrderExpectation<Expectation<TValue>, TValue|null>|TValue
|
||||||
*/
|
*/
|
||||||
public function __get(string $name)
|
public function __get(string $name): mixed
|
||||||
{
|
{
|
||||||
if (! self::hasMethod($name)) {
|
if (! self::hasMethod($name)) {
|
||||||
if (! is_object($this->value) && method_exists(PendingArchExpectation::class, $name)) {
|
if (! is_object($this->value) && method_exists(PendingArchExpectation::class, $name)) {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ use Pest\Arch\PendingArchExpectation;
|
|||||||
use Pest\Arch\SingleArchExpectation;
|
use Pest\Arch\SingleArchExpectation;
|
||||||
use Pest\Arch\Support\FileLineFinder;
|
use Pest\Arch\Support\FileLineFinder;
|
||||||
use Pest\Exceptions\InvalidExpectation;
|
use Pest\Exceptions\InvalidExpectation;
|
||||||
|
use Pest\Exceptions\MissingDependency;
|
||||||
use Pest\Expectation;
|
use Pest\Expectation;
|
||||||
use Pest\Support\Arr;
|
use Pest\Support\Arr;
|
||||||
use Pest\Support\Exporter;
|
use Pest\Support\Exporter;
|
||||||
@ -284,6 +285,10 @@ final readonly class OppositeExpectation
|
|||||||
*/
|
*/
|
||||||
public function toHaveSuspiciousCharacters(): ArchExpectation
|
public function toHaveSuspiciousCharacters(): ArchExpectation
|
||||||
{
|
{
|
||||||
|
if (! class_exists(Spoofchecker::class)) {
|
||||||
|
throw new MissingDependency(__FUNCTION__, 'ext-intl >= 2.0');
|
||||||
|
}
|
||||||
|
|
||||||
$checker = new Spoofchecker;
|
$checker = new Spoofchecker;
|
||||||
|
|
||||||
/** @var Expectation<array<int, string>|string> $original */
|
/** @var Expectation<array<int, string>|string> $original */
|
||||||
|
|||||||
@ -71,7 +71,7 @@ final readonly class Kernel
|
|||||||
$output,
|
$output,
|
||||||
);
|
);
|
||||||
|
|
||||||
register_shutdown_function(fn () => $kernel->shutdown());
|
register_shutdown_function($kernel->shutdown(...));
|
||||||
|
|
||||||
foreach (self::BOOTSTRAPPERS as $bootstrapper) {
|
foreach (self::BOOTSTRAPPERS as $bootstrapper) {
|
||||||
$bootstrapper = Container::getInstance()->get($bootstrapper);
|
$bootstrapper = Container::getInstance()->get($bootstrapper);
|
||||||
|
|||||||
@ -131,7 +131,7 @@ final readonly class Converter
|
|||||||
|
|
||||||
// clean the paths of each frame.
|
// clean the paths of each frame.
|
||||||
$frames = array_map(
|
$frames = array_map(
|
||||||
fn (string $frame): string => $this->toRelativePath($frame),
|
$this->toRelativePath(...),
|
||||||
$frames
|
$frames
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ namespace Pest;
|
|||||||
|
|
||||||
function version(): string
|
function version(): string
|
||||||
{
|
{
|
||||||
return '4.0.3';
|
return '4.3.2';
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDirectory(string $file = ''): string
|
function testDirectory(string $file = ''): string
|
||||||
|
|||||||
@ -99,6 +99,7 @@ final readonly class Help implements HandlesArguments
|
|||||||
{
|
{
|
||||||
$helpReflection = new PHPUnitHelp;
|
$helpReflection = new PHPUnitHelp;
|
||||||
|
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
$content = (fn (): array => $this->elements())->call($helpReflection);
|
$content = (fn (): array => $this->elements())->call($helpReflection);
|
||||||
|
|
||||||
$content['Configuration'] = [...[[
|
$content['Configuration'] = [...[[
|
||||||
@ -141,6 +142,9 @@ final readonly class Help implements HandlesArguments
|
|||||||
], [
|
], [
|
||||||
'arg' => '--retry',
|
'arg' => '--retry',
|
||||||
'desc' => 'Run non-passing tests first and stop execution upon first error or failure',
|
'desc' => 'Run non-passing tests first and stop execution upon first error or failure',
|
||||||
|
], [
|
||||||
|
'arg' => '--dirty',
|
||||||
|
'desc' => 'Only run tests that have uncommitted changes according to Git',
|
||||||
], ...$content['Selection']];
|
], ...$content['Selection']];
|
||||||
|
|
||||||
$content['Reporting'] = [...$content['Reporting'], ...[
|
$content['Reporting'] = [...$content['Reporting'], ...[
|
||||||
|
|||||||
@ -67,11 +67,11 @@ final class DatasetsRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Closure|array<int|string, mixed>
|
* @return array<int|string, mixed>
|
||||||
*
|
*
|
||||||
* @throws ShouldNotHappen
|
* @throws ShouldNotHappen
|
||||||
*/
|
*/
|
||||||
public static function get(string $filename, string $description): Closure|array // @phpstan-ignore-line
|
public static function get(string $filename, string $description): array // @phpstan-ignore-line
|
||||||
{
|
{
|
||||||
$dataset = self::$withs[$filename.self::SEPARATOR.$description];
|
$dataset = self::$withs[$filename.self::SEPARATOR.$description];
|
||||||
|
|
||||||
@ -191,6 +191,7 @@ final class DatasetsRepository
|
|||||||
return str_starts_with($currentTestFile, $datasetScope);
|
return str_starts_with($currentTestFile, $datasetScope);
|
||||||
}, ARRAY_FILTER_USE_KEY);
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
|
|
||||||
|
/** @var string|null $closestScopeDatasetKey */
|
||||||
$closestScopeDatasetKey = array_reduce(
|
$closestScopeDatasetKey = array_reduce(
|
||||||
array_keys($matchingDatasets),
|
array_keys($matchingDatasets),
|
||||||
fn (string|int|null $keyA, string|int|null $keyB): string|int|null => $keyA !== null && strlen((string) $keyA) > strlen((string) $keyB) ? $keyA : $keyB
|
fn (string|int|null $keyA, string|int|null $keyB): string|int|null => $keyA !== null && strlen((string) $keyA) > strlen((string) $keyB) ? $keyA : $keyB
|
||||||
|
|||||||
@ -46,6 +46,7 @@ final readonly class HigherOrderCallables
|
|||||||
*/
|
*/
|
||||||
public function and(mixed $value): Expectation
|
public function and(mixed $value): Expectation
|
||||||
{
|
{
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
return $this->expect($value);
|
return $this->expect($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,10 +31,8 @@ final class HigherOrderTapProxy
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamically pass properties gets to the target.
|
* Dynamically pass properties gets to the target.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function __get(string $property)
|
public function __get(string $property): mixed
|
||||||
{
|
{
|
||||||
if (property_exists($this->target, $property)) {
|
if (property_exists($this->target, $property)) {
|
||||||
return $this->target->{$property};
|
return $this->target->{$property};
|
||||||
|
|||||||
@ -79,7 +79,7 @@ final class Str
|
|||||||
return $subject;
|
return $subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
return substr($subject, 0, $pos);
|
return mb_substr($subject, 0, $pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -19,7 +19,7 @@ test('pass with dataset', function ($data) {
|
|||||||
[$filename] = TestSuite::getInstance()->snapshots->get();
|
[$filename] = TestSuite::getInstance()->snapshots->get();
|
||||||
|
|
||||||
expect($filename)->toStartWith('tests/.pest/snapshots-external/')
|
expect($filename)->toStartWith('tests/.pest/snapshots-external/')
|
||||||
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap')
|
->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
|
||||||
->and($this->snapshotable)->toMatchSnapshot();
|
->and($this->snapshotable)->toMatchSnapshot();
|
||||||
})->with(['my-datas-set-value']);
|
})->with(['my-datas-set-value']);
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ describe('within describe', function () {
|
|||||||
[$filename] = TestSuite::getInstance()->snapshots->get();
|
[$filename] = TestSuite::getInstance()->snapshots->get();
|
||||||
|
|
||||||
expect($filename)->toStartWith('tests/.pest/snapshots-external/')
|
expect($filename)->toStartWith('tests/.pest/snapshots-external/')
|
||||||
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap')
|
->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
|
||||||
->and($this->snapshotable)->toMatchSnapshot();
|
->and($this->snapshotable)->toMatchSnapshot();
|
||||||
});
|
});
|
||||||
})->with(['my-datas-set-value']);
|
})->with(['my-datas-set-value']);
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h1>Snapshot</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h1>Snapshot</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
Pest Testing Framework 4.0.3.
|
Pest Testing Framework 4.3.2.
|
||||||
|
|
||||||
USAGE: pest <file> [options]
|
USAGE: pest <file> [options]
|
||||||
|
|
||||||
@ -27,6 +27,8 @@
|
|||||||
--pr .... Output to standard output tests with the given pull request number
|
--pr .... Output to standard output tests with the given pull request number
|
||||||
--pull-request Output to standard output tests with the given pull request number (alias for --pr)
|
--pull-request Output to standard output tests with the given pull request number (alias for --pr)
|
||||||
--retry Run non-passing tests first and stop execution upon first error or failure
|
--retry Run non-passing tests first and stop execution upon first error or failure
|
||||||
|
--dirty ...... Only run tests that have uncommitted changes according to Git
|
||||||
|
--all .................... Ignore test selection from XML configuration file
|
||||||
--list-suites ................................... List available test suites
|
--list-suites ................................... List available test suites
|
||||||
--testsuite [name] ......... Only run tests from the specified test suite(s)
|
--testsuite [name] ......... Only run tests from the specified test suite(s)
|
||||||
--exclude-testsuite [name] .. Exclude tests from the specified test suite(s)
|
--exclude-testsuite [name] .. Exclude tests from the specified test suite(s)
|
||||||
@ -138,6 +140,7 @@
|
|||||||
--only-summary-for-coverage-text Option for code coverage report in text format: only show summary
|
--only-summary-for-coverage-text Option for code coverage report in text format: only show summary
|
||||||
--show-uncovered-for-coverage-text Option for code coverage report in text format: show uncovered files
|
--show-uncovered-for-coverage-text Option for code coverage report in text format: show uncovered files
|
||||||
--coverage-xml [dir] . Write code coverage report in XML format to directory
|
--coverage-xml [dir] . Write code coverage report in XML format to directory
|
||||||
|
--exclude-source-from-xml-coverage Exclude [source] element from code coverage report in XML format
|
||||||
--warm-coverage-cache ........................... Warm static analysis cache
|
--warm-coverage-cache ........................... Warm static analysis cache
|
||||||
--coverage-filter [dir] ........... Include [dir] in code coverage reporting
|
--coverage-filter [dir] ........... Include [dir] in code coverage reporting
|
||||||
--path-coverage .......... Report path coverage in addition to line coverage
|
--path-coverage .......... Report path coverage in addition to line coverage
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
Pest Testing Framework 4.0.3.
|
Pest Testing Framework 4.3.2.
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
##teamcity[testSuiteStarted name='Tests/tests/SuccessOnly' locationHint='pest_qn://tests/.tests/SuccessOnly.php' flowId='1234']
|
##teamcity[testSuiteStarted name='Tests/tests/SuccessOnly' locationHint='pest_qn://tests/.tests/SuccessOnly.php' flowId='1234']
|
||||||
##teamcity[testCount count='3' flowId='1234']
|
##teamcity[testCount count='4' flowId='1234']
|
||||||
##teamcity[testStarted name='it can pass with comparison' locationHint='pest_qn://tests/.tests/SuccessOnly.php::it can pass with comparison' flowId='1234']
|
##teamcity[testStarted name='it can pass with comparison' locationHint='pest_qn://tests/.tests/SuccessOnly.php::it can pass with comparison' flowId='1234']
|
||||||
##teamcity[testFinished name='it can pass with comparison' duration='100000' flowId='1234']
|
##teamcity[testFinished name='it can pass with comparison' duration='100000' flowId='1234']
|
||||||
##teamcity[testStarted name='can also pass' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can also pass' flowId='1234']
|
##teamcity[testStarted name='can also pass' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can also pass' flowId='1234']
|
||||||
##teamcity[testFinished name='can also pass' duration='100000' flowId='1234']
|
##teamcity[testFinished name='can also pass' duration='100000' flowId='1234']
|
||||||
##teamcity[testSuiteStarted name='can pass with dataset' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset' flowId='1234']
|
##teamcity[testSuiteStarted name='can pass with dataset' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset' flowId='1234']
|
||||||
##teamcity[testStarted name='can pass with dataset with data set "@(true)"' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset with data set "@(true)"' flowId='1234']
|
##teamcity[testStarted name='can pass with dataset with data set "(true)"' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset with data set "(true)"' flowId='1234']
|
||||||
##teamcity[testFinished name='can pass with dataset with data set "@(true)"' duration='100000' flowId='1234']
|
##teamcity[testFinished name='can pass with dataset with data set "(true)"' duration='100000' flowId='1234']
|
||||||
##teamcity[testSuiteFinished name='can pass with dataset' flowId='1234']
|
##teamcity[testSuiteFinished name='can pass with dataset' flowId='1234']
|
||||||
|
##teamcity[testSuiteStarted name='`block` → can pass with dataset in describe block' locationHint='pest_qn://tests/.tests/SuccessOnly.php::`block` → can pass with dataset in describe block' flowId='1234']
|
||||||
|
##teamcity[testStarted name='`block` → can pass with dataset in describe block with data set "(1)"' locationHint='pest_qn://tests/.tests/SuccessOnly.php::`block` → can pass with dataset in describe block with data set "(1)"' flowId='1234']
|
||||||
|
##teamcity[testFinished name='`block` → can pass with dataset in describe block with data set "(1)"' duration='100000' flowId='1234']
|
||||||
|
##teamcity[testSuiteFinished name='`block` → can pass with dataset in describe block' flowId='1234']
|
||||||
##teamcity[testSuiteFinished name='Tests/tests/SuccessOnly' flowId='1234']
|
##teamcity[testSuiteFinished name='Tests/tests/SuccessOnly' flowId='1234']
|
||||||
|
|
||||||
[90mTests:[39m [32;1m3 passed[39;22m[90m (3 assertions)[39m
|
[90mTests:[39m [32;1m4 passed[39;22m[90m (4 assertions)[39m
|
||||||
[90mDuration:[39m [39m1.00s[39m
|
[90mDuration:[39m [39m1.00s[39m
|
||||||
|
|
||||||
|
|||||||
@ -1782,4 +1782,4 @@
|
|||||||
✓ pass with dataset with ('my-datas-set-value')
|
✓ pass with dataset with ('my-datas-set-value')
|
||||||
✓ within describe → 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, 39 todos, 35 skipped, 1188 passed (2814 assertions)
|
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 35 skipped, 1188 passed (2813 assertions)
|
||||||
@ -13,3 +13,9 @@ test('can also pass', function () {
|
|||||||
test('can pass with dataset', function ($value) {
|
test('can pass with dataset', function ($value) {
|
||||||
expect($value)->toEqual(true);
|
expect($value)->toEqual(true);
|
||||||
})->with([true]);
|
})->with([true]);
|
||||||
|
|
||||||
|
describe('block', function () {
|
||||||
|
test('can pass with dataset in describe block', function ($number) {
|
||||||
|
expect($number)->toBeInt();
|
||||||
|
})->with([1]);
|
||||||
|
});
|
||||||
|
|||||||
@ -75,7 +75,7 @@ test('pass with dataset', function ($data) {
|
|||||||
[$filename] = TestSuite::getInstance()->snapshots->get();
|
[$filename] = TestSuite::getInstance()->snapshots->get();
|
||||||
|
|
||||||
expect($filename)->toStartWith('tests/.pest/snapshots/')
|
expect($filename)->toStartWith('tests/.pest/snapshots/')
|
||||||
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap')
|
->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
|
||||||
->and($this->snapshotable)->toMatchSnapshot();
|
->and($this->snapshotable)->toMatchSnapshot();
|
||||||
})->with(['my-datas-set-value']);
|
})->with(['my-datas-set-value']);
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ describe('within describe', function () {
|
|||||||
[$filename] = TestSuite::getInstance()->snapshots->get();
|
[$filename] = TestSuite::getInstance()->snapshots->get();
|
||||||
|
|
||||||
expect($filename)->toStartWith('tests/.pest/snapshots/')
|
expect($filename)->toStartWith('tests/.pest/snapshots/')
|
||||||
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap')
|
->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
|
||||||
->and($this->snapshotable)->toMatchSnapshot();
|
->and($this->snapshotable)->toMatchSnapshot();
|
||||||
});
|
});
|
||||||
})->with(['my-datas-set-value']);
|
})->with(['my-datas-set-value']);
|
||||||
|
|||||||
@ -36,8 +36,8 @@ test('junit output', function () use ($normalizedPath, $run) {
|
|||||||
expect($result['testsuite']['@attributes'])
|
expect($result['testsuite']['@attributes'])
|
||||||
->name->toBe('Tests\tests\SuccessOnly')
|
->name->toBe('Tests\tests\SuccessOnly')
|
||||||
->file->toBe($normalizedPath('tests/.tests/SuccessOnly.php'))
|
->file->toBe($normalizedPath('tests/.tests/SuccessOnly.php'))
|
||||||
->tests->toBe('3')
|
->tests->toBe('4')
|
||||||
->assertions->toBe('3')
|
->assertions->toBe('4')
|
||||||
->errors->toBe('0')
|
->errors->toBe('0')
|
||||||
->failures->toBe('0')
|
->failures->toBe('0')
|
||||||
->skipped->toBe('0');
|
->skipped->toBe('0');
|
||||||
|
|||||||
@ -16,7 +16,7 @@ $run = function () {
|
|||||||
|
|
||||||
test('parallel', function () use ($run) {
|
test('parallel', function () use ($run) {
|
||||||
expect($run('--exclude-group=integration'))
|
expect($run('--exclude-group=integration'))
|
||||||
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 26 skipped, 1178 passed (2790 assertions)')
|
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 26 skipped, 1177 passed (2789 assertions)')
|
||||||
->toContain('Parallel: 3 processes');
|
->toContain('Parallel: 3 processes');
|
||||||
})->skipOnWindows();
|
})->skipOnWindows();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user