theme.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. <?php
  2. /**
  3. * test wp-includes/theme.php
  4. *
  5. * @group themes
  6. */
  7. class Tests_Theme extends WP_UnitTestCase {
  8. protected $theme_slug = 'twentyeleven';
  9. protected $theme_name = 'Twenty Eleven';
  10. protected $default_themes = array(
  11. 'twentyten',
  12. 'twentyeleven',
  13. 'twentytwelve',
  14. 'twentythirteen',
  15. 'twentyfourteen',
  16. 'twentyfifteen',
  17. 'twentysixteen',
  18. 'twentyseventeen',
  19. 'twentynineteen',
  20. 'twentytwenty',
  21. 'twentytwentyone',
  22. );
  23. function setUp() {
  24. global $wp_theme_directories;
  25. parent::setUp();
  26. $backup_wp_theme_directories = $wp_theme_directories;
  27. $wp_theme_directories = array( WP_CONTENT_DIR . '/themes' );
  28. add_filter( 'extra_theme_headers', array( $this, '_theme_data_extra_headers' ) );
  29. wp_clean_themes_cache();
  30. unset( $GLOBALS['wp_themes'] );
  31. }
  32. function tearDown() {
  33. global $wp_theme_directories;
  34. $wp_theme_directories = $this->wp_theme_directories;
  35. remove_filter( 'extra_theme_headers', array( $this, '_theme_data_extra_headers' ) );
  36. wp_clean_themes_cache();
  37. unset( $GLOBALS['wp_themes'] );
  38. parent::tearDown();
  39. }
  40. function test_wp_get_themes_default() {
  41. $themes = wp_get_themes();
  42. $this->assertInstanceOf( 'WP_Theme', $themes[ $this->theme_slug ] );
  43. $this->assertSame( $this->theme_name, $themes[ $this->theme_slug ]->get( 'Name' ) );
  44. $single_theme = wp_get_theme( $this->theme_slug );
  45. $this->assertSame( $single_theme->get( 'Name' ), $themes[ $this->theme_slug ]->get( 'Name' ) );
  46. $this->assertEquals( $themes[ $this->theme_slug ], $single_theme );
  47. }
  48. /**
  49. * @expectedDeprecated get_theme
  50. * @expectedDeprecated get_themes
  51. */
  52. function test_get_themes_default() {
  53. $themes = get_themes();
  54. $this->assertInstanceOf( 'WP_Theme', $themes[ $this->theme_name ] );
  55. $this->assertSame( $themes[ $this->theme_name ], get_theme( $this->theme_name ) );
  56. $this->assertSame( $this->theme_name, $themes[ $this->theme_name ]['Name'] );
  57. $this->assertSame( $this->theme_name, $themes[ $this->theme_name ]->Name );
  58. $this->assertSame( $this->theme_name, $themes[ $this->theme_name ]->name );
  59. }
  60. /**
  61. * @expectedDeprecated get_theme
  62. * @expectedDeprecated get_themes
  63. */
  64. function test_get_theme() {
  65. $themes = get_themes();
  66. foreach ( array_keys( $themes ) as $name ) {
  67. $theme = get_theme( $name );
  68. // WP_Theme implements ArrayAccess. Even ArrayObject returns false for is_array().
  69. $this->assertFalse( is_array( $theme ) );
  70. $this->assertInstanceOf( 'WP_Theme', $theme );
  71. $this->assertSame( $theme, $themes[ $name ] );
  72. }
  73. }
  74. function test_wp_get_theme() {
  75. $themes = wp_get_themes();
  76. foreach ( $themes as $theme ) {
  77. $this->assertInstanceOf( 'WP_Theme', $theme );
  78. $this->assertFalse( $theme->errors() );
  79. $_theme = wp_get_theme( $theme->get_stylesheet() );
  80. // This primes internal WP_Theme caches for the next assertion (headers_sanitized, textdomain_loaded).
  81. $this->assertSame( $theme->get( 'Name' ), $_theme->get( 'Name' ) );
  82. $this->assertEquals( $theme, $_theme );
  83. }
  84. }
  85. /**
  86. * @expectedDeprecated get_themes
  87. */
  88. function test_get_themes_contents() {
  89. $themes = get_themes();
  90. // Generic tests that should hold true for any theme.
  91. foreach ( $themes as $k => $theme ) {
  92. // Don't run these checks for custom themes.
  93. if ( empty( $theme['Author'] ) || false === strpos( $theme['Author'], 'WordPress' ) ) {
  94. continue;
  95. }
  96. $this->assertSame( $theme['Name'], $k );
  97. $this->assertNotEmpty( $theme['Title'] );
  98. // Important attributes should all be set.
  99. $default_headers = array(
  100. 'Title' => 'Theme Title',
  101. 'Version' => 'Version',
  102. 'Parent Theme' => 'Parent Theme',
  103. 'Template Dir' => 'Template Dir',
  104. 'Stylesheet Dir' => 'Stylesheet Dir',
  105. 'Template' => 'Template',
  106. 'Stylesheet' => 'Stylesheet',
  107. 'Screenshot' => 'Screenshot',
  108. 'Description' => 'Description',
  109. 'Author' => 'Author',
  110. 'Tags' => 'Tags',
  111. // Introduced in WordPress 2.9.
  112. 'Theme Root' => 'Theme Root',
  113. 'Theme Root URI' => 'Theme Root URI',
  114. );
  115. foreach ( $default_headers as $name => $value ) {
  116. $this->assertTrue( isset( $theme[ $name ] ) );
  117. }
  118. // Make the tests work both for WordPress 2.8.5 and WordPress 2.9-rare.
  119. $dir = isset( $theme['Theme Root'] ) ? '' : WP_CONTENT_DIR;
  120. // Important attributes should all not be empty as well.
  121. $this->assertNotEmpty( $theme['Description'] );
  122. $this->assertNotEmpty( $theme['Author'] );
  123. $this->assertTrue( version_compare( $theme['Version'], 0 ) > 0 );
  124. $this->assertNotEmpty( $theme['Template'] );
  125. $this->assertNotEmpty( $theme['Stylesheet'] );
  126. // Template files should all exist.
  127. $this->assertTrue( is_array( $theme['Template Files'] ) );
  128. $this->assertTrue( count( $theme['Template Files'] ) > 0 );
  129. foreach ( $theme['Template Files'] as $file ) {
  130. $this->assertTrue( is_file( $dir . $file ) );
  131. $this->assertTrue( is_readable( $dir . $file ) );
  132. }
  133. // CSS files should all exist.
  134. $this->assertTrue( is_array( $theme['Stylesheet Files'] ) );
  135. $this->assertTrue( count( $theme['Stylesheet Files'] ) > 0 );
  136. foreach ( $theme['Stylesheet Files'] as $file ) {
  137. $this->assertTrue( is_file( $dir . $file ) );
  138. $this->assertTrue( is_readable( $dir . $file ) );
  139. }
  140. $this->assertTrue( is_dir( $dir . $theme['Template Dir'] ) );
  141. $this->assertTrue( is_dir( $dir . $theme['Stylesheet Dir'] ) );
  142. $this->assertSame( 'publish', $theme['Status'] );
  143. $this->assertTrue( is_file( $dir . $theme['Stylesheet Dir'] . '/' . $theme['Screenshot'] ) );
  144. $this->assertTrue( is_readable( $dir . $theme['Stylesheet Dir'] . '/' . $theme['Screenshot'] ) );
  145. }
  146. }
  147. function test_wp_get_theme_contents() {
  148. $theme = wp_get_theme( $this->theme_slug );
  149. $this->assertSame( $this->theme_name, $theme->get( 'Name' ) );
  150. $this->assertNotEmpty( $theme->get( 'Description' ) );
  151. $this->assertNotEmpty( $theme->get( 'Author' ) );
  152. $this->assertNotEmpty( $theme->get( 'Version' ) );
  153. $this->assertNotEmpty( $theme->get( 'AuthorURI' ) );
  154. $this->assertNotEmpty( $theme->get( 'ThemeURI' ) );
  155. $this->assertSame( $this->theme_slug, $theme->get_stylesheet() );
  156. $this->assertSame( $this->theme_slug, $theme->get_template() );
  157. $this->assertSame( 'publish', $theme->get( 'Status' ) );
  158. $this->assertSame( WP_CONTENT_DIR . '/themes/' . $this->theme_slug, $theme->get_stylesheet_directory(), 'get_stylesheet_directory' );
  159. $this->assertSame( WP_CONTENT_DIR . '/themes/' . $this->theme_slug, $theme->get_template_directory(), 'get_template_directory' );
  160. $this->assertSame( content_url( 'themes/' . $this->theme_slug ), $theme->get_stylesheet_directory_uri(), 'get_stylesheet_directory_uri' );
  161. $this->assertSame( content_url( 'themes/' . $this->theme_slug ), $theme->get_template_directory_uri(), 'get_template_directory_uri' );
  162. }
  163. /**
  164. * Make sure we update the default theme list to include the latest default theme.
  165. *
  166. * @ticket 29925
  167. */
  168. function test_default_theme_in_default_theme_list() {
  169. $latest_default_theme = WP_Theme::get_core_default_theme();
  170. if ( ! $latest_default_theme->exists() || 'twenty' !== substr( $latest_default_theme->get_stylesheet(), 0, 6 ) ) {
  171. $this->fail( 'No Twenty* series default themes are installed.' );
  172. }
  173. $this->assertContains( $latest_default_theme->get_stylesheet(), $this->default_themes );
  174. }
  175. function test_default_themes_have_textdomain() {
  176. foreach ( $this->default_themes as $theme ) {
  177. if ( wp_get_theme( $theme )->exists() ) {
  178. $this->assertSame( $theme, wp_get_theme( $theme )->get( 'TextDomain' ) );
  179. }
  180. }
  181. }
  182. /**
  183. * @ticket 48566
  184. */
  185. function test_year_in_readme() {
  186. // This test is designed to only run on trunk/master.
  187. $this->skipOnAutomatedBranches();
  188. foreach ( $this->default_themes as $theme ) {
  189. $wp_theme = wp_get_theme( $theme );
  190. $path_to_readme_txt = $wp_theme->get_theme_root() . '/' . $wp_theme->get_stylesheet() . '/readme.txt';
  191. $this->assertFileExists( $path_to_readme_txt );
  192. $readme = file_get_contents( $path_to_readme_txt );
  193. $this_year = gmdate( 'Y' );
  194. preg_match( '#Copyright (\d+) WordPress.org#', $readme, $matches );
  195. if ( $matches ) {
  196. $this->assertSame( $this_year, trim( $matches[1] ), "Bundled themes readme.txt's year needs to be updated to $this_year." );
  197. }
  198. preg_match( '#Copyright 20\d\d-(\d+) WordPress.org#', $readme, $matches );
  199. if ( $matches ) {
  200. $this->assertSame( $this_year, trim( $matches[1] ), "Bundled themes readme.txt's year needs to be updated to $this_year." );
  201. }
  202. }
  203. }
  204. /**
  205. * @ticket 20897
  206. * @expectedDeprecated get_theme_data
  207. */
  208. function test_extra_theme_headers() {
  209. $wp_theme = wp_get_theme( $this->theme_slug );
  210. $this->assertNotEmpty( $wp_theme->get( 'License' ) );
  211. $path_to_style_css = $wp_theme->get_theme_root() . '/' . $wp_theme->get_stylesheet() . '/style.css';
  212. $this->assertFileExists( $path_to_style_css );
  213. $theme_data = get_theme_data( $path_to_style_css );
  214. $this->assertArrayHasKey( 'License', $theme_data );
  215. $this->assertArrayNotHasKey( 'Not a Valid Key', $theme_data );
  216. $this->assertNotEmpty( $theme_data['License'] );
  217. $this->assertSame( $theme_data['License'], $wp_theme->get( 'License' ) );
  218. }
  219. function _theme_data_extra_headers() {
  220. return array( 'License' );
  221. }
  222. /**
  223. * @expectedDeprecated get_themes
  224. * @expectedDeprecated get_current_theme
  225. */
  226. function test_switch_theme() {
  227. $themes = get_themes();
  228. // Switch to each theme in sequence.
  229. // Do it twice to make sure we switch to the first theme, even if it's our starting theme.
  230. // Do it a third time to ensure switch_theme() works with one argument.
  231. for ( $i = 0; $i < 3; $i++ ) {
  232. foreach ( $themes as $name => $theme ) {
  233. // Switch to this theme.
  234. if ( 2 === $i ) {
  235. switch_theme( $theme['Template'], $theme['Stylesheet'] );
  236. } else {
  237. switch_theme( $theme['Stylesheet'] );
  238. }
  239. $this->assertSame( $name, get_current_theme() );
  240. // Make sure the various get_* functions return the correct values.
  241. $this->assertSame( $theme['Template'], get_template() );
  242. $this->assertSame( $theme['Stylesheet'], get_stylesheet() );
  243. $root_fs = get_theme_root();
  244. $this->assertTrue( is_dir( $root_fs ) );
  245. $root_uri = get_theme_root_uri();
  246. $this->assertTrue( ! empty( $root_uri ) );
  247. $this->assertSame( $root_fs . '/' . get_stylesheet(), get_stylesheet_directory() );
  248. $this->assertSame( $root_uri . '/' . get_stylesheet(), get_stylesheet_directory_uri() );
  249. $this->assertSame( $root_uri . '/' . get_stylesheet() . '/style.css', get_stylesheet_uri() );
  250. // $this->assertSame( $root_uri . '/' . get_stylesheet(), get_locale_stylesheet_uri() );
  251. $this->assertSame( $root_fs . '/' . get_template(), get_template_directory() );
  252. $this->assertSame( $root_uri . '/' . get_template(), get_template_directory_uri() );
  253. // get_query_template()
  254. // Template file that doesn't exist.
  255. $this->assertSame( '', get_query_template( rand_str() ) );
  256. // Template files that do exist.
  257. /*
  258. foreach ( $theme['Template Files'] as $path ) {
  259. $file = basename($path, '.php');
  260. FIXME: untestable because get_query_template() uses TEMPLATEPATH.
  261. $this->assertSame('', get_query_template($file));
  262. }
  263. */
  264. // These are kind of tautologies but at least exercise the code.
  265. $this->assertSame( get_404_template(), get_query_template( '404' ) );
  266. $this->assertSame( get_archive_template(), get_query_template( 'archive' ) );
  267. $this->assertSame( get_author_template(), get_query_template( 'author' ) );
  268. $this->assertSame( get_category_template(), get_query_template( 'category' ) );
  269. $this->assertSame( get_date_template(), get_query_template( 'date' ) );
  270. $this->assertSame( get_home_template(), get_query_template( 'home', array( 'home.php', 'index.php' ) ) );
  271. $this->assertSame( get_privacy_policy_template(), get_query_template( 'privacy_policy', array( 'privacy-policy.php' ) ) );
  272. $this->assertSame( get_page_template(), get_query_template( 'page' ) );
  273. $this->assertSame( get_search_template(), get_query_template( 'search' ) );
  274. $this->assertSame( get_single_template(), get_query_template( 'single' ) );
  275. $this->assertSame( get_attachment_template(), get_query_template( 'attachment' ) );
  276. $this->assertSame( get_tag_template(), get_query_template( 'tag' ) );
  277. // nb: This probably doesn't run because WP_INSTALLING is defined.
  278. $this->assertTrue( validate_current_theme() );
  279. }
  280. }
  281. }
  282. function test_switch_theme_bogus() {
  283. // Try switching to a theme that doesn't exist.
  284. $template = rand_str();
  285. $style = rand_str();
  286. update_option( 'template', $template );
  287. update_option( 'stylesheet', $style );
  288. $theme = wp_get_theme();
  289. $this->assertSame( $style, (string) $theme );
  290. $this->assertNotFalse( $theme->errors() );
  291. $this->assertFalse( $theme->exists() );
  292. // These return the bogus name - perhaps not ideal behaviour?
  293. $this->assertSame( $template, get_template() );
  294. $this->assertSame( $style, get_stylesheet() );
  295. }
  296. /**
  297. * Test _wp_keep_alive_customize_changeset_dependent_auto_drafts.
  298. *
  299. * @covers ::_wp_keep_alive_customize_changeset_dependent_auto_drafts
  300. */
  301. function test_wp_keep_alive_customize_changeset_dependent_auto_drafts() {
  302. $nav_created_post_ids = $this->factory()->post->create_many(
  303. 2,
  304. array(
  305. 'post_status' => 'auto-draft',
  306. 'post_date' => gmdate( 'Y-m-d H:i:s', strtotime( '-2 days' ) ),
  307. )
  308. );
  309. $data = array(
  310. 'nav_menus_created_posts' => array(
  311. 'value' => $nav_created_post_ids,
  312. ),
  313. );
  314. wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
  315. require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
  316. $wp_customize = new WP_Customize_Manager();
  317. do_action( 'customize_register', $wp_customize );
  318. // The post_date for auto-drafts is bumped to match the changeset post_date whenever it is modified
  319. // to keep them from from being garbage collected by wp_delete_auto_drafts().
  320. $wp_customize->save_changeset_post(
  321. array(
  322. 'data' => $data,
  323. )
  324. );
  325. $this->assertSame( get_post( $wp_customize->changeset_post_id() )->post_date, get_post( $nav_created_post_ids[0] )->post_date );
  326. $this->assertSame( get_post( $wp_customize->changeset_post_id() )->post_date, get_post( $nav_created_post_ids[1] )->post_date );
  327. $this->assertSame( 'auto-draft', get_post_status( $nav_created_post_ids[0] ) );
  328. $this->assertSame( 'auto-draft', get_post_status( $nav_created_post_ids[1] ) );
  329. // Stubs transition to drafts when changeset is saved as a draft.
  330. $wp_customize->save_changeset_post(
  331. array(
  332. 'status' => 'draft',
  333. 'data' => $data,
  334. )
  335. );
  336. $this->assertSame( 'draft', get_post_status( $nav_created_post_ids[0] ) );
  337. $this->assertSame( 'draft', get_post_status( $nav_created_post_ids[1] ) );
  338. // Status remains unchanged for stub that the user broke out of the changeset.
  339. wp_update_post(
  340. array(
  341. 'ID' => $nav_created_post_ids[1],
  342. 'post_status' => 'private',
  343. )
  344. );
  345. $wp_customize->save_changeset_post(
  346. array(
  347. 'status' => 'draft',
  348. 'data' => $data,
  349. )
  350. );
  351. $this->assertSame( 'draft', get_post_status( $nav_created_post_ids[0] ) );
  352. $this->assertSame( 'private', get_post_status( $nav_created_post_ids[1] ) );
  353. // Draft stub is trashed when the changeset is trashed.
  354. $wp_customize->trash_changeset_post( $wp_customize->changeset_post_id() );
  355. $this->assertSame( 'trash', get_post_status( $nav_created_post_ids[0] ) );
  356. $this->assertSame( 'private', get_post_status( $nav_created_post_ids[1] ) );
  357. }
  358. /**
  359. * @ticket 49406
  360. */
  361. public function test_register_theme_support_defaults() {
  362. $registered = register_theme_feature( 'test-feature' );
  363. $this->assertTrue( $registered );
  364. $expected = array(
  365. 'type' => 'boolean',
  366. 'variadic' => false,
  367. 'description' => '',
  368. 'show_in_rest' => false,
  369. );
  370. $this->assertSameSets( $expected, get_registered_theme_feature( 'test-feature' ) );
  371. }
  372. /**
  373. * @ticket 49406
  374. */
  375. public function test_register_theme_support_explicit() {
  376. $args = array(
  377. 'type' => 'array',
  378. 'variadic' => true,
  379. 'description' => 'My Feature',
  380. 'show_in_rest' => array(
  381. 'schema' => array(
  382. 'items' => array(
  383. 'type' => 'string',
  384. ),
  385. ),
  386. ),
  387. );
  388. register_theme_feature( 'test-feature', $args );
  389. $actual = get_registered_theme_feature( 'test-feature' );
  390. $this->assertSame( 'array', $actual['type'] );
  391. $this->assertTrue( $actual['variadic'] );
  392. $this->assertSame( 'My Feature', $actual['description'] );
  393. $this->assertSame( array( 'type' => 'string' ), $actual['show_in_rest']['schema']['items'] );
  394. }
  395. /**
  396. * @ticket 49406
  397. */
  398. public function test_register_theme_support_upgrades_show_in_rest() {
  399. register_theme_feature( 'test-feature', array( 'show_in_rest' => true ) );
  400. $expected = array(
  401. 'schema' => array(
  402. 'description' => '',
  403. 'type' => 'boolean',
  404. 'default' => false,
  405. ),
  406. 'name' => 'test-feature',
  407. 'prepare_callback' => null,
  408. );
  409. $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest'];
  410. $this->assertSameSets( $expected, $actual );
  411. }
  412. /**
  413. * @ticket 49406
  414. */
  415. public function test_register_theme_support_fills_schema() {
  416. register_theme_feature(
  417. 'test-feature',
  418. array(
  419. 'type' => 'array',
  420. 'description' => 'Cool Feature',
  421. 'show_in_rest' => array(
  422. 'schema' => array(
  423. 'items' => array(
  424. 'type' => 'string',
  425. ),
  426. 'minItems' => 1,
  427. ),
  428. ),
  429. )
  430. );
  431. $expected = array(
  432. 'description' => 'Cool Feature',
  433. 'type' => array( 'boolean', 'array' ),
  434. 'items' => array(
  435. 'type' => 'string',
  436. ),
  437. 'minItems' => 1,
  438. 'default' => false,
  439. );
  440. $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema'];
  441. $this->assertSameSets( $expected, $actual );
  442. }
  443. /**
  444. * @ticket 49406
  445. */
  446. public function test_register_theme_support_does_not_add_boolean_type_if_non_bool_default() {
  447. register_theme_feature(
  448. 'test-feature',
  449. array(
  450. 'type' => 'array',
  451. 'show_in_rest' => array(
  452. 'schema' => array(
  453. 'items' => array(
  454. 'type' => 'string',
  455. ),
  456. 'default' => array( 'standard' ),
  457. ),
  458. ),
  459. )
  460. );
  461. $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['type'];
  462. $this->assertSame( 'array', $actual );
  463. }
  464. /**
  465. * @ticket 49406
  466. */
  467. public function test_register_theme_support_defaults_additional_properties_to_false() {
  468. register_theme_feature(
  469. 'test-feature',
  470. array(
  471. 'type' => 'object',
  472. 'description' => 'Cool Feature',
  473. 'show_in_rest' => array(
  474. 'schema' => array(
  475. 'properties' => array(
  476. 'a' => array(
  477. 'type' => 'string',
  478. ),
  479. ),
  480. ),
  481. ),
  482. )
  483. );
  484. $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema'];
  485. $this->assertArrayHasKey( 'additionalProperties', $actual );
  486. $this->assertFalse( $actual['additionalProperties'] );
  487. }
  488. /**
  489. * @ticket 49406
  490. */
  491. public function test_register_theme_support_with_additional_properties() {
  492. register_theme_feature(
  493. 'test-feature',
  494. array(
  495. 'type' => 'object',
  496. 'description' => 'Cool Feature',
  497. 'show_in_rest' => array(
  498. 'schema' => array(
  499. 'properties' => array(),
  500. 'additionalProperties' => array(
  501. 'type' => 'string',
  502. ),
  503. ),
  504. ),
  505. )
  506. );
  507. $expected = array(
  508. 'type' => 'string',
  509. );
  510. $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['additionalProperties'];
  511. $this->assertSameSets( $expected, $actual );
  512. }
  513. /**
  514. * @ticket 49406
  515. */
  516. public function test_register_theme_support_defaults_additional_properties_to_false_in_array() {
  517. register_theme_feature(
  518. 'test-feature',
  519. array(
  520. 'type' => 'array',
  521. 'description' => 'Cool Feature',
  522. 'show_in_rest' => array(
  523. 'schema' => array(
  524. 'items' => array(
  525. 'type' => 'object',
  526. 'properties' => array(
  527. 'a' => array(
  528. 'type' => 'string',
  529. ),
  530. ),
  531. ),
  532. ),
  533. ),
  534. )
  535. );
  536. $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['items'];
  537. $this->assertArrayHasKey( 'additionalProperties', $actual );
  538. $this->assertFalse( $actual['additionalProperties'] );
  539. }
  540. /**
  541. * @ticket 49406
  542. *
  543. * @dataProvider _dp_register_theme_support_validation
  544. *
  545. * @param string $error_code The error code expected.
  546. * @param array $args The args to register.
  547. */
  548. public function test_register_theme_support_validation( $error_code, $args ) {
  549. $registered = register_theme_feature( 'test-feature', $args );
  550. $this->assertWPError( $registered );
  551. $this->assertSame( $error_code, $registered->get_error_code() );
  552. }
  553. public function _dp_register_theme_support_validation() {
  554. return array(
  555. array(
  556. 'invalid_type',
  557. array(
  558. 'type' => 'float',
  559. ),
  560. ),
  561. array(
  562. 'invalid_type',
  563. array(
  564. 'type' => array( 'string' ),
  565. ),
  566. ),
  567. array(
  568. 'variadic_must_be_array',
  569. array(
  570. 'variadic' => true,
  571. ),
  572. ),
  573. array(
  574. 'missing_schema',
  575. array(
  576. 'type' => 'object',
  577. 'show_in_rest' => true,
  578. ),
  579. ),
  580. array(
  581. 'missing_schema',
  582. array(
  583. 'type' => 'array',
  584. 'show_in_rest' => true,
  585. ),
  586. ),
  587. array(
  588. 'missing_schema_items',
  589. array(
  590. 'type' => 'array',
  591. 'show_in_rest' => array(
  592. 'schema' => array(
  593. 'type' => 'array',
  594. ),
  595. ),
  596. ),
  597. ),
  598. array(
  599. 'missing_schema_properties',
  600. array(
  601. 'type' => 'object',
  602. 'show_in_rest' => array(
  603. 'schema' => array(
  604. 'type' => 'object',
  605. ),
  606. ),
  607. ),
  608. ),
  609. array(
  610. 'invalid_rest_prepare_callback',
  611. array(
  612. 'show_in_rest' => array(
  613. 'prepare_callback' => 'this is not a valid function',
  614. ),
  615. ),
  616. ),
  617. );
  618. }
  619. }