Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
164 / 164
100.00% covered (success)
100.00%
18 / 18
CRAP
100.00% covered (success)
100.00%
1 / 1
phpbb_textformatter_s9e_factory_test
100.00% covered (success)
100.00%
164 / 164
100.00% covered (success)
100.00%
18 / 18
20
100.00% covered (success)
100.00%
1 / 1
 setUp
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getDataSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_cache_dir
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_factory
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
2
 run_configurator_assertions
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 test_get_configurator
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 test_regenerate
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 test_tidy
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 test_local_url
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
1
 test_smilies_special_chars
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 test_duplicate_smilies
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 test_inttext_token
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 test_preserve_comments
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 test_unsafe_bbcode
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 test_unsafe_default_bbcodes
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 test_malformed_bbcodes
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 test_configure_events
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 configure_event_callback
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14require_once __DIR__ . '/../../test_framework/phpbb_database_test_case.php';
15
16class phpbb_textformatter_s9e_factory_test extends phpbb_database_test_case
17{
18    /**
19     * @var phpbb_mock_cache
20     */
21    private $cache;
22
23    /**
24     * @var phpbb_mock_event_dispatcher
25     */
26    private $dispatcher;
27
28    protected function setUp(): void
29    {
30        $this->cache = new phpbb_mock_cache;
31        $this->dispatcher = new phpbb_mock_event_dispatcher;
32        parent::setUp();
33    }
34
35    public function getDataSet()
36    {
37        return $this->createXMLDataSet(__DIR__ . '/fixtures/factory.xml');
38    }
39
40    public function get_cache_dir()
41    {
42        return __DIR__ . '/../../tmp/';
43    }
44
45    public function get_factory($styles_path = null)
46    {
47        global $config, $phpbb_root_path, $request, $symfony_request, $user;
48
49        if (!isset($styles_path))
50        {
51            $styles_path = $phpbb_root_path . 'styles/';
52        }
53
54        $this->cache = new phpbb_mock_cache;
55        $dal = new \phpbb\textformatter\data_access(
56            $this->new_dbal(),
57            'phpbb_bbcodes',
58            'phpbb_smilies',
59            'phpbb_styles',
60            'phpbb_words',
61            $styles_path
62        );
63        $factory = new \phpbb\textformatter\s9e\factory(
64            $dal,
65            $this->cache,
66            $this->dispatcher,
67            new \phpbb\config\config(array('allowed_schemes_links' => 'http,https,ftp')),
68            new \phpbb\textformatter\s9e\link_helper,
69            $this->getMockBuilder('phpbb\\log\\log_interface')->getMock(),
70            $this->get_cache_dir(),
71            '_foo_parser',
72            '_foo_renderer'
73        );
74
75        // Global objects required by generate_board_url()
76        $config = new \phpbb\config\config(array(
77            'script_path'           => '/phpbb',
78            'server_name'           => 'localhost',
79            'server_port'           => 80,
80            'server_protocol'       => 'http://',
81        ));
82        $request = new phpbb_mock_request;
83        $symfony_request = new \phpbb\symfony_request($request);
84        $user = new phpbb_mock_user;
85
86        return $factory;
87    }
88
89    public function run_configurator_assertions($configurator)
90    {
91        $this->assertInstanceOf('s9e\\TextFormatter\\Configurator', $configurator);
92
93        $this->assertTrue(isset($configurator->plugins['Autoemail']));
94        $this->assertTrue(isset($configurator->plugins['Autolink']));
95
96        $this->assertTrue(isset($configurator->BBCodes['B']));
97        $this->assertTrue(isset($configurator->BBCodes['CODE']));
98        $this->assertTrue(isset($configurator->BBCodes['COLOR']));
99        $this->assertTrue(isset($configurator->BBCodes['EMAIL']));
100        $this->assertTrue(isset($configurator->BBCodes['I']));
101        $this->assertTrue(isset($configurator->BBCodes['IMG']));
102        $this->assertTrue(isset($configurator->BBCodes['LIST']));
103        $this->assertTrue(isset($configurator->BBCodes['*']));
104        $this->assertTrue(isset($configurator->BBCodes['QUOTE']));
105        $this->assertTrue(isset($configurator->BBCodes['SIZE']));
106        $this->assertTrue(isset($configurator->BBCodes['U']));
107        $this->assertTrue(isset($configurator->BBCodes['URL']));
108
109        // This custom BBCode should be set
110        $this->assertTrue(isset($configurator->BBCodes['CUSTOM']));
111
112        $this->assertTrue(isset($configurator->Emoticons[':D']));
113    }
114
115    public function test_get_configurator()
116    {
117        $configurator = $this->get_factory()->get_configurator();
118        $this->run_configurator_assertions($configurator);
119
120        // Test with twigified bbcode.html
121        $configurator = $this->get_factory(__DIR__ . '/fixtures/styles/')->get_configurator();
122        $this->run_configurator_assertions($configurator);
123
124    }
125
126    public function test_regenerate()
127    {
128        extract($this->get_factory()->regenerate());
129
130        $this->assertInstanceOf('s9e\\TextFormatter\\Parser', $parser);
131        $this->assertInstanceOf('s9e\\TextFormatter\\Renderer', $renderer);
132
133        $renderer_data = $this->cache->get('_foo_renderer');
134        $this->assertEquals($parser, $this->cache->get('_foo_parser'), 'The parser was not cached');
135        $this->assertEquals(get_class($renderer), $renderer_data['class']);
136        $this->assertInstanceOf('s9e\\TextFormatter\\Plugins\\Censor\\Helper', $renderer_data['censor']);
137
138        $file = $this->get_cache_dir() . get_class($renderer) . '.php';
139        $this->assertFileExists($file);
140        unlink($file);
141    }
142
143    public function test_tidy()
144    {
145        $factory = $this->get_factory();
146
147        // Create a fake "old" cache file
148        $old_file = $this->get_cache_dir() . 's9e_foo.php';
149        touch($old_file);
150
151        // Create a current renderer
152        extract($factory->regenerate());
153        $new_file = $this->get_cache_dir() . get_class($renderer) . '.php';
154
155        // Tidy the cache
156        $factory->tidy();
157
158        $this->assertFileExists($new_file, 'The current renderer has been deleted');
159        $this->assertFileDoesNotExist($old_file, 'The old renderer has not been deleted');
160
161        unlink($new_file);
162    }
163
164    public function test_local_url()
165    {
166        global $config, $user, $request, $symfony_request;
167        $config = new \phpbb\config\config(array(
168            'force_server_vars' => true,
169            'server_protocol' => 'http://',
170            'server_name' => 'path',
171            'server_port' => 80,
172            'script_path' => '/to',
173            'cookie_secure' => false
174        ));
175        $user = new phpbb_mock_user;
176        $request = new phpbb_mock_request;
177        $symfony_request = new \phpbb\symfony_request($request);
178
179        $fixture = __DIR__ . '/fixtures/local_url.xml';
180        $renderer = $this->get_test_case_helpers()->set_s9e_services(null, $fixture)->get('text_formatter.renderer');
181
182        $this->assertSame(
183            '<a href="http://path/to/foo">http://path/to/foo</a>',
184            $renderer->render('<r><LOCAL content="foo"><s>[local]</s>foo<e>[/local]</e></LOCAL></r>')
185        );
186    }
187
188    public function test_smilies_special_chars()
189    {
190        // Use a smiley that contains every special chars in every field
191        $fixture = __DIR__ . '/fixtures/smilies_special_chars.xml';
192        $renderer = $this->get_test_case_helpers()->set_s9e_services(null, $fixture)->get('text_formatter.renderer');
193
194        $this->assertSame(
195            '<img class="smilies" src="phpBB/images/smilies/%22%27%3C&amp;%3E.png" width="15" height="17" alt="&quot;\'&lt;&amp;&gt;" title="&quot;\'&lt;&amp;&gt;">',
196            $renderer->render('<r><E>"\'&lt;&amp;&gt;</E></r>')
197        );
198    }
199
200    public function test_duplicate_smilies()
201    {
202        $fixture = __DIR__ . '/fixtures/smilies_duplicate.xml';
203        $parser = $this->get_test_case_helpers()->set_s9e_services(null, $fixture)->get('text_formatter.parser');
204
205        $this->assertSame(
206            '<r><E>:)</E></r>',
207            $parser->parse(':)')
208        );
209    }
210
211    /**
212    * @testdox {INTTEXT} is supported in custom BBCodes
213    */
214    public function test_inttext_token()
215    {
216        $fixture = __DIR__ . '/fixtures/inttext_token.xml';
217        $container = $this->get_test_case_helpers()->set_s9e_services(null, $fixture);
218        $parser = $container->get('text_formatter.parser');
219        $renderer = $container->get('text_formatter.renderer');
220
221        $original = '[spoiler=ɎɆS]text[/spoiler]';
222        $expected = '<div class="spoiler"><div class="title">ɎɆS</div><div class="content">text</div></div>';
223        $this->assertSame($expected, $renderer->render($parser->parse($original)));
224
225        $original = '[spoiler=N:O:P:E]text[/spoiler]';
226        $expected = $original;
227        $this->assertSame($expected, $renderer->render($parser->parse($original)));
228    }
229
230    /**
231    * @testdox Preserves comments in custom BBCodes
232    */
233    public function test_preserve_comments()
234    {
235        $fixture = __DIR__ . '/fixtures/preserve_comments.xml';
236        $container = $this->get_test_case_helpers()->set_s9e_services(null, $fixture);
237        $parser = $container->get('text_formatter.parser');
238        $renderer = $container->get('text_formatter.renderer');
239
240        $original = '[X]';
241        $expected = '<!-- comment -->';
242        $this->assertSame($expected, $renderer->render($parser->parse($original)));
243    }
244
245    /**
246    * @testdox Accepts unsafe custom BBCodes
247    */
248    public function test_unsafe_bbcode()
249    {
250        $fixture = __DIR__ . '/fixtures/unsafe_bbcode.xml';
251        $container = $this->get_test_case_helpers()->set_s9e_services(null, $fixture);
252        $parser = $container->get('text_formatter.parser');
253        $renderer = $container->get('text_formatter.renderer');
254
255        $original = '[xss=javascript:alert(1)]text[/xss]';
256        $expected = '<a href="javascript:alert(1)">text</a>';
257        $this->assertSame($expected, $renderer->render($parser->parse($original)));
258    }
259
260    /**
261    * @testdox Accepts unsafe default BBCodes
262    */
263    public function test_unsafe_default_bbcodes()
264    {
265        $fixture   = __DIR__ . '/fixtures/unsafe_default_bbcodes.xml';
266        $style_dir = __DIR__ . '/fixtures/styles/';
267        $container = $this->get_test_case_helpers()->set_s9e_services(null, $fixture, $style_dir);
268        $parser    = $container->get('text_formatter.parser');
269        $renderer  = $container->get('text_formatter.renderer');
270
271        $original = '[b]alert(1)[/b]';
272        $expected = '<script>alert(1)</script>';
273        $this->assertSame($expected, $renderer->render($parser->parse($original)));
274    }
275
276    /**
277    * @testdox Logs malformed BBCodes
278    */
279    public function test_malformed_bbcodes()
280    {
281        $log = $this->getMockBuilder('phpbb\\log\\log_interface')->getMock();
282        $log->expects($this->once())
283            ->method('add')
284            ->with('critical', ANONYMOUS, '', 'LOG_BBCODE_CONFIGURATION_ERROR', false, ['[x !x]{TEXT}[/x]', 'Cannot interpret the BBCode definition']);
285
286        $container = new phpbb_mock_container_builder;
287        $container->set('log', $log);
288
289        $fixture   = __DIR__ . '/fixtures/malformed_bbcode.xml';
290        $this->get_test_case_helpers()->set_s9e_services($container, $fixture);
291    }
292
293    /**
294    * @testdox get_configurator() triggers events before and after configuration
295    */
296    public function test_configure_events()
297    {
298        $this->dispatcher = $this->createMock('phpbb\\event\\dispatcher_interface');
299        $matcher = $this->exactly(2);
300        $this->dispatcher
301            ->expects($matcher)
302            ->method('trigger_event')
303            ->willReturnCallback(function($event, $vars) use ($matcher) {
304                $callNr = $matcher->numberOfInvocations();
305                match($callNr) {
306                    1 => $this->assertEquals('core.text_formatter_s9e_configure_before', $event),
307                    2 => $this->assertEquals('core.text_formatter_s9e_configure_after', $event),
308                };
309                $this->assertTrue($this->configure_event_callback($vars));
310                return $vars;
311            });
312        $this->get_factory()->get_configurator();
313    }
314
315    public function configure_event_callback($vars)
316    {
317        return isset($vars['configurator']) && $vars['configurator'] instanceof \s9e\TextFormatter\Configurator;
318    }
319}