Completed
Pull Request — master (#5653)
by Damian
12:15
created

DBHTMLTextTest_Shortcode   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 11
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 11
rs 10
wmc 2
lcom 0
cbo 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A get_shortcodes() 0 4 1
A handle_shortcode() 0 4 1
1
<?php
2
3
4
5
use SilverStripe\ORM\FieldType\DBHTMLText;
6
use SilverStripe\ORM\FieldType\DBField;
7
8
9
/**
10
 * @package framework
11
 * @subpackage tests
12
 */
13
class DBHTMLTextTest extends SapphireTest {
14
15
	public function setUp() {
16
		parent::setUp();
17
18
		// Set test handler
19
		ShortcodeParser::get('htmltest')
20
			->register('test_shortcode', array('DBHTMLTextTest_Shortcode', 'handle_shortcode'));
21
		ShortcodeParser::set_active('htmltest');
22
	}
23
24
	public function tearDown() {
25
		ShortcodeParser::set_active('default');
26
		parent::tearDown();
27
	}
28
29
	/**
30
	 * Test {@link Text->LimitCharacters()}
31
	 */
32
	public function providerLimitCharacters()
33
	{
34
		// HTML characters are stripped safely
35
		return [
36
			['The little brown fox jumped over the lazy cow.', 'The little brown fox...'],
37
			['<p>Short &amp; Sweet</p>', 'Short &amp; Sweet'],
38
			['This text contains &amp; in it', 'This text contains &amp;...'],
39
		];
40
	}
41
42
	/**
43
	 * Test {@link DBHTMLText->LimitCharacters()}
44
	 * @dataProvider providerLimitCharacters
45
	 * @param string $originalValue
46
	 * @param string $expectedValue
47
	 */
48
	public function testLimitCharacters($originalValue, $expectedValue) {
49
		$textObj = DBField::create_field('HTMLFragment', $originalValue);
50
		$result = $textObj->obj('LimitCharacters')->forTemplate();
51
		$this->assertEquals($expectedValue, $result);
52
	}
53
54
	/**
55
	 * @return array
56
	 */
57
	public function providerLimitCharactersToClosestWord()
58
	{
59
		// HTML is converted safely to plain text
60
		return [
61
			// Standard words limited, ellipsis added if truncated
62
			['<p>Lorem ipsum dolor sit amet</p>', 24, 'Lorem ipsum dolor sit...'],
63
64
			// Complete words less than the character limit don't get truncated, ellipsis not added
65
			['<p>Lorem ipsum</p>', 24, 'Lorem ipsum'],
66
			['<p>Lorem</p>', 24, 'Lorem'],
67
			['', 24, ''],    // No words produces nothing!
68
69
			// Special characters are encoded safely
70
			['Nice &amp; Easy', 24, 'Nice &amp; Easy'],
71
72
			// HTML is safely converted to plain text
73
			['<p>Lorem ipsum dolor sit amet</p>', 24, 'Lorem ipsum dolor sit...'],
74
			['<p><span>Lorem ipsum dolor sit amet</span></p>', 24, 'Lorem ipsum dolor sit...'],
75
			['<p>Lorem ipsum</p>', 24, 'Lorem ipsum'],
76
			['Lorem &amp; ipsum dolor sit amet', 24, 'Lorem &amp; ipsum dolor sit...']
77
		];
78
	}
79
80
	/**
81
	 * Test {@link DBHTMLText->LimitCharactersToClosestWord()}
82
	 * @dataProvider providerLimitCharactersToClosestWord
83
	 *
84
	 * @param string $originalValue Raw string input
85
	 * @param int $limit
86
	 * @param string $expectedValue Expected template value
87
	 */
88
	public function testLimitCharactersToClosestWord($originalValue, $limit, $expectedValue) {
89
		$textObj = DBField::create_field('HTMLFragment', $originalValue);
90
		$result = $textObj->obj('LimitCharactersToClosestWord', [$limit])->forTemplate();
91
		$this->assertEquals($expectedValue, $result);
92
	}
93
94
	public function providerSummary()
95
	{
96
		return [
97
			[
98
				'<p>Should strip <b>tags, but leave</b> text</p>',
99
				50,
100
				'Should strip tags, but leave text',
101
			],
102
			[
103
				// Line breaks are preserved
104
				'<p>Unclosed tags <br>should not phase it</p>',
105
				50,
106
				"Unclosed tags <br />\nshould not phase it",
107
			],
108
			[
109
				// Paragraphs converted to linebreak
110
				'<p>Second paragraph</p><p>should not cause errors or appear in output</p>',
111
				50,
112
				"Second paragraph<br />\n<br />\nshould not cause errors or appear in output",
113
			],
114
			[
115
				'<img src="hello" /><p>Second paragraph</p><p>should not cause errors or appear in output</p>',
116
				50,
117
				"Second paragraph<br />\n<br />\nshould not cause errors or appear in output",
118
			],
119
			[
120
				'  <img src="hello" /><p>Second paragraph</p><p>should not cause errors or appear in output</p>',
121
				50,
122
				"Second paragraph<br />\n<br />\nshould not cause errors or appear in output",
123
			],
124
			[
125
				'<p><img src="remove me">example <img src="include me">text words hello<img src="hello"></p>',
126
				50,
127
				'example text words hello',
128
			],
129
130
			// Shorter limits
131
			[
132
				'<p>A long paragraph should be cut off if limit is set</p>',
133
				5,
134
				'A long paragraph should be...',
135
			],
136
			[
137
				'<p>No matter <i>how many <b>tags</b></i> are in it</p>',
138
				5,
139
				'No matter how many tags...',
140
			],
141
			[
142
				'<p>A sentence is. nicer than hard limits</p>',
143
				5,
144
				'A sentence is.',
145
			],
146
		];
147
	}
148
149
	/**
150
	 * @dataProvider providerSummary
151
	 * @param string $originalValue
152
	 * @param int $limit
153
	 * @param string $expectedValue
154
	 */
155
	public function testSummary($originalValue, $limit, $expectedValue) {
156
		$textObj = DBField::create_field('HTMLFragment', $originalValue);
157
		$result = $textObj->obj('Summary', [$limit])->forTemplate();
158
		$this->assertEquals($expectedValue, $result);
159
	}
160
161
	public function testSummaryEndings() {
162
		$cases = array(
163
			'...',
164
			' -> more',
165
			''
166
		);
167
168
		$orig = '<p>Cut it off, cut it off</p>';
169
		$match = 'Cut it off, cut';
170
171
		foreach($cases as $add) {
172
			$textObj = DBField::create_field('HTMLFragment', $orig);
173
			$result = $textObj->obj('Summary', [4, $add])->forTemplate();
174
			$this->assertEquals($match.Convert::raw2xml($add), $result);
175
		}
176
	}
177
178
179
180
	public function providerFirstSentence()
181
	{
182
		return [
183
			// Same behaviour as DBTextTest
184
			['', ''],
185
			['First sentence.', 'First sentence.'],
186
			['First sentence. Second sentence', 'First sentence.'],
187
			['First sentence? Second sentence', 'First sentence?'],
188
			['First sentence! Second sentence', 'First sentence!'],
189
190
			// DBHTHLText strips HTML first
191
			['<br />First sentence.', 'First sentence.'],
192
			['<p>First sentence. Second sentence. Third sentence</p>', 'First sentence.'],
193
		];
194
	}
195
196
	/**
197
	 * @dataProvider providerFirstSentence
198
	 * @param string $originalValue
199
	 * @param string $expectedValue
200
     */
201
	public function testFirstSentence($originalValue, $expectedValue) {
202
		$textObj = DBField::create_field('HTMLFragment', $originalValue);
203
		$result = $textObj->obj('FirstSentence')->forTemplate();
204
		$this->assertEquals($expectedValue, $result);
205
	}
206
207
	public function testCreate() {
208
		/** @var DBHTMLText $field */
209
		$field = Object::create_from_string("HTMLFragment(['whitelist' => 'link'])", 'MyField');
210
		$this->assertEquals(['link'], $field->getWhitelist());
211
		$field = Object::create_from_string("HTMLFragment(['whitelist' => 'link,a'])", 'MyField');
212
		$this->assertEquals(['link', 'a'], $field->getWhitelist());
213
		$field = Object::create_from_string("HTMLFragment(['whitelist' => ['link', 'a']])", 'MyField');
214
		$this->assertEquals(['link', 'a'], $field->getWhitelist());
215
		$field = Object::create_from_string("HTMLFragment", 'MyField');
216
		$this->assertEmpty($field->getWhitelist());
217
218
		// Test shortcodes
219
		$field = Object::create_from_string("HTMLFragment(['shortcodes' => true])", 'MyField');
220
		$this->assertEquals(true, $field->getProcessShortcodes());
221
		$field = Object::create_from_string("HTMLFragment(['shortcodes' => false])", 'MyField');
222
		$this->assertEquals(false, $field->getProcessShortcodes());
223
224
		// Mix options
225
		$field = Object::create_from_string("HTMLFragment(['shortcodes' => true, 'whitelist' => ['a'])", 'MyField');
226
		$this->assertEquals(true, $field->getProcessShortcodes());
227
		$this->assertEquals(['a'], $field->getWhitelist());
228
	}
229
230
	public function providerToPlain()
231
	{
232
		return [
233
			[
234
				'<p><img />Lots of <strong>HTML <i>nested</i></strong> tags',
235
				'Lots of HTML nested tags',
236
			],
237
			[
238
				'<p>Multi</p><p>Paragraph<br>Also has multilines.</p>',
239
				"Multi\n\nParagraph\nAlso has multilines.",
240
			],
241
			[
242
				'<p>Collapses</p><p></p><p>Excessive<br/><br /><br>Newlines</p>',
243
				"Collapses\n\nExcessive\n\nNewlines",
244
			]
245
		];
246
	}
247
248
	/**
249
	 * @dataProvider providerToPlain
250
	 * @param string $html
251
	 * @param string $plain
252
	 */
253
	public function testToPlain($html, $plain) {
254
		/** @var DBHTMLText $textObj */
255
		$textObj = DBField::create_field('HTMLFragment', $html);
256
		$this->assertEquals($plain, $textObj->Plain());
257
	}
258
259
	/**
260
	 * each test is in the format input, charactere limit, highlight, expected output
261
	 *
262
	 * @return array
263
	 */
264
	public function providerContextSummary()
265
	{
266
		return [
267
			[
268
				'This is some text. It is a test',
269
				20,
270
				'test',
271
				'... text. It is a <span class="highlight">test</span>'
272
			],
273
			[
274
				// Retains case of original string
275
				'This is some test text. Test test what if you have multiple keywords.',
276
				50,
277
				'some test',
278
				'This is <span class="highlight">some</span> <span class="highlight">test</span> text.'
279
				. ' <span class="highlight">Test</span> <span class="highlight">test</span> what if you have...'
280
			],
281
			[
282
				'Here is some text &amp; HTML included',
283
				20,
284
				'html',
285
				'... text &amp; <span class="highlight">HTML</span> inc...'
286
			],
287
			[
288
				'A dog ate a cat while looking at a Foobar',
289
				100,
290
				'a',
291
				// test that it does not highlight too much (eg every a)
292
				'A dog ate a cat while looking at a Foobar',
293
			],
294
			[
295
				'A dog ate a cat while looking at a Foobar',
296
				100,
297
				'ate',
298
				// it should highlight 3 letters or more.
299
				'A dog <span class="highlight">ate</span> a cat while looking at a Foobar',
300
			],
301
302
			// HTML Content is plain-textified, and incorrect tags removed
303
			[
304
				'<p>A dog ate a cat while <span class="highlight">looking</span> at a Foobar</p>',
305
				100,
306
				'ate',
307
				// it should highlight 3 letters or more.
308
				'A dog <span class="highlight">ate</span> a cat while looking at a Foobar',
309
			]
310
		];
311
	}
312
313
	/**
314
	 * @dataProvider providerContextSummary
315
	 * @param string $originalValue Input
316
	 * @param int $limit Numer of characters
317
	 * @param string $keywords Keywords to highlight
318
	 * @param string $expectedValue Expected output (XML encoded safely)
319
     */
320
	public function testContextSummary($originalValue, $limit, $keywords, $expectedValue)
321
	{
322
		$text = DBField::create_field('HTMLFragment', $originalValue);
323
		$result = $text->obj('ContextSummary', [$limit, $keywords])->forTemplate();
324
		// it should highlight 3 letters or more.
325
		$this->assertEquals($expectedValue, $result);
326
	}
327
328
	public function testRAW() {
329
		$data = DBField::create_field('HTMLFragment', 'This &amp; This');
330
		$this->assertEquals('This &amp; This', $data->RAW());
331
332
		$data = DBField::create_field('HTMLFragment', 'This & This');
333
		$this->assertEquals('This & This', $data->RAW());
334
	}
335
336
	public function testXML() {
337
		$data = DBField::create_field('HTMLFragment', 'This & This');
338
		$this->assertEquals('This &amp; This', $data->XML());
339
		$data = DBField::create_field('HTMLFragment', 'This &amp; This');
340
		$this->assertEquals('This &amp;amp; This', $data->XML());
341
	}
342
343
	public function testHTML() {
344
		$data = DBField::create_field('HTMLFragment', 'This & This');
345
		$this->assertEquals('This &amp; This', $data->HTML());
346
		$data = DBField::create_field('HTMLFragment', 'This &amp; This');
347
		$this->assertEquals('This &amp;amp; This', $data->HTML());
348
	}
349
350
	public function testJS() {
351
		$data = DBField::create_field('HTMLText', '"this is &amp; test"');
352
		$this->assertEquals('\"this is \x26amp; test\"', $data->JS());
353
	}
354
355
	public function testATT() {
356
		// HTML Fragment
357
		$data = DBField::create_field('HTMLFragment', '"this is a test"');
358
		$this->assertEquals('&quot;this is a test&quot;', $data->ATT());
359
360
		// HTML Text (passes shortcodes + tidy)
361
		$data = DBField::create_field('HTMLText', '"');
362
		$this->assertEquals('&quot;', $data->ATT());
363
	}
364
365
	public function testShortcodesProcessed()
366
	{
367
		/** @var DBHTMLText $obj */
368
		$obj = DBField::create_field(
369
			'HTMLText',
370
			'<p>Some content <strong>[test_shortcode]</strong> with shortcode</p>'
371
		);
372
		// Basic DBField methods process shortcodes
373
		$this->assertEquals(
374
			'Some content shortcode content with shortcode',
375
			$obj->Plain()
376
		);
377
		$this->assertEquals(
378
			'<p>Some content <strong>shortcode content</strong> with shortcode</p>',
379
			$obj->RAW()
380
		);
381
		$this->assertEquals(
382
			'&lt;p&gt;Some content &lt;strong&gt;shortcode content&lt;/strong&gt; with shortcode&lt;/p&gt;',
383
			$obj->XML()
384
		);
385
		$this->assertEquals(
386
			'&lt;p&gt;Some content &lt;strong&gt;shortcode content&lt;/strong&gt; with shortcode&lt;/p&gt;',
387
			$obj->HTML()
388
		);
389
		// Test summary methods
390
		$this->assertEquals(
391
			'Some content shortcode...',
392
			$obj->Summary(3)
393
		);
394
		$this->assertEquals(
395
			'Some content shortcode content with shortcode',
396
			$obj->LimitSentences(1)
397
		);
398
		$this->assertEquals(
399
			'Some content shortco...',
400
			$obj->LimitCharacters(20)
401
		);
402
	}
403
404
	public function testParse() {
405
		// Test parse
406
		/** @var DBHTMLText $obj */
407
		$obj = DBField::create_field(
408
			'HTMLText',
409
			'<p>[b]Some content[/b] [test_shortcode] with shortcode</p>'
410
		);
411
412
		// BBCode strips HTML and applies own formatting
413
		$this->assertEquals(
414
			'<strong>Some content</strong> shortcode content with shortcode',
415
			$obj->Parse('BBCodeParser')->forTemplate()
416
		);
417
	}
418
419
	function testExists() {
420
		$h = new DBHTMLText();
421
		$h->setValue("");
422
		$this->assertFalse($h->exists());
423
		$h->setValue("<p>content</p>");
424
		$this->assertTrue($h->exists());
425
	}
426
427
	function testWhitelist() {
428
		$textObj = new DBHTMLText('Test', ['whitelist'=> 'meta,link']);
429
		$this->assertEquals(
430
			'<meta content="Keep"><link href="Also Keep">',
431
			$textObj->whitelistContent('<meta content="Keep"><p>Remove</p><link href="Also Keep" />Remove Text'),
432
			'Removes any elements not in whitelist excluding text elements'
433
		);
434
435
		$textObj = new DBHTMLText('Test', ['whitelist'=> 'meta,link,text()']);
436
		$this->assertEquals(
437
			'<meta content="Keep"><link href="Also Keep">Keep Text',
438
			$textObj->whitelistContent('<meta content="Keep"><p>Remove</p><link href="Also Keep" />Keep Text'),
439
			'Removes any elements not in whitelist including text elements'
440
		);
441
	}
442
443
	public function testShortCodeParsedInRAW() {
444
		$parser = ShortcodeParser::get('HTMLTextTest');
445
		$parser->register('shortcode', function($arguments, $content, $parser, $tagName, $extra) {
0 ignored issues
show
Unused Code introduced by
The parameter $arguments is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $content is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $tagName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $extra is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
446
			return 'replaced';
447
		});
448
		ShortcodeParser::set_active('HTMLTextTest');
449
		/** @var DBHTMLText $field */
450
		$field = DBField::create_field('HTMLText', '<p>[shortcode]</p>');
451
		$this->assertEquals('<p>replaced</p>', $field->RAW());
452
		$this->assertEquals('<p>replaced</p>', (string)$field);
453
454
		$field->setOptions(array(
455
			'shortcodes' => false,
456
		));
457
458
		$this->assertEquals('<p>[shortcode]</p>', $field->RAW());
459
		$this->assertEquals('<p>[shortcode]</p>', (string)$field);
460
461
462
		ShortcodeParser::set_active('default');
463
	}
464
465
	public function testShortCodeParsedInTemplateHelpers() {
466
		$parser = ShortcodeParser::get('HTMLTextTest');
467
		$parser->register('shortcode', function($arguments, $content, $parser, $tagName, $extra) {
0 ignored issues
show
Unused Code introduced by
The parameter $arguments is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $content is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $tagName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $extra is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
468
			return 'Replaced short code with this. <a href="home">home</a>';
469
		});
470
		ShortcodeParser::set_active('HTMLTextTest');
471
		/** @var DBHTMLText $field */
472
		$field = DBField::create_field('HTMLText', '<p>[shortcode]</p>');
473
474
		$this->assertEquals(
475
			'&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;',
476
			$field->HTMLATT()
477
		);
478
		$this->assertEquals(
479
			'%3Cp%3EReplaced+short+code+with+this.+%3Ca+href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E',
480
			$field->URLATT()
481
		);
482
		$this->assertEquals(
483
			'%3Cp%3EReplaced%20short%20code%20with%20this.%20%3Ca%20href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E',
484
			$field->RAWURLATT()
485
		);
486
		$this->assertEquals(
487
			'&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;',
488
			$field->ATT()
489
		);
490
		$this->assertEquals(
491
			'<p>Replaced short code with this. <a href="home">home</a></p>',
492
			$field->RAW()
493
		);
494
		$this->assertEquals(
495
			'\x3cp\x3eReplaced short code with this. \x3ca href=\"home\"\x3ehome\x3c/a\x3e\x3c/p\x3e',
496
			$field->JS()
497
		);
498
		$this->assertEquals(
499
			'&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;',
500
			$field->HTML()
501
		);
502
		$this->assertEquals(
503
			'&lt;p&gt;Replaced short code with this. &lt;a href=&quot;home&quot;&gt;home&lt;/a&gt;&lt;/p&gt;',
504
			$field->XML()
505
		);
506
		$this->assertEquals(
507
			'Repl...',
508
			$field->LimitCharacters(4, '...')
509
		);
510
		$this->assertEquals(
511
			'Replaced...',
512
			$field->LimitCharactersToClosestWord(10, '...')
513
		);
514
		$this->assertEquals(
515
			'Replaced...',
516
			$field->LimitWordCount(1, '...')
517
		);
518
		$this->assertEquals(
519
			'<p>replaced short code with this. <a href="home">home</a></p>',
520
			$field->LowerCase()
521
		);
522
		$this->assertEquals(
523
			'<P>REPLACED SHORT CODE WITH THIS. <A HREF="HOME">HOME</A></P>',
524
			$field->UpperCase()
525
		);
526
		$this->assertEquals(
527
			'Replaced short code with this. home',
528
			$field->Plain()
529
		);
530
		Config::nest();
531
		Config::inst()->update('Director', 'alternate_base_url', 'http://example.com/');
532
		$this->assertEquals(
533
			'<p>Replaced short code with this. <a href="http://example.com/home">home</a></p>',
534
			$field->AbsoluteLinks()
535
		);
536
		Config::unnest();
537
		$this->assertEquals(
538
			'Replaced short code with this.',
539
			$field->LimitSentences(1)
540
		);
541
		$this->assertEquals(
542
			'Replaced short code with this.',
543
			$field->FirstSentence()
544
		);
545
		$this->assertEquals(
546
			'Replaced short...',
547
			$field->Summary(2)
548
		);
549
		$this->assertEquals(
550
			'Replaced short code with this. home',
551
			$field->FirstParagraph()
552
		);
553
		$this->assertEquals(
554
			'Replaced <span class="highlight">short</span> <span class="highlight">code</span> with this. home',
555
			$field->ContextSummary(500, 'short code')
556
		);
557
558
		ShortcodeParser::set_active('default');
559
	}
560
}
561
562
class DBHTMLTextTest_Shortcode implements ShortcodeHandler, TestOnly {
563
	public static function get_shortcodes()
564
	{
565
		return 'test';
566
	}
567
568
	public static function handle_shortcode($arguments, $content, $parser, $shortcode, $extra = array())
569
	{
570
		return 'shortcode content';
571
	}
572
}
573