MDL-69549 core: Add context export API
[moodle.git] / lib / tests / content / export / exportable_items / exportable_textarea_test.php
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/>.
17 /**
18  * Unit tests for core\content\exportable_items\exportable_textarea.
19  *
20  * @package     core
21  * @category    test
22  * @copyright   2020 Andrew Nicols <andrew@nicols.co.uk>
23  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 declare(strict_types=1);
28 namespace core\content\export\exportable_items;
30 use advanced_testcase;
31 use context;
32 use context_module;
33 use context_system;
34 use core\content\export\zipwriter;
35 use moodle_url;
36 use stdClass;
38 /**
39  * Unit tests for the `exportable_textarea` export item class.
40  *
41  * @coversDefaultClass core\content\exportable_items\exportable_textarea
42  */
43 class exportable_textarea_test extends advanced_testcase {
45     /**
46      * Ensure that an exportable textarea which does not relate to any content, does not attempt to export any content.
47      */
48     public function test_valid_table_without_content(): void {
49         $this->resetAfterTest(true);
51         $user = $this->getDataGenerator()->create_user();
53         $context = context_system::instance();
54         $component = 'page';
55         $uservisiblename = 'Page content';
56         $tablename = 'page';
57         $fieldname = 'content';
58         $fieldid = -1;
59         $formatfieldname = 'contentformat';
60         $filearea = 'content';
62         $exportable = new exportable_textarea(
63             $context,
64             $component,
65             $uservisiblename,
66             $tablename,
67             $fieldname,
68             $fieldid,
69             $formatfieldname
70         );
72         $this->assertInstanceOf(exportable_textarea::class, $exportable);
74         $this->assert_exportable_empty($component, $user, $context, $exportable);
75     }
77     /**
78      * Ensure that the an exportable textarea exports content from the appropriate locations, but without any files.
79      */
80     public function test_valid_table_with_content_no_filearea_specified(): void {
81         $this->resetAfterTest(true);
83         $content = '<h1>Hello</h1><p>World!</p>';
85         $user = $this->getDataGenerator()->create_user();
86         $course = $this->getDataGenerator()->create_course();
87         $page = $this->getDataGenerator()->create_module('page', (object) [
88             'course' => $course,
89             'content' => $content,
90             'contentformat' => FORMAT_HTML,
91         ]);
93         $context = context_module::instance($page->cmid);
94         $expectedfiles = $this->create_files($context, 'mod_page', 'content', (int) $page->id, 5);
96         // Unexpected files.
97         $this->create_files($context, 'mod_page', 'content', (int) $page->id + 1, 5);
98         $this->create_files($context, 'mod_page', 'othercontent', (int) $page->id, 5);
99         $this->create_files($context, 'mod_foo', 'content', (int) $page->id, 5);
101         $component = 'page';
102         $uservisiblename = 'Page content';
103         $tablename = 'page';
104         $fieldname = 'content';
105         $fieldid = (int) $page->id;
106         $formatfieldname = 'contentformat';
108         $exportable = new exportable_textarea(
109             $context,
110             $component,
111             $uservisiblename,
112             $tablename,
113             $fieldname,
114             $fieldid,
115             $formatfieldname
116         );
118         $this->assertInstanceOf(exportable_textarea::class, $exportable);
120         // Although files exist, the filearea and itemid were not included.
121         $this->assert_exportable_matches_file($component, $user, $context, null, $content, [], '', $exportable);
122     }
124     /**
125      * Ensure that the an exportable textarea exports content from the appropriate locations, but without any files.
126      */
127     public function test_valid_table_with_content_no_itemid_specified(): void {
128         $this->resetAfterTest(true);
130         $content = '<h1>Hello</h1><p>World!</p>';
132         $user = $this->getDataGenerator()->create_user();
133         $course = $this->getDataGenerator()->create_course();
134         $page = $this->getDataGenerator()->create_module('page', (object) [
135             'course' => $course,
136             'content' => $content,
137             'contentformat' => FORMAT_HTML,
138         ]);
140         $context = context_module::instance($page->cmid);
141         $expectedfiles = $this->create_files($context, 'mod_page', 'content', (int) $page->id, 5);
143         // Unexpected files.
144         $this->create_files($context, 'mod_page', 'content', (int) $page->id + 1, 5);
145         $this->create_files($context, 'mod_page', 'othercontent', (int) $page->id, 5);
146         $this->create_files($context, 'mod_foo', 'content', (int) $page->id, 5);
148         $component = 'page';
149         $uservisiblename = 'Page content';
150         $tablename = 'page';
151         $fieldname = 'content';
152         $fieldid = (int) $page->id;
153         $formatfieldname = 'contentformat';
154         $filearea = 'content';
156         $exportable = new exportable_textarea(
157             $context,
158             $component,
159             $uservisiblename,
160             $tablename,
161             $fieldname,
162             $fieldid,
163             $formatfieldname,
164             $filearea
165         );
167         $this->assertInstanceOf(exportable_textarea::class, $exportable);
169         // Although files exist, the filearea and itemid were not included.
170         $this->assert_exportable_matches_file($component, $user, $context, null, $content, [], '', $exportable);
171     }
173     /**
174      * Ensure that the an exportable textarea exports content from the appropriate locations, with files.
175      */
176     public function test_valid_table_with_content_and_files(): void {
177         $this->resetAfterTest(true);
178         $user = $this->getDataGenerator()->create_user();
180         $contentin = <<<EOF
181 <h1>Hello</h1><p>World!</p>
182 <img src='@@PLUGINFILE@@/file.txt'>
183 <img src='@@PLUGINFILE@@/other/file.txt'>
184 EOF;
185         $course = $this->getDataGenerator()->create_course();
186         $page = $this->getDataGenerator()->create_module('page', (object) [
187             'course' => $course,
188             'content' => $contentin,
189             'contentformat' => FORMAT_HTML,
190         ]);
192         $this->setUser($user);
194         $context = context_module::instance($page->cmid);
195         $expectedfiles = $this->create_files(
196             $context,
197             'mod_page',
198             'content',
199             (int) $page->id,
200             5,
201             'contentformat',
202             'content',
203             (int) $page->id,
204             5
205         );
207         // Unexpected files.
208         $this->create_files($context, 'mod_page', 'content', (int) $page->id + 1, 5);
209         $this->create_files($context, 'mod_page', 'othercontent', (int) $page->id, 5);
210         $this->create_files($context, 'mod_foo', 'content', (int) $page->id, 5);
212         $component = 'mod_page';
213         $uservisiblename = 'Page content';
214         $tablename = 'page';
215         $fieldname = 'content';
216         $fieldid = (int) $page->id;
217         $formatfieldname = 'contentformat';
218         $filearea = 'content';
219         $itemid = (int) $page->id;
221         $exportable = new exportable_textarea(
222             $context,
223             $component,
224             $uservisiblename,
225             $tablename,
226             $fieldname,
227             $fieldid,
228             $formatfieldname,
229             $filearea,
230             $itemid,
231             null
232         );
234         $this->assertInstanceOf(exportable_textarea::class, $exportable);
236         $pluginfilebase = moodle_url::make_pluginfile_url(
237             $context->id, $component, $filearea, null, '', '', false, true
238         )->out(false);
239         $expectedcontent = <<<EOF
240 <h1>Hello</h1><p>World!</p>
241 <img src='content/file.txt'>
242 <img src='{$pluginfilebase}/other/file.txt'>
243 EOF;
245         // Although files exist, the filearea and itemid were not included.
246         $this->assert_exportable_matches_file(
247             $component, $user, $context, $filearea, $expectedcontent, $expectedfiles, '', $exportable
248         );
249     }
251     /**
252      * Create files for use in testing.
253      *
254      * @param   context $context
255      * @param   string $component
256      * @param   string $filearea
257      * @param   int $itemid
258      * @param   int $count
259      * @return  stored_file[]
260      */
261     protected function create_files(context $context, string $component, string $filearea, int $itemid, int $count = 1): array {
262         $fs = get_file_storage();
264         $files = [];
265         for ($i = 0; $i < $count; $i++) {
267             $filepath = '/';
268             for ($j = 0; $j < $i; $j++) {
269                 $filepath .= "{$j}/";
270             }
272             $files[] = $fs->create_file_from_string(
273                 (object) [
274                     'contextid' => $context->id,
275                     'component' => $component,
276                     'filearea' => $filearea,
277                     'filepath' => $filepath,
278                     'filename' => "file.txt",
279                     'itemid' => $itemid,
280                 ],
281                 "File content: {$i}"
282             );
283         }
285         return $files;
286     }
288     /**
289      * Assert that the supplied expotable matches the supplied file.
290      *
291      * @param   string $component
292      * @param   stdClass $user
293      * @param   context $context
294      * @param   string $filearea
295      * @param   string $content
296      * @param   stored_file[] $expectedfiles
297      * @param   string $subdir
298      * @param   exportable_textarea $exportable
299      */
300     protected function assert_exportable_matches_file(
301         string $component,
302         stdClass $user,
303         context $context,
304         ?string $filearea,
305         string $content,
306         array $expectedfiles,
307         string $subdir,
308         exportable_textarea $exportable
309     ): void {
310         $archive = $this->getMockBuilder(zipwriter::class)
311             ->setConstructorArgs([$this->getMockBuilder(\ZipStream\ZipStream::class)->getmock()])
312             ->setMethods([
313                 'is_file_in_archive',
314                 'add_file_from_string',
315                 'add_file_from_stored_file',
316             ])
317             ->getMock();
319         $archive->expects($this->any())
320             ->method('is_file_in_archive')
321             ->willReturn(true);
323         $storedfileargs = [];
324         foreach ($expectedfiles as $file) {
325             $filepathinzip = dirname($subdir) . $file->get_filearea() . '/' . $file->get_filepath() . $file->get_filename();
326             $filepathinzip = ltrim(preg_replace('#/+#', '/', $filepathinzip), '/');
327             $storedfileargs[] = [
328                 $this->equalTo($context),
329                 $this->equalTo($filepathinzip),
330                 $this->equalTo($file),
331             ];
332         }
334         $archive->expects($this->exactly(count($expectedfiles)))
335             ->method('add_file_from_stored_file')
336             ->withConsecutive(...$storedfileargs);
338         $archive->expects($this->never())
339             ->method('add_file_from_string');
341         $exportable->add_to_archive($archive);
342     }
344     /**
345      * Assert that the supplied expotable matches the supplied file.
346      *
347      * @param   string $component
348      * @param   stdClass $user
349      * @param   context $context
350      * @param   exportable_textarea $exportable
351      */
352     protected function assert_exportable_empty(
353         string $component,
354         stdClass $user,
355         context $context,
356         exportable_textarea $exportable
357     ): void {
358         $archive = $this->getMockBuilder(zipwriter::class)
359             ->setConstructorArgs([$this->getMockBuilder(\ZipStream\ZipStream::class)->getmock()])
360             ->setMethods([
361                 'add_file_from_stored_file',
362                 'add_file_from_string',
363                 'add_file_from_template',
364             ])
365             ->getMock();
367         $archive->expects($this->never())
368             ->method('add_file_from_stored_file');
369         $archive->expects($this->never())
370             ->method('add_file_from_string');
371         $archive->expects($this->never())
372             ->method('add_file_from_template');
374         $exportable->add_to_archive($archive);
375     }