diff --git a/CHANGELOG.md b/CHANGELOG.md index 51343a9f..0b05a97d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [v1.13.0 (2021-07-28)](https://github.com/pestphp/pest/compare/v1.12.0...v1.13.0) +### Added +- `toBeIn` expectation ([#363](https://github.com/pestphp/pest/pull/363)) + +### Fixed +- `skip` with false condition marking test as skipped ([22b822c](https://github.com/pestphp/pest/commit/22b822ce87a3d19d84960fa5c93eb286820b525d)) + +## [v1.12.0 (2021-07-26)](https://github.com/pestphp/pest/compare/v1.11.0...v1.12.0) +### Added +- `--force` option to override tests in `pest:test` artisan command ([#353](https://github.com/pestphp/pest/pull/353)) +- Support for PHPUnit `^9.3.7` ([ca9d783](https://github.com/pestphp/pest/commit/ca9d783cf942a2caabc85ff7a728c7f28350c67a)) + +### Fixed +- `beforeAll` and `afterAll` behind called multiple times per test ([#357](https://github.com/pestphp/pest/pull/357)) + ## [v1.11.0 (2021-07-21)](https://github.com/pestphp/pest/compare/v1.10.0...v1.11.0) ### Added - Support for interacting with datasets in higher order tests ([#352](https://github.com/pestphp/pest/pull/352)) diff --git a/README.md b/README.md index 6823ffee..541d9f24 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,9 @@ We would like to extend our thanks to the following sponsors for funding Pest de ### Premium Sponsors - **[Akaunting](https://akaunting.com)** -- **[Scout APM](https://scoutapm.com)** +- **[Codecourse](https://codecourse.com/)** - **[Meema](https://meema.io/)** +- **[Scout APM](https://scoutapm.com)** - **[Spatie](https://spatie.be/)** -Pest was created by **[Nuno Maduro](https://twitter.com/enunomaduro)** under the **[Sponsorware license](https://github.com/sponsorware/docs)**. It got open-sourced and is now 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)**. diff --git a/composer.json b/composer.json index 2224ac7f..bee8f8b7 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "php": "^7.3 || ^8.0", "nunomaduro/collision": "^5.4.0", "pestphp/pest-plugin": "^1.0.0", - "phpunit/phpunit": ">= 9.3.7 <= 9.5.6" + "phpunit/phpunit": "^9.3.7" }, "autoload": { "psr-4": { diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php index 5c72e0f3..d8137311 100644 --- a/src/Concerns/Testable.php +++ b/src/Concerns/Testable.php @@ -73,6 +73,8 @@ trait Testable { $this->__test = $test; $this->__description = $description; + self::$beforeAll = null; + self::$afterAll = null; parent::__construct('__test', $data); } diff --git a/src/Expectation.php b/src/Expectation.php index e41f21d0..e76a30bd 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -371,6 +371,18 @@ final class Expectation return $this; } + /** + * Asserts that the value is one of the given values. + * + * @param iterable $values + */ + public function toBeIn(iterable $values): Expectation + { + Assert::assertContains($this->value, $values); + + return $this; + } + /** * Asserts that the value is infinite. */ diff --git a/src/Laravel/Commands/PestTestCommand.php b/src/Laravel/Commands/PestTestCommand.php index 55d9dc3a..ee18e076 100644 --- a/src/Laravel/Commands/PestTestCommand.php +++ b/src/Laravel/Commands/PestTestCommand.php @@ -21,7 +21,7 @@ final class PestTestCommand extends Command * * @var string */ - protected $signature = 'pest:test {name : The name of the file} {--unit : Create a unit test} {--dusk : Create a Dusk test} {--test-directory=tests : The name of the tests directory}'; + protected $signature = 'pest:test {name : The name of the file} {--unit : Create a unit test} {--dusk : Create a Dusk test} {--test-directory=tests : The name of the tests directory} {--force : Overwrite the existing test file with the same name}'; /** * The console command description. @@ -56,7 +56,7 @@ final class PestTestCommand extends Command File::makeDirectory(dirname($target), 0777, true, true); } - if (File::exists($target)) { + if (File::exists($target) && !(bool) $this->option('force')) { throw new InvalidConsoleArgument(sprintf('%s already exist', $target)); } diff --git a/src/PendingObjects/TestCall.php b/src/PendingObjects/TestCall.php index 4a4b78d5..d97f1ac2 100644 --- a/src/PendingObjects/TestCall.php +++ b/src/PendingObjects/TestCall.php @@ -150,6 +150,9 @@ final class TestCall ? $conditionOrMessage : $message; + /** @var callable(): bool $condition */ + $condition = $condition->bindTo(null); + $this->testCaseFactory ->chains ->addWhen($condition, Backtrace::file(), Backtrace::line(), 'markTestSkipped', [$message]); diff --git a/src/Pest.php b/src/Pest.php index cdc57677..d9efe05f 100644 --- a/src/Pest.php +++ b/src/Pest.php @@ -6,7 +6,7 @@ namespace Pest; function version(): string { - return '1.11.0'; + return '1.13.0'; } function testDirectory(string $file = ''): string diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 66fbdcbd..0a23a6e9 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -101,6 +101,7 @@ ✓ it gives access the the underlying expectException ✓ it catch exceptions ✓ it catch exceptions and messages + ✓ it can just define the message PASS Tests\Features\Expect\HigherOrder\methods ✓ it can access methods @@ -112,11 +113,14 @@ ✓ it works with sequence ✓ it can compose complex expectations ✓ it can handle nested method calls + ✓ it works with higher order tests PASS Tests\Features\Expect\HigherOrder\methodsAndProperties ✓ it can access methods and properties ✓ it can handle nested methods and properties + ✓ it works with higher order tests ✓ it can start a new higher order expectation using the and syntax + ✓ it can start a new higher order expectation using the and syntax in higher order tests PASS Tests\Features\Expect\HigherOrder\properties ✓ it allows properties to be accessed from the value @@ -128,6 +132,7 @@ ✓ it can compose complex expectations ✓ it works with objects ✓ it works with nested properties + ✓ it works with higher order tests PASS Tests\Features\Expect\each ✓ an exception is thrown if the the type is not iterable @@ -216,6 +221,11 @@ PASS Tests\Features\Expect\toBeGreatherThanOrEqual ✓ passes ✓ failures + ✓ not failures + + PASS Tests\Features\Expect\toBeIn + ✓ passes + ✓ failures ✓ not failures PASS Tests\Features\Expect\toBeInfinite @@ -411,7 +421,12 @@ ✓ it proxies calls to object ✓ it is capable doing multiple assertions ✓ it resolves expect callables correctly + ✓ does not treat method names as callables ✓ it can tap into the test + ✓ it can pass datasets into the expect callables with (1, 2, 3) + ✓ it can pass datasets into the tap callable with (1, 2, 3) + ✓ it can pass shared datasets into callables with (1) + ✓ it can pass shared datasets into callables with (2) WARN Tests\Features\Incompleted … incompleted @@ -432,8 +447,8 @@ PASS Tests\Features\PendingHigherOrderTests ✓ get 'foo' - ✓ get 'foo' → get 'bar' → expect true → toBeTrue - ✓ get 'foo' → expect true → toBeTrue + ✓ get 'foo' → get 'bar' → expect true → toBeTrue + ✓ get 'foo' → expect true → toBeTrue WARN Tests\Features\Skip ✓ it do not skips @@ -444,6 +459,8 @@ ✓ it do not skips with falsy closure condition - it skips with condition and message → skipped because foo - it skips when skip after assertion + - it can use something in the test case as a condition → This test was skipped + - it can user higher order callables and skip PASS Tests\Features\Test ✓ a test @@ -457,12 +474,14 @@ PASS Tests\Hooks\AfterAllTest ✓ global afterAll execution order + ✓ it only gets called once per file PASS Tests\Hooks\AfterEachTest ✓ global afterEach execution order PASS Tests\Hooks\BeforeAllTest ✓ global beforeAll execution order + ✓ it only gets called once per file PASS Tests\Hooks\BeforeEachTest ✓ global beforeEach execution order @@ -582,5 +601,5 @@ ✓ it is a test ✓ it uses correct parent class - Tests: 4 incompleted, 7 skipped, 365 passed - + Tests: 4 incompleted, 9 skipped, 381 passed + \ No newline at end of file diff --git a/tests/Features/Expect/toBeIn.php b/tests/Features/Expect/toBeIn.php new file mode 100644 index 00000000..6636a51b --- /dev/null +++ b/tests/Features/Expect/toBeIn.php @@ -0,0 +1,16 @@ +toBeIn(['a', 'b', 'c']); + expect('d')->not->toBeIn(['a', 'b', 'c']); +}); + +test('failures', function () { + expect('d')->toBeIn(['a', 'b', 'c']); +})->throws(ExpectationFailedException::class); + +test('not failures', function () { + expect('a')->not->toBeIn(['a', 'b', 'c']); +})->throws(ExpectationFailedException::class); diff --git a/tests/Hooks/AfterAllTest.php b/tests/Hooks/AfterAllTest.php index a34a5847..b9042815 100644 --- a/tests/Hooks/AfterAllTest.php +++ b/tests/Hooks/AfterAllTest.php @@ -2,26 +2,50 @@ global $globalHook; +// NOTE: this test does not have a $globalHook->calls offset since it is first +// in the directory and thus will always run before the others. See also the +// BeforeAllTest.php for details. + uses()->afterAll(function () use ($globalHook) { expect($globalHook) ->toHaveProperty('afterAll') ->and($globalHook->afterAll) - ->toBe(0); + ->toBe(0) + ->and($globalHook->calls) + ->afterAll + ->toBe(1); $globalHook->afterAll = 1; + $globalHook->calls->afterAll++; }); afterAll(function () use ($globalHook) { expect($globalHook) ->toHaveProperty('afterAll') ->and($globalHook->afterAll) - ->toBe(1); + ->toBe(1) + ->and($globalHook->calls) + ->afterAll + ->toBe(2); $globalHook->afterAll = 2; + $globalHook->calls->afterAll++; }); test('global afterAll execution order', function () use ($globalHook) { expect($globalHook) ->not() - ->toHaveProperty('afterAll'); + ->toHaveProperty('afterAll') + ->and($globalHook->calls) + ->afterAll + ->toBe(0); +}); + +it('only gets called once per file', function () use ($globalHook) { + expect($globalHook) + ->not() + ->toHaveProperty('afterAll') + ->and($globalHook->calls) + ->afterAll + ->toBe(0); }); diff --git a/tests/Hooks/BeforeAllTest.php b/tests/Hooks/BeforeAllTest.php index 11c996c5..05e57252 100644 --- a/tests/Hooks/BeforeAllTest.php +++ b/tests/Hooks/BeforeAllTest.php @@ -1,28 +1,56 @@ beforeAll(function () use ($globalHook) { +// HACK: we have to determine our $globalHook->calls baseline. This is because +// two other tests are executed before this one due to filename ordering. +$args = $_SERVER['argv'] ?? []; +$single = isset($args[1]) && Str::endsWith(__FILE__, $args[1]); +$offset = $single ? 0 : 2; + +uses()->beforeAll(function () use ($globalHook, $offset) { expect($globalHook) ->toHaveProperty('beforeAll') ->and($globalHook->beforeAll) - ->toBe(0); + ->toBe(0) + ->and($globalHook->calls) + ->beforeAll + ->toBe(1 + $offset); $globalHook->beforeAll = 1; + $globalHook->calls->beforeAll++; }); -beforeAll(function () use ($globalHook) { +beforeAll(function () use ($globalHook, $offset) { expect($globalHook) ->toHaveProperty('beforeAll') ->and($globalHook->beforeAll) - ->toBe(1); + ->toBe(1) + ->and($globalHook->calls) + ->beforeAll + ->toBe(2 + $offset); $globalHook->beforeAll = 2; + $globalHook->calls->beforeAll++; }); -test('global beforeAll execution order', function () use ($globalHook) { +test('global beforeAll execution order', function () use ($globalHook, $offset) { expect($globalHook) ->toHaveProperty('beforeAll') ->and($globalHook->beforeAll) - ->toBe(2); + ->toBe(2) + ->and($globalHook->calls) + ->beforeAll + ->toBe(3 + $offset); +}); + +it('only gets called once per file', function () use ($globalHook, $offset) { + expect($globalHook) + ->beforeAll + ->toBe(2) + ->and($globalHook->calls) + ->beforeAll + ->toBe(3 + $offset); }); diff --git a/tests/Pest.php b/tests/Pest.php index a8cd868d..d3fe584f 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -2,7 +2,8 @@ uses()->group('integration')->in('Visual'); -$globalHook = (object) []; // NOTE: global test value container to be mutated and checked across files, as needed +// NOTE: global test value container to be mutated and checked across files, as needed +$globalHook = (object) ['calls' => (object) ['beforeAll' => 0, 'afterAll' => 0]]; uses() ->beforeEach(function () { @@ -10,11 +11,13 @@ uses() }) ->beforeAll(function () use ($globalHook) { $globalHook->beforeAll = 0; + $globalHook->calls->beforeAll++; }) ->afterEach(function () { $this->ith = 0; }) ->afterAll(function () use ($globalHook) { $globalHook->afterAll = 0; + $globalHook->calls->afterAll++; }) ->in('Hooks');