rest-api.php 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383
  1. <?php
  2. /**
  3. * REST API functions.
  4. *
  5. * @package WordPress
  6. * @subpackage REST API
  7. */
  8. require_once ABSPATH . 'wp-admin/includes/admin.php';
  9. require_once ABSPATH . WPINC . '/rest-api.php';
  10. require_once __DIR__ . '/../includes/class-jsonserializable-object.php';
  11. /**
  12. * @group restapi
  13. */
  14. class Tests_REST_API extends WP_UnitTestCase {
  15. public function setUp() {
  16. parent::setUp();
  17. // Override the normal server with our spying server.
  18. $GLOBALS['wp_rest_server'] = new Spy_REST_Server();
  19. do_action( 'rest_api_init', $GLOBALS['wp_rest_server'] );
  20. }
  21. public function tearDown() {
  22. remove_filter( 'wp_rest_server_class', array( $this, 'filter_wp_rest_server_class' ) );
  23. parent::tearDown();
  24. }
  25. /**
  26. * Checks that the main classes are loaded.
  27. */
  28. function test_rest_api_active() {
  29. $this->assertTrue( class_exists( 'WP_REST_Server' ) );
  30. $this->assertTrue( class_exists( 'WP_REST_Request' ) );
  31. $this->assertTrue( class_exists( 'WP_REST_Response' ) );
  32. $this->assertTrue( class_exists( 'WP_REST_Posts_Controller' ) );
  33. }
  34. /**
  35. * The rest_api_init hook should have been registered with init, and should
  36. * have a default priority of 10.
  37. */
  38. function test_init_action_added() {
  39. $this->assertSame( 10, has_action( 'init', 'rest_api_init' ) );
  40. }
  41. public function test_add_extra_api_taxonomy_arguments() {
  42. $taxonomy = get_taxonomy( 'category' );
  43. $this->assertTrue( $taxonomy->show_in_rest );
  44. $this->assertSame( 'categories', $taxonomy->rest_base );
  45. $this->assertSame( 'WP_REST_Terms_Controller', $taxonomy->rest_controller_class );
  46. $taxonomy = get_taxonomy( 'post_tag' );
  47. $this->assertTrue( $taxonomy->show_in_rest );
  48. $this->assertSame( 'tags', $taxonomy->rest_base );
  49. $this->assertSame( 'WP_REST_Terms_Controller', $taxonomy->rest_controller_class );
  50. }
  51. public function test_add_extra_api_post_type_arguments() {
  52. $post_type = get_post_type_object( 'post' );
  53. $this->assertTrue( $post_type->show_in_rest );
  54. $this->assertSame( 'posts', $post_type->rest_base );
  55. $this->assertSame( 'WP_REST_Posts_Controller', $post_type->rest_controller_class );
  56. $post_type = get_post_type_object( 'page' );
  57. $this->assertTrue( $post_type->show_in_rest );
  58. $this->assertSame( 'pages', $post_type->rest_base );
  59. $this->assertSame( 'WP_REST_Posts_Controller', $post_type->rest_controller_class );
  60. $post_type = get_post_type_object( 'attachment' );
  61. $this->assertTrue( $post_type->show_in_rest );
  62. $this->assertSame( 'media', $post_type->rest_base );
  63. $this->assertSame( 'WP_REST_Attachments_Controller', $post_type->rest_controller_class );
  64. }
  65. /**
  66. * Check that a single route is canonicalized.
  67. *
  68. * Ensures that single and multiple routes are handled correctly.
  69. */
  70. public function test_route_canonicalized() {
  71. register_rest_route(
  72. 'test-ns',
  73. '/test',
  74. array(
  75. 'methods' => array( 'GET' ),
  76. 'callback' => '__return_null',
  77. 'permission_callback' => '__return_true',
  78. )
  79. );
  80. // Check the route was registered correctly.
  81. $endpoints = $GLOBALS['wp_rest_server']->get_raw_endpoint_data();
  82. $this->assertArrayHasKey( '/test-ns/test', $endpoints );
  83. // Check the route was wrapped in an array.
  84. $endpoint = $endpoints['/test-ns/test'];
  85. $this->assertArrayNotHasKey( 'callback', $endpoint );
  86. $this->assertArrayHasKey( 'namespace', $endpoint );
  87. $this->assertSame( 'test-ns', $endpoint['namespace'] );
  88. // Grab the filtered data.
  89. $filtered_endpoints = $GLOBALS['wp_rest_server']->get_routes();
  90. $this->assertArrayHasKey( '/test-ns/test', $filtered_endpoints );
  91. $endpoint = $filtered_endpoints['/test-ns/test'];
  92. $this->assertCount( 1, $endpoint );
  93. $this->assertArrayHasKey( 'callback', $endpoint[0] );
  94. $this->assertArrayHasKey( 'methods', $endpoint[0] );
  95. $this->assertArrayHasKey( 'args', $endpoint[0] );
  96. }
  97. /**
  98. * Check that a single route is canonicalized.
  99. *
  100. * Ensures that single and multiple routes are handled correctly.
  101. */
  102. public function test_route_canonicalized_multiple() {
  103. register_rest_route(
  104. 'test-ns',
  105. '/test',
  106. array(
  107. array(
  108. 'methods' => array( 'GET' ),
  109. 'callback' => '__return_null',
  110. 'permission_callback' => '__return_true',
  111. ),
  112. array(
  113. 'methods' => array( 'POST' ),
  114. 'callback' => '__return_null',
  115. 'permission_callback' => '__return_true',
  116. ),
  117. )
  118. );
  119. // Check the route was registered correctly.
  120. $endpoints = $GLOBALS['wp_rest_server']->get_raw_endpoint_data();
  121. $this->assertArrayHasKey( '/test-ns/test', $endpoints );
  122. // Check the route was wrapped in an array.
  123. $endpoint = $endpoints['/test-ns/test'];
  124. $this->assertArrayNotHasKey( 'callback', $endpoint );
  125. $this->assertArrayHasKey( 'namespace', $endpoint );
  126. $this->assertSame( 'test-ns', $endpoint['namespace'] );
  127. $filtered_endpoints = $GLOBALS['wp_rest_server']->get_routes();
  128. $endpoint = $filtered_endpoints['/test-ns/test'];
  129. $this->assertCount( 2, $endpoint );
  130. // Check for both methods.
  131. foreach ( array( 0, 1 ) as $key ) {
  132. $this->assertArrayHasKey( 'callback', $endpoint[ $key ] );
  133. $this->assertArrayHasKey( 'methods', $endpoint[ $key ] );
  134. $this->assertArrayHasKey( 'args', $endpoint[ $key ] );
  135. }
  136. }
  137. /**
  138. * Check that routes are merged by default.
  139. */
  140. public function test_route_merge() {
  141. register_rest_route(
  142. 'test-ns',
  143. '/test',
  144. array(
  145. 'methods' => array( 'GET' ),
  146. 'callback' => '__return_null',
  147. 'permission_callback' => '__return_true',
  148. )
  149. );
  150. register_rest_route(
  151. 'test-ns',
  152. '/test',
  153. array(
  154. 'methods' => array( 'POST' ),
  155. 'callback' => '__return_null',
  156. 'permission_callback' => '__return_true',
  157. )
  158. );
  159. // Check both routes exist.
  160. $endpoints = $GLOBALS['wp_rest_server']->get_routes();
  161. $endpoint = $endpoints['/test-ns/test'];
  162. $this->assertCount( 2, $endpoint );
  163. }
  164. /**
  165. * Check that we can override routes.
  166. */
  167. public function test_route_override() {
  168. register_rest_route(
  169. 'test-ns',
  170. '/test',
  171. array(
  172. 'methods' => array( 'GET' ),
  173. 'callback' => '__return_null',
  174. 'permission_callback' => '__return_true',
  175. 'should_exist' => false,
  176. )
  177. );
  178. register_rest_route(
  179. 'test-ns',
  180. '/test',
  181. array(
  182. 'methods' => array( 'POST' ),
  183. 'callback' => '__return_null',
  184. 'permission_callback' => '__return_true',
  185. 'should_exist' => true,
  186. ),
  187. true
  188. );
  189. // Check we only have one route.
  190. $endpoints = $GLOBALS['wp_rest_server']->get_routes();
  191. $endpoint = $endpoints['/test-ns/test'];
  192. $this->assertCount( 1, $endpoint );
  193. // Check it's the right one.
  194. $this->assertArrayHasKey( 'should_exist', $endpoint[0] );
  195. $this->assertTrue( $endpoint[0]['should_exist'] );
  196. }
  197. /**
  198. * Test that we reject routes without namespaces
  199. *
  200. * @expectedIncorrectUsage register_rest_route
  201. */
  202. public function test_route_reject_empty_namespace() {
  203. register_rest_route(
  204. '',
  205. '/test-empty-namespace',
  206. array(
  207. 'methods' => array( 'POST' ),
  208. 'callback' => '__return_null',
  209. 'permission_callback' => '__return_true',
  210. ),
  211. true
  212. );
  213. $endpoints = $GLOBALS['wp_rest_server']->get_routes();
  214. $this->assertFalse( isset( $endpoints['/test-empty-namespace'] ) );
  215. }
  216. /**
  217. * Test that we reject empty routes
  218. *
  219. * @expectedIncorrectUsage register_rest_route
  220. */
  221. public function test_route_reject_empty_route() {
  222. register_rest_route(
  223. '/test-empty-route',
  224. '',
  225. array(
  226. 'methods' => array( 'POST' ),
  227. 'callback' => '__return_null',
  228. 'permission_callback' => '__return_true',
  229. ),
  230. true
  231. );
  232. $endpoints = $GLOBALS['wp_rest_server']->get_routes();
  233. $this->assertFalse( isset( $endpoints['/test-empty-route'] ) );
  234. }
  235. /**
  236. * The rest_route query variable should be registered.
  237. */
  238. function test_rest_route_query_var() {
  239. rest_api_init();
  240. $this->assertTrue( in_array( 'rest_route', $GLOBALS['wp']->public_query_vars, true ) );
  241. }
  242. public function test_route_method() {
  243. register_rest_route(
  244. 'test-ns',
  245. '/test',
  246. array(
  247. 'methods' => array( 'GET' ),
  248. 'callback' => '__return_null',
  249. 'permission_callback' => '__return_true',
  250. )
  251. );
  252. $routes = $GLOBALS['wp_rest_server']->get_routes();
  253. $this->assertSame( $routes['/test-ns/test'][0]['methods'], array( 'GET' => true ) );
  254. }
  255. /**
  256. * The 'methods' arg should accept a single value as well as array.
  257. */
  258. public function test_route_method_string() {
  259. register_rest_route(
  260. 'test-ns',
  261. '/test',
  262. array(
  263. 'methods' => 'GET',
  264. 'callback' => '__return_null',
  265. 'permission_callback' => '__return_true',
  266. )
  267. );
  268. $routes = $GLOBALS['wp_rest_server']->get_routes();
  269. $this->assertSame( $routes['/test-ns/test'][0]['methods'], array( 'GET' => true ) );
  270. }
  271. /**
  272. * The 'methods' arg should accept a single value as well as array.
  273. */
  274. public function test_route_method_array() {
  275. register_rest_route(
  276. 'test-ns',
  277. '/test',
  278. array(
  279. 'methods' => array( 'GET', 'POST' ),
  280. 'callback' => '__return_null',
  281. 'permission_callback' => '__return_true',
  282. )
  283. );
  284. $routes = $GLOBALS['wp_rest_server']->get_routes();
  285. $this->assertSame(
  286. $routes['/test-ns/test'][0]['methods'],
  287. array(
  288. 'GET' => true,
  289. 'POST' => true,
  290. )
  291. );
  292. }
  293. /**
  294. * The 'methods' arg should a comma-separated string.
  295. */
  296. public function test_route_method_comma_separated() {
  297. register_rest_route(
  298. 'test-ns',
  299. '/test',
  300. array(
  301. 'methods' => 'GET,POST',
  302. 'callback' => '__return_null',
  303. 'permission_callback' => '__return_true',
  304. )
  305. );
  306. $routes = $GLOBALS['wp_rest_server']->get_routes();
  307. $this->assertSame(
  308. $routes['/test-ns/test'][0]['methods'],
  309. array(
  310. 'GET' => true,
  311. 'POST' => true,
  312. )
  313. );
  314. }
  315. public function test_options_request() {
  316. register_rest_route(
  317. 'test-ns',
  318. '/test',
  319. array(
  320. 'methods' => 'GET,POST',
  321. 'callback' => '__return_null',
  322. 'permission_callback' => '__return_true',
  323. )
  324. );
  325. $request = new WP_REST_Request( 'OPTIONS', '/test-ns/test' );
  326. $response = rest_handle_options_request( null, $GLOBALS['wp_rest_server'], $request );
  327. $response = rest_send_allow_header( $response, $GLOBALS['wp_rest_server'], $request );
  328. $headers = $response->get_headers();
  329. $this->assertArrayHasKey( 'Allow', $headers );
  330. $this->assertSame( 'GET, POST', $headers['Allow'] );
  331. }
  332. /**
  333. * Ensure that the OPTIONS handler doesn't kick in for non-OPTIONS requests.
  334. */
  335. public function test_options_request_not_options() {
  336. register_rest_route(
  337. 'test-ns',
  338. '/test',
  339. array(
  340. 'methods' => 'GET,POST',
  341. 'callback' => '__return_true',
  342. 'permission_callback' => '__return_true',
  343. )
  344. );
  345. $request = new WP_REST_Request( 'GET', '/test-ns/test' );
  346. $response = rest_handle_options_request( null, $GLOBALS['wp_rest_server'], $request );
  347. $this->assertNull( $response );
  348. }
  349. /**
  350. * Ensure that result fields are not allowed if no request['_fields'] is present.
  351. */
  352. public function test_rest_filter_response_fields_no_request_filter() {
  353. $response = new WP_REST_Response();
  354. $response->set_data( array( 'a' => true ) );
  355. $request = array();
  356. $response = rest_filter_response_fields( $response, null, $request );
  357. $this->assertSame( array( 'a' => true ), $response->get_data() );
  358. }
  359. /**
  360. * Ensure that result fields are allowed if request['_fields'] is present.
  361. */
  362. public function test_rest_filter_response_fields_single_field_filter() {
  363. $response = new WP_REST_Response();
  364. $response->set_data(
  365. array(
  366. 'a' => 0,
  367. 'b' => 1,
  368. 'c' => 2,
  369. )
  370. );
  371. $request = array(
  372. '_fields' => 'b',
  373. );
  374. $response = rest_filter_response_fields( $response, null, $request );
  375. $this->assertSame( array( 'b' => 1 ), $response->get_data() );
  376. }
  377. /**
  378. * Ensure that multiple comma-separated fields may be allowed with request['_fields'].
  379. */
  380. public function test_rest_filter_response_fields_multi_field_filter() {
  381. $response = new WP_REST_Response();
  382. $response->set_data(
  383. array(
  384. 'a' => 0,
  385. 'b' => 1,
  386. 'c' => 2,
  387. 'd' => 3,
  388. 'e' => 4,
  389. 'f' => 5,
  390. )
  391. );
  392. $request = array(
  393. '_fields' => 'b,c,e',
  394. );
  395. $response = rest_filter_response_fields( $response, null, $request );
  396. $this->assertSame(
  397. array(
  398. 'b' => 1,
  399. 'c' => 2,
  400. 'e' => 4,
  401. ),
  402. $response->get_data()
  403. );
  404. }
  405. /**
  406. * Ensure that multiple comma-separated fields may be allowed
  407. * with request['_fields'] using query parameter array syntax.
  408. */
  409. public function test_rest_filter_response_fields_multi_field_filter_array() {
  410. $response = new WP_REST_Response();
  411. $response->set_data(
  412. array(
  413. 'a' => 0,
  414. 'b' => 1,
  415. 'c' => 2,
  416. 'd' => 3,
  417. 'e' => 4,
  418. 'f' => 5,
  419. )
  420. );
  421. $request = array(
  422. '_fields' => array( 'b', 'c', 'e' ),
  423. );
  424. $response = rest_filter_response_fields( $response, null, $request );
  425. $this->assertSame(
  426. array(
  427. 'b' => 1,
  428. 'c' => 2,
  429. 'e' => 4,
  430. ),
  431. $response->get_data()
  432. );
  433. }
  434. /**
  435. * Ensure that request['_fields'] allowed list apply to items in response collections.
  436. */
  437. public function test_rest_filter_response_fields_numeric_array() {
  438. $response = new WP_REST_Response();
  439. $response->set_data(
  440. array(
  441. array(
  442. 'a' => 0,
  443. 'b' => 1,
  444. 'c' => 2,
  445. ),
  446. array(
  447. 'a' => 3,
  448. 'b' => 4,
  449. 'c' => 5,
  450. ),
  451. array(
  452. 'a' => 6,
  453. 'b' => 7,
  454. 'c' => 8,
  455. ),
  456. )
  457. );
  458. $request = array(
  459. '_fields' => 'b,c',
  460. );
  461. $response = rest_filter_response_fields( $response, null, $request );
  462. $this->assertSame(
  463. array(
  464. array(
  465. 'b' => 1,
  466. 'c' => 2,
  467. ),
  468. array(
  469. 'b' => 4,
  470. 'c' => 5,
  471. ),
  472. array(
  473. 'b' => 7,
  474. 'c' => 8,
  475. ),
  476. ),
  477. $response->get_data()
  478. );
  479. }
  480. /**
  481. * Ensure that nested fields may be allowed with request['_fields'].
  482. *
  483. * @ticket 42094
  484. */
  485. public function test_rest_filter_response_fields_nested_field_filter() {
  486. $response = new WP_REST_Response();
  487. $response->set_data(
  488. array(
  489. 'a' => 0,
  490. 'b' => array(
  491. '1' => 1,
  492. '2' => 2,
  493. ),
  494. 'c' => 3,
  495. 'd' => array(
  496. '4' => 4,
  497. '5' => 5,
  498. ),
  499. )
  500. );
  501. $request = array(
  502. '_fields' => 'b.1,c,d.5',
  503. );
  504. $response = rest_filter_response_fields( $response, null, $request );
  505. $this->assertSame(
  506. array(
  507. 'b' => array(
  508. '1' => 1,
  509. ),
  510. 'c' => 3,
  511. 'd' => array(
  512. '5' => 5,
  513. ),
  514. ),
  515. $response->get_data()
  516. );
  517. }
  518. /**
  519. * Ensure inclusion of deeply nested fields may be controlled with request['_fields'].
  520. *
  521. * @ticket 49648
  522. */
  523. public function test_rest_filter_response_fields_deeply_nested_field_filter() {
  524. $response = new WP_REST_Response();
  525. $response->set_data(
  526. array(
  527. 'field' => array(
  528. 'a' => array(
  529. 'i' => 'value i',
  530. 'ii' => 'value ii',
  531. ),
  532. 'b' => array(
  533. 'iii' => 'value iii',
  534. 'iv' => 'value iv',
  535. ),
  536. ),
  537. )
  538. );
  539. $request = array(
  540. '_fields' => 'field.a.i,field.b.iv',
  541. );
  542. $response = rest_filter_response_fields( $response, null, $request );
  543. $this->assertSame(
  544. array(
  545. 'field' => array(
  546. 'a' => array(
  547. 'i' => 'value i',
  548. ),
  549. 'b' => array(
  550. 'iv' => 'value iv',
  551. ),
  552. ),
  553. ),
  554. $response->get_data()
  555. );
  556. }
  557. /**
  558. * Ensure that specifying a single top-level key in _fields includes that field and all children.
  559. *
  560. * @ticket 48266
  561. */
  562. public function test_rest_filter_response_fields_top_level_key() {
  563. $response = new WP_REST_Response();
  564. $response->set_data(
  565. array(
  566. 'meta' => array(
  567. 'key1' => 1,
  568. 'key2' => 2,
  569. ),
  570. )
  571. );
  572. $request = array(
  573. '_fields' => 'meta',
  574. );
  575. $response = rest_filter_response_fields( $response, null, $request );
  576. $this->assertSame(
  577. array(
  578. 'meta' => array(
  579. 'key1' => 1,
  580. 'key2' => 2,
  581. ),
  582. ),
  583. $response->get_data()
  584. );
  585. }
  586. /**
  587. * Ensure that a top-level key in _fields supersedes any specified children of that field.
  588. *
  589. * @ticket 48266
  590. */
  591. public function test_rest_filter_response_fields_child_after_parent() {
  592. $response = new WP_REST_Response();
  593. $response->set_data(
  594. array(
  595. 'meta' => array(
  596. 'key1' => 1,
  597. 'key2' => 2,
  598. ),
  599. )
  600. );
  601. $request = array(
  602. '_fields' => 'meta,meta.key1',
  603. );
  604. $response = rest_filter_response_fields( $response, null, $request );
  605. $this->assertSame(
  606. array(
  607. 'meta' => array(
  608. 'key1' => 1,
  609. 'key2' => 2,
  610. ),
  611. ),
  612. $response->get_data()
  613. );
  614. }
  615. /**
  616. * Ensure that specifying two sibling properties in _fields causes both to be included.
  617. *
  618. * @ticket 48266
  619. */
  620. public function test_rest_filter_response_fields_include_all_specified_siblings() {
  621. $response = new WP_REST_Response();
  622. $response->set_data(
  623. array(
  624. 'meta' => array(
  625. 'key1' => 1,
  626. 'key2' => 2,
  627. ),
  628. )
  629. );
  630. $request = array(
  631. '_fields' => 'meta.key1,meta.key2',
  632. );
  633. $response = rest_filter_response_fields( $response, null, $request );
  634. $this->assertSame(
  635. array(
  636. 'meta' => array(
  637. 'key1' => 1,
  638. 'key2' => 2,
  639. ),
  640. ),
  641. $response->get_data()
  642. );
  643. }
  644. /**
  645. * @ticket 42094
  646. */
  647. public function test_rest_is_field_included() {
  648. $fields = array(
  649. 'id',
  650. 'title',
  651. 'content.raw',
  652. 'custom.property',
  653. );
  654. $this->assertTrue( rest_is_field_included( 'id', $fields ) );
  655. $this->assertTrue( rest_is_field_included( 'title', $fields ) );
  656. $this->assertTrue( rest_is_field_included( 'title.raw', $fields ) );
  657. $this->assertTrue( rest_is_field_included( 'title.rendered', $fields ) );
  658. $this->assertTrue( rest_is_field_included( 'content', $fields ) );
  659. $this->assertTrue( rest_is_field_included( 'content.raw', $fields ) );
  660. $this->assertTrue( rest_is_field_included( 'custom.property', $fields ) );
  661. $this->assertFalse( rest_is_field_included( 'content.rendered', $fields ) );
  662. $this->assertFalse( rest_is_field_included( 'type', $fields ) );
  663. $this->assertFalse( rest_is_field_included( 'meta', $fields ) );
  664. $this->assertFalse( rest_is_field_included( 'meta.value', $fields ) );
  665. }
  666. /**
  667. * The get_rest_url function should return a URL consistently terminated with a "/",
  668. * whether the blog is configured with pretty permalink support or not.
  669. */
  670. public function test_rest_url_generation() {
  671. // In pretty permalinks case, we expect a path of wp-json/ with no query.
  672. $this->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' );
  673. $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/wp-json/', get_rest_url() );
  674. // In index permalinks case, we expect a path of index.php/wp-json/ with no query.
  675. $this->set_permalink_structure( '/index.php/%year%/%monthnum%/%day%/%postname%/' );
  676. $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/index.php/wp-json/', get_rest_url() );
  677. // In non-pretty case, we get a query string to invoke the rest router.
  678. $this->set_permalink_structure( '' );
  679. $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/index.php?rest_route=/', get_rest_url() );
  680. }
  681. /**
  682. * @ticket 34299
  683. */
  684. public function test_rest_url_scheme() {
  685. $_SERVER['SERVER_NAME'] = parse_url( home_url(), PHP_URL_HOST );
  686. $_siteurl = get_option( 'siteurl' );
  687. set_current_screen( 'edit.php' );
  688. $this->assertTrue( is_admin() );
  689. // Test an HTTP URL.
  690. unset( $_SERVER['HTTPS'] );
  691. $url = get_rest_url();
  692. $this->assertSame( 'http', parse_url( $url, PHP_URL_SCHEME ) );
  693. // Test an HTTPS URL.
  694. $_SERVER['HTTPS'] = 'on';
  695. $url = get_rest_url();
  696. $this->assertSame( 'https', parse_url( $url, PHP_URL_SCHEME ) );
  697. // Switch to an admin request on a different domain name.
  698. $_SERVER['SERVER_NAME'] = 'admin.example.org';
  699. update_option( 'siteurl', 'http://admin.example.org' );
  700. $this->assertNotEquals( $_SERVER['SERVER_NAME'], parse_url( home_url(), PHP_URL_HOST ) );
  701. // Test an HTTP URL.
  702. unset( $_SERVER['HTTPS'] );
  703. $url = get_rest_url();
  704. $this->assertSame( 'http', parse_url( $url, PHP_URL_SCHEME ) );
  705. // Test an HTTPS URL.
  706. $_SERVER['HTTPS'] = 'on';
  707. $url = get_rest_url();
  708. $this->assertSame( 'https', parse_url( $url, PHP_URL_SCHEME ) );
  709. // Reset.
  710. update_option( 'siteurl', $_siteurl );
  711. set_current_screen( 'front' );
  712. }
  713. /**
  714. * @ticket 42452
  715. */
  716. public function test_always_prepend_path_with_slash_in_rest_url_filter() {
  717. $filter = new MockAction();
  718. add_filter( 'rest_url', array( $filter, 'filter' ), 10, 2 );
  719. // Passing no path should return a slash.
  720. get_rest_url();
  721. $args = $filter->get_args();
  722. $this->assertSame( '/', $args[0][1] );
  723. $filter->reset();
  724. // Paths without a prepended slash should have one added.
  725. get_rest_url( null, 'wp/media/' );
  726. $args = $filter->get_args();
  727. $this->assertSame( '/wp/media/', $args[0][1] );
  728. $filter->reset();
  729. // Do not modify paths with a prepended slash.
  730. get_rest_url( null, '/wp/media/' );
  731. $args = $filter->get_args();
  732. $this->assertSame( '/wp/media/', $args[0][1] );
  733. unset( $filter );
  734. }
  735. public function jsonp_callback_provider() {
  736. return array(
  737. // Standard names.
  738. array( 'Springfield', true ),
  739. array( 'shelby.ville', true ),
  740. array( 'cypress_creek', true ),
  741. array( 'KampKrusty1', true ),
  742. // Invalid names.
  743. array( 'ogden-ville', false ),
  744. array( 'north haverbrook', false ),
  745. array( "Terror['Lake']", false ),
  746. array( 'Cape[Feare]', false ),
  747. array( '"NewHorrorfield"', false ),
  748. array( 'Scream\\ville', false ),
  749. );
  750. }
  751. /**
  752. * @dataProvider jsonp_callback_provider
  753. */
  754. public function test_jsonp_callback_check( $callback, $valid ) {
  755. $this->assertSame( $valid, wp_check_jsonp_callback( $callback ) );
  756. }
  757. public function rest_date_provider() {
  758. return array(
  759. // Valid dates with timezones.
  760. array( '2017-01-16T11:30:00-05:00', gmmktime( 11, 30, 0, 1, 16, 2017 ) + 5 * HOUR_IN_SECONDS ),
  761. array( '2017-01-16T11:30:00-05:30', gmmktime( 11, 30, 0, 1, 16, 2017 ) + 5.5 * HOUR_IN_SECONDS ),
  762. array( '2017-01-16T11:30:00-05', gmmktime( 11, 30, 0, 1, 16, 2017 ) + 5 * HOUR_IN_SECONDS ),
  763. array( '2017-01-16T11:30:00+05', gmmktime( 11, 30, 0, 1, 16, 2017 ) - 5 * HOUR_IN_SECONDS ),
  764. array( '2017-01-16T11:30:00-00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  765. array( '2017-01-16T11:30:00+00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  766. array( '2017-01-16T11:30:00Z', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  767. // Valid dates without timezones.
  768. array( '2017-01-16T11:30:00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  769. // Invalid dates (TODO: support parsing partial dates as ranges, see #38641).
  770. array( '2017-01-16T11:30:00-5', false ),
  771. array( '2017-01-16T11:30', false ),
  772. array( '2017-01-16T11', false ),
  773. array( '2017-01-16T', false ),
  774. array( '2017-01-16', false ),
  775. array( '2017-01', false ),
  776. array( '2017', false ),
  777. );
  778. }
  779. /**
  780. * @dataProvider rest_date_provider
  781. */
  782. public function test_rest_parse_date( $string, $value ) {
  783. $this->assertEquals( $value, rest_parse_date( $string ) );
  784. }
  785. public function rest_date_force_utc_provider() {
  786. return array(
  787. // Valid dates with timezones.
  788. array( '2017-01-16T11:30:00-05:00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  789. array( '2017-01-16T11:30:00-05:30', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  790. array( '2017-01-16T11:30:00-05', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  791. array( '2017-01-16T11:30:00+05', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  792. array( '2017-01-16T11:30:00-00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  793. array( '2017-01-16T11:30:00+00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  794. array( '2017-01-16T11:30:00Z', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  795. // Valid dates without timezones.
  796. array( '2017-01-16T11:30:00', gmmktime( 11, 30, 0, 1, 16, 2017 ) ),
  797. // Invalid dates (TODO: support parsing partial dates as ranges, see #38641).
  798. array( '2017-01-16T11:30:00-5', false ),
  799. array( '2017-01-16T11:30', false ),
  800. array( '2017-01-16T11', false ),
  801. array( '2017-01-16T', false ),
  802. array( '2017-01-16', false ),
  803. array( '2017-01', false ),
  804. array( '2017', false ),
  805. );
  806. }
  807. /**
  808. * @dataProvider rest_date_force_utc_provider
  809. */
  810. public function test_rest_parse_date_force_utc( $string, $value ) {
  811. $this->assertSame( $value, rest_parse_date( $string, true ) );
  812. }
  813. public function filter_wp_rest_server_class( $class_name ) {
  814. return 'Spy_REST_Server';
  815. }
  816. public function test_register_rest_route_without_server() {
  817. $GLOBALS['wp_rest_server'] = null;
  818. add_filter( 'wp_rest_server_class', array( $this, 'filter_wp_rest_server_class' ) );
  819. register_rest_route(
  820. 'test-ns',
  821. '/test',
  822. array(
  823. 'methods' => array( 'GET' ),
  824. 'callback' => '__return_null',
  825. 'permission_callback' => '__return_true',
  826. )
  827. );
  828. $routes = $GLOBALS['wp_rest_server']->get_routes();
  829. $this->assertSame( $routes['/test-ns/test'][0]['methods'], array( 'GET' => true ) );
  830. }
  831. function test_rest_preload_api_request_with_method() {
  832. $rest_server = $GLOBALS['wp_rest_server'];
  833. $GLOBALS['wp_rest_server'] = null;
  834. $preload_paths = array(
  835. '/wp/v2/types',
  836. array( '/wp/v2/media', 'OPTIONS' ),
  837. );
  838. $preload_data = array_reduce(
  839. $preload_paths,
  840. 'rest_preload_api_request',
  841. array()
  842. );
  843. $this->assertSame( array_keys( $preload_data ), array( '/wp/v2/types', 'OPTIONS' ) );
  844. $this->assertTrue( isset( $preload_data['OPTIONS']['/wp/v2/media'] ) );
  845. $GLOBALS['wp_rest_server'] = $rest_server;
  846. }
  847. /**
  848. * @ticket 40614
  849. */
  850. function test_rest_ensure_request_accepts_path_string() {
  851. $request = rest_ensure_request( '/wp/v2/posts' );
  852. $this->assertInstanceOf( 'WP_REST_Request', $request );
  853. $this->assertSame( '/wp/v2/posts', $request->get_route() );
  854. $this->assertSame( 'GET', $request->get_method() );
  855. }
  856. /**
  857. * @dataProvider _dp_rest_parse_embed_param
  858. */
  859. public function test_rest_parse_embed_param( $expected, $embed ) {
  860. $this->assertSame( $expected, rest_parse_embed_param( $embed ) );
  861. }
  862. public function _dp_rest_parse_embed_param() {
  863. return array(
  864. array( true, '' ),
  865. array( true, null ),
  866. array( true, '1' ),
  867. array( true, 'true' ),
  868. array( true, array() ),
  869. array( array( 'author' ), 'author' ),
  870. array( array( 'author', 'replies' ), 'author,replies' ),
  871. array( array( 'author', 'replies' ), 'author,replies ' ),
  872. array( array( 'wp:term' ), 'wp:term' ),
  873. array( array( 'wp:term', 'wp:attachment' ), 'wp:term,wp:attachment' ),
  874. array( array( 'author' ), array( 'author' ) ),
  875. array( array( 'author', 'replies' ), array( 'author', 'replies' ) ),
  876. array( array( 'https://api.w.org/term' ), 'https://api.w.org/term' ),
  877. array( array( 'https://api.w.org/term', 'https://api.w.org/attachment' ), 'https://api.w.org/term,https://api.w.org/attachment' ),
  878. array( array( 'https://api.w.org/term', 'https://api.w.org/attachment' ), array( 'https://api.w.org/term', 'https://api.w.org/attachment' ) ),
  879. );
  880. }
  881. /**
  882. * @ticket 48819
  883. *
  884. * @dataProvider _dp_rest_filter_response_by_context
  885. */
  886. public function test_rest_filter_response_by_context( $schema, $data, $expected ) {
  887. $this->assertSame( $expected, rest_filter_response_by_context( $data, $schema, 'view' ) );
  888. }
  889. /**
  890. * @ticket 49749
  891. */
  892. public function test_register_route_with_invalid_namespace() {
  893. $this->setExpectedIncorrectUsage( 'register_rest_route' );
  894. register_rest_route(
  895. '/my-namespace/v1/',
  896. '/my-route',
  897. array(
  898. 'callback' => '__return_true',
  899. 'permission_callback' => '__return_true',
  900. )
  901. );
  902. $routes = rest_get_server()->get_routes( 'my-namespace/v1' );
  903. $this->assertCount( 2, $routes );
  904. $this->assertTrue( rest_do_request( '/my-namespace/v1/my-route' )->get_data() );
  905. }
  906. /**
  907. * @ticket 50075
  908. */
  909. public function test_register_route_with_missing_permission_callback_top_level_route() {
  910. $this->setExpectedIncorrectUsage( 'register_rest_route' );
  911. $registered = register_rest_route(
  912. 'my-ns/v1',
  913. '/my-route',
  914. array(
  915. 'callback' => '__return_true',
  916. )
  917. );
  918. $this->assertTrue( $registered );
  919. }
  920. /**
  921. * @ticket 50075
  922. */
  923. public function test_register_route_with_missing_permission_callback_single_wrapped_route() {
  924. $this->setExpectedIncorrectUsage( 'register_rest_route' );
  925. $registered = register_rest_route(
  926. 'my-ns/v1',
  927. '/my-route',
  928. array(
  929. array(
  930. 'callback' => '__return_true',
  931. ),
  932. )
  933. );
  934. $this->assertTrue( $registered );
  935. }
  936. /**
  937. * @ticket 50075
  938. */
  939. public function test_register_route_with_missing_permission_callback_multiple_wrapped_route() {
  940. $this->setExpectedIncorrectUsage( 'register_rest_route' );
  941. $registered = register_rest_route(
  942. 'my-ns/v1',
  943. '/my-route',
  944. array(
  945. array(
  946. 'callback' => '__return_true',
  947. ),
  948. array(
  949. 'callback' => '__return_true',
  950. 'permission_callback' => '__return_true',
  951. ),
  952. )
  953. );
  954. $this->assertTrue( $registered );
  955. }
  956. public function _dp_rest_filter_response_by_context() {
  957. return array(
  958. 'default' => array(
  959. array(
  960. '$schema' => 'http://json-schema.org/draft-04/schema#',
  961. 'type' => 'object',
  962. 'properties' => array(
  963. 'first' => array(
  964. 'type' => 'string',
  965. 'context' => array( 'view', 'edit' ),
  966. ),
  967. 'second' => array(
  968. 'type' => 'string',
  969. 'context' => array( 'edit' ),
  970. ),
  971. ),
  972. ),
  973. array(
  974. 'first' => 'a',
  975. 'second' => 'b',
  976. ),
  977. array( 'first' => 'a' ),
  978. ),
  979. 'keeps missing context' => array(
  980. array(
  981. '$schema' => 'http://json-schema.org/draft-04/schema#',
  982. 'type' => 'object',
  983. 'properties' => array(
  984. 'first' => array(
  985. 'type' => 'string',
  986. 'context' => array( 'view', 'edit' ),
  987. ),
  988. 'second' => array(
  989. 'type' => 'string',
  990. ),
  991. ),
  992. ),
  993. array(
  994. 'first' => 'a',
  995. 'second' => 'b',
  996. ),
  997. array(
  998. 'first' => 'a',
  999. 'second' => 'b',
  1000. ),
  1001. ),
  1002. 'removes empty context' => array(
  1003. array(
  1004. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1005. 'type' => 'object',
  1006. 'properties' => array(
  1007. 'first' => array(
  1008. 'type' => 'string',
  1009. 'context' => array( 'view', 'edit' ),
  1010. ),
  1011. 'second' => array(
  1012. 'type' => 'string',
  1013. 'context' => array(),
  1014. ),
  1015. ),
  1016. ),
  1017. array(
  1018. 'first' => 'a',
  1019. 'second' => 'b',
  1020. ),
  1021. array( 'first' => 'a' ),
  1022. ),
  1023. 'nested properties' => array(
  1024. array(
  1025. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1026. 'type' => 'object',
  1027. 'properties' => array(
  1028. 'parent' => array(
  1029. 'type' => 'object',
  1030. 'context' => array( 'view', 'edit' ),
  1031. 'properties' => array(
  1032. 'child' => array(
  1033. 'type' => 'string',
  1034. 'context' => array( 'view', 'edit' ),
  1035. ),
  1036. 'hidden' => array(
  1037. 'type' => 'string',
  1038. 'context' => array( 'edit' ),
  1039. ),
  1040. ),
  1041. ),
  1042. ),
  1043. ),
  1044. array(
  1045. 'parent' => array(
  1046. 'child' => 'hi',
  1047. 'hidden' => 'there',
  1048. ),
  1049. ),
  1050. array( 'parent' => array( 'child' => 'hi' ) ),
  1051. ),
  1052. 'grand child properties' => array(
  1053. array(
  1054. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1055. 'type' => 'object',
  1056. 'properties' => array(
  1057. 'parent' => array(
  1058. 'type' => 'object',
  1059. 'context' => array( 'view', 'edit' ),
  1060. 'properties' => array(
  1061. 'child' => array(
  1062. 'type' => 'object',
  1063. 'context' => array( 'view', 'edit' ),
  1064. 'properties' => array(
  1065. 'grand' => array(
  1066. 'type' => 'string',
  1067. 'context' => array( 'view', 'edit' ),
  1068. ),
  1069. 'hidden' => array(
  1070. 'type' => 'string',
  1071. 'context' => array( 'edit' ),
  1072. ),
  1073. ),
  1074. ),
  1075. ),
  1076. ),
  1077. ),
  1078. ),
  1079. array(
  1080. 'parent' => array(
  1081. 'child' => array(
  1082. 'grand' => 'hi',
  1083. ),
  1084. ),
  1085. ),
  1086. array( 'parent' => array( 'child' => array( 'grand' => 'hi' ) ) ),
  1087. ),
  1088. 'array' => array(
  1089. array(
  1090. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1091. 'type' => 'object',
  1092. 'properties' => array(
  1093. 'arr' => array(
  1094. 'type' => 'array',
  1095. 'context' => array( 'view', 'edit' ),
  1096. 'items' => array(
  1097. 'type' => 'object',
  1098. 'context' => array( 'view', 'edit' ),
  1099. 'properties' => array(
  1100. 'visible' => array(
  1101. 'type' => 'string',
  1102. 'context' => array( 'view', 'edit' ),
  1103. ),
  1104. 'hidden' => array(
  1105. 'type' => 'string',
  1106. 'context' => array( 'edit' ),
  1107. ),
  1108. ),
  1109. ),
  1110. ),
  1111. ),
  1112. ),
  1113. array(
  1114. 'arr' => array(
  1115. array(
  1116. 'visible' => 'hi',
  1117. 'hidden' => 'there',
  1118. ),
  1119. ),
  1120. ),
  1121. array( 'arr' => array( array( 'visible' => 'hi' ) ) ),
  1122. ),
  1123. 'additional properties' => array(
  1124. array(
  1125. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1126. 'type' => 'object',
  1127. 'properties' => array(
  1128. 'additional' => array(
  1129. 'type' => 'object',
  1130. 'context' => array( 'view', 'edit' ),
  1131. 'properties' => array(
  1132. 'a' => array(
  1133. 'type' => 'string',
  1134. 'context' => array( 'view', 'edit' ),
  1135. ),
  1136. 'b' => array(
  1137. 'type' => 'string',
  1138. 'context' => array( 'edit' ),
  1139. ),
  1140. ),
  1141. 'additionalProperties' => array(
  1142. 'type' => 'string',
  1143. 'context' => array( 'edit' ),
  1144. ),
  1145. ),
  1146. ),
  1147. ),
  1148. array(
  1149. 'additional' => array(
  1150. 'a' => '1',
  1151. 'b' => '2',
  1152. 'c' => '3',
  1153. ),
  1154. ),
  1155. array( 'additional' => array( 'a' => '1' ) ),
  1156. ),
  1157. 'pattern properties' => array(
  1158. array(
  1159. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1160. 'type' => 'object',
  1161. 'properties' => array(
  1162. 'a' => array(
  1163. 'type' => 'string',
  1164. 'context' => array( 'view', 'edit' ),
  1165. ),
  1166. ),
  1167. 'patternProperties' => array(
  1168. '[0-9]' => array(
  1169. 'type' => 'string',
  1170. 'context' => array( 'view', 'edit' ),
  1171. ),
  1172. 'c.*' => array(
  1173. 'type' => 'string',
  1174. 'context' => array( 'edit' ),
  1175. ),
  1176. ),
  1177. 'additionalProperties' => array(
  1178. 'type' => 'string',
  1179. 'context' => array( 'edit' ),
  1180. ),
  1181. ),
  1182. array(
  1183. 'a' => '1',
  1184. 'b' => '2',
  1185. '0' => '3',
  1186. 'ca' => '4',
  1187. ),
  1188. array(
  1189. 'a' => '1',
  1190. '0' => '3',
  1191. ),
  1192. ),
  1193. 'multiple types object' => array(
  1194. array(
  1195. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1196. 'type' => 'object',
  1197. 'properties' => array(
  1198. 'multi' => array(
  1199. 'type' => array( 'object', 'string' ),
  1200. 'context' => array( 'view', 'edit' ),
  1201. 'properties' => array(
  1202. 'a' => array(
  1203. 'type' => 'string',
  1204. 'context' => array( 'view', 'edit' ),
  1205. ),
  1206. 'b' => array(
  1207. 'type' => 'string',
  1208. 'context' => array( 'edit' ),
  1209. ),
  1210. ),
  1211. ),
  1212. ),
  1213. ),
  1214. array(
  1215. 'multi' => array(
  1216. 'a' => '1',
  1217. 'b' => '2',
  1218. ),
  1219. ),
  1220. array( 'multi' => array( 'a' => '1' ) ),
  1221. ),
  1222. 'multiple types array' => array(
  1223. array(
  1224. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1225. 'type' => 'object',
  1226. 'properties' => array(
  1227. 'multi' => array(
  1228. 'type' => array( 'array', 'string' ),
  1229. 'context' => array( 'view', 'edit' ),
  1230. 'items' => array(
  1231. 'type' => 'object',
  1232. 'context' => array( 'view', 'edit' ),
  1233. 'properties' => array(
  1234. 'visible' => array(
  1235. 'type' => 'string',
  1236. 'context' => array( 'view', 'edit' ),
  1237. ),
  1238. 'hidden' => array(
  1239. 'type' => 'string',
  1240. 'context' => array( 'edit' ),
  1241. ),
  1242. ),
  1243. ),
  1244. ),
  1245. ),
  1246. ),
  1247. array(
  1248. 'multi' => array(
  1249. array(
  1250. 'visible' => '1',
  1251. 'hidden' => '2',
  1252. ),
  1253. ),
  1254. ),
  1255. array( 'multi' => array( array( 'visible' => '1' ) ) ),
  1256. ),
  1257. 'does not traverse missing context' => array(
  1258. array(
  1259. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1260. 'type' => 'object',
  1261. 'properties' => array(
  1262. 'parent' => array(
  1263. 'type' => 'object',
  1264. 'context' => array( 'view', 'edit' ),
  1265. 'properties' => array(
  1266. 'child' => array(
  1267. 'type' => 'object',
  1268. 'properties' => array(
  1269. 'grand' => array(
  1270. 'type' => 'string',
  1271. 'context' => array( 'view', 'edit' ),
  1272. ),
  1273. 'hidden' => array(
  1274. 'type' => 'string',
  1275. 'context' => array( 'edit' ),
  1276. ),
  1277. ),
  1278. ),
  1279. ),
  1280. ),
  1281. ),
  1282. ),
  1283. array(
  1284. 'parent' => array(
  1285. 'child' => array(
  1286. 'grand' => 'hi',
  1287. 'hidden' => 'there',
  1288. ),
  1289. ),
  1290. ),
  1291. array(
  1292. 'parent' => array(
  1293. 'child' => array(
  1294. 'grand' => 'hi',
  1295. 'hidden' => 'there',
  1296. ),
  1297. ),
  1298. ),
  1299. ),
  1300. 'object with no matching properties' => array(
  1301. array(
  1302. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1303. 'type' => 'object',
  1304. 'properties' => array(
  1305. 'a' => array(
  1306. 'type' => 'string',
  1307. 'context' => array( 'edit' ),
  1308. ),
  1309. 'b' => array(
  1310. 'type' => 'string',
  1311. 'context' => array( 'edit' ),
  1312. ),
  1313. ),
  1314. ),
  1315. array(
  1316. 'a' => 'hi',
  1317. 'b' => 'hello',
  1318. ),
  1319. array(),
  1320. ),
  1321. 'array whose type does not match' => array(
  1322. array(
  1323. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1324. 'type' => 'object',
  1325. 'properties' => array(
  1326. 'arr' => array(
  1327. 'type' => 'array',
  1328. 'context' => array( 'view' ),
  1329. 'items' => array(
  1330. 'type' => 'string',
  1331. 'context' => array( 'edit' ),
  1332. ),
  1333. ),
  1334. ),
  1335. ),
  1336. array(
  1337. 'arr' => array( 'foo', 'bar', 'baz' ),
  1338. ),
  1339. array( 'arr' => array() ),
  1340. ),
  1341. 'array and object type passed object' => array(
  1342. array(
  1343. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1344. 'type' => array( 'array', 'object' ),
  1345. 'properties' => array(
  1346. 'a' => array(
  1347. 'type' => 'string',
  1348. 'context' => array( 'view' ),
  1349. ),
  1350. 'b' => array(
  1351. 'type' => 'string',
  1352. 'context' => array( 'view' ),
  1353. ),
  1354. ),
  1355. 'items' => array(
  1356. 'type' => 'object',
  1357. 'context' => array( 'edit' ),
  1358. 'properties' => array(
  1359. 'a' => array(
  1360. 'type' => 'string',
  1361. 'context' => array( 'view' ),
  1362. ),
  1363. 'b' => array(
  1364. 'type' => 'string',
  1365. 'context' => array( 'view' ),
  1366. ),
  1367. ),
  1368. ),
  1369. ),
  1370. array(
  1371. 'a' => 'foo',
  1372. 'b' => 'bar',
  1373. ),
  1374. array(
  1375. 'a' => 'foo',
  1376. 'b' => 'bar',
  1377. ),
  1378. ),
  1379. 'array and object type passed array' => array(
  1380. array(
  1381. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1382. 'type' => array( 'array', 'object' ),
  1383. 'properties' => array(
  1384. 'a' => array(
  1385. 'type' => 'string',
  1386. 'context' => array( 'view' ),
  1387. ),
  1388. 'b' => array(
  1389. 'type' => 'string',
  1390. 'context' => array( 'view' ),
  1391. ),
  1392. ),
  1393. 'items' => array(
  1394. 'type' => 'object',
  1395. 'context' => array( 'edit' ),
  1396. 'properties' => array(
  1397. 'a' => array(
  1398. 'type' => 'string',
  1399. 'context' => array( 'view' ),
  1400. ),
  1401. 'b' => array(
  1402. 'type' => 'string',
  1403. 'context' => array( 'view' ),
  1404. ),
  1405. ),
  1406. ),
  1407. ),
  1408. array(
  1409. array(
  1410. 'a' => 'foo',
  1411. 'b' => 'bar',
  1412. ),
  1413. array(
  1414. 'a' => 'foo',
  1415. 'b' => 'bar',
  1416. ),
  1417. ),
  1418. array(),
  1419. ),
  1420. 'anyOf applies the correct schema' => array(
  1421. array(
  1422. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1423. 'type' => 'object',
  1424. 'anyOf' => array(
  1425. array(
  1426. 'properties' => array(
  1427. 'a' => array(
  1428. 'type' => 'string',
  1429. 'context' => array( 'view' ),
  1430. ),
  1431. 'b' => array(
  1432. 'type' => 'string',
  1433. 'context' => array( 'edit' ),
  1434. ),
  1435. ),
  1436. ),
  1437. array(
  1438. 'properties' => array(
  1439. 'a' => array(
  1440. 'type' => 'integer',
  1441. 'context' => array( 'edit' ),
  1442. ),
  1443. 'b' => array(
  1444. 'type' => 'integer',
  1445. 'context' => array( 'view' ),
  1446. ),
  1447. ),
  1448. ),
  1449. ),
  1450. ),
  1451. array(
  1452. 'a' => 1,
  1453. 'b' => 2,
  1454. ),
  1455. array(
  1456. 'b' => 2,
  1457. ),
  1458. ),
  1459. 'anyOf is ignored if no valid schema is found' => array(
  1460. array(
  1461. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1462. 'type' => 'object',
  1463. 'anyOf' => array(
  1464. array(
  1465. 'properties' => array(
  1466. 'a' => array(
  1467. 'type' => 'string',
  1468. 'context' => array( 'view' ),
  1469. ),
  1470. 'b' => array(
  1471. 'type' => 'string',
  1472. 'context' => array( 'edit' ),
  1473. ),
  1474. ),
  1475. ),
  1476. array(
  1477. 'properties' => array(
  1478. 'a' => array(
  1479. 'type' => 'integer',
  1480. 'context' => array( 'edit' ),
  1481. ),
  1482. 'b' => array(
  1483. 'type' => 'integer',
  1484. 'context' => array( 'view' ),
  1485. ),
  1486. ),
  1487. ),
  1488. ),
  1489. ),
  1490. array(
  1491. 'a' => true,
  1492. 'b' => false,
  1493. ),
  1494. array(
  1495. 'a' => true,
  1496. 'b' => false,
  1497. ),
  1498. ),
  1499. 'oneOf applies the correct schema' => array(
  1500. array(
  1501. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1502. 'type' => 'object',
  1503. 'oneOf' => array(
  1504. array(
  1505. 'properties' => array(
  1506. 'a' => array(
  1507. 'type' => 'string',
  1508. 'context' => array( 'view' ),
  1509. ),
  1510. 'b' => array(
  1511. 'type' => 'string',
  1512. 'context' => array( 'edit' ),
  1513. ),
  1514. ),
  1515. ),
  1516. array(
  1517. 'properties' => array(
  1518. 'a' => array(
  1519. 'type' => 'integer',
  1520. 'context' => array( 'edit' ),
  1521. ),
  1522. 'b' => array(
  1523. 'type' => 'integer',
  1524. 'context' => array( 'view' ),
  1525. ),
  1526. ),
  1527. ),
  1528. ),
  1529. ),
  1530. array(
  1531. 'a' => 1,
  1532. 'b' => 2,
  1533. ),
  1534. array(
  1535. 'b' => 2,
  1536. ),
  1537. ),
  1538. 'oneOf ignored if no valid schema was found' => array(
  1539. array(
  1540. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1541. 'type' => 'object',
  1542. 'anyOf' => array(
  1543. array(
  1544. 'properties' => array(
  1545. 'a' => array(
  1546. 'type' => 'string',
  1547. 'context' => array( 'view' ),
  1548. ),
  1549. 'b' => array(
  1550. 'type' => 'string',
  1551. 'context' => array( 'edit' ),
  1552. ),
  1553. ),
  1554. ),
  1555. array(
  1556. 'properties' => array(
  1557. 'a' => array(
  1558. 'type' => 'integer',
  1559. 'context' => array( 'edit' ),
  1560. ),
  1561. 'b' => array(
  1562. 'type' => 'integer',
  1563. 'context' => array( 'view' ),
  1564. ),
  1565. ),
  1566. ),
  1567. ),
  1568. ),
  1569. array(
  1570. 'a' => true,
  1571. 'b' => false,
  1572. ),
  1573. array(
  1574. 'a' => true,
  1575. 'b' => false,
  1576. ),
  1577. ),
  1578. 'oneOf combined with base' => array(
  1579. array(
  1580. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1581. 'type' => 'object',
  1582. 'properties' => array(
  1583. 'c' => array(
  1584. 'type' => 'integer',
  1585. 'context' => array( 'edit' ),
  1586. ),
  1587. ),
  1588. 'oneOf' => array(
  1589. array(
  1590. 'properties' => array(
  1591. 'a' => array(
  1592. 'type' => 'string',
  1593. 'context' => array( 'view' ),
  1594. ),
  1595. 'b' => array(
  1596. 'type' => 'string',
  1597. 'context' => array( 'edit' ),
  1598. ),
  1599. ),
  1600. ),
  1601. array(
  1602. 'properties' => array(
  1603. 'a' => array(
  1604. 'type' => 'integer',
  1605. 'context' => array( 'edit' ),
  1606. ),
  1607. 'b' => array(
  1608. 'type' => 'integer',
  1609. 'context' => array( 'view' ),
  1610. ),
  1611. ),
  1612. ),
  1613. ),
  1614. ),
  1615. array(
  1616. 'a' => 1,
  1617. 'b' => 2,
  1618. 'c' => 3,
  1619. ),
  1620. array(
  1621. 'b' => 2,
  1622. ),
  1623. ),
  1624. );
  1625. }
  1626. function test_rest_ensure_response_accepts_wp_error_and_returns_wp_error() {
  1627. $response = rest_ensure_response( new WP_Error() );
  1628. $this->assertInstanceOf( 'WP_Error', $response );
  1629. }
  1630. /**
  1631. * @dataProvider rest_ensure_response_data_provider
  1632. *
  1633. * @param mixed $response The response passed to rest_ensure_response().
  1634. * @param mixed $expected_data The expected data a response should include.
  1635. */
  1636. function test_rest_ensure_response_returns_instance_of_wp_rest_response( $response, $expected_data ) {
  1637. $response_object = rest_ensure_response( $response );
  1638. $this->assertInstanceOf( 'WP_REST_Response', $response_object );
  1639. $this->assertSame( $expected_data, $response_object->get_data() );
  1640. }
  1641. /**
  1642. * Data provider for test_rest_ensure_response_returns_instance_of_wp_rest_response().
  1643. *
  1644. * @return array
  1645. */
  1646. function rest_ensure_response_data_provider() {
  1647. return array(
  1648. array( null, null ),
  1649. array( array( 'chocolate' => 'cookies' ), array( 'chocolate' => 'cookies' ) ),
  1650. array( 123, 123 ),
  1651. array( true, true ),
  1652. array( 'chocolate', 'chocolate' ),
  1653. array( new WP_HTTP_Response( 'http' ), 'http' ),
  1654. array( new WP_REST_Response( 'rest' ), 'rest' ),
  1655. );
  1656. }
  1657. /**
  1658. * @ticket 49116
  1659. */
  1660. public function test_rest_get_route_for_post_non_post() {
  1661. $this->assertSame( '', rest_get_route_for_post( 'garbage' ) );
  1662. }
  1663. /**
  1664. * @ticket 49116
  1665. */
  1666. public function test_rest_get_route_for_post_invalid_post_type() {
  1667. register_post_type( 'invalid' );
  1668. $post = self::factory()->post->create_and_get( array( 'post_type' => 'invalid' ) );
  1669. unregister_post_type( 'invalid' );
  1670. $this->assertSame( '', rest_get_route_for_post( $post ) );
  1671. }
  1672. /**
  1673. * @ticket 49116
  1674. */
  1675. public function test_rest_get_route_for_post_non_rest() {
  1676. $post = self::factory()->post->create_and_get( array( 'post_type' => 'custom_css' ) );
  1677. $this->assertSame( '', rest_get_route_for_post( $post ) );
  1678. }
  1679. /**
  1680. * @ticket 49116
  1681. */
  1682. public function test_rest_get_route_for_post_custom_controller() {
  1683. $post = self::factory()->post->create_and_get( array( 'post_type' => 'wp_block' ) );
  1684. $this->assertSame( '', rest_get_route_for_post( $post ) );
  1685. }
  1686. /**
  1687. * @ticket 49116
  1688. */
  1689. public function test_rest_get_route_for_post() {
  1690. $post = self::factory()->post->create_and_get();
  1691. $this->assertSame( '/wp/v2/posts/' . $post->ID, rest_get_route_for_post( $post ) );
  1692. }
  1693. /**
  1694. * @ticket 49116
  1695. */
  1696. public function test_rest_get_route_for_media() {
  1697. $post = self::factory()->attachment->create_and_get();
  1698. $this->assertSame( '/wp/v2/media/' . $post->ID, rest_get_route_for_post( $post ) );
  1699. }
  1700. /**
  1701. * @ticket 49116
  1702. */
  1703. public function test_rest_get_route_for_post_id() {
  1704. $post = self::factory()->post->create_and_get();
  1705. $this->assertSame( '/wp/v2/posts/' . $post->ID, rest_get_route_for_post( $post->ID ) );
  1706. }
  1707. /**
  1708. * @ticket 49116
  1709. */
  1710. public function test_rest_get_route_for_term_non_term() {
  1711. $this->assertSame( '', rest_get_route_for_term( 'garbage' ) );
  1712. }
  1713. /**
  1714. * @ticket 49116
  1715. */
  1716. public function test_rest_get_route_for_term_invalid_term_type() {
  1717. register_taxonomy( 'invalid', 'post' );
  1718. $term = self::factory()->term->create_and_get( array( 'taxonomy' => 'invalid' ) );
  1719. unregister_taxonomy( 'invalid' );
  1720. $this->assertSame( '', rest_get_route_for_term( $term ) );
  1721. }
  1722. /**
  1723. * @ticket 49116
  1724. */
  1725. public function test_rest_get_route_for_term_non_rest() {
  1726. $term = self::factory()->term->create_and_get( array( 'taxonomy' => 'post_format' ) );
  1727. $this->assertSame( '', rest_get_route_for_term( $term ) );
  1728. }
  1729. /**
  1730. * @ticket 49116
  1731. */
  1732. public function test_rest_get_route_for_term() {
  1733. $term = self::factory()->term->create_and_get();
  1734. $this->assertSame( '/wp/v2/tags/' . $term->term_id, rest_get_route_for_term( $term ) );
  1735. }
  1736. /**
  1737. * @ticket 49116
  1738. */
  1739. public function test_rest_get_route_for_category() {
  1740. $term = self::factory()->category->create_and_get();
  1741. $this->assertSame( '/wp/v2/categories/' . $term->term_id, rest_get_route_for_term( $term ) );
  1742. }
  1743. /**
  1744. * @ticket 49116
  1745. */
  1746. public function test_rest_get_route_for_term_id() {
  1747. $term = self::factory()->term->create_and_get();
  1748. $this->assertSame( '/wp/v2/tags/' . $term->term_id, rest_get_route_for_term( $term->term_id ) );
  1749. }
  1750. /**
  1751. * @ticket 50300
  1752. *
  1753. * @dataProvider _dp_rest_is_object
  1754. *
  1755. * @param bool $expected Expected result of the check.
  1756. * @param mixed $value The value to check.
  1757. */
  1758. public function test_rest_is_object( $expected, $value ) {
  1759. $is_object = rest_is_object( $value );
  1760. if ( $expected ) {
  1761. $this->assertTrue( $is_object );
  1762. } else {
  1763. $this->assertFalse( $is_object );
  1764. }
  1765. }
  1766. public function _dp_rest_is_object() {
  1767. return array(
  1768. array(
  1769. true,
  1770. '',
  1771. ),
  1772. array(
  1773. true,
  1774. new stdClass(),
  1775. ),
  1776. array(
  1777. true,
  1778. new JsonSerializable_Object( array( 'hi' => 'there' ) ),
  1779. ),
  1780. array(
  1781. true,
  1782. array( 'hi' => 'there' ),
  1783. ),
  1784. array(
  1785. true,
  1786. array(),
  1787. ),
  1788. array(
  1789. true,
  1790. array( 'a', 'b' ),
  1791. ),
  1792. array(
  1793. false,
  1794. new Basic_Object(),
  1795. ),
  1796. array(
  1797. false,
  1798. new JsonSerializable_Object( 'str' ),
  1799. ),
  1800. array(
  1801. false,
  1802. 'str',
  1803. ),
  1804. array(
  1805. false,
  1806. 5,
  1807. ),
  1808. );
  1809. }
  1810. /**
  1811. * @ticket 50300
  1812. *
  1813. * @dataProvider _dp_rest_sanitize_object
  1814. *
  1815. * @param array $expected Expected sanitized version.
  1816. * @param mixed $value The value to sanitize.
  1817. */
  1818. public function test_rest_sanitize_object( $expected, $value ) {
  1819. $sanitized = rest_sanitize_object( $value );
  1820. $this->assertSame( $expected, $sanitized );
  1821. }
  1822. public function _dp_rest_sanitize_object() {
  1823. return array(
  1824. array(
  1825. array(),
  1826. '',
  1827. ),
  1828. array(
  1829. array( 'a' => '1' ),
  1830. (object) array( 'a' => '1' ),
  1831. ),
  1832. array(
  1833. array( 'hi' => 'there' ),
  1834. new JsonSerializable_Object( array( 'hi' => 'there' ) ),
  1835. ),
  1836. array(
  1837. array( 'hi' => 'there' ),
  1838. array( 'hi' => 'there' ),
  1839. ),
  1840. array(
  1841. array(),
  1842. array(),
  1843. ),
  1844. array(
  1845. array( 'a', 'b' ),
  1846. array( 'a', 'b' ),
  1847. ),
  1848. array(
  1849. array(),
  1850. new Basic_Object(),
  1851. ),
  1852. array(
  1853. array(),
  1854. new JsonSerializable_Object( 'str' ),
  1855. ),
  1856. array(
  1857. array(),
  1858. 'str',
  1859. ),
  1860. array(
  1861. array(),
  1862. 5,
  1863. ),
  1864. );
  1865. }
  1866. /**
  1867. * @ticket 50300
  1868. *
  1869. * @dataProvider _dp_rest_is_array
  1870. *
  1871. * @param bool $expected Expected result of the check.
  1872. * @param mixed $value The value to check.
  1873. */
  1874. public function test_rest_is_array( $expected, $value ) {
  1875. $is_array = rest_is_array( $value );
  1876. if ( $expected ) {
  1877. $this->assertTrue( $is_array );
  1878. } else {
  1879. $this->assertFalse( $is_array );
  1880. }
  1881. }
  1882. public function _dp_rest_is_array() {
  1883. return array(
  1884. array(
  1885. true,
  1886. '',
  1887. ),
  1888. array(
  1889. true,
  1890. array( 'a', 'b' ),
  1891. ),
  1892. array(
  1893. true,
  1894. array(),
  1895. ),
  1896. array(
  1897. true,
  1898. 'a,b,c',
  1899. ),
  1900. array(
  1901. true,
  1902. 'a',
  1903. ),
  1904. array(
  1905. true,
  1906. 5,
  1907. ),
  1908. array(
  1909. false,
  1910. new stdClass(),
  1911. ),
  1912. array(
  1913. false,
  1914. new JsonSerializable_Object( array( 'hi' => 'there' ) ),
  1915. ),
  1916. array(
  1917. false,
  1918. array( 'hi' => 'there' ),
  1919. ),
  1920. array(
  1921. false,
  1922. new Basic_Object(),
  1923. ),
  1924. array(
  1925. false,
  1926. new JsonSerializable_Object( 'str' ),
  1927. ),
  1928. array(
  1929. false,
  1930. null,
  1931. ),
  1932. );
  1933. }
  1934. /**
  1935. * @ticket 50300
  1936. *
  1937. * @dataProvider _dp_rest_sanitize_array
  1938. *
  1939. * @param array $expected Expected sanitized version.
  1940. * @param mixed $value The value to sanitize.
  1941. */
  1942. public function test_rest_sanitize_array( $expected, $value ) {
  1943. $sanitized = rest_sanitize_array( $value );
  1944. $this->assertSame( $expected, $sanitized );
  1945. }
  1946. public function _dp_rest_sanitize_array() {
  1947. return array(
  1948. array(
  1949. array(),
  1950. '',
  1951. ),
  1952. array(
  1953. array( 'a', 'b' ),
  1954. array( 'a', 'b' ),
  1955. ),
  1956. array(
  1957. array(),
  1958. array(),
  1959. ),
  1960. array(
  1961. array( 'a', 'b', 'c' ),
  1962. 'a,b,c',
  1963. ),
  1964. array(
  1965. array( 'a' ),
  1966. 'a',
  1967. ),
  1968. array(
  1969. array( 'a', 'b' ),
  1970. 'a,b,',
  1971. ),
  1972. array(
  1973. array( '5' ),
  1974. 5,
  1975. ),
  1976. array(
  1977. array(),
  1978. new stdClass(),
  1979. ),
  1980. array(
  1981. array(),
  1982. new JsonSerializable_Object( array( 'hi' => 'there' ) ),
  1983. ),
  1984. array(
  1985. array( 'there' ),
  1986. array( 'hi' => 'there' ),
  1987. ),
  1988. array(
  1989. array(),
  1990. new Basic_Object(),
  1991. ),
  1992. array(
  1993. array(),
  1994. new JsonSerializable_Object( 'str' ),
  1995. ),
  1996. array(
  1997. array(),
  1998. null,
  1999. ),
  2000. );
  2001. }
  2002. /**
  2003. * @ticket 51146
  2004. *
  2005. * @dataProvider _dp_rest_is_integer
  2006. *
  2007. * @param bool $expected Expected result of the check.
  2008. * @param mixed $value The value to check.
  2009. */
  2010. public function test_rest_is_integer( $expected, $value ) {
  2011. $is_integer = rest_is_integer( $value );
  2012. if ( $expected ) {
  2013. $this->assertTrue( $is_integer );
  2014. } else {
  2015. $this->assertFalse( $is_integer );
  2016. }
  2017. }
  2018. public function _dp_rest_is_integer() {
  2019. return array(
  2020. array(
  2021. true,
  2022. 1,
  2023. ),
  2024. array(
  2025. true,
  2026. '1',
  2027. ),
  2028. array(
  2029. true,
  2030. 0,
  2031. ),
  2032. array(
  2033. true,
  2034. -1,
  2035. ),
  2036. array(
  2037. true,
  2038. '05',
  2039. ),
  2040. array(
  2041. false,
  2042. 'garbage',
  2043. ),
  2044. array(
  2045. false,
  2046. 5.5,
  2047. ),
  2048. array(
  2049. false,
  2050. '5.5',
  2051. ),
  2052. array(
  2053. false,
  2054. array(),
  2055. ),
  2056. array(
  2057. false,
  2058. true,
  2059. ),
  2060. );
  2061. }
  2062. /**
  2063. * @ticket 50300
  2064. *
  2065. * @dataProvider _dp_get_best_type_for_value
  2066. *
  2067. * @param string $expected The expected best type.
  2068. * @param mixed $value The value to test.
  2069. * @param array $types The list of available types.
  2070. */
  2071. public function test_get_best_type_for_value( $expected, $value, $types ) {
  2072. $this->assertSame( $expected, rest_get_best_type_for_value( $value, $types ) );
  2073. }
  2074. public function _dp_get_best_type_for_value() {
  2075. return array(
  2076. array(
  2077. 'array',
  2078. array( 'hi' ),
  2079. array( 'array' ),
  2080. ),
  2081. array(
  2082. 'object',
  2083. array( 'hi' => 'there' ),
  2084. array( 'object' ),
  2085. ),
  2086. array(
  2087. 'integer',
  2088. 5,
  2089. array( 'integer' ),
  2090. ),
  2091. array(
  2092. 'number',
  2093. 4.0,
  2094. array( 'number' ),
  2095. ),
  2096. array(
  2097. 'boolean',
  2098. true,
  2099. array( 'boolean' ),
  2100. ),
  2101. array(
  2102. 'string',
  2103. 'str',
  2104. array( 'string' ),
  2105. ),
  2106. array(
  2107. 'null',
  2108. null,
  2109. array( 'null' ),
  2110. ),
  2111. array(
  2112. 'string',
  2113. '',
  2114. array( 'array', 'string' ),
  2115. ),
  2116. array(
  2117. 'string',
  2118. '',
  2119. array( 'object', 'string' ),
  2120. ),
  2121. array(
  2122. 'string',
  2123. 'Hello',
  2124. array( 'object', 'string' ),
  2125. ),
  2126. array(
  2127. 'object',
  2128. array( 'hello' => 'world' ),
  2129. array( 'object', 'string' ),
  2130. ),
  2131. array(
  2132. 'number',
  2133. '5.0',
  2134. array( 'number', 'string' ),
  2135. ),
  2136. array(
  2137. 'string',
  2138. '5.0',
  2139. array( 'string', 'number' ),
  2140. ),
  2141. array(
  2142. 'boolean',
  2143. 'false',
  2144. array( 'boolean', 'string' ),
  2145. ),
  2146. array(
  2147. 'string',
  2148. 'false',
  2149. array( 'string', 'boolean' ),
  2150. ),
  2151. array(
  2152. 'string',
  2153. 'a,b',
  2154. array( 'string', 'array' ),
  2155. ),
  2156. array(
  2157. 'array',
  2158. 'a,b',
  2159. array( 'array', 'string' ),
  2160. ),
  2161. array(
  2162. 'string',
  2163. 'hello',
  2164. array( 'integer', 'string' ),
  2165. ),
  2166. );
  2167. }
  2168. /**
  2169. * @ticket 51722
  2170. * @dataProvider data_rest_preload_api_request_embeds_links
  2171. *
  2172. * @param string $embed The embed parameter.
  2173. * @param string[] $expected The list of link relations that should be embedded.
  2174. * @param string[] $not_expected The list of link relations that should not be embedded.
  2175. */
  2176. public function test_rest_preload_api_request_embeds_links( $embed, $expected, $not_expected ) {
  2177. wp_set_current_user( 1 );
  2178. $post_id = self::factory()->post->create();
  2179. self::factory()->comment->create_post_comments( $post_id );
  2180. $url = sprintf( '/wp/v2/posts/%d?%s', $post_id, $embed );
  2181. $preload_paths = array( $url );
  2182. $preload_data = array_reduce(
  2183. $preload_paths,
  2184. 'rest_preload_api_request',
  2185. array()
  2186. );
  2187. $this->assertSame( array_keys( $preload_data ), $preload_paths );
  2188. $this->assertArrayHasKey( 'body', $preload_data[ $url ] );
  2189. $this->assertArrayHasKey( '_links', $preload_data[ $url ]['body'] );
  2190. if ( $expected ) {
  2191. $this->assertArrayHasKey( '_embedded', $preload_data[ $url ]['body'] );
  2192. } else {
  2193. $this->assertArrayNotHasKey( '_embedded', $preload_data[ $url ]['body'] );
  2194. }
  2195. foreach ( $expected as $rel ) {
  2196. $this->assertArrayHasKey( $rel, $preload_data[ $url ]['body']['_embedded'] );
  2197. }
  2198. foreach ( $not_expected as $rel ) {
  2199. $this->assertArrayNotHasKey( $rel, $preload_data[ $url ]['body']['_embedded'] );
  2200. }
  2201. }
  2202. public function data_rest_preload_api_request_embeds_links() {
  2203. return array(
  2204. array( '_embed=wp:term,author', array( 'wp:term', 'author' ), array( 'replies' ) ),
  2205. array( '_embed[]=wp:term&_embed[]=author', array( 'wp:term', 'author' ), array( 'replies' ) ),
  2206. array( '_embed', array( 'wp:term', 'author', 'replies' ), array() ),
  2207. array( '_embed=1', array( 'wp:term', 'author', 'replies' ), array() ),
  2208. array( '_embed=true', array( 'wp:term', 'author', 'replies' ), array() ),
  2209. array( '', array(), array() ),
  2210. );
  2211. }
  2212. }