shortcode.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. /**
  3. * @group shortcode
  4. */
  5. class Tests_Shortcode extends WP_UnitTestCase {
  6. protected $shortcodes = array( 'test-shortcode-tag', 'footag', 'bartag', 'baztag', 'dumptag', 'hyphen', 'hyphen-foo', 'hyphen-foo-bar' );
  7. function setUp() {
  8. parent::setUp();
  9. foreach ( $this->shortcodes as $shortcode )
  10. add_shortcode( $shortcode, array( $this, '_shortcode_' . str_replace( '-', '_', $shortcode ) ) );
  11. $this->atts = null;
  12. $this->content = null;
  13. $this->tagname = null;
  14. }
  15. function tearDown() {
  16. global $shortcode_tags;
  17. parent::tearDown();
  18. foreach ( $this->shortcodes as $shortcode )
  19. unset( $shortcode_tags[ $shortcode ] );
  20. }
  21. function _shortcode_test_shortcode_tag( $atts, $content = null, $tagname = null ) {
  22. $this->atts = $atts;
  23. $this->content = $content;
  24. $this->tagname = $tagname;
  25. $this->filter_atts_out = null;
  26. $this->filter_atts_pairs = null;
  27. $this->filter_atts_atts = null;
  28. }
  29. // [footag foo="bar"]
  30. function _shortcode_footag( $atts ) {
  31. return @"foo = {$atts['foo']}";
  32. }
  33. // [bartag foo="bar"]
  34. function _shortcode_bartag( $atts ) {
  35. extract(shortcode_atts(array(
  36. 'foo' => 'no foo',
  37. 'baz' => 'default baz',
  38. ), $atts, 'bartag'));
  39. return "foo = {$foo}";
  40. }
  41. // [baztag]content[/baztag]
  42. function _shortcode_baztag( $atts, $content = '' ) {
  43. return 'content = '.do_shortcode($content);
  44. }
  45. function _shortcode_dumptag( $atts ) {
  46. $out = '';
  47. foreach ($atts as $k=>$v)
  48. $out .= "$k = $v\n";
  49. return $out;
  50. }
  51. function _shortcode_hyphen() {
  52. return __FUNCTION__;
  53. }
  54. function _shortcode_hyphen_foo() {
  55. return __FUNCTION__;
  56. }
  57. function _shortcode_hyphen_foo_bar() {
  58. return __FUNCTION__;
  59. }
  60. function test_noatts() {
  61. do_shortcode('[test-shortcode-tag /]');
  62. $this->assertEquals( '', $this->atts );
  63. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  64. }
  65. function test_one_att() {
  66. do_shortcode('[test-shortcode-tag foo="asdf" /]');
  67. $this->assertEquals( array('foo' => 'asdf'), $this->atts );
  68. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  69. }
  70. function test_not_a_tag() {
  71. $out = do_shortcode('[not-a-shortcode-tag]');
  72. $this->assertEquals( '[not-a-shortcode-tag]', $out );
  73. }
  74. /**
  75. * @ticket 17657
  76. */
  77. function test_tag_hyphen_not_tag() {
  78. $out = do_shortcode( '[dumptag-notreal]' );
  79. $this->assertEquals( '[dumptag-notreal]', $out );
  80. }
  81. function test_tag_underscore_not_tag() {
  82. $out = do_shortcode( '[dumptag_notreal]' );
  83. $this->assertEquals( '[dumptag_notreal]', $out );
  84. }
  85. function test_tag_not_tag() {
  86. $out = do_shortcode( '[dumptagnotreal]' );
  87. $this->assertEquals( '[dumptagnotreal]', $out );
  88. }
  89. /**
  90. * @ticket 17657
  91. */
  92. function test_tag_hyphen() {
  93. $this->assertEquals( '_shortcode_hyphen', do_shortcode( '[hyphen]' ) );
  94. $this->assertEquals( '_shortcode_hyphen_foo', do_shortcode( '[hyphen-foo]' ) );
  95. $this->assertEquals( '_shortcode_hyphen_foo_bar', do_shortcode( '[hyphen-foo-bar]' ) );
  96. $this->assertEquals( '[hyphen-baz]', do_shortcode( '[hyphen-baz]' ) );
  97. $this->assertEquals( '[hyphen-foo-bar-baz]', do_shortcode( '[hyphen-foo-bar-baz]' ) );
  98. }
  99. function test_two_atts() {
  100. do_shortcode('[test-shortcode-tag foo="asdf" bar="bing" /]');
  101. $this->assertEquals( array('foo' => 'asdf', 'bar' => 'bing'), $this->atts );
  102. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  103. }
  104. function test_noatts_enclosing() {
  105. do_shortcode('[test-shortcode-tag]content[/test-shortcode-tag]');
  106. $this->assertEquals( '', $this->atts );
  107. $this->assertEquals( 'content', $this->content );
  108. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  109. }
  110. function test_one_att_enclosing() {
  111. do_shortcode('[test-shortcode-tag foo="bar"]content[/test-shortcode-tag]');
  112. $this->assertEquals( array('foo' => 'bar'), $this->atts );
  113. $this->assertEquals( 'content', $this->content );
  114. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  115. }
  116. function test_two_atts_enclosing() {
  117. do_shortcode('[test-shortcode-tag foo="bar" baz="bing"]content[/test-shortcode-tag]');
  118. $this->assertEquals( array('foo' => 'bar', 'baz' => 'bing'), $this->atts );
  119. $this->assertEquals( 'content', $this->content );
  120. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  121. }
  122. function test_unclosed() {
  123. $out = do_shortcode('[test-shortcode-tag]');
  124. $this->assertEquals( '', $out );
  125. $this->assertEquals( '', $this->atts );
  126. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  127. }
  128. function test_positional_atts_num() {
  129. $out = do_shortcode('[test-shortcode-tag 123]');
  130. $this->assertEquals( '', $out );
  131. $this->assertEquals( array(0=>'123'), $this->atts );
  132. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  133. }
  134. function test_positional_atts_url() {
  135. $out = do_shortcode('[test-shortcode-tag http://www.youtube.com/watch?v=eBGIQ7ZuuiU]');
  136. $this->assertEquals( '', $out );
  137. $this->assertEquals( array(0=>'http://www.youtube.com/watch?v=eBGIQ7ZuuiU'), $this->atts );
  138. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  139. }
  140. function test_positional_atts_quotes() {
  141. $out = do_shortcode('[test-shortcode-tag "something in quotes" "something else"]');
  142. $this->assertEquals( '', $out );
  143. $this->assertEquals( array(0=>'something in quotes', 1=>'something else'), $this->atts );
  144. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  145. }
  146. function test_positional_atts_mixed() {
  147. $out = do_shortcode('[test-shortcode-tag 123 http://wordpress.com/ 0 "foo" bar]');
  148. $this->assertEquals( '', $out );
  149. $this->assertEquals( array(0=>'123', 1=>'http://wordpress.com/', 2=>'0', 3=>'foo', 4=>'bar'), $this->atts );
  150. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  151. }
  152. function test_positional_and_named_atts() {
  153. $out = do_shortcode('[test-shortcode-tag 123 url=http://wordpress.com/ foo bar="baz"]');
  154. $this->assertEquals( '', $out );
  155. $this->assertEquals( array(0=>'123', 'url' => 'http://wordpress.com/', 1=>'foo', 'bar' => 'baz'), $this->atts );
  156. $this->assertEquals( 'test-shortcode-tag', $this->tagname );
  157. }
  158. function test_footag_default() {
  159. $out = do_shortcode('[footag]');
  160. $this->assertEquals('foo = ', $out);
  161. }
  162. function test_footag_val() {
  163. $val = rand_str();
  164. $out = do_shortcode('[footag foo="'.$val.'"]');
  165. $this->assertEquals('foo = '.$val, $out);
  166. }
  167. function test_nested_tags() {
  168. $out = do_shortcode('[baztag][dumptag abc="foo" def=123 http://wordpress.com/][/baztag]');
  169. $expected = "content = abc = foo\ndef = 123\n0 = http://wordpress.com\n";
  170. $this->assertEquals($expected, $out);
  171. }
  172. /**
  173. * @ticket 6518
  174. */
  175. function test_tag_escaped() {
  176. $out = do_shortcode('[[footag]] [[bartag foo="bar"]]');
  177. $this->assertEquals('[footag] [bartag foo="bar"]', $out);
  178. $out = do_shortcode('[[footag /]] [[bartag foo="bar" /]]');
  179. $this->assertEquals('[footag /] [bartag foo="bar" /]', $out);
  180. $out = do_shortcode('[[baztag foo="bar"]the content[/baztag]]');
  181. $this->assertEquals('[baztag foo="bar"]the content[/baztag]', $out);
  182. // double escaped
  183. $out = do_shortcode('[[[footag]]] [[[bartag foo="bar"]]]');
  184. $this->assertEquals('[[footag]] [[bartag foo="bar"]]', $out);
  185. }
  186. function test_tag_not_escaped() {
  187. // these have square brackets on either end but aren't actually escaped
  188. $out = do_shortcode('[[footag] [bartag foo="bar"]]');
  189. $this->assertEquals('[foo = foo = bar]', $out);
  190. $out = do_shortcode('[[footag /] [bartag foo="bar" /]]');
  191. $this->assertEquals('[foo = foo = bar]', $out);
  192. $out = do_shortcode('[[baztag foo="bar"]the content[/baztag]');
  193. $this->assertEquals('[content = the content', $out);
  194. $out = do_shortcode('[[not-a-tag]]');
  195. $this->assertEquals('[[not-a-tag]]', $out);
  196. $out = do_shortcode('[[[footag] [bartag foo="bar"]]]');
  197. $this->assertEquals('[[foo = foo = bar]]', $out);
  198. }
  199. function test_mixed_tags() {
  200. $in = <<<EOF
  201. So this is a post with [footag foo="some stuff"] and a bunch of tags.
  202. [bartag]
  203. [baztag]
  204. Here's some content
  205. on more than one line
  206. [/baztag]
  207. [bartag foo=1] [baztag] [footag foo="2"] [baztag]
  208. [baztag]
  209. more content
  210. [/baztag]
  211. EOF;
  212. $expected = <<<EOF
  213. So this is a post with foo = some stuff and a bunch of tags.
  214. foo = no foo
  215. content =
  216. Here's some content
  217. on more than one line
  218. foo = 1 content = foo = 2 content =
  219. content =
  220. more content
  221. EOF;
  222. $out = do_shortcode($in);
  223. $this->assertEquals(strip_ws($expected), strip_ws($out));
  224. }
  225. /**
  226. * @ticket 6562
  227. */
  228. function test_utf8_whitespace_1() {
  229. // NO-BREAK SPACE: U+00A0
  230. do_shortcode("[test-shortcode-tag foo=\"bar\" \xC2\xA0baz=\"123\"]");
  231. $this->assertEquals( array('foo' => 'bar', 'baz' => '123'), $this->atts );
  232. $this->assertEquals( '', $this->content );
  233. }
  234. /**
  235. * @ticket 6562
  236. */
  237. function test_utf8_whitespace_2() {
  238. // ZERO WIDTH SPACE: U+200B
  239. do_shortcode("[test-shortcode-tag foo=\"bar\" \xE2\x80\x8Babc=\"def\"]");
  240. $this->assertEquals( array('foo' => 'bar', 'abc' => 'def'), $this->atts );
  241. $this->assertEquals( '', $this->content );
  242. }
  243. /**
  244. * @ticket 14050
  245. */
  246. function test_shortcode_unautop() {
  247. // a blank line is added at the end, so test with it already there
  248. $test_string = "[footag]\n";
  249. $this->assertEquals( $test_string, shortcode_unautop( wpautop( $test_string ) ) );
  250. }
  251. /**
  252. * @ticket 14050
  253. */
  254. function test_multiple_shortcode_unautop() {
  255. // a blank line is added at the end, so test with it already there
  256. $test_string = "[footag]\n[footag]\n";
  257. $actual = shortcode_unautop( wpautop( $test_string ) );
  258. $this->assertEquals( $test_string, $actual );
  259. }
  260. /**
  261. * @ticket 10326
  262. */
  263. function test_strip_shortcodes() {
  264. $this->assertEquals('before', strip_shortcodes('before[gallery]'));
  265. $this->assertEquals('after', strip_shortcodes('[gallery]after'));
  266. $this->assertEquals('beforeafter', strip_shortcodes('before[gallery]after'));
  267. }
  268. // Store passed in shortcode_atts_{$shortcode} args
  269. function _filter_atts( $out, $pairs, $atts ) {
  270. $this->filter_atts_out = $out;
  271. $this->filter_atts_pairs = $pairs;
  272. $this->filter_atts_atts = $atts;
  273. return $out;
  274. }
  275. // Filter shortcode atts in various ways
  276. function _filter_atts2( $out, $pairs, $atts ) {
  277. // If foo attribute equals "foo1", change it to be default value
  278. if ( isset( $out['foo'] ) && 'foo1' == $out['foo'] )
  279. $out['foo'] = $pairs['foo'];
  280. // If baz attribute is set, remove it
  281. if ( isset( $out['baz'] ) )
  282. unset( $out['baz'] );
  283. $this->filter_atts_out = $out;
  284. return $out;
  285. }
  286. function test_shortcode_atts_filter_passes_original_arguments() {
  287. add_filter( 'shortcode_atts_bartag', array( $this, '_filter_atts' ), 10, 3 );
  288. do_shortcode('[bartag foo="foo1" /]');
  289. $this->assertEquals( array( 'foo' => 'foo1', 'baz' => 'default baz' ), $this->filter_atts_out );
  290. $this->assertEquals( array( 'foo' => 'no foo', 'baz' => 'default baz' ), $this->filter_atts_pairs );
  291. $this->assertEquals( array( 'foo' => 'foo1' ), $this->filter_atts_atts );
  292. remove_filter( 'shortcode_atts_bartag', array( $this, '_filter_atts' ), 10, 3 );
  293. }
  294. function test_shortcode_atts_filtering() {
  295. add_filter( 'shortcode_atts_bartag', array( $this, '_filter_atts2' ), 10, 3 );
  296. $out = do_shortcode('[bartag foo="foo1" baz="baz1" /]');
  297. $this->assertEquals( array( 'foo' => 'no foo' ), $this->filter_atts_out );
  298. $this->assertEquals( 'foo = no foo', $out );
  299. $out = do_shortcode('[bartag foo="foo2" /]');
  300. $this->assertEquals( 'foo = foo2', $out );
  301. remove_filter( 'shortcode_atts_bartag', array( $this, '_filter_atts2' ), 10, 3 );
  302. }
  303. }