Compare commits

..

14 Commits

Author SHA1 Message Date
60c0636523 release: v1.18.0 2021-08-30 00:05:26 +01:00
16b6f96b47 Merge pull request #389 from dansysanalyst/toHaveLength-mblen
Use mb_strlen instead of grapheme_strlen
2021-08-29 12:18:38 +01:00
042f2ec3f3 Use mb_strlen instead of grapheme_strlen
Due to inconsistent behave, mb_strlen will be used.
2021-08-29 12:55:37 +02:00
851ce36010 Merge pull request #386 from dansysanalyst/expectation-toHaveLength
Adds toHaveLength() Expectation & Tests
2021-08-29 00:45:39 +01:00
4f386894bd Revert "Use toHaveCount"
This reverts commit 2289adade2.
2021-08-28 18:14:55 +02:00
2289adade2 Use toHaveCount
changes to toHaveCount
2021-08-28 17:54:29 +02:00
29e21e3814 Merge pull request #385 from dansysanalyst/snapshot-contrib
Update snapshots
2021-08-28 16:47:25 +01:00
8367af22e7 Adds toHaveLength() Expectation & Tests
Adds toHaveLength() Expectation and its tests. toHaveLength checks if the given value has the informed length. Works with strings, array, object and collections.
2021-08-28 17:02:09 +02:00
e3d678dc04 Update snapshots
Adds update:snapshots to CONTRIBUTING
2021-08-28 16:30:17 +02:00
4ae482c707 feat: adds support for nunomaduro/collision:^6.0 2021-08-27 22:32:18 +01:00
075c31bc78 release: v1.17.0 2021-08-26 21:17:03 +01:00
2125bf9668 chore: adjusts tests 2021-08-26 21:14:56 +01:00
dbf3c0a8cf Merge pull request #361 from kbond/throw-expectation
Add `toThrow` expectation
2021-08-26 21:02:38 +01:00
c776bcf86d add toThrow expectation 2021-08-01 12:33:09 -04:00
9 changed files with 217 additions and 4 deletions

View File

@ -4,6 +4,15 @@ 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.18.0 (2021-08-30)](https://github.com/pestphp/pest/compare/v1.17.0...v1.18.0)
### Added
- `toHaveLength` expectation ([#386](https://github.com/pestphp/pest/pull/386))
- `nunomaduro/collision:^6.0` support ([4ae482c](https://github.com/pestphp/pest/commit/4ae482c7073fb77782b8a4b5738ef1fcea0f82ab))
## [v1.17.0 (2021-08-26)](https://github.com/pestphp/pest/compare/v1.16.0...v1.17.0)
### Added
- `toThrow` expectation ([#361](https://github.com/pestphp/pest/pull/361))
## [v1.16.0 (2021-08-19)](https://github.com/pestphp/pest/compare/v1.15.0...v1.16.0)
### Added
- Support for new parallel options ([#369](https://github.com/pestphp/pest/pull/369))

View File

@ -31,6 +31,10 @@ composer lint
```
## Tests
Update the snapshots:
```bash
composer update:snapshots
```
Run all tests:
```bash
composer test

View File

@ -18,7 +18,7 @@
],
"require": {
"php": "^7.3 || ^8.0",
"nunomaduro/collision": "^5.4.0",
"nunomaduro/collision": "^5.4.0|^6.0",
"pestphp/pest-plugin": "^1.0.0",
"phpunit/phpunit": "^9.5.5"
},

View File

@ -5,13 +5,19 @@ declare(strict_types=1);
namespace Pest;
use BadMethodCallException;
use Closure;
use InvalidArgumentException;
use Pest\Concerns\Extendable;
use Pest\Concerns\RetrievesValues;
use Pest\Support\Arr;
use Pest\Support\NullClosure;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use ReflectionFunction;
use ReflectionNamedType;
use SebastianBergmann\Exporter\Exporter;
use Throwable;
/**
* @internal
@ -321,6 +327,36 @@ final class Expectation
return $this;
}
/**
* Asserts that $number matches value's Length.
*/
public function toHaveLength(int $number): Expectation
{
if (is_string($this->value)) {
Assert::assertEquals($number, mb_strlen($this->value));
return $this;
}
if (is_iterable($this->value)) {
return $this->toHaveCount($number);
}
if (is_object($this->value)) {
if (method_exists($this->value, 'toArray')) {
$array = $this->value->toArray();
} else {
$array = (array) $this->value;
}
Assert::assertCount($number, $array);
return $this;
}
throw new BadMethodCallException('Expectation value length is not countable.');
}
/**
* Asserts that $count matches the number of elements of the value.
*/
@ -749,6 +785,56 @@ final class Expectation
return $this;
}
/**
* Asserts that executing value throws an exception.
*
* @param (Closure(Throwable): mixed)|string $exception
*/
public function toThrow($exception, string $exceptionMessage = null): Expectation
{
$callback = NullClosure::create();
if ($exception instanceof Closure) {
$callback = $exception;
$parameters = (new ReflectionFunction($exception))->getParameters();
if (1 !== count($parameters)) {
throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.');
}
if (!($type = $parameters[0]->getType()) instanceof ReflectionNamedType) {
throw new InvalidArgumentException('The given closure\'s parameter must be type-hinted as the class string.');
}
$exception = $type->getName();
}
try {
($this->value)();
} catch (Throwable $e) { // @phpstan-ignore-line
if (!class_exists($exception)) {
Assert::assertStringContainsString($exception, $e->getMessage());
return $this;
}
if ($exceptionMessage !== null) {
Assert::assertStringContainsString($exceptionMessage, $e->getMessage());
}
Assert::assertInstanceOf($exception, $e);
$callback($e);
return $this;
}
if (!class_exists($exception)) {
throw new ExpectationFailedException("Exception with message \"{$exception}\" not thrown.");
}
throw new ExpectationFailedException("Exception \"{$exception}\" not thrown.");
}
/**
* Exports the given value.
*

View File

@ -6,7 +6,7 @@ namespace Pest;
function version(): string
{
return '1.16.0';
return '1.18.0';
}
function testDirectory(string $file = ''): string

View File

@ -22,7 +22,7 @@ use PHPUnit\Framework\TestCase;
final class TestRepository
{
/**
* @var string
* @var non-empty-string
*/
private const SEPARATOR = '>>>';

View File

@ -419,6 +419,20 @@
✓ failures
✓ not failures
PASS Tests\Features\Expect\toHaveLength
✓ it passes with ('Fortaleza')
✓ it passes with ('Sollefteå')
✓ it passes with ('Ιεράπετρα')
✓ it passes with (stdClass Object (...))
✓ it passes with (Illuminate\Support\Collection Object (...))
✓ it passes with array
✓ it passes with *not*
✓ it properly fails with *not*
✓ it fails with (1)
✓ it fails with (1.5)
✓ it fails with (true)
✓ it fails with (null)
PASS Tests\Features\Expect\toHaveProperty
✓ pass
✓ failures
@ -449,6 +463,19 @@
✓ failures
✓ not failures
PASS Tests\Features\Expect\toThrow
✓ passes
✓ failures 1
✓ failures 2
✓ failures 3
✓ failures 4
✓ failures 5
✓ failures 6
✓ failures 7
✓ not failures
✓ closure missing parameter
✓ closure missing type-hint
PASS Tests\Features\Helpers
✓ it can set/get properties on $this
✓ it throws error if property do not exist
@ -649,5 +676,5 @@
✓ it is a test
✓ it uses correct parent class
Tests: 4 incompleted, 9 skipped, 421 passed
Tests: 4 incompleted, 9 skipped, 444 passed

View File

@ -0,0 +1,27 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
it('passes', function ($value) {
expect($value)->toHaveLength(9);
})->with([
'Fortaleza', 'Sollefteå', 'Ιεράπετρα',
(object) [1, 2, 3, 4, 5, 6, 7, 8, 9],
collect([1, 2, 3, 4, 5, 6, 7, 8, 9]),
]);
it('passes with array', function () {
expect([1, 2, 3])->toHaveLength(3);
});
it('passes with *not*', function () {
expect('')->not->toHaveLength(1);
});
it('properly fails with *not*', function () {
expect('pest')->not->toHaveLength(4);
})->throws(ExpectationFailedException::class);
it('fails', function ($value) {
expect($value)->toHaveLength(1);
})->with([1, 1.5, true, null])->throws(BadMethodCallException::class);

View File

@ -0,0 +1,60 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('passes', function () {
expect(function () { throw new RuntimeException(); })->toThrow(RuntimeException::class);
expect(function () { throw new RuntimeException(); })->toThrow(Exception::class);
expect(function () { throw new RuntimeException(); })->toThrow(function (RuntimeException $e) {});
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (Exception $e) {
expect($e->getMessage())->toBe('actual message');
});
expect(function () {})->not->toThrow(Exception::class);
expect(function () { throw new RuntimeException('actual message'); })->toThrow('actual message');
expect(function () { throw new Exception(); })->not->toThrow(RuntimeException::class);
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'actual message');
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (RuntimeException $e) {}, 'actual message');
});
test('failures 1', function () {
expect(function () {})->toThrow(RuntimeException::class);
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
test('failures 2', function () {
expect(function () {})->toThrow(function (RuntimeException $e) {});
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
test('failures 3', function () {
expect(function () { throw new Exception(); })->toThrow(function (RuntimeException $e) {});
})->throws(ExpectationFailedException::class, 'Failed asserting that Exception Object');
test('failures 4', function () {
expect(function () { throw new Exception('actual message'); })
->toThrow(function (Exception $e) {
expect($e->getMessage())->toBe('expected message');
});
})->throws(ExpectationFailedException::class, 'Failed asserting that two strings are identical');
test('failures 5', function () {
expect(function () { throw new Exception('actual message'); })->toThrow('expected message');
})->throws(ExpectationFailedException::class, 'Failed asserting that \'actual message\' contains "expected message".');
test('failures 6', function () {
expect(function () {})->toThrow('actual message');
})->throws(ExpectationFailedException::class, 'Exception with message "actual message" not thrown');
test('failures 7', function () {
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'expected message');
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(function () { throw new RuntimeException(); })->not->toThrow(RuntimeException::class);
})->throws(ExpectationFailedException::class);
test('closure missing parameter', function () {
expect(function () {})->toThrow(function () {});
})->throws(InvalidArgumentException::class, 'The given closure must have a single parameter type-hinted as the class string.');
test('closure missing type-hint', function () {
expect(function () {})->toThrow(function ($e) {});
})->throws(InvalidArgumentException::class, 'The given closure\'s parameter must be type-hinted as the class string.');