From c69255637b07c326d060bc4191dcae8d9ec561c6 Mon Sep 17 00:00:00 2001 From: Sean Fisher Date: Tue, 14 Nov 2023 10:48:26 -0500 Subject: [PATCH] Adding block assertions to strings (#463) * Overhauling the generics of factories to return the proper type * Adding a global constant to make testing easier * Include some functionality from core * Ensure that post factories work in data providers * Switch to get_comment_delimited_block_content() to compile blocks * Ensure that a new line is added before/after content * Adding block assertions for string/content * Adjust message * Adjusting to support 1.0-3.0 * Fixing PHPCS --- composer.json | 1 + src/mantle/faker/class-faker-provider.php | 26 +++ .../testing/concerns/trait-assertions.php | 3 +- .../concerns/trait-block-assertions.php | 156 ++++++++++++++++++ .../testing/concerns/trait-with-faker.php | 7 +- .../concerns/test-block-assertions.php | 43 +++++ 6 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 src/mantle/testing/concerns/trait-block-assertions.php create mode 100644 tests/testing/concerns/test-block-assertions.php diff --git a/composer.json b/composer.json index efd32400f..c19213eec 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,7 @@ }, "require-dev": { "alleyinteractive/alley-coding-standards": "^1.0.1", + "alleyinteractive/wp-match-blocks": "^1.0 || ^2.0 || ^3.0", "guzzlehttp/guzzle": "^7.7", "league/flysystem-aws-s3-v3": "^1.0", "mockery/mockery": "^1.6.6", diff --git a/src/mantle/faker/class-faker-provider.php b/src/mantle/faker/class-faker-provider.php index bf1f82d36..52b6f7ae4 100644 --- a/src/mantle/faker/class-faker-provider.php +++ b/src/mantle/faker/class-faker-provider.php @@ -14,6 +14,32 @@ * Faker Block Provider */ class Faker_Provider extends Base { + /** + * Compile a set of blocks. + * + * @param array $blocks Blocks to compile. + * @return string + */ + public static function blocks( array $blocks ): string { + return implode( "\n\n", $blocks ); + } + + /** + * Build a heading block. + * + * @param int $level Heading level. + * @return string + */ + public static function heading_block( int $level = 2 ): string { + return static::block( + 'heading', + sprintf( '%s', $level, Lorem::sentence(), $level ), + [ + 'level' => $level, + ], + ); + } + /** * Build a paragraph block. * diff --git a/src/mantle/testing/concerns/trait-assertions.php b/src/mantle/testing/concerns/trait-assertions.php index 0c6bc7ef9..3c4bec91a 100644 --- a/src/mantle/testing/concerns/trait-assertions.php +++ b/src/mantle/testing/concerns/trait-assertions.php @@ -23,7 +23,8 @@ * Assorted Test_Cast assertions. */ trait Assertions { - use Asset_Assertions; + use Asset_Assertions, + Block_Assertions; /** * Asserts that the given value is an instance of WP_Error. diff --git a/src/mantle/testing/concerns/trait-block-assertions.php b/src/mantle/testing/concerns/trait-block-assertions.php new file mode 100644 index 000000000..62644f928 --- /dev/null +++ b/src/mantle/testing/concerns/trait-block-assertions.php @@ -0,0 +1,156 @@ +assertNotEmpty( + match_blocks( $string, $args ), + 'Failed asserting that string matches block with arguments ' . print_r( $args, true ), + ); + } + + /** + * Assert that a string does not match a block. + * + * The arguments are passed directly to `match_block()`. + * + * @see \Alley\WP\match_block() + * + * @param string $string The string to check. + * @param array $args The arguments to pass to `match_block()`. + */ + public function assertStringNotMatchesBlock( string $string, array $args ): void { + $this->assertEmpty( + match_blocks( $string, $args ), + 'Failed asserting that string does not match block with arguments ' . print_r( $args, true ), + ); + } + + /** + * Assert that a string has a block. + * + * @param string $string The string to check. + * @param string|string[] $block_name The block name(s) to check for. Will attempt to match any of the names. + * @param array $attrs Optional. Attributes to check for. + */ + public function assertStringHasBlock( string $string, string|array $block_name, array $attrs = [] ): void { + $this->assertNotEmpty( + match_blocks( + $string, + [ + 'attrs' => $this->convert_arguments_for_matching( $attrs ), + 'name' => $block_name, + ], + ), + ! empty( $attrs ) + ? "Failed asserting that string has block [{$block_name}] with attributes " . print_r( $attrs, true ) + : "Failed asserting that string has block [{$block_name}]." + ); + } + + /** + * Assert that a string does not have a block. + * + * @param string $string The string to check. + * @param string|string[] $block_name The block name(s) to check for. Will attempt to match any of the names. + * @param array $attrs Optional. Attributes to check for. + */ + public function assertStringNotHasBlock( string $string, string|array $block_name, array $attrs = [] ): void { + $this->assertEmpty( + match_blocks( + $string, + [ + 'attrs' => $this->convert_arguments_for_matching( $attrs ), + 'name' => $block_name, + ], + ), + ! empty( $attrs ) + ? "Failed asserting that string does not have block [{$block_name}] with attributes " . print_r( $attrs, true ) + : "Failed asserting that string does not have block [{$block_name}]", + ); + } + + /** + * Assert that a post has a block in its content. + * + * @param Post|WP_Post $post The post to check. + * @param string|string[] $block_name The block name(s) to check for. Will attempt to match any of the names. + * @param array $attrs Optional. Attributes to check for. + */ + public function assertPostHasBlock( Post|WP_Post $post, string|array $block_name, array $attrs = [] ): void { + $this->assertStringHasBlock( $post->post_content, $block_name, $attrs ); + } + + /** + * Assert that a post does not have a block in its content. + * + * @param Post|WP_Post $post The post to check. + * @param string|string[] $block_name The block name(s) to check for. Will attempt to match any of the names. + * @param array $attrs Optional. Attributes to check for. + */ + public function assertPostNotHasBlock( Post|WP_Post $post, string|array $block_name, array $attrs = [] ): void { + $this->assertStringNotHasBlock( $post->post_content, $block_name, $attrs ); + } + + /** + * Convert the key/value arguments to an array that can be passed to + * `match_block()`. + * + * @param array $args The arguments to convert. + * @return array + */ + protected function convert_arguments_for_matching( array $args ): array { + // PHPCS is crashing on these lines for some reason. Disabling it for now + // until we've upgrading to WPCS 3.0. + + /* phpcs:disable */ + return collect( $args )->reduce( + function ( array $carry, $value, $key ) { + // Allow for passing an argument pair directly. + if ( is_array( $value ) && isset( $value['key'], $value['value'] ) ) { + $carry[] = $value; + + return $carry; + } + + $carry[] = [ + $key => $value, + 'value' => $value, + ]; + + return $carry; + }, + [] + ); + /* phpcs:enable */ + } +} diff --git a/src/mantle/testing/concerns/trait-with-faker.php b/src/mantle/testing/concerns/trait-with-faker.php index b90f7d787..33ad6e73c 100644 --- a/src/mantle/testing/concerns/trait-with-faker.php +++ b/src/mantle/testing/concerns/trait-with-faker.php @@ -9,6 +9,7 @@ use Faker\Generator; use Faker\Factory; +use Mantle\Faker\Faker_Provider; /** * This trait sets up a faker instance for use in tests. @@ -44,6 +45,10 @@ protected function make_faker(): Generator { return $this->app->make( Generator::class, [ 'locale' => $locale ] ); } - return Factory::create( $locale ); + $generator = Factory::create( $locale ); + + $generator->addProvider( new Faker_Provider( $generator ) ); + + return $generator; } } diff --git a/tests/testing/concerns/test-block-assertions.php b/tests/testing/concerns/test-block-assertions.php new file mode 100644 index 000000000..97a8805af --- /dev/null +++ b/tests/testing/concerns/test-block-assertions.php @@ -0,0 +1,43 @@ +post = static::factory()->post->create_and_get( [ + 'post_content' => $this->faker->blocks( [ + $this->faker->paragraph_block, + $this->faker->heading_block( 3 ), + $this->faker->paragraph_block, + $this->faker->heading_block( 4 ), + $this->faker->paragraph_block, + ] ), + ] ); + } + + public function test_string_has_content() { + $this->assertStringHasBlock( $this->post->post_content, 'core/paragraph' ); + $this->assertStringHasBlock( $this->post->post_content, 'core/heading' ); + $this->assertStringHasBlock( $this->post->post_content, 'core/heading', [ 'level' => 3 ] ); + $this->assertStringNotHasBlock( $this->post->post_content, 'core/heading', [ 'level' => 5 ] ); + } + + public function test_post_has_content() { + $this->assertPostHasBlock( $this->post, 'core/paragraph' ); + $this->assertPostHasBlock( $this->post, 'core/heading' ); + $this->assertPostHasBlock( $this->post, 'core/heading', [ 'level' => 3 ] ); + $this->assertPostNotHasBlock( $this->post, 'core/heading', [ 'level' => 5 ] ); + } +}