WorksheetTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
  3. use Exception;
  4. use PhpOffice\PhpSpreadsheet\Cell\DataType;
  5. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  6. use PhpOffice\PhpSpreadsheet\Worksheet\CellIterator;
  7. use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  8. use PHPUnit\Framework\TestCase;
  9. class WorksheetTest extends TestCase
  10. {
  11. public function testSetTitle(): void
  12. {
  13. $testTitle = str_repeat('a', 31);
  14. $worksheet = new Worksheet();
  15. $worksheet->setTitle($testTitle);
  16. self::assertSame($testTitle, $worksheet->getTitle());
  17. }
  18. public function setTitleInvalidProvider(): array
  19. {
  20. return [
  21. [str_repeat('a', 32), 'Maximum 31 characters allowed in sheet title.'],
  22. ['invalid*title', 'Invalid character found in sheet title'],
  23. ];
  24. }
  25. /**
  26. * @param string $title
  27. * @param string $expectMessage
  28. *
  29. * @dataProvider setTitleInvalidProvider
  30. */
  31. public function testSetTitleInvalid($title, $expectMessage): void
  32. {
  33. // First, test setting title with validation disabled -- should be successful
  34. $worksheet = new Worksheet();
  35. $worksheet->setTitle($title, true, false);
  36. // Next, test again with validation enabled -- this time we should fail
  37. $worksheet = new Worksheet();
  38. $this->expectException(Exception::class);
  39. $this->expectExceptionMessage($expectMessage);
  40. $worksheet->setTitle($title);
  41. }
  42. public function testSetTitleDuplicate(): void
  43. {
  44. // Create a Spreadsheet with three Worksheets (the first is created automatically)
  45. $spreadsheet = new Spreadsheet();
  46. $spreadsheet->createSheet();
  47. $spreadsheet->createSheet();
  48. // Set unique title -- should be unchanged
  49. $sheet = $spreadsheet->getSheet(0);
  50. $sheet->setTitle('Test Title');
  51. self::assertSame('Test Title', $sheet->getTitle());
  52. // Set duplicate title -- should have numeric suffix appended
  53. $sheet = $spreadsheet->getSheet(1);
  54. $sheet->setTitle('Test Title');
  55. self::assertSame('Test Title 1', $sheet->getTitle());
  56. // Set duplicate title with validation disabled -- should be unchanged
  57. $sheet = $spreadsheet->getSheet(2);
  58. $sheet->setTitle('Test Title', true, false);
  59. self::assertSame('Test Title', $sheet->getTitle());
  60. }
  61. public function testSetCodeName(): void
  62. {
  63. $testCodeName = str_repeat('a', 31);
  64. $worksheet = new Worksheet();
  65. $worksheet->setCodeName($testCodeName);
  66. self::assertSame($testCodeName, $worksheet->getCodeName());
  67. }
  68. public function setCodeNameInvalidProvider(): array
  69. {
  70. return [
  71. [str_repeat('a', 32), 'Maximum 31 characters allowed in sheet code name.'],
  72. ['invalid*code*name', 'Invalid character found in sheet code name'],
  73. ];
  74. }
  75. /**
  76. * @param string $codeName
  77. * @param string $expectMessage
  78. *
  79. * @dataProvider setCodeNameInvalidProvider
  80. */
  81. public function testSetCodeNameInvalid($codeName, $expectMessage): void
  82. {
  83. // First, test setting code name with validation disabled -- should be successful
  84. $worksheet = new Worksheet();
  85. $worksheet->setCodeName($codeName, false);
  86. // Next, test again with validation enabled -- this time we should fail
  87. $worksheet = new Worksheet();
  88. $this->expectException(Exception::class);
  89. $this->expectExceptionMessage($expectMessage);
  90. $worksheet->setCodeName($codeName);
  91. }
  92. public function testSetCodeNameDuplicate(): void
  93. {
  94. // Create a Spreadsheet with three Worksheets (the first is created automatically)
  95. $spreadsheet = new Spreadsheet();
  96. $spreadsheet->createSheet();
  97. $spreadsheet->createSheet();
  98. // Set unique code name -- should be massaged to Snake_Case
  99. $sheet = $spreadsheet->getSheet(0);
  100. $sheet->setCodeName('Test Code Name');
  101. self::assertSame('Test_Code_Name', $sheet->getCodeName());
  102. // Set duplicate code name -- should be massaged and have numeric suffix appended
  103. $sheet = $spreadsheet->getSheet(1);
  104. $sheet->setCodeName('Test Code Name');
  105. self::assertSame('Test_Code_Name_1', $sheet->getCodeName());
  106. // Set duplicate code name with validation disabled -- should be unchanged, and unmassaged
  107. $sheet = $spreadsheet->getSheet(2);
  108. $sheet->setCodeName('Test Code Name', false);
  109. self::assertSame('Test Code Name', $sheet->getCodeName());
  110. }
  111. public function testFreezePaneSelectedCell(): void
  112. {
  113. $worksheet = new Worksheet();
  114. $worksheet->freezePane('B2');
  115. self::assertSame('B2', $worksheet->getTopLeftCell());
  116. }
  117. public function extractSheetTitleProvider(): array
  118. {
  119. return [
  120. ['B2', '', '', 'B2'],
  121. ['testTitle!B2', 'testTitle', 'B2', 'B2'],
  122. ['test!Title!B2', 'test!Title', 'B2', 'B2'],
  123. ['test Title!B2', 'test Title', 'B2', 'B2'],
  124. ['test!Title!B2', 'test!Title', 'B2', 'B2'],
  125. ["'testSheet 1'!A3", "'testSheet 1'", 'A3', 'A3'],
  126. ["'testSheet1'!A2", "'testSheet1'", 'A2', 'A2'],
  127. ["'testSheet 2'!A1", "'testSheet 2'", 'A1', 'A1'],
  128. ];
  129. }
  130. /**
  131. * @param string $range
  132. * @param string $expectTitle
  133. * @param string $expectCell
  134. * @param string $expectCell2
  135. *
  136. * @dataProvider extractSheetTitleProvider
  137. */
  138. public function testExtractSheetTitle($range, $expectTitle, $expectCell, $expectCell2): void
  139. {
  140. // only cell reference
  141. self::assertSame($expectCell, Worksheet::extractSheetTitle($range));
  142. // with title in array
  143. $arRange = Worksheet::extractSheetTitle($range, true);
  144. self::assertSame($expectTitle, $arRange[0]);
  145. self::assertSame($expectCell2, $arRange[1]);
  146. }
  147. /**
  148. * Fix https://github.com/PHPOffice/PhpSpreadsheet/issues/868 when cells are not removed correctly
  149. * on row deletion.
  150. */
  151. public function testRemoveCellsCorrectlyWhenRemovingRow(): void
  152. {
  153. $workbook = new Spreadsheet();
  154. $worksheet = $workbook->getActiveSheet();
  155. $worksheet->getCell('A2')->setValue('A2');
  156. $worksheet->getCell('C1')->setValue('C1');
  157. $worksheet->removeRow(1);
  158. self::assertEquals(
  159. 'A2',
  160. $worksheet->getCell('A1')->getValue()
  161. );
  162. self::assertNull(
  163. $worksheet->getCell('C1')->getValue()
  164. );
  165. }
  166. public function removeColumnProvider(): array
  167. {
  168. return [
  169. 'Remove first column' => [
  170. [
  171. ['A1', 'B1', 'C1'],
  172. ['A2', 'B2', 'C2'],
  173. ],
  174. 'A',
  175. 1,
  176. [
  177. ['B1', 'C1'],
  178. ['B2', 'C2'],
  179. ],
  180. 'B',
  181. ],
  182. 'Remove middle column' => [
  183. [
  184. ['A1', 'B1', 'C1'],
  185. ['A2', 'B2', 'C2'],
  186. ],
  187. 'B',
  188. 1,
  189. [
  190. ['A1', 'C1'],
  191. ['A2', 'C2'],
  192. ],
  193. 'B',
  194. ],
  195. 'Remove last column' => [
  196. [
  197. ['A1', 'B1', 'C1'],
  198. ['A2', 'B2', 'C2'],
  199. ],
  200. 'C',
  201. 1,
  202. [
  203. ['A1', 'B1'],
  204. ['A2', 'B2'],
  205. ],
  206. 'B',
  207. ],
  208. 'Remove a column out of range' => [
  209. [
  210. ['A1', 'B1', 'C1'],
  211. ['A2', 'B2', 'C2'],
  212. ],
  213. 'D',
  214. 1,
  215. [
  216. ['A1', 'B1', 'C1'],
  217. ['A2', 'B2', 'C2'],
  218. ],
  219. 'C',
  220. ],
  221. 'Remove multiple columns' => [
  222. [
  223. ['A1', 'B1', 'C1'],
  224. ['A2', 'B2', 'C2'],
  225. ],
  226. 'B',
  227. 5,
  228. [
  229. ['A1'],
  230. ['A2'],
  231. ],
  232. 'A',
  233. ],
  234. ];
  235. }
  236. /**
  237. * @dataProvider removeColumnProvider
  238. */
  239. public function testRemoveColumn(
  240. array $initialData,
  241. string $columnToBeRemoved,
  242. int $columnsToBeRemoved,
  243. array $expectedData,
  244. string $expectedHighestColumn
  245. ): void {
  246. $spreadsheet = new Spreadsheet();
  247. $worksheet = $spreadsheet->getActiveSheet();
  248. $worksheet->fromArray($initialData);
  249. $worksheet->removeColumn($columnToBeRemoved, $columnsToBeRemoved);
  250. self::assertSame($expectedHighestColumn, $worksheet->getHighestColumn());
  251. self::assertSame($expectedData, $worksheet->toArray());
  252. }
  253. public function removeRowsProvider(): array
  254. {
  255. return [
  256. 'Remove all rows except first one' => [
  257. [
  258. ['A1', 'B1', 'C1'],
  259. ['A2', 'B2', 'C2'],
  260. ['A3', 'B3', 'C3'],
  261. ['A4', 'B4', 'C4'],
  262. ],
  263. 2,
  264. 3,
  265. [
  266. ['A1', 'B1', 'C1'],
  267. ],
  268. 1,
  269. ],
  270. 'Remove all rows except last one' => [
  271. [
  272. ['A1', 'B1', 'C1'],
  273. ['A2', 'B2', 'C2'],
  274. ['A3', 'B3', 'C3'],
  275. ['A4', 'B4', 'C4'],
  276. ],
  277. 1,
  278. 3,
  279. [
  280. ['A4', 'B4', 'C4'],
  281. ],
  282. 1,
  283. ],
  284. 'Remove last row' => [
  285. [
  286. ['A1', 'B1', 'C1'],
  287. ['A2', 'B2', 'C2'],
  288. ['A3', 'B3', 'C3'],
  289. ['A4', 'B4', 'C4'],
  290. ],
  291. 4,
  292. 1,
  293. [
  294. ['A1', 'B1', 'C1'],
  295. ['A2', 'B2', 'C2'],
  296. ['A3', 'B3', 'C3'],
  297. ],
  298. 3,
  299. ],
  300. 'Remove first row' => [
  301. [
  302. ['A1', 'B1', 'C1'],
  303. ['A2', 'B2', 'C2'],
  304. ['A3', 'B3', 'C3'],
  305. ['A4', 'B4', 'C4'],
  306. ],
  307. 1,
  308. 1,
  309. [
  310. ['A2', 'B2', 'C2'],
  311. ['A3', 'B3', 'C3'],
  312. ['A4', 'B4', 'C4'],
  313. ],
  314. 3,
  315. ],
  316. 'Remove all rows except first and last' => [
  317. [
  318. ['A1', 'B1', 'C1'],
  319. ['A2', 'B2', 'C2'],
  320. ['A3', 'B3', 'C3'],
  321. ['A4', 'B4', 'C4'],
  322. ],
  323. 2,
  324. 2,
  325. [
  326. ['A1', 'B1', 'C1'],
  327. ['A4', 'B4', 'C4'],
  328. ],
  329. 2,
  330. ],
  331. 'Remove non existing rows' => [
  332. [
  333. ['A1', 'B1', 'C1'],
  334. ['A2', 'B2', 'C2'],
  335. ['A3', 'B3', 'C3'],
  336. ['A4', 'B4', 'C4'],
  337. ],
  338. 2,
  339. 10,
  340. [
  341. ['A1', 'B1', 'C1'],
  342. ],
  343. 1,
  344. ],
  345. 'Remove only non existing rows' => [
  346. [
  347. ['A1', 'B1', 'C1'],
  348. ['A2', 'B2', 'C2'],
  349. ['A3', 'B3', 'C3'],
  350. ['A4', 'B4', 'C4'],
  351. ],
  352. 5,
  353. 10,
  354. [
  355. ['A1', 'B1', 'C1'],
  356. ['A2', 'B2', 'C2'],
  357. ['A3', 'B3', 'C3'],
  358. ['A4', 'B4', 'C4'],
  359. ],
  360. 4,
  361. ],
  362. ];
  363. }
  364. /**
  365. * @dataProvider removeRowsProvider
  366. */
  367. public function testRemoveRows(
  368. array $initialData,
  369. int $rowToRemove,
  370. int $rowsQtyToRemove,
  371. array $expectedData,
  372. int $expectedHighestRow
  373. ): void {
  374. $workbook = new Spreadsheet();
  375. $worksheet = $workbook->getActiveSheet();
  376. $worksheet->fromArray($initialData);
  377. $worksheet->removeRow($rowToRemove, $rowsQtyToRemove);
  378. self::assertSame($expectedData, $worksheet->toArray());
  379. self::assertSame($expectedHighestRow, $worksheet->getHighestRow());
  380. }
  381. private static function getPopulatedSheetForEmptyRowTest(Spreadsheet $spreadsheet): Worksheet
  382. {
  383. $sheet = $spreadsheet->getActiveSheet();
  384. $sheet->setCellValueExplicit('A1', 'Hello World', DataType::TYPE_STRING);
  385. $sheet->setCellValueExplicit('B3', null, DataType::TYPE_NULL);
  386. $sheet->setCellValueExplicit('B4', '', DataType::TYPE_STRING);
  387. $sheet->setCellValueExplicit('B5', null, DataType::TYPE_NULL);
  388. $sheet->setCellValueExplicit('C5', '', DataType::TYPE_STRING);
  389. $sheet->setCellValueExplicit('B6', null, DataType::TYPE_NULL);
  390. $sheet->setCellValueExplicit('C6', 'PHP', DataType::TYPE_STRING);
  391. $sheet->setCellValueExplicit('B7', '', DataType::TYPE_STRING);
  392. $sheet->setCellValueExplicit('C7', 'PHP', DataType::TYPE_STRING);
  393. $sheet->setCellValueExplicit('B8', null, DataType::TYPE_NULL);
  394. $sheet->setCellValueExplicit('C8', '', DataType::TYPE_STRING);
  395. $sheet->setCellValueExplicit('D8', 'PHP', DataType::TYPE_STRING);
  396. return $sheet;
  397. }
  398. private static function getPopulatedSheetForEmptyColumnTest(Spreadsheet $spreadsheet): Worksheet
  399. {
  400. $sheet = $spreadsheet->getActiveSheet();
  401. $sheet->setCellValueExplicit('A1', 'Hello World', DataType::TYPE_STRING);
  402. $sheet->setCellValueExplicit('C2', null, DataType::TYPE_NULL);
  403. $sheet->setCellValueExplicit('D2', '', DataType::TYPE_STRING);
  404. $sheet->setCellValueExplicit('E2', null, DataType::TYPE_NULL);
  405. $sheet->setCellValueExplicit('E3', '', DataType::TYPE_STRING);
  406. $sheet->setCellValueExplicit('F2', null, DataType::TYPE_NULL);
  407. $sheet->setCellValueExplicit('F3', 'PHP', DataType::TYPE_STRING);
  408. $sheet->setCellValueExplicit('G2', '', DataType::TYPE_STRING);
  409. $sheet->setCellValueExplicit('G3', 'PHP', DataType::TYPE_STRING);
  410. $sheet->setCellValueExplicit('H2', null, DataType::TYPE_NULL);
  411. $sheet->setCellValueExplicit('H3', '', DataType::TYPE_STRING);
  412. $sheet->setCellValueExplicit('H4', 'PHP', DataType::TYPE_STRING);
  413. return $sheet;
  414. }
  415. /**
  416. * @dataProvider emptyRowProvider
  417. */
  418. public function testIsEmptyRow(int $rowId, bool $expectedEmpty): void
  419. {
  420. $spreadsheet = new Spreadsheet();
  421. $sheet = self::getPopulatedSheetForEmptyRowTest($spreadsheet);
  422. $isEmpty = $sheet->isEmptyRow($rowId, CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL | CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
  423. self::assertSame($expectedEmpty, $isEmpty);
  424. $spreadsheet->disconnectWorksheets();
  425. }
  426. public function emptyRowProvider(): array
  427. {
  428. return [
  429. [1, false],
  430. [2, true],
  431. [3, true],
  432. [4, true],
  433. [5, true],
  434. [6, false],
  435. [7, false],
  436. [8, false],
  437. [9, true],
  438. ];
  439. }
  440. /**
  441. * @dataProvider emptyColumnProvider
  442. */
  443. public function testIsEmptyColumn(string $columnId, bool $expectedEmpty): void
  444. {
  445. $spreadsheet = new Spreadsheet();
  446. $sheet = self::getPopulatedSheetForEmptyColumnTest($spreadsheet);
  447. $isEmpty = $sheet->isEmptyColumn($columnId, CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL | CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
  448. self::assertSame($expectedEmpty, $isEmpty);
  449. $spreadsheet->disconnectWorksheets();
  450. }
  451. public function emptyColumnProvider(): array
  452. {
  453. return [
  454. ['A', false],
  455. ['B', true],
  456. ['C', true],
  457. ['D', true],
  458. ['E', true],
  459. ['F', false],
  460. ['G', false],
  461. ['H', false],
  462. ['I', true],
  463. ];
  464. }
  465. }