From a735afc0884a8266b65f21f362152255778d028f Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Tue, 15 Jun 2021 07:13:11 -0700 Subject: [PATCH] Autofilter Part 2 Most of the remaining 32-bit-unsafe date handling that remains in PhpSpreadsheet is in AutoFilter. Cleaning this up demonstrated that there are a lot of problems with AutoFilter, and I will do it in two pieces. Part 1 was PR #2141 which I have just merged. In this PR: - Fix remaining 32-bit dates in filterTestInDateGroupSet. - Also in some of the existing AutoFilter samples. Note that the comments in two of those said the filter was being set for the first day of each month, but the code specifies the last day - I have corrected the comments. - Remove mocking in unit tests for AutoFilter in favor of 'real' tests. - Code coverage is now 100% in all of AutoFilter, AutoFilter/Column, and AutoFilter/Common/Rule. - No remaining AutoFilter(/Column(/Rule)) exceptions in Phpstan baseline. - Documentation for escaping of asterisk, question mark, and tilde in text filters included spurious backslashes which are now removed. - Text filter escaping of question mark did not work. There had been no unit tests for any text filtering. - Likewise there had been no testing for TopTen. - Above- and below- average filters were not working because they acquired their Calculation instance incorrectly. There had been no tests. - Several unchanging private static arrays in Rule were changed to private const arrays. - Clones are now tested. - RuleTest is moved to same directory as other tests. --- docs/topics/autofilters.md | 16 +- phpstan-baseline.neon | 165 ---------- .../Autofilter/10_Autofilter_selection_1.php | 13 +- .../Autofilter/10_Autofilter_selection_2.php | 7 +- .../10_Autofilter_selection_display.php | 13 +- src/PhpSpreadsheet/Worksheet/AutoFilter.php | 114 ++++--- .../Worksheet/AutoFilter/Column.php | 6 +- .../Worksheet/AutoFilter/Column/Rule.php | 52 ++-- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 21 +- .../AutoFilter/AutoFilterAverageTop10Test.php | 166 ++++++++++ .../AutoFilter/AutoFilterCustomTextTest.php | 105 +++++++ .../AutoFilter/AutoFilterMonthTest.php | 25 +- .../AutoFilter/AutoFilterQuarterTest.php | 27 +- .../Worksheet/AutoFilter/AutoFilterTest.php | 268 ++++++++++------ .../AutoFilter/AutoFilterTodayTest.php | 20 +- .../AutoFilter/AutoFilterWeekTest.php | 24 +- .../AutoFilter/AutoFilterYearTest.php | 37 +-- .../Worksheet/AutoFilter/Column/RuleTest.php | 111 ------- .../Worksheet/AutoFilter/ColumnTest.php | 291 ++++++++++++------ .../Worksheet/AutoFilter/DateGroupTest.php | 232 ++++++++++++++ .../Worksheet/AutoFilter/RuleCustomTest.php | 58 ++++ .../AutoFilter/RuleDateGroupTest.php | 137 +++++++++ .../Worksheet/AutoFilter/RuleTest.php | 122 ++++++++ .../Worksheet/AutoFilter/SetupTeardown.php | 68 ++++ 24 files changed, 1446 insertions(+), 652 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterAverageTop10Test.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php delete mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/DateGroupTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleCustomTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleDateGroupTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/AutoFilter/SetupTeardown.php diff --git a/docs/topics/autofilters.md b/docs/topics/autofilters.md index 16efb8ff..cd4291f4 100644 --- a/docs/topics/autofilters.md +++ b/docs/topics/autofilters.md @@ -251,16 +251,16 @@ $columnFilter->createRule() ); ``` -MS Excel uses \* as a wildcard to match any number of characters, and ? -as a wildcard to match a single character. 'U\*' equates to "begins with -a 'U'"; '\*U' equates to "ends with a 'U'"; and '\*U\*' equates to -"contains a 'U'" +MS Excel uses `*` as a wildcard to match any number of characters, and `?` +as a wildcard to match a single character. `U*` equates to "begins with +a 'U'"; `*U` equates to "ends with a 'U'"; and `*U*` equates to +"contains a 'U'". -If you want to match explicitly against a \* or a ? character, you can -escape it with a tilde (\~), so ?\~\*\* would explicitly match for a \* -character as the second character in the cell value, followed by any +If you want to match explicitly against `*` or `?`, you can +escape it with a tilde `~`, so `?~**` would explicitly match for `*` +as the second character in the cell value, followed by any number of other characters. The only other character that needs escaping -is the \~ itself. +is the `~` itself. To create a "between" condition, we need to define two rules: diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9dee7fae..ec8e0f4d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -5290,156 +5290,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Style/Style.php - - - message: "#^Result of && is always true\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Parameter \\#1 \\$excelTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Date\\:\\:excelToTimestamp\\(\\) expects float\\|int, float\\|int\\|string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Parameter \\#1 \\$number of function floor expects float, float\\|int\\<1, max\\>\\|string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:\\$toReplace has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$columnID with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$endRow with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$ruleType with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$ruleValue with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$startRow with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot call method rangeToArray\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot call method getRowDimension\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot assign offset 'year' to array\\\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot assign offset 'month' to array\\\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot assign offset 'day' to array\\\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot assign offset 'hour' to array\\\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot assign offset 'minute' to array\\\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot assign offset 'second' to array\\\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Parameter \\#1 \\$str of function preg_quote expects string, array\\\\|string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Binary operation \"\\*\" between 0\\|array\\\\|string and float\\|int results in an error\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Left side of && is always true\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\('PhpOffice\\\\\\\\PhpSpreadsheet\\\\\\\\Worksheet\\\\\\\\AutoFilter', mixed\\) given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$ruleTypes has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$dateTimeGroups has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$dynamicTypes has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$operators has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$topTenValue has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$topTenType has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$parent \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php - - message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#" count: 1 @@ -7030,16 +6880,6 @@ parameters: count: 2 path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array\\\\|string given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - - message: "#^Argument of an invalid type array\\\\|string supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int\\|string given\\.$#" count: 1 @@ -7270,11 +7110,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Style/ConditionalTest.php - - - message: "#^Parameter \\#1 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:setValue\\(\\) expects array\\\\|string, int given\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php - - message: "#^Parameter \\#2 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttribute\\(\\) expects string, int given\\.$#" count: 1 diff --git a/samples/Autofilter/10_Autofilter_selection_1.php b/samples/Autofilter/10_Autofilter_selection_1.php index 556ef61c..511417f0 100644 --- a/samples/Autofilter/10_Autofilter_selection_1.php +++ b/samples/Autofilter/10_Autofilter_selection_1.php @@ -31,7 +31,8 @@ $spreadsheet->getActiveSheet()->setCellValue('A1', 'Financial Year') ->setCellValue('D1', 'Date') ->setCellValue('E1', 'Sales Value') ->setCellValue('F1', 'Expenditure'); -$startYear = $endYear = $currentYear = date('Y'); +$dateTime = new DateTime(); +$startYear = $endYear = $currentYear = (int) $dateTime->format('Y'); --$startYear; ++$endYear; @@ -52,7 +53,9 @@ $row = 2; foreach ($years as $year) { foreach ($periods as $period) { foreach ($countries as $country) { - $endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year)); + $dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period); + $dateTime = new DateTime($dateString); + $endDays = (int) $dateTime->format('t'); for ($i = 1; $i <= $endDays; ++$i) { $eDate = Date::formattedPHPToExcel( $year, @@ -124,10 +127,12 @@ $autoFilter->getColumn('C') 'japan' ) ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); -// Filter the Date column on a filter value of the first day of every period of the current year +// Filter the Date column on a filter value of the last day of every period of the current year // We us a dateGroup ruletype for this, although it is still a standard filter foreach ($periods as $period) { - $endDate = date('t', mktime(0, 0, 0, $period, 1, (int) $currentYear)); + $dateString = sprintf('%04d-%02d-01T00:00:00', $currentYear, $period); + $dateTime = new DateTime($dateString); + $endDate = (int) $dateTime->format('t'); $autoFilter->getColumn('D') ->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER) diff --git a/samples/Autofilter/10_Autofilter_selection_2.php b/samples/Autofilter/10_Autofilter_selection_2.php index 4bae0aba..9e0680fc 100644 --- a/samples/Autofilter/10_Autofilter_selection_2.php +++ b/samples/Autofilter/10_Autofilter_selection_2.php @@ -31,7 +31,8 @@ $spreadsheet->getActiveSheet()->setCellValue('A1', 'Financial Year') ->setCellValue('D1', 'Date') ->setCellValue('E1', 'Sales Value') ->setCellValue('F1', 'Expenditure'); -$startYear = $endYear = $currentYear = date('Y'); +$dateTime = new DateTime(); +$startYear = $endYear = $currentYear = (int) $dateTime->format('Y'); --$startYear; ++$endYear; @@ -52,7 +53,9 @@ $row = 2; foreach ($years as $year) { foreach ($periods as $period) { foreach ($countries as $country) { - $endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year)); + $dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period); + $dateTime = new DateTime($dateString); + $endDays = (int) $dateTime->format('t'); for ($i = 1; $i <= $endDays; ++$i) { $eDate = Date::formattedPHPToExcel( $year, diff --git a/samples/Autofilter/10_Autofilter_selection_display.php b/samples/Autofilter/10_Autofilter_selection_display.php index 4810348c..b07266b9 100644 --- a/samples/Autofilter/10_Autofilter_selection_display.php +++ b/samples/Autofilter/10_Autofilter_selection_display.php @@ -31,7 +31,8 @@ $spreadsheet->getActiveSheet()->setCellValue('A1', 'Financial Year') ->setCellValue('D1', 'Date') ->setCellValue('E1', 'Sales Value') ->setCellValue('F1', 'Expenditure'); -$startYear = $endYear = $currentYear = date('Y'); +$dateTime = new DateTime(); +$startYear = $endYear = $currentYear = (int) $dateTime->format('Y'); --$startYear; ++$endYear; @@ -52,7 +53,9 @@ $row = 2; foreach ($years as $year) { foreach ($periods as $period) { foreach ($countries as $country) { - $endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year)); + $dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period); + $dateTime = new DateTime($dateString); + $endDays = (int) $dateTime->format('t'); for ($i = 1; $i <= $endDays; ++$i) { $eDate = Date::formattedPHPToExcel( $year, @@ -124,10 +127,12 @@ $autoFilter->getColumn('C') 'japan' ) ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); -// Filter the Date column on a filter value of the first day of every period of the current year +// Filter the Date column on a filter value of the last day of every period of the current year // We us a dateGroup ruletype for this, although it is still a standard filter foreach ($periods as $period) { - $endDate = date('t', mktime(0, 0, 0, $period, 1, (int) $currentYear)); + $dateString = sprintf('%04d-%02d-01T00:00:00', $currentYear, $period); + $dateTime = new DateTime($dateString); + $endDate = (int) $dateTime->format('t'); $autoFilter->getColumn('D') ->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER) diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/src/PhpSpreadsheet/Worksheet/AutoFilter.php index 6c23805d..e705c9e3 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -91,26 +91,25 @@ class AutoFilter { // extract coordinate [$worksheet, $pRange] = Worksheet::extractSheetTitle($pRange, true); - - if (strpos($pRange, ':') !== false) { - $this->range = $pRange; - } elseif (empty($pRange)) { - $this->range = ''; - } else { - throw new PhpSpreadsheetException('Autofilter must be set on a range of cells.'); - } - if (empty($pRange)) { // Discard all column rules $this->columns = []; - } else { - // Discard any column rules that are no longer valid within this range - [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); - foreach ($this->columns as $key => $value) { - $colIndex = Coordinate::columnIndexFromString($key); - if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) { - unset($this->columns[$key]); - } + $this->range = ''; + + return $this; + } + + if (strpos($pRange, ':') === false) { + throw new PhpSpreadsheetException('Autofilter must be set on a range of cells.'); + } + + $this->range = $pRange; + // Discard any column rules that are no longer valid within this range + [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); + foreach ($this->columns as $key => $value) { + $colIndex = Coordinate::columnIndexFromString($key); + if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) { + unset($this->columns[$key]); } } @@ -215,7 +214,7 @@ class AutoFilter if (is_string($pColumn)) { $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this); - } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) { + } else { $pColumn->setParent($this); $this->columns[$column] = $pColumn; } @@ -306,20 +305,22 @@ class AutoFilter if (($cellValue == '') || ($cellValue === null)) { return $blanks; } + $timeZone = new DateTimeZone('UTC'); if (is_numeric($cellValue)) { - $dateValue = Date::excelToTimestamp($cellValue); + $dateTime = Date::excelToDateTimeObject((float) $cellValue, $timeZone); + $cellValue = (float) $cellValue; if ($cellValue < 1) { // Just the time part - $dtVal = date('His', $dateValue); + $dtVal = $dateTime->format('His'); $dateSet = $dateSet['time']; } elseif ($cellValue == floor($cellValue)) { // Just the date part - $dtVal = date('Ymd', $dateValue); + $dtVal = $dateTime->format('Ymd'); $dateSet = $dateSet['date']; } else { // date and time parts - $dtVal = date('YmdHis', $dateValue); + $dtVal = $dateTime->format('YmdHis'); $dateSet = $dateSet['dateTime']; } foreach ($dateSet as $dateValue) { @@ -453,12 +454,10 @@ class AutoFilter /** * Search/Replace arrays to convert Excel wildcard syntax to a regexp syntax for preg_matching. - * - * @var array */ - private static $fromReplace = ['\*', '\?', '~~', '~.*', '~.?']; + private const FROM_REPLACE = ['~~', '~\\*', '\\*', '~\\?', '\\?', "\x1c"]; - private static $toReplace = ['.*', '.', '~', '\*', '\?']; + private const TO_REPLACE = ["\x1c", '[*]', '.*', '[?]', '.', '~']; private static function makeDateObject(int $year, int $month, int $day, int $hour = 0, int $minute = 0, int $second = 0): DateTime { @@ -710,21 +709,37 @@ class AutoFilter return ['method' => 'filterTestInCustomDataSet', 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND]]; } + /** + * Apply the AutoFilter rules to the AutoFilter Range. + * + * @param string $columnID + * @param int $startRow + * @param int $endRow + * @param ?string $ruleType + * @param mixed $ruleValue + * + * @return mixed + */ private function calculateTopTenValue($columnID, $startRow, $endRow, $ruleType, $ruleValue) { $range = $columnID . $startRow . ':' . $columnID . $endRow; - $dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false)); + $retVal = null; + if ($this->workSheet !== null) { + $dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false)); + $dataValues = array_filter($dataValues); - $dataValues = array_filter($dataValues); - if ($ruleType == Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) { - rsort($dataValues); - } else { - sort($dataValues); + if ($ruleType == Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) { + rsort($dataValues); + } else { + sort($dataValues); + } + + $slice = array_slice($dataValues, 0, $ruleValue); + + $retVal = array_pop($slice); } - $slice = array_slice($dataValues, 0, $ruleValue); - - return array_pop($slice); + return $retVal; } /** @@ -734,6 +749,9 @@ class AutoFilter */ public function showHideRows() { + if ($this->workSheet === null) { + return $this; + } [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); // The heading row should always be visible @@ -771,6 +789,9 @@ class AutoFilter 'dateTime' => [], ]; foreach ($ruleDataSet as $ruleValue) { + if (!is_array($ruleValue)) { + continue; + } $date = $time = ''; if ( (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) && @@ -830,10 +851,10 @@ class AutoFilter // Build a list of the filter value selections foreach ($rules as $rule) { $ruleValue = $rule->getValue(); - if (!is_numeric($ruleValue)) { + if (!is_array($ruleValue) && !is_numeric($ruleValue)) { // Convert to a regexp allowing for regexp reserved characters, wildcards and escaped wildcards - $ruleValue = preg_quote($ruleValue); - $ruleValue = str_replace(self::$fromReplace, self::$toReplace, $ruleValue); + $ruleValue = preg_quote("$ruleValue"); + $ruleValue = str_replace(self::FROM_REPLACE, self::TO_REPLACE, $ruleValue); if (trim($ruleValue) == '') { $customRuleForBlanks = true; $ruleValue = trim($ruleValue); @@ -860,7 +881,8 @@ class AutoFilter // Number (Average) based // Calculate the average $averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')'; - $average = Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1')); + $spreadsheet = ($this->workSheet === null) ? null : $this->workSheet->getParent(); + $average = Calculation::getInstance($spreadsheet)->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1')); // Set above/below rule based on greaterThan or LessTan $operator = ($dynamicRuleType === Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ? Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN @@ -915,8 +937,8 @@ class AutoFilter $ruleValue = $rule->getValue(); $ruleOperator = $rule->getOperator(); } - if ($ruleOperator === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) { - $ruleValue = floor($ruleValue * ($dataRowCount / 100)); + if (is_numeric($ruleValue) && $ruleOperator === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) { + $ruleValue = floor((float) $ruleValue * ($dataRowCount / 100)); } if (!is_array($ruleValue) && $ruleValue < 1) { $ruleValue = 1; @@ -947,18 +969,16 @@ class AutoFilter foreach ($columnFilterTests as $columnID => $columnFilterTest) { $cellValue = $this->workSheet->getCell($columnID . $row)->getCalculatedValue(); // Execute the filter test - $result = $result && - call_user_func_array( - [self::class, $columnFilterTest['method']], - [$cellValue, $columnFilterTest['arguments']] - ); + $result = // $result && // phpstan says $result is always true here + // @phpstan-ignore-next-line + call_user_func_array([self::class, $columnFilterTest['method']], [$cellValue, $columnFilterTest['arguments']]); // If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests if (!$result) { break; } } // Set show/hide for the row based on the result of the autoFilter result - $this->workSheet->getRowDimension($row)->setVisible($result); + $this->workSheet->getRowDimension((int) $row)->setVisible($result); } return $this; diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php index 1309b30f..6bedceca 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php @@ -244,7 +244,7 @@ class Column /** * Get AutoFilter Column Attributes. * - * @return string[] + * @return int[]|string[] */ public function getAttributes() { @@ -256,7 +256,7 @@ class Column * * @param string $pName Attribute Name * - * @return null|string + * @return null|int|string */ public function getAttribute($pName) { @@ -370,8 +370,6 @@ class Column $cloned->setParent($this); // attach the new cloned Rule to this new cloned Autofilter Cloned object $this->ruleset[$k] = $cloned; } - } elseif (is_object($value)) { - $this->$key = clone $value; } else { $this->$key = $value; } diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php index 330a1641..37ea070b 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php @@ -13,7 +13,7 @@ class Rule const AUTOFILTER_RULETYPE_DYNAMICFILTER = 'dynamicFilter'; const AUTOFILTER_RULETYPE_TOPTENFILTER = 'top10Filter'; - private static $ruleTypes = [ + private const RULE_TYPES = [ // Currently we're not handling // colorFilter // extLst @@ -32,7 +32,7 @@ class Rule const AUTOFILTER_RULETYPE_DATEGROUP_MINUTE = 'minute'; const AUTOFILTER_RULETYPE_DATEGROUP_SECOND = 'second'; - private static $dateTimeGroups = [ + private const DATE_TIME_GROUPS = [ self::AUTOFILTER_RULETYPE_DATEGROUP_YEAR, self::AUTOFILTER_RULETYPE_DATEGROUP_MONTH, self::AUTOFILTER_RULETYPE_DATEGROUP_DAY, @@ -88,7 +88,7 @@ class Rule const AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE = 'aboveAverage'; const AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE = 'belowAverage'; - private static $dynamicTypes = [ + private const DYNAMIC_TYPES = [ self::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY, self::AUTOFILTER_RULETYPE_DYNAMIC_TODAY, self::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW, @@ -141,7 +141,7 @@ class Rule const AUTOFILTER_COLUMN_RULE_LESSTHAN = 'lessThan'; const AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL = 'lessThanOrEqual'; - private static $operators = [ + private const OPERATORS = [ self::AUTOFILTER_COLUMN_RULE_EQUAL, self::AUTOFILTER_COLUMN_RULE_NOTEQUAL, self::AUTOFILTER_COLUMN_RULE_GREATERTHAN, @@ -153,7 +153,7 @@ class Rule const AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE = 'byValue'; const AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT = 'byPercent'; - private static $topTenValue = [ + private const TOP_TEN_VALUE = [ self::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, self::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT, ]; @@ -161,7 +161,7 @@ class Rule const AUTOFILTER_COLUMN_RULE_TOPTEN_TOP = 'top'; const AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM = 'bottom'; - private static $topTenType = [ + private const TOP_TEN_TYPE = [ self::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP, self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, ]; @@ -203,7 +203,7 @@ class Rule /** * Autofilter Column. * - * @var Column + * @var ?Column */ private $parent; @@ -217,7 +217,7 @@ class Rule /** * Autofilter Rule Value. * - * @var string|string[] + * @var int|int[]|string|string[] */ private $value = ''; @@ -237,8 +237,6 @@ class Rule /** * Create a new Rule. - * - * @param Column $pParent */ public function __construct(?Column $pParent = null) { @@ -264,7 +262,7 @@ class Rule */ public function setRuleType($pRuleType) { - if (!in_array($pRuleType, self::$ruleTypes)) { + if (!in_array($pRuleType, self::RULE_TYPES)) { throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.'); } @@ -276,7 +274,7 @@ class Rule /** * Get AutoFilter Rule Value. * - * @return string|string[] + * @return int|int[]|string|string[] */ public function getValue() { @@ -286,7 +284,7 @@ class Rule /** * Set AutoFilter Rule Value. * - * @param string|string[] $pValue + * @param int|int[]|string|string[] $pValue * * @return $this */ @@ -296,19 +294,19 @@ class Rule $grouping = -1; foreach ($pValue as $key => $value) { // Validate array entries - if (!in_array($key, self::$dateTimeGroups)) { + if (!in_array($key, self::DATE_TIME_GROUPS)) { // Remove any invalid entries from the value array unset($pValue[$key]); } else { // Work out what the dateTime grouping will be - $grouping = max($grouping, array_search($key, self::$dateTimeGroups)); + $grouping = max($grouping, array_search($key, self::DATE_TIME_GROUPS)); } } if (count($pValue) == 0) { throw new PhpSpreadsheetException('Invalid rule value for column AutoFilter Rule.'); } // Set the dateTime grouping that we've anticipated - $this->setGrouping(self::$dateTimeGroups[$grouping]); + $this->setGrouping(self::DATE_TIME_GROUPS[$grouping]); } $this->value = $pValue; @@ -338,8 +336,8 @@ class Rule $pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL; } if ( - (!in_array($pOperator, self::$operators)) && - (!in_array($pOperator, self::$topTenValue)) + (!in_array($pOperator, self::OPERATORS)) && + (!in_array($pOperator, self::TOP_TEN_VALUE)) ) { throw new PhpSpreadsheetException('Invalid operator for column AutoFilter Rule.'); } @@ -369,11 +367,11 @@ class Rule { if ( ($pGrouping !== null) && - (!in_array($pGrouping, self::$dateTimeGroups)) && - (!in_array($pGrouping, self::$dynamicTypes)) && - (!in_array($pGrouping, self::$topTenType)) + (!in_array($pGrouping, self::DATE_TIME_GROUPS)) && + (!in_array($pGrouping, self::DYNAMIC_TYPES)) && + (!in_array($pGrouping, self::TOP_TEN_TYPE)) ) { - throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.'); + throw new PhpSpreadsheetException('Invalid grouping for column AutoFilter Rule.'); } $this->grouping = $pGrouping; @@ -384,7 +382,7 @@ class Rule * Set AutoFilter Rule. * * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_* - * @param string|string[] $pValue + * @param int|int[]|string|string[] $pValue * @param string $pGrouping * * @return $this @@ -406,7 +404,7 @@ class Rule /** * Get this Rule's AutoFilter Column Parent. * - * @return Column + * @return ?Column */ public function getParent() { @@ -416,8 +414,6 @@ class Rule /** * Set this Rule's AutoFilter Column Parent. * - * @param Column $pParent - * * @return $this */ public function setParent(?Column $pParent = null) @@ -435,11 +431,9 @@ class Rule $vars = get_object_vars($this); foreach ($vars as $key => $value) { if (is_object($value)) { - if ($key == 'parent') { + if ($key == 'parent') { // this is only object // Detach from autofilter column parent $this->$key = null; - } else { - $this->$key = clone $value; } } else { $this->$key = $value; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 70d0a414..548ea497 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -917,15 +917,18 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('type', $rule->getGrouping()); $val = $column->getAttribute('val'); if ($val !== null) { - $objWriter->writeAttribute('val', $val); + $objWriter->writeAttribute('val', "$val"); } $maxVal = $column->getAttribute('maxVal'); if ($maxVal !== null) { - $objWriter->writeAttribute('maxVal', $maxVal); + $objWriter->writeAttribute('maxVal', "$maxVal"); } } elseif ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_TOPTENFILTER) { // Top 10 Filter Rule - $objWriter->writeAttribute('val', $rule->getValue()); + $ruleValue = $rule->getValue(); + if (!is_array($ruleValue)) { + $objWriter->writeAttribute('val', "$ruleValue"); + } $objWriter->writeAttribute('percent', (($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) ? '1' : '0')); $objWriter->writeAttribute('top', (($rule->getGrouping() === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) ? '1' : '0')); } else { @@ -937,14 +940,18 @@ class Worksheet extends WriterPart } if ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_DATEGROUP) { // Date Group filters - foreach ($rule->getValue() as $key => $value) { - if ($value > '') { - $objWriter->writeAttribute($key, $value); + $ruleValue = $rule->getValue(); + if (is_array($ruleValue)) { + foreach ($ruleValue as $key => $value) { + $objWriter->writeAttribute($key, "$value"); } } $objWriter->writeAttribute('dateTimeGrouping', $rule->getGrouping()); } else { - $objWriter->writeAttribute('val', $rule->getValue()); + $ruleValue = $rule->getValue(); + if (!is_array($ruleValue)) { + $objWriter->writeAttribute('val', "$ruleValue"); + } } $objWriter->endElement(); diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterAverageTop10Test.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterAverageTop10Test.php new file mode 100644 index 00000000..33eca40b --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterAverageTop10Test.php @@ -0,0 +1,166 @@ +getSheet(); + $sheet->getCell('A1')->setValue('Header'); + $sheet->getCell('A2')->setValue(1); + $sheet->getCell('A3')->setValue(3); + $sheet->getCell('A4')->setValue(5); + $sheet->getCell('A5')->setValue(7); + $sheet->getCell('A6')->setValue(9); + $sheet->getCell('A7')->setValue(2); + $sheet->getCell('A8')->setValue(4); + $sheet->getCell('A9')->setValue(6); + $sheet->getCell('A10')->setValue(8); + $this->maxRow = 10; + + return $sheet; + } + + public function providerAverage(): array + { + return [ + [[5, 6, 9, 10], Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE], + [[2, 3, 7, 8], Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE], + ]; + } + + /** + * @dataProvider providerAverage + */ + public function testAboveAverage(array $expectedVisible, string $rule): void + { + $sheet = $this->initSheet(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + '', + $rule + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER); + + self::assertEquals($expectedVisible, $this->getVisible()); + } + + public function providerTop10(): array + { + return [ + [[6, 10], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP, 2], + [[2, 3, 7], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, 3], + [[6], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP, 10], + [[2, 3, 7], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, 40], + ]; + } + + /** + * @dataProvider providerTop10 + */ + public function testTop10(array $expectedVisible, string $rule, string $ruleType, int $count): void + { + $sheet = $this->initSheet(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER); + $columnFilter->createRule() + ->setRule( + $rule, + $count, + $ruleType + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER); + + self::assertEquals($expectedVisible, $this->getVisible()); + } + + public function initsheetTies(): Worksheet + { + $sheet = $this->getSheet(); + $sheet->getCell('A1')->setValue('Header'); + $sheet->getCell('A2')->setValue(1); + $sheet->getCell('A3')->setValue(3); + $sheet->getCell('A4')->setValue(3); + $sheet->getCell('A5')->setValue(7); + $sheet->getCell('A6')->setValue(9); + $sheet->getCell('A7')->setValue(4); + $sheet->getCell('A8')->setValue(4); + $sheet->getCell('A9')->setValue(8); + $sheet->getCell('A10')->setValue(8); + $this->maxRow = 10; + + return $sheet; + } + + public function providerTop10Ties(): array + { + return [ + [[2, 3, 4], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, 2], + [[2], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, 1], + [[5, 6, 7, 8, 9, 10], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP, 5], + [[6], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP, 1], + [[2, 3, 4], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, 25], + [[6, 9, 10], Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT, Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP, 25], + ]; + } + + /** + * @dataProvider providerTop10Ties + */ + public function testTop10Ties(array $expectedVisible, string $rule, string $ruleType, int $count): void + { + $sheet = $this->initSheetTies(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER); + $columnFilter->createRule() + ->setRule( + $rule, + $count, + $ruleType + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER); + + self::assertEquals($expectedVisible, $this->getVisible()); + } + + public function testTop10Exceeds500(): void + { + $sheet = $this->getSheet(); + $sheet->getCell('A1')->setValue('Heading'); + for ($row = 2; $row < 602; ++$row) { + $sheet->getCell("A$row")->setValue($row); + } + $maxRow = $this->maxRow = 601; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE, + 550, + Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER); + + $visible = $this->getVisible(); + self::assertCount(500, $this->getVisible(), 'Top10 Filter limited to 500 items plus ties'); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php new file mode 100644 index 00000000..643ffc0f --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php @@ -0,0 +1,105 @@ +getSheet(); + $sheet->getCell('A1')->setValue('Header'); + $sheet->getCell('A2')->setValue('abc'); + $sheet->getCell('A3')->setValue('cba'); + $sheet->getCell('A4')->setValue('cab'); + // nothing in cell A5 + $sheet->getCell('A6')->setValue('c*b'); + $sheet->getCell('A7')->setValue('c?b'); + $sheet->getCell('A8')->setValue('a'); + $sheet->getCell('A9')->setValue('zzbc'); + $sheet->getCell('A10')->setValue('zzbcd'); + $sheet->getCell('A11')->setValue('~pqr'); + $sheet->getCell('A12')->setValue('pqr~'); + $this->maxRow = 12; + + return $sheet; + } + + public function providerCustomText(): array + { + return [ + 'begins with a' => [[2, 8], 'a*'], + 'ends with b' => [[4, 6, 7], '*b'], + 'contains c' => [[2, 3, 4, 6, 7, 9, 10], '*c*'], + 'empty' => [[5], ''], + 'contains asterisk' => [[6], '*~**'], + 'contains question mark' => [[7], '*~?*'], + 'c followed by character followed by b' => [[4, 6, 7], 'c?b'], + 'one character followed by bc' => [[2], '?bc'], + 'two characters followed by bc' => [[9], '??bc'], + 'starts with z ends with c' => [[9], 'z*c'], + 'starts with tilde' => [[11], '~~*'], + 'contains tilde' => [[11, 12], '*~~*'], + ]; + } + + /** + * @dataProvider providerCustomText + */ + public function testCustomTest(array $expectedVisible, string $pattern): void + { + $sheet = $this->initSheet(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + $pattern + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); + + self::assertEquals($expectedVisible, $this->getVisible()); + } + + public function testCustomTestNotEqual(): void + { + $sheet = $this->initSheet(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL, + '' + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); + + self::assertEquals([2, 3, 4, 6, 7, 8, 9, 10, 11, 12], $this->getVisible()); + } + + public function testCustomTestGreaterThan(): void + { + $sheet = $this->initSheet(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:A$maxRow"); + $columnFilter = $autoFilter->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN, + '' + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); + + self::assertEquals([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], $this->getVisible()); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterMonthTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterMonthTest.php index 064c0dac..c13e5a24 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterMonthTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterMonthTest.php @@ -3,13 +3,11 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; use DateTimeImmutable; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -use PHPUnit\Framework\TestCase; -class AutoFilterMonthTest extends TestCase +class AutoFilterMonthTest extends SetupTeardown { public function providerMonth(): array { @@ -20,7 +18,7 @@ class AutoFilterMonthTest extends TestCase ]; } - private static function setCells(Worksheet $sheet, int $startMonth): void + private function setCells(Worksheet $sheet, int $startMonth): void { $sheet->getCell('A1')->setValue('Date'); $sheet->getCell('A2')->setValue('=TODAY()'); @@ -47,6 +45,7 @@ class AutoFilterMonthTest extends TestCase } $sheet->getCell('A8')->setValue('=DATE(YEAR(A2) + 1, MONTH(A2), 1)'); $sheet->getCell('A9')->setValue('=DATE(YEAR(A2) - 1, MONTH(A2), 1)'); + $this->maxRow = 9; } /** @@ -57,15 +56,14 @@ class AutoFilterMonthTest extends TestCase // Loop to avoid rare edge case where first calculation // and second do not take place in same day. do { - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); + $sheet = $this->getSheet(); $dtStart = new DateTimeImmutable(); $startDay = (int) $dtStart->format('d'); $startMonth = (int) $dtStart->format('m'); - self::setCells($sheet, $startMonth); + $this->setCells($sheet, $startMonth); - $maxRow = 9; - $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); $autoFilter->setRange("A1:A$maxRow"); $columnFilter = $autoFilter->getColumn('A'); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -80,12 +78,7 @@ class AutoFilterMonthTest extends TestCase $dtEnd = new DateTimeImmutable(); $endDay = (int) $dtEnd->format('d'); } while ($startDay !== $endDay); - $actualVisible = []; - for ($row = 2; $row <= $maxRow; ++$row) { - if ($sheet->getRowDimension($row)->getVisible()) { - $actualVisible[] = $row; - } - } - self::assertEquals($expectedVisible, $actualVisible); + + self::assertEquals($expectedVisible, $this->getVisible()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterQuarterTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterQuarterTest.php index 73c48c4a..859f7fdd 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterQuarterTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterQuarterTest.php @@ -3,13 +3,10 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; use DateTimeImmutable; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -use PHPUnit\Framework\TestCase; -class AutoFilterQuarterTest extends TestCase +class AutoFilterQuarterTest extends SetupTeardown { public function providerQuarter(): array { @@ -20,8 +17,9 @@ class AutoFilterQuarterTest extends TestCase ]; } - private static function setCells(Worksheet $sheet): void + private function setCells(): void { + $sheet = $this->getSheet(); $sheet->getCell('A1')->setValue('Date'); $sheet->getCell('A2')->setValue('=TODAY()'); $sheet->getCell('A3')->setValue('=DATE(YEAR(A2), MONTH(A2), 1)'); @@ -31,6 +29,7 @@ class AutoFilterQuarterTest extends TestCase $sheet->getCell('A7')->setValue('=DATE(YEAR(A2), MONTH(A2) - 6, 1)'); $sheet->getCell('A8')->setValue('=DATE(YEAR(A2) + 1, MONTH(A2), 1)'); $sheet->getCell('A9')->setValue('=DATE(YEAR(A2) - 1, MONTH(A2), 1)'); + $this->maxRow = 9; } /** @@ -41,14 +40,13 @@ class AutoFilterQuarterTest extends TestCase // Loop to avoid rare edge case where first calculation // and second do not take place in same day. do { - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); + $sheet = $this->getSheet(); $dtStart = new DateTimeImmutable(); $startDay = (int) $dtStart->format('d'); - self::setCells($sheet); + $this->setCells(); - $maxRow = 9; - $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); $autoFilter->setRange("A1:A$maxRow"); $columnFilter = $autoFilter->getColumn('A'); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -63,12 +61,7 @@ class AutoFilterQuarterTest extends TestCase $dtEnd = new DateTimeImmutable(); $endDay = (int) $dtEnd->format('d'); } while ($startDay !== $endDay); - $actualVisible = []; - for ($row = 2; $row <= $maxRow; ++$row) { - if ($sheet->getRowDimension($row)->getVisible()) { - $actualVisible[] = $row; - } - } - self::assertEquals($expectedVisible, $actualVisible); + + self::assertEquals($expectedVisible, $this->getVisible()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php index a6693d34..28bbbb87 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php @@ -2,95 +2,78 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; -use PhpOffice\PhpSpreadsheet\Collection\Cells; +use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; -class AutoFilterTest extends TestCase +class AutoFilterTest extends SetupTeardown { - /** - * @var string - */ - private $testInitialRange = 'H2:O256'; - - /** - * @var AutoFilter - */ - private $testAutoFilterObject; - - /** - * @var Worksheet&MockObject - */ - private $mockWorksheetObject; - - /** - * @var Cells&MockObject - */ - private $cellCollection; - - protected function setUp(): void - { - $this->mockWorksheetObject = $this->getMockBuilder(Worksheet::class) - ->disableOriginalConstructor() - ->getMock(); - $this->cellCollection = $this->getMockBuilder(Cells::class) - ->disableOriginalConstructor() - ->getMock(); - $this->mockWorksheetObject->expects(self::any()) - ->method('getCellCollection') - ->willReturn($this->cellCollection); - - $this->testAutoFilterObject = new AutoFilter($this->testInitialRange, $this->mockWorksheetObject); - } + private const INITIAL_RANGE = 'H2:O256'; public function testToString(): void { - $expectedResult = $this->testInitialRange; + $expectedResult = self::INITIAL_RANGE; + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // magic __toString should return the active autofilter range - $result = $this->testAutoFilterObject; + $result = (string) $autoFilter; self::assertEquals($expectedResult, $result); } public function testGetParent(): void { - $result = $this->testAutoFilterObject->getParent(); - self::assertInstanceOf(Worksheet::class, $result); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $result = $autoFilter->getParent(); + self::assertSame($sheet, $result); } public function testSetParent(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $spreadsheet = $this->getSpreadsheet(); + $sheet2 = $spreadsheet->createSheet(); // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setParent($this->mockWorksheetObject); + $result = $autoFilter->setParent($sheet2); self::assertInstanceOf(AutoFilter::class, $result); } public function testGetRange(): void { - $expectedResult = $this->testInitialRange; + $expectedResult = self::INITIAL_RANGE; + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // Result should be the active autofilter range - $result = $this->testAutoFilterObject->getRange(); + $result = $autoFilter->getRange(); self::assertEquals($expectedResult, $result); } public function testSetRange(): void { + $sheet = $this->getSheet(); + $title = $sheet->getTitle(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); $ranges = [ - 'G1:J512' => 'Worksheet1!G1:J512', + 'G1:J512' => "$title!G1:J512", 'K1:N20' => 'K1:N20', ]; foreach ($ranges as $actualRange => $fullRange) { // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setRange($fullRange); + $result = $autoFilter->setRange($fullRange); self::assertInstanceOf(AutoFilter::class, $result); // Result should be the new autofilter range - $result = $this->testAutoFilterObject->getRange(); + $result = $autoFilter->getRange(); self::assertEquals($actualRange, $result); } } @@ -98,29 +81,36 @@ class AutoFilterTest extends TestCase public function testClearRange(): void { $expectedResult = ''; + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setRange(''); + $result = $autoFilter->setRange(''); self::assertInstanceOf(AutoFilter::class, $result); // Result should be a clear range - $result = $this->testAutoFilterObject->getRange(); + $result = $autoFilter->getRange(); self::assertEquals($expectedResult, $result); } public function testSetRangeInvalidRange(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); $expectedResult = 'A1'; - $this->testAutoFilterObject->setRange($expectedResult); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange($expectedResult); } public function testGetColumnsEmpty(): void { // There should be no columns yet defined - $result = $this->testAutoFilterObject->getColumns(); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $result = $autoFilter->getColumns(); self::assertIsArray($result); self::assertCount(0, $result); } @@ -132,33 +122,42 @@ class AutoFilterTest extends TestCase 'K' => 3, 'M' => 5, ]; + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // If we request a specific column by its column ID, we should get an // integer returned representing the column offset within the range foreach ($columnIndexes as $columnIndex => $columnOffset) { - $result = $this->testAutoFilterObject->getColumnOffset($columnIndex); + $result = $autoFilter->getColumnOffset($columnIndex); self::assertEquals($columnOffset, $result); } } public function testGetInvalidColumnOffset(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); $invalidColumn = 'G'; + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); - $this->testAutoFilterObject->getColumnOffset($invalidColumn); + $autoFilter->getColumnOffset($invalidColumn); } public function testSetColumnWithString(): void { $expectedResult = 'L'; + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setColumn($expectedResult); + $result = $autoFilter->setColumn($expectedResult); self::assertInstanceOf(AutoFilter::class, $result); - $result = $this->testAutoFilterObject->getColumns(); + $result = $autoFilter->getColumns(); // Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter\Column // objects for each column we set indexed by the column ID self::assertIsArray($result); @@ -169,23 +168,29 @@ class AutoFilterTest extends TestCase public function testSetInvalidColumnWithString(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); $invalidColumn = 'A'; - $this->testAutoFilterObject->setColumn($invalidColumn); + $autoFilter->setColumn($invalidColumn); } public function testSetColumnWithColumnObject(): void { $expectedResult = 'M'; - $columnObject = new AutoFilter\Column($expectedResult); + $columnObject = new Column($expectedResult); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setColumn($columnObject); + $result = $autoFilter->setColumn($columnObject); self::assertInstanceOf(AutoFilter::class, $result); - $result = $this->testAutoFilterObject->getColumns(); + $result = $autoFilter->getColumns(); // Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter\Column // objects for each column we set indexed by the column ID self::assertIsArray($result); @@ -196,30 +201,40 @@ class AutoFilterTest extends TestCase public function testSetInvalidColumnWithObject(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); $invalidColumn = 'E'; - $this->testAutoFilterObject->setColumn($invalidColumn); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $autoFilter->setColumn($invalidColumn); } public function testSetColumnWithInvalidDataType(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); $invalidColumn = 123.456; // @phpstan-ignore-next-line - $this->testAutoFilterObject->setColumn($invalidColumn); + $autoFilter->setColumn($invalidColumn); } public function testGetColumns(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $columnIndexes = ['L', 'M']; foreach ($columnIndexes as $columnIndex) { - $this->testAutoFilterObject->setColumn($columnIndex); + $autoFilter->setColumn($columnIndex); } - $result = $this->testAutoFilterObject->getColumns(); + $result = $autoFilter->getColumns(); // Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter\Column // objects for each column we set indexed by the column ID self::assertIsArray($result); @@ -228,26 +243,38 @@ class AutoFilterTest extends TestCase self::assertArrayHasKey($columnIndex, $result); self::assertInstanceOf(Column::class, $result[$columnIndex]); } + + $autoFilter->setRange(''); + self::assertCount(0, $autoFilter->getColumns()); + self::assertSame('', $autoFilter->getRange()); } public function testGetColumn(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $columnIndexes = ['L', 'M']; foreach ($columnIndexes as $columnIndex) { - $this->testAutoFilterObject->setColumn($columnIndex); + $autoFilter->setColumn($columnIndex); } // If we request a specific column by its column ID, we should // get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter\Column object returned foreach ($columnIndexes as $columnIndex) { - $result = $this->testAutoFilterObject->getColumn($columnIndex); + $result = $autoFilter->getColumn($columnIndex); self::assertInstanceOf(Column::class, $result); } } public function testGetColumnByOffset(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $columnIndexes = [ 0 => 'H', 3 => 'K', @@ -257,7 +284,7 @@ class AutoFilterTest extends TestCase // If we request a specific column by its offset, we should // get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter\Column object returned foreach ($columnIndexes as $columnIndex => $columnID) { - $result = $this->testAutoFilterObject->getColumnByOffset($columnIndex); + $result = $autoFilter->getColumnByOffset($columnIndex); self::assertInstanceOf(Column::class, $result); self::assertEquals($result->getColumnIndex(), $columnID); } @@ -265,83 +292,152 @@ class AutoFilterTest extends TestCase public function testGetColumnIfNotSet(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // If we request a specific column by its column ID, we should // get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter\Column object returned - $result = $this->testAutoFilterObject->getColumn('K'); + $result = $autoFilter->getColumn('K'); self::assertInstanceOf(Column::class, $result); } public function testGetColumnWithoutRangeSet(): void { $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); // Clear the range - $this->testAutoFilterObject->setRange(''); - $this->testAutoFilterObject->getColumn('A'); + $autoFilter->setRange(''); + $autoFilter->getColumn('A'); } public function testClearRangeWithExistingColumns(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); $expectedResult = ''; $columnIndexes = ['L', 'M', 'N']; foreach ($columnIndexes as $columnIndex) { - $this->testAutoFilterObject->setColumn($columnIndex); + $autoFilter->setColumn($columnIndex); } // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setRange(''); + $result = $autoFilter->setRange(''); self::assertInstanceOf(AutoFilter::class, $result); // Range should be cleared - $result = $this->testAutoFilterObject->getRange(); + $result = $autoFilter->getRange(); self::assertEquals($expectedResult, $result); // Column array should be cleared - $result = $this->testAutoFilterObject->getColumns(); + $result = $autoFilter->getColumns(); self::assertIsArray($result); self::assertCount(0, $result); } public function testSetRangeWithExistingColumns(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); $expectedResult = 'G1:J512'; // These columns should be retained $columnIndexes1 = ['I', 'J']; foreach ($columnIndexes1 as $columnIndex) { - $this->testAutoFilterObject->setColumn($columnIndex); + $autoFilter->setColumn($columnIndex); } // These columns should be discarded $columnIndexes2 = ['K', 'L', 'M']; foreach ($columnIndexes2 as $columnIndex) { - $this->testAutoFilterObject->setColumn($columnIndex); + $autoFilter->setColumn($columnIndex); } // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setRange($expectedResult); + $result = $autoFilter->setRange($expectedResult); self::assertInstanceOf(AutoFilter::class, $result); // Range should be correctly set - $result = $this->testAutoFilterObject->getRange(); + $result = $autoFilter->getRange(); self::assertEquals($expectedResult, $result); // Only columns that existed in the original range and that // still fall within the new range should be retained - $result = $this->testAutoFilterObject->getColumns(); + $result = $autoFilter->getColumns(); self::assertIsArray($result); self::assertCount(count($columnIndexes1), $result); } public function testClone(): void { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); $columnIndexes = ['L', 'M']; foreach ($columnIndexes as $columnIndex) { - $this->testAutoFilterObject->setColumn($columnIndex); + $autoFilter->setColumn($columnIndex); } - $result = clone $this->testAutoFilterObject; + $result = clone $autoFilter; self::assertInstanceOf(AutoFilter::class, $result); + self::assertSame($autoFilter->getRange(), $result->getRange()); + self::assertNull($result->getParent()); + self::assertNotNull($autoFilter->getParent()); + self::assertInstanceOf(Worksheet::class, $autoFilter->getParent()); + $autoColumns = $autoFilter->getColumns(); + $resultColumns = $result->getColumns(); + self::assertIsArray($autoColumns); + self::assertIsArray($resultColumns); + self::assertCount(2, $autoColumns); + self::assertCount(2, $resultColumns); + self::assertArrayHasKey('L', $autoColumns); + self::assertArrayHasKey('L', $resultColumns); + self::assertArrayHasKey('M', $autoColumns); + self::assertArrayHasKey('M', $resultColumns); + self::assertInstanceOf(Column::class, $autoColumns['L']); + self::assertInstanceOf(Column::class, $resultColumns['L']); + self::assertInstanceOf(Column::class, $autoColumns['M']); + self::assertInstanceOf(Column::class, $resultColumns['M']); + } + + public function testNoWorksheet(): void + { + $autoFilter = new AutoFilter(); + self::assertSame($autoFilter, $autoFilter->showHideRows()); + } + + public function testClearColumn(): void + { + $sheet = $this->getSheet(); + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange(self::INITIAL_RANGE); + $columnIndexes = ['J', 'K', 'L', 'M']; + + foreach ($columnIndexes as $columnIndex) { + $autoFilter->setColumn($columnIndex); + } + $columns = $autoFilter->getColumns(); + self::assertCount(4, $columns); + self::assertArrayHasKey('J', $columns); + self::assertArrayHasKey('K', $columns); + self::assertArrayHasKey('L', $columns); + self::assertArrayHasKey('M', $columns); + $autoFilter->clearColumn('K'); + $columns = $autoFilter->getColumns(); + self::assertCount(3, $columns); + self::assertArrayHasKey('J', $columns); + self::assertArrayHasKey('L', $columns); + self::assertArrayHasKey('M', $columns); + $autoFilter->shiftColumn('L', 'K'); + $columns = $autoFilter->getColumns(); + self::assertCount(3, $columns); + self::assertArrayHasKey('J', $columns); + self::assertArrayHasKey('K', $columns); + self::assertArrayHasKey('M', $columns); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTodayTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTodayTest.php index 3875eb8d..0a8aa723 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTodayTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTodayTest.php @@ -3,12 +3,10 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; use DateTimeImmutable; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; -use PHPUnit\Framework\TestCase; -class AutoFilterTodayTest extends TestCase +class AutoFilterTodayTest extends SetupTeardown { public function providerYesterdayTodayTomorrow(): array { @@ -27,8 +25,7 @@ class AutoFilterTodayTest extends TestCase // Loop to avoid rare edge case where first calculation // and second do not take place in same day. do { - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); + $sheet = $this->getSheet(); $dtStart = new DateTimeImmutable(); $startDay = $dtStart->format('d'); $sheet->getCell('A1')->setValue('Date'); @@ -38,8 +35,8 @@ class AutoFilterTodayTest extends TestCase $sheet->getCell('A5')->setValue('=TODAY()'); $sheet->getCell('A6')->setValue('=A5+1'); $sheet->getCell('A7')->setValue('=A5-1'); - $maxRow = 7; - $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); + $this->maxRow = $maxRow = 7; + $autoFilter = $sheet->getAutoFilter(); $autoFilter->setRange("A1:A$maxRow"); $columnFilter = $autoFilter->getColumn('A'); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -54,12 +51,7 @@ class AutoFilterTodayTest extends TestCase $dtEnd = new DateTimeImmutable(); $endDay = $dtEnd->format('d'); } while ($startDay !== $endDay); - $actualVisible = []; - for ($row = 2; $row <= $maxRow; ++$row) { - if ($sheet->getRowDimension($row)->getVisible()) { - $actualVisible[] = $row; - } - } - self::assertEquals($expectedVisible, $actualVisible); + + self::assertEquals($expectedVisible, $this->getVisible()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterWeekTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterWeekTest.php index 765bf11c..f9a51870 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterWeekTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterWeekTest.php @@ -3,13 +3,11 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; use DateTimeImmutable; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -use PHPUnit\Framework\TestCase; -class AutoFilterWeekTest extends TestCase +class AutoFilterWeekTest extends SetupTeardown { public function providerWeek(): array { @@ -20,8 +18,9 @@ class AutoFilterWeekTest extends TestCase ]; } - private static function setCells(Worksheet $sheet): void + private function setCells(Worksheet $sheet): void { + $sheet = $this->getSheet(); $sheet->getCell('A1')->setValue('Date'); $sheet->getCell('A2')->setValue('=TODAY()'); $sheet->getCell('B2')->setValue('=WEEKDAY(A2) - 1'); // subtract to get to Sunday @@ -32,6 +31,7 @@ class AutoFilterWeekTest extends TestCase $sheet->getCell('A7')->setValue('=DATE(YEAR(A3), MONTH(A3), DAY(A3) - 12)'); $sheet->getCell('A8')->setValue('=DATE(YEAR(A2) + 1, MONTH(A2), 1)'); $sheet->getCell('A9')->setValue('=DATE(YEAR(A2) - 1, MONTH(A2), 1)'); + $this->maxRow = 9; } /** @@ -42,14 +42,13 @@ class AutoFilterWeekTest extends TestCase // Loop to avoid rare edge case where first calculation // and second do not take place in same day. do { - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); + $sheet = $this->getSheet(); $dtStart = new DateTimeImmutable(); $startDay = (int) $dtStart->format('d'); self::setCells($sheet); - $maxRow = 9; - $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); + $maxRow = $this->maxRow; + $autoFilter = $sheet->getAutoFilter(); $autoFilter->setRange("A1:A$maxRow"); $columnFilter = $autoFilter->getColumn('A'); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -64,12 +63,7 @@ class AutoFilterWeekTest extends TestCase $dtEnd = new DateTimeImmutable(); $endDay = (int) $dtEnd->format('d'); } while ($startDay !== $endDay); - $actualVisible = []; - for ($row = 2; $row <= $maxRow; ++$row) { - if ($sheet->getRowDimension($row)->getVisible()) { - $actualVisible[] = $row; - } - } - self::assertEquals($expectedVisible, $actualVisible); + + self::assertEquals($expectedVisible, $this->getVisible()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterYearTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterYearTest.php index 8c416cc3..442f846e 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterYearTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterYearTest.php @@ -3,12 +3,10 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; use DateTimeImmutable; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; -use PHPUnit\Framework\TestCase; -class AutoFilterYearTest extends TestCase +class AutoFilterYearTest extends SetupTeardown { public function providerYear(): array { @@ -30,8 +28,7 @@ class AutoFilterYearTest extends TestCase // Loop to avoid rare edge case where first calculation // and second do not take place in same day. do { - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); + $sheet = $this->getSheet(); $dtStart = new DateTimeImmutable(); $startDay = (int) $dtStart->format('d'); $sheet->getCell('A1')->setValue('Date'); @@ -48,8 +45,9 @@ class AutoFilterYearTest extends TestCase } ++$row; $sheet->getCell("A$row")->setValue('=DATE(2041, 1, 1)'); // beyond epoch - $maxRow = $row; - $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); + ++$row; // empty row at end + $this->maxRow = $maxRow = $row; + $autoFilter = $sheet->getAutoFilter(); $autoFilter->setRange("A1:A$maxRow"); $columnFilter = $autoFilter->getColumn('A'); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -64,13 +62,8 @@ class AutoFilterYearTest extends TestCase $dtEnd = new DateTimeImmutable(); $endDay = (int) $dtEnd->format('d'); } while ($startDay !== $endDay); - $actualVisible = []; - for ($row = 2; $row <= $maxRow; ++$row) { - if ($sheet->getRowDimension($row)->getVisible()) { - $actualVisible[] = $row; - } - } - self::assertEquals($expectedVisible, $actualVisible); + + self::assertEquals($expectedVisible, $this->getVisible()); } public function testYearToDate(): void @@ -78,8 +71,7 @@ class AutoFilterYearTest extends TestCase // Loop to avoid rare edge case where first calculation // and second do not take place in same day. do { - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); + $sheet = $this->getSheet(); $dtStart = new DateTimeImmutable(); $startDay = (int) $dtStart->format('d'); $startMonth = (int) $dtStart->format('m'); @@ -90,8 +82,8 @@ class AutoFilterYearTest extends TestCase $sheet->getCell('A5')->setValue('=DATE(YEAR(A2), 1, 1)'); $sheet->getCell('A6')->setValue('=A5 - 1'); - $maxRow = 6; - $autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter(); + $this->maxRow = $maxRow = 6; + $autoFilter = $sheet->getAutoFilter(); $autoFilter->setRange("A1:A$maxRow"); $columnFilter = $autoFilter->getColumn('A'); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -106,13 +98,8 @@ class AutoFilterYearTest extends TestCase $dtEnd = new DateTimeImmutable(); $endDay = (int) $dtEnd->format('d'); } while ($startDay !== $endDay); - $actualVisible = []; - for ($row = 2; $row <= $maxRow; ++$row) { - if ($sheet->getRowDimension($row)->getVisible()) { - $actualVisible[] = $row; - } - } + $expected = ($startMonth === 12 && $startDay === 31) ? [2, 3, 5] : [2, 5]; - self::assertEquals($expected, $actualVisible); + self::assertEquals($expected, $this->getVisible()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php deleted file mode 100644 index 276836c9..00000000 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php +++ /dev/null @@ -1,111 +0,0 @@ -mockAutoFilterColumnObject = $this->getMockBuilder(Column::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->testAutoFilterRuleObject = new Rule( - $this->mockAutoFilterColumnObject - ); - } - - public function testGetRuleType(): void - { - $result = $this->testAutoFilterRuleObject->getRuleType(); - self::assertEquals(Rule::AUTOFILTER_RULETYPE_FILTER, $result); - } - - public function testSetRuleType(): void - { - $expectedResult = Rule::AUTOFILTER_RULETYPE_DATEGROUP; - - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterRuleObject->setRuleType($expectedResult); - self::assertInstanceOf(Rule::class, $result); - - $result = $this->testAutoFilterRuleObject->getRuleType(); - self::assertEquals($expectedResult, $result); - } - - public function testSetValue(): void - { - $expectedResult = 100; - - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterRuleObject->setValue($expectedResult); - self::assertInstanceOf(Rule::class, $result); - - $result = $this->testAutoFilterRuleObject->getValue(); - self::assertEquals($expectedResult, $result); - } - - public function testGetOperator(): void - { - $result = $this->testAutoFilterRuleObject->getOperator(); - self::assertEquals(Rule::AUTOFILTER_COLUMN_RULE_EQUAL, $result); - } - - public function testSetOperator(): void - { - $expectedResult = Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN; - - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterRuleObject->setOperator($expectedResult); - self::assertInstanceOf(Rule::class, $result); - - $result = $this->testAutoFilterRuleObject->getOperator(); - self::assertEquals($expectedResult, $result); - } - - public function testSetGrouping(): void - { - $expectedResult = Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH; - - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterRuleObject->setGrouping($expectedResult); - self::assertInstanceOf(Rule::class, $result); - - $result = $this->testAutoFilterRuleObject->getGrouping(); - self::assertEquals($expectedResult, $result); - } - - public function testGetParent(): void - { - $result = $this->testAutoFilterRuleObject->getParent(); - self::assertInstanceOf(Column::class, $result); - } - - public function testSetParent(): void - { - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterRuleObject->setParent($this->mockAutoFilterColumnObject); - self::assertInstanceOf(Rule::class, $result); - } - - public function testClone(): void - { - $result = clone $this->testAutoFilterRuleObject; - self::assertInstanceOf(Rule::class, $result); - } -} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php index 93369626..65fefb2f 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php @@ -2,148 +2,177 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter; -use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter; +use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; +use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -class ColumnTest extends TestCase +class ColumnTest extends SetupTeardown { - /** - * @var string - */ - private $testInitialColumn = 'H'; - - /** - * @var Column - */ - private $testAutoFilterColumnObject; - - /** - * @var AutoFilter&MockObject - */ - private $mockAutoFilterObject; - - protected function setUp(): void + protected function initSheet(): Worksheet { - $this->mockAutoFilterObject = $this->getMockBuilder(AutoFilter::class) - ->disableOriginalConstructor() - ->getMock(); + $sheet = $this->getSheet(); + $sheet->getCell('G1')->setValue('Heading'); + $sheet->getCell('G2')->setValue(2); + $sheet->getCell('G3')->setValue(3); + $sheet->getCell('G4')->setValue(4); + $sheet->getCell('H1')->setValue('Heading2'); + $sheet->getCell('H2')->setValue(1); + $sheet->getCell('H3')->setValue(2); + $sheet->getCell('H4')->setValue(3); + $this->maxRow = $maxRow = 4; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("G1:H$maxRow"); - $this->mockAutoFilterObject->expects(self::any()) - ->method('testColumnInRange') - ->willReturn(3); - - $this->testAutoFilterColumnObject = new Column($this->testInitialColumn, $this->mockAutoFilterObject); + return $sheet; } - public function testGetColumnIndex(): void + public function testVariousGets(): void { - $result = $this->testAutoFilterColumnObject->getColumnIndex(); - self::assertEquals($this->testInitialColumn, $result); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $autoFilterRuleObject = new Rule($columnFilter); + $result = $columnFilter->getColumnIndex(); + self::assertEquals('H', $result); + } + + public function testGetBadColumnIndex(): void + { + $this->expectException(PhpSpreadsheetException::class); + $this->expectExceptionMessage('Column is outside of current autofilter range.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('B'); } public function testSetColumnIndex(): void { - $expectedResult = 'L'; + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $autoFilterRuleObject = new Rule($columnFilter); + $expectedResult = 'G'; - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterColumnObject->setColumnIndex($expectedResult); + $result = $columnFilter->setColumnIndex($expectedResult); self::assertInstanceOf(Column::class, $result); - $result = $this->testAutoFilterColumnObject->getColumnIndex(); + $result = $result->getColumnIndex(); self::assertEquals($expectedResult, $result); } - public function testGetParent(): void - { - $result = $this->testAutoFilterColumnObject->getParent(); - self::assertInstanceOf(AutoFilter::class, $result); - } - public function testSetParent(): void { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterColumnObject->setParent($this->mockAutoFilterObject); + $result = $columnFilter->setParent(null); self::assertInstanceOf(Column::class, $result); } - public function testGetFilterType(): void + public function testVariousSets(): void { - $result = $this->testAutoFilterColumnObject->getFilterType(); - self::assertEquals(Column::AUTOFILTER_FILTERTYPE_FILTER, $result); - } + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $autoFilterRuleObject = new Rule($columnFilter); - public function testSetFilterType(): void - { - $result = $this->testAutoFilterColumnObject->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); + $result = $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); self::assertInstanceOf(Column::class, $result); - $result = $this->testAutoFilterColumnObject->getFilterType(); + $result = $columnFilter->getFilterType(); self::assertEquals(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER, $result); + + $result = $columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND); + self::assertInstanceOf(Column::class, $result); + + $result = $columnFilter->getJoin(); + self::assertEquals(Column::AUTOFILTER_COLUMN_JOIN_AND, $result); } public function testSetInvalidFilterTypeThrowsException(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); + $this->expectExceptionMessage('Invalid filter type for column AutoFilter.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); $expectedResult = 'Unfiltered'; - $this->testAutoFilterColumnObject->setFilterType($expectedResult); - } - - public function testGetJoin(): void - { - $result = $this->testAutoFilterColumnObject->getJoin(); - self::assertEquals(Column::AUTOFILTER_COLUMN_JOIN_OR, $result); - } - - public function testSetJoin(): void - { - $result = $this->testAutoFilterColumnObject->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND); - self::assertInstanceOf(Column::class, $result); - - $result = $this->testAutoFilterColumnObject->getJoin(); - self::assertEquals(Column::AUTOFILTER_COLUMN_JOIN_AND, $result); + $columnFilter->setFilterType($expectedResult); } public function testSetInvalidJoinThrowsException(): void { - $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); + $this->expectException(PhpSpreadsheetException::class); + $this->expectExceptionMessage('Invalid rule connection for column AutoFilter.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); $expectedResult = 'Neither'; - $this->testAutoFilterColumnObject->setJoin($expectedResult); - } - - public function testSetAttributes(): void - { - $attributeSet = [ - 'val' => 100, - 'maxVal' => 200, - ]; - - // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterColumnObject->setAttributes($attributeSet); - self::assertInstanceOf(Column::class, $result); + $columnFilter->setJoin($expectedResult); } public function testGetAttributes(): void { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); $attributeSet = [ 'val' => 100, 'maxVal' => 200, ]; - $this->testAutoFilterColumnObject->setAttributes($attributeSet); + $result = $columnFilter->setAttributes($attributeSet); + self::assertInstanceOf(Column::class, $result); - $result = $this->testAutoFilterColumnObject->getAttributes(); - self::assertIsArray($result); - self::assertCount(count($attributeSet), $result); + $result = $columnFilter->getAttributes(); + self::assertSame($attributeSet, $result); } public function testSetAttribute(): void { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $attributeSet = [ 'val' => 100, 'maxVal' => 200, @@ -151,37 +180,103 @@ class ColumnTest extends TestCase foreach ($attributeSet as $attributeName => $attributeValue) { // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterColumnObject->setAttribute($attributeName, $attributeValue); + $result = $columnFilter->setAttribute($attributeName, $attributeValue); self::assertInstanceOf(Column::class, $result); } + self::assertSame($attributeSet, $columnFilter->getAttributes()); } public function testGetAttribute(): void { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $attributeSet = [ 'val' => 100, 'maxVal' => 200, ]; - $this->testAutoFilterColumnObject->setAttributes($attributeSet); + $result = $columnFilter->setAttributes($attributeSet); foreach ($attributeSet as $attributeName => $attributeValue) { - $result = $this->testAutoFilterColumnObject->getAttribute($attributeName); - self::assertEquals($attributeValue, $result); + $result = $columnFilter->getAttribute($attributeName); } - $result = $this->testAutoFilterColumnObject->getAttribute('nonExistentAttribute'); + $result = $columnFilter->getAttribute('nonExistentAttribute'); self::assertNull($result); } public function testClone(): void { - $originalRule = $this->testAutoFilterColumnObject->createRule(); - $result = clone $this->testAutoFilterColumnObject; - self::assertInstanceOf(Column::class, $result); - self::assertCount(1, $result->getRules()); - self::assertContainsOnlyInstancesOf(AutoFilter\Column\Rule::class, $result->getRules()); - $clonedRule = $result->getRules()[0]; - self::assertNotSame($originalRule, $clonedRule); - self::assertSame($result, $clonedRule->getParent()); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $originalRule = $columnFilter->getRules(); + $result = clone $columnFilter; + self::assertSame($columnFilter->getColumnIndex(), $result->getColumnIndex()); + self::assertSame($columnFilter->getFilterType(), $result->getFilterType()); + self::assertSame($columnFilter->getJoin(), $result->getJoin()); + self::assertNull($result->getParent()); + self::assertNotNull($columnFilter->getParent()); + self::assertContainsOnlyInstancesOf(Rule::class, $result->getRules()); + $clonedRule = $result->getRules(); + self::assertCount(1, $clonedRule); + self::assertCount(1, $originalRule); + self::assertNotSame($originalRule[0], $clonedRule[0]); + self::assertSame($originalRule[0]->getRuleType(), $clonedRule[0]->getRuleType()); + self::assertSame($originalRule[0]->getValue(), $clonedRule[0]->getValue()); + self::assertSame($originalRule[0]->getOperator(), $clonedRule[0]->getOperator()); + self::assertSame($originalRule[0]->getGrouping(), $clonedRule[0]->getGrouping()); + self::assertSame($result, $clonedRule[0]->getParent()); + } + + public function testRuleManipulation(): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('H'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $originalRules = $columnFilter->getRules(); + self::assertCount(1, $originalRules); + $rule0 = $columnFilter->getRule(0); + self::assertSame($originalRules[0], $rule0); + $rule1 = $columnFilter->getRule(1); + self::assertInstanceOf(Rule::class, $rule1); + self::assertNotEquals($originalRules[0], $rule1); + self::assertCount(2, $columnFilter->getRules()); + self::assertSame(Column::AUTOFILTER_COLUMN_JOIN_OR, $columnFilter->getJoin()); + $columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND); + $rule2 = new Rule(); + $columnFilter->addRule($rule2); + self::assertCount(3, $columnFilter->getRules()); + self::assertSame(Column::AUTOFILTER_COLUMN_JOIN_AND, $columnFilter->getJoin()); + $columnFilter->deleteRule(2); + self::assertCount(2, $columnFilter->getRules()); + self::assertSame(Column::AUTOFILTER_COLUMN_JOIN_AND, $columnFilter->getJoin()); + $columnFilter->deleteRule(1); + self::assertCount(1, $columnFilter->getRules()); + self::assertSame(Column::AUTOFILTER_COLUMN_JOIN_OR, $columnFilter->getJoin()); + $columnFilter->addRule($rule1); + $columnFilter->addRule($rule2); + $columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND); + self::assertCount(3, $columnFilter->getRules()); + self::assertSame(Column::AUTOFILTER_COLUMN_JOIN_AND, $columnFilter->getJoin()); + $columnFilter->clearRules(); + self::assertCount(0, $columnFilter->getRules()); + self::assertSame(Column::AUTOFILTER_COLUMN_JOIN_OR, $columnFilter->getJoin()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/DateGroupTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/DateGroupTest.php new file mode 100644 index 00000000..6d62ed1a --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/DateGroupTest.php @@ -0,0 +1,232 @@ +getSheet(); + $sheet->getCell('A1')->setValue('Date'); + $sheet->getCell('B1')->setValue('Time'); + $sheet->getCell('C1')->setValue('DateTime'); + $sheet->getCell('C1')->setValue('Row*10'); + for ($row = 2; $row < 63; ++$row) { + $sheet->getCell("A$row")->setValue("=DATE($year,11,30)+$row"); + $hour = $row % 24; + $minute = $row % 10; + $second = $row % 20; + $sheet->getCell("B$row")->setValue("=TIME($hour,$minute,$second)"); + $sheet->getCell("C$row")->setValue("=A$row+B$row"); + $sheet->getCell("D$row")->setValue("=10*$row"); + } + $this->maxRow = $maxRow = 62; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:C$maxRow"); + + return $sheet; + } + + public function testYearMonthDayGroup(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('C'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => $year, + 'month' => 12, + 'day' => 6, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([6], $this->getVisible()); + } + + public function testYearMonthDayHourMinuteSecond1Group(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('C'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => $year, + 'month' => 12, + 'day' => 6, + 'hour' => 6, + 'minute' => 6, + 'second' => 6, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + $sheet->getCell('C5')->setValue(''); // make an empty cell in range + self::assertEquals([6], $this->getVisible()); + } + + public function testYearMonthDayHourMinuteSecond2Group(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('C'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => $year, + 'month' => 12, + 'day' => 6, + 'hour' => 6, + 'minute' => 6, + 'second' => 7, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([], $this->getVisible()); + } + + public function testDayGroupEpoch(): void + { + $year = 2040; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('C'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => $year, + 'month' => 12, + 'day' => 6, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([6], $this->getVisible()); + } + + public function testDayGroupNonArray(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $cellA2 = $sheet->getCell('A2')->getCalculatedValue(); + $columnFilter = $sheet->getAutoFilter()->getColumn('C'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + $cellA2 + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([], $this->getVisible()); + } + + public function testHourGroup(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('B'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'hour' => 14, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([14, 38, 62], $this->getVisible()); + } + + public function testHourMinuteGroup(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('B'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'hour' => 14, + 'minute' => 8, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([38], $this->getVisible()); + } + + public function testHourMinuteSecondGroup1(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('B'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'hour' => 14, + 'minute' => 8, + 'second' => 18, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([38], $this->getVisible()); + } + + public function testHourMinuteSecondGroup2(): void + { + $year = 2011; + $sheet = $this->initSheet($year); + $columnFilter = $sheet->getAutoFilter()->getColumn('B'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'hour' => 14, + 'minute' => 8, + 'second' => 19, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([], $this->getVisible()); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleCustomTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleCustomTest.php new file mode 100644 index 00000000..39103463 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleCustomTest.php @@ -0,0 +1,58 @@ +getSheet(); + $sheet->getCell('A1')->setValue('Heading'); + $sheet->getCell('A2')->setValue(2); + $sheet->getCell('A3')->setValue(3); + $sheet->getCell('A4')->setValue(4); + $sheet->getCell('B1')->setValue('Heading2'); + $sheet->getCell('B2')->setValue(1); + $sheet->getCell('B3')->setValue(2); + $sheet->getCell('B4')->setValue(3); + $this->maxRow = $maxRow = 4; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:B$maxRow"); + + return $sheet; + } + + /** + * @dataProvider providerCondition + */ + public function testRuleCondition(array $expectedResult, string $condition): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); + $columnFilter->createRule() + ->setRule( + $condition, + 3 + ) + ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals($expectedResult, $this->getVisible()); + } + + public function providerCondition(): array + { + return [ + [[3], Rule::AUTOFILTER_COLUMN_RULE_EQUAL], + [[2, 4], Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL], + [[4], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN], + [[3, 4], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL], + [[2], Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN], + [[2, 3], Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleDateGroupTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleDateGroupTest.php new file mode 100644 index 00000000..24e33797 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleDateGroupTest.php @@ -0,0 +1,137 @@ +getSheet(); + $sheet->getCell('A1')->setValue('Date'); + $sheet->getCell('A2')->setValue('=DATE(2011,1,10)'); + $sheet->getCell('A3')->setValue('=DATE(2012,1,10)'); + $sheet->getCell('A4')->setValue('=DATE(2011,1,10)'); + $sheet->getCell('A5')->setValue('=DATE(2012,2,10)'); + $sheet->getCell('A6')->setValue('=DATE(2012,1,1)'); + $sheet->getCell('A7')->setValue('=DATE(2012,12,31)'); + $sheet->getCell('B1')->setValue('Heading2'); + $sheet->getCell('B2')->setValue(1); + $sheet->getCell('B3')->setValue(2); + $sheet->getCell('B4')->setValue(3); + $sheet->getCell('B5')->setValue(4); + $sheet->getCell('B6')->setValue(5); + $sheet->getCell('B7')->setValue(6); + $this->maxRow = $maxRow = 7; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:B$maxRow"); + + return $sheet; + } + + public function testYearGroup(): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => 2012, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([3, 5, 6, 7], $this->getVisible()); + } + + public function testYearGroupWithInvalidIndex(): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => 2012, + 'xyz' => 5, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([3, 5, 6, 7], $this->getVisible()); + } + + public function testYearGroupNoValidIndexes(): void + { + $this->expectException(SpException::class); + $this->expectExceptionMessage('Invalid rule value for column AutoFilter Rule.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'zzyear' => 2012, + 'xyz' => 5, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([3, 5, 6, 7], $this->getVisible()); + } + + public function testYearGroupBadRuleType(): void + { + $this->expectException(SpException::class); + $this->expectExceptionMessage('Invalid rule type for column AutoFilter Rule.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => 2012, + ] + ) + ->setRuleType( + 'xyz' + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([3, 5, 6, 7], $this->getVisible()); + } + + public function testYearMonthGroup(): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + [ + 'year' => 2012, + 'month' => 1, + ] + ) + ->setRuleType( + Rule::AUTOFILTER_RULETYPE_DATEGROUP + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals([3, 6], $this->getVisible()); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleTest.php new file mode 100644 index 00000000..c9eaa4f2 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/RuleTest.php @@ -0,0 +1,122 @@ +getSheet(); + $sheet->getCell('A1')->setValue('Heading'); + $sheet->getCell('A2')->setValue(2); + $sheet->getCell('A3')->setValue(3); + $sheet->getCell('A4')->setValue(4); + $sheet->getCell('B1')->setValue('Heading2'); + $sheet->getCell('B2')->setValue(1); + $sheet->getCell('B3')->setValue(2); + $sheet->getCell('B4')->setValue(3); + $this->maxRow = $maxRow = 4; + $autoFilter = $sheet->getAutoFilter(); + $autoFilter->setRange("A1:B$maxRow"); + + return $sheet; + } + + public function testRule(): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $autoFilterRuleObject = new Rule($columnFilter); + self::assertEquals(Rule::AUTOFILTER_RULETYPE_FILTER, $autoFilterRuleObject->getRuleType()); + self::assertEquals([3], $this->getVisible()); + $ruleParent = $autoFilterRuleObject->getParent(); + if ($ruleParent === null) { + self::fail('Unexpected null parent'); + } else { + self::assertEquals('A', $ruleParent->getColumnIndex()); + self::assertSame($columnFilter, $ruleParent); + } + } + + public function testSetParent(): void + { + $sheet = $this->initSheet(); + $autoFilterRuleObject = new Rule(); + $autoFilterRuleObject->setParent($sheet->getAutoFilter()->getColumn('B')); + $columnFilter = $autoFilterRuleObject->getParent(); + if ($columnFilter === null) { + self::fail('Unexpected null parent'); + } else { + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + self::assertEquals(Rule::AUTOFILTER_RULETYPE_FILTER, $autoFilterRuleObject->getRuleType()); + self::assertEquals([4], $this->getVisible()); + } + } + + public function testBadSetRule(): void + { + $this->expectException(SpException::class); + $this->expectExceptionMessage('Invalid operator for column AutoFilter Rule.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + 'xyz', + 3 + ); + } + + public function testBadSetGrouping(): void + { + $this->expectException(SpException::class); + $this->expectExceptionMessage('Invalid grouping for column AutoFilter Rule.'); + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + '', + 3 + ); + $autoFilterRuleObject = new Rule($columnFilter); + $autoFilterRuleObject->setGrouping('xyz'); + } + + public function testClone(): void + { + $sheet = $this->initSheet(); + $columnFilter = $sheet->getAutoFilter()->getColumn('A'); + $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER); + $columnFilter->createRule() + ->setRule( + Rule::AUTOFILTER_COLUMN_RULE_EQUAL, + 3 + ); + $autoFilterRuleObject = new Rule($columnFilter); + $result = clone $autoFilterRuleObject; + self::assertSame($autoFilterRuleObject->getRuleType(), $result->getRuleType()); + self::assertSame($autoFilterRuleObject->getValue(), $result->getValue()); + self::assertSame($autoFilterRuleObject->getRuleType(), $result->getRuleType()); + self::assertSame($autoFilterRuleObject->getOperator(), $result->getOperator()); + self::assertSame($autoFilterRuleObject->getGrouping(), $result->getGrouping()); + self::assertNotNull($autoFilterRuleObject->getParent()); + self::assertNull($result->getParent()); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/SetupTeardown.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/SetupTeardown.php new file mode 100644 index 00000000..95d78012 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/SetupTeardown.php @@ -0,0 +1,68 @@ +sheet = null; + if ($this->spreadsheet !== null) { + $this->spreadsheet->disconnectWorksheets(); + $this->spreadsheet = null; + } + } + + protected function getSpreadsheet(): Spreadsheet + { + if ($this->spreadsheet !== null) { + return $this->spreadsheet; + } + $this->spreadsheet = new Spreadsheet(); + + return $this->spreadsheet; + } + + protected function getSheet(): Worksheet + { + if ($this->sheet !== null) { + return $this->sheet; + } + $this->sheet = $this->getSpreadsheet()->getActiveSheet(); + + return $this->sheet; + } + + public function getVisible(): array + { + $sheet = $this->getSheet(); + $sheet->getAutoFilter()->showHideRows(); + $actualVisible = []; + for ($row = 2; $row <= $this->maxRow; ++$row) { + if ($sheet->getRowDimension($row)->getVisible()) { + $actualVisible[] = $row; + } + } + + return $actualVisible; + } +}