mirror of https://github.com/FriendsOfTYPO3/tea.git synced 2024-11-10 00:36:13 +01:00

[FEATURE] Tea model and repository (#10)

This commit is contained in:
Oliver Klee 2018-05-25 16:19:12 +02:00 committed by GitHub
parent 7fbde75e2b
commit e2f39033c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 576 additions and 3 deletions

View file

@ -7,8 +7,12 @@ php:
- 7.1
env:
- TYPO3_VERSION="^7.6"
- TYPO3_VERSION="^8.7"
global:
- typo3DatabaseHost=localhost typo3DatabaseName=typo3 typo3DatabaseUsername=travis typo3DatabasePassword=''
- TYPO3_PATH_ROOT=$PWD/.Build/public
matrix:
- TYPO3_VERSION="^7.6"
- TYPO3_VERSION="^8.7"
matrix:
exclude:
@ -24,7 +28,6 @@ before_install:
install:
- composer require-typo3-version "$TYPO3_VERSION"
- git checkout .
- export TYPO3_PATH_ROOT=$PWD/.Build/public
script:
- >
@ -36,3 +39,13 @@ script:
echo;
echo "Linting all TypoScript files";
composer ci:ts:lint;
- >
echo;
echo "Running the unit tests";
composer ci:tests:unit;
- >
echo;
echo "Running the functional tests";
composer ci:tests:functional;

View file

@ -0,0 +1,59 @@
<?php
declare(strict_types = 1);
namespace OliverKlee\Tea\Domain\Model\Product;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* This class represents a tea (flavor), e.g., "Earl Grey".
*
* @author Oliver Klee <typo3-coding@oliverklee.de
*/
class Tea extends AbstractEntity
{
/**
* @var string
*/
protected $title = '';
/**
* @var string
*/
protected $description = '';
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*
* @return void
*/
public function setTitle(string $title)
{
$this->title = $title;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @param string $description
*
* @return void
*/
public function setDescription(string $description)
{
$this->description = $description;
}
}

View file

@ -0,0 +1,16 @@
<?php
declare(strict_types = 1);
namespace OliverKlee\Tea\Domain\Repository\Product;
use OliverKlee\Tea\Domain\Repository\Traits\StoragePageAgnosticTrait;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Repository for Tea models.
*
* @author Oliver Klee <typo3-coding@oliverklee.de
*/
class TeaRepository extends Repository
{
use StoragePageAgnosticTrait;
}

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types = 1);
namespace OliverKlee\Tea\Domain\Repository\Traits;
use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface;
/**
* This trait for repositories makes the repository ignore the storage page setting when fetching models.
*
* @author Oliver Klee <typo3-coding@oliverklee.de
*/
trait StoragePageAgnosticTrait
{
/**
* Initializes this object.
*
* @return void
*/
public function initializeObject()
{
/** @var QuerySettingsInterface $querySettings */
$querySettings = $this->objectManager->get(QuerySettingsInterface::class);
$querySettings->setRespectStoragePage(false);
$this->setDefaultQuerySettings($querySettings);
}
}

View file

@ -0,0 +1,42 @@
<?php
return [
'ctrl' => [
'title' => 'LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_product_tea',
'label' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'delete' => 'deleted',
'default_sortby' => 'title',
'dividers2tabs' => true,
'iconfile' => 'EXT:tea/Resources/Public/Icons/Record.svg',
'searchFields' => 'title, description',
],
'interface' => [
'showRecordFieldList' => 'title, description',
],
'types' => [
'1' => ['showitem' => 'title, description'],
],
'columns' => [
'title' => [
'label' => 'LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_product_tea.title',
'config' => [
'type' => 'input',
'size' => 40,
'max' => 255,
'eval' => 'trim,required',
],
],
'description' => [
'label' => 'LLL:EXT:tea/Resources/Private/Language/locallang_db.xlf:tx_tea_domain_model_product_tea.description',
'config' => [
'type' => 'text',
'rows' => 8,
'cols' => 40,
'max' => 2000,
'eval' => 'trim',
],
],
],
];

View file

@ -16,6 +16,46 @@ For information on the different ways to execute the tests, please have a look
at the [handout to my workshops on test-driven development (TDD)](https://github.com/oliverklee/tdd-reader).
## Running the tests in PhpStorm
### General PHPUnit setup
```bash
composer install
```
File > Settings > Languages & Frameworks > PHP > Test Frameworks
- (*) Use Composer autoloader
- Path to script: select `vendor/autoload.php` in your project folder
In the Run configurations, edit the PHPUnit configuration and use these
settings so this configuration can serve as a template:
- Directory: use the `Tests/Unit` directory in your project
- [x] Use alternative configuration file
- use `.Build/vendor/nimut/testing-framework/res/Configuration/UnitTests.xml`
in your project folder
- Add the following environment variables:
- typo3DatabaseUsername
- typo3DatabasePassword
- typo3DatabaseHost
- typo3DatabaseName
### Unit tests configuration
In the Run configurations, copy the PHPUnit configuration and use these settings:
- Directory: use the `Tests/Unit` directory in your project
### Functional tests configuration
In the Run configurations, copy the PHPUnit configuration and use these settings:
- Directory: use the `Tests/Functionsal` directory in your project
- [x] Use alternative configuration file
- use `.Build/vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml`
## Creating new extensions with automated tests
For creating new extensions, I recommend taking

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
<file source-language="en" target-language="de" datatype="plaintext" original="messages">
<header/>
<body>
<trans-unit id="tx_tea_domain_model_product_tea">
<source>Tea</source>
<target>Tee</target>
</trans-unit>
<trans-unit id="tx_tea_domain_model_product_tea.title">
<source>Title</source>
<target>Titel</target>
</trans-unit>
<trans-unit id="tx_tea_domain_model_product_tea.description">
<source>Description</source>
<target>Beschreibung</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
<file source-language="en" datatype="plaintext" original="messages">
<header/>
<body>
<trans-unit id="tx_tea_domain_model_product_tea">
<source>Tea</source>
</trans-unit>
<trans-unit id="tx_tea_domain_model_product_tea.title">
<source>Title</source>
</trans-unit>
<trans-unit id="tx_tea_domain_model_product_tea.description">
<source>Description</source>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 457.8778 342.01929"
xml:space="preserve"
sodipodi:docname="hot-tea.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
width="457.87781"
height="342.01929"><metadata
id="metadata988"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs986"><linearGradient
id="linearGradient992"
osb:paint="solid"><stop
style="stop-color:#ff862b;stop-opacity:1;"
offset="0"
id="stop990" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient992"
id="linearGradient994"
x1="0"
y1="320.008"
x2="480"
y2="320.008"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient992"
id="linearGradient996"
gradientUnits="userSpaceOnUse"
x1="0"
y1="320.008"
x2="480"
y2="320.008" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient992"
id="linearGradient998"
gradientUnits="userSpaceOnUse"
x1="0"
y1="320.008"
x2="480"
y2="320.008" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="1140"
id="namedview984"
showgrid="false"
inkscape:zoom="1.94375"
inkscape:cx="240"
inkscape:cy="102.02732"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g931"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" /><g
id="g933"
transform="matrix(0.95391211,0,0,1.187567,0,-209.02129)"
style="fill:url(#linearGradient994);fill-opacity:1"><g
id="g931"
style="fill:url(#linearGradient998);fill-opacity:1"><path
d="m 400,208.008 h -48 v -16 c 0,-8.832 -7.168,-16 -16,-16 H 16 c -8.832,0 -16,7.168 -16,16 v 128 c 0,79.392 64.608,144 144,144 h 64 c 62.496,0 115.264,-40.256 135.168,-96 H 400 c 21.6,0 41.696,-8.416 56.288,-23.392 C 471.584,329.704 480,309.608 480,288.008 c 0,-44.096 -35.872,-80 -80,-80 z m 33.664,113.952 c -8.832,9.056 -20.8,14.048 -33.664,14.048 h -49.632 c 0.608,-5.312 1.632,-10.528 1.632,-16 v -80 h 48 c 26.464,0 48,21.536 48,48 0,12.896 -4.992,24.832 -14.336,33.952 z"
id="path929"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient996);fill-opacity:1"
sodipodi:nodetypes="scssssssscscsscscscssc" /></g></g><g
id="g953"
transform="translate(0,-176.00801)" /><g
id="g955"
transform="translate(0,-176.00801)" /><g
id="g957"
transform="translate(0,-176.00801)" /><g
id="g959"
transform="translate(0,-176.00801)" /><g
id="g961"
transform="translate(0,-176.00801)" /><g
id="g963"
transform="translate(0,-176.00801)" /><g
id="g965"
transform="translate(0,-176.00801)" /><g
id="g967"
transform="translate(0,-176.00801)" /><g
id="g969"
transform="translate(0,-176.00801)" /><g
id="g971"
transform="translate(0,-176.00801)" /><g
id="g973"
transform="translate(0,-176.00801)" /><g
id="g975"
transform="translate(0,-176.00801)" /><g
id="g977"
transform="translate(0,-176.00801)" /><g
id="g979"
transform="translate(0,-176.00801)" /><g
id="g981"
transform="translate(0,-176.00801)" /></svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<tx_tea_domain_model_product_tea>
<uid>1</uid>
<pid>1</pid>
<title>Earl Grey</title>
<description>Fresh and hot.</description>
</tx_tea_domain_model_product_tea>
</dataset>

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types = 1);
namespace OliverKlee\Tea\Tests\Functional\Domain\Repository\Product;
use Nimut\TestingFramework\TestCase\FunctionalTestCase;
use OliverKlee\Tea\Domain\Model\Product\Tea;
use OliverKlee\Tea\Domain\Repository\Product\TeaRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
/**
* Test case.
*
* @author Oliver Klee <typo3-coding@oliverklee.de
*/
class TeaRepositoryTest extends FunctionalTestCase
{
/**
* @var string[]
*/
protected $testExtensionsToLoad = ['typo3conf/ext/tea'];
/**
* @var TeaRepository
*/
private $subject = null;
protected function setUp()
{
parent::setUp();
/** @var ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->subject = $objectManager->get(TeaRepository::class);
}
/**
* @test
*/
public function findAllForNoRecordsReturnsEmptyContainer()
{
$container = $this->subject->findAll();
static::assertCount(0, $container);
}
/**
* @test
*/
public function findAllWithRecordsFindsRecordsFromAllPages()
{
$this->importDataSet(__DIR__ . '/../Fixtures/Product/Tea.xml');
$container = $this->subject->findAll();
static::assertGreaterThanOrEqual(1, \count($container));
}
/**
* @test
*/
public function findByUidForExistingRecordReturnsModelWithData()
{
$this->importDataSet(__DIR__ . '/../Fixtures/Product/Tea.xml');
$uid = 1;
/** @var Tea $model */
$model = $this->subject->findByUid($uid);
static::assertNotNull($model);
static::assertSame('Earl Grey', $model->getTitle());
static::assertSame('Fresh and hot.', $model->getDescription());
}
}

View file

@ -0,0 +1,71 @@
<?php
declare(strict_types = 1);
namespace OliverKlee\Tea\Tests\Unit\Domain\Model\Product;
use Nimut\TestingFramework\TestCase\UnitTestCase;
use OliverKlee\Tea\Domain\Model\Product\Tea;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* Test case.
*
* @author Oliver Klee <typo3-coding@oliverklee.de
*/
class TeaTest extends UnitTestCase
{
/**
* @var Tea
*/
private $subject = null;
protected function setUp()
{
$this->subject = new Tea();
}
/**
* @test
*/
public function isAbstractEntity()
{
static::assertInstanceOf(AbstractEntity::class, $this->subject);
}
/**
* @test
*/
public function getTitleInitiallyReturnsEmptyString()
{
static::assertSame('', $this->subject->getTitle());
}
/**
* @test
*/
public function setTitleSetsTitle()
{
$value = 'Club-Mate';
$this->subject->setTitle($value);
static::assertSame($value, $this->subject->getTitle());
}
/**
* @test
*/
public function getDescriptionInitiallyReturnsEmptyString()
{
static::assertSame('', $this->subject->getDescription());
}
/**
* @test
*/
public function setDescriptionSetsDescription()
{
$value = 'Club-Mate';
$this->subject->setDescription($value);
static::assertSame($value, $this->subject->getDescription());
}
}

View file

@ -0,0 +1,42 @@
<?php
declare(strict_types = 1);
namespace OliverKlee\Tea\Tests\Unit\Domain\Repository\Product;
use Nimut\TestingFramework\TestCase\UnitTestCase;
use OliverKlee\Tea\Domain\Repository\Product\TeaRepository;
use Prophecy\Prophecy\ProphecySubjectInterface;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Test case.
*
* @author Oliver Klee <typo3-coding@oliverklee.de
*/
class TeaRepositoryTest extends UnitTestCase
{
/**
* @var TeaRepository
*/
private $subject = null;
/**
* @var ObjectManagerInterface|ProphecySubjectInterface
*/
protected $objectManager = null;
protected function setUp()
{
$objectManagerProphecy = $this->prophesize(ObjectManagerInterface::class);
$this->objectManager = $objectManagerProphecy->reveal();
$this->subject = new TeaRepository($this->objectManager);
}
/**
* @test
*/
public function isRepository()
{
static::assertInstanceOf(Repository::class, $this->subject);
}
}

View file

@ -65,6 +65,15 @@
"scripts": {
"ci:php:lint": "find *.php Classes/ Configuration/ Tests/ -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l",
"ci:ts:lint": "typoscript-lint -c Configuration/TsLint.yml --ansi -n --fail-on-warnings -vvv Configuration/TypoScript/",
"ci:tests:unit": "phpunit -c .Build/vendor/nimut/testing-framework/res/Configuration/UnitTests.xml Tests/Unit/",
"ci:tests:functional": "phpunit -c .Build/vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml Tests/Functional/",
"ci:tests": [
"@ci:tests:unit",
"@ci:tests:functional"
],
"ci:dynamic": [
"@ci:tests"
],
"ci:static": [
"@ci:php:lint",
"@ci:ts:lint"

16
ext_tables.sql Normal file
View file

@ -0,0 +1,16 @@
CREATE TABLE tx_tea_domain_model_product_tea (
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
crdate int(11) unsigned DEFAULT '0' NOT NULL,
cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
title varchar(255) DEFAULT '' NOT NULL,
description varchar(2000) DEFAULT '' NOT NULL,
PRIMARY KEY (uid),
KEY parent (pid)
);