3 declare(strict_types
=1);
5 namespace PhpMyAdmin\Tests\Plugins\Export
;
9 use PhpMyAdmin\ConfigStorage\Relation
;
10 use PhpMyAdmin\ConfigStorage\RelationParameters
;
11 use PhpMyAdmin\Current
;
12 use PhpMyAdmin\Dbal\DatabaseInterface
;
13 use PhpMyAdmin\Export\Export
;
14 use PhpMyAdmin\Http\Factory\ServerRequestFactory
;
15 use PhpMyAdmin\Identifiers\TableName
;
16 use PhpMyAdmin\Identifiers\TriggerName
;
17 use PhpMyAdmin\Plugins\Export\ExportHtmlword
;
18 use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup
;
19 use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup
;
20 use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem
;
21 use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem
;
22 use PhpMyAdmin\Properties\Options\Items\TextPropertyItem
;
23 use PhpMyAdmin\Properties\Plugins\ExportPluginProperties
;
24 use PhpMyAdmin\Tests\AbstractTestCase
;
25 use PhpMyAdmin\Tests\Stubs\DbiDummy
;
26 use PhpMyAdmin\Tests\Stubs\DummyResult
;
27 use PhpMyAdmin\Transformations
;
28 use PhpMyAdmin\Triggers\Event
;
29 use PhpMyAdmin\Triggers\Timing
;
30 use PhpMyAdmin\Triggers\Trigger
;
31 use PHPUnit\Framework\Attributes\CoversClass
;
32 use PHPUnit\Framework\Attributes\Medium
;
34 use ReflectionProperty
;
37 use function ob_get_clean
;
38 use function ob_start
;
40 #[CoversClass(ExportHtmlword::class)]
42 class ExportHtmlwordTest
extends AbstractTestCase
44 protected DatabaseInterface
$dbi;
46 protected DbiDummy
$dummyDbi;
48 protected ExportHtmlword
$object;
51 * Configures global environment.
53 protected function setUp(): void
57 $this->dummyDbi
= $this->createDbiDummy();
58 $this->dbi
= $this->createDatabaseInterface($this->dummyDbi
);
59 DatabaseInterface
::$instance = $this->dbi
;
60 $this->object = new ExportHtmlword(
61 new Relation($this->dbi
),
62 new Export($this->dbi
),
63 new Transformations(),
65 Export
::$outputKanjiConversion = false;
66 Export
::$outputCharsetConversion = false;
67 Export
::$bufferNeeded = false;
68 Export
::$asFile = true;
69 Export
::$saveOnServer = false;
70 Current
::$database = '';
73 Config
::getInstance()->selectedServer
['DisableIS'] = true;
77 * tearDown for test cases
79 protected function tearDown(): void
86 public function testSetProperties(): void
88 $method = new ReflectionMethod(ExportHtmlword
::class, 'setProperties');
89 $method->invoke($this->object, null);
91 $attrProperties = new ReflectionProperty(ExportHtmlword
::class, 'properties');
92 $properties = $attrProperties->getValue($this->object);
94 self
::assertInstanceOf(ExportPluginProperties
::class, $properties);
97 'Microsoft Word 2000',
98 $properties->getText(),
103 $properties->getExtension(),
107 'application/vnd.ms-word',
108 $properties->getMimeType(),
113 $properties->getOptionsText(),
117 $properties->getForceFile(),
120 $options = $properties->getOptions();
122 self
::assertInstanceOf(OptionsPropertyRootGroup
::class, $options);
125 'Format Specific Options',
129 $generalOptionsArray = $options->getProperties();
130 $generalOptions = $generalOptionsArray->current();
131 $generalOptionsArray->next();
133 self
::assertInstanceOf(OptionsPropertyMainGroup
::class, $generalOptions);
137 $generalOptions->getName(),
142 $generalOptions->getText(),
145 $generalProperties = $generalOptions->getProperties();
147 $property = $generalProperties->current();
149 self
::assertInstanceOf(RadioPropertyItem
::class, $property);
153 $property->getName(),
157 ['structure' => __('structure'), 'data' => __('data'), 'structure_and_data' => __('structure and data')],
158 $property->getValues(),
161 $generalOptions = $generalOptionsArray->current();
163 self
::assertInstanceOf(OptionsPropertyMainGroup
::class, $generalOptions);
167 $generalOptions->getName(),
172 $generalOptions->getText(),
177 $generalOptions->getForce(),
180 $generalProperties = $generalOptions->getProperties();
182 $property = $generalProperties->current();
183 $generalProperties->next();
185 self
::assertInstanceOf(TextPropertyItem
::class, $property);
189 $property->getName(),
193 'Replace NULL with:',
194 $property->getText(),
197 $property = $generalProperties->current();
199 self
::assertInstanceOf(BoolPropertyItem
::class, $property);
203 $property->getName(),
207 'Put columns names in the first row',
208 $property->getText(),
212 public function testExportHeader(): void
215 $this->object->exportHeader();
216 $result = ob_get_clean();
218 $expected = '<html xmlns:o="urn:schemas-microsoft-com:office:office"
219 xmlns:x="urn:schemas-microsoft-com:office:word"
220 xmlns="http://www.w3.org/TR/REC-html40">
222 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
223 . ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
226 <meta http-equiv="Content-type" content="text/html;charset='
231 self
::assertSame($expected, $result);
235 Current
::$charset = 'ISO-8859-1';
237 $this->object->exportHeader();
238 $result = ob_get_clean();
240 $expected = '<html xmlns:o="urn:schemas-microsoft-com:office:office"
241 xmlns:x="urn:schemas-microsoft-com:office:word"
242 xmlns="http://www.w3.org/TR/REC-html40">
244 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
245 . ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
248 <meta http-equiv="Content-type" content="text/html;charset='
253 self
::assertSame($expected, $result);
256 public function testExportFooter(): void
260 $this->object->exportFooter(),
262 $result = ob_get_clean();
264 self
::assertSame('</body></html>', $result);
267 public function testExportDBHeader(): void
271 $this->object->exportDBHeader('d"b'),
273 $result = ob_get_clean();
275 self
::assertSame('<h1>Database d"b</h1>', $result);
278 public function testExportDBFooter(): void
281 $this->object->exportDBFooter('testDB'),
285 public function testExportDBCreate(): void
288 $this->object->exportDBCreate('testDB'),
292 public function testExportData(): void
295 Export
::$outputKanjiConversion = false;
296 Export
::$outputCharsetConversion = false;
297 Export
::$bufferNeeded = false;
298 Export
::$asFile = true;
299 Export
::$saveOnServer = false;
301 $request = ServerRequestFactory
::create()->createServerRequest('POST', 'https://example.com/')
302 ->withParsedBody(['htmlword_columns' => 'On']);
304 $this->object->setExportOptions($request, []);
307 self
::assertTrue($this->object->exportData(
310 'SELECT * FROM `test_db`.`test_table`;',
312 $result = ob_get_clean();
315 '<h2>Dumping data for table test_table</h2>'
316 . '<table width="100%" cellspacing="1"><tr class="print-category">'
317 . '<td class="print"><strong>id</strong></td>'
318 . '<td class="print"><strong>name</strong></td>'
319 . '<td class="print"><strong>datetimefield</strong></td>'
320 . '</tr><tr class="print-category">'
321 . '<td class="print">1</td><td class="print">abcd</td><td class="print">2011-01-20 02:00:02</td>'
322 . '</tr><tr class="print-category">'
323 . '<td class="print">2</td><td class="print">foo</td><td class="print">2010-01-20 02:00:02</td>'
324 . '</tr><tr class="print-category">'
325 . '<td class="print">3</td><td class="print">Abcd</td><td class="print">2012-01-20 02:00:02</td>'
331 public function testGetTableDefStandIn(): void
333 $this->object = $this->getMockBuilder(ExportHtmlword
::class)
334 ->onlyMethods(['formatOneColumnDefinition'])
335 ->disableOriginalConstructor()
340 $keys = [['Non_unique' => 0, 'Column_name' => 'name1'], ['Non_unique' => 1, 'Column_name' => 'name2']];
342 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
343 ->disableOriginalConstructor()
346 $dbi->expects(self
::once())
347 ->method('getTableIndexes')
348 ->with('database', 'view')
351 $column = new Column('column', '', null, false, '', null, '', '', '');
353 $dbi->expects(self
::once())
354 ->method('getColumns')
355 ->with('database', 'view')
356 ->willReturn([$column]);
358 DatabaseInterface
::$instance = $dbi;
360 $this->object->expects(self
::once())
361 ->method('formatOneColumnDefinition')
362 ->with($column, ['name1'], 'column')
366 '<table width="100%" cellspacing="1">' .
367 '<tr class="print-category"><th class="print">Column</th>' .
368 '<td class="print"><strong>Type</strong></td>' .
369 '<td class="print"><strong>Null</strong></td>' .
370 '<td class="print"><strong>Default</strong></td></tr>' .
372 $this->object->getTableDefStandIn('database', 'view'),
376 public function testGetTableDef(): void
378 $this->object = $this->getMockBuilder(ExportHtmlword
::class)
379 ->onlyMethods(['formatOneColumnDefinition'])
380 ->setConstructorArgs([new Relation($this->dbi
), new Export($this->dbi
), new Transformations()])
383 $keys = [['Non_unique' => 0, 'Column_name' => 'name1'], ['Non_unique' => 1, 'Column_name' => 'name2']];
387 $resultStub = self
::createMock(DummyResult
::class);
389 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
390 ->disableOriginalConstructor()
393 $dbi->expects(self
::exactly(2))
394 ->method('fetchResult')
397 ['fieldname' => ['values' => 'test-', 'transformation' => 'testfoo', 'mimetype' => 'test<']],
400 $dbi->expects(self
::once())
401 ->method('getTableIndexes')
402 ->with('database', '')
405 $column = new Column('fieldname', '', null, false, '', null, '', '', '');
406 $dbi->expects(self
::once())
407 ->method('getColumns')
408 ->with('database', '')
409 ->willReturn([$column]);
411 $dbi->expects(self
::once())
412 ->method('tryQueryAsControlUser')
413 ->willReturn($resultStub);
415 $resultStub->expects(self
::once())
419 $resultStub->expects(self
::once())
420 ->method('fetchAssoc')
421 ->willReturn(['comment' => 'testComment']);
423 DatabaseInterface
::$instance = $dbi;
424 $this->object->relation
= new Relation($dbi);
426 $this->object->expects(self
::exactly(3))
427 ->method('formatOneColumnDefinition')
428 ->with($column, ['name1'])
431 $relationParameters = RelationParameters
::fromArray([
437 'column_info' => 'col',
439 (new ReflectionProperty(Relation
::class, 'cache'))->setValue(null, $relationParameters);
441 $request = ServerRequestFactory
::create()->createServerRequest('POST', 'https://example.com/')
442 ->withParsedBody(['htmlword_relation' => 'On', 'htmlword_mime' => 'On', 'htmlword_comments' => 'On']);
444 $this->object->setExportOptions($request, []);
446 $result = $this->object->getTableDef('database', '');
449 '<table width="100%" cellspacing="1">' .
450 '<tr class="print-category"><th class="print">Column</th>' .
451 '<td class="print"><strong>Type</strong></td>' .
452 '<td class="print"><strong>Null</strong></td>' .
453 '<td class="print"><strong>Default</strong></td>' .
454 '<td class="print"><strong>Comments</strong></td>' .
455 '<td class="print"><strong>Media type</strong></td></tr>' .
456 '1<td class="print"></td><td class="print">Test<</td></tr></table>',
462 $resultStub = self
::createMock(DummyResult
::class);
464 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
465 ->disableOriginalConstructor()
468 $dbi->expects(self
::exactly(2))
469 ->method('fetchResult')
471 ['fieldname' => ['foreign_table' => 'ftable', 'foreign_field' => 'ffield']],
472 ['field' => ['values' => 'test-', 'transformation' => 'testfoo', 'mimetype' => 'test<']],
475 $dbi->expects(self
::once())
476 ->method('getTableIndexes')
477 ->with('database', '')
480 $column = new Column('fieldname', '', null, false, '', null, '', '', '');
482 $dbi->expects(self
::once())
483 ->method('getColumns')
484 ->with('database', '')
485 ->willReturn([$column]);
487 $dbi->expects(self
::once())
488 ->method('tryQueryAsControlUser')
489 ->willReturn($resultStub);
491 $resultStub->expects(self
::once())
495 $resultStub->expects(self
::once())
496 ->method('fetchAssoc')
497 ->willReturn(['comment' => 'testComment']);
499 DatabaseInterface
::$instance = $dbi;
500 $this->object->relation
= new Relation($dbi);
502 $relationParameters = RelationParameters
::fromArray([
508 'column_info' => 'col',
510 (new ReflectionProperty(Relation
::class, 'cache'))->setValue(null, $relationParameters);
512 $result = $this->object->getTableDef('database', '');
514 self
::assertStringContainsString('<td class="print">ftable (ffield)</td>', $result);
516 self
::assertStringContainsString('<td class="print"></td><td class="print"></td>', $result);
520 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
521 ->disableOriginalConstructor()
524 $dbi->expects(self
::once())
525 ->method('getTableIndexes')
526 ->with('database', '')
529 $column = new Column('fieldname', '', null, false, '', null, '', '', '');
531 $dbi->expects(self
::once())
532 ->method('getColumns')
533 ->with('database', '')
534 ->willReturn([$column]);
536 $dbi->expects(self
::never())
537 ->method('tryQuery');
539 DatabaseInterface
::$instance = $dbi;
541 $relationParameters = RelationParameters
::fromArray([
544 'column_info' => 'col',
546 (new ReflectionProperty(Relation
::class, 'cache'))->setValue(null, $relationParameters);
548 $request = ServerRequestFactory
::create()->createServerRequest('POST', 'https://example.com/')
549 ->withParsedBody(['htmlword_relation' => 'On', 'htmlword_mime' => 'On']);
551 $this->object->setExportOptions($request, []);
553 $result = $this->object->getTableDef('database', '');
556 '<table width="100%" cellspacing="1">' .
557 '<tr class="print-category"><th class="print">Column</th>' .
558 '<td class="print"><strong>Type</strong></td>' .
559 '<td class="print"><strong>Null</strong></td>' .
560 '<td class="print"><strong>Default</strong></td></tr>1</tr></table>',
565 public function testGetTriggers(): void
569 TriggerName
::from('tna"me'),
572 TableName
::from('table'),
574 'test_user@localhost',
578 $method = new ReflectionMethod(ExportHtmlword
::class, 'getTriggers');
579 $result = $method->invoke($this->object, $triggers);
581 self
::assertStringContainsString(
582 '<td class="print">tna"me</td>' .
583 '<td class="print">BEFORE</td>' .
584 '<td class="print">UPDATE</td>' .
585 '<td class="print">def</td>',
590 public function testExportStructure(): void
593 $this->dummyDbi
->addSelectDb('test_db');
594 self
::assertTrue($this->object->exportStructure('test_db', 'test_table', 'create_table'));
595 $this->dummyDbi
->assertAllSelectsConsumed();
596 $result = ob_get_clean();
599 '<h2>Table structure for table test_table</h2>'
600 . '<table width="100%" cellspacing="1"><tr class="print-category">'
601 . '<th class="print">Column</th><td class="print"><strong>Type</strong></td>'
602 . '<td class="print"><strong>Null</strong></td><td class="print"><strong>Default</strong></td></tr>'
603 . '<tr class="print-category"><td class="print"><em><strong>id</strong></em></td>'
604 . '<td class="print">int(11)</td><td class="print">No</td><td class="print">NULL</td></tr>'
605 . '<tr class="print-category"><td class="print">name</td><td class="print">varchar(20)</td>'
606 . '<td class="print">No</td><td class="print">NULL</td></tr><tr class="print-category">'
607 . '<td class="print">datetimefield</td><td class="print">datetime</td>'
608 . '<td class="print">No</td><td class="print">NULL</td></tr></table>',
613 self
::assertTrue($this->object->exportStructure('test_db', 'test_table', 'triggers'));
614 $result = ob_get_clean();
617 '<h2>Triggers test_table</h2><table width="100%" cellspacing="1">'
618 . '<tr class="print-category"><th class="print">Name</th>'
619 . '<td class="print"><strong>Time</strong></td><td class="print"><strong>Event</strong></td>'
620 . '<td class="print"><strong>Definition</strong></td></tr><tr class="print-category">'
621 . '<td class="print">test_trigger</td><td class="print">AFTER</td>'
622 . '<td class="print">INSERT</td><td class="print">BEGIN END</td></tr></table>',
627 $this->dummyDbi
->addSelectDb('test_db');
628 self
::assertTrue($this->object->exportStructure('test_db', 'test_table', 'create_view'));
629 $this->dummyDbi
->assertAllSelectsConsumed();
630 $result = ob_get_clean();
633 '<h2>Structure for view test_table</h2>'
634 . '<table width="100%" cellspacing="1"><tr class="print-category">'
635 . '<th class="print">Column</th><td class="print"><strong>Type</strong></td>'
636 . '<td class="print"><strong>Null</strong></td><td class="print"><strong>Default</strong>'
637 . '</td></tr><tr class="print-category"><td class="print"><em><strong>id</strong></em></td>'
638 . '<td class="print">int(11)</td><td class="print">No</td><td class="print">NULL</td></tr>'
639 . '<tr class="print-category"><td class="print">name</td><td class="print">varchar(20)</td>'
640 . '<td class="print">No</td><td class="print">NULL</td></tr><tr class="print-category">'
641 . '<td class="print">datetimefield</td><td class="print">datetime</td>'
642 . '<td class="print">No</td><td class="print">NULL</td></tr></table>',
647 self
::assertTrue($this->object->exportStructure('test_db', 'test_table', 'stand_in'));
648 $result = ob_get_clean();
651 '<h2>Stand-in structure for view test_table</h2>'
652 . '<table width="100%" cellspacing="1"><tr class="print-category">'
653 . '<th class="print">Column</th><td class="print"><strong>Type</strong></td>'
654 . '<td class="print"><strong>Null</strong></td><td class="print"><strong>Default</strong></td>'
655 . '</tr><tr class="print-category">'
656 . '<td class="print"><em><strong>id</strong></em></td><td class="print">int(11)</td>'
657 . '<td class="print">No</td><td class="print">NULL</td></tr><tr class="print-category">'
658 . '<td class="print">name</td><td class="print">varchar(20)</td><td class="print">No</td>'
659 . '<td class="print">NULL</td></tr><tr class="print-category">'
660 . '<td class="print">datetimefield</td><td class="print">datetime</td>'
661 . '<td class="print">No</td><td class="print">NULL</td></tr></table>',
666 public function testFormatOneColumnDefinition(): void
668 $method = new ReflectionMethod(ExportHtmlword
::class, 'formatOneColumnDefinition');
670 $column = new Column('field', 'set(abc)enum123', null, true, 'PRI', null, '', '', '');
672 $uniqueKeys = ['field'];
675 '<tr class="print-category"><td class="print"><em>' .
676 '<strong>field</strong></em></td><td class="print">set(abc)</td>' .
677 '<td class="print">Yes</td><td class="print">NULL</td>',
678 $method->invoke($this->object, $column, $uniqueKeys),
681 $column = new Column('fields', '', null, false, 'COMP', 'def', '', '', '');
683 $uniqueKeys = ['field'];
686 '<tr class="print-category"><td class="print">fields</td>' .
687 '<td class="print">&nbsp;</td><td class="print">No</td>' .
688 '<td class="print">def</td>',
689 $method->invoke($this->object, $column, $uniqueKeys),