[FEATURE] Render basic SEO meta tags in frontend
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Unit / Page / PageGeneratorTest.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Tests\Unit\Page;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Prophecy\Argument;
18 use TYPO3\CMS\Core\Page\PageRenderer;
19 use TYPO3\CMS\Core\TypoScript\TemplateService;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
22 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
23 use TYPO3\CMS\Frontend\Page\PageGenerator;
24
25 /**
26 * Test case
27 */
28 class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
29 {
30 /**
31 * Subject is not notice free, disable E_NOTICES
32 */
33 protected static $suppressNotices = true;
34
35 /**
36 * @var array A backup of registered singleton instances
37 */
38 protected $singletonInstances = [];
39
40 protected function setUp()
41 {
42 $this->singletonInstances = GeneralUtility::getSingletonInstances();
43 }
44
45 protected function tearDown()
46 {
47 GeneralUtility::purgeInstances();
48 GeneralUtility::resetSingletonInstances($this->singletonInstances);
49 parent::tearDown();
50 }
51
52 /**
53 * @return array
54 */
55 public function generateMetaTagHtmlGeneratesCorrectTagsDataProvider()
56 {
57 return [
58 'simple meta' => [
59 [
60 'author' => 'Markus Klein',
61 ],
62 '',
63 [
64 'type' => 'name',
65 'name' => 'author',
66 'content' => 'Markus Klein'
67 ]
68 ],
69 'httpEquivalent meta' => [
70 [
71 'X-UA-Compatible' => 'IE=edge,chrome=1',
72 'X-UA-Compatible.' => ['httpEquivalent' => 1]
73 ],
74 'IE=edge,chrome=1',
75 [
76 'type' => 'http-equiv',
77 'name' => 'X-UA-Compatible',
78 'content' => 'IE=edge,chrome=1'
79 ]
80 ],
81 'httpEquivalent meta xhtml new notation' => [
82 [
83 'X-UA-Compatible' => 'IE=edge,chrome=1',
84 'X-UA-Compatible.' => ['attribute' => 'http-equiv']
85 ],
86 'IE=edge,chrome=1',
87 [
88 'type' => 'http-equiv',
89 'name' => 'X-UA-Compatible',
90 'content' => 'IE=edge,chrome=1'
91 ]
92 ],
93 'refresh meta' => [
94 [
95 'refresh' => '10',
96 ],
97 '',
98 [
99 'type' => 'http-equiv',
100 'name' => 'refresh',
101 'content' => '10'
102 ]
103 ],
104 'refresh meta new notation' => [
105 [
106 'refresh' => '10',
107 'refresh.' => ['attribute' => 'http-equiv']
108 ],
109 '10',
110 [
111 'type' => 'http-equiv',
112 'name' => 'refresh',
113 'content' => '10'
114 ]
115 ],
116 'meta with dot' => [
117 [
118 'DC.author' => 'Markus Klein',
119 ],
120 '',
121 [
122 'type' => 'name',
123 'name' => 'DC.author',
124 'content' => 'Markus Klein'
125 ]
126 ],
127 'meta with colon' => [
128 [
129 'OG:title' => 'Magic Tests',
130 ],
131 '',
132 [
133 'type' => 'name',
134 'name' => 'OG:title',
135 'content' => 'Magic Tests'
136 ]
137 ],
138 'different attribute name' => [
139 [
140 'og:site_title' => 'My TYPO3 site',
141 'og:site_title.' => ['attribute' => 'property'],
142 ],
143 'My TYPO3 site',
144 [
145 'type' => 'property',
146 'name' => 'og:site_title',
147 'content' => 'My TYPO3 site'
148 ]
149 ],
150 'meta with 0 value' => [
151 [
152 'custom:key' => '0',
153 ],
154 '',
155 [
156 'type' => 'name',
157 'name' => 'custom:key',
158 'content' => '0'
159 ]
160 ],
161 ];
162 }
163
164 /**
165 * @test
166 */
167 public function generateMetaTagExpectExceptionOnBogusTags()
168 {
169 $stdWrapResult = '10';
170
171 $typoScript = [
172 'refresh' => '10',
173 'refresh.' => ['attribute' => 'http-equiv-new']
174 ];
175
176 $expectedTags = [
177 'type' => 'http-equiv-new',
178 'name' => 'refresh',
179 'content' => '10'
180 ];
181
182 $cObj = $this->prophesize(ContentObjectRenderer::class);
183 $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
184 $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
185 $tmpl = $this->prophesize(TemplateService::class);
186 $tsfe = $this->prophesize(TypoScriptFrontendController::class);
187 $tsfe->generatePageTitle()->willReturn('');
188 $tsfe->generateMetaTags()->shouldBeCalled();
189 $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
190 $tsfe->cObj = $cObj->reveal();
191 $tsfe->tmpl = $tmpl->reveal();
192 $tsfe->page = [
193 'title' => ''
194 ];
195 $tsfe->pSetup = [
196 'meta.' => $typoScript
197 ];
198 $GLOBALS['TSFE'] = $tsfe->reveal();
199
200 $pageRendererProphecy = $this->prophesize(PageRenderer::class);
201 GeneralUtility::setSingletonInstance(PageRenderer::class, $pageRendererProphecy->reveal());
202
203 PageGenerator::renderContentWithHeader('');
204
205 $pageRendererProphecy->setMetaTag($expectedTags['type'], $expectedTags['name'], $expectedTags['content'])->willThrow(\InvalidArgumentException::class);
206 }
207
208 /**
209 * @test
210 * @dataProvider generateMetaTagHtmlGeneratesCorrectTagsDataProvider
211 *
212 * @param array $typoScript
213 * @param string $stdWrapResult
214 * @param array $expectedTags
215 */
216 public function generateMetaTagHtmlGeneratesCorrectTags(array $typoScript, string $stdWrapResult, array $expectedTags)
217 {
218 $cObj = $this->prophesize(ContentObjectRenderer::class);
219 $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
220 $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
221 $tmpl = $this->prophesize(TemplateService::class);
222 $tsfe = $this->prophesize(TypoScriptFrontendController::class);
223 $tsfe->generatePageTitle()->willReturn('');
224 $tsfe->generateMetaTags()->shouldBeCalled();
225 $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
226 $tsfe->cObj = $cObj->reveal();
227 $tsfe->tmpl = $tmpl->reveal();
228 $tsfe->config['config'] = [];
229 $tsfe->page = [
230 'title' => ''
231 ];
232 $tsfe->pSetup = [
233 'meta.' => $typoScript
234 ];
235 $GLOBALS['TSFE'] = $tsfe->reveal();
236
237 $pageRendererProphecy = $this->prophesize(PageRenderer::class);
238 GeneralUtility::setSingletonInstance(PageRenderer::class, $pageRendererProphecy->reveal());
239
240 PageGenerator::renderContentWithHeader('');
241
242 $pageRendererProphecy->setMetaTag($expectedTags['type'], $expectedTags['name'], $expectedTags['content'], [], false)->shouldHaveBeenCalled();
243 }
244
245 /**
246 * @test
247 */
248 public function generateMetaTagHtmlGenerateNoTagWithEmptyContent()
249 {
250 $stdWrapResult = '';
251
252 $typoScript = [
253 'custom:key' => '',
254 ];
255
256 $cObj = $this->prophesize(ContentObjectRenderer::class);
257 $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
258 $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
259 $tmpl = $this->prophesize(TemplateService::class);
260 $tsfe = $this->prophesize(TypoScriptFrontendController::class);
261 $tsfe->generatePageTitle()->willReturn('');
262 $tsfe->generateMetaTags()->shouldBeCalled();
263 $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
264 $tsfe->cObj = $cObj->reveal();
265 $tsfe->tmpl = $tmpl->reveal();
266 $tsfe->config['config'] = [];
267 $tsfe->page = [
268 'title' => ''
269 ];
270 $tsfe->pSetup = [
271 'meta.' => $typoScript
272 ];
273 $GLOBALS['TSFE'] = $tsfe->reveal();
274
275 $pageRendererProphecy = $this->prophesize(PageRenderer::class);
276 GeneralUtility::setSingletonInstance(PageRenderer::class, $pageRendererProphecy->reveal());
277
278 PageGenerator::renderContentWithHeader('');
279
280 $pageRendererProphecy->setMetaTag(null, null, null)->shouldNotBeCalled();
281 }
282
283 public function generateMultipleMetaTagsDataProvider()
284 {
285 return [
286 'multi value attribute name' => [
287 [
288 'og:locale:alternate.' => [
289 'attribute' => 'property',
290 'value' => [
291 10 => 'nl_NL',
292 20 => 'de_DE',
293 ]
294 ],
295 ],
296 '',
297 [
298 [
299 'type' => 'property',
300 'name' => 'og:locale:alternate',
301 'content' => 'nl_NL'
302 ],
303 [
304 'type' => 'property',
305 'name' => 'og:locale:alternate',
306 'content' => 'de_DE'
307 ]
308 ]
309 ],
310 'multi value attribute name (empty values are skipped)' => [
311 [
312 'og:locale:alternate.' => [
313 'attribute' => 'property',
314 'value' => [
315 10 => 'nl_NL',
316 20 => '',
317 30 => 'de_DE',
318 ]
319 ],
320 ],
321 '',
322 [
323 [
324 'type' => 'property',
325 'name' => 'og:locale:alternate',
326 'content' => 'nl_NL'
327 ],
328 [
329 'type' => 'property',
330 'name' => 'og:locale:alternate',
331 'content' => 'de_DE'
332 ]
333 ],
334 ],
335 ];
336 }
337
338 /**
339 * @test
340 * @dataProvider generateMultipleMetaTagsDataProvider
341 *
342 * @param array $typoScript
343 * @param string $stdWrapResult
344 * @param array $expectedTags
345 */
346 public function generateMultipleMetaTags(array $typoScript, string $stdWrapResult, array $expectedTags)
347 {
348 $cObj = $this->prophesize(ContentObjectRenderer::class);
349 $cObj->cObjGet(Argument::cetera())->shouldBeCalled();
350 $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult);
351 $tmpl = $this->prophesize(TemplateService::class);
352 $tsfe = $this->prophesize(TypoScriptFrontendController::class);
353 $tsfe->generatePageTitle()->willReturn('');
354 $tsfe->generateMetaTags()->shouldBeCalled();
355 $tsfe->INTincScript_loadJSCode()->shouldBeCalled();
356 $tsfe->cObj = $cObj->reveal();
357 $tsfe->tmpl = $tmpl->reveal();
358 $tsfe->config['config'] = [];
359 $tsfe->page = [
360 'title' => ''
361 ];
362 $tsfe->pSetup = [
363 'meta.' => $typoScript
364 ];
365 $GLOBALS['TSFE'] = $tsfe->reveal();
366
367 $pageRendererProphecy = $this->prophesize(PageRenderer::class);
368 GeneralUtility::setSingletonInstance(PageRenderer::class, $pageRendererProphecy->reveal());
369
370 PageGenerator::renderContentWithHeader('');
371
372 $pageRendererProphecy->setMetaTag($expectedTags[0]['type'], $expectedTags[0]['name'], $expectedTags[0]['content'], [], false)->shouldHaveBeenCalled();
373 $pageRendererProphecy->setMetaTag($expectedTags[1]['type'], $expectedTags[1]['name'], $expectedTags[1]['content'], [], false)->shouldHaveBeenCalled();
374 }
375 }