Passed
Push — 4 ( 229672...2c54a3 )
by Maxime
08:01 queued 11s
created

ShortcodeParserTest::testExtractBefore()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\View\Tests\Parsers;
4
5
use SilverStripe\Dev\SapphireTest;
6
use SilverStripe\View\Parsers\ShortcodeParser;
7
8
class ShortcodeParserTest extends SapphireTest
9
{
10
11
    protected $arguments, $contents, $tagName, $parser;
12
    protected $extra = [];
13
14
    protected function setUp()
15
    {
16
        ShortcodeParser::get('test')->register('test_shortcode', [$this, 'shortcodeSaver']);
17
        $this->parser = ShortcodeParser::get('test');
18
19
        parent::setUp();
20
    }
21
22
    protected function tearDown()
23
    {
24
        ShortcodeParser::get('test')->unregister('test_shortcode');
25
26
        parent::tearDown();
27
    }
28
29
    /**
30
     * Tests that valid short codes that have not been registered are not replaced.
31
     */
32
    public function testNotRegisteredShortcode()
33
    {
34
        ShortcodeParser::$error_behavior = ShortcodeParser::STRIP;
35
36
        $this->assertEquals(
37
            '',
38
            $this->parser->parse('[not_shortcode]')
39
        );
40
41
        $this->assertEquals(
42
            '<img class="">',
43
            $this->parser->parse('<img class="[not_shortcode]">')
44
        );
45
46
        ShortcodeParser::$error_behavior = ShortcodeParser::WARN;
47
48
        $this->assertEquals(
49
            '<strong class="warning">[not_shortcode]</strong>',
50
            $this->parser->parse('[not_shortcode]')
51
        );
52
53
        ShortcodeParser::$error_behavior = ShortcodeParser::LEAVE;
54
55
        $this->assertEquals(
56
            '[not_shortcode]',
57
            $this->parser->parse('[not_shortcode]')
58
        );
59
        $this->assertEquals(
60
            '[not_shortcode /]',
61
            $this->parser->parse('[not_shortcode /]')
62
        );
63
        $this->assertEquals(
64
            '[not_shortcode,foo="bar"]',
65
            $this->parser->parse('[not_shortcode,foo="bar"]')
66
        );
67
        $this->assertEquals(
68
            '[not_shortcode]a[/not_shortcode]',
69
            $this->parser->parse('[not_shortcode]a[/not_shortcode]')
70
        );
71
        $this->assertEquals(
72
            '[/not_shortcode]',
73
            $this->parser->parse('[/not_shortcode]')
74
        );
75
76
        $this->assertEquals(
77
            '<img class="[not_shortcode]">',
78
            $this->parser->parse('<img class="[not_shortcode]">')
79
        );
80
    }
81
82
    public function simpleTagDataProvider()
83
    {
84
        return [
85
            ['[test_shortcode]'],
86
            ['[test_shortcode ]'],
87
            ['[test_shortcode,]'],
88
            ['[test_shortcode, ][test_shortcode/]'],
89
            ['[test_shortcode /]'],
90
            ['[test_shortcode,/]'],
91
            ['[test_shortcode, /]']
92
        ];
93
    }
94
95
    /**
96
     * @dataProvider simpleTagDataProvider
97
     */
98
    public function testSimpleTag($test)
99
    {
100
        $this->parser->parse($test);
101
        $this->assertEquals([], $this->arguments, $test);
102
        $this->assertEquals('', $this->contents, $test);
103
        $this->assertEquals('test_shortcode', $this->tagName, $test);
104
    }
105
106
    public function oneArgumentDataProvider()
107
    {
108
        return [
109
            ['[test_shortcode foo="bar"]'],
110
            ['[test_shortcode,foo="bar"]'],
111
            ["[test_shortcode foo='bar']"],
112
            ["[test_shortcode,foo='bar']"],
113
            ["[test_shortcode foo=bar]"],
114
            ["[test_shortcode,foo=bar]"],
115
            ['[test_shortcode  foo  =  "bar"  /]'],
116
            ['[test_shortcode,  foo  =  "bar"  /]']
117
        ];
118
    }
119
120
    /**
121
     * @dataProvider oneArgumentDataProvider
122
     */
123
    public function testOneArgument($test)
124
    {
125
        $this->parser->parse($test);
126
127
        $this->assertEquals(['foo' => 'bar'], $this->arguments, $test);
128
        $this->assertEquals('', $this->contents, $test);
129
        $this->assertEquals('test_shortcode', $this->tagName, $test);
130
    }
131
132
    public function testMultipleArguments()
133
    {
134
        $this->parser->parse('[test_shortcode foo = "bar",bar=\'foo\', baz="buz"]');
135
136
        $this->assertEquals(['foo' => 'bar', 'bar' => 'foo', 'baz' => 'buz'], $this->arguments);
137
        $this->assertEquals('', $this->contents);
138
        $this->assertEquals('test_shortcode', $this->tagName);
139
    }
140
141
    public function emptyArgumentsDataProvider()
142
    {
143
        return [
144
            ['[test_shortcode foo=""]'],
145
            ['[test_shortcode,foo=\'\']'],
146
            ['[test_shortcode foo=""][/test_shortcode]'],
147
        ];
148
    }
149
150
    /**
151
     * @dataProvider emptyArgumentsDataProvider
152
     */
153
    public function testEmptyArguments($test)
154
    {
155
        $this->parser->parse($test);
156
        $this->assertEquals(['foo' => ''], $this->arguments);
157
        $this->assertEquals('', $this->contents);
158
        $this->assertEquals('test_shortcode', $this->tagName);
159
    }
160
161
    public function testEnclosing()
162
    {
163
        $this->parser->parse('[test_shortcode]foo[/test_shortcode]');
164
165
        $this->assertEquals([], $this->arguments);
166
        $this->assertEquals('foo', $this->contents);
167
        $this->assertEquals('test_shortcode', $this->tagName);
168
    }
169
170
    public function testEnclosingWithArguments()
171
    {
172
        $this->parser->parse('[test_shortcode,foo = "bar",bar=\'foo\',baz="buz"]foo[/test_shortcode]');
173
174
        $this->assertEquals(['foo' => 'bar', 'bar' => 'foo', 'baz' => 'buz'], $this->arguments);
175
        $this->assertEquals('foo', $this->contents);
176
        $this->assertEquals('test_shortcode', $this->tagName);
177
    }
178
179
    public function testShortcodeEscaping()
180
    {
181
        $this->assertEquals(
182
            '[test_shortcode]',
183
            $this->parser->parse('[[test_shortcode]]')
184
        );
185
186
        $this->assertEquals(
187
            '[test_shortcode /]',
188
            $this->parser->parse('[[test_shortcode /]]')
189
        );
190
191
        $this->assertEquals(
192
            '[test_shortcode]content[/test_shortcode]',
193
            $this->parser->parse('[[test_shortcode]content[/test_shortcode]]')
194
        );
195
196
        $this->assertEquals(
197
            '[test_shortcode]content',
198
            $this->parser->parse('[[test_shortcode]][test_shortcode]content[/test_shortcode]')
199
        );
200
201
        $this->assertEquals(
202
            '[test_shortcode]content[/test_shortcode]content2',
203
            $this->parser->parse('[[test_shortcode]content[/test_shortcode]][test_shortcode]content2[/test_shortcode]')
204
        );
205
206
        $this->assertEquals(
207
            '[[Doesnt strip double [ character if not a shortcode',
208
            $this->parser->parse('[[Doesnt strip double [ character if not a [test_shortcode]shortcode[/test_shortcode]')
209
        );
210
211
        $this->assertEquals(
212
            '[[Doesnt shortcode get confused by double ]] characters',
213
            $this->parser->parse(
214
                '[[Doesnt [test_shortcode]shortcode[/test_shortcode] get confused by double ]] characters'
215
            )
216
        );
217
    }
218
219
    public function testUnquotedArguments()
220
    {
221
        $this->assertEquals('', $this->parser->parse('[test_shortcode,foo=bar!,baz = buz123]'));
222
        $this->assertEquals(['foo' => 'bar!', 'baz' => 'buz123'], $this->arguments);
223
    }
224
225
    public function testSpacesForDelimiter()
226
    {
227
        $this->assertEquals('', $this->parser->parse('[test_shortcode foo=bar! baz = buz123]'));
228
        $this->assertEquals(['foo' => 'bar!', 'baz' => 'buz123'], $this->arguments);
229
    }
230
231
    public function testSelfClosingTag()
232
    {
233
        $this->assertEquals(
234
            'morecontent',
235
            $this->parser->parse('[test_shortcode,id="1"/]more[test_shortcode,id="2"]content[/test_shortcode]'),
236
            'Assert that self-closing tags are respected during parsing.'
237
        );
238
239
        $this->assertEquals(2, $this->arguments['id']);
240
    }
241
242
    public function testConsecutiveTags()
243
    {
244
        $this->assertEquals('', $this->parser->parse('[test_shortcode][test_shortcode]'));
245
    }
246
247
    protected function assertEqualsIgnoringWhitespace($a, $b, $message = null)
248
    {
249
        $this->assertEquals(preg_replace('/\s+/', '', $a), preg_replace('/\s+/', '', $b), $message);
250
    }
251
252
    public function testExtractBefore()
253
    {
254
        // Left extracts to before the current block
255
        $this->assertEqualsIgnoringWhitespace(
256
            'Code<div>FooBar</div>',
257
            $this->parser->parse('<div>Foo[test_shortcode class=left]Code[/test_shortcode]Bar</div>')
258
        );
259
        // Even if the immediate parent isn't a the current block
260
        $this->assertEqualsIgnoringWhitespace(
261
            'Code<div>Foo<b>BarBaz</b>Qux</div>',
262
            $this->parser->parse('<div>Foo<b>Bar[test_shortcode class=left]Code[/test_shortcode]Baz</b>Qux</div>')
263
        );
264
    }
265
266
    public function testExtractSplit()
267
    {
268
        $this->markTestSkipped(
269
            'Feature disabled due to https://github.com/silverstripe/silverstripe-framework/issues/5987'
270
        );
271
        // Center splits the current block
272
        $this->assertEqualsIgnoringWhitespace(
273
            '<div>Foo</div>Code<div>Bar</div>',
274
            $this->parser->parse('<div>Foo[test_shortcode class=center]Code[/test_shortcode]Bar</div>')
275
        );
276
        // Even if the immediate parent isn't a the current block
277
        $this->assertEqualsIgnoringWhitespace(
278
            '<div>Foo<b>Bar</b></div>Code<div><b>Baz</b>Qux</div>',
279
            $this->parser->parse('<div>Foo<b>Bar[test_shortcode class=center]Code[/test_shortcode]Baz</b>Qux</div>')
280
        );
281
    }
282
283
    public function testExtractNone()
284
    {
285
        // No class means don't extract
286
        $this->assertEqualsIgnoringWhitespace(
287
            '<div>FooCodeBar</div>',
288
            $this->parser->parse('<div>Foo[test_shortcode]Code[/test_shortcode]Bar</div>')
289
        );
290
    }
291
292
    public function testShortcodesInsideScriptTag()
293
    {
294
        $this->assertEqualsIgnoringWhitespace(
295
            '<script>hello</script>',
296
            $this->parser->parse('<script>[test_shortcode]hello[/test_shortcode]</script>')
297
        );
298
    }
299
300
    public function testFalseyArguments()
301
    {
302
        $this->parser->parse('<p>[test_shortcode falsey=0]');
303
304
        $this->assertEquals(
305
            [
306
            'falsey' => '',
307
            ],
308
            $this->arguments
309
        );
310
    }
311
312
    public function testNumericShortcodes()
313
    {
314
        $this->assertEqualsIgnoringWhitespace(
315
            '[2]',
316
            $this->parser->parse('[2]')
317
        );
318
        $this->assertEqualsIgnoringWhitespace(
319
            '<script>[2]</script>',
320
            $this->parser->parse('<script>[2]</script>')
321
        );
322
323
        $this->parser->register(
324
            '2',
325
            function () {
326
                return 'this is 2';
327
            }
328
        );
329
330
        $this->assertEqualsIgnoringWhitespace(
331
            'this is 2',
332
            $this->parser->parse('[2]')
333
        );
334
        $this->assertEqualsIgnoringWhitespace(
335
            '<script>this is 2</script>',
336
            $this->parser->parse('<script>[2]</script>')
337
        );
338
339
        $this->parser->unregister('2');
340
    }
341
342
    public function testExtraContext()
343
    {
344
        $this->parser->parse('<a href="[test_shortcode]">Test</a>');
345
346
        $this->assertInstanceOf('DOMNode', $this->extra['node']);
347
        $this->assertInstanceOf('DOMElement', $this->extra['element']);
348
        $this->assertEquals($this->extra['element']->tagName, 'a');
349
    }
350
351
    public function testShortcodeWithAnchorAndQuerystring()
352
    {
353
        $result = $this->parser->parse('<a href="[test_shortcode]?my-string=this&thing=2#my-anchor">Link</a>');
354
355
        $this->assertContains('my-string=this', $result);
356
        $this->assertContains('thing=2', $result);
357
        $this->assertContains('my-anchor', $result);
358
    }
359
360
    public function testNoParseAttemptIfNoCode()
361
    {
362
        $stub = $this->getMockBuilder(ShortcodeParser::class)->setMethods(['replaceElementTagsWithMarkers'])
363
            ->getMock();
364
        $stub->register(
365
            'test',
366
            function () {
367
                return '';
368
            }
369
        );
370
371
        $stub->expects($this->never())
372
            ->method('replaceElementTagsWithMarkers')->will($this->returnValue(['', '']));
373
374
        $stub->parse('<p>test</p>');
375
    }
376
377
    public function testSelfClosingHtmlTags()
378
    {
379
        $this->parser->register('img', function () {
380
            return '<img src="http://example.com/image.jpg">';
381
        });
382
383
        $result = $this->parser->parse('[img]');
384
385
        $this->assertContains('http://example.com/image.jpg', $result);
386
    }
387
388
    // -----------------------------------------------------------------------------------------------------------------
389
390
    /**
391
     * Stores the result of a shortcode parse in object properties for easy testing access.
392
     */
393
    public function shortcodeSaver($arguments, $content, $parser, $tagName, $extra)
394
    {
395
        $this->arguments = $arguments;
396
        $this->contents = $content;
397
        $this->tagName = $tagName;
398
        $this->extra = $extra;
399
400
        return $content;
401
    }
402
}
403