Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#15)
by Der Mundschenk
02:30
created

PHP_Typography_Test   D

Complexity

Total Complexity 151

Size/Duplication

Total Lines 2864
Duplicated Lines 8.52 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 151
lcom 1
cbo 5
dl 244
loc 2864
rs 4.4102
c 0
b 0
f 0

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PHP_Typography_Test often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PHP_Typography_Test, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 *  This file is part of PHP-Typography.
4
 *
5
 *  Copyright 2015-2017 Peter Putzer.
6
 *
7
 *  This program is free software; you can redistribute it and/or
8
 *  modify it under the terms of the GNU General Public License
9
 *  as published by the Free Software Foundation; either version 2
10
 *  of the License, or ( at your option ) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, write to the Free Software
19
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
 *
21
 *  @package mundschenk-at/php-typography/tests
22
 *  @license http://www.gnu.org/licenses/gpl-2.0.html
23
 */
24
25
namespace PHP_Typography\Tests;
26
27
use \PHP_Typography\PHP_Typography;
28
use \PHP_Typography\Hyphenator_Cache;
29
use \PHP_Typography\Settings;
30
use \PHP_Typography\Fixes\Node_Fix;
31
use \PHP_Typography\Fixes\Token_Fix;
32
use \PHP_Typography\Strings;
33
use \PHP_Typography\U;
34
35
/**
36
 * PHP_Typography unit test.
37
 *
38
 * @coversDefaultClass PHP_Typography\PHP_Typography
39
 * @usesDefaultClass PHP_Typography\PHP_Typography
40
 *
41
 * @uses PHP_Typography\PHP_Typography
42
 * @uses PHP_Typography\Hyphenator_Cache
43
 * @uses PHP_Typography\Settings
44
 * @uses PHP_Typography\Settings\Simple_Dashes
45
 * @uses PHP_Typography\Settings\Simple_Quotes
46
 * @uses PHP_Typography\Strings
47
 * @uses PHP_Typography\Arrays
48
 * @uses PHP_Typography\DOM
49
 * @uses PHP_Typography\RE
50
 * @uses PHP_Typography\Fixes\Node_Fixes\Abstract_Node_Fix
51
 * @uses PHP_Typography\Fixes\Node_Fixes\Classes_Dependent_Fix
52
 * @uses PHP_Typography\Fixes\Node_Fixes\Simple_Style_Fix
53
 * @uses PHP_Typography\Fixes\Node_Fixes\Process_Words_Fix
54
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Fractions_Fix
55
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Ordinal_Suffix_Fix
56
 * @uses PHP_Typography\Fixes\Node_Fixes\Style_Hanging_Punctuation_Fix
57
 * @uses PHP_Typography\Fixes\Node_Fixes\Style_Initial_Quotes_Fix
58
 * @uses PHP_Typography\Fixes\Token_Fixes\Abstract_Token_Fix
59
 * @uses PHP_Typography\Fixes\Token_Fixes\Hyphenate_Compounds_Fix
60
 * @uses PHP_Typography\Fixes\Token_Fixes\Hyphenate_Fix
61
 * @uses PHP_Typography\Fixes\Token_Fixes\Wrap_Emails_Fix
62
 * @uses PHP_Typography\Fixes\Token_Fixes\Wrap_Hard_Hyphens_Fix
63
 * @uses PHP_Typography\Fixes\Token_Fixes\Wrap_URLs_Fix
64
 * @uses PHP_Typography\Fixes\Node_Fixes\Dash_Spacing_Fix
65
 * @uses PHP_Typography\Fixes\Node_Fixes\Dewidow_Fix
66
 * @uses PHP_Typography\Fixes\Node_Fixes\French_Punctuation_Spacing_Fix
67
 * @uses PHP_Typography\Fixes\Node_Fixes\Numbered_Abbreviation_Spacing_Fix
68
 * @uses PHP_Typography\Fixes\Node_Fixes\Simple_Regex_Replacement_Fix
69
 * @uses PHP_Typography\Fixes\Node_Fixes\Single_Character_Word_Spacing_Fix
70
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Dashes_Fix
71
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Diacritics_Fix
72
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Ellipses_Fix
73
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Exponents_Fix
74
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Marks_Fix
75
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Maths_Fix
76
 * @uses PHP_Typography\Fixes\Node_Fixes\Smart_Quotes_Fix
77
 * @uses PHP_Typography\Fixes\Node_Fixes\Space_Collapse_Fix
78
 * @uses PHP_Typography\Fixes\Node_Fixes\Style_Ampersands_Fix
79
 * @uses PHP_Typography\Fixes\Node_Fixes\Style_Caps_Fix
80
 * @uses PHP_Typography\Fixes\Node_Fixes\Style_Numbers_Fix
81
 * @uses PHP_Typography\Fixes\Node_Fixes\Unit_Spacing_Fix
82
 */
83
class PHP_Typography_Test extends PHP_Typography_Testcase {
84
85
	/**
86
	 * The PHP_Typography instance.
87
	 *
88
	 * @var PHP_Typography
89
	 */
90
	protected $typo;
91
92
	/**
93
	 * The Settings instance.
94
	 *
95
	 * @var Settings
96
	 */
97
	protected $s;
98
99
	/**
100
	 * Sets up the fixture, for example, opens a network connection.
101
	 * This method is called before a test is executed.
102
	 */
103
	protected function setUp() { // @codingStandardsIgnoreLine
104
		$this->typo = new PHP_Typography();
105
		$this->s = new Settings( false );
106
	}
107
108
	/**
109
	 * Tears down the fixture, for example, closes a network connection.
110
	 * This method is called after a test is executed.
111
	 */
112
	protected function tearDown() { // @codingStandardsIgnoreLine
113
	}
114
115
	/**
116
	 * Test set_tags_to_ignore.
117
	 *
118
	 * @coversNothing
119
	 *
120
	 * @uses PHP_Typography\Text_Parser
121
	 * @uses PHP_Typography\Text_Parser\Token
122
	 */
123
	public function test_set_tags_to_ignore() {
124
		$s = $this->s;
125
126
		$always_ignore = [
127
			'iframe',
128
			'textarea',
129
			'button',
130
			'select',
131
			'optgroup',
132
			'option',
133
			'map',
134
			'style',
135
			'head',
136
			'title',
137
			'script',
138
			'applet',
139
			'object',
140
			'param',
141
		];
142
		$self_closing_tags = [
143
			'area',
144
			'base',
145
			'basefont',
146
			'br',
147
			'frame',
148
			'hr',
149
			'img',
150
			'input',
151
			'link',
152
			'meta',
153
		];
154
155
		// Default tags.
156
		$s->set_tags_to_ignore( [
157
			'code',
158
			'head',
159
			'kbd',
160
			'object',
161
			'option',
162
			'pre',
163
			'samp',
164
			'script',
165
			'noscript',
166
			'noembed',
167
			'select',
168
			'style',
169
			'textarea',
170
			'title',
171
			'var',
172
			'math',
173
		] );
174
175
		// Inspect settings.
176
		$this->assertArraySubset( [ 'code', 'head', 'kbd', 'object', 'option', 'pre', 'samp', 'script', 'noscript', 'noembed', 'select', 'style', 'textarea', 'title', 'var', 'math' ], $s['ignoreTags'] );
177
		foreach ( $always_ignore as $tag ) {
178
			$this->assertContains( $tag, $s['ignoreTags'] );
179
		}
180
		foreach ( $self_closing_tags as $tag ) {
181
			$this->assertNotContains( $tag, $s['ignoreTags'] );
182
		}
183
184
		// Auto-close tag and something else.
185
		$s->set_tags_to_ignore( [ 'img', 'foo' ] );
186
		$this->assertContains( 'foo', $s['ignoreTags'] );
187
		foreach ( $self_closing_tags as $tag ) {
188
			$this->assertNotContains( $tag, $s['ignoreTags'] );
189
		}
190
		foreach ( $always_ignore as $tag ) {
191
			$this->assertContains( $tag, $s['ignoreTags'] );
192
		}
193
194
		$s->set_tags_to_ignore( 'img foo  \	' ); // Should not result in an error.
195
		$s->set_smart_quotes( true );
196
		$html = '<p><foo>Ignore this "quote",</foo><span class="other"> but not "this" one.</span></p>';
197
		$expected = '<p><foo>Ignore this "quote",</foo><span class="other"> but not &ldquo;this&rdquo; one.</span></p>';
198
		$this->assertSame( $expected, $this->clean_html( $this->typo->process( $html, $s ) ) );
199
	}
200
201
	/**
202
	 * Test set_classes_to_ignore.
203
	 *
204
	 * @coversNothing
205
	 *
206
	 * @uses PHP_Typography\Text_Parser
207
	 * @uses PHP_Typography\Text_Parser\Token
208
	 */
209
	public function test_set_classes_to_ignore() {
210
		$s = $this->s;
211
212
		$s->set_classes_to_ignore( 'foo bar' );
213
214
		$this->assertContains( 'foo', $s['ignoreClasses'] );
215
		$this->assertContains( 'bar', $s['ignoreClasses'] );
216
217
		$html = '<p><span class="foo">Ignore this "quote",</span><span class="other"> but not "this" one.</span></p>
218
				 <p class="bar">"This" should also be ignored. <span>And "this".</span></p>
219
				 <p><span>"But" not this.</span></p>';
220
		$expected = '<p><span class="foo">Ignore this "quote",</span><span class="other"> but not &ldquo;this&rdquo; one.</span></p>
221
				 <p class="bar">"This" should also be ignored. <span>And "this".</span></p>
222
				 <p><span>&ldquo;But&rdquo; not this.</span></p>';
223
		$s->set_smart_quotes( true );
224
		$this->assertSame( $expected, $this->clean_html( $this->typo->process( $html, $s ) ) );
225
	}
226
227
	/**
228
	 * Test set_ids_to_ignore.
229
	 *
230
	 * @coversNothing
231
	 *
232
	 * @uses PHP_Typography\Text_Parser
233
	 * @uses PHP_Typography\Text_Parser\Token
234
	 */
235
	public function test_set_ids_to_ignore() {
236
		$s = $this->s;
237
238
		$s->set_ids_to_ignore( 'foobar barfoo' );
239
240
		$this->assertContains( 'foobar', $s['ignoreIDs'] );
241
		$this->assertContains( 'barfoo', $s['ignoreIDs'] );
242
243
		$html = '<p><span id="foobar">Ignore this "quote",</span><span class="other"> but not "this" one.</span></p>
244
				 <p id="barfoo">"This" should also be ignored. <span>And "this".</span></p>
245
				 <p><span>"But" not this.</span></p>';
246
		$expected = '<p><span id="foobar">Ignore this "quote",</span><span class="other"> but not &ldquo;this&rdquo; one.</span></p>
247
				 <p id="barfoo">"This" should also be ignored. <span>And "this".</span></p>
248
				 <p><span>&ldquo;But&rdquo; not this.</span></p>';
249
		$s->set_smart_quotes( true );
250
		$this->assertSame( $expected, $this->clean_html( $this->typo->process( $html, $s ) ) );
251
	}
252
253
	/**
254
	 * Integrate all three "ignore" variants.
255
	 *
256
	 * @covers ::query_tags_to_ignore
257
	 *
258
	 * @depends test_set_ids_to_ignore
259
	 * @depends test_set_classes_to_ignore
260
	 * @depends test_set_tags_to_ignore
261
	 *
262
	 * @uses PHP_Typography\Text_Parser
263
	 * @uses PHP_Typography\Text_Parser\Token
264
	 */
265
	public function test_complete_ignore() {
266
		$s = $this->s;
267
268
		$s->set_ids_to_ignore( 'foobar barfoo' );
269
		$s->set_classes_to_ignore( 'foo bar' );
270
		$s->set_tags_to_ignore( [ 'img', 'foo' ] );
271
272
		$html = '<p><span class="foo">Ignore this "quote",</span><span class="other"> but not "this" one.</span></p>
273
				 <p class="bar">"This" should also be ignored. <span>And "this".</span></p>
274
				 <p><span>"But" not this.</span></p>';
275
		$expected = '<p><span class="foo">Ignore this "quote",</span><span class="other"> but not &ldquo;this&rdquo; one.</span></p>
276
				 <p class="bar">"This" should also be ignored. <span>And "this".</span></p>
277
				 <p><span>&ldquo;But&rdquo; not this.</span></p>';
278
		$s->set_smart_quotes( true );
279
		$this->assertSame( $expected, $this->clean_html( $this->typo->process( $html, $s ) ) );
280
	}
281
282
	/**
283
	 * Tests register_node_fix.
284
	 *
285
	 * @covers ::register_node_fix
286
	 */
287
	public function test_register_node_fix() {
288
		foreach ( PHP_Typography::GROUPS as $group ) {
289
			// Create a stub for the Node_Fix interface.
290
			$fake_node_fixer = $this->createMock( Node_Fix::class );
291
			$fake_node_fixer->method( 'apply' )->willReturn( 'foo' );
292
293
			$this->typo->register_node_fix( $fake_node_fixer, $group );
294
			$this->assertContains( $fake_node_fixer, $this->readAttribute( $this->typo, 'node_fixes' )[ $group ] );
295
		}
296
	}
297
298
	/**
299
	 * Tests register_node_fix.
300
	 *
301
	 * @covers ::register_node_fix
302
	 *
303
	 * @expectedException \InvalidArgumentException
304
	 * @expectedExceptionMessageRegExp /^Invalid fixer group .+\.$/
305
	 */
306
	public function test_register_node_fix_invalid_group() {
307
308
		// Create a stub for the Node_Fix interface.
309
		$fake_node_fixer = $this->createMock( Node_Fix::class );
310
		$fake_node_fixer->method( 'apply' )->willReturn( 'foo' );
311
312
		$this->typo->register_node_fix( $fake_node_fixer, 'invalid group parameter' );
313
	}
314
315
	/**
316
	 * Tests register_token_fix.
317
	 *
318
	 * @covers ::register_token_fix
319
	 */
320
	public function test_register_token_fix() {
321
322
		// Create a stub for the Token_Fix interface.
323
		$fake_token_fixer = $this->createMock( Token_Fix::class );
324
		$fake_token_fixer->method( 'apply' )->willReturn( 'foo' );
325
		$fake_token_fixer->method( 'target' )->willReturn( Token_Fix::MIXED_WORDS );
326
327
		$this->typo->register_token_fix( $fake_token_fixer );
328
		$this->assertTrue( true, 'An error occured during Token_Fix registration.' );
329
	}
330
331
332
	/**
333
	 * Test get_hyphenation_languages.
334
	 *
335
	 * @covers ::get_hyphenation_languages
336
	 * @covers ::get_language_plugin_list
337
	 */
338
	public function test_get_hyphenation_languages() {
339
340
		$expected = [
341
			'af',
342
			'bg',
343
			'ca',
344
			'cs',
345
			'cy',
346
			'da',
347
			'de',
348
			'de-1901',
349
			'el-Mono',
350
			'el-Poly',
351
			'en-GB',
352
			'en-US',
353
			'es',
354
			'et',
355
			'eu',
356
			'fi',
357
			'fr',
358
			'ga',
359
			'gl',
360
			'grc',
361
			'hr',
362
			'hu',
363
			'hy',
364
			'ia',
365
			'id',
366
			'is',
367
			'it',
368
			'ka',
369
			'la',
370
			'la-classic',
371
			'la-liturgic',
372
			'lt',
373
			'lv',
374
			'mn-Cyrl',
375
			'nl',
376
			'no',
377
			'pl',
378
			'pt',
379
			'ro',
380
			'ru',
381
			'sa',
382
			'sh-Cyrl',
383
			'sh-Latn',
384
			'sk',
385
			'sl',
386
			'sr-Cyrl',
387
			'sv',
388
			'th',
389
			'tr',
390
			'uk',
391
			'zh-Latn',
392
		];
393
		$not_expected = [ 'klingon', 'de-DE' ];
394
395
		$actual = $this->typo->get_hyphenation_languages();
396
		foreach ( $expected as $lang_code ) {
397
			$this->assertArrayHasKey( $lang_code, $actual );
398
		}
399
		foreach ( $not_expected as $lang_code ) {
400
			$this->assertArrayNotHasKey( $lang_code, $actual );
401
		}
402
	}
403
404
405
	/**
406
	 * Test get_diacritic_languages.
407
	 *
408
	 * @covers ::get_diacritic_languages
409
	 * @covers ::get_language_plugin_list
410
	 */
411
	public function test_get_diacritic_languages() {
412
413
		$expected = [ 'de-DE', 'en-US' ];
414
		$not_expected = [
415
			'es',
416
			'et',
417
			'eu',
418
			'fi',
419
			'fr',
420
			'ga',
421
			'gl',
422
			'grc',
423
			'hr',
424
			'hu',
425
			'ia',
426
			'id',
427
			'is',
428
			'it',
429
			'la',
430
			'lt',
431
			'mn-Cyrl',
432
			'no',
433
			'pl',
434
			'pt',
435
			'ro',
436
			'ru',
437
			'sa',
438
			'sh-Cyrl',
439
			'sh-Latn',
440
			'sk',
441
			'sl',
442
			'sr-Cyrl',
443
			'sv',
444
			'tr',
445
			'uk',
446
			'zh-Latn',
447
		];
448
449
		$actual = $this->typo->get_diacritic_languages();
450
		foreach ( $expected as $lang_code ) {
451
			$this->assertArrayHasKey( $lang_code, $actual );
452
		}
453
		foreach ( $not_expected as $lang_code ) {
454
			$this->assertArrayNotHasKey( $lang_code, $actual );
455
		}
456
	}
457
458
	/**
459
	 * Provide data for testing the complete processing.
460
	 *
461
	 * @return array
462
	 */
463
	public function provide_process_data() {
464
		return [
465
			[ '3*3=3^2', '<span class="numbers">3</span>&times;<span class="numbers">3</span>=<span class="numbers">3</span><sup><span class="numbers">2</span></sup>', false ], // smart math.
466
			[ '"Hey there!"', '<span class="pull-double">&ldquo;</span>Hey there!&rdquo;', '&ldquo;Hey there!&rdquo;' ], // smart quotes.
467
			[ 'Hey - there', 'Hey&thinsp;&mdash;&thinsp;there', 'Hey &mdash; there' ], // smart dashes.
468
			[ 'Hey...', 'Hey&hellip;', true ], // smart ellipses.
469
			[ '(c)', '&copy;', true ], // smart marks.
470
			[ 'creme', 'cr&egrave;me', false ], // diacritics.
471
			[ 'a a a', 'a a&nbsp;a', false ], // single characgter word spacing.
472
			[ '3 cm', '<span class="numbers">3</span>&nbsp;cm', false ], // unit spacing without true no-break narrow space.
473
			[ 'a/b', 'a/&#8203;b', false ], // dash spacing.
474
			[ '<span class="numbers">5</span>', '<span class="numbers">5</span>', true ], // class present, no change.
475
			[ '1st', '<span class="numbers">1</span><sup class="ordinal">st</sup>', false ], // smart ordinal suffixes.
476
			[ '1^1', '<span class="numbers">1</span><sup><span class="numbers">1</span></sup>', false ], // smart exponents.
477
			[ 'a &amp; b', 'a <span class="amp">&amp;</span>&nbsp;b', false ], // wrap amps.
478
			[ 'a  b', 'a b', false ], // space collapse.
479
			[ 'NATO', '<span class="caps">NATO</span>', false ], // style caps.
480
			[ 'superfluous', 'super&shy;flu&shy;ous', false ], // hyphenate.
481
			[ 'http://example.org', 'http://&#8203;exam&#8203;ple&#8203;.org', false ], // wrap URLs.
482
			[ '[email protected]', 'foo@&#8203;example.&#8203;org', false ], // wrap emails.
483
			[ '<span> </span>', '<span> </span>', true ], // whitespace is ignored.
484
			[ '<span class="noTypo">123</span>', '<span class="noTypo">123</span>', true ], // skipped class.
485
			[
486
				'<section id="main-content" class="container">
487
				<!-- Start Page Content -->
488
				<div class="row-wrapper-x"></div></section><section class="blox aligncenter  page-title-x  " style=" padding-top:px; padding-bottom:px;  background: url( \'http://www.feinschliff.hamburg/wp-content/uploads/2014/09/nails_02.jpg\' ) no-repeat ; background-position: center center;background-size: cover; min-height:px; "></section>',
489
				'<section id="main-content" class="container">
490
				<!-- Start Page Content -->
491
				<div class="row-wrapper-x"></div></section><section class="blox aligncenter  page-title-x  " style=" padding-top:px; padding-bottom:px;  background: url( \'http://www.feinschliff.hamburg/wp-content/uploads/2014/09/nails_02.jpg\' ) no-repeat ; background-position: center center;background-size: cover; min-height:px; "></section>',
492
				true,
493
			],
494
			[ '<section id="main"></section>', '<section id="main"></section>', true ],
495
			[ '<section id="main"><!-- comment --></section>', '<section id="main"><!-- comment --></section>', true ],
496
			[ '<section id="main"><!-- comment --><div></div></section>', '<section id="main"><!-- comment --><div></div></section>', true ],
497
			[ 'ช่วยฉัน/ผมหน่อยได้ไหม คะ/ครับ333', 'ช่วยฉัน/ผมหน่อยได้ไหม คะ/ครับ<span class="numbers">333</span>', false ], // Unicode characters in regular expressions.
498
		];
499
	}
500
501
502
	/**
503
	 * Test process.
504
	 *
505
	 * @covers ::process
506
	 * @covers ::apply_fixes_to_html_node
507
	 *
508
	 * @uses ::process_textnodes
509
	 * @uses PHP_Typography\Hyphenator
510
	 * @uses PHP_Typography\Hyphenator\Trie_Node
511
	 * @uses PHP_Typography\Text_Parser
512
	 * @uses PHP_Typography\Text_Parser\Token
513
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
514
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
515
	 *
516
	 * @dataProvider provide_process_data
517
	 *
518
	 * @param string $html   HTML input.
519
	 * @param string $result Entity-escaped output.
520
	 * @param bool   $feed   Use process_feed.
521
	 */
522
	public function test_process( $html, $result, $feed ) {
523
		$s = $this->s;
524
		$s->set_defaults();
525
526
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $s ) ) );
527
	}
528
529
530
	/**
531
	 * Test process_feed.
532
	 *
533
	 * @covers ::process_feed
534
	 * @covers ::apply_fixes_to_feed_node
535
	 *
536
	 * @uses ::process_textnodes
537
	 * @uses PHP_Typography\Hyphenator
538
	 * @uses PHP_Typography\Hyphenator\Trie_Node
539
	 * @uses PHP_Typography\Text_Parser
540
	 * @uses PHP_Typography\Text_Parser\Token
541
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
542
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
543
	 *
544
	 * @dataProvider provide_process_data
545
	 *
546
	 * @param string      $html   HTML input.
547
	 * @param string      $result Entity-escaped output.
548
	 * @param bool|string $feed   Use process_feed. If $feed is a string, use instead of $result.
549
	 */
550
	public function test_process_feed( $html, $result, $feed ) {
551
		$s = $this->s;
552
		$s->set_defaults();
553
554
		if ( is_string( $feed ) ) {
555
			$this->assertSame( $feed, $this->clean_html( $this->typo->process_feed( $html, $s ) ) );
556
		} elseif ( $feed ) {
557
			$this->assertSame( $result, $this->clean_html( $this->typo->process_feed( $html, $s ) ) );
558
		} else {
559
			$this->assertSame( $html, $this->typo->process_feed( $html, $s ) );
560
		}
561
	}
562
563
	/**
564
	 * Test process_textnodes.
565
	 *
566
	 * @covers ::process_textnodes
567
	 *
568
	 * @uses PHP_Typography\Hyphenator
569
	 * @uses PHP_Typography\Hyphenator\Trie_Node
570
	 * @uses PHP_Typography\Text_Parser
571
	 * @uses PHP_Typography\Text_Parser\Token
572
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
573
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
574
	 *
575
	 * @dataProvider provide_process_data
576
	 *
577
	 * @param string      $html   HTML input.
578
	 * @param string      $result Entity-escaped output.
579
	 * @param bool|string $feed   Use process_feed. If $feed is a string, use instead of $result.
580
	 */
581
	public function test_process_textnodes( $html, $result, $feed ) {
582
		$s = $this->s;
583
		$s->set_defaults();
584
585
		$this->assertSame( $html, $this->clean_html( $this->typo->process_textnodes( $html, function ( $node ) {
586
		}, $s ) ) );
587
	}
588
589
	/**
590
	 * Provide invalid data for testing process_textnodes.
591
	 *
592
	 * @return array
593
	 */
594
	public function provide_process_textnodes_invalid_html_data() {
595
		return [
596
			[ '<div>foo-bar</div></p>', false ],
597
			[ '<div>foo-bar</div></p>', true ],
598
		];
599
	}
600
601
602
	/**
603
	 * Test process_textnodes with invalid HTML.
604
	 *
605
	 * @covers ::process_textnodes
606
	 *
607
	 * @uses PHP_Typography\Hyphenator
608
	 * @uses PHP_Typography\Hyphenator\Trie_Node
609
	 * @uses PHP_Typography\Text_Parser
610
	 * @uses PHP_Typography\Text_Parser\Token
611
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
612
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
613
	 *
614
	 * @dataProvider provide_process_textnodes_invalid_html_data
615
	 *
616
	 * @param string $html HTML input.
617
	 * @param string $feed Ignored.
618
	 */
619
	public function test_process_textnodes_invalid_html( $html, $feed ) {
620
		$s = $this->s;
621
		$s->set_defaults();
622
623
		$this->assertSame( $html, $this->clean_html( $this->typo->process_textnodes( $html, function ( $node ) {
624
			return 'XXX';
625
		}, $s ) ) );
626
	}
627
628
629
	/**
630
	 * Test process_textnodes without a fixer instance.
631
	 *
632
	 * @covers ::process_textnodes
633
	 *
634
	 * @uses PHP_Typography\Hyphenator
635
	 * @uses PHP_Typography\Hyphenator\Trie_Node
636
	 * @uses PHP_Typography\Text_Parser
637
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
638
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
639
	 *
640
	 * @expectedException \TypeError
641
	 *
642
	 * @dataProvider provide_process_data
643
	 *
644
	 * @param string      $html   HTML input.
645
	 * @param string      $result Ignored.
646
	 * @param bool|string $feed   Ignored.
647
	 */
648
	public function test_process_textnodes_no_fixer( $html, $result, $feed ) {
649
		$s = $this->s;
650
		$s->set_defaults();
651
652
		$this->typo->process_textnodes( $html, 'bar', $s );
653
	}
654
655
	/**
656
	 * Test process_textnodes with alternate settings for titles.
657
	 *
658
	 * @covers ::process_textnodes
659
	 *
660
	 * @uses PHP_Typography\Hyphenator
661
	 * @uses PHP_Typography\Hyphenator\Trie_Node
662
	 * @uses PHP_Typography\Text_Parser
663
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
664
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
665
	 *
666
	 * @dataProvider provide_process_data
667
	 *
668
	 * @param string      $html   HTML input.
669
	 * @param string      $result Ignored.
670
	 * @param bool|string $feed   Ignored.
671
	 */
672
	public function test_process_textnodes_alternate_settings_title( $html, $result, $feed ) {
673
		$s = new Settings( true );
674
		$s->set_tags_to_ignore( [ 'h1', 'h2' ] );
675
676
		$this->assertSame( $html, $this->clean_html( $this->typo->process_textnodes( $html, function ( $node ) {
677
		}, $s, true ) ) );
678
	}
679
680
681
	/**
682
	 * Provide data for testing process with $is_title set to true.
683
	 *
684
	 * @return array
685
	 */
686
	public function provide_process_with_title_data() {
687
		return [
688
			[ 'Really...', 'Real&shy;ly&hellip;', 'Really&hellip;', '' ], // processed.
689
			[ 'Really...', 'Really...', true, [ 'h1' ] ], // skipped.
690
		];
691
	}
692
693
694
	/**
695
	 * Test process.
696
	 *
697
	 * @covers ::process
698
	 *
699
	 * @uses PHP_Typography\Hyphenator
700
	 * @uses PHP_Typography\Hyphenator\Trie_Node
701
	 * @uses PHP_Typography\Text_Parser
702
	 * @uses PHP_Typography\Text_Parser\Token
703
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
704
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
705
	 *
706
	 * @dataProvider provide_process_with_title_data
707
	 *
708
	 * @param string      $html      HTML input.
709
	 * @param string      $result    Expected entity-escaped result.
710
	 * @param bool|string $feed      Ignored.
711
	 * @param array       $skip_tags Tags to skip.
712
	 */
713
	public function test_process_with_title( $html, $result, $feed, $skip_tags ) {
714
		$s = $this->s;
715
		$s->set_defaults();
716
717
		$s->set_tags_to_ignore( $skip_tags );
718
719
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $s, true ) ) );
720
	}
721
722
723
	/**
724
	 * Test process_feed.
725
	 *
726
	 * @covers ::process_feed
727
	 *
728
	 * @uses PHP_Typography\Hyphenator
729
	 * @uses PHP_Typography\Hyphenator\Trie_Node
730
	 * @uses PHP_Typography\Text_Parser
731
	 * @uses PHP_Typography\Text_Parser\Token
732
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
733
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
734
	 *
735
	 * @dataProvider provide_process_with_title_data
736
	 *
737
	 * @param string      $html      HTML input.
738
	 * @param string      $result    Expected entity-escaped result.
739
	 * @param bool|string $feed      Whether process_feed should be used. If string, use instead of $result.
740
	 * @param array       $skip_tags Tags to skip.
741
	 */
742
	public function test_process_feed_with_title( $html, $result, $feed, $skip_tags ) {
743
		$s = $this->s;
744
		$s->set_defaults();
745
746
		$s->set_tags_to_ignore( $skip_tags );
747
748
		if ( is_string( $feed ) ) {
749
			$this->assertSame( $feed, $this->clean_html( $this->typo->process_feed( $html, $s, true ) ) );
750
		} elseif ( $feed ) {
751
			$this->assertSame( $result, $this->clean_html( $this->typo->process_feed( $html, $s, true ) ) );
752
		} else {
753
			$this->assertSame( $html, $this->typo->process_feed( $html, $s, true ) );
754
		}
755
	}
756
757
	/**
758
	 * Provide data for testing handle_parsing_errors.
759
	 *
760
	 * @return [ $errno, $errstr, $errfile, $errline, $errcontext, $result ]
761
	 */
762
	public function provide_handle_parsing_errors() {
763
		return [
764
			[ E_USER_WARNING, 'Fake error message', '/some/path/DOMTreeBuilder.php', 666, [], true ],
765
			[ E_USER_ERROR,   'Fake error message', '/some/path/DOMTreeBuilder.php', 666, [], false ],
766
			[ E_USER_WARNING, 'Fake error message', '/some/path/SomeFile.php',       666, [], false ],
767
			[ E_USER_NOTICE,  'Fake error message', '/some/path/DOMTreeBuilder.php', 666, [], false ],
768
		];
769
	}
770
771
	/**
772
	 * Test handle_parsing_errors.
773
	 *
774
	 * @covers ::handle_parsing_errors
775
	 *
776
	 * @dataProvider provide_handle_parsing_errors
777
	 *
778
	 * @param  int    $errno      Error type constant.
779
	 * @param  string $errstr     Error message.
780
	 * @param  string $errfile    File path.
781
	 * @param  int    $errline    Line number.
782
	 * @param  array  $errcontext Stack context.
783
	 * @param  bool   $result     The expected result.
784
	 */
785
	public function test_handle_parsing_errors( $errno, $errstr, $errfile, $errline, $errcontext, $result ) {
786
787
		if ( $result ) {
788
			$this->assertTrue( $this->typo->handle_parsing_errors( $errno, $errstr, $errfile, $errline, $errcontext ) );
789
		} else {
790
			$this->assertFalse( $this->typo->handle_parsing_errors( $errno, $errstr, $errfile, $errline, $errcontext ) );
791
		}
792
793
		// Try again when we are not interested.
794
		$old_level = error_reporting( 0 );
795
		$this->assertTrue( $this->typo->handle_parsing_errors( $errno, $errstr, $errfile, $errline, $errcontext ) );
796
		error_reporting( $old_level );
797
	}
798
799
	/**
800
	 * Provide data for testing smart_quotes.
801
	 *
802
	 * @return array
803
	 */
804
	public function provide_smart_quotes_data() {
805
		return array(
806
			[ '<span>"Double", \'single\'</span>', '<span>&ldquo;Double&rdquo;, &lsquo;single&rsquo;</span>' ],
807
			[ '<p>"<em>This is nuts.</em>"</p>',   '<p>&ldquo;<em>This is nuts.</em>&rdquo;</p>' ],
808
			[ '"This is so 1996", he said.',       '&ldquo;This is so 1996&rdquo;, he said.' ],
809
			[ '6\'5"',                             '6&prime;5&Prime;' ],
810
			[ '6\' 5"',                            '6&prime; 5&Prime;' ],
811
			[ '6\'&nbsp;5"',                       '6&prime;&nbsp;5&Prime;' ],
812
			[ " 6'' ",                             ' 6&Prime; ' ], // nobody uses this for quotes, so it should be OK to keep the primes here.
813
			[ 'ein 32"-Fernseher',                 'ein 32&Prime;-Fernseher' ],
814
			[ "der 8'-Ölbohrer",                   'der 8&prime;-&Ouml;lbohrer' ],
815
			[ "der 1/4'-Bohrer",                   'der 1/4&prime;-Bohrer' ],
816
			[ 'Hier 1" "Typ 2" einsetzen',         'Hier 1&Prime; &ldquo;Typ 2&rdquo; einsetzen' ],
817
			[ "2/4'",                              '2/4&prime;' ],
818
			[ '3/44"',                             '3/44&Prime;' ],
819
			array( '("Some" word',                      '(&ldquo;Some&rdquo; word' ),
820
		);
821
	}
822
823
	/**
824
	 * Test smart_quotes.
825
	 *
826
	 * @coversNothing
827
	 *
828
	 * @uses PHP_Typography\Text_Parser
829
	 * @uses PHP_Typography\Text_Parser\Token
830
	 *
831
	 * @dataProvider provide_smart_quotes_data
832
	 *
833
	 * @param string $html   HTML input.
834
	 * @param string $result Entity-escaped result string.
835
	 */
836
	public function test_smart_quotes( $html, $result ) {
837
		$this->s->set_smart_quotes( true );
838
839
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
840
	}
841
842
843
	/**
844
	 * Test smart_quotes.
845
	 *
846
	 * @coversNothing
847
	 *
848
	 * @uses PHP_Typography\Text_Parser
849
	 * @uses PHP_Typography\Text_Parser\Token
850
	 *
851
	 * @dataProvider provide_smart_quotes_data
852
	 *
853
	 * @param string $html   HTML input.
854
	 * @param string $result Entity-escaped result string.
855
	 */
856
	public function test_smart_quotes_off( $html, $result ) {
857
		$this->s->set_smart_quotes( false );
858
859
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
860
	}
861
862
	/**
863
	 * Provide data for testing smart quotes.
864
	 *
865
	 * @return array
866
	 */
867
	public function provide_smart_quotes_special_data() {
868
		return array(
869
			array( '("Some" word', '(&raquo;Some&laquo; word', 'doubleGuillemetsReversed', 'singleGuillemetsReversed' ),
870
		);
871
	}
872
873
	/**
874
	 * Test smart_quotes.
875
	 *
876
	 * @coversNothing
877
	 *
878
	 * @uses PHP_Typography\Text_Parser
879
	 * @uses PHP_Typography\Text_Parser\Token
880
	 *
881
	 * @dataProvider provide_smart_quotes_special_data
882
	 *
883
	 * @param string $html      HTML input.
884
	 * @param string $result    Expected entity-escaped result.
885
	 * @param string $primary   Primary quote style.
886
	 * @param string $secondary Secondard  quote style.
887
	 */
888
	public function test_smart_quotes_special( $html, $result, $primary, $secondary ) {
889
		$this->s->set_smart_quotes( true );
890
		$this->s->set_smart_quotes_primary( $primary );
891
		$this->s->set_smart_quotes_secondary( $secondary );
892
893
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
894
	}
895
896
897
	/**
898
	 * Test smart_dashes.
899
	 *
900
	 * @coversNothing
901
	 *
902
	 * @uses PHP_Typography\Text_Parser
903
	 * @uses PHP_Typography\Text_Parser\Token
904
	 *
905
	 * @dataProvider provide_smart_dashes_data
906
	 *
907
	 * @param string $input      HTML input.
908
	 * @param string $result_us  Entity-escaped result with US dash style.
909
	 * @param string $result_int Entity-escaped result with international dash style.
910
	 */
911
	public function test_smart_dashes_with_dash_spacing_off( $input, $result_us, $result_int ) {
912
		$this->s->set_smart_dashes( true );
913
		$this->s->set_dash_spacing( false );
914
915
		$this->s->set_smart_dashes_style( 'traditionalUS' );
916
		$this->assertSame( $result_us, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
917
918
		$this->s->set_smart_dashes_style( 'international' );
919
		$this->assertSame( $result_int, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
920
	}
921
922
923
	/**
924
	 * Test smart_dashes.
925
	 *
926
	 * @coversNothing
927
	 *
928
	 * @uses PHP_Typography\Text_Parser
929
	 * @uses PHP_Typography\Text_Parser\Token
930
	 *
931
	 * @dataProvider provide_smart_dashes_data
932
	 *
933
	 * @param string $input      HTML input.
934
	 * @param string $result_us  Entity-escaped result with US dash style.
935
	 * @param string $result_int Entity-escaped result with international dash style.
936
	 */
937
	public function test_smart_dashes_off( $input, $result_us, $result_int ) {
938
		$this->s->set_smart_dashes( false );
939
		$this->s->set_dash_spacing( false );
940
941
		$this->s->set_smart_dashes_style( 'traditionalUS' );
942
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $this->typo->process( $input, $this->s ) ) );
943
944
		$this->s->set_smart_dashes_style( 'international' );
945
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $this->typo->process( $input, $this->s ) ) );
946
	}
947
948
	/**
949
	 * Provide data for testing smart_ellipses.
950
	 *
951
	 * @return array
952
	 */
953
	public function provide_smart_ellipses_data() {
954
		return [
955
			[ 'Where are we going... Really....?', 'Where are we going&hellip; Really.&hellip;?' ],
956
		];
957
	}
958
959
	/**
960
	 * Test smart_ellipses.
961
	 *
962
	 * @coversNothing
963
	 *
964
	 * @uses PHP_Typography\Text_Parser
965
	 * @uses PHP_Typography\Text_Parser\Token
966
	 *
967
	 * @dataProvider provide_smart_ellipses_data
968
	 *
969
	 * @param string $input  HTML intput.
970
	 * @param string $result Expected result.
971
	 */
972
	public function test_smart_ellipses( $input, $result ) {
973
		$this->s->set_smart_ellipses( true );
974
975
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
976
	}
977
978
	/**
979
	 * Test smart_ellipses.
980
	 *
981
	 * @coversNothing
982
	 *
983
	 * @uses PHP_Typography\Text_Parser
984
	 * @uses PHP_Typography\Text_Parser\Token
985
	 *
986
	 * @dataProvider provide_smart_ellipses_data
987
	 *
988
	 * @param string $input  HTML intput.
989
	 * @param string $result Ignored.
990
	 */
991
	public function test_smart_ellipses_off( $input, $result ) {
992
		$this->s->set_smart_ellipses( false );
993
994
		$this->assertSame( $input, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
995
	}
996
997
	/**
998
	 * Provide data for testing smart_diacritics.
999
	 *
1000
	 * @return array
1001
	 */
1002
	public function provide_smart_diacritics_data() {
1003
		return [
1004
			[ '<p>creme brulee</p>', '<p>crème brûlée</p>', 'en-US' ],
1005
			[ 'no diacritics to replace, except creme', 'no diacritics to replace, except crème', 'en-US' ],
1006
			[ 'ne vs. seine vs einzelne', 'né vs. seine vs einzelne', 'de-DE' ],
1007
			[ 'ne vs. sei&shy;ne vs einzelne', 'né vs. sei&shy;ne vs einzelne', 'de-DE' ],
1008
		];
1009
	}
1010
1011
	/**
1012
	 * Test smart_diacritics.
1013
	 *
1014
	 * @coversNothing
1015
	 *
1016
	 * @uses PHP_Typography\Text_Parser
1017
	 * @uses PHP_Typography\Text_Parser\Token
1018
	 *
1019
	 * @dataProvider provide_smart_diacritics_data
1020
	 *
1021
	 * @param string $html   HTML input.
1022
	 * @param string $result Expected result.
1023
	 * @param string $lang   Language code.
1024
	 */
1025
	public function test_smart_diacritics( $html, $result, $lang ) {
1026
		$this->s->set_smart_diacritics( true );
1027
		$this->s->set_diacritic_language( $lang );
1028
1029
		$this->assertSame( $this->clean_html( $result ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
1030
	}
1031
1032
	/**
1033
	 * Test smart_diacritics.
1034
	 *
1035
	 * @coversNothing
1036
	 *
1037
	 * @uses PHP_Typography\Text_Parser
1038
	 * @uses PHP_Typography\Text_Parser\Token
1039
	 *
1040
	 * @dataProvider provide_smart_diacritics_data
1041
	 *
1042
	 * @param string $html   HTML input.
1043
	 * @param string $result Ignored.
1044
	 * @param string $lang   Language code.
1045
	 */
1046
	public function test_smart_diacritics_off( $html, $result, $lang ) {
1047
		$this->s->set_smart_diacritics( false );
1048
		$this->s->set_diacritic_language( $lang );
1049
1050
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
1051
	}
1052
1053
	/**
1054
	 * Provide data for testing smart_diacritics.
1055
	 *
1056
	 * @return array
1057
	 */
1058
	public function provide_smart_diacritics_error_in_pattern_data() {
1059
		return [
1060
			[ 'no diacritics to replace, except creme', 'en-US', 'creme' ],
1061
		];
1062
	}
1063
1064
	/**
1065
	 * Test smart_diacritics.
1066
	 *
1067
	 * @coversNothing
1068
	 *
1069
	 * @uses PHP_Typography\Text_Parser
1070
	 * @uses PHP_Typography\Text_Parser\Token
1071
	 *
1072
	 * @dataProvider provide_smart_diacritics_error_in_pattern_data
1073
	 *
1074
	 * @param string $html   HTML input.
1075
	 * @param string $lang   Language code.
1076
	 * @param string $unset  Replacement to unset.
1077
	 */
1078
	public function test_smart_diacritics_error_in_pattern( $html, $lang, $unset ) {
1079
1080
		$this->s->set_smart_diacritics( true );
1081
		$this->s->set_diacritic_language( $lang );
1082
		$s = $this->s;
1083
1084
		$replacements = $s['diacriticReplacement'];
1085
		unset( $replacements['replacements'][ $unset ] );
1086
		$s['diacriticReplacement'] = $replacements;
1087
1088
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $s, false ) ) );
1089
	}
1090
1091
	/**
1092
	 * Provide data for testing smart_marks.
1093
	 *
1094
	 * @return array
1095
	 */
1096
	public function provide_smart_marks_data() {
1097
		return [
1098
			[ '(c)',  '&copy;' ],
1099
			[ '(C)',  '&copy;' ],
1100
			[ '(r)',  '&reg;' ],
1101
			[ '(R)',  '&reg;' ],
1102
			[ '(p)',  '&#8471;' ],
1103
			[ '(P)',  '&#8471;' ],
1104
			[ '(sm)', '&#8480;' ],
1105
			[ '(SM)', '&#8480;' ],
1106
			[ '(tm)', '&trade;' ],
1107
			[ '(TM)', '&trade;' ],
1108
			[ '501(c)(1)', '501(c)(1)' ],      // protected.
1109
			[ '501(c)(29)', '501(c)(29)' ],    // protected.
1110
			[ '501(c)(30)', '501&copy;(30)' ], // not protected.
1111
		];
1112
	}
1113
1114
	/**
1115
	 * Test smart_marks.
1116
	 *
1117
	 * @coversNothing
1118
	 *
1119
	 * @uses PHP_Typography\Text_Parser
1120
	 * @uses PHP_Typography\Text_Parser\Token
1121
	 *
1122
	 * @dataProvider provide_smart_marks_data
1123
	 *
1124
	 * @param string $input  HTML input.
1125
	 * @param string $result Expected result.
1126
	 */
1127
	public function test_smart_marks( $input, $result ) {
1128
		$this->s->set_smart_marks( true );
1129
1130
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1131
	}
1132
1133
	/**
1134
	 * Test smart_marks.
1135
	 *
1136
	 * @coversNothing
1137
	 *
1138
	 * @uses PHP_Typography\Text_Parser
1139
	 * @uses PHP_Typography\Text_Parser\Token
1140
	 *
1141
	 * @dataProvider provide_smart_marks_data
1142
	 *
1143
	 * @param string $input  HTML input.
1144
	 * @param string $result Ignored.
1145
	 */
1146
	public function test_smart_marks_off( $input, $result ) {
1147
		$this->s->set_smart_marks( false );
1148
1149
		$this->assertSame( $input, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1150
	}
1151
1152
	/**
1153
	 * Data provider for smarth_math test.
1154
	 */
1155
	public function provide_smart_math_data() {
1156
		return [
1157
			[ 'xx 7&minus;3=4 xx',              'xx 7-3=4 xx',      true ],
1158
			[ 'xx 3&times;3=5&divide;2 xx',     'xx 3*3=5/2 xx',    true ],
1159
			[ 'xx 0815-4711 xx',                'xx 0815-4711 xx',  true ],
1160
			[ 'xx 1/2 xx',                      'xx 1/2 xx',        true ],
1161
			[ 'xx 2001-13-12 xx',               'xx 2001-13-12 xx', false ], // not a valid date.
1162
			[ 'xx 2001-12-13 xx',               'xx 2001-12-13 xx', true ],
1163
			[ 'xx 2001-13-13 xx',               'xx 2001-13-13 xx', false ], // not a valid date.
1164
			[ 'xx 13-12-2002 xx',               'xx 13-12-2002 xx', true ],
1165
			[ 'xx 12-13-2002 xx',               'xx 12-13-2002 xx', true ],
1166
			[ 'xx 13-13-2002 xx',               'xx 13-13-2002 xx', false ], // not a valid date.
1167
			[ 'xx 2001-12 xx',                  'xx 2001-12 xx',    true ],
1168
			[ 'xx 2001-13 xx',                  'xx 2001-13 xx',    true ], // apparently a valid day count.
1169
			[ 'xx 2001-100 xx',                 'xx 2001-100 xx',   true ],
1170
			[ 'xx 12/13/2010 xx',               'xx 12/13/2010 xx', true ],
1171
			[ 'xx 13/12/2010 xx',               'xx 13/12/2010 xx', true ],
1172
			[ 'xx 13&divide;13&divide;2010 xx', 'xx 13/13/2010 xx', true ], // not a valid date.
1173
		];
1174
	}
1175
1176
	/**
1177
	 * Test smart_math.
1178
	 *
1179
	 * @coversNothing
1180
	 *
1181
	 * @uses PHP_Typography\Text_Parser
1182
	 * @uses PHP_Typography\Text_Parser\Token
1183
	 *
1184
	 * @dataProvider provide_smart_math_data
1185
	 *
1186
	 * @param string $result Expected result.
1187
	 * @param string $input  HTML input.
1188
	 * @param bool   $same   Result expected to be the same or not the same.
1189
	 */
1190
	public function test_smart_math( $result, $input, $same ) {
1191
		$this->s->set_smart_math( true );
1192
1193
		if ( $same ) {
1194
			$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1195
		} else {
1196
			$this->assertNotSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1197
		}
1198
	}
1199
1200
	/**
1201
	 * Test smart_math.
1202
	 *
1203
	 * @coversNothing
1204
	 *
1205
	 * @uses PHP_Typography\Text_Parser
1206
	 * @uses PHP_Typography\Text_Parser\Token
1207
	 *
1208
	 * @dataProvider provide_smart_math_data
1209
	 *
1210
	 * @param string $result Ignored.
1211
	 * @param string $input  HTML input.
1212
	 * @param bool   $same   Ignored.
1213
	 */
1214
	public function test_smart_math_off( $result, $input, $same ) {
1215
		$this->s->set_smart_math( false );
1216
1217
		$this->assertSame( $input, $this->typo->process( $input, $this->s ) );
1218
	}
1219
1220
	/**
1221
	 * Test smart_exponents.
1222
	 *
1223
	 * @coversNothing
1224
	 *
1225
	 * @uses PHP_Typography\Text_Parser
1226
	 * @uses PHP_Typography\Text_Parser\Token
1227
	 */
1228
	public function test_smart_exponents() {
1229
		$this->s->set_smart_exponents( true );
1230
1231
		$this->assertSame( '10<sup>12</sup>', $this->typo->process( '10^12', $this->s ) );
1232
	}
1233
1234
	/**
1235
	 * Test smart_exponents.
1236
	 *
1237
	 * @coversNothing
1238
	 *
1239
	 * @uses PHP_Typography\Text_Parser
1240
	 * @uses PHP_Typography\Text_Parser\Token
1241
	 */
1242
	public function test_smart_exponents_off() {
1243
		$this->s->set_smart_exponents( false );
1244
1245
		$this->assertSame( '10^12', $this->typo->process( '10^12', $this->s ) );
1246
	}
1247
1248
	/**
1249
	 * Provide data for testing smart_fractions.
1250
	 *
1251
	 * @return array
1252
	 */
1253
	public function provide_smart_fractions_data() {
1254
		return [
1255
			[
1256
				'1/2 3/300 999/1000',
1257
				'<sup>1</sup>&frasl;<sub>2</sub> <sup>3</sup>&frasl;<sub>300</sub> <sup>999</sup>&frasl;<sub>1000</sub>',
1258
				'<sup>1</sup>&frasl;<sub>2</sub>&#8239;<sup>3</sup>&frasl;<sub>300</sub> <sup>999</sup>&frasl;<sub>1000</sub>',
1259
				'',
1260
				'',
1261
			],
1262
			[
1263
				'1/2 4/2015 1999/2000 999/1000',
1264
				'<sup>1</sup>&frasl;<sub>2</sub> 4/2015 1999/2000 <sup>999</sup>&frasl;<sub>1000</sub>',
1265
				'<sup>1</sup>&frasl;<sub>2</sub>&#8239;4/2015 1999/2000&#8239;<sup>999</sup>&frasl;<sub>1000</sub>',
1266
				'',
1267
				'',
1268
			],
1269
			[
1270
				'1/2 3/300 999/1000',
1271
				'<sup class="num">1</sup>&frasl;<sub class="denom">2</sub> <sup class="num">3</sup>&frasl;<sub class="denom">300</sub> <sup class="num">999</sup>&frasl;<sub class="denom">1000</sub>',
1272
				'<sup class="num">1</sup>&frasl;<sub class="denom">2</sub>&#8239;<sup class="num">3</sup>&frasl;<sub class="denom">300</sub> <sup class="num">999</sup>&frasl;<sub class="denom">1000</sub>',
1273
				'num',
1274
				'denom',
1275
			],
1276
			[
1277
				'1/2 4/2015 1999/2000 999/1000',
1278
				'<sup class="num">1</sup>&frasl;<sub class="denom">2</sub> 4/2015 1999/2000 <sup class="num">999</sup>&frasl;<sub class="denom">1000</sub>',
1279
				'<sup class="num">1</sup>&frasl;<sub class="denom">2</sub>&#8239;4/2015 1999/2000&#8239;<sup class="num">999</sup>&frasl;<sub class="denom">1000</sub>',
1280
				'num',
1281
				'denom',
1282
			],
1283
		];
1284
	}
1285
1286
	/**
1287
	 * Test smart_fractions.
1288
	 *
1289
	 * @coversNothing
1290
	 *
1291
	 * @uses PHP_Typography\Text_Parser
1292
	 * @uses PHP_Typography\Text_Parser\Token
1293
	 *
1294
	 * @dataProvider provide_smart_fractions_data
1295
	 *
1296
	 * @param string $input           HTML input.
1297
	 * @param string $result          Expected result.
1298
	 * @param string $result_spacing  Expected result with spacing enabled.
1299
	 * @param string $num_css_class   CSS class for numerator.
1300
	 * @param string $denom_css_class CSS class for denominator.
1301
	 */
1302
	public function test_smart_fractions( $input, $result, $result_spacing, $num_css_class, $denom_css_class ) {
1303
		$typo = new PHP_Typography_CSS_Classes( 'now', [
1304
			'numerator'   => $num_css_class,
1305
			'denominator' => $denom_css_class,
1306
		] );
1307
		$this->s->set_smart_fractions( true );
1308
		$this->s->set_true_no_break_narrow_space( true );
1309
1310
		$this->s->set_fraction_spacing( false );
1311
		$this->assertSame( $result, $this->clean_html( $typo->process( $input, $this->s ) ) );
1312
1313
		$this->s->set_fraction_spacing( true );
1314
		$this->assertSame( $result_spacing, $this->clean_html( $typo->process( $input, $this->s ) ) );
1315
	}
1316
1317
1318
	/**
1319
	 * Test smart_fractions.
1320
	 *
1321
	 * @coversNothing
1322
	 *
1323
	 * @uses PHP_Typography\Text_Parser
1324
	 * @uses PHP_Typography\Text_Parser\Token
1325
	 *
1326
	 * @dataProvider provide_smart_fractions_data
1327
	 *
1328
	 * @param string $input           HTML input.
1329
	 * @param string $result          Expected result.
1330
	 * @param string $result_spacing  Expected result with spacing enabled.
1331
	 * @param string $num_css_class   CSS class for numerator.
1332
	 * @param string $denom_css_class CSS class for denominator.
1333
	 */
1334
	public function test_smart_fractions_off( $input, $result, $result_spacing, $num_css_class, $denom_css_class ) {
1335
		$typo = new PHP_Typography_CSS_Classes( 'now', [
1336
			'numerator'   => $num_css_class,
1337
			'denominator' => $denom_css_class,
1338
		] );
1339
		$this->s->set_smart_fractions( false );
1340
		$this->s->set_fraction_spacing( false );
1341
1342
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $typo->process( $input, $this->s ) ) );
1343
	}
1344
1345
	/**
1346
	 * Provide data for testing smart_fractions and smart_quotes together.
1347
	 *
1348
	 * @return array
1349
	 */
1350
	public function provide_smart_fractions_smart_quotes_data() {
1351
		return [
1352
			[
1353
				'1/2" 1/2\' 3/4″',
1354
				'<sup class="num">1</sup>&frasl;<sub class="denom">2</sub>&Prime; <sup class="num">1</sup>&frasl;<sub class="denom">2</sub>&prime; <sup class="num">3</sup>&frasl;<sub class="denom">4</sub>&Prime;',
1355
				'num',
1356
				'denom',
1357
			],
1358
		];
1359
	}
1360
1361
	/**
1362
	 * Test smart_fractions.
1363
	 *
1364
	 * @coversNothing
1365
	 *
1366
	 * @uses PHP_Typography\Text_Parser
1367
	 * @uses PHP_Typography\Text_Parser\Token
1368
	 *
1369
	 * @dataProvider provide_smart_fractions_smart_quotes_data
1370
	 *
1371
	 * @param string $input           HTML input.
1372
	 * @param string $result          Expected result.
1373
	 * @param string $num_css_class   CSS class for numerator.
1374
	 * @param string $denom_css_class CSS class for denominator.
1375
	 */
1376
	public function test_smart_fractions_with_smart_quotes( $input, $result, $num_css_class, $denom_css_class ) {
1377
		$typo = new PHP_Typography_CSS_Classes( 'now', [
1378
			'numerator'   => $num_css_class,
1379
			'denominator' => $denom_css_class,
1380
		] );
1381
		$this->s->set_smart_fractions( true );
1382
		$this->s->set_smart_quotes( true );
1383
		$this->s->set_true_no_break_narrow_space( true );
1384
		$this->s->set_fraction_spacing( false );
1385
1386
		$this->assertSame( $result, $this->clean_html( $typo->process( $input, $this->s ) ) );
1387
	}
1388
1389
	/**
1390
	 * Provide data for testing fraction spacing.
1391
	 *
1392
	 * @return array
1393
	 */
1394
	public function provide_fraction_spacing_data() {
1395
		return [
1396
			[
1397
				'1/2 3/300 999/1000',
1398
				'1/2&nbsp;3/300 999/1000',
1399
			],
1400
			[
1401
				'1/2 4/2015 1999/2000 999/1000',
1402
				'1/2&nbsp;4/2015 1999/2000&nbsp;999/1000',
1403
			],
1404
		];
1405
	}
1406
1407
1408
	/**
1409
	 * Test smart_fractions.
1410
	 *
1411
	 * @coversNothing
1412
	 *
1413
	 * @uses PHP_Typography\Text_Parser
1414
	 * @uses PHP_Typography\Text_Parser\Token
1415
	 *
1416
	 * @dataProvider provide_fraction_spacing_data
1417
	 *
1418
	 * @param string $input  HTML input.
1419
	 * @param string $result Expected result.
1420
	 */
1421
	public function test_smart_fractions_only_spacing( $input, $result ) {
1422
		$this->s->set_smart_fractions( false );
1423
		$this->s->set_fraction_spacing( true );
1424
1425
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1426
	}
1427
1428
	/**
1429
	 * Provide data for testing ordinal suffixes.
1430
	 *
1431
	 * @return array
1432
	 */
1433
	public function provide_smart_ordinal_suffix() {
1434
		return [
1435
			[ 'in the 1st instance',      'in the 1<sup>st</sup> instance', '' ],
1436
			[ 'in the 2nd degree',        'in the 2<sup>nd</sup> degree',   '' ],
1437
			[ 'a 3rd party',              'a 3<sup>rd</sup> party',         '' ],
1438
			[ '12th Night',               '12<sup>th</sup> Night',          '' ],
1439
			[ 'in the 1st instance, we',  'in the 1<sup class="ordinal">st</sup> instance, we',  'ordinal' ],
1440
			[ 'murder in the 2nd degree', 'murder in the 2<sup class="ordinal">nd</sup> degree', 'ordinal' ],
1441
			[ 'a 3rd party',              'a 3<sup class="ordinal">rd</sup> party',              'ordinal' ],
1442
			[ 'the 12th Night',           'the 12<sup class="ordinal">th</sup> Night',           'ordinal' ],
1443
		];
1444
	}
1445
1446
	/**
1447
	 * Test smart_ordinal_suffix.
1448
	 *
1449
	 * @coversNothing
1450
	 *
1451
	 * @uses PHP_Typography\Text_Parser
1452
	 * @uses PHP_Typography\Text_Parser\Token
1453
	 *
1454
	 * @dataProvider provide_smart_ordinal_suffix
1455
	 *
1456
	 * @param string $input     HTML input.
1457
	 * @param string $result    Expected result.
1458
	 * @param string $css_class CSS class for ordinal suffix.
1459
	 */
1460
	public function test_smart_ordinal_suffix( $input, $result, $css_class ) {
1461
		$typo = new PHP_Typography_CSS_Classes( 'now', [
1462
			'ordinal' => $css_class,
1463
		] );
1464
		$this->s->set_smart_ordinal_suffix( true );
1465
1466
		$this->assertSame( $result, $this->clean_html( $typo->process( $input, $this->s ) ) );
1467
	}
1468
1469
1470
	/**
1471
	 * Test smart_ordinal_suffix.
1472
	 *
1473
	 * @coversNothing
1474
	 *
1475
	 * @uses PHP_Typography\Text_Parser
1476
	 * @uses PHP_Typography\Text_Parser\Token
1477
	 *
1478
	 * @dataProvider provide_smart_ordinal_suffix
1479
	 *
1480
	 * @param string $input     HTML input.
1481
	 * @param string $result    Expected result.
1482
	 * @param string $css_class CSS class for ordinal suffix.
1483
	 */
1484
	public function test_smart_ordinal_suffix_off( $input, $result, $css_class ) {
1485
		$typo = new PHP_Typography_CSS_Classes( 'now', [
1486
			'ordinal' => $css_class,
1487
		] );
1488
		$this->s->set_smart_ordinal_suffix( false );
1489
1490
		$this->assertSame( $input, $this->clean_html( $typo->process( $input, $this->s ) ) );
1491
	}
1492
1493
	/**
1494
	 * Provide data for testing single character word spacing.
1495
	 *
1496
	 * @return array
1497
	 */
1498
	public function provide_single_character_word_spacing_data() {
1499
		return [
1500
			[ 'A cat in a tree', 'A cat in a&nbsp;tree' ],
1501
			[ 'Works with strange characters like ä too. But not Ä or does it?', 'Works with strange characters like &auml;&nbsp;too. But not &Auml;&nbsp;or does it?' ],
1502
			[ 'Should work even here: <span>a word</span> does not want to be alone.', 'Should work even here: <span>a&nbsp;word</span> does not want to be alone.' ],
1503
			[ 'And here:<span> </span>a word does not want to be alone.', 'And here:<span> </span>a&nbsp;word does not want to be alone.' ],
1504
		];
1505
	}
1506
1507
	/**
1508
	 * Test single_character_word_spacing.
1509
	 *
1510
	 * @coversNothing
1511
	 *
1512
	 * @uses PHP_Typography\Text_Parser
1513
	 * @uses PHP_Typography\Text_Parser\Token
1514
	 *
1515
	 * @dataProvider provide_single_character_word_spacing_data
1516
	 *
1517
	 * @param string $input  HTML input.
1518
	 * @param string $result Expected result.
1519
	 */
1520
	public function test_single_character_word_spacing( $input, $result ) {
1521
		$this->s->set_single_character_word_spacing( true );
1522
1523
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1524
	}
1525
1526
1527
	/**
1528
	 * Test single_character_word_spacing.
1529
	 *
1530
	 * @coversNothing
1531
	 *
1532
	 * @uses PHP_Typography\Text_Parser
1533
	 * @uses PHP_Typography\Text_Parser\Token
1534
	 *
1535
	 * @dataProvider provide_single_character_word_spacing_data
1536
	 *
1537
	 * @param string $input  HTML input.
1538
	 * @param string $result Expected result.
1539
	 */
1540
	public function test_single_character_word_spacing_off( $input, $result ) {
1541
		$this->s->set_single_character_word_spacing( false );
1542
1543
		$this->assertSame( $input, $this->typo->process( $input, $this->s ) );
1544
	}
1545
1546
	/**
1547
	 * Provide data for testing dash spacing.
1548
	 *
1549
	 * @return array
1550
	 */
1551
	public function provide_dash_spacing_data() {
1552
		return [
1553
			[
1554
				'Ein - mehr oder weniger - guter Gedanke, 1908-2008',
1555
				'Ein&thinsp;&mdash;&thinsp;mehr oder weniger&thinsp;&mdash;&thinsp;guter Gedanke, 1908&thinsp;&ndash;&thinsp;2008',
1556
				'Ein &ndash; mehr oder weniger &ndash; guter Gedanke, 1908&#8202;&ndash;&#8202;2008',
1557
			],
1558
			[
1559
				"We just don't know --- really---, but you know, --",
1560
				"We just don't know&thinsp;&mdash;&thinsp;really&thinsp;&mdash;&thinsp;, but you know, &ndash;",
1561
				"We just don't know&#8202;&mdash;&#8202;really&#8202;&mdash;&#8202;, but you know, &ndash;",
1562
			],
1563
			[
1564
				'Auch 3.-8. März sollte die - richtigen - Gedankenstriche verwenden.',
1565
				'Auch 3.&thinsp;&ndash;&thinsp;8. M&auml;rz sollte die&thinsp;&mdash;&thinsp;richtigen&thinsp;&mdash;&thinsp;Gedankenstriche verwenden.',
1566
				'Auch 3.&#8202;&ndash;&#8202;8. M&auml;rz sollte die &ndash; richtigen &ndash; Gedankenstriche verwenden.',
1567
			],
1568
		];
1569
	}
1570
1571
	/**
1572
	 * Provide data for testing smart dashes.
1573
	 *
1574
	 * @return array
1575
	 */
1576
	public function provide_smart_dashes_data() {
1577
		return [
1578
			[
1579
				'Ein - mehr oder weniger - guter Gedanke, 1908-2008',
1580
				'Ein &mdash; mehr oder weniger &mdash; guter Gedanke, 1908&ndash;2008',
1581
				'Ein &ndash; mehr oder weniger &ndash; guter Gedanke, 1908&ndash;2008',
1582
			],
1583
			[
1584
				"We just don't know --- really---, but you know, --",
1585
				"We just don't know &mdash; really&mdash;, but you know, &ndash;",
1586
				"We just don't know &mdash; really&mdash;, but you know, &ndash;",
1587
			],
1588
			[
1589
				'что природа жизни - это Блаженство',
1590
				'что природа жизни &mdash; это Блаженство',
1591
				'что природа жизни &ndash; это Блаженство',
1592
			],
1593
			[
1594
				'Auch 3.-8. März sollte die - richtigen - Gedankenstriche verwenden.',
1595
				'Auch 3.&ndash;8. M&auml;rz sollte die &mdash; richtigen &mdash; Gedankenstriche verwenden.',
1596
				'Auch 3.&ndash;8. M&auml;rz sollte die &ndash; richtigen &ndash; Gedankenstriche verwenden.',
1597
			],
1598
			[
1599
				'20.-30.',
1600
				'20.&ndash;30.',
1601
				'20.&ndash;30.',
1602
			],
1603
		];
1604
	}
1605
1606
	/**
1607
	 * Provide data for testing smart dashes (where hyphen should not be changed).
1608
	 *
1609
	 * @return array
1610
	 */
1611
	public function provide_dash_spacing_unchanged_data() {
1612
		return [
1613
			[ 'Vor- und Nachteile, i-Tüpfelchen, 100-jährig, Fritz-Walter-Stadion, 2015-12-03, 01-01-1999, 2012-04' ],
1614
			[ 'Bananen-Milch und -Brot' ],
1615
			[ 'pick-me-up' ],
1616
			[ 'You may see a yield that is two-, three-, or fourfold.' ],
1617
		];
1618
	}
1619
1620
1621
	/**
1622
	 * Test dash_spacing.
1623
	 *
1624
	 * @coversNothing
1625
	 *
1626
	 * @uses PHP_Typography\Text_Parser
1627
	 * @uses PHP_Typography\Text_Parser\Token
1628
	 *
1629
	 * @dataProvider provide_dash_spacing_data
1630
	 *
1631
	 * @param string $input      HTML input.
1632
	 * @param string $result_us  Entity-escaped result with US dash style.
1633
	 * @param string $result_int Entity-escaped result with international dash style.
1634
	 */
1635
	public function test_dash_spacing( $input, $result_us, $result_int ) {
1636
		$this->s->set_smart_dashes( true );
1637
		$this->s->set_dash_spacing( true );
1638
1639
		$this->s->set_smart_dashes_style( 'traditionalUS' );
1640
		$this->assertSame( $result_us, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1641
1642
		$this->s->set_smart_dashes_style( 'international' );
1643
		$this->assertSame( $result_int, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1644
	}
1645
1646
1647
	/**
1648
	 * Test dash_spacing.
1649
	 *
1650
	 * @coversNothing
1651
	 *
1652
	 * @uses PHP_Typography\Text_Parser
1653
	 * @uses PHP_Typography\Text_Parser\Token
1654
	 *
1655
	 * @dataProvider provide_dash_spacing_unchanged_data
1656
	 *
1657
	 * @param string $input  HTML input.
1658
	 */
1659
	public function test_dash_spacing_unchanged( $input ) {
1660
		$this->s->set_smart_dashes( true );
1661
		$this->s->set_dash_spacing( true );
1662
1663
		$this->s->set_smart_dashes_style( 'traditionalUS' );
1664
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1665
1666
		$this->s->set_smart_dashes_style( 'international' );
1667
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1668
	}
1669
1670
	/**
1671
	 * Provide data for special white space collapsing.
1672
	 *
1673
	 * @return array
1674
	 */
1675
	public function provide_space_collapse_data() {
1676
		return [
1677
			[ 'A  new hope&nbsp;  arises.', 'A new hope&nbsp;arises.' ],
1678
			[ 'A &thinsp;new hope &nbsp;  arises.', 'A&thinsp;new hope&nbsp;arises.' ],
1679
			[ '<p>  &nbsp;A  new hope&nbsp;  arises.</p>', '<p>A new hope&nbsp;arises.</p>' ],
1680
		];
1681
	}
1682
1683
1684
	/**
1685
	 * Test space_collapse.
1686
	 *
1687
	 * @coversNothing
1688
	 *
1689
	 * @uses PHP_Typography\Text_Parser
1690
	 * @uses PHP_Typography\Text_Parser\Token
1691
	 *
1692
	 * @dataProvider provide_space_collapse_data
1693
	 *
1694
	 * @param string $input  HTML input.
1695
	 * @param string $result Expected result.
1696
	 */
1697
	public function test_space_collapse( $input, $result ) {
1698
		$this->s->set_space_collapse( true );
1699
1700
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1701
	}
1702
1703
1704
	/**
1705
	 * Test space_collapse.
1706
	 *
1707
	 * @coversNothing
1708
	 *
1709
	 * @uses PHP_Typography\Text_Parser
1710
	 * @uses PHP_Typography\Text_Parser\Token
1711
	 *
1712
	 * @dataProvider provide_space_collapse_data
1713
	 *
1714
	 * @param string $input  HTML input.
1715
	 * @param string $result Expected result.
1716
	 */
1717
	public function test_space_collapse_off( $input, $result ) {
1718
		$this->s->set_space_collapse( false );
1719
1720
		$this->assertSame( $input, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1721
	}
1722
1723
	/**
1724
	 * Provide data for testing unit_spacing.
1725
	 *
1726
	 * @return array
1727
	 */
1728
	public function provide_unit_spacing_data() {
1729
		return [
1730
			[ 'It was 2 m from', 'It was 2&#8239;m from' ],
1731
			[ '3 km/h', '3&#8239;km/h' ],
1732
			[ '5 sg 44 kg', '5 sg 44&#8239;kg' ],
1733
			[ '100 &deg;C', '100&#8239;&deg;C' ],
1734
		];
1735
	}
1736
1737
	/**
1738
	 * Test unit_spacing.
1739
	 *
1740
	 * @coversNothing
1741
	 *
1742
	 * @uses PHP_Typography\Text_Parser
1743
	 * @uses PHP_Typography\Text_Parser\Token
1744
	 *
1745
	 * @dataProvider provide_unit_spacing_data
1746
	 *
1747
	 * @param string $input  HTML input.
1748
	 * @param string $result Expected result.
1749
	 */
1750
	public function test_unit_spacing( $input, $result ) {
1751
		$this->s->set_unit_spacing( true );
1752
		$this->s->set_true_no_break_narrow_space( true );
1753
1754
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1755
	}
1756
1757
	/**
1758
	 * Test unit_spacing.
1759
	 *
1760
	 * @coversNothing
1761
	 *
1762
	 * @uses PHP_Typography\Text_Parser
1763
	 * @uses PHP_Typography\Text_Parser\Token
1764
	 *
1765
	 * @dataProvider provide_unit_spacing_data
1766
	 *
1767
	 * @param string $input  HTML input.
1768
	 * @param string $result Expected result.
1769
	 */
1770
	public function test_unit_spacing_off( $input, $result ) {
1771
		$this->s->set_unit_spacing( false );
1772
1773
		$this->assertSame( $input, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1774
	}
1775
1776
	/**
1777
	 * Provide data for testing unit_spacing.
1778
	 *
1779
	 * @return array
1780
	 */
1781
	public function provide_numbered_abbreviation_spacing_data() {
1782
		return [
1783
			[ 'ÖNORM A 1080:2007', '&Ouml;NORM A&nbsp;1080:2007' ],
1784
			[ 'Das steht in der ÖNORM EN ISO 13920!', 'Das steht in der &Ouml;NORM EN ISO&nbsp;13920!' ],
1785
			[ 'ONR 191160:2010', 'ONR&nbsp;191160:2010' ],
1786
			[ 'DIN ISO 2936', 'DIN ISO&nbsp;2936' ],
1787
			[ 'DIN ISO/IEC 10561', 'DIN ISO/IEC&nbsp;10561' ],
1788
			[ 'VG 96936', 'VG&nbsp;96936' ],
1789
			[ 'LN 9118-2', 'LN&nbsp;9118-2' ],
1790
			[ 'DIN 5032 Lichtmessung', 'DIN&nbsp;5032 Lichtmessung' ],
1791
			[ 'DIN EN 118 Holzschutzmittel', 'DIN EN&nbsp;118 Holzschutzmittel' ],
1792
			[ 'DIN EN ISO 9001 Qualitätsmanagementsysteme', 'DIN EN ISO&nbsp;9001 Qualit&auml;tsmanagementsysteme' ],
1793
			[ 'Enthält E 100.', 'Enth&auml;lt E&nbsp;100.' ],
1794
			[ 'E 160a', 'E&nbsp;160a' ],
1795
			[ 'ISO/IEC 13818', 'ISO/IEC&nbsp;13818' ],
1796
		];
1797
	}
1798
1799
	/**
1800
	 * Test numbered_abbreviation_spacing.
1801
	 *
1802
	 * @coversNothing
1803
	 *
1804
	 * @uses PHP_Typography\Text_Parser
1805
	 * @uses PHP_Typography\Text_Parser\Token
1806
	 *
1807
	 * @dataProvider provide_numbered_abbreviation_spacing_data
1808
	 *
1809
	 * @param string $input  HTML input.
1810
	 * @param string $result Expected result.
1811
	 */
1812
	public function test_numbered_abbreviation_spacing( $input, $result ) {
1813
		$this->s->set_numbered_abbreviation_spacing( true );
1814
1815
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1816
	}
1817
1818
	/**
1819
	 * Test numbered_abbreviation_spacing.
1820
	 *
1821
	 * @coversNothing
1822
	 *
1823
	 * @uses PHP_Typography\Text_Parser
1824
	 * @uses PHP_Typography\Text_Parser\Token
1825
	 *
1826
	 * @dataProvider provide_numbered_abbreviation_spacing_data
1827
	 *
1828
	 * @param string $input  HTML input.
1829
	 * @param string $result Expected result.
1830
	 */
1831
	public function test_numbered_abbreviation_spacing_off( $input, $result ) {
1832
		$this->s->set_numbered_abbreviation_spacing( false );
1833
1834
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1835
	}
1836
1837
	/**
1838
	 * Provide data for testing French punctuation rules.
1839
	 *
1840
	 * @return array
1841
	 */
1842
	public function provide_french_punctuation_spacing_data() {
1843
		return [
1844
			[ "Je t'aime ; m'aimes-tu ?", "Je t'aime&#8239;; m'aimes-tu&#8239;?", false ],
1845
			[ "Je t'aime; m'aimes-tu?", "Je t'aime&#8239;; m'aimes-tu&#8239;?", false ],
1846
			[ 'Au secours !', 'Au secours&#8239;!', false ],
1847
			[ 'Au secours!', 'Au secours&#8239;!', false ],
1848
			[ 'Jean a dit : Foo', 'Jean a dit&nbsp;: Foo', false ],
1849
			[ 'Jean a dit: Foo', 'Jean a dit&nbsp;: Foo', false ],
1850
			[ 'http://example.org', 'http://example.org', false ],
1851
			[ 'foo &Ouml; & ; bar', 'foo &Ouml; &amp; ; bar', false ],
1852
			[ '5 > 3', '5 > 3', false ],
1853
			[ 'Les « courants de bord ouest » du Pacifique ? Eh bien : ils sont "fabuleux".', 'Les &laquo;&#8239;courants de bord ouest&#8239;&raquo; du Pacifique&#8239;? Eh bien&nbsp;: ils sont "fabuleux".', false ],
1854
			[ '"diabète de type 3"', '&laquo;&#8239;diab&egrave;te de type 3&#8239;&raquo;', true ],
1855
			[ '« Hello, this is a sentence. »', '&laquo;&#8239;Hello, this is a sentence.&#8239;&raquo;', false ],
1856
			[ 'À «programmer»?', '&Agrave; &laquo;&#8239;programmer&#8239;&raquo;&#8239;?', false ],
1857
			[ 'À "programmer"?', '&Agrave; &laquo;&#8239;programmer&#8239;&raquo;&#8239;?', true ],
1858
			[ 'À "programmer":', '&Agrave; &laquo;&#8239;programmer&#8239;&raquo;&nbsp;:', true ],
1859
			[ '<a href="#">foo</a>: bar', '<a href="#">foo</a>&nbsp;: bar', true ],
1860
			[ 'À «<a href="#">programmer</a>»:', '&Agrave; &laquo;&#8239;<a href="#">programmer</a>&#8239;&raquo;&nbsp;:', true ],
1861
		];
1862
	}
1863
1864
1865
	/**
1866
	 * Test french_punctuation_spacing.
1867
	 *
1868
	 * @coversNothing
1869
	 *
1870
	 * @uses PHP_Typography\Text_Parser
1871
	 * @uses PHP_Typography\Text_Parser\Token
1872
	 * @uses ::set_smart_quotes
1873
	 * @uses ::set_smart_quotes_primary
1874
	 *
1875
	 * @dataProvider provide_french_punctuation_spacing_data
1876
	 *
1877
	 * @param string $input             HTML input.
1878
	 * @param string $result            Expected result.
1879
	 * @param bool   $use_french_quotes Enable French primary quotes style.
1880
	 */
1881
	public function test_french_punctuation_spacing( $input, $result, $use_french_quotes ) {
1882
		$this->s->set_french_punctuation_spacing( true );
1883
		$this->s->set_true_no_break_narrow_space( true );
1884
1885
		if ( $use_french_quotes ) {
1886
			$this->s->set_smart_quotes_primary( 'doubleGuillemetsFrench' );
1887
			$this->s->set_smart_quotes( true );
1888
		}
1889
1890
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1891
	}
1892
1893
1894
	/**
1895
	 * Test french_punctuation_spacing.
1896
	 *
1897
	 * @coversNothing
1898
	 *
1899
	 * @uses PHP_Typography\Text_Parser
1900
	 * @uses PHP_Typography\Text_Parser\Token
1901
	 *
1902
	 * @dataProvider provide_french_punctuation_spacing_data
1903
	 *
1904
	 * @param string $input  HTML input.
1905
	 * @param string $result Expected result.
1906
	 */
1907
	public function test_french_punctuation_spacing_off( $input, $result ) {
1908
		$this->s->set_french_punctuation_spacing( false );
1909
1910
		$this->assertSame( $this->clean_html( $input ), $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1911
	}
1912
1913
	/**
1914
	 * Provide data for testing wrap_hard_hyphens.
1915
	 *
1916
	 * @return array
1917
	 */
1918
	public function provide_wrap_hard_hyphens_data() {
1919
		return [
1920
			[ 'This-is-a-hyphenated-word', 'This-&#8203;is-&#8203;a-&#8203;hyphenated-&#8203;word' ],
1921
			[ 'This-is-a-hyphenated-', 'This-&#8203;is-&#8203;a-&#8203;hyphenated-' ],
1922
1923
		];
1924
	}
1925
1926
1927
	/**
1928
	 * Test wrap_hard_hyphens.
1929
	 *
1930
	 * @coversNothing
1931
	 *
1932
	 * @uses PHP_Typography\Text_Parser
1933
	 * @uses PHP_Typography\Text_Parser\Token
1934
	 *
1935
	 * @dataProvider provide_wrap_hard_hyphens_data
1936
	 *
1937
	 * @param string $input  HTML input.
1938
	 * @param string $result Expected result.
1939
	 */
1940
	public function test_wrap_hard_hyphens( $input, $result ) {
1941
		$this->typo->process( '', $this->s );
1942
		$this->s->set_wrap_hard_hyphens( true );
1943
		$s = $this->s;
1944
1945
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1946
	}
1947
1948
1949
	/**
1950
	 * Test wrap_hard_hyphens.
1951
	 *
1952
	 * @coversNothing
1953
	 *
1954
	 * @uses PHP_Typography\Text_Parser
1955
	 * @uses PHP_Typography\Text_Parser\Token
1956
	 *
1957
	 * @dataProvider provide_wrap_hard_hyphens_data
1958
	 *
1959
	 * @param string $input  HTML input.
1960
	 * @param string $result Expected result.
1961
	 */
1962
	public function test_wrap_hard_hyphens_with_smart_dashes( $input, $result ) {
1963
		$this->typo->process( '', $this->s );
1964
		$this->s->set_wrap_hard_hyphens( true );
1965
		$this->s->set_smart_dashes( true );
1966
1967
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
1968
	}
1969
1970
1971
	/**
1972
	 * Test wrap_hard_hyphens.
1973
	 *
1974
	 * @coversNothing
1975
	 *
1976
	 * @uses PHP_Typography\Text_Parser
1977
	 * @uses PHP_Typography\Text_Parser\Token
1978
	 *
1979
	 * @dataProvider provide_wrap_hard_hyphens_data
1980
	 *
1981
	 * @param string $input  HTML input.
1982
	 * @param string $result Expected result.
1983
	 */
1984
	public function test_wrap_hard_hyphens_off( $input, $result ) {
1985
		$this->typo->process( '', $this->s );
1986
		$this->s->set_wrap_hard_hyphens( false );
1987
1988
		$this->assertSame( $input, $this->typo->process( $input, $this->s ) );
1989
	}
1990
1991
	/**
1992
	 * Provide data for testing dewidowing.
1993
	 *
1994
	 * @return array
1995
	 */
1996
	public function provide_dewidow_data() {
1997
		return [
1998
			[ 'bla foo b', 'bla foo&nbsp;b', 3, 2 ],
1999
			[ 'bla foo&thinsp;b', 'bla foo&thinsp;b', 3, 2 ], // don't replace thin space...
2000
			[ 'bla foo&#8202;b', 'bla foo&#8202;b', 3, 2 ],   // ... or hair space.
2001
			[ 'bla foo bar', 'bla foo bar', 2, 2 ],
2002
			[ 'bla foo bär...', 'bla foo&nbsp;b&auml;r...', 3, 3 ],
2003
			[ 'bla foo&nbsp;bär...', 'bla foo&nbsp;b&auml;r...', 3, 3 ],
2004
			[ 'bla föö&#8203;bar s', 'bla f&ouml;&ouml;&#8203;bar&nbsp;s', 3, 2 ],
2005
			[ 'bla foo&#8203;bar s', 'bla foo&#8203;bar s', 2, 2 ],
2006
			[ 'bla foo&shy;bar', 'bla foo&shy;bar', 3, 3 ], // &shy; not matched.
2007
			[ 'bla foo&shy;bar bar', 'bla foo&shy;bar&nbsp;bar', 3, 3 ], // &shy; not matched, but syllable after is.
2008
			[ 'bla foo&#8203;bar bar', 'bla foo&#8203;bar&nbsp;bar', 3, 3 ],
2009
			[ 'bla foo&nbsp;bar bar', 'bla foo&nbsp;bar bar', 3, 3 ], // widow not replaced because the &nbsp; would pull too many letters from previous.
2010
		];
2011
	}
2012
2013
	/**
2014
	 * Provide data for testing dewidowing.
2015
	 *
2016
	 * @return array
2017
	 */
2018
	public function provide_dewidow_with_hyphenation_data() {
2019
		return [
2020
			[ 'this is riding ri...', 'this is rid&shy;ing&nbsp;ri...', 4, 2 ],
2021
			[ 'this is riding riding', 'this is rid&shy;ing riding', 4, 2 ], // No soft hyphens in widows.
2022
		];
2023
	}
2024
2025
	/**
2026
	 * Test dewidow.
2027
	 *
2028
	 * @coversNothing
2029
	 *
2030
	 * @uses PHP_Typography\Text_Parser
2031
	 * @uses PHP_Typography\Text_Parser\Token
2032
	 *
2033
	 * @dataProvider provide_dewidow_data
2034
	 *
2035
	 * @param string $html       HTML input.
2036
	 * @param string $result     Expected result.
2037
	 * @param int    $max_pull   Maximum number of pulled characters.
2038
	 * @param int    $max_length Maximum word length for dewidowing.
2039
	 */
2040
	public function test_dewidow( $html, $result, $max_pull, $max_length ) {
2041
		$this->s->set_dewidow( true );
2042
		$this->s->set_max_dewidow_pull( $max_pull );
2043
		$this->s->set_max_dewidow_length( $max_length );
2044
2045
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2046
	}
2047
2048
2049
	/**
2050
	 * Test dewidow.
2051
	 *
2052
	 * @coversNothing
2053
	 *
2054
	 * @uses PHP_Typography\Text_Parser
2055
	 * @uses PHP_Typography\Text_Parser\Token
2056
	 * @uses PHP_Typography\Hyphenator
2057
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2058
	 *
2059
	 * @dataProvider provide_dewidow_with_hyphenation_data
2060
	 *
2061
	 * @param string $html       HTML input.
2062
	 * @param string $result     Expected result.
2063
	 * @param int    $max_pull   Maximum number of pulled characters.
2064
	 * @param int    $max_length Maximum word length for dewidowing.
2065
	 */
2066
	public function test_dewidow_with_hyphenation( $html, $result, $max_pull, $max_length ) {
2067
		$this->s->set_dewidow( true );
2068
		$this->s->set_hyphenation( true );
2069
		$this->s->set_hyphenation_language( 'en-US' );
2070
		$this->s->set_min_length_hyphenation( 2 );
2071
		$this->s->set_min_before_hyphenation( 2 );
2072
		$this->s->set_min_after_hyphenation( 2 );
2073
		$this->s->set_max_dewidow_pull( $max_pull );
2074
		$this->s->set_max_dewidow_length( $max_length );
2075
2076
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2077
	}
2078
2079
	/**
2080
	 * Test dewidow.
2081
	 *
2082
	 * @coversNothing
2083
	 *
2084
	 * @uses PHP_Typography\Text_Parser
2085
	 * @uses PHP_Typography\Text_Parser\Token
2086
	 *
2087
	 * @dataProvider provide_dewidow_data
2088
	 *
2089
	 * @param string $html       HTML input.
2090
	 * @param string $result     Expected result.
2091
	 * @param int    $max_pull   Maximum number of pulled characters.
2092
	 * @param int    $max_length Maximum word length for dewidowing.
2093
	 */
2094
	public function test_dewidow_off( $html, $result, $max_pull, $max_length ) {
2095
		$this->s->set_dewidow( false );
2096
		$this->s->set_max_dewidow_pull( $max_pull );
2097
		$this->s->set_max_dewidow_length( $max_length );
2098
2099
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2100
	}
2101
2102
	/**
2103
	 * Provide data for testing wrap_urls.
2104
	 *
2105
	 * @return array
2106
	 */
2107
	public function provide_wrap_urls_data() {
2108
		return [
2109
			[ 'https://example.org/',                'https://&#8203;example&#8203;.org/',          2 ],
2110
			[ 'http://example.org/',                 'http://&#8203;example&#8203;.org/',           2 ],
2111
			[ 'https://my-example.org',              'https://&#8203;my&#8203;-example&#8203;.org', 2 ],
2112
			[ 'https://example.org/some/long/path/', 'https://&#8203;example&#8203;.org/&#8203;s&#8203;o&#8203;m&#8203;e&#8203;/&#8203;l&#8203;o&#8203;n&#8203;g&#8203;/&#8203;path/', 5 ],
2113
			[ 'https://example.org:8080/',           'https://&#8203;example&#8203;.org:8080/',     2 ],
2114
		];
2115
	}
2116
2117
	/**
2118
	 * Test wrap_urls.
2119
	 *
2120
	 * @coversNothing
2121
	 *
2122
	 * @uses PHP_Typography\Text_Parser
2123
	 * @uses PHP_Typography\Text_Parser\Token
2124
	 *
2125
	 * @dataProvider provide_wrap_urls_data
2126
	 *
2127
	 * @param string $input     HTML input.
2128
	 * @param string $result    Expected result.
2129
	 * @param int    $min_after Minimum number of characters after URL wrapping.
2130
	 */
2131
	public function test_wrap_urls( $input, $result, $min_after ) {
2132
		$this->s->set_url_wrap( true );
2133
		$this->s->set_min_after_url_wrap( $min_after );
2134
2135
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $input, $this->s ) ) );
2136
	}
2137
2138
2139
	/**
2140
	 * Test wrap_urls.
2141
	 *
2142
	 * @coversNothing
2143
	 *
2144
	 * @uses PHP_Typography\Text_Parser
2145
	 * @uses PHP_Typography\Text_Parser\Token
2146
	 *
2147
	 * @dataProvider provide_wrap_urls_data
2148
	 *
2149
	 * @param string $html       HTML input.
2150
	 * @param string $result     Expected result.
2151
	 * @param int    $min_after  Minimum number of characters after URL wrapping.
2152
	 */
2153
	public function test_wrap_urls_off( $html, $result, $min_after ) {
2154
		$this->s->set_url_wrap( false );
2155
		$this->s->set_min_after_url_wrap( $min_after );
2156
2157
		$this->assertSame( $html, $this->typo->process( $html, $this->s ) );
2158
	}
2159
2160
	/**
2161
	 * Provide data for testing wrap_emails.
2162
	 *
2163
	 * @return array
2164
	 */
2165
	public function provide_wrap_emails_data() {
2166
		return [
2167
			[ '[email protected]',         'code@&#8203;example.&#8203;org' ],
2168
			[ '[email protected]', 'some.&#8203;name@&#8203;sub.&#8203;domain.&#8203;org' ],
2169
			[ '[email protected]',     'funny1&#8203;2&#8203;3&#8203;@&#8203;summer1&#8203;.&#8203;org' ],
2170
		];
2171
	}
2172
2173
	/**
2174
	 * Test wrap_emails.
2175
	 *
2176
	 * @coversNothing
2177
	 *
2178
	 * @uses PHP_Typography\Text_Parser
2179
	 * @uses PHP_Typography\Text_Parser\Token
2180
	 *
2181
	 * @dataProvider provide_wrap_emails_data
2182
	 *
2183
	 * @param string $html   HTML input.
2184
	 * @param string $result Expected result.
2185
	 */
2186
	public function test_wrap_emails( $html, $result ) {
2187
		$this->s->set_email_wrap( true );
2188
2189
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2190
	}
2191
2192
2193
	/**
2194
	 * Test wrap_emails.
2195
	 *
2196
	 * @coversNothing
2197
	 *
2198
	 * @uses PHP_Typography\Text_Parser
2199
	 * @uses PHP_Typography\Text_Parser\Token
2200
	 *
2201
	 * @dataProvider provide_wrap_emails_data
2202
	 *
2203
	 * @param string $html   HTML input.
2204
	 * @param string $result Expected result.
2205
	 */
2206
	public function test_wrap_emails_off( $html, $result ) {
2207
		$this->s->set_email_wrap( false );
2208
2209
		$this->assertSame( $html, $this->typo->process( $html, $this->s ) );
2210
	}
2211
2212
	/**
2213
	 * Provide data for testing caps styling.
2214
	 *
2215
	 * @return array
2216
	 */
2217
	public function provide_style_caps_data() {
2218
		return [
2219
			[ 'foo BAR bar', 'foo <span class="caps">BAR</span> bar' ],
2220
			[ 'foo BARbaz', 'foo BARbaz' ],
2221
			[ 'foo BAR123 baz', 'foo <span class="caps">BAR123</span> baz' ],
2222
			[ 'foo 123BAR baz', 'foo <span class="caps">123BAR</span> baz' ],
2223
		];
2224
	}
2225
2226
	/**
2227
	 * Test style_caps.
2228
	 *
2229
	 * @coversNothing
2230
	 *
2231
	 * @uses PHP_Typography\Text_Parser
2232
	 * @uses PHP_Typography\Text_Parser\Token
2233
	 *
2234
	 * @dataProvider provide_style_caps_data
2235
	 *
2236
	 * @param string $html   HTML input.
2237
	 * @param string $result Expected result.
2238
	 */
2239
	public function test_style_caps( $html, $result ) {
2240
		$this->s->set_style_caps( true );
2241
2242
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2243
	}
2244
2245
2246
	/**
2247
	 * Test style_caps.
2248
	 *
2249
	 * @coversNothing
2250
	 *
2251
	 * @uses PHP_Typography\Text_Parser
2252
	 * @uses PHP_Typography\Text_Parser\Token
2253
	 *
2254
	 * @dataProvider provide_style_caps_data
2255
	 *
2256
	 * @param string $html   HTML input.
2257
	 * @param string $result Expected result.
2258
	 */
2259
	public function test_style_caps_off( $html, $result ) {
2260
		$this->s->set_style_caps( false );
2261
2262
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2263
	}
2264
2265
2266
	/**
2267
	 * Test replace_node_with_html.
2268
	 *
2269
	 * @covers ::replace_node_with_html
2270
	 */
2271
	public function test_replace_node_with_html() {
2272
		$s = $this->s;
2273
		$dom = $this->typo->parse_html( $this->typo->get_html5_parser(), '<p>foo</p>', $s );
2274
2275
		$this->assertInstanceOf( '\DOMDocument', $dom );
2276
		$original_node = $dom->getElementsByTagName( 'p' )->item( 0 );
2277
		$parent        = $original_node->parentNode;
2278
		$new_nodes     = (array) $this->typo->replace_node_with_html( $original_node, '<div><span>bar</span></div>' );
2279
2280
		$this->assertTrue( is_array( $new_nodes ) );
2281
		$this->assertContainsOnlyInstancesOf( '\DOMNode', $new_nodes );
2282
		foreach ( $new_nodes as $node ) {
2283
			$this->assertSame( $parent, $node->parentNode );
2284
		}
2285
	}
2286
2287
2288
	/**
2289
	 * Test replace_node_with_html.
2290
	 *
2291
	 * @covers ::replace_node_with_html
2292
	 */
2293
	public function test_replace_node_with_html_invalid() {
2294
2295
		$node = new \DOMText( 'foo' );
2296
		$new_node = $this->typo->replace_node_with_html( $node, 'bar' );
2297
2298
		// Without a parent node, it's not possible to replace anything.
2299
		$this->assertSame( $node, $new_node );
2300
	}
2301
2302
	/**
2303
	 * Provide data for testing style_numbers.
2304
	 *
2305
	 * @return array
2306
	 */
2307
	public function provide_style_numbers_data() {
2308
		return [
2309
			[ 'foo 123 bar', 'foo <span class="numbers">123</span> bar' ],
2310
			[ 'foo 123bar baz', 'foo <span class="numbers">123</span>bar baz' ],
2311
			[ 'foo bar123 baz', 'foo bar<span class="numbers">123</span> baz' ],
2312
			[ 'foo 123BAR baz', 'foo <span class="numbers">123</span>BAR baz' ],
2313
		];
2314
	}
2315
2316
2317
	/**
2318
	 * Test style_numbers.
2319
	 *
2320
	 * @coversNothing
2321
	 *
2322
	 * @uses PHP_Typography\Text_Parser
2323
	 * @uses PHP_Typography\Text_Parser\Token
2324
	 *
2325
	 * @dataProvider provide_style_numbers_data
2326
	 *
2327
	 * @param string $html   HTML input.
2328
	 * @param string $result Expected result.
2329
	 */
2330
	public function test_style_numbers( $html, $result ) {
2331
		$this->s->set_style_numbers( true );
2332
2333
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2334
	}
2335
2336
2337
	/**
2338
	 * Test style_numbers.
2339
	 *
2340
	 * @coversNothing
2341
	 *
2342
	 * @uses PHP_Typography\Text_Parser
2343
	 * @uses PHP_Typography\Text_Parser\Token
2344
	 *
2345
	 * @dataProvider provide_style_numbers_data
2346
	 *
2347
	 * @param string $html   HTML input.
2348
	 * @param string $result Expected result.
2349
	 */
2350
	public function test_style_numbers_off( $html, $result ) {
2351
		$this->s->set_style_numbers( false );
2352
2353
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2354
	}
2355
2356
	/**
2357
	 * Provide data for testing the injection of CSS hooks.
2358
	 *
2359
	 * @return array
2360
	 */
2361
	public function provide_style_caps_and_numbers_data() {
2362
		return [
2363
			[ 'foo 123 BAR', 'foo <span class="numbers">123</span> <span class="caps">BAR</span>' ],
2364
			[ 'FOO-BAR', '<span class="caps">FOO-BAR</span>' ],
2365
			[ 'foo 123-BAR baz', 'foo <span class="caps"><span class="numbers">123</span>-BAR</span> baz' ],
2366
			[ 'foo BAR123 baz', 'foo <span class="caps">BAR<span class="numbers">123</span></span> baz' ],
2367
			[ 'foo 123BAR baz', 'foo <span class="caps"><span class="numbers">123</span>BAR</span> baz' ],
2368
		];
2369
	}
2370
2371
	/**
2372
	 * Test styling caps and numbers at the same time.
2373
	 *
2374
	 * @coversNothing
2375
	 *
2376
	 * @uses PHP_Typography\Text_Parser
2377
	 * @uses PHP_Typography\Text_Parser\Token
2378
	 *
2379
	 * @dataProvider provide_style_caps_and_numbers_data
2380
	 *
2381
	 * @param string $html   HTML input.
2382
	 * @param string $result Expected result.
2383
	 */
2384
	public function test_style_caps_and_numbers( $html, $result ) {
2385
		$this->s->set_style_caps( true );
2386
		$this->s->set_style_numbers( true );
2387
2388
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2389
	}
2390
2391
	/**
2392
	 * Provide data for testing stye_hanging_punctuation.
2393
	 *
2394
	 * @return array
2395
	 */
2396
	public function provide_style_hanging_punctuation_data() {
2397
		return [
2398
			[ '"First "second "third.', '<span class="pull-double">"</span>First <span class="push-double"></span>&#8203;<span class="pull-double">"</span>second <span class="push-double"></span>&#8203;<span class="pull-double">"</span>third.' ],
2399
			[ '<span>"only pull"</span><span>"push & pull"</span>', '<span><span class="pull-double">"</span>only pull"</span><span><span class="push-double"></span>&#8203;<span class="pull-double">"</span>push &amp; pull"</span>' ],
2400
			[ '<p><span>"Pull"</span> <span>\'Single Push\'</span></p>', '<p><span><span class="pull-double">"</span>Pull"</span> <span><span class="push-single"></span>&#8203;<span class="pull-single">\'</span>Single Push\'</span></p>' ],
2401
		];
2402
	}
2403
2404
	/**
2405
	 * Test style_hanging_punctuation.
2406
	 *
2407
	 * @coversNothing
2408
	 *
2409
	 * @uses PHP_Typography\Text_Parser
2410
	 * @uses PHP_Typography\Text_Parser\Token
2411
	 *
2412
	 * @dataProvider provide_style_hanging_punctuation_data
2413
	 *
2414
	 * @param string $html   HTML input.
2415
	 * @param string $result Expected result.
2416
	 */
2417
	public function test_style_hanging_punctuation( $html, $result ) {
2418
		$this->s->set_style_hanging_punctuation( true );
2419
2420
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2421
	}
2422
2423
2424
	/**
2425
	 * Test style_hanging_punctuation.
2426
	 *
2427
	 * @coversNothing
2428
	 *
2429
	 * @uses PHP_Typography\Text_Parser
2430
	 * @uses PHP_Typography\Text_Parser\Token
2431
	 *
2432
	 * @dataProvider provide_style_hanging_punctuation_data
2433
	 *
2434
	 * @param string $html   HTML input.
2435
	 * @param string $result Expected result.
2436
	 */
2437
	public function test_style_hanging_punctuation_off( $html, $result ) {
2438
		$this->s->set_style_hanging_punctuation( false );
2439
2440
		$this->assertSame( $this->clean_html( $html ), $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2441
	}
2442
2443
2444
	/**
2445
	 * Test style_ampersands.
2446
	 *
2447
	 * @coversNothing
2448
	 *
2449
	 * @uses PHP_Typography\Text_Parser
2450
	 * @uses PHP_Typography\Text_Parser\Token
2451
	 */
2452
	public function test_style_ampersands() {
2453
		$this->s->set_style_ampersands( true );
2454
2455
		$this->assertSame( 'foo <span class="amp">&amp;</span> bar', $this->clean_html( $this->typo->process( 'foo & bar', $this->s ) ) );
2456
	}
2457
2458
2459
	/**
2460
	 * Test style_ampersands.
2461
	 *
2462
	 * @coversNothing
2463
	 *
2464
	 * @uses PHP_Typography\Text_Parser
2465
	 * @uses PHP_Typography\Text_Parser\Token
2466
	 */
2467
	public function test_style_ampersands_off() {
2468
		$this->s->set_style_ampersands( false );
2469
2470
		$this->assertSame( 'foo &amp; bar', $this->clean_html( $this->typo->process( 'foo & bar', $this->s ) ) );
2471
	}
2472
2473
	/**
2474
	 * Provide data for testing initial quotes' styling.
2475
	 *
2476
	 * @return array
2477
	 */
2478
	public function provide_style_initial_quotes_data() {
2479
		return [
2480
			[ '<p>no quote</p>', '<p>no quote</p>', false ],
2481
			[ '<p>"double quote"</p>', '<p><span class="dquo">"</span>double quote"</p>', false ],
2482
			[ "<p>'single quote'</p>", "<p><span class=\"quo\">'</span>single quote'</p>", false ],
2483
			[ '"no title quote"', '"no title quote"', false ],
2484
			[ '"title quote"', '<span class="dquo">"</span>title quote"', true ],
2485
		];
2486
	}
2487
2488
	/**
2489
	 * Test style_initial_quotes.
2490
	 *
2491
	 * @coversNothing
2492
	 *
2493
	 * @uses PHP_Typography\Text_Parser
2494
	 * @uses PHP_Typography\Text_Parser\Token
2495
	 *
2496
	 * @dataProvider provide_style_initial_quotes_data
2497
	 *
2498
	 * @param string $html     HTML input.
2499
	 * @param string $result   Expected result.
2500
	 * @param bool   $is_title Treat as heading tag.
2501
	 */
2502
	public function test_style_initial_quotes( $html, $result, $is_title ) {
2503
		$this->s->set_style_initial_quotes( true );
2504
		$this->s->set_initial_quote_tags();
2505
2506
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s, $is_title ) ) );
2507
	}
2508
2509
2510
	/**
2511
	 * Test style_initial_quotes.
2512
	 *
2513
	 * @coversNothing
2514
	 *
2515
	 * @uses PHP_Typography\Text_Parser
2516
	 * @uses PHP_Typography\Text_Parser\Token
2517
	 *
2518
	 * @dataProvider provide_style_initial_quotes_data
2519
	 *
2520
	 * @param string $html     HTML input.
2521
	 * @param string $result   Expected result.
2522
	 * @param bool   $is_title Treat as heading tag.
2523
	 */
2524
	public function test_style_initial_quotes_off( $html, $result, $is_title ) {
2525
		$this->s->set_style_initial_quotes( false );
2526
		$this->s->set_initial_quote_tags();
2527
2528
		$this->assertSame( $html, $this->typo->process( $html, $this->s, $is_title ) );
2529
	}
2530
2531
	/**
2532
	 * Provide data for testing hyphenation.
2533
	 *
2534
	 * @return array
2535
	 */
2536
	public function provide_hyphenate_data() {
2537
		return [
2538
			[ 'A few words to hyphenate, like KINGdesk. Really, there should be more hyphenation here!', 'A few words to hy&shy;phen&shy;ate, like KING&shy;desk. Re&shy;al&shy;ly, there should be more hy&shy;phen&shy;ation here!', 'en-US', true, true, true, false ],
2539
			// Not working with new de pattern file: [ 'Sauerstofffeldflasche', 'Sau&shy;er&shy;stoff&shy;feld&shy;fla&shy;sche', 'de', true, true, true, false ],.
2540
			[ 'Sauerstofffeldflasche', 'Sauer&shy;stoff&shy;feld&shy;fla&shy;sche', 'de', true, true, true, false ],
2541
			// Not working with new de pattern file: [ 'Sauerstoff-Feldflasche', 'Sau&shy;er&shy;stoff-Feld&shy;fla&shy;sche', 'de', true, true, true, true ],.
2542
			[ 'Sauerstoff-Feldflasche', 'Sauer&shy;stoff-Feld&shy;fla&shy;sche', 'de', true, true, true, true ],
2543
			[ 'Sauerstoff-Feldflasche', 'Sauerstoff-Feldflasche', 'de', true, true, true, false ],
2544
			[ 'Geschäftsübernahme', 'Ge&shy;sch&auml;fts&shy;&uuml;ber&shy;nah&shy;me', 'de', true, true, true, false ],
2545
			[ 'Trinkwasserinstallation', 'Trink&shy;was&shy;ser&shy;in&shy;stal&shy;la&shy;ti&shy;on', 'de', true, true, true, false ],
2546
		];
2547
	}
2548
2549
2550
	/**
2551
	 * Test hyphenate.
2552
	 *
2553
	 * @coversNothing
2554
	 *
2555
	 * @uses PHP_Typography\Text_Parser
2556
	 * @uses PHP_Typography\Text_Parser\Token
2557
	 * @uses PHP_Typography\Hyphenator
2558
	 *
2559
	 * @dataProvider provide_hyphenate_data
2560
	 *
2561
	 * @param string $html                 HTML input.
2562
	 * @param string $result               Expected result.
2563
	 * @param string $lang                 Language code.
2564
	 * @param bool   $hyphenate_headings   Hyphenate headings.
2565
	 * @param bool   $hyphenate_all_caps   Hyphenate words in ALL caps.
2566
	 * @param bool   $hyphenate_title_case Hyphenate words in Title Case.
2567
	 * @param bool   $hyphenate_compunds   Hyphenate compound-words.
2568
	 */
2569
	public function test_hyphenate_off( $html, $result, $lang, $hyphenate_headings, $hyphenate_all_caps, $hyphenate_title_case, $hyphenate_compunds ) {
2570
		$this->s->set_hyphenation( false );
2571
		$this->s->set_hyphenation_language( $lang );
2572
		$this->s->set_min_length_hyphenation( 2 );
2573
		$this->s->set_min_before_hyphenation( 2 );
2574
		$this->s->set_min_after_hyphenation( 2 );
2575
		$this->s->set_hyphenate_headings( $hyphenate_headings );
2576
		$this->s->set_hyphenate_all_caps( $hyphenate_all_caps );
2577
		$this->s->set_hyphenate_title_case( $hyphenate_title_case );
2578
		$this->s->set_hyphenate_compounds( $hyphenate_compunds );
2579
		$this->s->set_hyphenation_exceptions( [ 'KING-desk' ] );
2580
2581
		$this->assertSame( $html, $this->typo->process( $html, $this->s ) );
2582
	}
2583
2584
2585
	/**
2586
	 * Test hyphenate.
2587
	 *
2588
	 * @coversNothing
2589
	 *
2590
	 * @uses PHP_Typography\Text_Parser
2591
	 * @uses PHP_Typography\Text_Parser\Token
2592
	 * @uses PHP_Typography\Hyphenator
2593
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2594
	 *
2595
	 * @dataProvider provide_hyphenate_data
2596
	 *
2597
	 * @param string $html                 HTML input.
2598
	 * @param string $result               Expected result.
2599
	 * @param string $lang                 Language code.
2600
	 * @param bool   $hyphenate_headings   Hyphenate headings.
2601
	 * @param bool   $hyphenate_all_caps   Hyphenate words in ALL caps.
2602
	 * @param bool   $hyphenate_title_case Hyphenate words in Title Case.
2603
	 * @param bool   $hyphenate_compunds   Hyphenate compound-words.
2604
	 */
2605
	public function test_hyphenate( $html, $result, $lang, $hyphenate_headings, $hyphenate_all_caps, $hyphenate_title_case, $hyphenate_compunds ) {
2606
		$this->s->set_hyphenation( true );
2607
		$this->s->set_hyphenation_language( $lang );
2608
		$this->s->set_min_length_hyphenation( 2 );
2609
		$this->s->set_min_before_hyphenation( 2 );
2610
		$this->s->set_min_after_hyphenation( 2 );
2611
		$this->s->set_hyphenate_headings( $hyphenate_headings );
2612
		$this->s->set_hyphenate_all_caps( $hyphenate_all_caps );
2613
		$this->s->set_hyphenate_title_case( $hyphenate_title_case );
2614
		$this->s->set_hyphenate_compounds( $hyphenate_compunds );
2615
		$this->s->set_hyphenation_exceptions( [ 'KING-desk' ] );
2616
2617
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2618
	}
2619
2620
	/**
2621
	 * Provide data for testing hyphenation with custom exceptions.
2622
	 *
2623
	 * @return array
2624
	 */
2625
	public function provide_hyphenate_with_exceptions_data() {
2626
		return [
2627
			[ 'A few words to hyphenate, like KINGdesk. Really, there should be more hyphenation here!', 'A few words to hy&shy;phen&shy;ate, like KING&shy;desk. Re&shy;al&shy;ly, there should be more hy&shy;phen&shy;ation here!', [ 'KING-desk' ], 'en-US', true, true, true, false ],
2628
			[ 'Geschäftsübernahme', 'Ge&shy;sch&auml;fts&shy;&uuml;ber&shy;nah&shy;me', [], 'de', true, true, true, false ],
2629
			[ 'Geschäftsübernahme', 'Ge&shy;sch&auml;fts&shy;&uuml;ber&shy;nah&shy;me', [ 'Ge-schäfts-über-nah-me' ], 'de', true, true, true, false ],
2630
			[ 'Trinkwasserinstallation', 'Trink&shy;was&shy;ser&shy;in&shy;stal&shy;la&shy;ti&shy;on', [], 'de', true, true, true, false ],
2631
			[ 'Trinkwasserinstallation', 'Trink&shy;wasser&shy;in&shy;stal&shy;la&shy;tion', [ 'Trink-wasser-in-stal-la-tion' ], 'de', true, true, true, false ],
2632
		];
2633
	}
2634
2635
2636
	/**
2637
	 * Test hyphenate.
2638
	 *
2639
	 * @coversNothing
2640
	 *
2641
	 * @uses PHP_Typography\Text_Parser
2642
	 * @uses PHP_Typography\Text_Parser\Token
2643
	 * @uses PHP_Typography\Hyphenator
2644
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2645
	 *
2646
	 * @dataProvider provide_hyphenate_with_exceptions_data
2647
	 *
2648
	 * @param string $html                 HTML input.
2649
	 * @param string $result               Expected result.
2650
	 * @param array  $exceptions           Custom hyphenation exceptions.
2651
	 * @param string $lang                 Language code.
2652
	 * @param bool   $hyphenate_headings   Hyphenate headings.
2653
	 * @param bool   $hyphenate_all_caps   Hyphenate words in ALL caps.
2654
	 * @param bool   $hyphenate_title_case Hyphenate words in Title Case.
2655
	 * @param bool   $hyphenate_compunds   Hyphenate compound-words.
2656
	 */
2657
	public function test_hyphenate_with_exceptions( $html, $result, $exceptions, $lang, $hyphenate_headings, $hyphenate_all_caps, $hyphenate_title_case, $hyphenate_compunds ) {
2658
		$this->s->set_hyphenation( true );
2659
		$this->s->set_hyphenation_language( $lang );
2660
		$this->s->set_min_length_hyphenation( 2 );
2661
		$this->s->set_min_before_hyphenation( 2 );
2662
		$this->s->set_min_after_hyphenation( 2 );
2663
		$this->s->set_hyphenate_headings( $hyphenate_headings );
2664
		$this->s->set_hyphenate_all_caps( $hyphenate_all_caps );
2665
		$this->s->set_hyphenate_title_case( $hyphenate_title_case );
2666
		$this->s->set_hyphenate_compounds( $hyphenate_compunds );
2667
		$this->s->set_hyphenation_exceptions( $exceptions );
2668
2669
		$this->assertSame( $result, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2670
	}
2671
2672
	/**
2673
	 * Test hyphenate.
2674
	 *
2675
	 * @coversNothing
2676
	 *
2677
	 * @uses PHP_Typography\Text_Parser
2678
	 * @uses PHP_Typography\Text_Parser\Token
2679
	 * @uses PHP_Typography\Hyphenator
2680
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2681
	 */
2682
	public function test_hyphenate_headings_disabled() {
2683
2684
		$this->s->set_hyphenation( true );
2685
		$this->s->set_hyphenation_language( 'en-US' );
2686
		$this->s->set_min_length_hyphenation( 2 );
2687
		$this->s->set_min_before_hyphenation( 2 );
2688
		$this->s->set_min_after_hyphenation( 2 );
2689
		$this->s->set_hyphenate_headings( false );
2690
		$this->s->set_hyphenate_all_caps( true );
2691
		$this->s->set_hyphenate_title_case( true );
2692
		$this->s->set_hyphenation_exceptions( [ 'KING-desk' ] );
2693
2694
		$html = '<h2>A few words to hyphenate, like KINGdesk. Really, there should be no hyphenation here!</h2>';
2695
		$this->assertSame( $html, $this->clean_html( $this->typo->process( $html, $this->s ) ) );
2696
	}
2697
2698
	/**
2699
	 * Test hyphenate.
2700
	 *
2701
	 * @coversNothing
2702
	 *
2703
	 * @uses PHP_Typography\Hyphenator
2704
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2705
	 * @uses PHP_Typography\Text_Parser
2706
	 * @uses PHP_Typography\Text_Parser\Token
2707
	 */
2708
	public function test_hyphenate_no_custom_exceptions() {
2709
2710
		$this->s->set_hyphenation( true );
2711
		$this->s->set_hyphenation_language( 'en-US' );
2712
		$this->s->set_min_length_hyphenation( 2 );
2713
		$this->s->set_min_before_hyphenation( 2 );
2714
		$this->s->set_min_after_hyphenation( 2 );
2715
		$this->s->set_hyphenate_headings( true );
2716
		$this->s->set_hyphenate_all_caps( true );
2717
		$this->s->set_hyphenate_title_case( true );
2718
2719
		$this->assertSame('A few words to hy&shy;phen&shy;ate, like KINGdesk. Re&shy;al&shy;ly, there should be more hy&shy;phen&shy;ation here!',
2720
						   $this->clean_html( $this->typo->process( 'A few words to hyphenate, like KINGdesk. Really, there should be more hyphenation here!', $this->s ) ) );
2721
	}
2722
2723
2724
	/**
2725
	 * Test hyphenate.
2726
	 *
2727
	 * @coversNothing
2728
	 *
2729
	 * @uses PHP_Typography\Hyphenator
2730
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2731
	 * @uses PHP_Typography\Text_Parser
2732
	 * @uses PHP_Typography\Text_Parser\Token
2733
	 */
2734
	public function test_hyphenate_no_exceptions_at_all() {
2735
2736
		$this->s->set_hyphenation( true );
2737
		$this->s->set_hyphenation_language( 'en-US' );
2738
		$this->s->set_min_length_hyphenation( 2 );
2739
		$this->s->set_min_before_hyphenation( 2 );
2740
		$this->s->set_min_after_hyphenation( 2 );
2741
		$this->s->set_hyphenate_headings( true );
2742
		$this->s->set_hyphenate_all_caps( true );
2743
		$this->s->set_hyphenate_title_case( true );
2744
		$s = $this->s;
2745
2746
		$s['hyphenationPatternExceptions'] = [];
2747
		unset( $s['hyphenationExceptions'] );
2748
2749
		$this->assertSame( 'A few words to hy&shy;phen&shy;ate, like KINGdesk. Re&shy;al&shy;ly, there should be more hy&shy;phen&shy;ation here!',
2750
						   $this->clean_html( $this->typo->process( 'A few words to hyphenate, like KINGdesk. Really, there should be more hyphenation here!', $s, false ) ) );
2751
	}
2752
2753
	/**
2754
	 * Test init.
2755
	 *
2756
	 * @covers ::init
2757
	 * @covers ::__construct
2758
	 *
2759
	 * @uses PHP_Typography\Hyphenator
2760
	 * @uses PHP_Typography\Hyphenator\Trie_Node
2761
	 * @uses PHP_Typography\Settings\Dash_Style::get_styled_dashes
2762
	 * @uses PHP_Typography\Settings\Quote_Style::get_styled_quotes
2763
	 */
2764
	public function test_init() {
2765
		$second_typo = new PHP_Typography( PHP_Typography::INIT_LAZY );
2766
		$this->assertAttributeEmpty( 'process_words_fix', $second_typo );
2767
2768
		$second_typo->init();
2769
2770
		$this->assertAttributeNotEmpty( 'process_words_fix', $second_typo );
2771
	}
2772
2773
	/**
2774
	 * Test get_html.
2775
	 *
2776
	 * @covers ::get_html5_parser
2777
	 */
2778
	public function test_get_html5_parser() {
2779
2780
		$this->assertAttributeEmpty( 'html5_parser', $this->typo );
2781
2782
		$parser1 = $this->typo->get_html5_parser();
2783
		$this->assertInstanceOf( '\Masterminds\HTML5', $parser1 );
2784
2785
		$parser2 = $this->typo->get_html5_parser();
2786
		$this->assertInstanceOf( '\Masterminds\HTML5', $parser2 );
2787
2788
		$this->assertSame( $parser1, $parser2 );
2789
		$this->assertAttributeInstanceOf( '\Masterminds\HTML5', 'html5_parser', $this->typo );
2790
	}
2791
2792
	/**
2793
	 * Test parse_html.
2794
	 *
2795
	 * @covers ::parse_html
2796
	 */
2797
	public function test_parse_html() {
2798
		$dom = $this->typo->parse_html( $this->typo->get_html5_parser(), '<p>some text</p>', $this->s );
2799
2800
		$this->assertInstanceOf( '\DOMDocument', $dom );
2801
		$this->assertEquals( 1, $dom->getElementsByTagName( 'p' )->length );
2802
	}
2803
2804
2805
	/**
2806
	 * Test parse_html.
2807
	 *
2808
	 * @covers ::parse_html
2809
	 *
2810
	 * @dataProvider provide_process_data
2811
	 *
2812
	 * @param string $html    HTML input.
2813
	 * @param string $ignore1 Ignored.
2814
	 * @param string $ignore2 Ignored.
2815
	 */
2816
	public function test_parse_html_extended( $html, $ignore1, $ignore2 ) {
2817
				$p    = $this->typo->get_html5_parser();
2818
		$dom  = $this->typo->parse_html( $p, $html, $this->s );
2819
2820
		$this->assertInstanceOf( '\DOMDocument', $dom );
2821
2822
		// Serialize the stuff again.
2823
		$xpath     = new \DOMXPath( $dom );
2824
		$body_node = $xpath->query( '/html/body' )->item( 0 );
2825
2826
		$this->assertEquals( $html, $p->saveHTML( $body_node->childNodes ) );
2827
	}
2828
2829
	/**
2830
	 * Provide data for testing parsing HTML containing markup errors.
2831
	 *
2832
	 * @return array
2833
	 */
2834
	public function provide_parse_html_with_errors_data() {
2835
		return [
2836
			[ '<div>foobar</div></p>', 'Line 0, Col 0: Could not find closing tag for p' ],
2837
			[ '<a href="http://example.org?foo=xx&bar=yy">foobar</a>', "Line 1, Col 65: No match in entity table for 'bar'" ],
2838
		];
2839
	}
2840
2841
	/**
2842
	 * Test parse_html.
2843
	 *
2844
	 * @covers ::parse_html
2845
	 *
2846
	 * @dataProvider provide_parse_html_with_errors_data
2847
	 *
2848
	 * @param  string $html      HTML input.
2849
	 * @param  string $error_msg Expected error message.
2850
	 */
2851
	public function test_parse_html_with_errors( $html, $error_msg ) {
2852
				$s = $this->s;
2853
2854
		// Without an error handler.
2855
		$dom = $this->typo->parse_html( $this->typo->get_html5_parser(), $html, $s );
2856
		$this->assertNull( $dom );
2857
2858
		// With error handler.
2859
		$s->set_parser_errors_handler(function ( $errors ) {
2860
			foreach ( $errors as $error ) {
2861
				echo $error; // WPCS: XSS ok.
2862
			}
2863
2864
			return [];
2865
		});
2866
2867
		$this->expectOutputString( $error_msg );
2868
		$dom = $this->typo->parse_html( $this->typo->get_html5_parser(), $html, $s );
2869
		$this->assertInstanceOf( 'DOMDocument', $dom );
2870
	}
2871
2872
	/**
2873
	 * Provide data for testing arrays_intersect.
2874
	 *
2875
	 * @return array
2876
	 */
2877
	public function provide_arrays_intersect_data() {
2878
		return [
2879
			[ [], [], false ],
2880
			[ [ 1, 2, 3 ], [ 2, 4, 1 ], true ],
2881
			[ [ 1, 2, 3 ], [], false ],
2882
			[ [], [ 1, 2, 3 ], false ],
2883
		];
2884
	}
2885
2886
	/**
2887
	 * $a1 and $a2 need to be arrays of object indexes < 10
2888
	 *
2889
	 * @covers ::arrays_intersect
2890
	 * @dataProvider provide_arrays_intersect_data
2891
	 *
2892
	 * @param  array $a1     First array.
2893
	 * @param  array $a2     Second array.
2894
	 * @param  bool  $result Expected result.
2895
	 */
2896
	public function test_arrays_intersect( array $a1, array $a2, $result ) {
2897
		$nodes = [];
2898
		for ( $i = 0; $i < 10; ++$i ) {
2899
			$nodes[] = new \DOMText( "foo $i" );
2900
		}
2901
2902
		$array1 = [];
2903
		foreach ( $a1 as $index ) {
2904
			if ( isset( $nodes[ $index ] ) ) {
2905
				$array1[] = $nodes[ $index ];
2906
			}
2907
		}
2908
2909
		$array2 = [];
2910
		foreach ( $a2 as $index ) {
2911
			if ( isset( $nodes[ $index ] ) ) {
2912
				$array2[ spl_object_hash( $nodes[ $index ] ) ] = $nodes[ $index ];
2913
			}
2914
		}
2915
2916
		$this->assertSame( $result, $this->invokeStaticMethod( PHP_Typography::class, 'arrays_intersect', [ $array1, $array2 ] ) );
2917
	}
2918
2919
	/**
2920
	 * Tests get_hyphenator_cache.
2921
	 *
2922
	 * @covers ::get_hyphenator_cache
2923
	 */
2924
	public function test_get_hyphenator_cache() {
2925
		$cache = $this->typo->get_hyphenator_cache();
2926
2927
		$this->assertInstanceOf( Hyphenator_Cache::class, $cache );
2928
	}
2929
2930
	/**
2931
	 * Tests set_hyphenator_cache.
2932
	 *
2933
	 * @covers ::set_hyphenator_cache
2934
	 *
2935
	 * @uses ::get_hyphenator_cache
2936
	 */
2937
	public function test_set_hyphenator_cache() {
2938
		$new_cache = new Hyphenator_Cache();
2939
		$old_cache = $this->typo->get_hyphenator_cache();
2940
2941
		$this->assertNotSame( $old_cache, $new_cache );
2942
2943
		$this->typo->set_hyphenator_cache( $new_cache );
2944
		$this->assertSame( $new_cache, $this->typo->get_hyphenator_cache() );
2945
	}
2946
}
2947