custom-css-setting.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <?php
  2. /**
  3. * Test Test_WP_Customize_Custom_CSS_Setting.
  4. *
  5. * Tests WP_Customize_Custom_CSS_Setting.
  6. *
  7. * @group customize
  8. */
  9. class Test_WP_Customize_Custom_CSS_Setting extends WP_UnitTestCase {
  10. /**
  11. * Instance of WP_Customize_Manager which is reset for each test.
  12. *
  13. * @var WP_Customize_Manager
  14. */
  15. public $wp_customize;
  16. /**
  17. * The Setting instance.
  18. *
  19. * @var WP_Customize_Custom_CSS_Setting
  20. */
  21. public $setting;
  22. /**
  23. * Set up the test case.
  24. *
  25. * @see WP_UnitTestCase::setup()
  26. */
  27. function setUp() {
  28. parent::setUp();
  29. require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
  30. $user_id = self::factory()->user->create(
  31. array(
  32. 'role' => 'administrator',
  33. )
  34. );
  35. if ( is_multisite() ) {
  36. grant_super_admin( $user_id );
  37. }
  38. wp_set_current_user( $user_id );
  39. global $wp_customize;
  40. $this->wp_customize = new WP_Customize_Manager();
  41. $wp_customize = $this->wp_customize;
  42. do_action( 'customize_register', $this->wp_customize );
  43. $this->setting = new WP_Customize_Custom_CSS_Setting( $this->wp_customize, 'custom_css[' . get_stylesheet() . ']' );
  44. $this->wp_customize->add_setting( $this->setting );
  45. }
  46. /**
  47. * Tear down the test case.
  48. */
  49. function tearDown() {
  50. $this->setting = null;
  51. parent::tearDown();
  52. }
  53. /**
  54. * Delete the $wp_customize global when cleaning up scope.
  55. */
  56. function clean_up_global_scope() {
  57. global $wp_customize;
  58. $wp_customize = null;
  59. parent::clean_up_global_scope();
  60. }
  61. /**
  62. * Test constructor.
  63. *
  64. * Mainly validates that the correct hooks exist.
  65. *
  66. * Also checks for the post type and the Setting Type.
  67. *
  68. * @covers WP_Customize_Custom_CSS_Setting::__construct
  69. */
  70. function test_construct() {
  71. $this->assertTrue( post_type_exists( 'custom_css' ) );
  72. $this->assertSame( 'custom_css', $this->setting->type );
  73. $this->assertSame( get_stylesheet(), $this->setting->stylesheet );
  74. $this->assertSame( 'edit_css', $this->setting->capability );
  75. $exception = null;
  76. try {
  77. $x = new WP_Customize_Custom_CSS_Setting( $this->wp_customize, 'bad' );
  78. unset( $x );
  79. } catch ( Exception $e ) {
  80. $exception = $e;
  81. }
  82. $this->assertInstanceOf( 'Exception', $exception );
  83. $exception = null;
  84. try {
  85. $x = new WP_Customize_Custom_CSS_Setting( $this->wp_customize, 'custom_css' );
  86. unset( $x );
  87. } catch ( Exception $e ) {
  88. $exception = $e;
  89. }
  90. $this->assertInstanceOf( 'Exception', $exception );
  91. }
  92. /**
  93. * Test crud methods on WP_Customize_Custom_CSS_Setting.
  94. *
  95. * @covers ::wp_get_custom_css
  96. * @covers WP_Customize_Custom_CSS_Setting::value
  97. * @covers WP_Customize_Custom_CSS_Setting::preview
  98. * @covers WP_Customize_Custom_CSS_Setting::update
  99. */
  100. function test_crud() {
  101. $this->setting->default = '/* Hello World */';
  102. $this->assertSame( $this->setting->default, $this->setting->value() );
  103. $this->assertNull( wp_get_custom_css_post() );
  104. $this->assertNull( wp_get_custom_css_post( $this->setting->stylesheet ) );
  105. $this->assertNull( wp_get_custom_css_post( 'twentyten' ) );
  106. $original_css = 'body { color: black; }';
  107. $post_id = $this->factory()->post->create(
  108. array(
  109. 'post_title' => $this->setting->stylesheet,
  110. 'post_name' => $this->setting->stylesheet,
  111. 'post_content' => $original_css,
  112. 'post_status' => 'publish',
  113. 'post_type' => 'custom_css',
  114. )
  115. );
  116. $twentyten_css = 'body { color: red; }';
  117. $twentyten_post_id = $this->factory()->post->create(
  118. array(
  119. 'post_title' => 'twentyten',
  120. 'post_name' => 'twentyten',
  121. 'post_content' => $twentyten_css,
  122. 'post_status' => 'publish',
  123. 'post_type' => 'custom_css',
  124. )
  125. );
  126. $twentyten_setting = new WP_Customize_Custom_CSS_Setting( $this->wp_customize, 'custom_css[twentyten]' );
  127. remove_theme_mod( 'custom_css_post_id' );
  128. $this->assertSame( $post_id, wp_get_custom_css_post()->ID );
  129. $this->assertSame( $post_id, wp_get_custom_css_post( $this->setting->stylesheet )->ID );
  130. $this->assertSame( $twentyten_post_id, wp_get_custom_css_post( 'twentyten' )->ID );
  131. $this->assertSame( $original_css, wp_get_custom_css( $this->setting->stylesheet ) );
  132. $this->assertSame( $original_css, $this->setting->value() );
  133. $this->assertSame( $twentyten_css, wp_get_custom_css( 'twentyten' ) );
  134. $this->assertSame( $twentyten_css, $twentyten_setting->value() );
  135. $updated_css = 'body { color: blue; }';
  136. $this->wp_customize->set_post_value( $this->setting->id, $updated_css );
  137. $saved = $this->setting->save();
  138. $this->assertNotFalse( $saved );
  139. $this->assertSame( $updated_css, $this->setting->value() );
  140. $this->assertSame( $updated_css, wp_get_custom_css( $this->setting->stylesheet ) );
  141. $this->assertSame( $updated_css, get_post( $post_id )->post_content );
  142. $previewed_css = 'body { color: red; }';
  143. $this->wp_customize->set_post_value( $this->setting->id, $previewed_css );
  144. $this->setting->preview();
  145. $this->assertSame( $previewed_css, $this->setting->value() );
  146. $this->assertSame( $previewed_css, wp_get_custom_css( $this->setting->stylesheet ) );
  147. // Make sure that wp_update_custom_css_post() works as expected for updates.
  148. $r = wp_update_custom_css_post(
  149. 'body { color:red; }',
  150. array(
  151. 'stylesheet' => $this->setting->stylesheet,
  152. 'preprocessed' => "body\n\tcolor:red;",
  153. )
  154. );
  155. $this->assertInstanceOf( 'WP_Post', $r );
  156. $this->assertSame( $post_id, $r->ID );
  157. $this->assertSame( 'body { color:red; }', get_post( $r )->post_content );
  158. $this->assertSame( "body\n\tcolor:red;", get_post( $r )->post_content_filtered );
  159. $r = wp_update_custom_css_post( 'body { content: "\o/"; }' );
  160. $this->assertSame( $this->wp_customize->get_stylesheet(), get_post( $r )->post_name );
  161. $this->assertSame( 'body { content: "\o/"; }', get_post( $r )->post_content );
  162. $this->assertSame( '', get_post( $r )->post_content_filtered );
  163. // Make sure that wp_update_custom_css_post() works as expected for insertion.
  164. $r = wp_update_custom_css_post(
  165. 'body { background:black; }',
  166. array(
  167. 'stylesheet' => 'other',
  168. )
  169. );
  170. $this->assertInstanceOf( 'WP_Post', $r );
  171. $this->assertSame( 'other', get_post( $r )->post_name );
  172. $this->assertSame( 'body { background:black; }', get_post( $r )->post_content );
  173. $this->assertSame( 'publish', get_post( $r )->post_status );
  174. // Test deletion.
  175. wp_delete_post( $post_id );
  176. $this->assertNull( wp_get_custom_css_post() );
  177. $this->assertNull( wp_get_custom_css_post( get_stylesheet() ) );
  178. $this->assertSame( $previewed_css, wp_get_custom_css( get_stylesheet() ), 'Previewed value remains in spite of deleted post.' );
  179. wp_delete_post( $twentyten_post_id );
  180. $this->assertNull( wp_get_custom_css_post( 'twentyten' ) );
  181. $this->assertSame( '', wp_get_custom_css( 'twentyten' ) );
  182. }
  183. /**
  184. * Test revision saving on initial save of Custom CSS.
  185. *
  186. * @ticket 39032
  187. */
  188. function test_custom_css_revision_saved() {
  189. $inserted_css = 'body { background: black; }';
  190. $updated_css = 'body { background: red; }';
  191. $post = wp_update_custom_css_post(
  192. $inserted_css,
  193. array(
  194. 'stylesheet' => 'testtheme',
  195. )
  196. );
  197. $this->assertSame( $inserted_css, $post->post_content );
  198. $revisions = array_values( wp_get_post_revisions( $post ) );
  199. $this->assertCount( 1, $revisions );
  200. $this->assertSame( $inserted_css, $revisions[0]->post_content );
  201. wp_update_custom_css_post(
  202. $updated_css,
  203. array(
  204. 'stylesheet' => 'testtheme',
  205. )
  206. );
  207. $revisions = array_values( wp_get_post_revisions( $post ) );
  208. $this->assertCount( 2, $revisions );
  209. $this->assertSame( $updated_css, $revisions[0]->post_content );
  210. $this->assertSame( $inserted_css, $revisions[1]->post_content );
  211. }
  212. /**
  213. * Test that wp_get_custom_css_post() doesn't query for a post after caching a failed lookup.
  214. *
  215. * @ticket 39259
  216. */
  217. function test_get_custom_css_post_queries_after_failed_lookup() {
  218. set_theme_mod( 'custom_css_post_id', -1 );
  219. $queries_before = get_num_queries();
  220. wp_get_custom_css_post();
  221. $this->assertSame( get_num_queries(), $queries_before );
  222. }
  223. /**
  224. * Test that wp_update_custom_css_post() updates the 'custom_css_post_id' theme mod.
  225. *
  226. * @ticket 39259
  227. */
  228. function test_update_custom_css_updates_theme_mod() {
  229. set_theme_mod( 'custom_css_post_id', -1 );
  230. $post = wp_update_custom_css_post( 'body { background: blue; }' );
  231. $this->assertSame( $post->ID, get_theme_mod( 'custom_css_post_id' ) );
  232. }
  233. /**
  234. * Test crud methods on WP_Customize_Custom_CSS_Setting.
  235. *
  236. * @covers WP_Customize_Custom_CSS_Setting::value
  237. */
  238. function test_value_filter() {
  239. add_filter( 'customize_value_custom_css', array( $this, 'filter_value' ), 10, 2 );
  240. $this->setting->default = '/*default*/';
  241. $this->assertSame( '/*default*//*filtered*/', $this->setting->value() );
  242. $this->factory()->post->create(
  243. array(
  244. 'post_title' => $this->setting->stylesheet,
  245. 'post_name' => $this->setting->stylesheet,
  246. 'post_content' => '/*custom*/',
  247. 'post_status' => 'publish',
  248. 'post_type' => 'custom_css',
  249. )
  250. );
  251. remove_theme_mod( 'custom_css_post_id' );
  252. $this->assertSame( '/*custom*//*filtered*/', $this->setting->value() );
  253. $this->wp_customize->set_post_value( $this->setting->id, '/*overridden*/' );
  254. $this->setting->preview();
  255. $this->assertSame( '/*overridden*/', $this->setting->value(), 'Expected value to not be filtered since post value is present.' );
  256. }
  257. /**
  258. * Filter value.
  259. *
  260. * @param string $value Value.
  261. * @param WP_Customize_Setting $setting Setting.
  262. * @return string
  263. */
  264. function filter_value( $value, $setting ) {
  265. $this->assertInstanceOf( 'WP_Customize_Custom_CSS_Setting', $setting );
  266. $value .= '/*filtered*/';
  267. return $value;
  268. }
  269. /**
  270. * Test update filter on WP_Customize_Custom_CSS_Setting.
  271. *
  272. * @covers WP_Customize_Custom_CSS_Setting::update
  273. */
  274. function test_update_filter() {
  275. $original_css = 'body { color:red; }';
  276. $post_id = $this->factory()->post->create(
  277. array(
  278. 'post_title' => $this->setting->stylesheet,
  279. 'post_name' => $this->setting->stylesheet,
  280. 'post_content' => $original_css,
  281. 'post_status' => 'publish',
  282. 'post_type' => 'custom_css',
  283. )
  284. );
  285. $overridden_css = 'body { color:green; }';
  286. $this->wp_customize->set_post_value( $this->setting->id, $overridden_css );
  287. $post = get_post( $post_id );
  288. $original_title = $post->post_title;
  289. add_filter( 'update_custom_css_data', array( $this, 'filter_update_custom_css_data' ), 10, 3 );
  290. $this->setting->save();
  291. $post = get_post( $post_id );
  292. $this->assertSame( $original_title, $post->post_title );
  293. $this->assertContains( $overridden_css, $post->post_content );
  294. $this->assertContains( '/* filtered post_content */', $post->post_content );
  295. $this->assertContains( '/* filtered post_content_filtered */', $post->post_content_filtered );
  296. }
  297. /**
  298. * Filter `customize_update_custom_css_post_content_args`.
  299. *
  300. * @param array $data Data.
  301. * @param string $args Args.
  302. * @return array Data.
  303. */
  304. function filter_update_custom_css_data( $data, $args ) {
  305. $this->assertInternalType( 'array', $data );
  306. $this->assertSameSets( array( 'css', 'preprocessed' ), array_keys( $data ) );
  307. $this->assertSame( '', $data['preprocessed'] );
  308. $this->assertInternalType( 'array', $args );
  309. $this->assertSameSets( array( 'css', 'preprocessed', 'stylesheet' ), array_keys( $args ) );
  310. $this->assertSame( $args['css'], $data['css'] );
  311. $this->assertSame( $args['preprocessed'], $data['preprocessed'] );
  312. $data['css'] .= '/* filtered post_content */';
  313. $data['preprocessed'] = '/* filtered post_content_filtered */';
  314. $data['post_title'] = 'Ignored';
  315. return $data;
  316. }
  317. /**
  318. * Tests that validation errors are caught appropriately.
  319. *
  320. * Note that the $validity \WP_Error object must be reset each time
  321. * as it picks up the Errors and passes them to the next assertion.
  322. *
  323. * @covers WP_Customize_Custom_CSS_Setting::validate
  324. */
  325. function test_validate() {
  326. // Empty CSS throws no errors.
  327. $result = $this->setting->validate( '' );
  328. $this->assertTrue( $result );
  329. // Basic, valid CSS throws no errors.
  330. $basic_css = 'body { background: #f00; } h1.site-title { font-size: 36px; } a:hover { text-decoration: none; } input[type="text"] { padding: 1em; }';
  331. $result = $this->setting->validate( $basic_css );
  332. $this->assertTrue( $result );
  333. // Check for markup.
  334. $unclosed_comment = $basic_css . '</style>';
  335. $result = $this->setting->validate( $unclosed_comment );
  336. $this->assertTrue( array_key_exists( 'illegal_markup', $result->errors ) );
  337. }
  338. }