diff --git a/src/Expectation.php b/src/Expectation.php index d39d0904a..89d86f53d 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -177,6 +177,49 @@ public function sequence(...$callbacks): Expectation return $this; } + /** + * If the subject matches one of the expressions, the callback in the expression is run. + * + * @template TMatchValue + * + * @param Closure|bool|string $subject + * @param array $expressions + * + * @return \Pest\Expectation + */ + public function match($subject, array $expressions): Expectation + { + $subject = is_callable($subject) + ? $subject + : function () use ($subject) { + return $subject; + }; + + $subject = $subject(); + $keys = array_keys($expressions); + + if (in_array($subject, ['0', '1', false, true], true)) { + $subject = (int) $subject; + } + + foreach ($expressions as $key => $callback) { + if ($subject !== $key) { + continue; + } + + if (is_callable($callback)) { + $callback(new self($this->value)); + continue; + } + + $this->and($this->value)->toEqual($callback); + + break; + } + + return $this; + } + /** * Apply the callback if the given "condition" is truthy. * diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 40ab0a49f..2e201e93c 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -163,6 +163,17 @@ ✓ it properly parses json string ✓ fails with broken json string + PASS Tests\Features\Expect\matchExpectation + ✓ it pass + ✓ it failures + ✓ it runs with truthy + ✓ it runs with falsy + ✓ it runs with truthy closure condition + ✓ it runs with falsy closure condition + ✓ it can be passed non-callable values + ✓ it passes with empty data + ✓ it can be used in higher order tests + PASS Tests\Features\Expect\not ✓ not property calls diff --git a/tests/Features/Expect/matchExpectation.php b/tests/Features/Expect/matchExpectation.php new file mode 100644 index 000000000..f577cedc4 --- /dev/null +++ b/tests/Features/Expect/matchExpectation.php @@ -0,0 +1,150 @@ +matched = null; +}); + +it('pass', function () { + expect('baz') + ->match('foo', [ + 'bar' => function ($value) { + $this->matched = 'bar'; + + return $value->toEqual('bar'); + }, + 'foo' => function ($value) { + $this->matched = 'baz'; + + return $value->toEqual('baz'); + }, + ] + ) + ->toEqual($this->matched); + + expect(static::getCount())->toBe(2); +}); + +it('failures', function () { + expect(true) + ->match('foo', [ + 'bar' => function ($value) { + return $value->toBeTrue(); + }, + 'foo' => function ($value) { + return $value->toBeFalse(); + }, + ] + ); +})->throws(ExpectationFailedException::class, 'true is false'); + +it('runs with truthy', function () { + expect('foo') + ->match(1, [ + 'bar' => function ($value) { + $this->matched = 'bar'; + + return $value->toEqual('bar'); + }, + true => function ($value) { + $this->matched = 'foo'; + + return $value->toEqual('foo'); + }, + ] + ) + ->toEqual($this->matched); + + expect(static::getCount())->toBe(2); +}); + +it('runs with falsy', function () { + expect('foo') + ->match(false, [ + 'bar' => function ($value) { + $this->matched = 'bar'; + + return $value->toEqual('bar'); + }, + false => function ($value) { + $this->matched = 'foo'; + + return $value->toEqual('foo'); + }, + ] + ) + ->toEqual($this->matched); + + expect(static::getCount())->toBe(2); +}); + +it('runs with truthy closure condition', function () { + expect('foo') + ->match( + function () { return '1'; }, [ + 'bar' => function ($value) { + $this->matched = 'bar'; + + return $value->toEqual('bar'); + }, + true => function ($value) { + $this->matched = 'foo'; + + return $value->toEqual('foo'); + }, + ] + ) + ->toEqual($this->matched); + + expect(static::getCount())->toBe(2); +}); + +it('runs with falsy closure condition', function () { + expect('foo') + ->match( + function () { return '0'; }, [ + 'bar' => function ($value) { + $this->matched = 'bar'; + + return $value->toEqual('bar'); + }, + false => function ($value) { + $this->matched = 'foo'; + + return $value->toEqual('foo'); + }, + ] + ) + ->toEqual($this->matched); + + expect(static::getCount())->toBe(2); +}); + +it('can be passed non-callable values', function () { + expect('foo') + ->match('pest', [ + 'bar' => 'foo', + 'pest' => 'baz', + ] + ); +})->throws(ExpectationFailedException::class, 'two strings are equal'); + +it('passes with empty data', function () { + expect('foo') + ->match('bar', []) + ->toEqual('foo'); +}); + +it('can be used in higher order tests') + ->expect(true) + ->match( + function () { return true; }, [ + false => function ($value) { + return $value->toBeFalse(); + }, + true => function ($value) { + return $value->toBeTrue(); + }, + ] + );