Completed
Push — master ( d93a30...6a259d )
by Mark
41s queued 30s
created

TableTest::testUniqueTableNameOnBindToWorksheet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 12
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\Table;
4
5
use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
6
use PhpOffice\PhpSpreadsheet\Cell\CellRange;
7
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
8
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
9
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
10
use PhpOffice\PhpSpreadsheet\Worksheet\Table\Column;
11
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
12
13
class TableTest extends SetupTeardown
14
{
15
    private const INITIAL_RANGE = 'H2:O256';
16
17
    public function testToString(): void
18
    {
19
        $expectedResult = self::INITIAL_RANGE;
20
        $table = new Table(self::INITIAL_RANGE);
21
22
        //  magic __toString should return the active table range
23
        $result = (string) $table;
24
        self::assertEquals($expectedResult, $result);
25
    }
26
27
    /**
28
     * @dataProvider validTableNamesProvider
29
     */
30
    public function testValidTableNames(string $name, string $expected): void
31
    {
32
        $table = new Table(self::INITIAL_RANGE);
33
34
        $result = $table->setName($name);
35
        self::assertInstanceOf(Table::class, $result);
36
        self::assertEquals($expected, $table->getName());
37
    }
38
39
    public function validTableNamesProvider(): array
40
    {
41
        return [
42
            ['', ''],
43
            ['Table_1', 'Table_1'],
44
            ['_table_2', '_table_2'],
45
            ['\table_3', '\table_3'],
46
            ["	Table_4 \n", 'Table_4'],
47
            ['table.5', 'table.5'],
48
            ['தமிழ்', 'தமிழ்'], // UTF-8 letters with combined character
49
        ];
50
    }
51
52
    /**
53
     * @dataProvider invalidTableNamesProvider
54
     */
55
    public function testInvalidTableNames(string $name): void
56
    {
57
        $table = new Table(self::INITIAL_RANGE);
58
59
        $this->expectException(PhpSpreadsheetException::class);
60
61
        $table->setName($name);
62
    }
63
64
    public function invalidTableNamesProvider(): array
65
    {
66
        return [
67
            ['C'],
68
            ['c'],
69
            ['R'],
70
            ['r'],
71
            ['Z100'],
72
            ['Z$100'],
73
            ['R1C1'],
74
            ['R1C'],
75
            ['R11C11'],
76
            ['123'],
77
            ['=Table'],
78
            ['ிக'], // starting with UTF-8 combined character
79
            [bin2hex(random_bytes(255))], // random string with length greater than 255
80
        ];
81
    }
82
83
    public function testUniqueTableNameOnBindToWorksheet(): void
84
    {
85
        $this->expectException(PhpSpreadsheetException::class);
86
        $sheet = $this->getSheet();
87
88
        $table1 = new Table();
89
        $table1->setName('Table_1');
90
        $sheet->addTable($table1);
91
92
        $table2 = new Table();
93
        $table2->setName('tABlE_1'); // case insensitive
94
        $sheet->addTable($table2);
95
    }
96
97
    public function testUniqueTableNameOnNameChange(): void
98
    {
99
        $this->expectException(PhpSpreadsheetException::class);
100
        $sheet = $this->getSheet();
101
102
        $table1 = new Table();
103
        $table1->setName('Table_1');
104
        $sheet->addTable($table1);
105
106
        $table2 = new Table();
107
        $table2->setName('table_2'); // case insensitive
108
        $sheet->addTable($table2);
109
        $table2->setName('tAbLe_1');
110
    }
111
112
    public function testVariousSets(): void
113
    {
114
        $table = new Table(self::INITIAL_RANGE);
115
116
        $result = $table->setShowHeaderRow(false);
117
        self::assertInstanceOf(Table::class, $result);
118
        self::assertFalse($table->getShowHeaderRow());
119
120
        $result = $table->setShowTotalsRow(true);
121
        self::assertInstanceOf(Table::class, $result);
122
        self::assertTrue($table->getShowTotalsRow());
123
    }
124
125
    public function testGetWorksheet(): void
126
    {
127
        $sheet = $this->getSheet();
128
        $table = new Table(self::INITIAL_RANGE);
129
        $sheet->addTable($table);
130
        $result = $table->getWorksheet();
131
        self::assertSame($sheet, $result);
132
    }
133
134
    public function testSetWorksheet(): void
135
    {
136
        $table = new Table(self::INITIAL_RANGE);
137
        $spreadsheet = $this->getSpreadsheet();
138
        $sheet2 = $spreadsheet->createSheet();
139
        //  Setters return the instance to implement the fluent interface
140
        $result = $table->setWorksheet($sheet2);
141
        self::assertInstanceOf(Table::class, $result);
142
    }
143
144
    public function testGetRange(): void
145
    {
146
        $expectedResult = self::INITIAL_RANGE;
147
        $table = new Table(self::INITIAL_RANGE);
148
149
        //  Result should be the active table range
150
        $result = $table->getRange();
151
        self::assertEquals($expectedResult, $result);
152
    }
153
154
    /**
155
     * @dataProvider validTableRangeProvider
156
     *
157
     * @param AddressRange|array<int>|string $fullRange
158
     * @param string $fullRange
159
     */
160
    public function testSetRangeValidRange($fullRange, string $actualRange): void
161
    {
162
        $table = new Table(self::INITIAL_RANGE);
163
164
        $result = $table->setRange($fullRange);
165
        self::assertInstanceOf(Table::class, $result);
166
        self::assertEquals($actualRange, $table->getRange());
167
    }
168
169
    public function validTableRangeProvider(): array
170
    {
171
        $sheet = $this->getSheet();
172
        $title = $sheet->getTitle();
173
174
        return [
175
            ["$title!G1:J512", 'G1:J512'],
176
            ['K1:N20', 'K1:N20'],
177
            [[3, 5, 6, 8], 'C5:F8'],
178
            [new CellRange(new CellAddress('C5', $sheet), new CellAddress('F8', $sheet)), 'C5:F8'],
179
        ];
180
    }
181
182
    public function testClearRange(): void
183
    {
184
        $expectedResult = '';
185
        $table = new Table(self::INITIAL_RANGE);
186
187
        //  Setters return the instance to implement the fluent interface
188
        $result = $table->setRange('');
189
        self::assertInstanceOf(Table::class, $result);
190
191
        //  Result should be a clear range
192
        $result = $table->getRange();
193
        self::assertEquals($expectedResult, $result);
194
    }
195
196
    /**
197
     * @dataProvider invalidTableRangeProvider
198
     */
199
    public function testSetRangeInvalidRange(string $range): void
200
    {
201
        $this->expectException(PhpSpreadsheetException::class);
202
203
        new Table($range);
204
    }
205
206
    public function invalidTableRangeProvider(): array
207
    {
208
        return [
209
            ['A1'],
210
            ['A1:A1'],
211
            ['B1:A4'],
212
            ['A1:D1'],
213
            ['D1:A1'],
214
        ];
215
    }
216
217
    public function testGetColumnsEmpty(): void
218
    {
219
        //  There should be no columns yet defined
220
        $table = new Table(self::INITIAL_RANGE);
221
        $result = $table->getColumns();
222
        self::assertIsArray($result);
223
        self::assertCount(0, $result);
224
    }
225
226
    public function testGetColumnOffset(): void
227
    {
228
        $columnIndexes = [
229
            'H' => 0,
230
            'K' => 3,
231
            'M' => 5,
232
        ];
233
        $table = new Table(self::INITIAL_RANGE);
234
235
        //  If we request a specific column by its column ID, we should get an
236
        //    integer returned representing the column offset within the range
237
        foreach ($columnIndexes as $columnIndex => $columnOffset) {
238
            $result = $table->getColumnOffset($columnIndex);
239
            self::assertEquals($columnOffset, $result);
240
        }
241
    }
242
243
    public function testRemoveColumns(): void
244
    {
245
        $sheet = $this->getSheet();
246
        $sheet->fromArray(range('H', 'O'), null, 'H2');
247
        $table = new Table(self::INITIAL_RANGE);
248
        $table->getColumn('L')->setShowFilterButton(false);
249
        $sheet->addTable($table);
250
251
        $sheet->removeColumn('K', 2);
252
        $result = $table->getRange();
253
        self::assertEquals('H2:M256', $result);
254
255
        // Check that the prop that was set for column L is no longer set
256
        self::assertTrue($table->getColumn('L')->getShowFilterButton());
257
    }
258
259
    public function testRemoveRows(): void
260
    {
261
        $sheet = $this->getSheet();
262
        $sheet->fromArray(range('H', 'O'), null, 'H2');
263
        $table = new Table(self::INITIAL_RANGE);
264
        $sheet->addTable($table);
265
266
        $sheet->removeRow(42, 128);
267
        $result = $table->getRange();
268
        self::assertEquals('H2:O128', $result);
269
    }
270
271
    public function testInsertColumns(): void
272
    {
273
        $sheet = $this->getSheet();
274
        $sheet->fromArray(range('H', 'O'), null, 'H2');
275
        $table = new Table(self::INITIAL_RANGE);
276
        $table->getColumn('N')->setShowFilterButton(false);
277
        $sheet->addTable($table);
278
279
        $sheet->insertNewColumnBefore('N', 3);
280
        $result = $table->getRange();
281
        self::assertEquals('H2:R256', $result);
282
283
        // Check that column N no longer has a prop
284
        self::assertTrue($table->getColumn('N')->getShowFilterButton());
285
        // Check that the prop originally set in column N has been moved to column Q
286
        self::assertFalse($table->getColumn('Q')->getShowFilterButton());
287
    }
288
289
    public function testInsertRows(): void
290
    {
291
        $sheet = $this->getSheet();
292
        $sheet->fromArray(range('H', 'O'), null, 'H2');
293
        $table = new Table(self::INITIAL_RANGE);
294
        $sheet->addTable($table);
295
296
        $sheet->insertNewRowBefore(3, 4);
297
        $result = $table->getRange();
298
        self::assertEquals('H2:O260', $result);
299
    }
300
301
    public function testGetInvalidColumnOffset(): void
302
    {
303
        $this->expectException(PhpSpreadsheetException::class);
304
305
        $invalidColumn = 'G';
306
        $sheet = $this->getSheet();
307
        $table = new Table();
308
        $table->setWorksheet($sheet);
309
310
        $table->getColumnOffset($invalidColumn);
311
    }
312
313
    public function testSetColumnWithString(): void
314
    {
315
        $expectedResult = 'L';
316
        $table = new Table(self::INITIAL_RANGE);
317
318
        //  Setters return the instance to implement the fluent interface
319
        $result = $table->setColumn($expectedResult);
320
        self::assertInstanceOf(Table::class, $result);
321
322
        $result = $table->getColumns();
323
        //  Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column
324
        //    objects for each column we set indexed by the column ID
325
        self::assertIsArray($result);
326
        self::assertCount(1, $result);
327
        self::assertArrayHasKey($expectedResult, $result);
328
        self::assertInstanceOf(Column::class, $result[$expectedResult]);
329
    }
330
331
    public function testSetInvalidColumnWithString(): void
332
    {
333
        $this->expectException(PhpSpreadsheetException::class);
334
        $table = new Table(self::INITIAL_RANGE);
335
336
        $invalidColumn = 'A';
337
        $table->setColumn($invalidColumn);
338
    }
339
340
    public function testSetColumnWithColumnObject(): void
341
    {
342
        $expectedResult = 'M';
343
        $columnObject = new Column($expectedResult);
344
        $table = new Table(self::INITIAL_RANGE);
345
346
        //  Setters return the instance to implement the fluent interface
347
        $result = $table->setColumn($columnObject);
348
        self::assertInstanceOf(Table::class, $result);
349
350
        $result = $table->getColumns();
351
        //  Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column
352
        //    objects for each column we set indexed by the column ID
353
        self::assertIsArray($result);
354
        self::assertCount(1, $result);
355
        self::assertArrayHasKey($expectedResult, $result);
356
        self::assertInstanceOf(Column::class, $result[$expectedResult]);
357
    }
358
359
    public function testSetInvalidColumnWithObject(): void
360
    {
361
        $this->expectException(PhpSpreadsheetException::class);
362
363
        $invalidColumn = 'E';
364
        $table = new Table(self::INITIAL_RANGE);
365
        $table->setColumn($invalidColumn);
366
    }
367
368
    public function testSetColumnWithInvalidDataType(): void
369
    {
370
        $this->expectException(PhpSpreadsheetException::class);
371
372
        $table = new Table(self::INITIAL_RANGE);
373
        $invalidColumn = 123.456;
374
        // @phpstan-ignore-next-line
375
        $table->setColumn($invalidColumn);
376
    }
377
378
    public function testGetColumns(): void
379
    {
380
        $table = new Table(self::INITIAL_RANGE);
381
382
        $columnIndexes = ['L', 'M'];
383
384
        foreach ($columnIndexes as $columnIndex) {
385
            $table->setColumn($columnIndex);
386
        }
387
388
        $result = $table->getColumns();
389
        //  Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column
390
        //    objects for each column we set indexed by the column ID
391
        self::assertIsArray($result);
392
        self::assertCount(count($columnIndexes), $result);
393
        foreach ($columnIndexes as $columnIndex) {
394
            self::assertArrayHasKey($columnIndex, $result);
395
            self::assertInstanceOf(Column::class, $result[$columnIndex]);
396
        }
397
398
        $table->setRange('');
399
        self::assertCount(0, $table->getColumns());
400
        self::assertSame('', $table->getRange());
401
    }
402
403
    public function testGetColumn(): void
404
    {
405
        $table = new Table(self::INITIAL_RANGE);
406
407
        $columnIndexes = ['L', 'M'];
408
409
        foreach ($columnIndexes as $columnIndex) {
410
            $table->setColumn($columnIndex);
411
        }
412
413
        //  If we request a specific column by its column ID, we should
414
        //    get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column object returned
415
        foreach ($columnIndexes as $columnIndex) {
416
            $result = $table->getColumn($columnIndex);
417
            self::assertInstanceOf(Column::class, $result);
418
        }
419
    }
420
421
    public function testGetColumnByOffset(): void
422
    {
423
        $table = new Table(self::INITIAL_RANGE);
424
425
        $columnIndexes = [
426
            0 => 'H',
427
            3 => 'K',
428
            5 => 'M',
429
        ];
430
431
        //  If we request a specific column by its offset, we should
432
        //    get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column object returned
433
        foreach ($columnIndexes as $columnIndex => $columnID) {
434
            $result = $table->getColumnByOffset($columnIndex);
435
            self::assertInstanceOf(Column::class, $result);
436
            self::assertEquals($result->getColumnIndex(), $columnID);
437
        }
438
    }
439
440
    public function testGetColumnIfNotSet(): void
441
    {
442
        $table = new Table(self::INITIAL_RANGE);
443
        //  If we request a specific column by its column ID, we should
444
        //    get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column object returned
445
        $result = $table->getColumn('K');
446
        self::assertInstanceOf(Column::class, $result);
447
    }
448
449
    public function testGetColumnWithoutRangeSet(): void
450
    {
451
        $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
452
        $table = new Table(self::INITIAL_RANGE);
453
454
        //  Clear the range
455
        $table->setRange('');
456
        $table->getColumn('A');
457
    }
458
459
    public function testClearRangeWithExistingColumns(): void
460
    {
461
        $table = new Table(self::INITIAL_RANGE);
462
        $expectedResult = '';
463
464
        $columnIndexes = ['L', 'M', 'N'];
465
        foreach ($columnIndexes as $columnIndex) {
466
            $table->setColumn($columnIndex);
467
        }
468
469
        //  Setters return the instance to implement the fluent interface
470
        $result = $table->setRange('');
471
        self::assertInstanceOf(Table::class, $result);
472
473
        //  Range should be cleared
474
        $result = $table->getRange();
475
        self::assertEquals($expectedResult, $result);
476
477
        //  Column array should be cleared
478
        $result = $table->getColumns();
479
        self::assertIsArray($result);
480
        self::assertCount(0, $result);
481
    }
482
483
    public function testSetRangeWithExistingColumns(): void
484
    {
485
        $table = new Table(self::INITIAL_RANGE);
486
        $expectedResult = 'G1:J512';
487
488
        //  These columns should be retained
489
        $columnIndexes1 = ['I', 'J'];
490
        foreach ($columnIndexes1 as $columnIndex) {
491
            $table->setColumn($columnIndex);
492
        }
493
        //  These columns should be discarded
494
        $columnIndexes2 = ['K', 'L', 'M'];
495
        foreach ($columnIndexes2 as $columnIndex) {
496
            $table->setColumn($columnIndex);
497
        }
498
499
        //  Setters return the instance to implement the fluent interface
500
        $result = $table->setRange($expectedResult);
501
        self::assertInstanceOf(Table::class, $result);
502
503
        //  Range should be correctly set
504
        $result = $table->getRange();
505
        self::assertEquals($expectedResult, $result);
506
507
        //  Only columns that existed in the original range and that
508
        //    still fall within the new range should be retained
509
        $result = $table->getColumns();
510
        self::assertIsArray($result);
511
        self::assertCount(count($columnIndexes1), $result);
512
    }
513
514
    public function testClone(): void
515
    {
516
        $sheet = $this->getSheet();
517
        $table = new Table(self::INITIAL_RANGE);
518
        $sheet->addTable($table);
519
        $columnIndexes = ['L', 'M'];
520
521
        foreach ($columnIndexes as $columnIndex) {
522
            $table->setColumn($columnIndex);
523
        }
524
525
        $result = clone $table;
526
        self::assertInstanceOf(Table::class, $result);
527
        self::assertSame($table->getRange(), $result->getRange());
528
        self::assertNull($result->getWorksheet());
529
        self::assertNotNull($table->getWorksheet());
530
        self::assertInstanceOf(Worksheet::class, $table->getWorksheet());
531
        $tableColumns = $table->getColumns();
532
        $resultColumns = $result->getColumns();
533
        self::assertIsArray($tableColumns);
534
        self::assertIsArray($resultColumns);
535
        self::assertCount(2, $tableColumns);
536
        self::assertCount(2, $resultColumns);
537
        self::assertArrayHasKey('L', $tableColumns);
538
        self::assertArrayHasKey('L', $resultColumns);
539
        self::assertArrayHasKey('M', $tableColumns);
540
        self::assertArrayHasKey('M', $resultColumns);
541
        self::assertInstanceOf(Column::class, $tableColumns['L']);
542
        self::assertInstanceOf(Column::class, $resultColumns['L']);
543
        self::assertInstanceOf(Column::class, $tableColumns['M']);
544
        self::assertInstanceOf(Column::class, $resultColumns['M']);
545
    }
546
547
    public function testNoWorksheet(): void
548
    {
549
        $table = new Table();
550
        self::assertNull($table->getWorksheet());
551
    }
552
553
    public function testClearColumn(): void
554
    {
555
        $table = new Table(self::INITIAL_RANGE);
556
        $columnIndexes = ['J', 'K', 'L', 'M'];
557
558
        foreach ($columnIndexes as $columnIndex) {
559
            $table->setColumn($columnIndex);
560
        }
561
        $columns = $table->getColumns();
562
        self::assertCount(4, $columns);
563
        self::assertArrayHasKey('J', $columns);
564
        self::assertArrayHasKey('K', $columns);
565
        self::assertArrayHasKey('L', $columns);
566
        self::assertArrayHasKey('M', $columns);
567
        $table->clearColumn('K');
568
        $columns = $table->getColumns();
569
        self::assertCount(3, $columns);
570
        self::assertArrayHasKey('J', $columns);
571
        self::assertArrayHasKey('L', $columns);
572
        self::assertArrayHasKey('M', $columns);
573
    }
574
575
    public function testAutoFilterRule(): void
576
    {
577
        $table = new Table(self::INITIAL_RANGE);
578
        $columnFilter = $table->getAutoFilter()->getColumn('H');
579
        $columnFilter->setFilterType(AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER);
580
        $columnFilter->createRule()
581
            ->setRule(
582
                AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
583
                3
584
            );
585
        $autoFilterRuleObject = new AutoFilter\Column\Rule($columnFilter);
586
        self::assertEquals(AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_FILTER, $autoFilterRuleObject->getRuleType());
587
        $ruleParent = $autoFilterRuleObject->getParent();
588
        if ($ruleParent === null) {
589
            self::fail('Unexpected null parent');
590
        } else {
591
            self::assertEquals('H', $ruleParent->getColumnIndex());
592
            self::assertSame($columnFilter, $ruleParent);
593
        }
594
    }
595
}
596