Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Exporter testcase.
19
 *
20
 * @package    core
21
 * @copyright  2015 Damyon Wiese
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core;
26
 
27
use core_external\external_format_value;
28
use core_external\external_multiple_structure;
29
use core_external\external_settings;
30
use core_external\external_single_structure;
31
use core_external\external_value;
32
use core_external\util;
33
 
34
/**
35
 * Exporter testcase.
36
 *
37
 * @package    core
38
 * @copyright  2015 Damyon Wiese
39
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40
 */
1441 ariadna 41
final class exporter_test extends \advanced_testcase {
1 efrain 42
 
43
    protected $validrelated = null;
44
    protected $invalidrelated = null;
45
    protected $validdata = null;
46
    protected $invaliddata = null;
47
 
48
    public function setUp(): void {
1441 ariadna 49
        parent::setUp();
1 efrain 50
        $s = new \stdClass();
51
        $this->validrelated = array(
52
            'simplestdClass' => $s,
53
            'arrayofstdClass' => array($s, $s),
54
            'context' => null,
55
            'aint' => 5,
56
            'astring' => 'valid string',
57
            'abool' => false,
58
            'ints' => []
59
        );
60
        $this->invalidrelated = array(
61
            'simplestdClass' => 'a string',
62
            'arrayofstdClass' => 5,
63
            'context' => null,
64
            'aint' => false,
65
            'astring' => 4,
66
            'abool' => 'not a boolean',
67
            'ints' => null
68
        );
69
 
70
        $this->validdata = array('stringA' => 'A string', 'stringAformat' => FORMAT_HTML, 'intB' => 4);
71
 
72
        $this->invaliddata = array('stringA' => 'A string');
73
    }
74
 
11 efrain 75
    public function test_get_read_structure(): void {
1 efrain 76
        $structure = core_testable_exporter::get_read_structure();
77
 
78
        $this->assertInstanceOf(external_single_structure::class, $structure);
79
        $this->assertInstanceOf(external_value::class, $structure->keys['stringA']);
80
        $this->assertInstanceOf(external_format_value::class, $structure->keys['stringAformat']);
81
        $this->assertInstanceOf(external_value::class, $structure->keys['intB']);
82
        $this->assertInstanceOf(external_value::class, $structure->keys['otherstring']);
83
        $this->assertInstanceOf(external_multiple_structure::class, $structure->keys['otherstrings']);
84
    }
85
 
11 efrain 86
    public function test_get_create_structure(): void {
1 efrain 87
        $structure = core_testable_exporter::get_create_structure();
88
 
89
        $this->assertInstanceOf(external_single_structure::class, $structure);
90
        $this->assertInstanceOf(external_value::class, $structure->keys['stringA']);
91
        $this->assertInstanceOf(external_format_value::class, $structure->keys['stringAformat']);
92
        $this->assertInstanceOf(external_value::class, $structure->keys['intB']);
93
        $this->assertArrayNotHasKey('otherstring', $structure->keys);
94
        $this->assertArrayNotHasKey('otherstrings', $structure->keys);
95
    }
96
 
11 efrain 97
    public function test_get_update_structure(): void {
1 efrain 98
        $structure = core_testable_exporter::get_update_structure();
99
 
100
        $this->assertInstanceOf(external_single_structure::class, $structure);
101
        $this->assertInstanceOf(external_value::class, $structure->keys['stringA']);
102
        $this->assertInstanceOf(external_format_value::class, $structure->keys['stringAformat']);
103
        $this->assertInstanceOf(external_value::class, $structure->keys['intB']);
104
        $this->assertArrayNotHasKey('otherstring', $structure->keys);
105
        $this->assertArrayNotHasKey('otherstrings', $structure->keys);
106
    }
107
 
11 efrain 108
    public function test_invalid_data(): void {
1 efrain 109
        global $PAGE;
110
        $exporter = new core_testable_exporter($this->invaliddata, $this->validrelated);
111
        $output = $PAGE->get_renderer('core');
112
 
113
        // The exception message is a bit misleading, it actually indicates an expected property wasn't found.
114
        $this->expectException(\coding_exception::class);
115
        $this->expectExceptionMessage('Unexpected property stringAformat');
116
        $result = $exporter->export($output);
117
    }
118
 
11 efrain 119
    public function test_invalid_related(): void {
1 efrain 120
        $this->expectException(\coding_exception::class);
121
        $this->expectExceptionMessage('Exporter class is missing required related data: (core\core_testable_exporter) ' .
122
            'simplestdClass => stdClass');
123
        $exporter = new core_testable_exporter($this->validdata, $this->invalidrelated);
124
    }
125
 
11 efrain 126
    public function test_invalid_related_all_cases(): void {
1 efrain 127
        global $PAGE;
128
 
129
        foreach ($this->invalidrelated as $key => $value) {
130
            $data = $this->validrelated;
131
            $data[$key] = $value;
132
 
133
            try {
134
                $exporter = new core_testable_exporter($this->validdata, $data);
135
                $output = $PAGE->get_renderer('core');
136
                $result = $exporter->export($output);
137
            } catch (\coding_exception $e) {
138
                $this->assertNotFalse(strpos($e->getMessage(), $key));
139
            }
140
        }
141
    }
142
 
11 efrain 143
    public function test_valid_data_and_related(): void {
1 efrain 144
        global $PAGE;
145
        $output = $PAGE->get_renderer('core');
146
        $exporter = new core_testable_exporter($this->validdata, $this->validrelated);
147
        $result = $exporter->export($output);
148
        $this->assertSame('>Another string', $result->otherstring);
149
        $this->assertSame(array('String &gt;a', 'String b'), $result->otherstrings);
150
    }
151
 
11 efrain 152
    public function test_format_text(): void {
1 efrain 153
        global $PAGE;
154
 
155
        $this->resetAfterTest();
156
        $course = $this->getDataGenerator()->create_course();
157
        $syscontext = \context_system::instance();
158
        $coursecontext = \context_course::instance($course->id);
159
 
160
        external_settings::get_instance()->set_filter(true);
161
        filter_set_global_state('urltolink', TEXTFILTER_OFF);
162
        filter_set_local_state('urltolink', $coursecontext->id, TEXTFILTER_ON);
163
        set_config('formats', FORMAT_MARKDOWN, 'filter_urltolink');
164
        \filter_manager::reset_caches();
165
 
166
        $data = [
167
            'stringA' => '__Watch out:__ https://moodle.org @@PLUGINFILE@@/test.pdf',
168
            'stringAformat' => FORMAT_MARKDOWN,
169
            'intB' => 1
170
        ];
171
 
172
        // Export simulated in the system context.
173
        $output = $PAGE->get_renderer('core');
174
        $exporter = new core_testable_exporter($data, ['context' => $syscontext] + $this->validrelated);
175
        $result = $exporter->export($output);
176
 
177
        $youtube = 'https://moodle.org';
178
        $fileurl = (new \moodle_url('/webservice/pluginfile.php/' . $syscontext->id . '/test/area/9/test.pdf'))->out(false);
179
        $expected = "<p><strong>Watch out:</strong> $youtube $fileurl</p>\n";
180
        $this->assertEquals($expected, $result->stringA);
181
        $this->assertEquals(FORMAT_HTML, $result->stringAformat);
182
 
183
        // Export simulated in the course context where the filter is enabled.
184
        $exporter = new core_testable_exporter($data, ['context' => $coursecontext] + $this->validrelated);
185
        $result = $exporter->export($output);
186
        $youtube = '<a href="https://moodle.org" class="_blanktarget">https://moodle.org</a>';
187
        $fileurl = (new \moodle_url('/webservice/pluginfile.php/' . $coursecontext->id . '/test/area/9/test.pdf'))->out(false);
188
        $expected = "<p><strong>Watch out:</strong> $youtube <a href=\"$fileurl\" class=\"_blanktarget\">$fileurl</a></p>\n";
189
        $this->assertEquals($expected, $result->stringA);
190
        $this->assertEquals(FORMAT_HTML, $result->stringAformat);
191
    }
192
 
11 efrain 193
    public function test_properties_description(): void {
1 efrain 194
        $properties = core_testable_exporter::read_properties_definition();
195
        // Properties default description.
196
        $this->assertEquals('stringA', $properties['stringA']['description']);
197
        $this->assertEquals('stringAformat', $properties['stringAformat']['description']);
198
        // Properties custom description.
199
        $this->assertEquals('intB description', $properties['intB']['description']);
200
        // Other properties custom description.
201
        $this->assertEquals('otherstring description', $properties['otherstring']['description']);
202
        // Other properties default description.
203
        $this->assertEquals('otherstrings', $properties['otherstrings']['description']);
204
        // Assert nested elements are formatted correctly.
205
        $this->assertEquals('id', $properties['nestedarray']['type']['id']['description']);
206
    }
207
 
208
    /**
209
     * Tests for the handling of the default attribute of format properties in exporters.
210
     *
211
     * @covers \core\external\exporter::export
212
     * @return void
213
     */
214
    public function test_export_format_no_default(): void {
215
        global $PAGE;
216
        $output = $PAGE->get_renderer('core');
217
        $syscontext = \context_system::instance();
218
        $related = [
219
            'context' => $syscontext,
220
        ] + $this->validrelated;
221
 
222
        // Pass a data that does not have the format property for stringA.
223
        $data = [
224
            'stringA' => '__Go to:__ [Moodle.org](https://moodle.org)',
225
            'intB' => 1,
226
        ];
227
 
228
        // Note: For testing purposes only. Never extend exporter implementation. Only extend from the base exporter class!
229
        $testablexporterclass = new class($data, $related) extends core_testable_exporter {
230
            /**
231
             * Properties definition.
232
             */
233
            public static function define_properties(): array {
234
                $properties = parent::define_properties();
235
                $properties['stringAformat']['default'] = FORMAT_MARKDOWN;
236
                return $properties;
237
            }
238
        };
239
        // For a property format with default set, it should be able to export a data even if the property format is not passed.
240
        $result = $testablexporterclass->export($output);
241
        $expected = '<strong>Go to:</strong> <a href="https://moodle.org">Moodle.org</a>';
242
        $this->assertStringContainsString($expected, $result->stringA);
243
        $this->assertEquals(FORMAT_HTML, $result->stringAformat);
244
 
245
        // Passing data to an exporter with a required property format will throw an exception.
246
        $exporter = new core_testable_exporter($data, $related);
247
        $this->expectException(\coding_exception::class);
248
        $exporter->export($output);
249
    }
250
 
251
    /**
252
     * Test the processing of format properties.
253
     *
254
     * @covers \core\external\exporter::get_read_structure
255
     * @return void
256
     */
257
    public function test_format_properties_with_optional(): void {
258
        $testable = new class([]) extends \core\external\exporter {
259
            /**
260
             * Properties definition.
261
             *
262
             * @return array[]
263
             */
264
            public static function define_properties(): array {
265
                return [
266
                    'content' => [
267
                        'type' => PARAM_RAW,
268
                    ],
269
                    'contentformat' => [
270
                        'type' => PARAM_INT,
271
                        'optional' => true,
272
                    ],
273
                    'description' => [
274
                        'type' => PARAM_RAW,
275
                        'optional' => true,
276
                    ],
277
                    'descriptionformat' => [
278
                        'type' => PARAM_INT,
279
                        'default' => FORMAT_MARKDOWN,
280
                    ],
281
                    'summary' => [
282
                        'type' => PARAM_RAW,
283
                    ],
284
                    'summaryformat' => [
285
                        'type' => PARAM_INT,
286
                        'default' => null,
287
                    ],
288
                ];
289
            }
290
        };
291
 
292
        $definition = $testable::get_read_structure();
293
        // Check content and its format.
294
        $this->assertEquals(VALUE_REQUIRED, $definition->keys['content']->required);
295
        $this->assertEquals(VALUE_OPTIONAL, $definition->keys['contentformat']->required);
296
        $this->assertEquals(null, $definition->keys['contentformat']->default);
297
 
298
        // Check description and its format.
299
        $this->assertEquals(VALUE_OPTIONAL, $definition->keys['description']->required);
300
        $this->assertEquals(VALUE_DEFAULT, $definition->keys['descriptionformat']->required);
301
        $this->assertEquals(FORMAT_MARKDOWN, $definition->keys['descriptionformat']->default);
302
 
303
        // Check summary and its format.
304
        $this->assertEquals(VALUE_REQUIRED, $definition->keys['summary']->required);
305
        $this->assertEquals(null, $definition->keys['summary']->default);
306
        $this->assertEquals(VALUE_DEFAULT, $definition->keys['summaryformat']->required);
307
        $this->assertEquals(FORMAT_HTML, $definition->keys['summaryformat']->default);
308
    }
309
 
310
    /**
311
     * Test the processing of format properties when an invalid default format is passed.
312
     *
313
     * @covers \core\external\exporter::get_read_structure
314
     * @return void
315
     */
316
    public function test_optional_format_property_with_invalid_default(): void {
317
        $testable = new class([]) extends \core\external\exporter {
318
            /**
319
             * Properties definition.
320
             *
321
             * @return array[]
322
             */
323
            public static function define_properties(): array {
324
                return [
325
                    'description' => [
326
                        'type' => PARAM_RAW,
327
                    ],
328
                    'descriptionformat' => [
329
                        'type' => PARAM_INT,
330
                        'default' => 999,
331
                    ],
332
                ];
333
            }
334
        };
335
 
336
        $definition = $testable::get_read_structure();
337
        $this->assertDebuggingCalled(null, DEBUG_DEVELOPER);
338
 
339
        // Check description and its format.
340
        $this->assertEquals(VALUE_REQUIRED, $definition->keys['description']->required);
341
        $this->assertEquals(VALUE_DEFAULT, $definition->keys['descriptionformat']->required);
342
        $this->assertEquals(FORMAT_HTML, $definition->keys['descriptionformat']->default);
343
    }
344
}
345
 
346
/**
347
 * Example persistent class.
348
 *
349
 * @package    core
350
 * @copyright  2015 Frédéric Massart - FMCorz.net
351
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
352
 */
353
class core_testable_exporter extends \core\external\exporter {
354
 
355
    protected static function define_related() {
356
        // We cache the context so it does not need to be retrieved from the course.
357
        return array('simplestdClass' => 'stdClass', 'arrayofstdClass' => 'stdClass[]', 'context' => 'context?',
358
            'astring' => 'string', 'abool' => 'bool', 'aint' => 'int', 'ints' => 'int[]');
359
    }
360
 
361
    protected function get_other_values(\renderer_base $output) {
362
        return array(
363
            'otherstring' => '>Another <strong>string</strong>',
364
            'otherstrings' => array('String >a', 'String <strong>b</strong>')
365
        );
366
    }
367
 
368
    public static function define_properties() {
369
        return array(
370
            'stringA' => array(
371
                'type' => PARAM_RAW,
372
            ),
373
            'stringAformat' => array(
374
                'type' => PARAM_INT,
375
            ),
376
            'intB' => array(
377
                'type' => PARAM_INT,
378
                'description' => 'intB description',
379
            )
380
        );
381
    }
382
 
383
    public static function define_other_properties() {
384
        return array(
385
            'otherstring' => array(
386
                'type' => PARAM_TEXT,
387
                'description' => 'otherstring description',
388
            ),
389
            'otherstrings' => array(
390
                'type' => PARAM_TEXT,
391
                'multiple' => true
392
            ),
393
            'nestedarray' => array(
394
                'multiple' => true,
395
                'optional' => true,
396
                'type' => [
397
                    'id' => ['type' => PARAM_INT]
398
                ]
399
            )
400
        );
401
    }
402
 
403
    protected function get_format_parameters_for_stringA() {
404
        return [
405
            // For testing use the passed context if any.
406
            'context' => isset($this->related['context']) ? $this->related['context'] : \context_system::instance(),
407
            'component' => 'test',
408
            'filearea' => 'area',
409
            'itemid' => 9,
410
        ];
411
    }
412
 
413
    protected function get_format_parameters_for_otherstring() {
414
        return [
415
            'context' => \context_system::instance(),
416
            'options' => ['escape' => false]
417
        ];
418
    }
419
 
420
    protected function get_format_parameters_for_otherstrings() {
421
        return [
422
            'context' => \context_system::instance(),
423
        ];
424
    }
425
}