Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport theme.json tests from Core #58476

Merged
merged 20 commits into from
Feb 2, 2024
Merged

Conversation

ajlende
Copy link
Contributor

@ajlende ajlende commented Jan 30, 2024

What?

Merges changes with Core for the following tests:

  1. WP_REST_Global_Styles_Controller_Gutenberg_Test
  2. WP_Theme_JSON_Resolver_Gutenberg_Test
  3. WP_Theme_JSON_Schema_Gutenberg_Test
  4. WP_Theme_JSON_Gutenberg_Test

Paired with WordPress/wordpress-develop#5979.

Why?

Backport.

How?

I tried to be as methodical as possible with the changes.

  1. Accept both changes where possible.
  2. Otherwise, prefer the change that is newer or follows standards more closely.
  3. Ensure all _Gutenberg postfixes are applied/removed.
  4. Replace other known functions that have a Gutenberg/core equivalent.
  5. Reorder tests to match.
  6. Manually fix tests that are still failing.
  7. Clean up any small leftover changes from a diff view including removing ticket comments.

Partial diffs

Here are some partial diffs between Gutenberg and Core that don't include comments or renames.

# Example if you'd like to run it yourself.
diff -uB <(cat wordpress-develop/tests/phpunit/tests/rest-api/rest-global-styles-controller.php | sed -E 's|^[[:space:]]*\/?\*.*$||g') <(cat gutenberg/phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php | sed -E 's|^[[:space:]]*\/?\*.*$||g' | sed 's|_Gutenberg||g')
WP_REST_Global_Styles_Controller_Gutenberg_Test
--- /dev/fd/11	2024-02-01 11:23:20.000000000 -0600
+++ /dev/fd/12	2024-02-01 11:23:20.000000000 -0600
@@ -33,7 +29,7 @@
 
 	public function set_up() {
 		parent::set_up();
-		switch_theme( 'tt1-blocks' );
+		switch_theme( 'emptytheme' );
 	}
 
 
@@ -61,9 +57,9 @@
 				'post_status'  => 'publish',
 				'post_title'   => 'Custom Styles',
 				'post_type'    => 'wp_global_styles',
-				'post_name'    => 'wp-global-styles-tt1-blocks',
+				'post_name'    => 'wp-global-styles-emptytheme',
 				'tax_input'    => array(
-					'wp_theme' => 'tt1-blocks',
+					'wp_theme' => 'emptytheme',
 				),
 			)
 		);
@@ -91,7 +86,7 @@
 			'Single global style based on the given ID route does not exist'
 		);
 		$this->assertCount(
-			2,
+			4, // Double core because both sets get registered in the plugin.
 			$routes['/wp/v2/global-styles/(?P<id>[\/\w-]+)'],
 			'Single global style based on the given ID route does not have exactly two elements'
 		);
@@ -101,7 +96,7 @@
 			'Theme global styles route does not exist'
 		);
 		$this->assertCount(
-			1,
+			2, // Double core because both sets get registered in the plugin.
 			$routes['/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)'],
 			'Theme global styles route does not have exactly one element'
 		);
@@ -121,56 +116,13 @@
 
 	public function test_get_theme_items() {
 		wp_set_current_user( self::$admin_id );
-		switch_theme( 'block-theme' );
-		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/block-theme/variations' );
+		switch_theme( 'emptytheme' );
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme/variations' );
 		$response = rest_get_server()->dispatch( $request );
 		$data     = $response->get_data();
 		$expected = array(
 			array(
 				'version'  => 2,
-				'title'    => 'variation-a',
-				'settings' => array(
-					'blocks' => array(
-						'core/paragraph' => array(
-							'color' => array(
-								'palette' => array(
-									'theme' => array(
-										array(
-											'slug'  => 'light',
-											'name'  => 'Light',
-											'color' => '#f2f2f2',
-										),
-									),
-								),
-							),
-						),
-					),
-				),
-			),
-			array(
-				'version'  => 2,
-				'title'    => 'variation-b',
-				'settings' => array(
-					'blocks' => array(
-						'core/post-title' => array(
-							'color' => array(
-								'palette' => array(
-									'theme' => array(
-										array(
-											'slug'  => 'light',
-											'name'  => 'Light',
-											'color' => '#f1f1f1',
-										),
-									),
-								),
-							),
-						),
-					),
-				),
-			),
-			array(
-				'version'  => 2,
-				'title'    => 'Block theme variation',
 				'settings' => array(
 					'color' => array(
 						'palette' => array(
@@ -193,6 +145,7 @@
 						),
 					),
 				),
+				'title'    => 'variation',
 			),
 		);
 
@@ -212,10 +165,9 @@
 
 
 
-
 	public function test_get_theme_item_no_user() {
 		wp_set_current_user( 0 );
-		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme' );
 		$response = rest_get_server()->dispatch( $request );
 		$this->assertErrorResponse( 'rest_cannot_manage_global_styles', $response, 401 );
 	}
@@ -223,10 +175,9 @@
 
 
 
-
 	public function test_get_theme_item_permission_check() {
 		wp_set_current_user( self::$subscriber_id );
-		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme' );
 		$response = rest_get_server()->dispatch( $request );
 		$this->assertErrorResponse( 'rest_cannot_manage_global_styles', $response, 403 );
 	}
@@ -364,10 +312,9 @@
 
 
 
-
 	public function test_get_theme_item_fields() {
 		wp_set_current_user( self::$admin_id );
-		$request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
+		$request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme' );
 		$request->set_param( '_fields', 'settings' );
 		$response = rest_get_server()->dispatch( $request );
 		$data     = $response->get_data();
WP_Theme_JSON_Resolver_Gutenberg_Test
--- /dev/fd/11	2024-02-01 11:20:38.000000000 -0600
+++ /dev/fd/12	2024-02-01 11:20:38.000000000 -0600
@@ -93,7 +91,7 @@
 
 	public function set_up() {
 		parent::set_up();
-		$this->theme_root = realpath( DIR_TESTDATA . '/themedir1' );
+		$this->theme_root = realpath( __DIR__ . '/data/themedir1' );
 
 		$this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
 
@@ -115,7 +113,7 @@
 		unset( $GLOBALS['wp_themes'] );
 
 		// Reset data between tests.
-		wp_clean_theme_json_cache();
+		_gutenberg_clean_theme_json_caches();
 		parent::tear_down();
 	}
 
@@ -127,14 +125,9 @@
 		return 'pl_PL';
 	}
 
-
-
-
-
-
 	public function test_translations_are_applied() {
 		add_filter( 'locale', array( $this, 'filter_set_locale_to_polish' ) );
-		load_textdomain( 'block-theme', realpath( DIR_TESTDATA . '/languages/themes/block-theme-pl_PL.mo' ) );
+		load_textdomain( 'block-theme', realpath( __DIR__ . '/data/languages/themes/block-theme-pl_PL.mo' ) );
 
 		switch_theme( 'block-theme' );
 		$theme_data       = WP_Theme_JSON_Resolver::get_theme_data();
@@ -381,9 +372,8 @@
 
 
 
-
 	public function test_get_core_data( $should_fire_filter, $core_is_cached, $blocks_are_cached ) {
-		wp_clean_theme_json_cache();
+		_gutenberg_clean_theme_json_caches();
 
 		// If should cache core, then fire the method to cache it before running the tests.
 		if ( $core_is_cached ) {
@@ -638,7 +619,7 @@
 		);
 		for ( $i = 0; $i < 3; $i++ ) {
 			WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
-			wp_clean_theme_json_cache();
+			_gutenberg_clean_theme_json_caches();
 		}
 		$this->assertSame( 0, $global_styles_query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' );
 
@@ -651,7 +632,7 @@
 		$global_styles_query_count = 0;
 		for ( $i = 0; $i < 3; $i++ ) {
 			$new_user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
-			wp_clean_theme_json_cache();
+			_gutenberg_clean_theme_json_caches();
 			$this->assertSameSets( $user_cpt, $new_user_cpt, "User CPTs do not match on run {$i}." );
 		}
 		$this->assertSame( 1, $global_styles_query_count, 'Unexpected SQL queries detected for the wp_global_style post type after creation.' );
@@ -668,7 +649,7 @@
 		$query_count = get_num_queries();
 		for ( $i = 0; $i < 3; $i++ ) {
 			WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
-			wp_clean_theme_json_cache();
+			_gutenberg_clean_theme_json_caches();
 		}
 		$query_count = get_num_queries() - $query_count;
 		$this->assertSame( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' );
WP_Theme_JSON_Schema_Gutenberg_Test
--- /dev/fd/11	2024-02-01 11:21:27.000000000 -0600
+++ /dev/fd/12	2024-02-01 11:21:27.000000000 -0600
WP_Theme_JSON_Gutenberg_Test
--- /dev/fd/11	2024-02-01 11:20:07.000000000 -0600
+++ /dev/fd/12	2024-02-01 11:20:07.000000000 -0600
@@ -8,12 +8,7 @@
 
 
 
-
-
-
-
-class Tests_Theme_wpThemeJson extends WP_UnitTestCase {
-
+class WP_Theme_JSON_Test extends WP_UnitTestCase {
 
 
 
@@ -284,6 +272,7 @@
 			),
 			'position'   => array(
 				'sticky' => true,
+				'fixed'  => true,
 			),
 			'spacing'    => array(
 				'blockGap' => false,
@@ -322,6 +311,7 @@
 					),
 					'position'   => array(
 						'sticky' => true,
+						'fixed'  => true,
 					),
 					'spacing'    => array(
 						'blockGap' => false,
@@ -1123,6 +1051,124 @@
 
 
 
+
+
+
+	public function test_get_stylesheet_with_deprecated_feature_level_selectors() {
+		$theme_json = new WP_Theme_JSON(
+			array(
+				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'settings' => array(
+					'border'     => array(
+						'radius' => true,
+					),
+					'color'      => array(
+						'custom'  => false,
+						'palette' => array(
+							array(
+								'slug'  => 'green',
+								'color' => 'green',
+							),
+						),
+					),
+					'spacing'    => array(
+						'padding' => true,
+					),
+					'typography' => array(
+						'fontSize' => true,
+					),
+				),
+				'styles'   => array(
+					'blocks' => array(
+						'test/test' => array(
+							'border'     => array(
+								'radius' => '9999px',
+							),
+							'color'      => array(
+								'text' => 'green',
+							),
+							'spacing'    => array(
+								'padding' => '20px',
+							),
+							'typography' => array(
+								'fontSize' => '3em',
+							),
+						),
+					),
+				),
+			)
+		);
+
+		$base_styles   = 'body{--wp--preset--color--green: green;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}';
+		$block_styles  = '.wp-block-test, .wp-block-test__wrapper{color: green;}.wp-block-test .inner, .wp-block-test__wrapper .inner{border-radius: 9999px;padding: 20px;}.wp-block-test .sub-heading, .wp-block-test__wrapper .sub-heading{font-size: 3em;}';
+		$preset_styles = '.has-green-color{color: var(--wp--preset--color--green) !important;}.has-green-background-color{background-color: var(--wp--preset--color--green) !important;}.has-green-border-color{border-color: var(--wp--preset--color--green) !important;}';
+		$expected      = $base_styles . $block_styles . $preset_styles;
+
+		$this->assertEquals( $expected, $theme_json->get_stylesheet() );
+	}
+
+
+
+
+
+
+
+
+
+	public function test_get_stylesheet_with_block_json_selectors() {
+		$theme_json = new WP_Theme_JSON(
+			array(
+				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'settings' => array(
+					'border'     => array(
+						'radius' => true,
+					),
+					'color'      => array(
+						'custom'  => false,
+						'palette' => array(
+							array(
+								'slug'  => 'green',
+								'color' => 'green',
+							),
+						),
+					),
+					'spacing'    => array(
+						'padding' => true,
+					),
+					'typography' => array(
+						'fontSize' => true,
+					),
+				),
+				'styles'   => array(
+					'blocks' => array(
+						'my/block-with-selectors' => array(
+							'border'     => array(
+								'radius' => '9999px',
+							),
+							'color'      => array(
+								'background' => 'grey',
+								'text'       => 'navy',
+							),
+							'spacing'    => array(
+								'padding' => '20px',
+							),
+							'typography' => array(
+								'fontSize' => '3em',
+							),
+						),
+					),
+				),
+			)
+		);
+
+		$base_styles   = 'body{--wp--preset--color--green: green;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}';
+		$block_styles  = '.custom-root-selector{background-color: grey;padding: 20px;}.custom-root-selector img{border-radius: 9999px;}.custom-root-selector > figcaption{color: navy;font-size: 3em;}';
+		$preset_styles = '.has-green-color{color: var(--wp--preset--color--green) !important;}.has-green-background-color{background-color: var(--wp--preset--color--green) !important;}.has-green-border-color{border-color: var(--wp--preset--color--green) !important;}';
+		$expected      = $base_styles . $block_styles . $preset_styles;
+
+		$this->assertEquals( $expected, $theme_json->get_stylesheet() );
+	}
+
 	public function test_get_stylesheet_generates_layout_styles() {
 		$theme_json = new WP_Theme_JSON(
 			array(
@@ -1305,7 +1330,8 @@
 		);
 
 		$this->assertSame(
-			'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 1rem; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child:first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child:last-child { margin-block-end: 0; }body { --wp--style--block-gap: 1rem; }:where(body .is-layout-flow)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-flow)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-flow)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-constrained)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-constrained)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-constrained)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-flex) {gap: 1rem;}:where(body .is-layout-grid) {gap: 1rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}.wp-block-post-content{color: gray;}.wp-block-social-links-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-flex{gap: 0;}.wp-block-social-links-is-layout-grid{gap: 0;}.wp-block-buttons-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-flex{gap: 0;}.wp-block-buttons-is-layout-grid{gap: 0;}',
+			'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 1rem; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child:first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child:last-child { margin-block-end: 0; }body { --wp--style--block-gap: 1rem; }:where(body .is-layout-flow)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-flow)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-flow)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-constrained)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-constrained)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-constrained)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-flex) {gap: 1rem;}:where(body .is-layout-grid) {gap: 1rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}' .
+			'.wp-block-post-content{color: gray;}.wp-block-social-links-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-flex{gap: 0;}.wp-block-social-links-is-layout-grid{gap: 0;}.wp-block-buttons-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-flex{gap: 0;}.wp-block-buttons-is-layout-grid{gap: 0;}',
 			$theme_json->get_stylesheet()
 		);
 	}
@@ -3713,7 +3628,7 @@
 			'selector' => 'body',
 		);
 
-		$expected    = 'body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding):not(.is-layout-flex):not(.is-layout-grid)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}';
+		$expected    = 'body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding):not(.is-layout-flex):not(.is-layout-grid)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),.wp-block:not(.alignfull),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),.wp-block:not(.alignfull),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}';
 		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
 		$style_rules = $theme_json->get_styles_for_block( $metadata );
 		$this->assertSame( $expected, $root_rules . $style_rules );
@@ -4641,9 +4523,6 @@
 		);
 
 		$theme_json->set_spacing_sizes();
-
-		restore_error_handler();
-
 		$this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) );
 	}
 
@@ -5244,4 +5106,90 @@
 		$this->assertEquals( $small_font, $styles['blocks']['core/quote']['variations']['plain']['typography']['fontSize'], 'Block variations: font-size' );
 		$this->assertEquals( $secondary_color, $styles['blocks']['core/quote']['variations']['plain']['color']['background'], 'Block variations: color' );
 	}
+
+
+
+
+
+
+
+
+
+
+	public function test_get_block_style_variation_selector( $selector, $expected ) {
+		$theme_json = new ReflectionClass( 'WP_Theme_JSON' );
+
+		$func = $theme_json->getMethod( 'get_block_style_variation_selector' );
+		$func->setAccessible( true );
+
+		$actual = $func->invoke( null, 'custom', $selector );
+
+		$this->assertEquals( $expected, $actual );
+	}
+
+
+
+
+
+
+	public function data_get_block_style_variation_selector() {
+		return array(
+			'empty block selector'     => array(
+				'selector' => '',
+				'expected' => '.is-style-custom',
+			),
+			'class selector'           => array(
+				'selector' => '.wp-block',
+				'expected' => '.wp-block.is-style-custom',
+			),
+			'id selector'              => array(
+				'selector' => '#wp-block',
+				'expected' => '#wp-block.is-style-custom',
+			),
+			'element tag selector'     => array(
+				'selector' => 'p',
+				'expected' => 'p.is-style-custom',
+			),
+			'attribute selector'       => array(
+				'selector' => '[style*="color"]',
+				'expected' => '[style*="color"].is-style-custom',
+			),
+			'descendant selector'      => array(
+				'selector' => '.wp-block .inner',
+				'expected' => '.wp-block.is-style-custom .inner',
+			),
+			'comma separated selector' => array(
+				'selector' => '.wp-block .inner, .wp-block .alternative',
+				'expected' => '.wp-block.is-style-custom .inner, .wp-block.is-style-custom .alternative',
+			),
+			'pseudo selector'          => array(
+				'selector' => 'div:first-child',
+				'expected' => 'div.is-style-custom:first-child',
+			),
+			':is selector'             => array(
+				'selector' => '.wp-block:is(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:is(.outer .inner:first-child)',
+			),
+			':not selector'            => array(
+				'selector' => '.wp-block:not(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:not(.outer .inner:first-child)',
+			),
+			':has selector'            => array(
+				'selector' => '.wp-block:has(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:has(.outer .inner:first-child)',
+			),
+			':where selector'          => array(
+				'selector' => '.wp-block:where(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:where(.outer .inner:first-child)',
+			),
+			'wrapping :where selector' => array(
+				'selector' => ':where(.outer .inner:first-child)',
+				'expected' => ':where(.outer.is-style-custom .inner:first-child)',
+			),
+			'complex'                  => array(
+				'selector' => '.wp:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
+				'expected' => '.wp.is-style-custom:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp.is-style-custom:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
+			),
+		);
+	}
 }

There are a few things of note:

  1. The REST controller uses emptytheme in Gutenberg and tt1-blocks in core. I found a comment that seemed to indicate that was intentional, so I left it that way. However, it means the diff isn't as clean.
  2. Some features in class-wp-theme-json-gutenberg.php haven't been backported yet, so the corresponding tests were not included in core.
  3. class-wp-theme-json-schema-gutenberg.php did not exist. Part of my reason for merging all these tests was because I had a PR with tests only failing in core during the backport. Some of those changes needed to be in the shcema, so I copied it over here now.
  4. Some assets changed for the resolver tests, so I ended up adding them in both places. I believe it only affects tests, and all tests pass, so I think it's okay.

Full diffs

Here are the full diffs between Gutenberg and Core for reference.

WP_REST_Global_Styles_Controller_Gutenberg_Test
--- wordpress-develop/tests/phpunit/tests/rest-api/rest-global-styles-controller.php	2024-01-31 20:07:34.000000000 -0600
+++ gutenberg/phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php	2024-01-31 20:15:54.000000000 -0600
@@ -1,16 +1,12 @@
 <?php
 /**
- * Unit tests covering WP_REST_Global_Styles_Controller functionality.
+ * Unit tests covering WP_REST_Global_Styles_Controller_Gutenberg functionality.
  *
- * @package WordPress
- * @subpackage REST API
+ * @package Gutenberg
  *
- * @covers WP_REST_Global_Styles_Controller
- *
- * @group restapi-global-styles
- * @group restapi
+ * @covers WP_REST_Global_Styles_Controller_Gutenberg
  */
-class WP_REST_Global_Styles_Controller_Test extends WP_Test_REST_Controller_Testcase {
+class WP_REST_Global_Styles_Controller_Gutenberg_Test extends WP_Test_REST_Controller_Testcase {
 	/**
 	 * @var int
 	 */
@@ -33,7 +29,7 @@
 
 	public function set_up() {
 		parent::set_up();
-		switch_theme( 'tt1-blocks' );
+		switch_theme( 'emptytheme' );
 	}
 
 	/**
@@ -57,13 +53,13 @@
 		// This creates the global styles for the current theme.
 		self::$global_styles_id = $factory->post->create(
 			array(
-				'post_content' => '{"version": ' . WP_Theme_JSON::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }',
+				'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }',
 				'post_status'  => 'publish',
 				'post_title'   => 'Custom Styles',
 				'post_type'    => 'wp_global_styles',
-				'post_name'    => 'wp-global-styles-tt1-blocks',
+				'post_name'    => 'wp-global-styles-emptytheme',
 				'tax_input'    => array(
-					'wp_theme' => 'tt1-blocks',
+					'wp_theme' => 'emptytheme',
 				),
 			)
 		);
@@ -80,8 +76,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::register_routes
-	 * @ticket 54596
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::register_routes
 	 */
 	public function test_register_routes() {
 		$routes = rest_get_server()->get_routes();
@@ -91,7 +86,7 @@
 			'Single global style based on the given ID route does not exist'
 		);
 		$this->assertCount(
-			2,
+			4, // Double core because both sets get registered in the plugin.
 			$routes['/wp/v2/global-styles/(?P<id>[\/\w-]+)'],
 			'Single global style based on the given ID route does not have exactly two elements'
 		);
@@ -101,7 +96,7 @@
 			'Theme global styles route does not exist'
 		);
 		$this->assertCount(
-			1,
+			2, // Double core because both sets get registered in the plugin.
 			$routes['/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)'],
 			'Theme global styles route does not have exactly one element'
 		);
@@ -121,56 +116,13 @@
 
 	public function test_get_theme_items() {
 		wp_set_current_user( self::$admin_id );
-		switch_theme( 'block-theme' );
-		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/block-theme/variations' );
+		switch_theme( 'emptytheme' );
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme/variations' );
 		$response = rest_get_server()->dispatch( $request );
 		$data     = $response->get_data();
 		$expected = array(
 			array(
 				'version'  => 2,
-				'title'    => 'variation-a',
-				'settings' => array(
-					'blocks' => array(
-						'core/paragraph' => array(
-							'color' => array(
-								'palette' => array(
-									'theme' => array(
-										array(
-											'slug'  => 'light',
-											'name'  => 'Light',
-											'color' => '#f2f2f2',
-										),
-									),
-								),
-							),
-						),
-					),
-				),
-			),
-			array(
-				'version'  => 2,
-				'title'    => 'variation-b',
-				'settings' => array(
-					'blocks' => array(
-						'core/post-title' => array(
-							'color' => array(
-								'palette' => array(
-									'theme' => array(
-										array(
-											'slug'  => 'light',
-											'name'  => 'Light',
-											'color' => '#f1f1f1',
-										),
-									),
-								),
-							),
-						),
-					),
-				),
-			),
-			array(
-				'version'  => 2,
-				'title'    => 'Block theme variation',
 				'settings' => array(
 					'color' => array(
 						'palette' => array(
@@ -193,6 +145,7 @@
 						),
 					),
 				),
+				'title'    => 'variation',
 			),
 		);
 
@@ -210,30 +163,27 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_theme_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_theme_item
 	 */
 	public function test_get_theme_item_no_user() {
 		wp_set_current_user( 0 );
-		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme' );
 		$response = rest_get_server()->dispatch( $request );
 		$this->assertErrorResponse( 'rest_cannot_manage_global_styles', $response, 401 );
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_theme_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_theme_item
 	 */
 	public function test_get_theme_item_permission_check() {
 		wp_set_current_user( self::$subscriber_id );
-		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
+		$request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme' );
 		$response = rest_get_server()->dispatch( $request );
 		$this->assertErrorResponse( 'rest_cannot_manage_global_styles', $response, 403 );
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_theme_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_theme_item
 	 */
 	public function test_get_theme_item_invalid() {
 		wp_set_current_user( self::$admin_id );
@@ -244,8 +194,7 @@
 
 	/**
 	 * @dataProvider data_get_theme_item_invalid_theme_dirname
-	 * @covers WP_REST_Global_Styles_Controller::get_theme_item
-	 * @ticket 54596
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_theme_item
 	 *
 	 * @param string $theme_dirname Theme directory to test.
 	 * @param string $expected      Expected error code.
@@ -304,8 +253,7 @@
 
 	/**
 	 * @dataProvider data_get_theme_item
-	 * @covers WP_REST_Global_Styles_Controller::get_theme_item
-	 * @ticket 54596
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_theme_item
 	 *
 	 * @param string $theme Theme directory to test.
 	 */
@@ -362,12 +310,11 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_theme_item
-	 * @ticket 54595
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_theme_item
 	 */
 	public function test_get_theme_item_fields() {
 		wp_set_current_user( self::$admin_id );
-		$request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
+		$request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/emptytheme' );
 		$request->set_param( '_fields', 'settings' );
 		$response = rest_get_server()->dispatch( $request );
 		$data     = $response->get_data();
@@ -376,8 +323,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item
 	 */
 	public function test_get_item_no_user() {
 		wp_set_current_user( 0 );
@@ -387,8 +333,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item
 	 */
 	public function test_get_item_invalid_post() {
 		wp_set_current_user( self::$admin_id );
@@ -398,8 +343,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item
 	 */
 	public function test_get_item_permission_check() {
 		wp_set_current_user( self::$subscriber_id );
@@ -409,8 +353,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item
 	 */
 	public function test_get_item_no_user_edit() {
 		wp_set_current_user( 0 );
@@ -421,8 +364,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item
 	 */
 	public function test_get_item_permission_check_edit() {
 		wp_set_current_user( self::$subscriber_id );
@@ -433,7 +375,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item
 	 */
 	public function test_get_item() {
 		wp_set_current_user( self::$admin_id );
@@ -467,8 +409,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::update_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::update_item
 	 */
 	public function test_update_item() {
 		wp_set_current_user( self::$admin_id );
@@ -484,8 +425,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::update_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::update_item
 	 */
 	public function test_update_item_no_user() {
 		wp_set_current_user( 0 );
@@ -495,8 +435,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::update_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::update_item
 	 */
 	public function test_update_item_invalid_post() {
 		wp_set_current_user( self::$admin_id );
@@ -506,8 +445,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::update_item
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::update_item
 	 */
 	public function test_update_item_permission_check() {
 		wp_set_current_user( self::$subscriber_id );
@@ -517,8 +455,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::update_item
-	 * @ticket 57536
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::update_item
 	 */
 	public function test_update_item_valid_styles_css() {
 		wp_set_current_user( self::$admin_id );
@@ -537,8 +474,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::update_item
-	 * @ticket 57536
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::update_item
 	 */
 	public function test_update_item_invalid_styles_css() {
 		wp_set_current_user( self::$admin_id );
@@ -570,8 +506,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_item_schema
-	 * @ticket 54516
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_item_schema
 	 */
 	public function test_get_item_schema() {
 		$request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/global-styles/' . self::$global_styles_id );
@@ -586,7 +521,7 @@
 	}
 
 	/**
-	 * @covers WP_REST_Global_Styles_Controller::get_available_actions
+	 * @covers WP_REST_Global_Styles_Controller_Gutenberg::get_available_actions
 	 */
 	public function test_assign_edit_css_action_admin() {
 		wp_set_current_user( self::$admin_id );
WP_Theme_JSON_Resolver_Gutenberg_Test
--- wordpress-develop/tests/phpunit/tests/theme/wpThemeJsonResolver.php	2024-01-31 20:00:59.000000000 -0600
+++ gutenberg/phpunit/class-wp-theme-json-resolver-test.php	2024-01-31 20:00:59.000000000 -0600
@@ -1,16 +1,14 @@
 <?php
 
 /**
- * Test WP_Theme_JSON_Resolver class.
+ * Test WP_Theme_JSON_Resolver_Gutenberg class.
  *
- * @package WordPress
- * @subpackage Theme
+ * @package Gutenberg
  *
  * @since 5.8.0
- *
- * @group themes
  */
-class Tests_Theme_wpThemeJsonResolver extends WP_UnitTestCase {
+
+class WP_Theme_JSON_Resolver_Gutenberg_Test extends WP_UnitTestCase {
 
 	/**
 	 * Administrator ID.
@@ -20,30 +18,30 @@
 	protected static $administrator_id;
 
 	/**
-	 * WP_Theme_JSON_Resolver::$blocks_cache property.
+	 * WP_Theme_JSON_Resolver_Gutenberg::$blocks_cache property.
 	 *
 	 * @var ReflectionProperty
 	 */
 	private static $property_blocks_cache;
 
 	/**
-	 * Original value of the WP_Theme_JSON_Resolver::$blocks_cache property.
+	 * Original value of the WP_Theme_JSON_Resolver_Gutenberg::$blocks_cache property.
 	 *
 	 * @var array
 	 */
 	private static $property_blocks_cache_orig_value;
 
 	/**
-	 * WP_Theme_JSON_Resolver::$core property.
+	 * WP_Theme_JSON_Resolver_Gutenberg::$core property.
 	 *
 	 * @var ReflectionProperty
 	 */
 	private static $property_core;
 
 	/**
-	 * Original value of the WP_Theme_JSON_Resolver::$core property.
+	 * Original value of the WP_Theme_JSON_Resolver_Gutenberg::$core property.
 	 *
-	 * @var WP_Theme_JSON
+	 * @var WP_Theme_JSON_Gutenberg
 	 */
 	private static $property_core_orig_value;
 
@@ -76,11 +74,11 @@
 			)
 		);
 
-		static::$property_blocks_cache = new ReflectionProperty( WP_Theme_JSON_Resolver::class, 'blocks_cache' );
+		static::$property_blocks_cache = new ReflectionProperty( WP_Theme_JSON_Resolver_Gutenberg::class, 'blocks_cache' );
 		static::$property_blocks_cache->setAccessible( true );
 		static::$property_blocks_cache_orig_value = static::$property_blocks_cache->getValue();
 
-		static::$property_core = new ReflectionProperty( WP_Theme_JSON_Resolver::class, 'core' );
+		static::$property_core = new ReflectionProperty( WP_Theme_JSON_Resolver_Gutenberg::class, 'core' );
 		static::$property_core->setAccessible( true );
 		static::$property_core_orig_value = static::$property_core->getValue();
 	}
@@ -93,7 +91,7 @@
 
 	public function set_up() {
 		parent::set_up();
-		$this->theme_root = realpath( DIR_TESTDATA . '/themedir1' );
+		$this->theme_root = realpath( __DIR__ . '/data/themedir1' );
 
 		$this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
 
@@ -115,7 +113,7 @@
 		unset( $GLOBALS['wp_themes'] );
 
 		// Reset data between tests.
-		wp_clean_theme_json_cache();
+		_gutenberg_clean_theme_json_caches();
 		parent::tear_down();
 	}
 
@@ -127,18 +125,13 @@
 		return 'pl_PL';
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 * @ticket 56611
-	 */
 	public function test_translations_are_applied() {
 		add_filter( 'locale', array( $this, 'filter_set_locale_to_polish' ) );
-		load_textdomain( 'block-theme', realpath( DIR_TESTDATA . '/languages/themes/block-theme-pl_PL.mo' ) );
+		load_textdomain( 'block-theme', realpath( __DIR__ . '/data/languages/themes/block-theme-pl_PL.mo' ) );
 
 		switch_theme( 'block-theme' );
-		$theme_data       = WP_Theme_JSON_Resolver::get_theme_data();
-		$style_variations = WP_Theme_JSON_Resolver::get_style_variations();
+		$theme_data       = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data();
+		$style_variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations();
 
 		unload_textdomain( 'block-theme' );
 		remove_filter( 'locale', array( $this, 'filter_set_locale_to_polish' ) );
@@ -264,19 +257,18 @@
 	}
 
 	/**
-	 * Tests when WP_Theme_JSON_Resolver::$blocks_cache is empty or
+	 * Tests when WP_Theme_JSON_Resolver_Gutenberg::$blocks_cache is empty or
 	 * does not match the all registered blocks.
 	 *
 	 * Though this is a non-public method, it is vital to other functionality.
 	 * Therefore, tests are provided to validate it functions as expected.
 	 *
 	 * @dataProvider data_has_same_registered_blocks_when_all_blocks_not_cached
-	 * @ticket 56467
 	 *
 	 * @param string $origin The origin to test.
 	 */
 	public function test_has_same_registered_blocks_when_all_blocks_not_cached( $origin, array $cache = array() ) {
-		$has_same_registered_blocks = new ReflectionMethod( WP_Theme_JSON_Resolver::class, 'has_same_registered_blocks' );
+		$has_same_registered_blocks = new ReflectionMethod( WP_Theme_JSON_Resolver_Gutenberg::class, 'has_same_registered_blocks' );
 		$has_same_registered_blocks->setAccessible( true );
 		$expected_cache = $this->get_registered_block_names();
 
@@ -285,9 +277,9 @@
 		$blocks_cache[ $origin ] = $cache;
 		static::$property_blocks_cache->setValue( null, $blocks_cache );
 
-		$this->assertFalse( $has_same_registered_blocks->invoke( null, $origin ), 'WP_Theme_JSON_Resolver::has_same_registered_blocks() should return false when same blocks are not cached' );
+		$this->assertFalse( $has_same_registered_blocks->invoke( null, $origin ), 'WP_Theme_JSON_Resolver_Gutenberg::has_same_registered_blocks() should return false when same blocks are not cached' );
 		$blocks_cache = static::$property_blocks_cache->getValue();
-		$this->assertSameSets( $expected_cache, $blocks_cache[ $origin ], 'WP_Theme_JSON_Resolver::$blocks_cache should contain all expected block names for the given origin' );
+		$this->assertSameSets( $expected_cache, $blocks_cache[ $origin ], 'WP_Theme_JSON_Resolver_Gutenberg::$blocks_cache should contain all expected block names for the given origin' );
 	}
 
 	/**
@@ -338,19 +330,18 @@
 	}
 
 	/**
-	 * Tests when WP_Theme_JSON_Resolver::$blocks_cache is empty or
+	 * Tests when WP_Theme_JSON_Resolver_Gutenberg::$blocks_cache is empty or
 	 * does not match the all registered blocks.
 	 *
 	 * Though this is a non-public method, it is vital to other functionality.
 	 * Therefore, tests are provided to validate it functions as expected.
 	 *
 	 * @dataProvider data_has_same_registered_blocks_when_all_blocks_are_cached
-	 * @ticket 56467
 	 *
 	 * @param string $origin The origin to test.
 	 */
 	public function test_has_same_registered_blocks_when_all_blocks_are_cached( $origin ) {
-		$has_same_registered_blocks = new ReflectionMethod( WP_Theme_JSON_Resolver::class, 'has_same_registered_blocks' );
+		$has_same_registered_blocks = new ReflectionMethod( WP_Theme_JSON_Resolver_Gutenberg::class, 'has_same_registered_blocks' );
 		$has_same_registered_blocks->setAccessible( true );
 		$expected_cache = $this->get_registered_block_names();
 
@@ -359,8 +350,8 @@
 		$blocks_cache[ $origin ] = $this->get_registered_block_names();
 		static::$property_blocks_cache->setValue( null, $blocks_cache );
 
-		$this->assertTrue( $has_same_registered_blocks->invoke( null, $origin ), 'WP_Theme_JSON_Resolver::has_same_registered_blocks() should return true when using the cache' );
-		$this->assertSameSets( $expected_cache, $blocks_cache[ $origin ], 'WP_Theme_JSON_Resolver::$blocks_cache should contain all expected block names for the given origin' );
+		$this->assertTrue( $has_same_registered_blocks->invoke( null, $origin ), 'WP_Theme_JSON_Resolver_Gutenberg::has_same_registered_blocks() should return true when using the cache' );
+		$this->assertSameSets( $expected_cache, $blocks_cache[ $origin ], 'WP_Theme_JSON_Resolver_Gutenberg::$blocks_cache should contain all expected block names for the given origin' );
 	}
 
 	/**
@@ -379,15 +370,14 @@
 
 	/**
 	 * @dataProvider data_get_core_data
-	 * @covers WP_Theme_JSON_Resolver::get_core_data
-	 * @ticket 56467
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_core_data
 	 */
 	public function test_get_core_data( $should_fire_filter, $core_is_cached, $blocks_are_cached ) {
-		wp_clean_theme_json_cache();
+		_gutenberg_clean_theme_json_caches();
 
 		// If should cache core, then fire the method to cache it before running the tests.
 		if ( $core_is_cached ) {
-			WP_Theme_JSON_Resolver::get_core_data();
+			WP_Theme_JSON_Resolver_Gutenberg::get_core_data();
 		}
 
 		// If should cache registered blocks, then set them up before running the tests.
@@ -398,14 +388,14 @@
 		}
 
 		$expected_filter_count = did_filter( 'wp_theme_json_data_default' );
-		$actual                = WP_Theme_JSON_Resolver::get_core_data();
+		$actual                = WP_Theme_JSON_Resolver_Gutenberg::get_core_data();
 		if ( $should_fire_filter ) {
 			++$expected_filter_count;
 		}
 
 		$this->assertSame( $expected_filter_count, did_filter( 'wp_theme_json_data_default' ), 'The filter "wp_theme_json_data_default" should fire the given number of times' );
-		$this->assertInstanceOf( WP_Theme_JSON::class, $actual, 'WP_Theme_JSON_Resolver::get_core_data() should return instance of WP_Theme_JSON' );
-		$this->assertSame( static::$property_core->getValue(), $actual, 'WP_Theme_JSON_Resolver::$core property should be the same object as returned from WP_Theme_JSON_Resolver::get_core_data()' );
+		$this->assertInstanceOf( WP_Theme_JSON_Gutenberg::class, $actual, 'WP_Theme_JSON_Resolver_Gutenberg::get_core_data() should return instance of WP_Theme_JSON_Gutenberg' );
+		$this->assertSame( static::$property_core->getValue(), $actual, 'WP_Theme_JSON_Resolver_Gutenberg::$core property should be the same object as returned from WP_Theme_JSON_Resolver_Gutenberg::get_core_data()' );
 	}
 
 	/**
@@ -439,9 +429,6 @@
 	}
 
 	/**
-	 * @ticket 54336
-	 * @ticket 60118
-	 *
 	 * @covers ::add_theme_support
 	 */
 	public function test_add_theme_supports_are_loaded_for_themes_without_theme_json() {
@@ -467,7 +454,7 @@
 		add_theme_support( 'custom-line-height' );
 		add_theme_support( 'appearance-tools' );
 
-		$settings = WP_Theme_JSON_Resolver::get_theme_data()->get_settings();
+		$settings = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_settings();
 
 		remove_theme_support( 'custom-line-height' );
 		remove_theme_support( 'editor-color-palette' );
@@ -481,13 +468,11 @@
 
 	/**
 	 * Tests that classic themes still get core default settings such as color palette and duotone.
-	 *
-	 * @ticket 60136
 	 */
 	public function test_core_default_settings_are_loaded_for_themes_without_theme_json() {
 		switch_theme( 'default' );
 
-		$settings = WP_Theme_JSON_Resolver::get_merged_data( 'theme' )->get_settings();
+		$settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' )->get_settings();
 
 		$this->assertFalse( wp_theme_has_theme_json() );
 		$this->assertTrue( $settings['color']['defaultPalette'] );
@@ -495,14 +480,10 @@
 		$this->assertTrue( $settings['color']['defaultGradients'] );
 	}
 
-	/**
-	 * @ticket 54336
-	 * @ticket 56611
-	 */
 	public function test_merges_child_theme_json_into_parent_theme_json() {
 		switch_theme( 'block-theme-child' );
 
-		$actual_settings   = WP_Theme_JSON_Resolver::get_theme_data()->get_settings();
+		$actual_settings   = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_settings();
 		$expected_settings = array(
 			'color'      => array(
 				'custom'         => false,
@@ -613,19 +594,19 @@
 					'postTypes' => array( 'post' ),
 				),
 			),
-			WP_Theme_JSON_Resolver::get_theme_data()->get_custom_templates()
+			WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_custom_templates()
 		);
 	}
 
 	/**
-	 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles
 	 */
 	public function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries() {
 		// Switch to a theme that does have support.
 		switch_theme( 'block-theme' );
 		wp_set_current_user( self::$administrator_id );
 		$theme = wp_get_theme();
-		WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$global_styles_query_count = 0;
 		add_filter(
 			'query',
@@ -637,49 +618,48 @@
 			}
 		);
 		for ( $i = 0; $i < 3; $i++ ) {
-			WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
-			wp_clean_theme_json_cache();
+			WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
+			_gutenberg_clean_theme_json_caches();
 		}
 		$this->assertSame( 0, $global_styles_query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' );
 
-		$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' );
 
-		$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true );
+		$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true );
 		$this->assertNotEmpty( $user_cpt, 'User CPT is expected not to be empty.' );
 
 		$global_styles_query_count = 0;
 		for ( $i = 0; $i < 3; $i++ ) {
-			$new_user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
-			wp_clean_theme_json_cache();
+			$new_user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
+			_gutenberg_clean_theme_json_caches();
 			$this->assertSameSets( $user_cpt, $new_user_cpt, "User CPTs do not match on run {$i}." );
 		}
 		$this->assertSame( 1, $global_styles_query_count, 'Unexpected SQL queries detected for the wp_global_style post type after creation.' );
 	}
 
 	/**
-	 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles
 	 */
 	public function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries_for_logged_out_users() {
 		// Switch to a theme that does have support.
 		switch_theme( 'block-theme' );
 		$theme = wp_get_theme();
-		WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$query_count = get_num_queries();
 		for ( $i = 0; $i < 3; $i++ ) {
-			WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
-			wp_clean_theme_json_cache();
+			WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
+			_gutenberg_clean_theme_json_caches();
 		}
 		$query_count = get_num_queries() - $query_count;
 		$this->assertSame( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' );
 
-		$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' );
 	}
 
 	/**
-	 * @ticket 56945
-	 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles
 	 */
 	public function test_get_user_data_from_wp_global_styles_does_not_run_for_theme_without_support() {
 		// The 'default' theme does not support theme.json.
@@ -690,79 +670,75 @@
 		$start_queries = get_num_queries();
 
 		// When theme.json is not supported, the method should not run a query and always return an empty result.
-		$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' );
 		$this->assertSame( 0, get_num_queries() - $start_queries, 'Unexpected SQL query detected for theme without theme.json support.' );
 
-		$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true );
+		$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true );
 		$this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' );
 		$this->assertSame( 0, get_num_queries() - $start_queries, 'Unexpected SQL query detected for theme without theme.json support.' );
 	}
 
 	/**
-	 * @ticket 55392
-	 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles
 	 */
 	public function test_get_user_data_from_wp_global_styles_does_exist() {
 		// Switch to a theme that does have support.
 		switch_theme( 'block-theme' );
 		$theme = wp_get_theme();
-		$post1 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true );
+		$post1 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true );
 		$this->assertIsArray( $post1 );
 		$this->assertArrayHasKey( 'ID', $post1 );
 		wp_delete_post( $post1['ID'], true );
-		$post2 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true );
+		$post2 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true );
 		$this->assertIsArray( $post2 );
 		$this->assertArrayHasKey( 'ID', $post2 );
 	}
 
 	/**
-	 * @ticket 55392
-	 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles
 	 */
 	public function test_get_user_data_from_wp_global_styles_create_post() {
 		// Switch to a theme that does have support.
 		switch_theme( 'block-theme' );
 		$theme = wp_get_theme( 'testing' );
-		$post1 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		$post1 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$this->assertIsArray( $post1 );
 		$this->assertSameSets( array(), $post1 );
-		$post2 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme );
+		$post2 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme );
 		$this->assertIsArray( $post2 );
 		$this->assertSameSets( array(), $post2 );
-		$post3 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true );
+		$post3 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true );
 		$this->assertIsArray( $post3 );
 		$this->assertArrayHasKey( 'ID', $post3 );
 	}
 
 	/**
-	 * @ticket 55392
-	 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles
 	 */
 	public function test_get_user_data_from_wp_global_styles_filter_state() {
 		// Switch to a theme that does have support.
 		switch_theme( 'block-theme' );
 		$theme = wp_get_theme( 'foo' );
-		$post1 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true, array( 'publish' ) );
+		$post1 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, true, array( 'publish' ) );
 		$this->assertIsArray( $post1 );
 		$this->assertArrayHasKey( 'ID', $post1 );
-		$post2 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, false, array( 'draft' ) );
+		$post2 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme, false, array( 'draft' ) );
 		$this->assertIsArray( $post2 );
 		$this->assertSameSets( array(), $post2 );
 	}
 
 	/**
-	 * @ticket 56835
-	 * @covers WP_Theme_JSON_Resolver::get_theme_data
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_theme_data
 	 */
 	public function test_get_theme_data_theme_supports_overrides_theme_json() {
 		switch_theme( 'default' );
 
-		// Test that get_theme_data() returns a WP_Theme_JSON object.
-		$theme_json_resolver = new WP_Theme_JSON_Resolver();
+		// Test that get_theme_data() returns a WP_Theme_JSON_Gutenberg object.
+		$theme_json_resolver = new WP_Theme_JSON_Resolver_Gutenberg();
 		$theme_json_resolver->get_merged_data();
 		$theme_data = $theme_json_resolver->get_theme_data();
-		$this->assertInstanceOf( 'WP_Theme_JSON', $theme_data, 'Theme data should be an instance of WP_Theme_JSON.' );
+		$this->assertInstanceOf( 'WP_Theme_JSON_Gutenberg', $theme_data, 'Theme data should be an instance of WP_Theme_JSON_Gutenberg.' );
 
 		// Test that wp_theme_json_data_theme filter has been called.
 		$this->assertGreaterThan( 0, did_filter( 'wp_theme_json_data_default' ), 'The filter "wp_theme_json_data_default" should fire.' );
@@ -780,14 +756,13 @@
 	}
 
 	/**
-	 * @ticket 56945
-	 * @covers WP_Theme_JSON_Resolver::get_theme_data
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_theme_data
 	 */
 	public function test_get_theme_data_does_not_parse_theme_json_if_not_present() {
 		// The 'default' theme does not support theme.json.
 		switch_theme( 'default' );
 
-		$theme_json_resolver = new WP_Theme_JSON_Resolver();
+		$theme_json_resolver = new WP_Theme_JSON_Resolver_Gutenberg();
 
 		// Force-unset $i18n_schema property to "unload" translation schema.
 		$property = new ReflectionProperty( $theme_json_resolver, 'i18n_schema' );
@@ -795,11 +770,11 @@
 		$property->setValue( null, null );
 
 		// A completely empty theme.json data set still has the 'version' key when parsed.
-		$empty_theme_json = array( 'version' => WP_Theme_JSON::LATEST_SCHEMA );
+		$empty_theme_json = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA );
 
 		// Call using 'with_supports' set to false, so that the method only considers theme.json.
 		$theme_data = $theme_json_resolver->get_theme_data( array(), array( 'with_supports' => false ) );
-		$this->assertInstanceOf( 'WP_Theme_JSON', $theme_data, 'Theme data should be an instance of WP_Theme_JSON.' );
+		$this->assertInstanceOf( 'WP_Theme_JSON_Gutenberg', $theme_data, 'Theme data should be an instance of WP_Theme_JSON_Gutenberg.' );
 		$this->assertSame( $empty_theme_json, $theme_data->get_raw_data(), 'Theme data should be empty without theme support.' );
 		$this->assertNull( $property->getValue(), 'Theme i18n schema should not have been loaded without theme support.' );
 	}
@@ -807,9 +782,7 @@
 	/**
 	 * Tests that get_merged_data returns the data merged up to the proper origin.
 	 *
-	 * @ticket 57545
-	 *
-	 * @covers WP_Theme_JSON_Resolver::get_merged_data
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_merged_data
 	 *
 	 * @dataProvider data_get_merged_data_returns_origin
 	 *
@@ -852,7 +825,7 @@
 
 		// Make sure there is data from the user origin.
 		wp_set_current_user( self::$administrator_id );
-		$user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( wp_get_theme(), true );
+		$user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme(), true );
 		$config   = json_decode( $user_cpt['post_content'], true );
 		$config['settings']['color']['palette']['custom'] = array(
 			array(
@@ -864,7 +837,7 @@
 		$user_cpt['post_content']                         = wp_json_encode( $config );
 		wp_update_post( $user_cpt, true, false );
 
-		$theme_json = WP_Theme_JSON_Resolver::get_merged_data( $origin );
+		$theme_json = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin );
 		$settings   = $theme_json->get_settings();
 		$styles     = $theme_json->get_styles_block_nodes();
 		$styles     = array_filter(
@@ -885,9 +858,7 @@
 	 * Tests that get_merged_data returns the data merged up to the proper origin
 	 * and that the core values have the proper data.
 	 *
-	 * @ticket 57824
-	 *
-	 * @covers WP_Theme_JSON_Resolver::get_merged_data
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_merged_data
 	 */
 	public function test_get_merged_data_returns_origin_proper() {
 		// Make sure the theme has a theme.json
@@ -896,7 +867,7 @@
 
 		// Make sure the user defined some data for styles.spacing.padding.
 		wp_set_current_user( self::$administrator_id );
-		$user_cpt                               = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( wp_get_theme(), true );
+		$user_cpt                               = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme(), true );
 		$config                                 = json_decode( $user_cpt['post_content'], true );
 		$config['styles']['spacing']['padding'] = array(
 			'top'    => '23px',
@@ -908,9 +879,9 @@
 		wp_update_post( $user_cpt, true, false );
 
 		// Query data from the user origin and then for the theme origin.
-		$theme_json_user  = WP_Theme_JSON_Resolver::get_merged_data( 'custom' );
+		$theme_json_user  = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'custom' );
 		$padding_user     = $theme_json_user->get_raw_data()['styles']['spacing']['padding'];
-		$theme_json_theme = WP_Theme_JSON_Resolver::get_merged_data( 'theme' );
+		$theme_json_theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' );
 		$padding_theme    = $theme_json_theme->get_raw_data()['styles']['spacing']['padding'];
 
 		$this->assertSame( '23px', $padding_user['top'] );
@@ -981,19 +952,17 @@
 	 * Tests that get_style_variations returns all variations, including parent theme variations if the theme is a child,
 	 * and that the child variation overwrites the parent variation of the same name.
 	 *
-	 * @ticket 57545
-	 *
-	 * @covers WP_Theme_JSON_Resolver::get_style_variations
+	 * @covers WP_Theme_JSON_Resolver_Gutenberg::get_style_variations
 	 */
 	public function test_get_style_variations_returns_all_variations() {
 		// Switch to a child theme.
 		switch_theme( 'block-theme-child' );
 		wp_set_current_user( self::$administrator_id );
 
-		$actual_settings   = WP_Theme_JSON_Resolver::get_style_variations();
+		$actual_settings   = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations();
 		$expected_settings = array(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'title'    => 'variation-a',
 				'settings' => array(
 					'blocks' => array(
@@ -1014,7 +983,7 @@
 				),
 			),
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'title'    => 'variation-b',
 				'settings' => array(
 					'blocks' => array(
@@ -1035,7 +1004,7 @@
 				),
 			),
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'title'    => 'Block theme variation',
 				'settings' => array(
 					'color' => array(
WP_Theme_JSON_Schema_Gutenberg_Test
--- wordpress-develop/tests/phpunit/tests/theme/wpThemeJsonSchema.php	2024-01-31 19:28:59.000000000 -0600
+++ gutenberg/phpunit/class-wp-theme-json-schema-test.php	2024-01-31 19:41:18.000000000 -0600
@@ -1,19 +1,13 @@
 <?php
 
 /**
- * Test WP_Theme_JSON_Schema class.
+ * Test WP_Theme_JSON_Schema_Gutenberg class.
  *
- * @package WordPress
- * @subpackage Theme
+ * @package Gutenberg
  *
  * @since 5.9.0
- *
- * @group themes
  */
-class Tests_Theme_wpThemeJsonSchema extends WP_UnitTestCase {
-	/**
-	 * @ticket 54336
-	 */
+class WP_Theme_JSON_Schema_Gutenberg_Test extends WP_UnitTestCase {
 	public function test_migrate_v1_to_latest() {
 		$theme_json_v1 = array(
 			'version'  => 1,
@@ -98,10 +92,10 @@
 			),
 		);
 
-		$actual = WP_Theme_JSON_Schema::migrate( $theme_json_v1 );
+		$actual = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json_v1 );
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'palette' => array(
WP_Theme_JSON_Gutenberg_Test
--- wordpress-develop/tests/phpunit/tests/theme/wpThemeJson.php	2024-01-31 19:48:35.000000000 -0600
+++ gutenberg/phpunit/class-wp-theme-json-test.php	2024-01-31 19:48:35.000000000 -0600
@@ -1,19 +1,14 @@
 <?php
 
 /**
- * Test WP_Theme_JSON class.
+ * Test WP_Theme_JSON_Gutenberg class.
  *
- * @package WordPress
- * @subpackage Theme
+ * @package Gutenberg
  *
- * @since 5.8.0
- *
- * @group themes
- *
- * @covers WP_Theme_JSON
+ * @covers WP_Theme_JSON_Gutenberg
  */
-class Tests_Theme_wpThemeJson extends WP_UnitTestCase {
 
+class WP_Theme_JSON_Gutenberg_Test extends WP_UnitTestCase {
 	/**
 	 * Administrator ID.
 	 *
@@ -44,14 +39,10 @@
 		static::$user_id = self::factory()->user->create();
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_settings() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'       => array(
 						'custom' => false,
@@ -103,13 +94,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 53397
-	 */
 	public function test_get_settings_presets_are_keyed_by_origin() {
-		$default_origin = new WP_Theme_JSON(
+		$default_origin = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'       => array(
 						'palette' => array(
@@ -136,9 +124,9 @@
 			),
 			'default'
 		);
-		$no_origin      = new WP_Theme_JSON(
+		$no_origin      = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'       => array(
 						'palette' => array(
@@ -169,7 +157,7 @@
 		$actual_no_origin = $no_origin->get_raw_data();
 
 		$expected_default   = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'  => array(
 					'palette' => array(
@@ -198,7 +186,7 @@
 			),
 		);
 		$expected_no_origin = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'  => array(
 					'palette' => array(
@@ -232,9 +220,9 @@
 	}
 
 	public function test_get_settings_appearance_true_opts_in() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'appearanceTools' => true,
 					'spacing'         => array(
@@ -284,6 +272,7 @@
 			),
 			'position'   => array(
 				'sticky' => true,
+				'fixed'  => true,
 			),
 			'spacing'    => array(
 				'blockGap' => false,
@@ -322,6 +311,7 @@
 					),
 					'position'   => array(
 						'sticky' => true,
+						'fixed'  => true,
 					),
 					'spacing'    => array(
 						'blockGap' => false,
@@ -339,9 +329,9 @@
 	}
 
 	public function test_get_settings_appearance_false_does_not_opt_in() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'appearanceTools' => false,
 					'border'          => array(
@@ -386,18 +376,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 53175
-	 * @ticket 54336
-	 * @ticket 56611
-	 * @ticket 58549
-	 * @ticket 58550
-	 * @ticket 60365
-	 */
 	public function test_get_stylesheet() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'      => array(
 						'text'      => 'value',
@@ -568,14 +550,10 @@
 		$this->assertSame( $all, $theme_json->get_stylesheet() );
 	}
 
-	/**
-	 * @ticket 54336
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_support_for_shorthand_and_longhand_values() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'blocks' => array(
 						'core/group' => array(
@@ -613,14 +591,10 @@
 		$this->assertSame( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 54336
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_skips_disabled_protected_properties() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => null,
@@ -646,15 +620,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 54336
-	 * @ticket 58548
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_renders_enabled_protected_properties() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => true,
@@ -673,14 +642,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_stylesheet_preset_classes_work_with_compounded_selectors() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'blocks' => array(
 						'core/heading' => array(
@@ -704,15 +669,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 53175
-	 * @ticket 54336
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_preset_rules_come_after_block_rules() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'blocks' => array(
 						'core/group' => array(
@@ -751,13 +711,10 @@
 		$this->assertSame( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_get_stylesheet_generates_proper_classes_and_css_vars_from_slugs() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'  => array(
 						'palette' => array(
@@ -796,15 +753,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 53175
-	 * @ticket 54336
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_preset_values_are_marked_as_important() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color' => array(
 						'palette' => array(
@@ -839,14 +791,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_handles_whitelisted_element_pseudo_selectors() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'link' => array(
@@ -886,14 +834,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_handles_only_pseudo_selector_rules_for_given_property() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'link' => array(
@@ -929,14 +873,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_ignores_pseudo_selectors_on_non_whitelisted_elements() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'h4' => array(
@@ -972,14 +912,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_ignores_non_whitelisted_pseudo_selectors() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'link' => array(
@@ -1016,14 +952,10 @@
 		$this->assertStringNotContainsString( 'a:levitate{', $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_handles_priority_of_elements_vs_block_elements_pseudo_selectors() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'blocks' => array(
 						'core/group' => array(
@@ -1067,14 +999,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_handles_whitelisted_block_level_element_pseudo_selectors() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'link' => array(
@@ -1119,14 +1047,132 @@
 	}
 
 	/**
-	 * @ticket 56467
-	 * @ticket 58548
-	 * @ticket 58550
+	 * This test relies on a block having already been registered prior to
+	 * theme.json generating block metadata. Until a core block, such as Image,
+	 * opts into feature level selectors, we need to register a test block.
+	 * This is achieved via `tests_add_filter()` in Gutenberg's phpunit
+	 * bootstrap. After a core block adopts feature level selectors we could
+	 * remove that filter and instead use the core block for the following test.
 	 */
+	public function test_get_stylesheet_with_deprecated_feature_level_selectors() {
+		$theme_json = new WP_Theme_JSON_Gutenberg(
+			array(
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+				'settings' => array(
+					'border'     => array(
+						'radius' => true,
+					),
+					'color'      => array(
+						'custom'  => false,
+						'palette' => array(
+							array(
+								'slug'  => 'green',
+								'color' => 'green',
+							),
+						),
+					),
+					'spacing'    => array(
+						'padding' => true,
+					),
+					'typography' => array(
+						'fontSize' => true,
+					),
+				),
+				'styles'   => array(
+					'blocks' => array(
+						'test/test' => array(
+							'border'     => array(
+								'radius' => '9999px',
+							),
+							'color'      => array(
+								'text' => 'green',
+							),
+							'spacing'    => array(
+								'padding' => '20px',
+							),
+							'typography' => array(
+								'fontSize' => '3em',
+							),
+						),
+					),
+				),
+			)
+		);
+
+		$base_styles   = 'body{--wp--preset--color--green: green;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}';
+		$block_styles  = '.wp-block-test, .wp-block-test__wrapper{color: green;}.wp-block-test .inner, .wp-block-test__wrapper .inner{border-radius: 9999px;padding: 20px;}.wp-block-test .sub-heading, .wp-block-test__wrapper .sub-heading{font-size: 3em;}';
+		$preset_styles = '.has-green-color{color: var(--wp--preset--color--green) !important;}.has-green-background-color{background-color: var(--wp--preset--color--green) !important;}.has-green-border-color{border-color: var(--wp--preset--color--green) !important;}';
+		$expected      = $base_styles . $block_styles . $preset_styles;
+
+		$this->assertEquals( $expected, $theme_json->get_stylesheet() );
+	}
+
+	/**
+	 * This test relies on a block having already been registered prior to
+	 * theme.json generating block metadata. Until a core block adopts the
+	 * new selectors API, we need to register a test block.
+	 * This is achieved via `tests_add_filter()` in Gutenberg's phpunit
+	 * bootstrap. After a core block adopts feature level selectors we could
+	 * remove that filter and instead use the core block for the following test.
+	 */
+	public function test_get_stylesheet_with_block_json_selectors() {
+		$theme_json = new WP_Theme_JSON_Gutenberg(
+			array(
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
+				'settings' => array(
+					'border'     => array(
+						'radius' => true,
+					),
+					'color'      => array(
+						'custom'  => false,
+						'palette' => array(
+							array(
+								'slug'  => 'green',
+								'color' => 'green',
+							),
+						),
+					),
+					'spacing'    => array(
+						'padding' => true,
+					),
+					'typography' => array(
+						'fontSize' => true,
+					),
+				),
+				'styles'   => array(
+					'blocks' => array(
+						'my/block-with-selectors' => array(
+							'border'     => array(
+								'radius' => '9999px',
+							),
+							'color'      => array(
+								'background' => 'grey',
+								'text'       => 'navy',
+							),
+							'spacing'    => array(
+								'padding' => '20px',
+							),
+							'typography' => array(
+								'fontSize' => '3em',
+							),
+						),
+					),
+				),
+			)
+		);
+
+		$base_styles   = 'body{--wp--preset--color--green: green;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}';
+		$block_styles  = '.custom-root-selector{background-color: grey;padding: 20px;}.custom-root-selector img{border-radius: 9999px;}.custom-root-selector > figcaption{color: navy;font-size: 3em;}';
+		$preset_styles = '.has-green-color{color: var(--wp--preset--color--green) !important;}.has-green-background-color{background-color: var(--wp--preset--color--green) !important;}.has-green-border-color{border-color: var(--wp--preset--color--green) !important;}';
+		$expected      = $base_styles . $block_styles . $preset_styles;
+
+		$this->assertEquals( $expected, $theme_json->get_stylesheet() );
+	}
+
 	public function test_get_stylesheet_generates_layout_styles() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => true,
@@ -1148,15 +1194,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58548
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_generates_layout_styles_with_spacing_presets() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => true,
@@ -1178,14 +1219,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_generates_fallback_gap_layout_styles() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => null,
@@ -1208,14 +1245,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_generates_base_fallback_gap_layout_styles() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => null,
@@ -1233,15 +1266,11 @@
 		);
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_skips_layout_styles() {
 		add_theme_support( 'disable-layout-styles' );
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => null,
@@ -1260,14 +1289,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_generates_valid_block_gap_values_and_skips_null_or_false_values() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'spacing' => array(
 						'blockGap' => true,
@@ -1305,19 +1330,16 @@
 		);
 
 		$this->assertSame(
-			'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 1rem; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child:first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child:last-child { margin-block-end: 0; }body { --wp--style--block-gap: 1rem; }:where(body .is-layout-flow)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-flow)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-flow)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-constrained)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-constrained)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-constrained)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-flex) {gap: 1rem;}:where(body .is-layout-grid) {gap: 1rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}.wp-block-post-content{color: gray;}.wp-block-social-links-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-flex{gap: 0;}.wp-block-social-links-is-layout-grid{gap: 0;}.wp-block-buttons-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-flex{gap: 0;}.wp-block-buttons-is-layout-grid{gap: 0;}',
+			'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 1rem; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child:first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child:last-child { margin-block-end: 0; }body { --wp--style--block-gap: 1rem; }:where(body .is-layout-flow)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-flow)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-flow)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-constrained)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-constrained)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-constrained)  > *{margin-block-start: 1rem;margin-block-end: 0;}:where(body .is-layout-flex) {gap: 1rem;}:where(body .is-layout-grid) {gap: 1rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}' .
+			'.wp-block-post-content{color: gray;}.wp-block-social-links-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-social-links-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-social-links-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links-is-layout-flex{gap: 0;}.wp-block-social-links-is-layout-grid{gap: 0;}.wp-block-buttons-is-layout-flow > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-flow > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > :first-child:first-child{margin-block-start: 0;}.wp-block-buttons-is-layout-constrained > :last-child:last-child{margin-block-end: 0;}.wp-block-buttons-is-layout-constrained > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons-is-layout-flex{gap: 0;}.wp-block-buttons-is-layout-grid{gap: 0;}',
 			$theme_json->get_stylesheet()
 		);
 	}
 
-	/**
-	 * @ticket 57354
-	 * @ticket 58550
-	 */
 	public function test_get_stylesheet_returns_outline_styles() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'button' => array(
@@ -1349,15 +1371,10 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet() );
 	}
 
-	/**
-	 * Tests that a custom root selector is correctly applied when generating a stylesheet.
-	 *
-	 * @ticket 60343
-	 */
 	public function test_get_stylesheet_custom_root_selector() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'color' => array(
 						'text' => 'teal',
@@ -1379,9 +1396,9 @@
 	}
 
 	public function test_allow_indirect_properties() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'   => array(
 					'blocks'  => array(
 						'core/social-links' => array(
@@ -1407,7 +1424,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'   => array(
 				'blocks'  => array(
 					'core/social-links' => array(
@@ -1434,14 +1451,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_merge_incoming_data() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'  => array(
 						'custom'  => false,
@@ -1473,7 +1486,7 @@
 		);
 
 		$add_new_block = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'blocks' => array(
 					'core/list' => array(
@@ -1498,7 +1511,7 @@
 		);
 
 		$add_key_in_settings = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color' => array(
 					'customGradient' => true,
@@ -1507,7 +1520,7 @@
 		);
 
 		$update_key_in_settings = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color' => array(
 					'custom' => true,
@@ -1516,7 +1529,7 @@
 		);
 
 		$add_styles = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'blocks' => array(
 					'core/group' => array(
@@ -1531,7 +1544,7 @@
 		);
 
 		$add_key_in_styles = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'blocks' => array(
 					'core/group' => array(
@@ -1546,7 +1559,7 @@
 		);
 
 		$add_invalid_context = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'blocks' => array(
 					'core/para' => array(
@@ -1559,7 +1572,7 @@
 		);
 
 		$update_presets = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'palette'   => array(
@@ -1593,7 +1606,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'custom'         => true,
@@ -1671,26 +1684,22 @@
 			),
 		);
 
-		$theme_json->merge( new WP_Theme_JSON( $add_new_block ) );
-		$theme_json->merge( new WP_Theme_JSON( $add_key_in_settings ) );
-		$theme_json->merge( new WP_Theme_JSON( $update_key_in_settings ) );
-		$theme_json->merge( new WP_Theme_JSON( $add_styles ) );
-		$theme_json->merge( new WP_Theme_JSON( $add_key_in_styles ) );
-		$theme_json->merge( new WP_Theme_JSON( $add_invalid_context ) );
-		$theme_json->merge( new WP_Theme_JSON( $update_presets ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_new_block ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_key_in_settings ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $update_key_in_settings ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_styles ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_key_in_styles ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $add_invalid_context ) );
+		$theme_json->merge( new WP_Theme_JSON_Gutenberg( $update_presets ) );
 		$actual = $theme_json->get_raw_data();
 
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 53175
-	 * @ticket 54336
-	 */
 	public function test_merge_incoming_data_empty_presets() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'      => array(
 						'duotone'   => array(
@@ -1728,9 +1737,9 @@
 		);
 
 		$theme_json->merge(
-			new WP_Theme_JSON(
+			new WP_Theme_JSON_Gutenberg(
 				array(
-					'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+					'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 					'settings' => array(
 						'color'      => array(
 							'duotone'   => array(),
@@ -1750,7 +1759,7 @@
 
 		$actual   = $theme_json->get_raw_data();
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'duotone'   => array(
@@ -1777,14 +1786,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 53175
-	 * @ticket 54336
-	 */
 	public function test_merge_incoming_data_null_presets() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'      => array(
 						'duotone'   => array(
@@ -1822,9 +1827,9 @@
 		);
 
 		$theme_json->merge(
-			new WP_Theme_JSON(
+			new WP_Theme_JSON_Gutenberg(
 				array(
-					'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+					'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 					'settings' => array(
 						'color'      => array(
 							'custom' => false,
@@ -1842,7 +1847,7 @@
 
 		$actual   = $theme_json->get_raw_data();
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'custom'    => false,
@@ -1893,9 +1898,9 @@
 	}
 
 	public function test_merge_incoming_data_color_presets_with_same_slugs_as_default_are_removed() {
-		$defaults = new WP_Theme_JSON(
+		$defaults = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'  => array(
 						'defaultPalette' => true,
@@ -1929,9 +1934,9 @@
 			),
 			'default'
 		);
-		$theme    = new WP_Theme_JSON(
+		$theme    = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'  => array(
 						'palette' => array(
@@ -1975,7 +1980,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'  => array(
 					'palette'        => array(
@@ -2033,9 +2038,9 @@
 	}
 
 	public function test_merge_incoming_data_color_presets_with_same_slugs_as_default_are_not_removed_if_defaults_are_disabled() {
-		$defaults = new WP_Theme_JSON(
+		$defaults = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'  => array(
 						'defaultPalette' => true, // Emulate the defaults from core theme.json.
@@ -2069,9 +2074,9 @@
 			),
 			'default'
 		);
-		$theme    = new WP_Theme_JSON(
+		$theme    = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'  => array(
 						'defaultPalette' => false,
@@ -2116,7 +2121,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'  => array(
 					'defaultPalette' => false,
@@ -2188,13 +2193,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54640
-	 */
 	public function test_merge_incoming_data_presets_use_default_names() {
-		$defaults   = new WP_Theme_JSON(
+		$defaults   = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'typography' => array(
 						'fontSizes' => array(
@@ -2214,9 +2216,9 @@
 			),
 			'default'
 		);
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'typography' => array(
 						'fontSizes' => array(
@@ -2240,7 +2242,7 @@
 			'theme'
 		);
 		$expected   = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'typography' => array(
 					'fontSizes' => array(
@@ -2282,13 +2284,10 @@
 		$this->assertSameSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_remove_insecure_properties_removes_unsafe_styles() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'color'    => array(
 						'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')',
@@ -2337,7 +2336,7 @@
 		);
 
 		$expected = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'color'    => array(
 					'text' => 'var(--wp--preset--color--dark-red)',
@@ -2374,13 +2373,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'border'   => array(
 						'radius' => array(
@@ -2448,7 +2444,7 @@
 		);
 
 		$expected = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'border'   => array(
 					'radius' => array(
@@ -2509,13 +2505,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_remove_insecure_properties_removes_non_preset_settings() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'   => array(
 						'custom'  => true,
@@ -2576,7 +2569,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'  => array(
 					'palette' => array(
@@ -2629,13 +2622,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_remove_insecure_properties_removes_unsafe_preset_settings() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'      => array(
 						'palette' => array(
@@ -2724,7 +2714,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'palette' => array(
@@ -2768,13 +2758,10 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_remove_insecure_properties_applies_safe_styles() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'color' => array(
 						'text' => '#abcabc ', // Trailing space.
@@ -2785,7 +2772,7 @@
 		);
 
 		$expected = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'color' => array(
 					'text' => '#abcabc ',
@@ -2796,14 +2783,12 @@
 	}
 
 	/**
-	 * @ticket 57321
-	 *
-	 * @covers WP_Theme_JSON::remove_insecure_properties
+	 * @covers WP_Theme_JSON_Gutenberg::remove_insecure_properties
 	 */
 	public function test_remove_insecure_properties_should_allow_indirect_properties() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'   => array(
 					'spacing' => array(
 						'blockGap' => '3em',
@@ -2829,7 +2814,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'   => array(
 				'spacing' => array(
 					'blockGap' => '3em',
@@ -2856,13 +2841,11 @@
 		$this->assertSameSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 56467
-	 */
+
 	public function test_remove_invalid_element_pseudo_selectors() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'elements' => array(
 						'link' => array(
@@ -2889,7 +2872,7 @@
 		);
 
 		$expected = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'elements' => array(
 					'link' => array(
@@ -2911,11 +2894,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_get_custom_templates() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'         => 1,
 				'customTemplates' => array(
@@ -2940,11 +2920,8 @@
 		);
 	}
 
-	/**
-	 * @ticket 54336
-	 */
 	public function test_get_template_parts() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'       => 1,
 				'templateParts' => array(
@@ -2970,9 +2947,6 @@
 		);
 	}
 
-	/**
-	 * @ticket 52991
-	 */
 	public function test_get_from_editor_settings() {
 		$input = array(
 			'disableCustomColors'    => true,
@@ -3004,7 +2978,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'custom'         => false,
@@ -3041,15 +3015,11 @@
 			),
 		);
 
-		$actual = WP_Theme_JSON::get_from_editor_settings( $input );
+		$actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( $input );
 
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_editor_settings_no_theme_support() {
 		$input = array(
 			'__unstableEnableFullSiteEditingBlocks' => false,
@@ -3081,7 +3051,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'color'      => array(
 					'custom'         => true,
@@ -3097,32 +3067,24 @@
 			),
 		);
 
-		$actual = WP_Theme_JSON::get_from_editor_settings( $input );
+		$actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( $input );
 
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_editor_settings_blank() {
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(),
 		);
-		$actual   = WP_Theme_JSON::get_from_editor_settings( array() );
+		$actual   = WP_Theme_JSON_Gutenberg::get_from_editor_settings( array() );
 
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_editor_settings_custom_units_can_be_disabled() {
 		add_theme_support( 'custom-units', array() );
-		$actual = WP_Theme_JSON::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
+		$actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
 		remove_theme_support( 'custom-units' );
 
 		$expected = array(
@@ -3133,13 +3095,9 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_editor_settings_custom_units_can_be_enabled() {
 		add_theme_support( 'custom-units' );
-		$actual = WP_Theme_JSON::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
+		$actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
 		remove_theme_support( 'custom-units' );
 
 		$expected = array(
@@ -3150,13 +3108,9 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
 	}
 
-	/**
-	 * @ticket 52991
-	 * @ticket 54336
-	 */
 	public function test_get_editor_settings_custom_units_can_be_filtered() {
 		add_theme_support( 'custom-units', 'rem', 'em' );
-		$actual = WP_Theme_JSON::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
+		$actual = WP_Theme_JSON_Gutenberg::get_from_editor_settings( get_classic_theme_supports_block_editor_settings() );
 		remove_theme_support( 'custom-units' );
 
 		$expected = array(
@@ -3166,11 +3120,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] );
 	}
 
-	/**
-	 * @ticket 55505
-	 */
 	public function test_export_data() {
-		$theme = new WP_Theme_JSON(
+		$theme = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3191,7 +3142,7 @@
 				),
 			)
 		);
-		$user  = new WP_Theme_JSON(
+		$user  = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3244,11 +3195,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 55505
-	 */
 	public function test_export_data_deals_with_empty_user_data() {
-		$theme = new WP_Theme_JSON(
+		$theme = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3294,11 +3242,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 55505
-	 */
 	public function test_export_data_deals_with_empty_theme_data() {
-		$user = new WP_Theme_JSON(
+		$user = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3345,11 +3290,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 55505
-	 */
 	public function test_export_data_deals_with_empty_data() {
-		$theme_v2    = new WP_Theme_JSON(
+		$theme_v2    = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 			),
@@ -3359,7 +3301,7 @@
 		$expected_v2 = array( 'version' => 2 );
 		$this->assertEqualSetsWithIndex( $expected_v2, $actual_v2 );
 
-		$theme_v1    = new WP_Theme_JSON(
+		$theme_v1    = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 1,
 			),
@@ -3370,11 +3312,8 @@
 		$this->assertEqualSetsWithIndex( $expected_v1, $actual_v1 );
 	}
 
-	/**
-	 * @ticket 55505
-	 */
 	public function test_export_data_sets_appearance_tools() {
-		$theme = new WP_Theme_JSON(
+		$theme = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3404,11 +3343,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 56611
-	 */
 	public function test_export_data_sets_use_root_padding_aware_alignments() {
-		$theme = new WP_Theme_JSON(
+		$theme = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3439,9 +3375,9 @@
 	}
 
 	public function test_remove_invalid_font_family_settings() {
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'typography' => array(
 						'fontFamilies' => array(
@@ -3465,7 +3401,7 @@
 		);
 
 		$expected = array(
-			'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+			'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'settings' => array(
 				'typography' => array(
 					'fontFamilies' => array(
@@ -3484,22 +3420,16 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 56467
-	 */
 	public function test_get_element_class_name_button() {
 		$expected = 'wp-element-button';
-		$actual   = WP_Theme_JSON::get_element_class_name( 'button' );
+		$actual   = WP_Theme_JSON_Gutenberg::get_element_class_name( 'button' );
 
 		$this->assertSame( $expected, $actual );
 	}
 
-	/**
-	 * @ticket 56467
-	 */
 	public function test_get_element_class_name_invalid() {
 		$expected = '';
-		$actual   = WP_Theme_JSON::get_element_class_name( 'unknown-element' );
+		$actual   = WP_Theme_JSON_Gutenberg::get_element_class_name( 'unknown-element' );
 
 		$this->assertSame( $expected, $actual );
 	}
@@ -3507,12 +3437,9 @@
 	/**
 	 * Testing that dynamic properties in theme.json return the value they reference,
 	 * e.g. array( 'ref' => 'styles.color.background' ) => "#ffffff".
-	 *
-	 * @ticket 56467
-	 * @ticket 58550
 	 */
 	public function test_get_property_value_valid() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3550,15 +3477,13 @@
 	 *
 	 * @dataProvider data_get_property_value_should_return_string_for_invalid_paths_or_null_values
 	 *
-	 * @ticket 56620
-	 *
-	 * @covers WP_Theme_JSON::get_property_value
+	 * @covers WP_Theme_JSON_Gutenberg::get_property_value
 	 *
 	 * @param array $styles An array with style definitions.
 	 * @param array $path   Path to the desired properties.
 	 */
 	public function test_get_property_value_should_return_string_for_invalid_paths_or_null_values( $styles, $path ) {
-		$reflection_class = new ReflectionClass( WP_Theme_JSON::class );
+		$reflection_class = new ReflectionClass( WP_Theme_JSON_Gutenberg::class );
 
 		$get_property_value_method = $reflection_class->getMethod( 'get_property_value' );
 		$get_property_value_method->setAccessible( true );
@@ -3590,12 +3515,10 @@
 	 * refer to other dynamic properties in a loop
 	 * should be left untouched.
 	 *
-	 * @ticket 56467
-	 * @ticket 58550
 	 * @expectedIncorrectUsage get_property_value
 	 */
 	public function test_get_property_value_loop() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3626,12 +3549,10 @@
 	 * refer to other dynamic properties
 	 * should be left unprocessed.
 	 *
-	 * @ticket 56467
-	 * @ticket 58550
 	 * @expectedIncorrectUsage get_property_value
 	 */
 	public function test_get_property_value_recursion() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3661,12 +3582,10 @@
 	 * Testing that dynamic properties in theme.json that
 	 * refer to themselves should be left unprocessed.
 	 *
-	 * @ticket 56467
-	 * @ticket 58550
 	 * @expectedIncorrectUsage get_property_value
 	 */
 	public function test_get_property_value_self() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3684,12 +3603,8 @@
 		$this->assertSame( $expected, $theme_json->get_stylesheet() );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_styles_for_block_with_padding_aware_alignments() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'styles'   => array(
@@ -3713,18 +3628,14 @@
 			'selector' => 'body',
 		);
 
-		$expected    = 'body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding):not(.is-layout-flex):not(.is-layout-grid)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}';
-		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
+		$expected    = 'body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding:not(.wp-block-block)) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding):not(.is-layout-flex):not(.is-layout-grid)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),.wp-block:not(.alignfull),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),.wp-block:not(.alignfull),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}';
+		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON_Gutenberg::ROOT_BLOCK_SELECTOR, $metadata );
 		$style_rules = $theme_json->get_styles_for_block( $metadata );
 		$this->assertSame( $expected, $root_rules . $style_rules );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_styles_for_block_without_padding_aware_alignments() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3746,17 +3657,13 @@
 		);
 
 		$expected    = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}';
-		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
+		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON_Gutenberg::ROOT_BLOCK_SELECTOR, $metadata );
 		$style_rules = $theme_json->get_styles_for_block( $metadata );
 		$this->assertSame( $expected, $root_rules . $style_rules );
 	}
 
-	/**
-	 * @ticket 56467
-	 * @ticket 58550
-	 */
 	public function test_get_styles_for_block_with_content_width() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3774,18 +3681,13 @@
 		);
 
 		$expected    = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}';
-		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
+		$root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON_Gutenberg::ROOT_BLOCK_SELECTOR, $metadata );
 		$style_rules = $theme_json->get_styles_for_block( $metadata );
 		$this->assertSame( $expected, $root_rules . $style_rules );
 	}
 
-	/**
-	 * @ticket 56611
-	 * @ticket 58548
-	 * @ticket 58550
-	 */
 	public function test_get_styles_with_appearance_tools() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -3800,15 +3702,12 @@
 		);
 
 		$expected   = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: ; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child:first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child:last-child { margin-block-end: 0; }body { --wp--style--block-gap: ; }:where(body .is-layout-flow)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-flow)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-flow)  > *{margin-block-start: 1;margin-block-end: 0;}:where(body .is-layout-constrained)  > :first-child:first-child{margin-block-start: 0;}:where(body .is-layout-constrained)  > :last-child:last-child{margin-block-end: 0;}:where(body .is-layout-constrained)  > *{margin-block-start: 1;margin-block-end: 0;}:where(body .is-layout-flex) {gap: 1;}:where(body .is-layout-grid) {gap: 1;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}';
-		$root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
+		$root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON_Gutenberg::ROOT_BLOCK_SELECTOR, $metadata );
 		$this->assertSame( $expected, $root_rules );
 	}
 
-	/**
-	 * @ticket 54487
-	 */
 	public function test_sanitization() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3847,11 +3746,8 @@
 		$this->assertEqualSetsWithIndex( $expected, $actual );
 	}
 
-	/*
-	 * @ticket 58462
-	 */
 	public function test_sanitize_for_unregistered_style_variations() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3896,15 +3792,13 @@
 	}
 
 	/**
-	 * @ticket 57583
-	 *
 	 * @dataProvider data_sanitize_for_block_with_style_variations
 	 *
 	 * @param array $theme_json_variations Theme.json variations to test.
 	 * @param array $expected_sanitized    Expected results after sanitizing.
 	 */
 	public function test_sanitize_for_block_with_style_variations( $theme_json_variations, $expected_sanitized ) {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -3984,11 +3878,9 @@
 
 	/**
 	 * Tests that invalid properties are removed from the theme.json inside indexed arrays as settings.typography.fontFamilies.
-	 *
-	 * @ticket 60360
 	 */
 	public function test_sanitize_indexed_arrays() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => '2',
 				'badKey2'  => 'I am Evil!',
@@ -4118,14 +4010,12 @@
 	}
 
 	/**
-	 * @ticket 57583
-	 *
 	 * @dataProvider data_sanitize_with_invalid_style_variation
 	 *
 	 * @param array $theme_json_variations The theme.json variations to test.
 	 */
 	public function test_sanitize_with_invalid_style_variation( $theme_json_variations ) {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -4163,8 +4053,6 @@
 	}
 
 	/**
-	 * @ticket 57583
-	 *
 	 * @dataProvider data_get_styles_for_block_with_style_variations
 	 *
 	 * @param array  $theme_json_variations Theme.json variations to test.
@@ -4172,7 +4060,7 @@
 	 * @param string $expected              Expected results for styling.
 	 */
 	public function test_get_styles_for_block_with_style_variations( $theme_json_variations, $metadata_variations, $expected ) {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version' => 2,
 				'styles'  => array(
@@ -4243,7 +4131,7 @@
 		wp_set_current_user( static::$administrator_id );
 
 		$expected = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'blocks' => array(
 					'core/button' => array(
@@ -4262,7 +4150,7 @@
 			),
 		);
 
-		$actual = WP_Theme_JSON::remove_insecure_properties( $expected );
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties( $expected );
 
 		$this->assertSameSetsWithIndex( $expected, $actual );
 	}
@@ -4271,7 +4159,7 @@
 		wp_set_current_user( static::$administrator_id );
 
 		$partially_invalid_variation = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'blocks' => array(
 					'core/button' => array(
@@ -4294,7 +4182,7 @@
 		);
 
 		$expected = array(
-			'version' => WP_Theme_JSON::LATEST_SCHEMA,
+			'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 			'styles'  => array(
 				'blocks' => array(
 					'core/button' => array(
@@ -4313,7 +4201,7 @@
 			),
 		);
 
-		$actual = WP_Theme_JSON::remove_insecure_properties( $partially_invalid_variation );
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties( $partially_invalid_variation );
 
 		$this->assertSameSetsWithIndex( $expected, $actual );
 	}
@@ -4321,12 +4209,10 @@
 	/**
 	 * Tests generating the spacing presets array based on the spacing scale provided.
 	 *
-	 * @ticket 56467
-	 *
 	 * @dataProvider data_set_spacing_sizes
 	 */
 	public function test_set_spacing_sizes( $spacing_scale, $expected_output ) {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -4344,8 +4230,6 @@
 	/**
 	 * Data provider for spacing scale tests.
 	 *
-	 * @ticket 56467
-	 *
 	 * @return array
 	 */
 	public function data_set_spacing_sizes() {
@@ -4609,8 +4493,6 @@
 	/**
 	 * Tests generating the spacing presets array based on the spacing scale provided.
 	 *
-	 * @ticket 56467
-	 *
 	 * @dataProvider data_set_spacing_sizes_when_invalid
 	 *
 	 * @param array $spacing_scale   Example spacing scale definitions from the data provider.
@@ -4620,7 +4502,7 @@
 		$this->expectException( Exception::class );
 		$this->expectExceptionMessage( 'Some of the theme.json settings.spacing.spacingScale values are invalid' );
 
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
 				'version'  => 2,
 				'settings' => array(
@@ -4641,17 +4523,12 @@
 		);
 
 		$theme_json->set_spacing_sizes();
-
-		restore_error_handler();
-
 		$this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) );
 	}
 
 	/**
 	 * Data provider for spacing scale tests.
 	 *
-	 * @ticket 56467
-	 *
 	 * @return array
 	 */
 	public function data_set_spacing_sizes_when_invalid() {
@@ -4711,9 +4588,6 @@
 	/**
 	 * Tests the core separator block outbut based on various provided settings.
 	 *
-	 * @ticket 56903
-	 * @ticket 58550
-	 *
 	 * @dataProvider data_update_separator_declarations
 	 *
 	 * @param array $separator_block_settings Example separator block settings from the data provider.
@@ -4721,9 +4595,9 @@
 	 */
 	public function test_update_separator_declarations( $separator_block_settings, $expected_output ) {
 		// If only background is defined, test that includes border-color to the style so it is applied on the front end.
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'blocks' => array(
 						'core/separator' => $separator_block_settings,
@@ -4801,13 +4675,10 @@
 		);
 	}
 
-	/**
-	 * @ticket 57559
-	 */
 	public function test_shadow_preset_styles() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'shadow' => array(
 						'presets' => array(
@@ -4830,14 +4701,10 @@
 		$this->assertSame( $expected_styles, $theme_json->get_stylesheet( array( 'variables' ) ), 'Styles returned from "::get_stylesheet()" when requiring "variables" type does not match expectations' );
 	}
 
-	/**
-	 * @ticket 57559
-	 * @ticket 58550
-	 */
 	public function test_get_shadow_styles_for_blocks() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'shadow' => array(
 						'presets' => array(
@@ -4872,13 +4739,10 @@
 		$this->assertSame( $expected_styles, $theme_json->get_stylesheet() );
 	}
 
-	/**
-	 * @ticket 57536
-	 */
 	public function test_get_custom_css_handles_global_custom_css() {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'css'    => 'body {color:purple;}',
 					'blocks' => array(
@@ -4897,8 +4761,6 @@
 	/**
 	 * Tests that custom CSS is kept for users with correct capabilities and removed for others.
 	 *
-	 * @ticket 57536
-	 *
 	 * @dataProvider data_custom_css_for_user_caps
 	 *
 	 * @param string $user_property The property name for current user.
@@ -4907,9 +4769,9 @@
 	public function test_custom_css_for_user_caps( $user_property, array $expected ) {
 		wp_set_current_user( static::${$user_property} );
 
-		$actual = WP_Theme_JSON::remove_insecure_properties(
+		$actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'css'    => 'body { color:purple; }',
 					'blocks' => array(
@@ -4936,7 +4798,7 @@
 			'allows custom css for users with caps'     => array(
 				'user_property' => 'administrator_id',
 				'expected'      => array(
-					'version' => WP_Theme_JSON::LATEST_SCHEMA,
+					'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 					'styles'  => array(
 						'css'    => 'body { color:purple; }',
 						'blocks' => array(
@@ -4952,7 +4814,7 @@
 			'removes custom css for users without caps' => array(
 				'user_property' => 'user_id',
 				'expected'      => array(
-					'version' => WP_Theme_JSON::LATEST_SCHEMA,
+					'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 					'styles'  => array(
 						'blocks' => array(
 							'core/separator' => array(
@@ -4974,9 +4836,9 @@
 	 * @param string $expected Expected results.
 	 */
 	public function test_process_blocks_custom_css( $input, $expected ) {
-		$theme_json = new WP_Theme_JSON(
+		$theme_json = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(),
 			)
 		);
@@ -5029,9 +4891,9 @@
 	}
 
 	public function test_internal_syntax_is_converted_to_css_variables() {
-		$result = new WP_Theme_JSON(
+		$result = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version' => WP_Theme_JSON::LATEST_SCHEMA,
+				'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'styles'  => array(
 					'color'    => array(
 						'background' => 'var:preset|color|primary',
@@ -5099,9 +4961,9 @@
 		$raw_color_value = '#efefef';
 		$large_font      = '18px';
 		$small_font      = '12px';
-		$theme_json      = new WP_Theme_JSON(
+		$theme_json      = new WP_Theme_JSON_Gutenberg(
 			array(
-				'version'  => WP_Theme_JSON::LATEST_SCHEMA,
+				'version'  => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
 				'settings' => array(
 					'color'      => array(
 						'palette' => array(
@@ -5244,4 +5106,90 @@
 		$this->assertEquals( $small_font, $styles['blocks']['core/quote']['variations']['plain']['typography']['fontSize'], 'Block variations: font-size' );
 		$this->assertEquals( $secondary_color, $styles['blocks']['core/quote']['variations']['plain']['color']['background'], 'Block variations: color' );
 	}
+
+	/**
+	 * Tests the correct application of a block style variation's selector to
+	 * a block's selector.
+	 *
+	 * @dataProvider data_get_block_style_variation_selector
+	 *
+	 * @param string $selector  CSS selector.
+	 * @param string $expected  Expected block style variation CSS selector.
+	 */
+	public function test_get_block_style_variation_selector( $selector, $expected ) {
+		$theme_json = new ReflectionClass( 'WP_Theme_JSON_Gutenberg' );
+
+		$func = $theme_json->getMethod( 'get_block_style_variation_selector' );
+		$func->setAccessible( true );
+
+		$actual = $func->invoke( null, 'custom', $selector );
+
+		$this->assertEquals( $expected, $actual );
+	}
+
+	/**
+	 * Data provider for generating block style variation selectors.
+	 *
+	 * @return array[]
+	 */
+	public function data_get_block_style_variation_selector() {
+		return array(
+			'empty block selector'     => array(
+				'selector' => '',
+				'expected' => '.is-style-custom',
+			),
+			'class selector'           => array(
+				'selector' => '.wp-block',
+				'expected' => '.wp-block.is-style-custom',
+			),
+			'id selector'              => array(
+				'selector' => '#wp-block',
+				'expected' => '#wp-block.is-style-custom',
+			),
+			'element tag selector'     => array(
+				'selector' => 'p',
+				'expected' => 'p.is-style-custom',
+			),
+			'attribute selector'       => array(
+				'selector' => '[style*="color"]',
+				'expected' => '[style*="color"].is-style-custom',
+			),
+			'descendant selector'      => array(
+				'selector' => '.wp-block .inner',
+				'expected' => '.wp-block.is-style-custom .inner',
+			),
+			'comma separated selector' => array(
+				'selector' => '.wp-block .inner, .wp-block .alternative',
+				'expected' => '.wp-block.is-style-custom .inner, .wp-block.is-style-custom .alternative',
+			),
+			'pseudo selector'          => array(
+				'selector' => 'div:first-child',
+				'expected' => 'div.is-style-custom:first-child',
+			),
+			':is selector'             => array(
+				'selector' => '.wp-block:is(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:is(.outer .inner:first-child)',
+			),
+			':not selector'            => array(
+				'selector' => '.wp-block:not(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:not(.outer .inner:first-child)',
+			),
+			':has selector'            => array(
+				'selector' => '.wp-block:has(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:has(.outer .inner:first-child)',
+			),
+			':where selector'          => array(
+				'selector' => '.wp-block:where(.outer .inner:first-child)',
+				'expected' => '.wp-block.is-style-custom:where(.outer .inner:first-child)',
+			),
+			'wrapping :where selector' => array(
+				'selector' => ':where(.outer .inner:first-child)',
+				'expected' => ':where(.outer.is-style-custom .inner:first-child)',
+			),
+			'complex'                  => array(
+				'selector' => '.wp:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
+				'expected' => '.wp.is-style-custom:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp.is-style-custom:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
+			),
+		);
+	}
 }

Testing Instructions

Run the PHPUnit tests for the following filters:

  1. WP_REST_Global_Styles_Controller_Gutenberg_Test
  2. WP_Theme_JSON_Resolver_Gutenberg_Test
  3. WP_Theme_JSON_Schema_Gutenberg_Test
  4. WP_Theme_JSON_Gutenberg_Test

Testing Instructions for Keyboard

N/A

Screenshots or screencast

N/A

Copy link

github-actions bot commented Jan 30, 2024

This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress.

If so, it is recommended to create a new Trac ticket and submit a pull request to the WordPress Core Github repository soon after this pull request is merged.

If you're unsure, you can always ask for help in the #core-editor channel in WordPress Slack.

Thank you! ❤️

View changed files
❔ lib/class-wp-theme-json-schema-gutenberg.php
❔ phpunit/class-wp-theme-json-schema-test.php
❔ phpunit/data/themedir1/block-theme-child/styles/variation-b.json
❔ phpunit/data/themedir1/block-theme/styles/variation.json
❔ lib/class-wp-theme-json-gutenberg.php
❔ lib/class-wp-theme-json-resolver-gutenberg.php
❔ lib/load.php
❔ phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php
❔ phpunit/class-wp-theme-json-resolver-test.php
❔ phpunit/class-wp-theme-json-test.php
❔ phpunit/data/languages/themes/block-theme-pl_PL.mo
❔ phpunit/data/languages/themes/block-theme-pl_PL.po
❔ phpunit/data/themedir1/block-theme/theme.json

@ajlende ajlende changed the title Merge global styles tests from Core Backport theme.json tests from Core Jan 30, 2024
@ajlende ajlende added the Backport from WordPress Core Pull request that needs to be backported to a Gutenberg release from WordPress Core label Jan 30, 2024
@ajlende ajlende self-assigned this Jan 30, 2024
Copy link

github-actions bot commented Jan 30, 2024

Flaky tests detected in a1c948a.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7717738234
📝 Reported issues:

@ajlende ajlende force-pushed the backport/global-styles-tests branch 6 times, most recently from e6169d5 to 6a181f4 Compare January 31, 2024 18:29
@ajlende ajlende force-pushed the backport/global-styles-tests branch from cccaf59 to 5ee08b4 Compare February 1, 2024 01:48
@ajlende ajlende marked this pull request as ready for review February 1, 2024 02:51
@ajlende ajlende requested a review from spacedmonkey as a code owner February 1, 2024 02:51
@@ -2638,7 +2638,7 @@ public function get_root_layout_rules( $selector, $block_metadata ) {
$css .= '--wp--style--global--wide-size: ' . $wide_size . ';';
}

$css .= '}';
Copy link
Contributor Author

@ajlende ajlende Feb 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were a bunch of places in the tests where this was different from core, so I figured I'd just add the space to make them the same. This is the only non-test change.

Copy link
Contributor

@jeryj jeryj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests are all passing for me, and nothing stands out as odd when I look through all the diffs. Admittedly, there are a lot so it's hard to comb through! Thanks for being so tedious about this!

Copy link

github-actions bot commented Feb 1, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @jeryj.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

Core SVN

If you're a Core Committer, use this list when committing to wordpress-develop in SVN:

Props: ajlende, scruffian.

GitHub Merge commits

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: jeryj.

Co-authored-by: ajlende <ajlende@git.wordpress.org>
Co-authored-by: scruffian <scruffian@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@@ -4,6 +4,8 @@
* Test WP_Theme_JSON_Resolver_Gutenberg class.
*
* @package Gutenberg
*
* @since 5.8.0
*/

class WP_Theme_JSON_Resolver_Gutenberg_Test extends WP_UnitTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In core this is called Tests_Theme_wpThemeJsonResolver. Should we rename this to Tests_Theme_wpThemeJsonResolver_Gutenberg?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should match the file path too -> tests/phpunit/tests/theme/wpThemeJsonResolver.php

@@ -695,7 +695,7 @@ public function __construct( $theme_json = array(), $origin = 'theme' ) {
$origin = 'theme';
}

$this->theme_json = WP_Theme_JSON_Schema::migrate( $theme_json );
$this->theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if this renaming was part of the build process....

*
* @since 5.9.0
*/
class WP_Theme_JSON_Schema_Gutenberg_Test extends WP_UnitTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comments as above for this file

Copy link
Contributor

@scruffian scruffian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good. I left a few comments but nothing that should stop us from merging this.

@ajlende ajlende merged commit c4eb224 into trunk Feb 2, 2024
63 checks passed
@ajlende ajlende deleted the backport/global-styles-tests branch February 2, 2024 16:32
@github-actions github-actions bot added this to the Gutenberg 17.7 milestone Feb 2, 2024
@ajlende
Copy link
Contributor Author

ajlende commented Feb 2, 2024

In core this is called Tests_Theme_wpThemeJsonResolver. Should we rename this to Tests_Theme_wpThemeJsonResolver_Gutenberg?

I wonder if we should match the file path too -> tests/phpunit/tests/theme/wpThemeJsonResolver.php

Personally, I like the Gutenberg naming convention for the class/file name. It matches the class/file it's testing so the connection makes more sense to me. If Core has a good reason for the conventions it uses, then go with that. But I couldn't source the decisions for why it was renamed when merged with Core.

It would be great if this renaming was part of the build process....

Absolutely. There's a few things that I think need to happen first to make it automatable.

  1. Make the testing environment the same. There's some places here where we're using different themes in Core vs GB because the environment isn't the same.
  2. Define a naming convention that can be enforced with linting rules in Gutenberg. We're using a few different conventions around the code and getting people to switch to one won't happen unless there's a linting rule for it.
  3. Do full sweeps like this one elsewhere to get Gutenberg and Core back in sync. There are lots of changes in Core that never made it into the plugin.

I would love to see an automatic PR opened in Gutenberg when Core changes are made and vice versa, or at the very least a patch generated that can easily be turned into a PR.

@MaggieCabrera MaggieCabrera added the Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json label Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backport from WordPress Core Pull request that needs to be backported to a Gutenberg release from WordPress Core Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json
Projects
Development

Successfully merging this pull request may close these issues.

4 participants