Skip to content

Commit 9486733

Browse files
committed
Added fulfills to enable custom truth tests
1 parent c6fb50e commit 9486733

File tree

4 files changed

+140
-3
lines changed

4 files changed

+140
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ The following additional assertions are available:
259259
| `enumValueExists($value, string $enumClass, string $message = '')` | Check that a value is a value in an enum |
260260
| `keysExists($value, array $keys, string $message = '')` | Check that a value has all the specified keys (array or ArrayAccess) |
261261
| `nullOr($value, callable $callback)` | Execute the callback if the value is not null |
262+
| `fulfills($value, callable $callback, string $message = '')` | Check that a value fulfills a custom condition |
262263

263264
## Development
264265

src/Base.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
*/
1818
class Base extends WebmozartAssert
1919
{
20-
protected static function reportInvalidArgument($message): void
20+
protected static function reportInvalidArgument($message, ?InvalidArgumentException $previous = null): void
2121
{
22-
throw new InvalidArgumentException($message);
22+
throw new InvalidArgumentException($message, previous: $previous);
2323
}
2424

2525
/**
@@ -264,7 +264,7 @@ public static function keysExists($value, array $keys, string $message = ''): vo
264264
foreach ($keys as $key) {
265265
try {
266266
self::keyExists($value, $key);
267-
} catch (InvalidArgumentException) {
267+
} catch (InvalidArgumentException $exception) {
268268
$normalizer = fn (array $keys) => implode(
269269
', ',
270270
array_map(
@@ -282,8 +282,39 @@ public static function keysExists($value, array $keys, string $message = ''): vo
282282
$expected,
283283
$actual,
284284
),
285+
$exception
285286
);
286287
}
287288
}
288289
}
290+
291+
/**
292+
* @param mixed $value
293+
* @param callable $callback
294+
* @param string $message
295+
* @return void
296+
*/
297+
public static function fulfills($value, callable $callback, string $message = ''): void
298+
{
299+
$defaultMessage = 'Value does not fulfill the given condition.';
300+
301+
$previous = null;
302+
try {
303+
$match = $callback($value);
304+
} catch (InvalidArgumentException $exception) {
305+
$match = $exception;
306+
$previous = $exception;
307+
}
308+
309+
if ($match instanceof InvalidArgumentException) {
310+
$defaultMessage .= ' ' . $match->getMessage();
311+
}
312+
313+
if ($match !== true) {
314+
static::reportInvalidArgument(
315+
$message ?: $defaultMessage,
316+
$previous
317+
);
318+
}
319+
}
289320
}

src/Concerns/Mixin.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,78 @@ public function allNullOrFloat($message = ''): static
13451345

13461346
// endregion [ Base::float ]
13471347

1348+
// region [ Base::fulfills ]
1349+
1350+
/**
1351+
* @param callable $callback
1352+
* @param string $message
1353+
*
1354+
* @see Base::fulfills
1355+
*/
1356+
public function fulfills($callback, $message = ''): static
1357+
{
1358+
$this->used = true;
1359+
1360+
Base::fulfills($this->value, ...func_get_args());
1361+
1362+
return $this;
1363+
}
1364+
1365+
/**
1366+
* @param callable $callback
1367+
* @param string $message
1368+
*
1369+
* @see Base::nullOrFulfills
1370+
*/
1371+
public function nullOrFulfills($callback, $message = ''): static
1372+
{
1373+
$this->used = true;
1374+
1375+
if ($this->value === null) {
1376+
return $this;
1377+
}
1378+
1379+
Base::fulfills($this->value, ...func_get_args());
1380+
1381+
return $this;
1382+
}
1383+
1384+
/**
1385+
* @param callable $callback
1386+
* @param string $message
1387+
*
1388+
* @see Base::allFulfills
1389+
*/
1390+
public function allFulfills($callback, $message = ''): static
1391+
{
1392+
$this->used = true;
1393+
1394+
$args = func_get_args();
1395+
1396+
return $this->each(
1397+
fn (self $assert) => $assert->fulfills(...$args)
1398+
);
1399+
}
1400+
1401+
/**
1402+
* @param callable $callback
1403+
* @param string $message
1404+
*
1405+
* @see Base::allNullOrFulfills
1406+
*/
1407+
public function allNullOrFulfills($callback, $message = ''): static
1408+
{
1409+
$this->used = true;
1410+
1411+
$args = func_get_args();
1412+
1413+
return $this->each(
1414+
fn (self $assert) => $assert->nullOrFulfills(...$args)
1415+
);
1416+
}
1417+
1418+
// endregion [ Base::fulfills ]
1419+
13481420
// region [ Base::greaterThan ]
13491421

13501422
/**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use ExeQue\FluentAssert\Assert;
6+
use ExeQue\FluentAssert\Exceptions\InvalidArgumentException;
7+
8+
it('can fulfill a condition', function () {
9+
10+
Assert::for('hello')
11+
->fulfills(fn (string $value) => $value === 'hello')
12+
->fulfills(fn (string $value) => strlen($value) === 5);
13+
14+
$this->expectNotToPerformAssertions();
15+
});
16+
17+
it('fails if condition is not fulfilled', function () {
18+
expect(
19+
fn () => Assert::for('hello')
20+
->fulfills(fn (string $value) => $value === 'world', 'Value is not "world"'),
21+
)->toThrow(InvalidArgumentException::class, 'Value is not "world"');
22+
});
23+
24+
it('fails if a sub assertion is not fulfilled', function () {
25+
expect(
26+
fn () => Assert::for('hello')
27+
->fulfills(function (string $value) {
28+
Assert::for($value)->same('world', 'Value is not "world"');
29+
30+
return true;
31+
}),
32+
)->toThrow(InvalidArgumentException::class, 'Value is not "world"');
33+
});

0 commit comments

Comments
 (0)