ODFTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <?php
  2. /* Copyright (C) 2007-2017 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2023 - Thomas Negre - contact@open-dsi.fr
  4. * Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * \file test/unit/ODFTest.php
  21. * \ingroup odf
  22. * \brief PHPUnit test for odf class.
  23. */
  24. global $conf,$user,$langs,$db;
  25. //define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver
  26. //require_once 'PHPUnit/Autoload.php';
  27. require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
  28. require_once dirname(__FILE__).'/../../htdocs/includes/odtphp/odf.php';
  29. if (empty($user->id)) {
  30. print "Load permissions for admin user nb 1\n";
  31. $user->fetch(1);
  32. $user->getrights();
  33. }
  34. $conf->global->MAIN_DISABLE_ALL_MAILS=1;
  35. $langs->load("main");
  36. /**
  37. * Class for PHPUnit tests
  38. *
  39. * @backupGlobals disabled
  40. * @backupStaticAttributes enabled
  41. * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
  42. */
  43. class ODFTest extends PHPUnit\Framework\TestCase
  44. {
  45. protected $savconf;
  46. protected $savuser;
  47. protected $savlangs;
  48. protected $savdb;
  49. /**
  50. * Constructor
  51. * We save global variables into local variables
  52. *
  53. * @param string $name Name
  54. * @return ODFTest
  55. */
  56. public function __construct($name = '')
  57. {
  58. parent::__construct($name);
  59. //$this->sharedFixture
  60. global $conf,$user,$langs,$db;
  61. $this->savconf=$conf;
  62. $this->savuser=$user;
  63. $this->savlangs=$langs;
  64. $this->savdb=$db;
  65. print __METHOD__." db->type=".$db->type." user->id=".$user->id;
  66. //print " - db ".$db->db;
  67. print "\n";
  68. }
  69. /**
  70. * setUpBeforeClass
  71. *
  72. * @return void
  73. */
  74. public static function setUpBeforeClass(): void
  75. {
  76. global $conf,$user,$langs,$db;
  77. $db->begin(); // This is to have all actions inside a transaction even if test launched without suite.
  78. print __METHOD__."\n";
  79. }
  80. /**
  81. * tearDownAfterClass
  82. *
  83. * @return void
  84. */
  85. public static function tearDownAfterClass(): void
  86. {
  87. global $conf,$user,$langs,$db;
  88. $db->rollback();
  89. print __METHOD__."\n";
  90. }
  91. /**
  92. * Init phpunit tests
  93. *
  94. * @return void
  95. */
  96. protected function setUp(): void
  97. {
  98. global $conf,$user,$langs,$db;
  99. $conf=$this->savconf;
  100. $user=$this->savuser;
  101. $langs=$this->savlangs;
  102. $db=$this->savdb;
  103. print __METHOD__."\n";
  104. }
  105. /**
  106. * End phpunit tests
  107. *
  108. * @return void
  109. */
  110. protected function tearDown(): void
  111. {
  112. print __METHOD__."\n";
  113. }
  114. /**
  115. * test ODF convertVarToOdf
  116. *
  117. * @return int
  118. */
  119. public function testODFconvertVarToOdf()
  120. {
  121. global $conf,$user,$langs,$db;
  122. $conf=$this->savconf;
  123. $user=$this->savuser;
  124. $langs=$this->savlangs;
  125. $db=$this->savdb;
  126. // we test using template_invoice, it does not matter, we just need a valid odt.
  127. $filename = '../../htdocs/install/doctemplates/invoices/template_invoice.odt';
  128. $config = [
  129. 'PATH_TO_TMP' => "/tmp",
  130. 'ZIP_PROXY' => "PclZipProxy",
  131. 'DELIMITER_LEFT' => "{",
  132. 'DELIMITER_RIGHT' => "}",
  133. ];
  134. $to_test = [
  135. /** No HTML **/
  136. // Simple strings
  137. 1 => [
  138. 'to_convert' => 'Simple string',
  139. 'encode' => true,
  140. 'charset' => null,
  141. 'expected' => 'Simple string',
  142. ],
  143. 2 => [
  144. 'to_convert' => 'Simple string',
  145. 'encode' => false,
  146. 'charset' => null,
  147. 'expected' => 'Simple string',
  148. ],
  149. 3 => [
  150. 'to_convert' => "Simple string\nwith line break",
  151. 'encode' => true,
  152. 'charset' => null,
  153. 'expected' => "Simple string<text:line-break/>with line break",
  154. ],
  155. 4 => [
  156. 'to_convert' => "Simple string\nwith line break",
  157. 'encode' => false,
  158. 'charset' => null,
  159. 'expected' => "Simple string<text:line-break/>with line break",
  160. ],
  161. // Special chars
  162. 5 => [
  163. 'to_convert' => 'One&two',
  164. 'encode' => true,
  165. 'charset' => null,
  166. 'expected' => 'One&amp;two',
  167. ],
  168. 6 => [
  169. 'to_convert' => 'One&two',
  170. 'encode' => false,
  171. 'charset' => null,
  172. 'expected' => 'One&two',
  173. ],
  174. 7 => [
  175. 'to_convert' => "/a&él'èàüöç€Ğ~<>",
  176. 'encode' => true,
  177. 'charset' => null,
  178. 'expected' => mb_convert_encoding("/a&amp;él&apos;èàüöç€Ğ~&lt;&gt;", 'UTF-8', 'ISO-8859-1'),
  179. ],
  180. 8 => [
  181. 'to_convert' => "/a&él'èàüöç€Ğ~<>",
  182. 'encode' => false,
  183. 'charset' => null,
  184. 'expected' => mb_convert_encoding("/a&él'èàüöç€Ğ~<>", 'UTF-8', 'ISO-8859-1'),
  185. ],
  186. // special chars with non-default charset
  187. 9 => [
  188. 'to_convert' => "/a&él'èàüöç€Ğ~<>",
  189. 'encode' => true,
  190. 'charset' => 'UTF-16',
  191. 'expected' => "/a&amp;él&apos;èàüöç€Ğ~&lt;&gt;",
  192. ],
  193. 10 => [
  194. 'to_convert' => "/a&él'èàüöç€Ğ~<>",
  195. 'encode' => false,
  196. 'charset' => 'UTF-16', // When the charset differs from ISO-8859 string is not converted.
  197. 'expected' => "/a&él'èàüöç€Ğ~<>",
  198. ],
  199. 11 => [
  200. 'to_convert' => "Greater > than",
  201. 'encode' => true,
  202. 'charset' => null,
  203. 'expected' => mb_convert_encoding("Greater &gt; than", 'UTF-8', 'ISO-8859-1'),
  204. ],
  205. 12 => [
  206. 'to_convert' => "Greater > than",
  207. 'encode' => false,
  208. 'charset' => null,
  209. 'expected' => mb_convert_encoding("Greater > than", 'UTF-8', 'ISO-8859-1'),
  210. ],
  211. 13 => [
  212. 'to_convert' => "Smaller < than",
  213. 'encode' => true,
  214. 'charset' => null,
  215. 'expected' => mb_convert_encoding("Smaller &lt; than", 'UTF-8', 'ISO-8859-1'),
  216. ],
  217. 14 => [
  218. 'to_convert' => "Smaller < than",
  219. 'encode' => false,
  220. 'charset' => null,
  221. 'expected' => mb_convert_encoding("Smaller < than", 'UTF-8', 'ISO-8859-1'),
  222. ],
  223. /** HTML **/
  224. // break lines
  225. 15 => [
  226. 'to_convert' => "Break<br>line",
  227. 'encode' => true,
  228. 'charset' => null,
  229. 'expected' => mb_convert_encoding("Break<text:line-break/>line", 'UTF-8', 'ISO-8859-1'),
  230. ],
  231. 16 => [
  232. 'to_convert' => "Break<br>line",
  233. 'encode' => false,
  234. 'charset' => null,
  235. 'expected' => mb_convert_encoding("Break<text:line-break/>line", 'UTF-8', 'ISO-8859-1'),
  236. ],
  237. 17 => [
  238. 'to_convert' => "Break<br />line",
  239. 'encode' => true,
  240. 'charset' => null,
  241. 'expected' => mb_convert_encoding("Break<text:line-break/>line", 'UTF-8', 'ISO-8859-1'),
  242. ],
  243. 18 => [
  244. 'to_convert' => "Break<br />line",
  245. 'encode' => false,
  246. 'charset' => null,
  247. 'expected' => mb_convert_encoding("Break<text:line-break/>line", 'UTF-8', 'ISO-8859-1'),
  248. ],
  249. // HTML tags
  250. 19 => [
  251. 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>",
  252. 'encode' => false,
  253. 'charset' => 'UTF-8',
  254. 'expected' => 'text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&ciàlchärs éè l\'</text:span>',
  255. ],
  256. 20 => [
  257. 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>",
  258. 'encode' => true,
  259. 'charset' => 'UTF-8',
  260. 'expected' => 'text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&amp;ciàlchärs éè l&apos;</text:span>',
  261. ],
  262. 21 => [
  263. 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>",
  264. 'encode' => false,
  265. 'charset' => null,
  266. 'expected' => mb_convert_encoding('text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&ciàlchärs éè l\'</text:span>', 'UTF-8', 'ISO-8859-1'),
  267. ],
  268. 22 => [
  269. 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>",
  270. 'encode' => true,
  271. 'charset' => null,
  272. 'expected' => mb_convert_encoding('text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&amp;ciàlchärs éè l&apos;</text:span>', 'UTF-8', 'ISO-8859-1'),
  273. ],
  274. 23 => [
  275. 'to_convert' => "text with <strong>intricated<u>tags</u></strong>",
  276. 'encode' => true,
  277. 'charset' => null,
  278. 'expected' => mb_convert_encoding('text with <text:span text:style-name="boldText">intricated<text:span text:style-name="underlineText">tags</text:span></text:span>', 'UTF-8', 'ISO-8859-1'),
  279. ],
  280. // One can also pass html-encoded string to the method
  281. 24 => [
  282. 'to_convert' => 'One&amp;two',
  283. 'encode' => true,
  284. 'charset' => null,
  285. 'expected' => 'One&amp;two',
  286. ],
  287. 25 => [
  288. 'to_convert' => "text with &lt;strong&gt;strong, &lt;/strong&gt;&lt;em&gt;emphasis&lt;/em&gt; and &lt;u&gt;underlined&lt;/u&gt; words with &lt;i&gt;it@lic sp&amp;ciàlchärs éè l'&lt;/i&gt;",
  289. 'encode' => false,
  290. 'charset' => 'UTF-8',
  291. 'expected' => 'text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&ciàlchärs éè l\'</text:span>',
  292. ],
  293. 26 => [
  294. 'to_convert' => "text with &lt;strong&gt;strong, &lt;/strong&gt;&lt;em&gt;emphasis&lt;/em&gt; and &lt;u&gt;underlined&lt;/u&gt; words with &lt;i&gt;it@lic sp&amp;ciàlchärs éè l'&lt;/i&gt;",
  295. 'encode' => true,
  296. 'charset' => 'UTF-8',
  297. 'expected' => 'text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&amp;ciàlchärs éè l&apos;</text:span>',
  298. ],
  299. 27 => [
  300. 'to_convert' => "text with &lt;strong&gt;strong, &lt;/strong&gt;&lt;em&gt;emphasis&lt;/em&gt; and &lt;u&gt;underlined&lt;/u&gt; words with &lt;i&gt;it@lic sp&amp;ciàlchärs éè l'&lt;/i&gt;",
  301. 'encode' => false,
  302. 'charset' => null,
  303. 'expected' => mb_convert_encoding('text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&ciàlchärs éè l\'</text:span>', 'UTF-8', 'ISO-8859-1'),
  304. ],
  305. 28 => [
  306. 'to_convert' => "text with &lt;strong&gt;strong, &lt;/strong&gt;&lt;em&gt;emphasis&lt;/em&gt; and &lt;u&gt;underlined&lt;/u&gt; words with &lt;i&gt;it@lic sp&amp;ciàlchärs éè l'&lt;/i&gt;",
  307. 'encode' => true,
  308. 'charset' => null,
  309. 'expected' => mb_convert_encoding('text with <text:span text:style-name="boldText">strong, </text:span><text:span text:style-name="italicText">emphasis</text:span> and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&amp;ciàlchärs éè l&apos;</text:span>', 'UTF-8', 'ISO-8859-1'),
  310. ],
  311. // // TODO custom styles are not tested for now : the custom style have a custom ID based on time. Not random, but hard to mock or predict. generated in _replaceHtmlWithOdtTag() case 'span'.
  312. // [
  313. // 'to_convert' => '123 <span style="color:#e74c3c">trucmachin > truc < troc > trac</span>bla bla',
  314. // 'encode' => true,
  315. // 'charset' => 'UTF-8',
  316. // 'expected' => "123 <text:span text:style-name="customStyle1668592427018">trucmachin &gt; truc &lt; troc &gt; trac</text:span>bla bla'",
  317. // ],
  318. /* Tests that can evolve */
  319. // Following tests reflect the current behavior. They may evolve if the method behavior changes.
  320. // The method removes hyperlinks and tags that are not dealt with.
  321. 29 => [
  322. 'to_convert' => '123 <a href="/test.php">trucmachin > truc < troc > trac</a>bla bla',
  323. 'encode' => true,
  324. 'charset' => null,
  325. 'expected' => "123 trucmachin &gt; truc &lt; troc &gt; tracbla bla",
  326. ],
  327. 30 => [
  328. 'to_convert' => '123 <h3>Title</h3> bla',
  329. 'encode' => true,
  330. 'charset' => null,
  331. 'expected' => "123 Title bla",
  332. ],
  333. // HTML should not take \n into account, but only <br />.
  334. 31 => [
  335. 'to_convert' => "text with <strong>strong text </strong>, a line\nbreak and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>",
  336. 'encode' => false,
  337. 'charset' => 'UTF-8',
  338. 'expected' => 'text with <text:span text:style-name="boldText">strong text </text:span>, a line'."\n".'break and <text:span text:style-name="underlineText">underlined</text:span> words with <text:span text:style-name="italicText">it@lic sp&ciàlchärs éè l\'</text:span>',
  339. ],
  340. ];
  341. $odf=new Odf($filename, array());
  342. if (is_object($odf)) {
  343. $result = 1;
  344. } // Just to test
  345. foreach ($to_test as $case) {
  346. if ($case['charset'] !== null) {
  347. $res = $odf->convertVarToOdf($case['to_convert'], $case['encode'], $case['charset']);
  348. } else {
  349. $res = $odf->convertVarToOdf($case['to_convert'], $case['encode']);
  350. }
  351. $this->assertEquals($res, $case['expected']);
  352. }
  353. print __METHOD__." result=".$result."\n";
  354. return $result;
  355. }
  356. }