Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / tests / Drupal / Tests / Core / EventSubscriber / ActiveLinkResponseFilterTest.php
1 <?php
2
3 namespace Drupal\Tests\Core\EventSubscriber;
4
5 use Drupal\Component\Serialization\Json;
6 use Drupal\Core\EventSubscriber\ActiveLinkResponseFilter;
7 use Drupal\Core\Template\Attribute;
8 use Drupal\Tests\UnitTestCase;
9
10 /**
11  * @coversDefaultClass \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
12  * @group EventSubscriber
13  */
14 class ActiveLinkResponseFilterTest extends UnitTestCase {
15
16   /**
17    * Provides test data for testSetLinkActiveClass().
18    *
19    * @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter::setLinkActiveClass()
20    */
21   public function providerTestSetLinkActiveClass() {
22     // Define all the variations that *don't* affect whether or not an
23     // "is-active" class is set, but that should remain unchanged:
24     // - surrounding HTML
25     // - tags for which to test the setting of the "is-active" class
26     // - content of said tags
27     $edge_case_html5 = '<audio src="foo.ogg">
28   <track kind="captions" src="foo.en.vtt" srclang="en" label="English">
29   <track kind="captions" src="foo.sv.vtt" srclang="sv" label="Svenska">
30 </audio>';
31     $html = [
32       // Simple HTML.
33       0 => ['prefix' => '<div><p>', 'suffix' => '</p></div>'],
34       // Tricky HTML5 example that's unsupported by PHP <=5.4's DOMDocument:
35       // https://www.drupal.org/comment/7938201#comment-7938201.
36       1 => ['prefix' => '<div><p>', 'suffix' => '</p>' . $edge_case_html5 . '</div>'],
37       // Multi-byte content *before* the HTML that needs the "is-active" class.
38       2 => ['prefix' => '<div><p>αβγδεζηθικλμνξοσὠ</p><p>', 'suffix' => '</p></div>'],
39     ];
40     $tags = [
41       // Of course, it must work on anchors.
42       'a',
43       // Unfortunately, it must also work on list items.
44       'li',
45       // … and therefore, on *any* tag, really.
46       'foo',
47     ];
48     $contents = [
49       // Regular content.
50       'test',
51       // Mix of UTF-8 and HTML entities, both must be retained.
52       '☆ 3 × 4 = €12 and 4 &times; 3 = &euro;12 &#9734',
53       // Multi-byte content.
54       'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ',
55       // Text that closely approximates an important attribute, but should be
56       // ignored.
57       'data-drupal-link-system-path=&quot;&lt;front&gt;&quot;',
58     ];
59
60     // Define all variations that *do* affect whether or not an "is-active"
61     // class is set: all possible situations that can be encountered.
62     $situations = [];
63
64     // Situations with context: front page, English, no query.
65     $context = [
66       'path' => 'myfrontpage',
67       'front' => TRUE,
68       'language' => 'en',
69       'query' => [],
70     ];
71     // Nothing to do.
72     $markup = '<foo>bar</foo>';
73     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => []];
74     // Matching path, plus all matching variations.
75     $attributes = [
76       'data-drupal-link-system-path' => 'myfrontpage',
77     ];
78     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
79     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'en']];
80     // Matching path, plus all non-matching variations.
81     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl']];
82     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => '{"foo":"bar"}']];
83     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
84     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
85     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => '{"foo":"bar"}']];
86     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => ""]];
87     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => TRUE]];
88     // Special matching path, plus all variations.
89     $attributes = [
90       'data-drupal-link-system-path' => '<front>',
91     ];
92     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
93     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'en']];
94     // Special matching path, plus all non-matching variations.
95     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl']];
96     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => '{"foo":"bar"}']];
97     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
98     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
99     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => '{"foo":"bar"}']];
100     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => ""]];
101     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => TRUE]];
102
103     // Situations with context: non-front page, Dutch, no query.
104     $context = [
105       'path' => 'llama',
106       'front' => FALSE,
107       'language' => 'nl',
108       'query' => [],
109     ];
110     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => []];
111     // Matching path, plus all matching variations.
112     $attributes = [
113       'data-drupal-link-system-path' => 'llama',
114     ];
115     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
116     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'nl']];
117     // Matching path, plus all non-matching variations.
118     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en']];
119     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => '{"foo":"bar"}']];
120     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
121     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
122     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => '{"foo":"bar"}']];
123     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => ""]];
124     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => TRUE]];
125     // Special non-matching path, plus all variations.
126     $attributes = [
127       'data-drupal-link-system-path' => '<front>',
128     ];
129     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes];
130     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en']];
131     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => '{"foo":"bar"}']];
132     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
133     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
134     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => '{"foo":"bar"}']];
135     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => ""]];
136     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => TRUE]];
137
138     // Situations with context: non-front page, Dutch, with query.
139     $context = [
140       'path' => 'llama',
141       'front' => FALSE,
142       'language' => 'nl',
143       'query' => ['foo' => 'bar'],
144     ];
145     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => []];
146     // Matching path, plus all matching variations.
147     $attributes = [
148       'data-drupal-link-system-path' => 'llama',
149       'data-drupal-link-query' => Json::encode(['foo' => 'bar']),
150     ];
151     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
152     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'nl']];
153     // Matching path, plus all non-matching variations.
154     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en']];
155     unset($attributes['data-drupal-link-query']);
156     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => ""]];
157     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => TRUE]];
158     // Special non-matching path, plus all variations.
159     $attributes = [
160       'data-drupal-link-system-path' => '<front>',
161     ];
162     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes];
163     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl']];
164     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en']];
165     unset($attributes['data-drupal-link-query']);
166     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => ""]];
167     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => TRUE]];
168
169     // Situations with context: non-front page, Dutch, with query.
170     $context = [
171       'path' => 'llama',
172       'front' => FALSE,
173       'language' => 'nl',
174       'query' => ['foo' => 'bar'],
175     ];
176     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => []];
177     // Matching path, plus all matching variations.
178     $attributes = [
179       'data-drupal-link-system-path' => 'llama',
180       'data-drupal-link-query' => Json::encode(['foo' => 'bar']),
181     ];
182     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
183     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'nl']];
184     // Matching path, plus all non-matching variations.
185     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en']];
186     unset($attributes['data-drupal-link-query']);
187     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
188     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
189     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => ""]];
190     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => TRUE]];
191     // Special non-matching path, plus all variations.
192     $attributes = [
193       'data-drupal-link-system-path' => '<front>',
194     ];
195     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes];
196     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl']];
197     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en']];
198     unset($attributes['data-drupal-link-query']);
199     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
200     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
201     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => ""]];
202     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl', 'data-drupal-link-query' => TRUE]];
203
204     // Situations with context: front page, English, query.
205     $context = [
206       'path' => 'myfrontpage',
207       'front' => TRUE,
208       'language' => 'en',
209       'query' => ['foo' => 'bar'],
210     ];
211     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => []];
212     // Matching path, plus all matching variations.
213     $attributes = [
214       'data-drupal-link-system-path' => 'myfrontpage',
215       'data-drupal-link-query' => Json::encode(['foo' => 'bar']),
216     ];
217     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
218     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'en']];
219     // Matching path, plus all non-matching variations.
220     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl']];
221     unset($attributes['data-drupal-link-query']);
222     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
223     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
224     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => ""]];
225     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => TRUE]];
226     // Special matching path, plus all variations.
227     $attributes = [
228       'data-drupal-link-system-path' => '<front>',
229       'data-drupal-link-query' => Json::encode(['foo' => 'bar']),
230     ];
231     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
232     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes + ['hreflang' => 'en']];
233     // Special matching path, plus all non-matching variations.
234     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'nl']];
235     unset($attributes['data-drupal-link-query']);
236     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => ""]];
237     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['data-drupal-link-query' => TRUE]];
238     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => ""]];
239     $situations[] = ['context' => $context, 'is active' => FALSE, 'attributes' => $attributes + ['hreflang' => 'en', 'data-drupal-link-query' => TRUE]];
240
241     // Query with unsorted keys must match when the attribute is in sorted form.
242     $context = [
243       'path' => 'myfrontpage',
244       'front' => TRUE,
245       'language' => 'en',
246       'query' => ['foo' => 'bar', 'baz' => 'qux'],
247     ];
248     $attributes = [
249       'data-drupal-link-system-path' => 'myfrontpage',
250       'data-drupal-link-query' => Json::encode(['baz' => 'qux', 'foo' => 'bar']),
251     ];
252     $situations[] = ['context' => $context, 'is active' => TRUE, 'attributes' => $attributes];
253
254     // Loop over the surrounding HTML variations.
255     $data = [];
256     for ($h = 0; $h < count($html); $h++) {
257       $html_prefix = $html[$h]['prefix'];
258       $html_suffix = $html[$h]['suffix'];
259       // Loop over the tag variations.
260       for ($t = 0; $t < count($tags); $t++) {
261         $tag = $tags[$t];
262         // Loop over the tag contents variations.
263         for ($c = 0; $c < count($contents); $c++) {
264           $tag_content = $contents[$c];
265
266           $create_markup = function (Attribute $attributes) use ($html_prefix, $html_suffix, $tag, $tag_content) {
267             return $html_prefix . '<' . $tag . $attributes . '>' . $tag_content . '</' . $tag . '>' . $html_suffix;
268           };
269
270           // Loop over the situations.
271           for ($s = 0; $s < count($situations); $s++) {
272             $situation = $situations[$s];
273
274             // Build the source markup.
275             $source_markup = $create_markup(new Attribute($situation['attributes']));
276
277             // Build the target markup. If no "is-active" class should be set,
278             // the resulting HTML should be identical. Otherwise, it should get
279             // an "is-active" class, either by extending an existing "class"
280             // attribute or by adding a "class" attribute.
281             $target_markup = NULL;
282             if (!$situation['is active']) {
283               $target_markup = $source_markup;
284             }
285             else {
286               $active_attributes = $situation['attributes'];
287               if (!isset($active_attributes['class'])) {
288                 $active_attributes['class'] = [];
289               }
290               $active_attributes['class'][] = 'is-active';
291               $target_markup = $create_markup(new Attribute($active_attributes));
292             }
293
294             $data[] = [$source_markup, $situation['context']['path'], $situation['context']['front'], $situation['context']['language'], $situation['context']['query'], $target_markup];
295           }
296         }
297       }
298     }
299
300     // Test case to verify that the 'is-active' class is not added multiple
301     // times.
302     $data[] = [
303       0 => '<a data-drupal-link-system-path="&lt;front&gt;">Once</a> <a data-drupal-link-system-path="&lt;front&gt;">Twice</a>',
304       1 => '',
305       2 => TRUE,
306       3 => 'en',
307       4 => [],
308       5 => '<a data-drupal-link-system-path="&lt;front&gt;" class="is-active">Once</a> <a data-drupal-link-system-path="&lt;front&gt;" class="is-active">Twice</a>',
309     ];
310
311     // Test cases to verify that the 'is-active' class is added when on the
312     // front page, and there are two different kinds of matching links on the
313     // page:
314     // - the matching path (the resolved front page path)
315     // - the special matching path ('<front>')
316     $front_special_link = '<a data-drupal-link-system-path="&lt;front&gt;">Front</a>';
317     $front_special_link_active = '<a data-drupal-link-system-path="&lt;front&gt;" class="is-active">Front</a>';
318     $front_path_link = '<a data-drupal-link-system-path="myfrontpage">Front Path</a>';
319     $front_path_link_active = '<a data-drupal-link-system-path="myfrontpage" class="is-active">Front Path</a>';
320     $data[] = [
321       0 => $front_path_link . ' ' . $front_special_link,
322       1 => 'myfrontpage',
323       2 => TRUE,
324       3 => 'en',
325       4 => [],
326       5 => $front_path_link_active . ' ' . $front_special_link_active,
327     ];
328     $data[] = [
329       0 => $front_special_link . ' ' . $front_path_link,
330       1 => 'myfrontpage',
331       2 => TRUE,
332       3 => 'en',
333       4 => [],
334       5 => $front_special_link_active . ' ' . $front_path_link_active,
335     ];
336
337     // Test cases to verify that links to the front page do not get the
338     // 'is-active' class when not on the front page.
339     $other_link = '<a data-drupal-link-system-path="otherpage">Other page</a>';
340     $other_link_active = '<a data-drupal-link-system-path="otherpage" class="is-active">Other page</a>';
341     $data['<front>-and-other-link-on-other-path'] = [
342       0 => $front_special_link . ' ' . $other_link,
343       1 => 'otherpage',
344       2 => FALSE,
345       3 => 'en',
346       4 => [],
347       5 => $front_special_link . ' ' . $other_link_active,
348     ];
349     $data['front-and-other-link-on-other-path'] = [
350       0 => $front_path_link . ' ' . $other_link,
351       1 => 'otherpage',
352       2 => FALSE,
353       3 => 'en',
354       4 => [],
355       5 => $front_path_link . ' ' . $other_link_active,
356     ];
357     $data['other-and-<front>-link-on-other-path'] = [
358       0 => $other_link . ' ' . $front_special_link,
359       1 => 'otherpage',
360       2 => FALSE,
361       3 => 'en',
362       4 => [],
363       5 => $other_link_active . ' ' . $front_special_link,
364     ];
365     $data['other-and-front-link-on-other-path'] = [
366       0 => $other_link . ' ' . $front_path_link,
367       1 => 'otherpage',
368       2 => FALSE,
369       3 => 'en',
370       4 => [],
371       5 => $other_link_active . ' ' . $front_path_link,
372     ];
373     return $data;
374   }
375
376   /**
377    * Tests setLinkActiveClass().
378    *
379    * @param string $html_markup
380    *   The original HTML markup.
381    * @param string $current_path
382    *   The system path of the currently active page.
383    * @param bool $is_front
384    *   Whether the current page is the front page (which implies the current
385    *   path might also be <front>).
386    * @param string $url_language
387    *   The language code of the current URL.
388    * @param array $query
389    *   The query string for the current URL.
390    * @param string $expected_html_markup
391    *   The expected updated HTML markup.
392    *
393    * @dataProvider providerTestSetLinkActiveClass
394    * @covers ::setLinkActiveClass
395    */
396   public function testSetLinkActiveClass($html_markup, $current_path, $is_front, $url_language, array $query, $expected_html_markup) {
397     $this->assertSame($expected_html_markup, ActiveLinkResponseFilter::setLinkActiveClass($html_markup, $current_path, $is_front, $url_language, $query));
398   }
399
400 }