diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 46c68e4..00bf2ea 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,11 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "7.3" + - name: Keep composer at 1.x run: sudo composer selfupdate --1 @@ -61,6 +66,11 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "7.3" + - name: Install xmllint run: sudo apt-get install libxml2-utils @@ -85,9 +95,6 @@ jobs: - name: PHPUnit configuration file run: xmllint --schema vendor/phpunit/phpunit/phpunit.xsd --noout phpunit.xml.dist - - name: PHPCodeSniffer configuration file - run: xmllint --schema vendor/squizlabs/php_codesniffer/phpcs.xsd --noout phpcs.xml.dist - - name: Fetch schema for xliff run: wget https://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd --output-document=.Build/xliff-core-1.2-strict.xsd @@ -102,6 +109,11 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "7.3" + - name: Keep composer at 1.x run: sudo composer selfupdate --1 @@ -121,7 +133,7 @@ jobs: run: composer install --prefer-dist --no-progress --no-suggest - name: Coding Guideline - run: ./vendor/bin/phpcs + run: ./vendor/bin/ecs tests: runs-on: ubuntu-latest diff --git a/Classes/Controller/Frontend/CalendarController.php b/Classes/Controller/Frontend/CalendarController.php index 9a4f34e..67345db 100644 --- a/Classes/Controller/Frontend/CalendarController.php +++ b/Classes/Controller/Frontend/CalendarController.php @@ -27,9 +27,35 @@ use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter; use WerkraumMedia\Calendar\Domain\Model\Day; use WerkraumMedia\Calendar\Domain\Model\Month; use WerkraumMedia\Calendar\Domain\Model\Week; +use WerkraumMedia\Calendar\Domain\Model\Year; class CalendarController extends ActionController { + public function initializeYearAction() + { + if ($this->request->hasArgument('year') === false) { + $this->request->setArguments([ + 'year' => [ + 'year' => date('Y'), + ], + ]); + } + + $this->arguments->getArgument('year') + ->getPropertyMappingConfiguration() + ->allowAllProperties(); + } + + /** + * @Extbase\IgnoreValidation("year") + */ + public function yearAction(Year $year) + { + $this->view->assignMultiple([ + 'year' => $year, + ]); + } + public function initializeMonthAction() { if ($this->request->hasArgument('month') === false) { diff --git a/Classes/Domain/Model/ForeignDataFactory.php b/Classes/Domain/Model/ForeignDataFactory.php index 6eceab2..f47b4c4 100644 --- a/Classes/Domain/Model/ForeignDataFactory.php +++ b/Classes/Domain/Model/ForeignDataFactory.php @@ -24,7 +24,8 @@ namespace WerkraumMedia\Calendar\Domain\Model; interface ForeignDataFactory { /** - * + * Receives a specific day and may return arbirtrary data. + * This data is attached to the day and available through getter. */ public function getData(Day $day); } diff --git a/Classes/Domain/Model/Month.php b/Classes/Domain/Model/Month.php index 1180081..1bc5102 100644 --- a/Classes/Domain/Model/Month.php +++ b/Classes/Domain/Model/Month.php @@ -41,6 +41,11 @@ class Month */ private $weeks = []; + /** + * @var Day[] + */ + private $days = []; + public function __construct( int $month, int $year @@ -82,6 +87,23 @@ class Month return $this->weeks; } + public function getDays(): array + { + if ($this->days !== []) { + return $this->days; + } + + $currentDay = $this->getDateTimeInstance()->modify('first day of this month'); + $endOfWeek = $this->getDateTimeInstance()->modify('last day of this month'); + + while ($currentDay <= $endOfWeek) { + $this->days[] = new Day(\DateTime::createFromImmutable($currentDay)); + $currentDay = $currentDay->modify('+1 day'); + } + + return $this->days; + } + public function getPreviousMonth(): Month { $previousMonth = $this->month - 1; diff --git a/Classes/Domain/Model/Year.php b/Classes/Domain/Model/Year.php new file mode 100644 index 0000000..88f005f --- /dev/null +++ b/Classes/Domain/Model/Year.php @@ -0,0 +1,109 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Persistence\ObjectStorage; + +class Year +{ + /** + * @var int + */ + private $year; + + /** + * @var Month[] + */ + private $months = []; + + public function __construct( + int $year + ) { + $this->year = $year; + } + + public function isActive(): bool + { + foreach ($this->getMonths() as $month) { + if ($month->isActive()) { + return true; + } + } + + return false; + } + + public function getMonths(): array + { + if ($this->months !== []) { + return $this->months; + } + + $lastMonth = new \DateTimeImmutable($this->year . '-12-31'); + $currentMonth = new \DateTimeImmutable($this->year . '-01-01'); + + while ($currentMonth <= $lastMonth) { + $this->months[] = new Month( + (int) $currentMonth->format('n'), + $this->year + ); + + $currentMonth = $currentMonth->modify('+1 month'); + } + + return $this->months; + } + + public function getDays(): array + { + $days = []; + + foreach ($this->getMonths() as $month) { + $days = array_merge($days, $month->getDays()); + } + + return $days; + } + + public function getPreviousYear(): Year + { + return new self($this->year - 1); + } + + public function getNextYear(): Year + { + return new self($this->year + 1); + } + + public function getAsUrlArgument(): array + { + return [ + 'year' => $this->year, + ]; + } + + public function getDateTimeInstance(): \DateTimeImmutable + { + return new \DateTimeImmutable($this->year . '-01-01'); + } +} diff --git a/Tests/Unit/Controller/Frontend/CalendarControllerTest.php b/Tests/Unit/Controller/Frontend/CalendarControllerTest.php index 9080b8f..353d63e 100644 --- a/Tests/Unit/Controller/Frontend/CalendarControllerTest.php +++ b/Tests/Unit/Controller/Frontend/CalendarControllerTest.php @@ -34,6 +34,7 @@ use WerkraumMedia\Calendar\Controller\Frontend\CalendarController; use WerkraumMedia\Calendar\Domain\Model\Day; use WerkraumMedia\Calendar\Domain\Model\Month; use WerkraumMedia\Calendar\Domain\Model\Week; +use WerkraumMedia\Calendar\Domain\Model\Year; use WerkraumMedia\Calendar\Tests\ForcePropertyTrait; /** @@ -45,6 +46,68 @@ class CalendarControllerTest extends TestCase use ProphecyTrait; use ForcePropertyTrait; + /** + * @test + */ + public function setsCurrentYearAsDefaultArgument(): void + { + $subject = new CalendarController(); + + $arguments = $this->allowsMappingOfAllPropertiesForArgument('year')['arguments']; + + $request = $this->prophesize(Request::class); + $request->hasArgument('year')->willReturn(false); + $request->setArguments([ + 'year' => [ + 'year' => date('Y'), + ], + ])->shouldBeCalled(); + + $this->forceProperty($subject, 'request', $request->reveal()); + $this->forceProperty($subject, 'arguments', $arguments->reveal()); + + $subject->initializeYearAction(); + $request->checkProphecyMethodsPredictions(); + } + + /** + * @test + */ + public function allowsYearToBeMapped(): void + { + $subject = new CalendarController(); + + $arguments = $this->allowsMappingOfAllPropertiesForArgument('year')['arguments']; + + $request = $this->prophesize(Request::class); + $request->hasArgument('year')->willReturn(true); + + $this->forceProperty($subject, 'request', $request->reveal()); + $this->forceProperty($subject, 'arguments', $arguments->reveal()); + + $subject->initializeYearAction(); + $arguments->checkProphecyMethodsPredictions(); + } + + /** + * @test + */ + public function addsYearToView(): void + { + $subject = new CalendarController(); + + $year = $this->prophesize(Year::class); + $view = $this->prophesize(ViewInterface::class); + $view->assignMultiple([ + 'year' => $year->reveal(), + ])->shouldBeCalled(); + + $this->forceProperty($subject, 'view', $view->reveal()); + + $subject->yearAction($year->reveal()); + $view->checkProphecyMethodsPredictions(); + } + /** * @test */ diff --git a/Tests/Unit/Domain/Model/MonthTest.php b/Tests/Unit/Domain/Model/MonthTest.php index c385303..02a45f7 100644 --- a/Tests/Unit/Domain/Model/MonthTest.php +++ b/Tests/Unit/Domain/Model/MonthTest.php @@ -126,6 +126,44 @@ class MonthTest extends TestCase self::assertSame($subject->getWeeks(), $subject->getWeeks()); } + /** + * @test + */ + public function returnsAllDaysOfTheFebruaryMonth2021(): void + { + $subject = new Month(02, 2021); + + $result = $subject->getDays(); + + self::assertCount(28, $result); + self::assertSame('2021-02-01', $result[0]->getDateTimeInstance()->format('Y-m-d')); + self::assertSame('2021-02-28', $result[27]->getDateTimeInstance()->format('Y-m-d')); + } + + /** + * @test + */ + public function returnsAllDaysOfTheJuneMonth2021(): void + { + $subject = new Month(06, 2021); + + $result = $subject->getDays(); + + self::assertCount(30, $result); + self::assertSame('2021-06-01', $result[0]->getDateTimeInstance()->format('Y-m-d')); + self::assertSame('2021-06-30', $result[29]->getDateTimeInstance()->format('Y-m-d')); + } + + /** + * @test + */ + public function returnsSameDaysOnSecondCall(): void + { + $subject = new Month(06, 2021); + + self::assertSame($subject->getDays(), $subject->getDays()); + } + /** * @test */ diff --git a/Tests/Unit/Domain/Model/NullDataFactoryTest.php b/Tests/Unit/Domain/Model/NullDataFactoryTest.php index 7e4ea07..e50aac7 100644 --- a/Tests/Unit/Domain/Model/NullDataFactoryTest.php +++ b/Tests/Unit/Domain/Model/NullDataFactoryTest.php @@ -21,10 +21,10 @@ namespace WerkraumMedia\Calendar\Tests\Unit\Domain\Model; * 02110-1301, USA. */ +use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use WerkraumMedia\Calendar\Domain\Model\Day; use WerkraumMedia\Calendar\Domain\Model\NullDataFactory; -use PHPUnit\Framework\TestCase; /** * @covers WerkraumMedia\Calendar\Domain\Model\NullDataFactory diff --git a/Tests/Unit/Domain/Model/YearTest.php b/Tests/Unit/Domain/Model/YearTest.php new file mode 100644 index 0000000..97eee36 --- /dev/null +++ b/Tests/Unit/Domain/Model/YearTest.php @@ -0,0 +1,158 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\Calendar\Domain\Model\Month; +use WerkraumMedia\Calendar\Domain\Model\Year; +use WerkraumMedia\Calendar\Tests\ForcePropertyTrait; + +/** + * @covers WerkraumMedia\Calendar\Domain\Model\Year + * @testdox A year + */ +class YearTest extends TestCase +{ + use ProphecyTrait; + use ForcePropertyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new Year(2020); + + self::assertInstanceOf(Year::class, $subject); + } + + /** + * @test + */ + public function returnsPreviousYear(): void + { + $subject = new Year(2020); + + $result = $subject->getPreviousYear(); + + self::assertInstanceOf(Year::class, $result); + self::assertSame('2019', $result->getDateTimeInstance()->format('Y')); + } + + /** + * @test + */ + public function returnsNextYear(): void + { + $subject = new Year(2020); + + $result = $subject->getNextYear(); + + self::assertInstanceOf(Year::class, $result); + self::assertSame('2021', $result->getDateTimeInstance()->format('Y')); + } + + /** + * @test + */ + public function returnsAsUrlArguments(): void + { + $subject = new Year(2020); + + self::assertSame([ + 'year' => 2020, + ], $subject->getAsUrlArgument()); + } + + /** + * @test + */ + public function returnsMonthsForYear2020(): void + { + $subject = new Year(2020); + + $result = $subject->getMonths(); + + self::assertCount(12, $result); + + foreach ($result as $index => $month) { + self::assertInstanceOf(Month::class, $month); + $monthNumber = $index + 1; + self::assertSame((string) $monthNumber, $month->getDateTimeInstance()->format('n')); + } + } + + /** + * @test + */ + public function returnsSameMonthsOnSecondCall(): void + { + $subject = new Year(2020); + + self::assertSame($subject->getMonths(), $subject->getMonths()); + } + + /** + * @test + */ + public function returnsAllDaysFor2020(): void + { + $subject = new Year(2020); + + $result = $subject->getDays(); + + self::assertCount(366, $result); + self::assertSame('2020-01-01', $result[0]->getDateTimeInstance()->format('Y-m-d')); + self::assertSame('2020-12-31', $result[365]->getDateTimeInstance()->format('Y-m-d')); + } + + /** + * @test + */ + public function returnsNotActiveIfAllMonthsAreInactive(): void + { + $subject = new Year(2020); + + $month = $this->prophesize(Month::class); + $month->isActive()->willReturn(false); + $months = [$month->reveal()]; + $this->forceProperty($subject, 'months', $months); + + self::assertFalse($subject->isActive()); + } + + /** + * @test + */ + public function returnsActiveIfASingleMonthIsActive(): void + { + $subject = new Year(2020); + + $month = $this->prophesize(Month::class); + $month->isActive()->willReturn(true); + $months = [$month->reveal()]; + $this->forceProperty($subject, 'months', $months); + + self::assertTrue($subject->isActive()); + } +} diff --git a/composer.json b/composer.json index 584e6e4..13eaaee 100644 --- a/composer.json +++ b/composer.json @@ -31,8 +31,8 @@ "jangregor/phpstan-prophecy": "^0.6.2", "maglnet/composer-require-checker": "^2.1", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.4", - "squizlabs/php_codesniffer": "^3.5" + "phpunit/phpunit": "^9.5", + "symplify/easy-coding-standard": "^9.2" }, "extra": { "typo3/cms": { diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..379a801 --- /dev/null +++ b/ecs.php @@ -0,0 +1,35 @@ +parameters(); + $parameters->set(Option::PATHS, [ + __DIR__ . '/Classes', + __DIR__ . '/Tests', + __DIR__ . '/ext_emconf.php', + __DIR__ . '/ecs.php', + ]); + + $parameters->set(Option::SETS, [ + // run and fix, one by one + // SetList::SPACES, + // SetList::ARRAY, + // SetList::DOCBLOCK, + // SetList::NAMESPACES, + // SetList::CONTROL_STRUCTURES, + // SetList::CLEAN_CODE, + SetList::PSR_12, + ]); + + $services = $containerConfigurator->services(); + $services->set(ArraySyntaxFixer::class) + ->call('configure', [[ + 'syntax' => 'short', + ]]); +}; diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 68b095a..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,19 +0,0 @@ - - - This project coding standard - - Classes/ - Tests/ - - - - - - - - - - - /Tests/* - -