519 lines
20 KiB
PHP
519 lines
20 KiB
PHP
<?php
|
|
|
|
namespace PhpOffice\PhpSpreadsheetTests\Style\ConditionalFormatting;
|
|
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellMatcher;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class CellMatcherTest extends TestCase
|
|
{
|
|
/**
|
|
* @var Spreadsheet
|
|
*/
|
|
protected $spreadsheet;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$filename = 'tests/data/Style/ConditionalFormatting/CellMatcher.xlsx';
|
|
$reader = IOFactory::createReader('Xlsx');
|
|
$this->spreadsheet = $reader->load($filename);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider basicCellIsComparisonDataProvider
|
|
*/
|
|
public function testBasicCellIsComparison(string $sheetname, string $cellAddress, array $expectedMatches): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyles = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
foreach ($cfStyles as $cfIndex => $cfStyle) {
|
|
$match = $matcher->evaluateConditional($cfStyle);
|
|
self::assertSame($expectedMatches[$cfIndex], $match);
|
|
}
|
|
}
|
|
|
|
public function basicCellIsComparisonDataProvider(): array
|
|
{
|
|
return [
|
|
// Less than/Equal/Greater than with Literal
|
|
'A2' => ['cellIs Comparison', 'A2', [false, false, true]],
|
|
'C3' => ['cellIs Comparison', 'C3', [false, true, false]],
|
|
'E6' => ['cellIs Comparison', 'E6', [true, false, false]],
|
|
// Less than/Equal/Greater than with Cell Reference
|
|
'A12' => ['cellIs Comparison', 'A12', [false, false, true]],
|
|
'C12' => ['cellIs Comparison', 'C12', [false, true, false]],
|
|
'E12' => ['cellIs Comparison', 'E12', [true, false, false]],
|
|
// Compare Text with Cell containing Formula
|
|
'A20' => ['cellIs Comparison', 'A20', [true]],
|
|
'B20' => ['cellIs Comparison', 'B20', [false]],
|
|
// Compare Text with Formula referencing relative cells
|
|
'A24' => ['cellIs Comparison', 'A24', [true]],
|
|
'B24' => ['cellIs Comparison', 'B24', [false]],
|
|
'A25' => ['cellIs Comparison', 'A25', [false]],
|
|
'B25' => ['cellIs Comparison', 'B25', [true]],
|
|
// Compare Cell Greater/Less with Vertical Cell Reference
|
|
'A30' => ['cellIs Comparison', 'A30', [false, true]],
|
|
'A31' => ['cellIs Comparison', 'A31', [true, false]],
|
|
'A32' => ['cellIs Comparison', 'A32', [false, true]],
|
|
'A33' => ['cellIs Comparison', 'A33', [true, false]],
|
|
'A34' => ['cellIs Comparison', 'A34', [false, false]],
|
|
'A35' => ['cellIs Comparison', 'A35', [false, true]],
|
|
'A36' => ['cellIs Comparison', 'A36', [true, false]],
|
|
'A37' => ['cellIs Comparison', 'A37', [true, false]],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider rangeCellIsComparisonDataProvider
|
|
*/
|
|
public function testRangeCellIsComparison(string $sheetname, string $cellAddress, bool $expectedMatch): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyle = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
$match = $matcher->evaluateConditional($cfStyle[0]);
|
|
self::assertSame($expectedMatch, $match);
|
|
}
|
|
|
|
public function rangeCellIsComparisonDataProvider(): array
|
|
{
|
|
return [
|
|
// Range between Literals
|
|
'A2' => ['cellIs Range Comparison', 'A2', false],
|
|
'A3' => ['cellIs Range Comparison', 'A3', true],
|
|
'A4' => ['cellIs Range Comparison', 'A4', true],
|
|
'A5' => ['cellIs Range Comparison', 'A5', true],
|
|
'A6' => ['cellIs Range Comparison', 'A6', false],
|
|
// Range between Cell References
|
|
'A11' => ['cellIs Range Comparison', 'A11', false],
|
|
'A12' => ['cellIs Range Comparison', 'A12', false],
|
|
'A13' => ['cellIs Range Comparison', 'A13', true],
|
|
// Range between unordered Cell References
|
|
'A17' => ['cellIs Range Comparison', 'A17', true],
|
|
'A18' => ['cellIs Range Comparison', 'A18', true],
|
|
// Range between with Formula
|
|
'A22' => ['cellIs Range Comparison', 'A22', false],
|
|
'A23' => ['cellIs Range Comparison', 'A23', true],
|
|
'A24' => ['cellIs Range Comparison', 'A24', false],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider cellIsExpressionMultipleDataProvider
|
|
*/
|
|
public function testCellIsMultipleExpression(string $sheetname, string $cellAddress, array $expectedMatches): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyles = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
foreach ($cfStyles as $cfIndex => $cfStyle) {
|
|
$match = $matcher->evaluateConditional($cfStyle);
|
|
self::assertSame($expectedMatches[$cfIndex], $match);
|
|
}
|
|
}
|
|
|
|
public function cellIsExpressionMultipleDataProvider(): array
|
|
{
|
|
return [
|
|
// Odd/Even
|
|
'A2' => ['cellIs Expression', 'A2', [false, true]],
|
|
'A3' => ['cellIs Expression', 'A3', [true, false]],
|
|
'B3' => ['cellIs Expression', 'B3', [false, true]],
|
|
'C3' => ['cellIs Expression', 'C3', [true, false]],
|
|
'E4' => ['cellIs Expression', 'E4', [false, true]],
|
|
'E5' => ['cellIs Expression', 'E5', [true, false]],
|
|
'E6' => ['cellIs Expression', 'E6', [false, true]],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider cellIsExpressionDataProvider
|
|
*/
|
|
public function testCellIsExpression(string $sheetname, string $cellAddress, bool $expectedMatch): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyle = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
$match = $matcher->evaluateConditional($cfStyle[0]);
|
|
self::assertSame($expectedMatch, $match);
|
|
}
|
|
|
|
public function cellIsExpressionDataProvider(): array
|
|
{
|
|
return [
|
|
// Sales Grid for Country
|
|
['cellIs Expression', 'A12', false],
|
|
['cellIs Expression', 'B12', false],
|
|
['cellIs Expression', 'C12', false],
|
|
['cellIs Expression', 'D12', false],
|
|
['cellIs Expression', 'B13', true],
|
|
['cellIs Expression', 'C13', true],
|
|
['cellIs Expression', 'B15', true],
|
|
['cellIs Expression', 'B16', true],
|
|
['cellIs Expression', 'C17', false],
|
|
// Sales Grid for Country and Quarter
|
|
['cellIs Expression', 'A22', false],
|
|
['cellIs Expression', 'B22', false],
|
|
['cellIs Expression', 'C22', false],
|
|
['cellIs Expression', 'D22', false],
|
|
['cellIs Expression', 'B23', true],
|
|
['cellIs Expression', 'C23', true],
|
|
['cellIs Expression', 'B25', false],
|
|
['cellIs Expression', 'B26', true],
|
|
['cellIs Expression', 'C27', false],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider textExpressionsDataProvider
|
|
*/
|
|
public function testTextExpressions(string $sheetname, string $cellAddress, bool $expectedMatch): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyle = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
$match = $matcher->evaluateConditional($cfStyle[0]);
|
|
self::assertSame($expectedMatch, $match);
|
|
}
|
|
|
|
public function textExpressionsDataProvider(): array
|
|
{
|
|
return [
|
|
// Text Begins With Literal
|
|
['Text Expressions', 'A2', true],
|
|
['Text Expressions', 'B2', false],
|
|
['Text Expressions', 'A3', false],
|
|
['Text Expressions', 'B3', false],
|
|
['Text Expressions', 'A4', false],
|
|
['Text Expressions', 'B4', true],
|
|
// Text Ends With Literal
|
|
['Text Expressions', 'A8', false],
|
|
['Text Expressions', 'B8', false],
|
|
['Text Expressions', 'A9', true],
|
|
['Text Expressions', 'B9', true],
|
|
['Text Expressions', 'A10', false],
|
|
['Text Expressions', 'B10', true],
|
|
// Text Contains Literal
|
|
['Text Expressions', 'A14', true],
|
|
['Text Expressions', 'B14', false],
|
|
['Text Expressions', 'A15', true],
|
|
['Text Expressions', 'B15', true],
|
|
['Text Expressions', 'A16', false],
|
|
['Text Expressions', 'B16', true],
|
|
// Text Doesn't Contain Literal
|
|
['Text Expressions', 'A20', true],
|
|
['Text Expressions', 'B20', true],
|
|
['Text Expressions', 'A21', true],
|
|
['Text Expressions', 'B21', true],
|
|
['Text Expressions', 'A22', false],
|
|
['Text Expressions', 'B22', true],
|
|
// Text Begins With Cell Reference
|
|
['Text Expressions', 'D2', true],
|
|
['Text Expressions', 'E2', false],
|
|
['Text Expressions', 'D3', false],
|
|
['Text Expressions', 'E3', false],
|
|
['Text Expressions', 'D4', false],
|
|
['Text Expressions', 'E4', true],
|
|
// Text Ends With Cell Reference
|
|
['Text Expressions', 'D8', false],
|
|
['Text Expressions', 'E8', false],
|
|
['Text Expressions', 'D9', true],
|
|
['Text Expressions', 'E9', true],
|
|
['Text Expressions', 'D10', false],
|
|
['Text Expressions', 'E10', true],
|
|
// Text Contains Cell Reference
|
|
['Text Expressions', 'D14', true],
|
|
['Text Expressions', 'E14', false],
|
|
['Text Expressions', 'D15', true],
|
|
['Text Expressions', 'E15', true],
|
|
['Text Expressions', 'D16', false],
|
|
['Text Expressions', 'E16', true],
|
|
// Text Doesn't Contain Cell Reference
|
|
['Text Expressions', 'D20', true],
|
|
['Text Expressions', 'E20', true],
|
|
['Text Expressions', 'D21', true],
|
|
['Text Expressions', 'E21', true],
|
|
['Text Expressions', 'D22', false],
|
|
['Text Expressions', 'E22', true],
|
|
// Text Begins With Formula
|
|
['Text Expressions', 'G2', true],
|
|
['Text Expressions', 'H2', false],
|
|
['Text Expressions', 'G3', false],
|
|
['Text Expressions', 'H3', false],
|
|
['Text Expressions', 'G4', false],
|
|
['Text Expressions', 'H4', true],
|
|
// Text Ends With Formula
|
|
['Text Expressions', 'G8', false],
|
|
['Text Expressions', 'H8', false],
|
|
['Text Expressions', 'G9', true],
|
|
['Text Expressions', 'H9', true],
|
|
['Text Expressions', 'G10', false],
|
|
['Text Expressions', 'H10', true],
|
|
// Text Contains Formula
|
|
['Text Expressions', 'G14', true],
|
|
['Text Expressions', 'H14', false],
|
|
['Text Expressions', 'G15', true],
|
|
['Text Expressions', 'H15', true],
|
|
['Text Expressions', 'G16', false],
|
|
['Text Expressions', 'H16', true],
|
|
// Text Doesn't Contain Formula
|
|
['Text Expressions', 'G20', true],
|
|
['Text Expressions', 'H20', true],
|
|
['Text Expressions', 'G21', true],
|
|
['Text Expressions', 'H21', true],
|
|
['Text Expressions', 'G22', false],
|
|
['Text Expressions', 'H22', true],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider blanksDataProvider
|
|
*/
|
|
public function testBlankExpressions(string $sheetname, string $cellAddress, array $expectedMatches): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyles = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
foreach ($cfStyles as $cfIndex => $cfStyle) {
|
|
$match = $matcher->evaluateConditional($cfStyle);
|
|
self::assertSame($expectedMatches[$cfIndex], $match);
|
|
}
|
|
}
|
|
|
|
public function blanksDataProvider(): array
|
|
{
|
|
return [
|
|
// Blank/Not Blank
|
|
'A2' => ['Blank Expressions', 'A2', [false, true]],
|
|
'B2' => ['Blank Expressions', 'B2', [true, false]],
|
|
'A3' => ['Blank Expressions', 'A3', [true, false]],
|
|
'B3' => ['Blank Expressions', 'B3', [false, true]],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider errorDataProvider
|
|
*/
|
|
public function testErrorExpressions(string $sheetname, string $cellAddress, array $expectedMatches): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyles = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
foreach ($cfStyles as $cfIndex => $cfStyle) {
|
|
$match = $matcher->evaluateConditional($cfStyle);
|
|
self::assertSame($expectedMatches[$cfIndex], $match);
|
|
}
|
|
}
|
|
|
|
public function errorDataProvider(): array
|
|
{
|
|
return [
|
|
// Error/Not Error
|
|
'C2' => ['Error Expressions', 'C2', [false, true]],
|
|
'C4' => ['Error Expressions', 'C4', [true, false]],
|
|
'C5' => ['Error Expressions', 'C5', [false, true]],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider dateOccurringDataProvider
|
|
*/
|
|
public function testDateOccurringExpressions(string $sheetname, string $cellAddress, bool $expectedMatch): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyle = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
$match = $matcher->evaluateConditional($cfStyle[0]);
|
|
self::assertSame($expectedMatch, $match);
|
|
}
|
|
|
|
public function dateOccurringDataProvider(): array
|
|
{
|
|
return [
|
|
// Today
|
|
['Date Expressions', 'B9', false],
|
|
['Date Expressions', 'B10', true],
|
|
['Date Expressions', 'B11', false],
|
|
// Yesterday
|
|
['Date Expressions', 'C9', true],
|
|
['Date Expressions', 'C10', false],
|
|
['Date Expressions', 'C11', false],
|
|
// Tomorrow
|
|
['Date Expressions', 'D9', false],
|
|
['Date Expressions', 'D10', false],
|
|
['Date Expressions', 'D11', true],
|
|
// Last Daye
|
|
['Date Expressions', 'E7', false],
|
|
['Date Expressions', 'E8', true],
|
|
['Date Expressions', 'E9', true],
|
|
['Date Expressions', 'E10', true],
|
|
['Date Expressions', 'E11', false],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider duplicatesDataProvider
|
|
*/
|
|
public function testDuplicatesExpressions(string $sheetname, string $cellAddress, array $expectedMatches): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyles = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
foreach ($cfStyles as $cfIndex => $cfStyle) {
|
|
$match = $matcher->evaluateConditional($cfStyle);
|
|
self::assertSame($expectedMatches[$cfIndex], $match);
|
|
}
|
|
}
|
|
|
|
public function duplicatesDataProvider(): array
|
|
{
|
|
return [
|
|
// Duplicate/Unique
|
|
'A2' => ['Duplicates Expressions', 'A2', [true, false]],
|
|
'B2' => ['Duplicates Expressions', 'B2', [false, true]],
|
|
'A4' => ['Duplicates Expressions', 'A4', [true, false]],
|
|
'A5' => ['Duplicates Expressions', 'A5', [false, true]],
|
|
'B5' => ['Duplicates Expressions', 'B5', [true, false]],
|
|
'A9' => ['Duplicates Expressions', 'A9', [true, false]],
|
|
'B9' => ['Duplicates Expressions', 'B9', [false, true]],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider textCrossWorksheetDataProvider
|
|
*/
|
|
public function testCrossWorksheetExpressions(string $sheetname, string $cellAddress, bool $expectedMatch): void
|
|
{
|
|
$worksheet = $this->spreadsheet->getSheetByName($sheetname);
|
|
if ($worksheet === null) {
|
|
self::markTestSkipped("{$sheetname} not found in test workbook");
|
|
}
|
|
$cell = $worksheet->getCell($cellAddress);
|
|
|
|
$cfRange = $worksheet->getConditionalRange($cell->getCoordinate());
|
|
if ($cfRange === null) {
|
|
self::markTestSkipped("{$cellAddress} is not in a Conditional Format range");
|
|
}
|
|
$cfStyle = $worksheet->getConditionalStyles($cell->getCoordinate());
|
|
|
|
$matcher = new CellMatcher($cell, $cfRange);
|
|
|
|
$match = $matcher->evaluateConditional($cfStyle[0]);
|
|
self::assertSame($expectedMatch, $match);
|
|
}
|
|
|
|
public function textCrossWorksheetDataProvider(): array
|
|
{
|
|
return [
|
|
// Relative Cell References in another Worksheet
|
|
'A1' => ['CrossSheet References', 'A1', false],
|
|
'A2' => ['CrossSheet References', 'A2', false],
|
|
'A3' => ['CrossSheet References', 'A3', true],
|
|
'A4' => ['CrossSheet References', 'A4', false],
|
|
'A5' => ['CrossSheet References', 'A5', false],
|
|
];
|
|
}
|
|
}
|