From a0733cc5382bd9dcb77d9ae02a501ee6d632ddc5 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Wed, 5 Apr 2023 12:53:33 -0400 Subject: [PATCH] feat: add HTML expectation --- .github/workflows/ci.yml | 45 +++++++++++++++++++++++--- README.md | 70 +++++++++++++++++++++++++++++++++++++--- composer.json | 3 +- src/Assert.php | 10 ++++++ tests/AssertTest.php | 15 +++++++++ 5 files changed, 134 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc404d8..5ea11c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,18 +6,55 @@ on: schedule: - cron: '0 0 1,16 * *' +env: + COMPOSER_ROOT_VERSION: 1.x-dev + jobs: - tests: - uses: zenstruck/.github/.github/workflows/php-test.yml@main + test: + name: PHP ${{ matrix.php }}, ${{ matrix.deps }} + runs-on: ubuntu-latest + strategy: + matrix: + php: [ 8.0, 8.1, 8.2 ] + deps: [ highest ] + include: + - php: 8.0 + deps: lowest + steps: + - uses: zenstruck/.github@php-test-symfony + with: + php: ${{ matrix.php }} + deps: ${{ matrix.deps }} code-coverage: - uses: zenstruck/.github/.github/workflows/php-coverage-codecov.yml@main + name: Code Coverage + runs-on: ubuntu-latest + steps: + - uses: zenstruck/.github@php-coverage-codecov + with: + php: 8.2 composer-validate: uses: zenstruck/.github/.github/workflows/php-composer-validate.yml@main sca: - uses: zenstruck/.github/.github/workflows/php-stan.yml@main + name: Static Code Analysis + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + coverage: none + + - name: Install Dependencies + uses: ramsey/composer-install@v2 + + - name: Run PHPStan + run: vendor/bin/phpstan --error-format=github fixcs: name: Run php-cs-fixer diff --git a/README.md b/README.md index ca8e6c0..bfe40b8 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Assert::that([ // also works with json strings that decode to arrays Assert::that('[3, 1]')->isSubsetOf('[1, 2, 3]'); // pass -// equals (== comparison) +// equals (== comparison) Assert::that('foo')->equals('foo'); // pass Assert::that('6')->equals(6); // pass Assert::that('foo')->equals('bar'); // fail @@ -327,6 +327,68 @@ Assert::that(['foo', 'bar']) ; ``` +### HTML Expectations + +> **Note**: These expectations require the `zenstruck/assert-html` package. Install +> with `composer require --dev zenstruck/assert-html`. + +```php +use Zenstruck\Assert; + +Assert::html($someHtmlString) + ->contains('h1 title') + ->doesNotContain('invalid text') + ->containsIn('h1', 'title') + ->doesNotContainIn('h1', 'invalid text') + ->hasElement('h1') + ->doesNotHaveElement('h2') + ->hasElementCount('ul li', 2) + + // head assertions + ->containsIn('title', 'meta title') + ->attributeContains('meta[name="description"]', 'content', 'meta') + ->attributeDoesNotContain('meta[name="description"]', 'content', 'invalid') + ->attributeContains('html', 'lang', 'en') + + // form assertions + ->fieldEquals('Input 1', 'input 1') + ->fieldEquals('input1', 'input 1') + ->fieldEquals('input_1', 'input 1') + ->fieldDoesNotEqual('Input 1', 'invalid') + ->fieldDoesNotEqual('input1', 'invalid') + ->fieldDoesNotEqual('input_1', 'invalid') + ->fieldChecked('Input 3') + ->fieldChecked('input3') + ->fieldChecked('input_3') + ->fieldNotChecked('Input 2') + ->fieldNotChecked('input2') + ->fieldNotChecked('input_2') + ->fieldSelected('Input 4', 'option 1') + ->fieldSelected('input4', 'option 1') + ->fieldSelected('input_4', 'option 1') + ->fieldSelected('Input 7', 'option 1') + ->fieldSelected('input7', 'option 1') + ->fieldSelected('input_7[]', 'option 1') + ->fieldSelected('Input 7', 'option 3') + ->fieldSelected('input7', 'option 3') + ->fieldSelected('input_7[]', 'option 3') + ->fieldNotSelected('Input 4', 'option 2') + ->fieldNotSelected('input4', 'option 2') + ->fieldNotSelected('input_4', 'option 2') + ->fieldNotSelected('Input 7', 'option 2') + ->fieldNotSelected('input7', 'option 2') + ->fieldNotSelected('input_7[]', 'option 2') + ->fieldNotSelected('input_8', 'option 1') + ->fieldSelected('input_8', 'option 2') + ->fieldNotChecked('Radio 1') + ->fieldNotChecked('radio1') + ->fieldNotChecked('Radio 3') + ->fieldNotChecked('radio3') + ->fieldChecked('Radio 2') + ->fieldChecked('radio2') +; +``` + ## `AssertionFailed` Exception When triggering a failed assertion, it is important to provide a useful failure @@ -368,7 +430,7 @@ use Zenstruck\Assert\AssertionFailed; class StringContains { public function __construct(private string $haystack, private string $needle) {} - + public function __invoke(): void { if (!str_contains($this->haystack, $this->needle)) { @@ -404,7 +466,7 @@ use Zenstruck\Assert\Assertion\Negatable; class StringContains implements Negatable { public function __construct(private string $haystack, private string $needle) {} - + public function __invoke(): void { if (!str_contains($this->haystack, $this->needle)) { @@ -414,7 +476,7 @@ class StringContains implements Negatable ]); } } - + public function notFailure(): AssertionFailed { return new AssertionFailed( diff --git a/composer.json b/composer.json index 72ffe9b..fe13b26 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "require-dev": { "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^9.5", - "symfony/phpunit-bridge": "^6.2" + "symfony/phpunit-bridge": "^6.2", + "zenstruck/assert-html": "^0.2.1" }, "config": { "preferred-install": "dist", diff --git a/src/Assert.php b/src/Assert.php index f07fedd..97cfd94 100644 --- a/src/Assert.php +++ b/src/Assert.php @@ -15,6 +15,7 @@ use Zenstruck\Assert\AssertionFailed; use Zenstruck\Assert\Expectation; use Zenstruck\Assert\Handler; +use Zenstruck\Assert\HtmlExpectation; use Zenstruck\Assert\Not; /** @@ -143,6 +144,15 @@ public static function that($value): Expectation return new Expectation($value); } + public static function html(string $value): HtmlExpectation + { + if (!\class_exists(HtmlExpectation::class)) { + throw new \LogicException('zenstruck/assert-html is required to use the HTML expectation (composer require --dev zenstruck/assert-html).'); + } + + return new HtmlExpectation($value); + } + /** * Force a specific handler or use a custom one. */ diff --git a/tests/AssertTest.php b/tests/AssertTest.php index 5a3b618..8ccea98 100644 --- a/tests/AssertTest.php +++ b/tests/AssertTest.php @@ -253,4 +253,19 @@ public function try_failure(): void $this->assertSame('override message', $this->handler->failures()[1]->getMessage()); $this->assertSame('override message value RuntimeException exception message', $this->handler->failures()[2]->getMessage()); } + + /** + * @test + */ + public function html(): void + { + $this->assertSame(0, $this->handler->successCount()); + + Assert::html('

hello

') + ->contains('hello') + ->containsIn('h1', 'hello') + ; + + $this->assertSame(2, $this->handler->successCount()); + } }