WordPress.org

Make WordPress Core

Ticket #45097: 45097.2.diff

File 45097.2.diff, 21.7 KB (added by danielbachhuber, 2 years ago)
  • new file src/wp-includes/class-wp-block-type-registry.php

    diff --git a/src/wp-includes/class-wp-block-type-registry.php b/src/wp-includes/class-wp-block-type-registry.php
    new file mode 100644
    index 0000000000..11e42c14cb
    - +  
     1<?php
     2/**
     3 * Blocks API: WP_Block_Type_Registry class
     4 *
     5 * @package WordPress
     6 * @subpackage Blocks
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Core class used for interacting with block types.
     12 *
     13 * @since 5.0.0
     14 */
     15final class WP_Block_Type_Registry {
     16        /**
     17         * Registered block types, as `$name => $instance` pairs.
     18         *
     19         * @since 5.0.0
     20         * @access private
     21         * @var WP_Block_Type[]
     22         */
     23        private $registered_block_types = array();
     24
     25        /**
     26         * Container for the main instance of the class.
     27         *
     28         * @since 5.0.0
     29         * @access private
     30         * @static
     31         * @var WP_Block_Type_Registry|null
     32         */
     33        private static $instance = null;
     34
     35        /**
     36         * Registers a block type.
     37         *
     38         * @since 5.0.0
     39         * @access public
     40         *
     41         * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
     42         *                                   complete WP_Block_Type instance. In case a WP_Block_Type
     43         *                                   is provided, the $args parameter will be ignored.
     44         * @param array                $args {
     45         *     Optional. Array of block type arguments. Any arguments may be defined, however the
     46         *     ones described below are supported by default. Default empty array.
     47         *
     48         *     @type callable $render_callback Callback used to render blocks of this block type.
     49         *     @type array    $attributes      Block attributes mapping, property name to schema.
     50         * }
     51         * @return WP_Block_Type|false The registered block type on success, or false on failure.
     52         */
     53        public function register( $name, $args = array() ) {
     54                $block_type = null;
     55                if ( $name instanceof WP_Block_Type ) {
     56                        $block_type = $name;
     57                        $name       = $block_type->name;
     58                }
     59
     60                if ( ! is_string( $name ) ) {
     61                        $message = __( 'Block type names must be strings.' );
     62                        _doing_it_wrong( __METHOD__, $message, '5.0.0' );
     63                        return false;
     64                }
     65
     66                if ( preg_match( '/[A-Z]+/', $name ) ) {
     67                        $message = __( 'Block type names must not contain uppercase characters.' );
     68                        _doing_it_wrong( __METHOD__, $message, '5.0.0' );
     69                        return false;
     70                }
     71
     72                $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/';
     73                if ( ! preg_match( $name_matcher, $name ) ) {
     74                        $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type' );
     75                        _doing_it_wrong( __METHOD__, $message, '5.0.0' );
     76                        return false;
     77                }
     78
     79                if ( $this->is_registered( $name ) ) {
     80                        /* translators: 1: block name */
     81                        $message = sprintf( __( 'Block type "%s" is already registered.' ), $name );
     82                        _doing_it_wrong( __METHOD__, $message, '5.0.0' );
     83                        return false;
     84                }
     85
     86                if ( ! $block_type ) {
     87                        $block_type = new WP_Block_Type( $name, $args );
     88                }
     89
     90                $this->registered_block_types[ $name ] = $block_type;
     91
     92                return $block_type;
     93        }
     94
     95        /**
     96         * Unregisters a block type.
     97         *
     98         * @since 5.0.0
     99         * @access public
     100         *
     101         * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
     102         *                                   complete WP_Block_Type instance.
     103         * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
     104         */
     105        public function unregister( $name ) {
     106                if ( $name instanceof WP_Block_Type ) {
     107                        $name = $name->name;
     108                }
     109
     110                if ( ! $this->is_registered( $name ) ) {
     111                        /* translators: 1: block name */
     112                        $message = sprintf( __( 'Block type "%s" is not registered.' ), $name );
     113                        _doing_it_wrong( __METHOD__, $message, '5.0.0' );
     114                        return false;
     115                }
     116
     117                $unregistered_block_type = $this->registered_block_types[ $name ];
     118                unset( $this->registered_block_types[ $name ] );
     119
     120                return $unregistered_block_type;
     121        }
     122
     123        /**
     124         * Retrieves a registered block type.
     125         *
     126         * @since 5.0.0
     127         * @access public
     128         *
     129         * @param string $name Block type name including namespace.
     130         * @return WP_Block_Type|null The registered block type, or null if it is not registered.
     131         */
     132        public function get_registered( $name ) {
     133                if ( ! $this->is_registered( $name ) ) {
     134                        return null;
     135                }
     136
     137                return $this->registered_block_types[ $name ];
     138        }
     139
     140        /**
     141         * Retrieves all registered block types.
     142         *
     143         * @since 5.0.0
     144         * @access public
     145         *
     146         * @return WP_Block_Type[] Associative array of `$block_type_name => $block_type` pairs.
     147         */
     148        public function get_all_registered() {
     149                return $this->registered_block_types;
     150        }
     151
     152        /**
     153         * Checks if a block type is registered.
     154         *
     155         * @since 5.0.0
     156         * @access public
     157         *
     158         * @param string $name Block type name including namespace.
     159         * @return bool True if the block type is registered, false otherwise.
     160         */
     161        public function is_registered( $name ) {
     162                return isset( $this->registered_block_types[ $name ] );
     163        }
     164
     165        /**
     166         * Utility method to retrieve the main instance of the class.
     167         *
     168         * The instance will be created if it does not exist yet.
     169         *
     170         * @since 5.0.0
     171         * @access public
     172         * @static
     173         *
     174         * @return WP_Block_Type_Registry The main instance.
     175         */
     176        public static function get_instance() {
     177                if ( null === self::$instance ) {
     178                        self::$instance = new self();
     179                }
     180
     181                return self::$instance;
     182        }
     183}
  • new file src/wp-includes/class-wp-block-type.php

    diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php
    new file mode 100644
    index 0000000000..b064143dd7
    - +  
     1<?php
     2/**
     3 * Blocks API: WP_Block_Type class
     4 *
     5 * @package WordPress
     6 * @subpackage Blocks
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Core class representing a block type.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @see register_block_type()
     16 */
     17class WP_Block_Type {
     18        /**
     19         * Block type key.
     20         *
     21         * @since 5.0.0
     22         * @var string
     23         */
     24        public $name;
     25
     26        /**
     27         * Block type render callback.
     28         *
     29         * @since 5.0.0
     30         * @var callable
     31         */
     32        public $render_callback;
     33
     34        /**
     35         * Block type attributes property schemas.
     36         *
     37         * @since 5.0.0
     38         * @var array
     39         */
     40        public $attributes;
     41
     42        /**
     43         * Block type editor script handle.
     44         *
     45         * @since 5.0.0
     46         * @var string
     47         */
     48        public $editor_script;
     49
     50        /**
     51         * Block type front end script handle.
     52         *
     53         * @since 5.0.0
     54         * @var string
     55         */
     56        public $script;
     57
     58        /**
     59         * Block type editor style handle.
     60         *
     61         * @since 5.0.0
     62         * @var string
     63         */
     64        public $editor_style;
     65
     66        /**
     67         * Block type front end style handle.
     68         *
     69         * @since 5.0.0
     70         * @var string
     71         */
     72        public $style;
     73
     74        /**
     75         * Constructor.
     76         *
     77         * Will populate object properties from the provided arguments.
     78         *
     79         * @since 5.0.0
     80         *
     81         * @see register_block_type()
     82         *
     83         * @param string       $block_type Block type name including namespace.
     84         * @param array|string $args       Optional. Array or string of arguments for registering a block type.
     85         *                                 Default empty array.
     86         */
     87        public function __construct( $block_type, $args = array() ) {
     88                $this->name = $block_type;
     89
     90                $this->set_props( $args );
     91        }
     92
     93        /**
     94         * Renders the block type output for given attributes.
     95         *
     96         * @since 5.0.0
     97         *
     98         * @param array  $attributes Optional. Block attributes. Default empty array.
     99         * @param string $content    Optional. Block content. Default empty string.
     100         * @return string Rendered block type output.
     101         */
     102        public function render( $attributes = array(), $content = '' ) {
     103                if ( ! $this->is_dynamic() ) {
     104                        return '';
     105                }
     106
     107                $attributes = $this->prepare_attributes_for_render( $attributes );
     108
     109                return (string) call_user_func( $this->render_callback, $attributes, $content );
     110        }
     111
     112        /**
     113         * Returns true if the block type is dynamic, or false otherwise. A dynamic
     114         * block is one which defers its rendering to occur on-demand at runtime.
     115         *
     116         * @return boolean Whether block type is dynamic.
     117         */
     118        public function is_dynamic() {
     119                return is_callable( $this->render_callback );
     120        }
     121
     122        /**
     123         * Validates attributes against the current block schema, populating
     124         * defaulted and missing values, and omitting unknown attributes.
     125         *
     126         * @param  array $attributes Original block attributes.
     127         * @return array             Prepared block attributes.
     128         */
     129        public function prepare_attributes_for_render( $attributes ) {
     130                if ( ! isset( $this->attributes ) ) {
     131                        return $attributes;
     132                }
     133
     134                $prepared_attributes = array();
     135
     136                foreach ( $this->attributes as $attribute_name => $schema ) {
     137                        $value = null;
     138
     139                        if ( isset( $attributes[ $attribute_name ] ) ) {
     140                                $is_valid = rest_validate_value_from_schema( $attributes[ $attribute_name ], $schema );
     141                                if ( ! is_wp_error( $is_valid ) ) {
     142                                        $value = rest_sanitize_value_from_schema( $attributes[ $attribute_name ], $schema );
     143                                }
     144                        }
     145
     146                        if ( is_null( $value ) && isset( $schema['default'] ) ) {
     147                                $value = $schema['default'];
     148                        }
     149
     150                        $prepared_attributes[ $attribute_name ] = $value;
     151                }
     152
     153                return $prepared_attributes;
     154        }
     155
     156        /**
     157         * Sets block type properties.
     158         *
     159         * @since 5.0.0
     160         *
     161         * @param array|string $args Array or string of arguments for registering a block type.
     162         */
     163        public function set_props( $args ) {
     164                $args = wp_parse_args(
     165                        $args,
     166                        array(
     167                                'render_callback' => null,
     168                        )
     169                );
     170
     171                $args['name'] = $this->name;
     172
     173                foreach ( $args as $property_name => $property_value ) {
     174                        $this->$property_name = $property_value;
     175                }
     176        }
     177
     178        /**
     179         * Get all available block attributes including possible layout attribute from Columns block.
     180         *
     181         * @return array Array of attributes.
     182         */
     183        public function get_attributes() {
     184                return is_array( $this->attributes ) ?
     185                        array_merge(
     186                                $this->attributes,
     187                                array(
     188                                        'layout' => array(
     189                                                'type' => 'string',
     190                                        ),
     191                                )
     192                        ) :
     193                        array(
     194                                'layout' => array(
     195                                        'type' => 'string',
     196                                ),
     197                        );
     198        }
     199}
  • src/wp-settings.php

    diff --git a/src/wp-settings.php b/src/wp-settings.php
    index 44c8a91a07..ba0f9388d7 100644
    a b require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.p 
    240240require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' );
    241241require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-term-meta-fields.php' );
    242242require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-user-meta-fields.php' );
     243require( ABSPATH . WPINC . '/class-wp-block-type.php' );
     244require( ABSPATH . WPINC . '/class-wp-block-type-registry.php' );
    243245
    244246$GLOBALS['wp_embed'] = new WP_Embed();
    245247
  • new file tests/phpunit/tests/blocks/block-type-registry.php

    diff --git a/tests/phpunit/tests/blocks/block-type-registry.php b/tests/phpunit/tests/blocks/block-type-registry.php
    new file mode 100644
    index 0000000000..42ef341a76
    - +  
     1<?php
     2/**
     3 * WP_Block_Type_Registry Tests
     4 *
     5 * @package WordPress
     6 * @subpackage Blocks
     7 */
     8
     9/**
     10 * Tests for WP_Block_Type_Registry
     11 *
     12 * @group blocks
     13 */
     14class WP_Test_Block_Type_Registry extends WP_UnitTestCase {
     15
     16        /**
     17         * Dummy block type registry.
     18         *
     19         * @var WP_Block_Type_Registry
     20         */
     21        private $registry = null;
     22
     23        function setUp() {
     24                parent::setUp();
     25
     26                $this->registry = new WP_Block_Type_Registry();
     27        }
     28
     29        function tearDown() {
     30                parent::tearDown();
     31
     32                $this->registry = null;
     33        }
     34
     35        /**
     36         * Should reject numbers
     37         *
     38         * @expectedIncorrectUsage WP_Block_Type_Registry::register
     39         */
     40        function test_invalid_non_string_names() {
     41                $result = $this->registry->register( 1, array() );
     42                $this->assertFalse( $result );
     43        }
     44
     45        /**
     46         * Should reject blocks without a namespace
     47         *
     48         * @expectedIncorrectUsage WP_Block_Type_Registry::register
     49         */
     50        function test_invalid_names_without_namespace() {
     51                $result = $this->registry->register( 'paragraph', array() );
     52                $this->assertFalse( $result );
     53        }
     54
     55        /**
     56         * Should reject blocks with invalid characters
     57         *
     58         * @expectedIncorrectUsage WP_Block_Type_Registry::register
     59         */
     60        function test_invalid_characters() {
     61                $result = $this->registry->register( 'still/_doing_it_wrong', array() );
     62                $this->assertFalse( $result );
     63        }
     64
     65        /**
     66         * Should reject blocks with uppercase characters
     67         *
     68         * @expectedIncorrectUsage WP_Block_Type_Registry::register
     69         */
     70        function test_uppercase_characters() {
     71                $result = $this->registry->register( 'Core/Paragraph', array() );
     72                $this->assertFalse( $result );
     73        }
     74
     75        /**
     76         * Should accept valid block names
     77         */
     78        function test_register_block_type() {
     79                $name     = 'core/paragraph';
     80                $settings = array(
     81                        'icon' => 'editor-paragraph',
     82                );
     83
     84                $block_type = $this->registry->register( $name, $settings );
     85                $this->assertEquals( $name, $block_type->name );
     86                $this->assertEquals( $settings['icon'], $block_type->icon );
     87                $this->assertEquals( $block_type, $this->registry->get_registered( $name ) );
     88        }
     89
     90        /**
     91         * Should fail to re-register the same block
     92         *
     93         * @expectedIncorrectUsage WP_Block_Type_Registry::register
     94         */
     95        function test_register_block_type_twice() {
     96                $name     = 'core/paragraph';
     97                $settings = array(
     98                        'icon' => 'editor-paragraph',
     99                );
     100
     101                $result = $this->registry->register( $name, $settings );
     102                $this->assertNotFalse( $result );
     103                $result = $this->registry->register( $name, $settings );
     104                $this->assertFalse( $result );
     105        }
     106
     107        /**
     108         * Should accept a WP_Block_Type instance
     109         */
     110        function test_register_block_type_instance() {
     111                $block_type = new WP_Dummy_Block_Type( 'core/dummy' );
     112
     113                $result = $this->registry->register( $block_type );
     114                $this->assertSame( $block_type, $result );
     115        }
     116
     117        /**
     118         * Unregistering should fail if a block is not registered
     119         *
     120         * @expectedIncorrectUsage WP_Block_Type_Registry::unregister
     121         */
     122        function test_unregister_not_registered_block() {
     123                $result = $this->registry->unregister( 'core/unregistered' );
     124                $this->assertFalse( $result );
     125        }
     126
     127        /**
     128         * Should unregister existing blocks
     129         */
     130        function test_unregister_block_type() {
     131                $name     = 'core/paragraph';
     132                $settings = array(
     133                        'icon' => 'editor-paragraph',
     134                );
     135
     136                $this->registry->register( $name, $settings );
     137                $block_type = $this->registry->unregister( $name );
     138                $this->assertEquals( $name, $block_type->name );
     139                $this->assertEquals( $settings['icon'], $block_type->icon );
     140                $this->assertFalse( $this->registry->is_registered( $name ) );
     141        }
     142
     143        function test_get_all_registered() {
     144                $names    = array( 'core/paragraph', 'core/image', 'core/blockquote' );
     145                $settings = array(
     146                        'icon' => 'random',
     147                );
     148
     149                foreach ( $names as $name ) {
     150                        $this->registry->register( $name, $settings );
     151                }
     152
     153                $registered = $this->registry->get_all_registered();
     154                $this->assertEqualSets( $names, array_keys( $registered ) );
     155        }
     156}
  • new file tests/phpunit/tests/blocks/block-type.php

    diff --git a/tests/phpunit/tests/blocks/block-type.php b/tests/phpunit/tests/blocks/block-type.php
    new file mode 100644
    index 0000000000..44aee07031
    - +  
     1<?php
     2/**
     3 * WP_Block_Type Tests
     4 *
     5 * @package WordPress
     6 * @subpackage Blocks
     7 */
     8
     9/**
     10 * Tests for WP_Block_Type
     11 *
     12 * @group blocks
     13 */
     14class WP_Test_Block_Type extends WP_UnitTestCase {
     15        function setUp() {
     16                parent::setUp();
     17        }
     18
     19        /**
     20         * Editor user ID.
     21         *
     22         * @var int
     23         */
     24        protected static $editor_user_id;
     25
     26        /**
     27         * ID for a post containing blocks.
     28         *
     29         * @var int
     30         */
     31        protected static $post_with_blocks;
     32
     33        /**
     34         * ID for a post without blocks.
     35         *
     36         * @var int
     37         */
     38        protected static $post_without_blocks;
     39
     40        /**
     41         * Set up before class.
     42         */
     43        public static function wpSetUpBeforeClass() {
     44                self::$editor_user_id = self::factory()->user->create(
     45                        array(
     46                                'role' => 'editor',
     47                        )
     48                );
     49
     50                self::$post_with_blocks = self::factory()->post->create(
     51                        array(
     52                                'post_title'   => 'Example',
     53                                'post_content' => "<!-- wp:core/text {\"dropCap\":true} -->\n<p class=\"has-drop-cap\">Tester</p>\n<!-- /wp:core/text -->",
     54                        )
     55                );
     56
     57                self::$post_without_blocks = self::factory()->post->create(
     58                        array(
     59                                'post_title'   => 'Example',
     60                                'post_content' => 'Tester',
     61                        )
     62                );
     63        }
     64
     65        function test_set_props() {
     66                $name = 'core/dummy';
     67                $args = array(
     68                        'render_callback' => array( $this, 'render_dummy_block' ),
     69                        'foo'             => 'bar',
     70                );
     71
     72                $block_type = new WP_Block_Type( $name, $args );
     73
     74                $this->assertSame( $name, $block_type->name );
     75                $this->assertSame( $args['render_callback'], $block_type->render_callback );
     76                $this->assertSame( $args['foo'], $block_type->foo );
     77        }
     78
     79        function test_render() {
     80                $attributes = array(
     81                        'foo' => 'bar',
     82                        'bar' => 'foo',
     83                );
     84
     85                $block_type = new WP_Block_Type(
     86                        'core/dummy',
     87                        array(
     88                                'render_callback' => array( $this, 'render_dummy_block' ),
     89                        )
     90                );
     91                $output     = $block_type->render( $attributes );
     92                $this->assertEquals( $attributes, json_decode( $output, true ) );
     93        }
     94
     95        function test_render_with_content() {
     96                $attributes = array(
     97                        'foo' => 'bar',
     98                        'bar' => 'foo',
     99                );
     100
     101                $content = 'baz';
     102
     103                $expected = array_merge( $attributes, array( '_content' => $content ) );
     104
     105                $block_type = new WP_Block_Type(
     106                        'core/dummy',
     107                        array(
     108                                'render_callback' => array( $this, 'render_dummy_block_with_content' ),
     109                        )
     110                );
     111                $output     = $block_type->render( $attributes, $content );
     112                $this->assertEquals( $expected, json_decode( $output, true ) );
     113        }
     114
     115        function test_render_for_static_block() {
     116                $block_type = new WP_Block_Type( 'core/dummy', array() );
     117                $output     = $block_type->render();
     118
     119                $this->assertEquals( '', $output );
     120        }
     121
     122        function test_is_dynamic_for_static_block() {
     123                $block_type = new WP_Block_Type( 'core/dummy', array() );
     124
     125                $this->assertFalse( $block_type->is_dynamic() );
     126        }
     127
     128        function test_is_dynamic_for_dynamic_block() {
     129                $block_type = new WP_Block_Type(
     130                        'core/dummy',
     131                        array(
     132                                'render_callback' => array( $this, 'render_dummy_block' ),
     133                        )
     134                );
     135
     136                $this->assertTrue( $block_type->is_dynamic() );
     137        }
     138
     139        function test_prepare_attributes() {
     140                $attributes = array(
     141                        'correct'            => 'include',
     142                        'wrongType'          => 5,
     143                        'wrongTypeDefaulted' => 5,
     144                        /* missingDefaulted */
     145                        'undefined'          => 'omit',
     146                );
     147
     148                $block_type = new WP_Block_Type(
     149                        'core/dummy',
     150                        array(
     151                                'attributes' => array(
     152                                        'correct'            => array(
     153                                                'type' => 'string',
     154                                        ),
     155                                        'wrongType'          => array(
     156                                                'type' => 'string',
     157                                        ),
     158                                        'wrongTypeDefaulted' => array(
     159                                                'type'    => 'string',
     160                                                'default' => 'defaulted',
     161                                        ),
     162                                        'missingDefaulted'   => array(
     163                                                'type'    => 'string',
     164                                                'default' => 'define',
     165                                        ),
     166                                ),
     167                        )
     168                );
     169
     170                $prepared_attributes = $block_type->prepare_attributes_for_render( $attributes );
     171
     172                $this->assertEquals(
     173                        array(
     174                                'correct'            => 'include',
     175                                'wrongType'          => null,
     176                                'wrongTypeDefaulted' => 'defaulted',
     177                                'missingDefaulted'   => 'define',
     178                        ),
     179                        $prepared_attributes
     180                );
     181        }
     182
     183        function test_has_block_with_mixed_content() {
     184                $mixed_post_content = 'before' .
     185                '<!-- wp:core/dummy --><!-- /wp:core/dummy -->' .
     186                '<!-- wp:core/dummy_atts {"value":"b1"} --><!-- /wp:core/dummy_atts -->' .
     187                '<!-- wp:core/dummy-child -->
     188                <p>testing the test</p>
     189                <!-- /wp:core/dummy-child -->' .
     190                'between' .
     191                '<!-- wp:core/self-close-dummy /-->' .
     192                '<!-- wp:custom/dummy {"value":"b2"} /-->' .
     193                'after';
     194
     195                $this->assertTrue( has_block( 'core/dummy', $mixed_post_content ) );
     196
     197                $this->assertTrue( has_block( 'core/dummy_atts', $mixed_post_content ) );
     198
     199                $this->assertTrue( has_block( 'core/dummy-child', $mixed_post_content ) );
     200
     201                $this->assertTrue( has_block( 'core/self-close-dummy', $mixed_post_content ) );
     202
     203                $this->assertTrue( has_block( 'custom/dummy', $mixed_post_content ) );
     204
     205                // checking for a partial block name should fail.
     206                $this->assertFalse( has_block( 'core/dumm', $mixed_post_content ) );
     207
     208                // checking for a wrong namespace should fail.
     209                $this->assertFalse( has_block( 'custom/dummy_atts', $mixed_post_content ) );
     210
     211                // checking for namespace only should not work. Or maybe ... ?
     212                $this->assertFalse( has_block( 'core', $mixed_post_content ) );
     213        }
     214
     215        function test_has_block_with_invalid_content() {
     216                // some content with invalid HMTL comments and a single valid block.
     217                $invalid_content = 'before' .
     218                '<!- - wp:core/weird-space --><!-- /wp:core/weird-space -->' .
     219                '<!--wp:core/untrimmed-left --><!-- /wp:core/untrimmed -->' .
     220                '<!-- wp:core/dummy --><!-- /wp:core/dummy -->' .
     221                '<!-- wp:core/untrimmed-right--><!-- /wp:core/untrimmed2 -->' .
     222                'after';
     223
     224                $this->assertFalse( has_block( 'core/text', self::$post_without_blocks ) );
     225
     226                $this->assertFalse( has_block( 'core/weird-space', $invalid_content ) );
     227
     228                $this->assertFalse( has_block( 'core/untrimmed-left', $invalid_content ) );
     229
     230                $this->assertFalse( has_block( 'core/untrimmed-right', $invalid_content ) );
     231
     232                $this->assertTrue( has_block( 'core/dummy', $invalid_content ) );
     233        }
     234
     235        function test_post_has_block() {
     236                // should fail for a non-existent block `custom/dummy`.
     237                $this->assertFalse( has_block( 'custom/dummy', self::$post_with_blocks ) );
     238
     239                // this functions should not work without the second param until the $post global is set.
     240                $this->assertFalse( has_block( 'core/text' ) );
     241                $this->assertFalse( has_block( 'core/dummy' ) );
     242
     243                global $post;
     244                $post = get_post( self::$post_with_blocks );
     245
     246                // check if the function correctly detects content from the $post global.
     247                $this->assertTrue( has_block( 'core/text' ) );
     248                // even if it detects a proper $post global it should still be false for a missing block.
     249                $this->assertFalse( has_block( 'core/dummy' ) );
     250        }
     251
     252        function render_dummy_block( $attributes ) {
     253                return json_encode( $attributes );
     254        }
     255
     256        function render_dummy_block_with_content( $attributes, $content ) {
     257                $attributes['_content'] = $content;
     258
     259                return json_encode( $attributes );
     260        }
     261}