From ce2da2020b21eea4f977444acd8e53aad94612cc Mon Sep 17 00:00:00 2001 From: l0gic Date: Thu, 22 Jan 2026 04:34:33 +0500 Subject: [PATCH 1/6] Fix static analysis errors: correct AssertSame call and change non-empty-string to string in PHPDoc --- src/Traits/StringAssertions.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Traits/StringAssertions.php b/src/Traits/StringAssertions.php index c229fc0..e1811a3 100644 --- a/src/Traits/StringAssertions.php +++ b/src/Traits/StringAssertions.php @@ -23,7 +23,7 @@ trait StringAssertions * fact('abc123')->matchesRegularExpression('/^[a-z]+\d+$/'); // Passes * fact('123abc')->matchesRegularExpression('/^[a-z]+\d+$/'); // Fails * - * @param non-empty-string $pattern The regular expression pattern to match against. + * @param string $pattern The regular expression pattern to match against. * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -48,7 +48,7 @@ public function matchesRegularExpression(string $pattern, string $message = ''): * fact('123abc')->notMatchesRegularExpression('/^[a-z]+\d+$/'); // Passes * fact('abc123')->notMatchesRegularExpression('/^[a-z]+\d+$/'); // Fails * - * @param non-empty-string $pattern The regular expression pattern that should not match. + * @param string $pattern The regular expression pattern that should not match. * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -77,7 +77,7 @@ public function notMatchesRegularExpression(string $pattern, string $message = ' * fact('hello world')->containsString('world'); // Passes * fact('hello world')->containsString('foo'); // Fails * - * @param non-empty-string $string The substring to search for. + * @param string $string The substring to search for. * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -102,7 +102,7 @@ public function containsString(string $string, string $message = ''): self * fact('hello world')->notContainsString('foo'); // Passes * fact('hello world')->notContainsString('world'); // Fails * - * @param non-empty-string $string The substring that should not be present. + * @param string $string The substring that should not be present. * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -127,7 +127,7 @@ public function notContainsString(string $string, string $message = ''): self * fact('Hello World')->containsStringIgnoringCase('world'); // Passes * fact('Hello World')->containsStringIgnoringCase('foo'); // Fails * - * @param non-empty-string $string The substring to search for (case-insensitive). + * @param string $string The substring to search for (case-insensitive). * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -152,7 +152,7 @@ public function containsStringIgnoringCase(string $string, string $message = '') * fact('Hello World')->notContainsStringIgnoringCase('foo'); // Passes * fact('Hello World')->notContainsStringIgnoringCase('world'); // Fails * - * @param non-empty-string $string The substring that should not be present (case-insensitive). + * @param string $string The substring that should not be present (case-insensitive). * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -185,7 +185,7 @@ public function notContainsStringIgnoringCase(string $string, string $message = * fact('hello world')->startsWith('hello'); // Passes * fact('world hello')->startsWith('hello'); // Fails * - * @param non-empty-string $prefix The prefix to check for (must not be empty). + * @param string $prefix The prefix to check for (must not be empty). * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. @@ -211,7 +211,7 @@ public function startsWith(string $prefix, string $message = ''): self * fact('file.txt')->endsWith('.txt'); // Passes * fact('txt.file')->endsWith('.txt'); // Fails * - * @param non-empty-string $suffix The suffix to check for (must not be empty). + * @param string $suffix The suffix to check for (must not be empty). * @param string $message Optional custom error message. * * @return self Enables fluent chaining of assertion methods. From 6ce6566086c49021df5e08895d33abdd74669894 Mon Sep 17 00:00:00 2001 From: l0gic Date: Thu, 22 Jan 2026 04:36:58 +0500 Subject: [PATCH 2/6] Add rule for updating PR description when sending PR --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index 88dd9a8..2427e44 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,6 +6,7 @@ This file outlines the requirements and best practices for adding new assertion - **Version Compliance**: Always use the most current version of AGENTS.md and do not rely on cached or outdated copies. Refresh and re-read AGENTS.md before every change to ensure compliance with the latest requirements. - **Branching and Commits**: It is forbidden to commit directly to the `main` branch. All changes must be added via pull request from a feature branch. If the current branch is `main`, MUST checkout to a new branch before changing any files. Do not push changes automatically—only push after explicit user request. +- **Pull Requests**: When creating a pull request, update the PR description with a detailed summary of changes, including new methods added, files modified, and any breaking changes. Ensure the description follows the format: Summary, Changes, Testing, Validation. - **Method Signature**: All new methods must be public, accept an optional `$message` parameter (string, default empty), and return `self` to enable fluent chaining. - **Type Safety**: Specify strict types for parameters where applicable (e.g., `int|float` for numeric comparisons). Avoid `mixed` unless necessary. - **PHPUnit Integration**: Use appropriate PHPUnit assertion methods (e.g., `Assert::assertLessThan`) without named parameters for compatibility. From 624da495cbf08004deb49cf380f18bd836c77122 Mon Sep 17 00:00:00 2001 From: l0gic Date: Thu, 22 Jan 2026 04:41:39 +0500 Subject: [PATCH 3/6] Update README with categorized examples for all new methods; remove named parameters in test assertions --- README.md | 182 ++++++++++++++---- .../FluentAssertionsTestCase.php | 4 +- 2 files changed, 142 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 9a3a0cf..1ebbf37 100644 --- a/README.md +++ b/README.md @@ -18,77 +18,175 @@ Write tests as usual, just use fluent assertions short aliases ``` check($x)->.. ```php // arrange -$user = UserFactory::createOne([ - 'phone' => $phoneBefore = faker()->e164PhoneNumber; -]); +$user = UserFactory::createOne(); // act -$user->setPhone( - $phoneAfter = faker()->e164PhoneNumber -); - -// assert +$user->setPhone($e164PhoneNumber = faker()->e164PhoneNumber); // traditional PHPUnit assertions -self::assertSame(expected: $phoneAfter, actual: $user->getPhone()); -self::assertNotSame(expected: $phoneBefore, actual: $user->getPhone()); +self::assertSame($e164PhoneNumber, $user->getPhone()); + +// fluent assertions +fact($user->getPhone()) + ->is($e164PhoneNumber) + ->isString() + ->startsWith('+7') + // etc. + ; +``` -### Comparison and Equality Methods +### Array assertions ```php -fact(42)->is(42)->equals(42)->not(43); +fact([1, 2, 3])->count(3); // Passes +fact([1, 2])->count(3); // Fails + +fact([1, 2])->notCount(3); // Passes +fact([1, 2, 3])->notCount(3); // Fails + +fact(['a' => ['b' => 'c']])->arrayContainsAssociativeArray(['a' => ['b' => 'c']]); // Passes +fact(['a' => ['b' => 'd']])->arrayContainsAssociativeArray(['a' => ['b' => 'c']]); // Fails + +fact(['a' => 1])->arrayHasKey('a'); // Passes +fact(['a' => 1])->arrayHasKey('b'); // Fails + +fact(['a' => 1])->arrayNotHasKey('b'); // Passes +fact(['a' => 1])->arrayNotHasKey('a'); // Fails + +fact([1, 2, 3])->contains(2); // Passes +fact([1, 2])->contains(3); // Fails + +fact([1, 2])->doesNotContain(3); // Passes +fact([1, 2, 3])->doesNotContain(3); // Fails + +fact([1, 2])->hasSize(2); // Passes +fact([1, 2, 3])->hasSize(2); // Fails + +fact([])->isEmptyArray(); // Passes +fact([1, 2])->isEmptyArray(); // Fails + +fact([1, 2])->isNotEmptyArray(); // Passes +fact([])->isNotEmptyArray(); // Fails + ``` -### Boolean Methods +### Boolean assertions ```php -fact(true)->true()->notFalse(); +fact(true)->true(); // Passes +fact(1)->true(); // Fails due to strict comparison + +fact(false)->notTrue(); // Passes +fact(true)->notTrue(); // Fails + +fact(false)->false(); // Passes +fact(0)->false(); // Fails due to strict comparison + +fact(true)->notFalse(); // Passes +fact(false)->notFalse(); // Fails ``` -### Null Methods + +### Comparison and Equality assertions ```php -fact(null)->null()->notNull(); +fact(42)->is(42); // Passes +fact(42)->is('42'); // Fails due to type difference + +fact(42)->equals(42); // Passes +fact(42)->equals('42'); // Passes due to loose comparison + +fact(42)->not(43); // Passes +fact(42)->not(42); // Fails ``` -### Numeric Methods + +### Null assertions ```php -fact(5)->isLowerThan(10)->isGreaterThan(0)->isPositive()->isNegative()->isZero()->isBetween(1, 10); +fact(null)->null(); // Passes +fact('')->null(); // Fails + +fact(42)->notNull(); // Passes +fact(null)->notNull(); // Fails ``` -### String Methods +### Numeric assertions ```php -fact('hello world')->matchesRegularExpression('/\w+/')->startsWith('hello')->endsWith('world')->hasLength(11)->isEmptyString(); +fact(5)->isLowerThan(10); // Passes +fact(10)->isLowerThan(5); // Fails + +fact(10)->isGreaterThan(5); // Passes +fact(5)->isGreaterThan(10); // Fails + +fact(5)->isPositive(); // Passes +fact(-3)->isPositive(); // Fails + +fact(-3)->isNegative(); // Passes +fact(5)->isNegative(); // Fails + +fact(0)->isZero(); // Passes +fact(0.0)->isZero(); // Passes +fact(1)->isZero(); // Fails + +fact(5)->isBetween(1, 10); // Passes +fact(15)->isBetween(1, 10); // Fails ``` -### Array Methods +### Special assertions ```php -fact([1, 2, 3])->count(3)->contains(2)->doesNotContain(4)->hasSize(3)->isEmptyArray()->isNotEmptyArray(); +fact('01ARZ3NDEKTSV4RRFFQ69G5FAV')->ulid(); // Passes (if valid ULID) +fact('invalid-ulid')->ulid(); // Fails ``` -### Type Checking Methods +### String assertions ```php -fact(42)->isInt()->isString()->instanceOf(int::class)->hasProperty('name')->hasMethod('doSomething'); +fact('abc123')->matchesRegularExpression('/^[a-z]+\d+$/'); // Passes +fact('123abc')->matchesRegularExpression('/^[a-z]+\d+$/'); // Fails + +fact('123abc')->notMatchesRegularExpression('/^[a-z]+\d+$/'); // Passes +fact('abc123')->notMatchesRegularExpression('/^[a-z]+\d+$/'); // Fails + +fact('hello world')->containsString('world'); // Passes +fact('hello world')->containsString('foo'); // Fails + +fact('hello world')->notContainsString('foo'); // Passes +fact('hello world')->notContainsString('world'); // Fails + +fact('Hello World')->containsStringIgnoringCase('world'); // Passes +fact('Hello World')->containsStringIgnoringCase('foo'); // Fails + +fact('Hello World')->notContainsStringIgnoringCase('foo'); // Passes +fact('Hello World')->notContainsStringIgnoringCase('world'); // Fails + +fact('hello world')->startsWith('hello'); // Passes +fact('world hello')->startsWith('hello'); // Fails + +fact('file.txt')->endsWith('.txt'); // Passes +fact('txt.file')->endsWith('.txt'); // Fails + +fact('abc')->hasLength(3); // Passes +fact('abcd')->hasLength(3); // Fails + +fact('')->isEmptyString(); // Passes +fact('hello')->isEmptyString(); // Fails ``` -### Special Methods +### Type Checking assertions ```php -fact('01ARZ3NDEKTSV4RRFFQ69G5FAV')->ulid(); -``` - ... - ; +fact(new stdClass())->instanceOf(stdClass::class); // Passes +fact(new stdClass())->instanceOf(Exception::class); // Fails + +fact(new stdClass())->notInstanceOf(Exception::class); // Passes +fact(new stdClass())->notInstanceOf(stdClass::class); // Fails + +fact(42)->isInt(); // Passes +fact('42')->isInt(); // Fails + +fact('text')->isString(); // Passes +fact(42)->isString(); // Fails -fact( - [ - 'a' => ['any' => 'thing'], - 'b' => ['any' => 'thing', 'type' => 'candy', 'color' => 'green'], - 'c' => ['miss' => 'kiss', 'foo' => 'bar', 'any' => 'thing'], - 'd' => ['any' => 'thing'], - ] -)->arrayContainsAssociativeArray( - [ - 'c' => ['foo' => 'bar', 'miss' => 'kiss'], - 'b' => ['color' => 'green'], - ] -); // true +fact((object)['name' => 'John'])->hasProperty('name'); // Passes +fact((object)['name' => 'John'])->hasProperty('age'); // Fails +fact(new stdClass())->hasMethod('__construct'); // Passes +fact(new stdClass())->hasMethod('nonExistentMethod'); // Fails ``` ## Pull requests are always welcome diff --git a/tests/FluentAssertions/FluentAssertionsTestCase.php b/tests/FluentAssertions/FluentAssertionsTestCase.php index 663a540..db978dc 100644 --- a/tests/FluentAssertions/FluentAssertionsTestCase.php +++ b/tests/FluentAssertions/FluentAssertionsTestCase.php @@ -11,7 +11,7 @@ abstract class FluentAssertionsTestCase extends TestCase { protected function hasExpectedVariable(FluentAssertions $fluentAssertions, mixed $expected): void { - self::assertSame(expected: $expected, actual: $fluentAssertions->variable); + self::assertSame($expected, $fluentAssertions->variable); } protected function correctAssertionExecuted(): void @@ -23,7 +23,7 @@ protected function correctAssertionsExecuted(int $expected = 1): void { $performedAssertionCounts = Assert::getCount(); - self::assertSame(expected: $expected, actual: $performedAssertionCounts); + self::assertSame($expected, $performedAssertionCounts); } protected function incorrectAssertionExpected(): void From 871d6bd1618562be3a5c5d9a38b33d30db09d447 Mon Sep 17 00:00:00 2001 From: l0gic Date: Thu, 22 Jan 2026 04:45:29 +0500 Subject: [PATCH 4/6] Add isNotEmptyString method: asserts string is not empty, with tests and README update --- README.md | 3 ++ src/Traits/StringAssertions.php | 20 ++++++++ .../IsNotEmptyString/IsNotEmptyStringTest.php | 50 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/FluentAssertions/Asserts/IsNotEmptyString/IsNotEmptyStringTest.php diff --git a/README.md b/README.md index 1ebbf37..69a16e3 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,9 @@ fact('abcd')->hasLength(3); // Fails fact('')->isEmptyString(); // Passes fact('hello')->isEmptyString(); // Fails + +fact('hello')->isNotEmptyString(); // Passes +fact('')->isNotEmptyString(); // Fails ``` ### Type Checking assertions diff --git a/src/Traits/StringAssertions.php b/src/Traits/StringAssertions.php index e1811a3..4c9a5ea 100644 --- a/src/Traits/StringAssertions.php +++ b/src/Traits/StringAssertions.php @@ -273,5 +273,25 @@ public function isEmptyString(string $message = ''): self return $this; } + /** + * Asserts that a string is not empty. + * + * This method checks if the actual value is not an empty string. + * + * Example usage: + * fact('hello')->isNotEmptyString(); // Passes + * fact('')->isNotEmptyString(); // Fails + * + * @param string $message Optional custom error message. + * + * @return self Enables fluent chaining of assertion methods. + */ + public function isNotEmptyString(string $message = ''): self + { + Assert::assertNotEmpty($this->variable, $message); + + return $this; + } + // endregion Length Methods } \ No newline at end of file diff --git a/tests/FluentAssertions/Asserts/IsNotEmptyString/IsNotEmptyStringTest.php b/tests/FluentAssertions/Asserts/IsNotEmptyString/IsNotEmptyStringTest.php new file mode 100644 index 0000000..37c4c96 --- /dev/null +++ b/tests/FluentAssertions/Asserts/IsNotEmptyString/IsNotEmptyStringTest.php @@ -0,0 +1,50 @@ +isNotEmptyString(); + + // assert + $this->correctAssertionExecuted(); + } + + #[DataProvider('notIsNotEmptyStringDataProvider')] + public function testNotIsNotEmptyString(mixed $variable): void + { + // assert + $this->incorrectAssertionExpected(); + + // act + fact($variable)->isNotEmptyString(); + } + + public static function isNotEmptyStringDataProvider(): array + { + return [ + ['a'], + ['hello'], + [' '], + ]; + } + + public static function notIsNotEmptyStringDataProvider(): array + { + return [ + [''], + ]; + } +} \ No newline at end of file From 7e9dd8913ed28c11cd9923830b08dd6b0bd09b53 Mon Sep 17 00:00:00 2001 From: l0gic Date: Thu, 22 Jan 2026 04:48:40 +0500 Subject: [PATCH 5/6] Add isFloat, isBool, isArray type checking methods and isJson string method with tests and README updates --- README.md | 12 ++++ src/Traits/StringAssertions.php | 20 +++++++ src/Traits/TypeCheckingAssertions.php | 60 +++++++++++++++++++ .../Asserts/IsArray/IsArrayTest.php | 53 ++++++++++++++++ .../Asserts/IsBool/IsBoolTest.php | 52 ++++++++++++++++ .../Asserts/IsFloat/IsFloatTest.php | 53 ++++++++++++++++ .../Asserts/IsJson/IsJsonTest.php | 55 +++++++++++++++++ 7 files changed, 305 insertions(+) create mode 100644 tests/FluentAssertions/Asserts/IsArray/IsArrayTest.php create mode 100644 tests/FluentAssertions/Asserts/IsBool/IsBoolTest.php create mode 100644 tests/FluentAssertions/Asserts/IsFloat/IsFloatTest.php create mode 100644 tests/FluentAssertions/Asserts/IsJson/IsJsonTest.php diff --git a/README.md b/README.md index 69a16e3..4f5e401 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,9 @@ fact('hello')->isEmptyString(); // Fails fact('hello')->isNotEmptyString(); // Passes fact('')->isNotEmptyString(); // Fails + +fact('{"key": "value"}')->isJson(); // Passes +fact('invalid json')->isJson(); // Fails ``` ### Type Checking assertions @@ -190,6 +193,15 @@ fact((object)['name' => 'John'])->hasProperty('age'); // Fails fact(new stdClass())->hasMethod('__construct'); // Passes fact(new stdClass())->hasMethod('nonExistentMethod'); // Fails + +fact(3.14)->isFloat(); // Passes +fact(42)->isFloat(); // Fails + +fact(true)->isBool(); // Passes +fact(1)->isBool(); // Fails + +fact([1, 2])->isArray(); // Passes +fact('not array')->isArray(); // Fails ``` ## Pull requests are always welcome diff --git a/src/Traits/StringAssertions.php b/src/Traits/StringAssertions.php index 4c9a5ea..f95c671 100644 --- a/src/Traits/StringAssertions.php +++ b/src/Traits/StringAssertions.php @@ -293,5 +293,25 @@ public function isNotEmptyString(string $message = ''): self return $this; } + /** + * Asserts that a string is valid JSON. + * + * This method checks if the actual string is valid JSON. + * + * Example usage: + * fact('{"key": "value"}')->isJson(); // Passes + * fact('invalid json')->isJson(); // Fails + * + * @param string $message Optional custom error message. + * + * @return self Enables fluent chaining of assertion methods. + */ + public function isJson(string $message = ''): self + { + Assert::assertTrue(json_validate($this->variable), $message ?: 'String is not valid JSON.'); + + return $this; + } + // endregion Length Methods } \ No newline at end of file diff --git a/src/Traits/TypeCheckingAssertions.php b/src/Traits/TypeCheckingAssertions.php index 4ab118f..1a01ae5 100644 --- a/src/Traits/TypeCheckingAssertions.php +++ b/src/Traits/TypeCheckingAssertions.php @@ -135,4 +135,64 @@ public function hasMethod(string $method, string $message = ''): self return $this; } + + /** + * Asserts that a variable is of type float. + * + * This method checks if the actual value is a floating-point number. + * + * Example usage: + * fact(3.14)->isFloat(); // Passes + * fact(42)->isFloat(); // Fails + * + * @param string $message Optional custom error message. + * + * @return self Enables fluent chaining of assertion methods. + */ + public function isFloat(string $message = ''): self + { + Assert::assertIsFloat($this->variable, $message); + + return $this; + } + + /** + * Asserts that a variable is of type bool. + * + * This method checks if the actual value is a boolean. + * + * Example usage: + * fact(true)->isBool(); // Passes + * fact(1)->isBool(); // Fails + * + * @param string $message Optional custom error message. + * + * @return self Enables fluent chaining of assertion methods. + */ + public function isBool(string $message = ''): self + { + Assert::assertIsBool($this->variable, $message); + + return $this; + } + + /** + * Asserts that a variable is of type array. + * + * This method checks if the actual value is an array. + * + * Example usage: + * fact([1, 2])->isArray(); // Passes + * fact('not array')->isArray(); // Fails + * + * @param string $message Optional custom error message. + * + * @return self Enables fluent chaining of assertion methods. + */ + public function isArray(string $message = ''): self + { + Assert::assertIsArray($this->variable, $message); + + return $this; + } } \ No newline at end of file diff --git a/tests/FluentAssertions/Asserts/IsArray/IsArrayTest.php b/tests/FluentAssertions/Asserts/IsArray/IsArrayTest.php new file mode 100644 index 0000000..42691a8 --- /dev/null +++ b/tests/FluentAssertions/Asserts/IsArray/IsArrayTest.php @@ -0,0 +1,53 @@ +isArray(); + + // assert + $this->correctAssertionExecuted(); + } + + #[DataProvider('notIsArrayDataProvider')] + public function testNotIsArray(mixed $variable): void + { + // assert + $this->incorrectAssertionExpected(); + + // act + fact($variable)->isArray(); + } + + public static function isArrayDataProvider(): array + { + return [ + [[]], + [[1, 2, 3]], + [['key' => 'value']], + ]; + } + + public static function notIsArrayDataProvider(): array + { + return [ + ['string'], + [42], + [true], + [null], + ]; + } +} \ No newline at end of file diff --git a/tests/FluentAssertions/Asserts/IsBool/IsBoolTest.php b/tests/FluentAssertions/Asserts/IsBool/IsBoolTest.php new file mode 100644 index 0000000..1c8b39a --- /dev/null +++ b/tests/FluentAssertions/Asserts/IsBool/IsBoolTest.php @@ -0,0 +1,52 @@ +isBool(); + + // assert + $this->correctAssertionExecuted(); + } + + #[DataProvider('notIsBoolDataProvider')] + public function testNotIsBool(mixed $variable): void + { + // assert + $this->incorrectAssertionExpected(); + + // act + fact($variable)->isBool(); + } + + public static function isBoolDataProvider(): array + { + return [ + [true], + [false], + ]; + } + + public static function notIsBoolDataProvider(): array + { + return [ + [1], + [0], + ['true'], + [null], + ]; + } +} \ No newline at end of file diff --git a/tests/FluentAssertions/Asserts/IsFloat/IsFloatTest.php b/tests/FluentAssertions/Asserts/IsFloat/IsFloatTest.php new file mode 100644 index 0000000..7ff6d5b --- /dev/null +++ b/tests/FluentAssertions/Asserts/IsFloat/IsFloatTest.php @@ -0,0 +1,53 @@ +isFloat(); + + // assert + $this->correctAssertionExecuted(); + } + + #[DataProvider('notIsFloatDataProvider')] + public function testNotIsFloat(mixed $variable): void + { + // assert + $this->incorrectAssertionExpected(); + + // act + fact($variable)->isFloat(); + } + + public static function isFloatDataProvider(): array + { + return [ + [3.14], + [0.0], + [-1.5], + ]; + } + + public static function notIsFloatDataProvider(): array + { + return [ + [42], + ['3.14'], + [true], + [null], + ]; + } +} \ No newline at end of file diff --git a/tests/FluentAssertions/Asserts/IsJson/IsJsonTest.php b/tests/FluentAssertions/Asserts/IsJson/IsJsonTest.php new file mode 100644 index 0000000..7366a6a --- /dev/null +++ b/tests/FluentAssertions/Asserts/IsJson/IsJsonTest.php @@ -0,0 +1,55 @@ +isJson(); + + // assert + $this->correctAssertionExecuted(); + } + + #[DataProvider('notIsJsonDataProvider')] + public function testNotIsJson(mixed $variable): void + { + // assert + $this->incorrectAssertionExpected(); + + // act + fact($variable)->isJson(); + } + + public static function isJsonDataProvider(): array + { + return [ + ['{"key": "value"}'], + ['[1, 2, 3]'], + ['"string"'], + ['42'], + ['true'], + ]; + } + + public static function notIsJsonDataProvider(): array + { + return [ + ['invalid json'], + ['{key: value}'], + [''], + ['not json'], + ]; + } +} \ No newline at end of file From 874b9532ba97490491784a5bde75328b35257d74 Mon Sep 17 00:00:00 2001 From: l0gic Date: Thu, 22 Jan 2026 05:01:35 +0500 Subject: [PATCH 6/6] Add isValidEmail method for email validation and update README --- README.md | 3 ++ src/Traits/StringAssertions.php | 20 +++++++ .../Asserts/IsValidEmail/IsValidEmailTest.php | 52 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 tests/FluentAssertions/Asserts/IsValidEmail/IsValidEmailTest.php diff --git a/README.md b/README.md index 4f5e401..f579213 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,9 @@ fact('')->isNotEmptyString(); // Fails fact('{"key": "value"}')->isJson(); // Passes fact('invalid json')->isJson(); // Fails + +fact('user@example.com')->isValidEmail(); // Passes +fact('invalid-email')->isValidEmail(); // Fails ``` ### Type Checking assertions diff --git a/src/Traits/StringAssertions.php b/src/Traits/StringAssertions.php index f95c671..7e41f73 100644 --- a/src/Traits/StringAssertions.php +++ b/src/Traits/StringAssertions.php @@ -313,5 +313,25 @@ public function isJson(string $message = ''): self return $this; } + /** + * Asserts that a string is a valid email address. + * + * This method checks if the actual string is a valid email format. + * + * Example usage: + * fact('user@example.com')->isValidEmail(); // Passes + * fact('invalid-email')->isValidEmail(); // Fails + * + * @param string $message Optional custom error message. + * + * @return self Enables fluent chaining of assertion methods. + */ + public function isValidEmail(string $message = ''): self + { + Assert::assertTrue(filter_var($this->variable, FILTER_VALIDATE_EMAIL) !== false, $message ?: 'String is not a valid email address.'); + + return $this; + } + // endregion Length Methods } \ No newline at end of file diff --git a/tests/FluentAssertions/Asserts/IsValidEmail/IsValidEmailTest.php b/tests/FluentAssertions/Asserts/IsValidEmail/IsValidEmailTest.php new file mode 100644 index 0000000..cb7d4a9 --- /dev/null +++ b/tests/FluentAssertions/Asserts/IsValidEmail/IsValidEmailTest.php @@ -0,0 +1,52 @@ +isValidEmail(); + + // assert + $this->correctAssertionExecuted(); + } + + #[DataProvider('notIsValidEmailDataProvider')] + public function testNotIsValidEmail(mixed $variable): void + { + // assert + $this->incorrectAssertionExpected(); + + // act + fact($variable)->isValidEmail(); + } + + public static function isValidEmailDataProvider(): array + { + return [ + ['user@example.com'], + ['test.email+tag@domain.co.uk'], + ]; + } + + public static function notIsValidEmailDataProvider(): array + { + return [ + ['invalid-email'], + ['@example.com'], + ['user@'], + [''], + ]; + } +} \ No newline at end of file