diff --git a/.docs/instalace.md b/.docs/instalace.md index b1d2698f9..2ed8849ad 100644 --- a/.docs/instalace.md +++ b/.docs/instalace.md @@ -7,17 +7,20 @@ Aplikace vyžaduje: - Composer - Yarn +Všechny potřebné nástroje jsou instalovány v příslušných kontejnerech +Na hostujícím stroji musí být ve výchozím stavu volné porty 80 a 3306. Port 3306 je možné použít pro propojená s IDE + ## Docker -Pro lokální vývoj je připraven Docker container a konfigurace pro **docker-compose**. +Pro lokální vývoj je připraven Docker container a konfigurace pro **docker compose**. +Všechny potřebné příkazy jsou definované v Make file ```bash -docker volume create --name hskauting_mysql -docker compose up -d # Spustí container v detached modu +make up # Spustí container v detached modu ``` V kontejneru je možné spustit bash pomocným skriptem: ```bash -docker/ssh +make enter ``` ## Nastavení hosts @@ -30,12 +33,4 @@ Stačí přidat tento řádek do souboru `/etc/hosts`: ``` ## Příprava projektu -V kontejneru stačí spustit příkaz `phing init`. - -**Poznámka**: Při commitování se automaticky opravuje coding standard v PHP - to však vyžaduje lokálně nainstalované PHP. -Pokud nemáte mimo kontejner instalované PHP alespoň ve verzi jako používá Hospodaření, -je možné automatickou opravu coding standardu vypnout nastavením proměnné `HUSKY_SKIP_INSTALL` na `true` při instalaci -`yarn` závislostí. Tedy např.: - -- `export HUSKY_SKIP_INSTALL=true; phing init` -- `HUSKY_SKIP_INSTALL=true yarn install` +Stačí spustit příkaz `make init`. \ No newline at end of file diff --git a/.docs/nastroje.md b/.docs/nastroje.md index 8a63bf4a4..03ac76c6b 100644 --- a/.docs/nastroje.md +++ b/.docs/nastroje.md @@ -35,18 +35,27 @@ Pro testování používáme [Codeception](http://codeception.com/). Testy lze spustit příkazem v kontejneru: ```bash +phing tests # Jednotkové + Integrační testy + Akceptační testy phing tests-unit # Pouze jednotkové testy phing tests-integration # Pouze integrační testy -phing tests # Jednotkové + Integrační testy phing tests-acceptance # Akceptační testy ``` +nebo pomocí make přímo na hostujícím stroji: +```bash +make tests-all # Jednotkové + Integrační testy + Akceptační testy +make tests-unit # Pouze jednotkové testy +make tests-integration # Pouze integrační testy +make tests-acceptance # Akceptační testy +``` + + ## Coding standard Coding standard vychází z [Doctrine Coding Standardu](https://github.com/doctrine/coding-standard). Zda je projekt v souladu s CS lze ověřit pomocí příkazu v kontejneru: ```bash -phing coding-standard +phing coding-standard-ci ``` Automaticky lze nechat opravit pomocí: @@ -54,3 +63,9 @@ Automaticky lze nechat opravit pomocí: ```bash ./vendor/bin/phpcbf app ``` + +Nebo opět přímo z hostujícího stroje pomocí make: + +```bash +make coding-standard +``` \ No newline at end of file diff --git a/.docs/tipy-pro-testovani.md b/.docs/tipy-pro-testovani.md index 4339b74cc..63eaab7f9 100644 --- a/.docs/tipy-pro-testovani.md +++ b/.docs/tipy-pro-testovani.md @@ -27,7 +27,7 @@ Jednotlivé testy: vendor/bin/codecept run ``` -Všechny daného typu: +Všechny daného typu lze spustit v kontejneru: ```bash phing tests phing tests-acceptance @@ -35,3 +35,12 @@ phing tests-integration phing tests-unit phing tests-with-coverage ``` + +Pomocí make na hostitelském stroji: +```bash +make tests-all +make tests-acceptance +make tests-integration +make tests-unit +make tests-with-coverage +``` diff --git a/.env.local b/.env.local new file mode 100644 index 000000000..7ce37f24e --- /dev/null +++ b/.env.local @@ -0,0 +1,5 @@ +DB_HOST=mysql +DB_USER=hskauting +DB_PASSWORD=hskauting +DB_NAME=hskauting +DEVELOPMENT_MACHINE=true \ No newline at end of file diff --git a/.env.test b/.env.test new file mode 100644 index 000000000..5c4e4a0f8 --- /dev/null +++ b/.env.test @@ -0,0 +1,5 @@ +DB_HOST=mysql-test +DB_USER=hskauting +DB_PASSWORD=hskauting +DB_NAME=hskauting +DB_TEST=true \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 71450fdcd..268cb275c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,8 +26,6 @@ jobs: restore-keys: | ${{ runner.os }}-composer- - run: composer install - - name: Remove development docker-compose override - run: rm docker-compose.override.yml - name: Fix permissions run: chown 1000:1000 . -R && chmod 777 . -R - name: Create tarball @@ -59,10 +57,6 @@ jobs: runs-on: ubuntu-22.04 needs: workdir steps: - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.1' - name: Download workdir uses: actions/download-artifact@v1 with: @@ -71,8 +65,7 @@ jobs: - name: Extract workdir run: tar -xzpf workdir.tar.gz - name: Start application containers - run: docker-compose up -d mysql-test - - run: docker-compose run -T -u www-data app phing tests-integration + run: docker compose -f docker/docker-compose.yml run -T php-test phing tests-integration collect-code-coverage: name: "Collect code coverage" @@ -87,10 +80,8 @@ jobs: path: . - name: Extract workdir run: tar -xzpf workdir.tar.gz - - name: Start application containers - run: docker-compose up -d app mysql-test - name: Run tests with coverage - run: docker-compose exec -T -u www-data app phing tests-with-coverage + run: docker compose -f docker/docker-compose.yml run -T php-test phing tests-with-coverage - name: Upload code coverage uses: codecov/codecov-action@v3 with: @@ -110,30 +101,15 @@ jobs: path: . - name: Extract workdir run: tar -xzpf workdir.tar.gz - - name: Start application containers - run: docker-compose up -d + - name: Append to .env file + run: echo -e "\nHEADLESS='--headless'" >> .env.test + - name: Init app + run: docker compose -f docker/docker-compose.yml run -T php-test phing app-init + - name: Start selenium container + run: docker compose -f docker/docker-compose.yml up -d selenium nginx - run: mv app/config/config.ci.local.neon app/config/config.local.neon - - name: Install Node.js - uses: actions/setup-node@v3 - with: - node-version: '16' - # Copy & paste from https://github.com/actions/cache/blob/master/examples.md#node---yarn - - name: Get yarn cache - id: yarn-cache - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - run: npm install -g yarn - - name: Build frontend assets - run: yarn install && yarn build - - name: Prepare database - run: docker-compose exec -T -u www-data app bin/console migrations:migrate --no-interaction - - run: docker-compose exec -T -u www-data app phing tests-acceptance + - name: Run acceptance tests + run: docker compose -f docker/docker-compose.yml run -T php-test phing tests-acceptance - uses: actions/upload-artifact@v3 if: failure() with: @@ -143,6 +119,12 @@ jobs: static-analysis: name: "Run PHPStan analysis" runs-on: ubuntu-22.04 + env: + DB_HOST: mysql-test + DB_USER: hskauting + DB_PASSWORD: hskauting + DB_NAME: hskauting + DB_TEST: true container: image: skaut/lebeda:8.1 needs: workdir @@ -172,7 +154,7 @@ jobs: path: . - name: Extract workdir run: tar -xzpf workdir.tar.gz - - run: phing coding-standard + - run: phing coding-standard-ci latte-lint: name: "Lint Latte templates" diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..ff472cc96 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +CONTAINER_PHP=hskauting.app +CONTAINER_PHP_TEST=hskauting.app-test +CONTAINER_DB=hskauting.mysql +CONTAINER_DB_TEST=hskauting.mysql-test + +COMPOSE_FILE=-f docker/docker-compose.yml + +CONSOLE?= + +up: down + docker compose ${COMPOSE_FILE} up -d --force-recreate + +down: + docker compose ${COMPOSE_FILE} down --remove-orphans + +enter: + @docker exec -it ${CONTAINER_PHP} bash + +init: + docker exec -it $(CONTAINER_PHP) composer install + docker exec -it $(CONTAINER_PHP) /app/vendor/bin/phing app-init + +tests-all: + docker exec -it ${CONTAINER_PHP_TEST} /app/vendor/bin/phing tests + +tests-unit: + docker exec -it ${CONTAINER_PHP_TEST} /app/vendor/bin/phing tests-unit + +tests-integration: + docker exec -it ${CONTAINER_PHP_TEST} /app/vendor/bin/phing tests-integration + +tests-acceptance: + docker exec -it ${CONTAINER_PHP_TEST} /app/vendor/bin/phing tests-acceptance + +static-analysis: + docker exec -it ${CONTAINER_PHP} /app/vendor/bin/phing static-analysis + +coding-standard: + docker exec -it ${CONTAINER_PHP} /app/vendor/bin/phing coding-standard diff --git a/app/AccountancyModule/Components/BaseControl.php b/app/AccountancyModule/Components/BaseControl.php index f38505172..da959639a 100644 --- a/app/AccountancyModule/Components/BaseControl.php +++ b/app/AccountancyModule/Components/BaseControl.php @@ -16,6 +16,8 @@ */ abstract class BaseControl extends Control { + public const MAX_FILE_SIZE_VALUE = 15 * 1024 * 1024; + abstract public function render(): void; public function getPresenter(): BasePresenter|null diff --git a/app/AccountancyModule/Components/Cashbook/ChitScanControl.php b/app/AccountancyModule/Components/Cashbook/ChitScanControl.php index 000f4a9cc..0e6e497bf 100644 --- a/app/AccountancyModule/Components/Cashbook/ChitScanControl.php +++ b/app/AccountancyModule/Components/Cashbook/ChitScanControl.php @@ -81,7 +81,7 @@ protected function createComponentUploadForm(): BaseForm BaseForm::MIME_TYPE, 'Neplatný formát skenu, povolené formáty jsou ' . implode(', ', array_keys(IScanStorage::ALLOWED_MIME_TYPES)) . '.', IScanStorage::ALLOWED_MIME_TYPES, - )->addRule(BaseForm::MAX_FILE_SIZE, 'Maximální povolená velikost souboru je 15 MB', 15 * 1024 * 1024); + )->addRule(BaseForm::MAX_FILE_SIZE, 'Maximální povolená velikost souboru je 15 MB', BaseControl::MAX_FILE_SIZE_VALUE); $form->addSubmit('submit', 'Nahrát'); diff --git a/app/AccountancyModule/TravelModule/Components/RoadworthyControl.php b/app/AccountancyModule/TravelModule/Components/RoadworthyControl.php index e0b78f42f..a471b5d05 100644 --- a/app/AccountancyModule/TravelModule/Components/RoadworthyControl.php +++ b/app/AccountancyModule/TravelModule/Components/RoadworthyControl.php @@ -65,7 +65,7 @@ protected function createComponentUploadForm(): BaseForm BaseForm::MIME_TYPE, 'Neplatný formát skenu, povolené formáty jsou ' . implode(', ', array_keys(IScanStorage::ALLOWED_MIME_TYPES)) . '.', IScanStorage::ALLOWED_MIME_TYPES, - )->addRule(BaseForm::MAX_FILE_SIZE, 'Maximální povolená velikost souboru je 15 MB', 15 * 1024 * 1024); + )->addRule(BaseForm::MAX_FILE_SIZE, 'Maximální povolená velikost souboru je 15 MB', BaseControl::MAX_FILE_SIZE_VALUE); $form->addSubmit('submit', 'Ok'); diff --git a/app/Console/MigrationsDropCommand.php b/app/Console/MigrationsDropCommand.php new file mode 100644 index 000000000..fac324b97 --- /dev/null +++ b/app/Console/MigrationsDropCommand.php @@ -0,0 +1,64 @@ +setName(self::$defaultName) + ->setDescription('Drops all tables from the database'); + } + + /** @throws Exception */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + if (getenv('DB_TEST') !== 'true') { + $output->writeln('Cannot run on non testing environment'); + + return Command::FAILURE; + } + + $conn = $this->em->getConnection(); + $schemaManager = $conn->getSchemaManager(); + + // Drop tables + $tables = $schemaManager->listTables(); + $conn->executeStatement('SET foreign_key_checks = 0'); + foreach ($tables as $table) { + $tableName = $table->getName(); + $conn->executeStatement(sprintf('DROP TABLE %s', $tableName)); + $output->writeln(sprintf('Dropped table %s', $tableName)); + } + + // Drop views + $views = $schemaManager->listViews(); + foreach ($views as $view) { + $viewName = $view->getName(); + $conn->executeStatement(sprintf('DROP VIEW %s', $viewName)); + $output->writeln(sprintf('Dropped view %s', $viewName)); + } + + return Command::SUCCESS; + } +} diff --git a/app/bootstrap.php b/app/bootstrap.php index 46a625a8d..c91e3e4d1 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use Nette\Bootstrap\Configurator; + require __DIR__ . '/../vendor/autoload.php'; setlocale(LC_COLLATE, 'cs_CZ.utf8'); @@ -11,9 +13,11 @@ putenv('TMPDIR=' . $tempDir); -$configurator = new Nette\Configurator(); +$env = getenv(); + +$configurator = new Configurator(); $configurator->setDebugMode(getenv('DEVELOPMENT_MACHINE') === 'true' ?: '89.177.225.213'); -$configurator->enableDebugger($logDir); +$configurator->enableTracy($logDir); $configurator->setTempDirectory($tempDir); $configurator->createRobotLoader() @@ -21,6 +25,7 @@ ->register(true); $configurator->addConfig(__DIR__ . '/config/config.neon'); +$configurator->addDynamicParameters(['env' => $env]); $configurator->addConfig(__DIR__ . '/config/config.local.neon'); $configurator->addParameters(['logDir' => $logDir]); diff --git a/app/config/config.neon b/app/config/config.neon index 2f54f178a..990faf0ad 100644 --- a/app/config/config.neon +++ b/app/config/config.neon @@ -52,6 +52,7 @@ extensions: skautis: Skautis\Nette\SkautisExtension messenger: Fmasa\Messenger\DI\MessengerExtension console: Contributte\Console\DI\ConsoleExtension(%consoleMode%) + console.cache: Contributte\Console\Extra\DI\CacheConsoleExtension(%consoleMode%) search: command handlers: @@ -110,6 +111,8 @@ services: - Model\Common\Services\MessengerCommandBus(@messenger.commandBus.bus) - Model\Common\Services\MessengerQueryBus(@messenger.queryBus.bus) - Model\Common\Services\MessengerEventBus(@messenger.eventBus.bus) + - Console\MigrationsDropCommand + skautisCache: factory: Nette\Caching\Cache(namespace: 'skautis') diff --git a/app/config/config.sample.local.neon b/app/config/config.sample.local.neon index 41fc20757..370550f07 100644 --- a/app/config/config.sample.local.neon +++ b/app/config/config.sample.local.neon @@ -21,10 +21,10 @@ parameters: redirectUri: http://moje-hospodareni.cz/google/token database: - host: mysql - user: root - password: root - name: hskauting + host: %env.DB_HOST% + user: %env.DB_USER% + password: %env.DB_PASSWORD% + name: %env.DB_NAME% services: # Pro produkci diff --git a/app/config/model/doctrine.neon b/app/config/model/doctrine.neon index f4c190803..8965a9511 100644 --- a/app/config/model/doctrine.neon +++ b/app/config/model/doctrine.neon @@ -1,6 +1,7 @@ parameters: mappingDir: %appDir%/model/Infrastructure/mapping domainModelDir: %appDir%/model + cacheDir: %tempDir%/cache extensions: migrations: Nettrine\Migrations\DI\MigrationsExtension @@ -75,7 +76,7 @@ dbal: commented: true services: - - Model\Infrastructure\EntityManagerFactory(%debugMode%, %tempDir%) + - Model\Infrastructure\EntityManagerFactory(%debugMode%, %cacheDir%) - @Model\Infrastructure\EntityManagerFactory::create() - factory: Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand diff --git a/build.xml b/build.xml index 7e34c2875..fc2b34ac9 100644 --- a/build.xml +++ b/build.xml @@ -196,6 +196,12 @@ + + + + + + @@ -205,12 +211,19 @@ - + + + + + + + + @@ -220,15 +233,25 @@ + + + + + + + + + + - + @@ -240,6 +263,6 @@ - + diff --git a/codeception.yml b/codeception.yml index c8e6e3390..2cfb92b4f 100644 --- a/codeception.yml +++ b/codeception.yml @@ -1,4 +1,6 @@ actor: Tester +params: + - .env.test coverage: enabled: true @@ -10,7 +12,7 @@ paths: log: tests/_output data: tests/_data support: tests/_support - envs: tests/_envs + bootstrap: _bootstrap.php settings: colors: true diff --git a/composer.json b/composer.json index d74106266..372e60529 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,7 @@ "type": "project", "require": { "php": ">= 8.1", + "ext-iconv": "*", "beberlei/assert": "^2.9", "beberlei/doctrineextensions": "^1.3", "cakephp/chronos": "^2.3", @@ -10,6 +11,7 @@ "consistence-community/consistence": "^2.1", "consistence-community/consistence-doctrine": "^2.1", "contributte/console": "^0.9", + "contributte/console-extra": "^0.7.2", "fmasa/messenger": "^2.0", "fmasa/sentry-breadcrumbs-monolog-handler": "^2.3", "google/apiclient": "^2.12", @@ -48,6 +50,7 @@ "thecodingmachine/safe": "^2.4", "tracy/tracy": "~2.9", "ublaboo/datagrid": "^v6.9", + "vlucas/phpdotenv": "^5.5", "warhuhn/chronos-doctrine": "^3.0" }, "require-dev": { @@ -67,7 +70,8 @@ "phpstan/phpstan-mockery": "^1.0", "phpstan/phpstan-nette": "^1.0", "roave/security-advisories": "dev-master", - "squizlabs/php_codesniffer": "^3.6" + "squizlabs/php_codesniffer": "^3.6", + "ext-pdo": "*" }, "autoload-dev": { "classmap": ["code-quality/", "tests/_hacks"] diff --git a/composer.lock b/composer.lock index 6f1161782..2757241a7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "46111817e48fd153535bbc50e08cfe80", + "content-hash": "b02f03318dfb54663bc3514bab2c767f", "packages": [ { "name": "beberlei/assert", @@ -642,6 +642,89 @@ ], "time": "2023-05-24T08:48:41+00:00" }, + { + "name": "contributte/console-extra", + "version": "v0.7.2", + "source": { + "type": "git", + "url": "https://github.com/contributte/console-extra.git", + "reference": "2deeadc71090e3483feeca41200a4179d7c589ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/contributte/console-extra/zipball/2deeadc71090e3483feeca41200a4179d7c589ef", + "reference": "2deeadc71090e3483feeca41200a4179d7c589ef", + "shasum": "" + }, + "require": { + "contributte/di": "^0.5.0", + "php": ">=7.2", + "symfony/console": "^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "conflict": { + "nette/schema": "<1.0.1" + }, + "require-dev": { + "contributte/console": "~0.7", + "latte/latte": "^2.6.0", + "nette/application": "^3.1.3", + "nette/bootstrap": "^3.1.1", + "nette/caching": "^3.1.1", + "nette/security": "^3.1.1", + "ninjify/nunjuck": "^0.4", + "ninjify/qa": "^0.13", + "phpstan/phpstan": "^1.0.0", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-nette": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.0.0" + }, + "suggest": { + "contributte/console": "Symfony\\Console for Nette" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Contributte\\Console\\Extra\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Milan Felix Šulc", + "homepage": "https://f3l1x.io" + } + ], + "description": "Nette-based console commands for latte, DIC, security, utils and many others", + "homepage": "https://github.com/contributte/console-extra", + "keywords": [ + "console", + "nette", + "symfony" + ], + "support": { + "issues": "https://github.com/contributte/console-extra/issues", + "source": "https://github.com/contributte/console-extra/tree/v0.7.2" + }, + "funding": [ + { + "url": "https://contributte.org/partners.html", + "type": "custom" + }, + { + "url": "https://github.com/f3l1x", + "type": "github" + } + ], + "time": "2022-06-15T07:40:08+00:00" + }, { "name": "contributte/di", "version": "v0.5.5", @@ -2309,6 +2392,68 @@ }, "time": "2023-04-05T15:11:57+00:00" }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2023-02-25T20:23:15+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "6.5.8", @@ -5957,6 +6102,81 @@ }, "time": "2023-06-14T22:48:31+00:00" }, + { + "name": "phpoption/phpoption", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2023-02-25T19:38:58+00:00" + }, { "name": "phpseclib/phpseclib", "version": "3.0.19", @@ -9180,6 +9400,90 @@ ], "time": "2023-07-12T15:19:41+00:00" }, + { + "name": "vlucas/phpdotenv", + "version": "v5.5.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "5.5-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2022-10-16T01:01:54+00:00" + }, { "name": "warhuhn/chronos-doctrine", "version": "3.0.1", @@ -13284,8 +13588,11 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">= 8.1" + "php": ">= 8.1", + "ext-iconv": "*" + }, + "platform-dev": { + "ext-pdo": "*" }, - "platform-dev": [], "plugin-api-version": "2.3.0" } diff --git a/docker-compose.override.yml b/docker-compose.override.yml deleted file mode 100644 index edae19409..000000000 --- a/docker-compose.override.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3.4" - -services: - app: - ports: - - 80:80 - volumes: - - /var/www/html/temp - - /var/www/html/tests/_temp -# - nfsmount:/var/www/html - mysql: - ports: - - 3306:3306 - - adminer: - container_name: hskauting.adminer - image: adminer - ports: - - 8080:8080 - networks: - main: - aliases: - - mysql -volumes: - mysql: - name: hskauting_mysql - external: true diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 1d481bc80..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,56 +0,0 @@ -version: '3.4' - -services: - app: - container_name: hskauting.app - image: skaut/lebeda:8.1 - volumes: - - www:/var/www - - .:/var/www/html - depends_on: - - mysql - environment: - DEVELOPMENT_MACHINE: 'true' - networks: - main: - aliases: - - moje-hospodareni.cz - - mysql: - container_name: hskauting.mysql - image: mysql:8.0 - volumes: - - mysql:/var/lib/mysql - environment: - MYSQL_ROOT_PASSWORD: 'root' - MYSQL_DATABASE: hskauting - networks: - main: - aliases: - - mysql - - mysql-test: - container_name: hskauting.mysql-test - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: 'root' - MYSQL_DATABASE: hskauting - networks: - main: - aliases: - - mysql-test - - chrome: - container_name: hskauting.chrome - image: selenium/standalone-chrome:3.141 - networks: - main: - aliases: - - chrome - -volumes: - mysql: - www: - -networks: - main: diff --git a/docker/containers/adminer/import/.gitkeep b/docker/containers/adminer/import/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/docker/containers/adminer/ini/0-upload_large_dumps.ini b/docker/containers/adminer/ini/0-upload_large_dumps.ini new file mode 100644 index 000000000..952ab3edd --- /dev/null +++ b/docker/containers/adminer/ini/0-upload_large_dumps.ini @@ -0,0 +1,5 @@ +upload_max_filesize = 512M +post_max_size = 512M +memory_limit = 2G +max_execution_time = 600 +max_input_vars = 5000 \ No newline at end of file diff --git a/docker/containers/adminer/plugins/import-directory.php b/docker/containers/adminer/plugins/import-directory.php new file mode 100644 index 000000000..f57b90913 --- /dev/null +++ b/docker/containers/adminer/plugins/import-directory.php @@ -0,0 +1,14 @@ +username = getenv('ADMINER_USERNAME') ?: null; + $this->password = getenv('ADMINER_PASSWORD') ?: ''; + } + + /** @return array */ + public function credentials(): array + { + $inputUsername = $_GET['username']; + + if ($this->isPasswordlessUser($inputUsername)) { + return [SERVER, $inputUsername, $this->password]; + } + + return [SERVER, $inputUsername, get_password()]; + } + + public function login(string $username, string $password): bool + { + if ($this->isPasswordlessUser($username)) { + return true; + } + + if ($password === '') { + return lang('Adminer does not support accessing a database without a password, more information.', target_blank()); + } + + return true; + } + + public function isPasswordlessUser(string $username): bool + { + return $this->username !== null && $this->username === $username; + } +} diff --git a/docker/containers/adminer/plugins/readable-dates.php b/docker/containers/adminer/plugins/readable-dates.php new file mode 100644 index 000000000..ca1656caa --- /dev/null +++ b/docker/containers/adminer/plugins/readable-dates.php @@ -0,0 +1,50 @@ +prepend = <<'; + tds[i].innerHTML = tds[i].newDate; + tds[i].dateIsNew = true; + + tds[i].addEventListener('click', function(event) { + this.innerHTML = (this.dateIsNew ? this.oldDate : this.newDate); + this.dateIsNew = !this.dateIsNew; + }); + } + } +}); + +EOT; + } + + public function head(): void + { + echo script($this->prepend); + } +} diff --git a/docker/containers/mysql-test/my.cnf b/docker/containers/mysql-test/my.cnf new file mode 100644 index 000000000..6a5608d07 --- /dev/null +++ b/docker/containers/mysql-test/my.cnf @@ -0,0 +1,3 @@ +[mysqld] +collation-server = utf8mb4_czech_ci +character-set-server = utf8mb4 \ No newline at end of file diff --git a/docker/containers/mysql/my.cnf b/docker/containers/mysql/my.cnf new file mode 100644 index 000000000..6a5608d07 --- /dev/null +++ b/docker/containers/mysql/my.cnf @@ -0,0 +1,3 @@ +[mysqld] +collation-server = utf8mb4_czech_ci +character-set-server = utf8mb4 \ No newline at end of file diff --git a/docker/containers/nginx/conf.d/app.conf b/docker/containers/nginx/conf.d/app.conf new file mode 100644 index 000000000..c44083963 --- /dev/null +++ b/docker/containers/nginx/conf.d/app.conf @@ -0,0 +1,38 @@ +resolver 127.0.0.11; + +map $cookie_XDEBUG_SESSION $fastcgi_pass { + default php; + PHPSTORM php-xdebug; +} + +map $cookie_SELENIUM $fastcgi_pass { + default php; + SELENIUM php-test; +} + +server { + + + listen 80; + server_name moje-hospodareni.cz; + client_max_body_size 128M; + + root /app/www; + index index.php; + + location / { + try_files $uri /index.php$is_args$args; + } + + location ~ \.php$ { + try_files $uri =404; + + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass $fastcgi_pass:9000; + fastcgi_index index.php; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } +} diff --git a/docker/containers/php/Dockerfile b/docker/containers/php/Dockerfile new file mode 100644 index 000000000..2cc842943 --- /dev/null +++ b/docker/containers/php/Dockerfile @@ -0,0 +1,7 @@ +FROM thecodingmachine/php:8.1-v4-fpm-node16 + +USER root +USER docker + +ENV PATH="/app/vendor/bin:/app/bin:${PATH}" + diff --git a/docker/containers/php/ini/image.ini b/docker/containers/php/ini/image.ini new file mode 100644 index 000000000..364f38f53 --- /dev/null +++ b/docker/containers/php/ini/image.ini @@ -0,0 +1,26 @@ +# Highly configurable php container with node +# https://github.com/thecodingmachine/docker-images-php +TZ=Europe/Prague + +PHP_EXTENSION_GD=1 +PHP_EXTENSION_CURL=1 +PHP_EXTENSION_OAUTH=1 +PHP_EXTENSION_AMQP=1 +PHP_EXTENSION_DOM=1 +PHP_EXTENSION_JSON=1 +PHP_EXTENSION_SIMPLEXML=1 +PHP_EXTENSION_ICONV=1 +PHP_EXTENSION_ZIP=1 +PHP_EXTENSION_BCMATH=1 +PHP_EXTENSION_GEOIP=1 +PHP_EXTENSION_SOCKETS=1 +PHP_EXTENSION_FILEINFO=1 +PHP_EXTENSION_INTL=1 +PHP_EXTENSION_IMAGICK=1 + +PHP_INI_MEMORY_LIMIT=1024M +PHP_INI_MAX_EXECUTION_TIME=300 +PHP_INI_ERROR_REPORTING=E_ALL + +#upload +PHP_INI_UPLOAD_MAX_FILESIZE=15M \ No newline at end of file diff --git a/docker/containers/php/ini/php.ini b/docker/containers/php/ini/php.ini new file mode 100644 index 000000000..e69de29bb diff --git a/docker/containers/php/ini/xdebug.ini b/docker/containers/php/ini/xdebug.ini new file mode 100644 index 000000000..ddedb0dfc --- /dev/null +++ b/docker/containers/php/ini/xdebug.ini @@ -0,0 +1,9 @@ +PHP_EXTENSION_XDEBUG=1 + +PHP_INI_XDEBUG__DISCOVER_CLIENT_HOST=1 +PHP_INI_XDEBUG__CLIENT_AUTOSTART=0 +PHP_INI_XDEBUG__CLIENT_ENABLE=1 +PHP_INI_XDEBUG__CLIENT_HANDLER=dbgp +PHP_INI_XDEBUG__CLIENT_HOST=host.docker.internal +PHP_INI_XDEBUG__CLIENT_MODE=req +PHP_INI_XDEBUG__CLIENT_PORT=9003 \ No newline at end of file diff --git a/docker/containers/traefik/traefik.yml b/docker/containers/traefik/traefik.yml new file mode 100644 index 000000000..e591b0678 --- /dev/null +++ b/docker/containers/traefik/traefik.yml @@ -0,0 +1,21 @@ +log: + level: DEBUG + +api: + dashboard: true + debug: true + +entryPoints: + web: + address: ":80" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: traefik + watch: true + + file: + watch: true + filename: /traefik.yml \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..262105608 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,160 @@ +version: "3.8" +name: hskauting + +networks: + traefik: + +volumes: + mysql: + mysql-test: + +x-php-base: + &php-base + build: + context: ./containers/php + working_dir: /app + env_file: + - ./containers/php/ini/image.ini + networks: + - traefik + volumes: + - ../:/app + - ./containers/php/ini/php.ini:/etc/php/8.1/fpm/conf.d/php.ini + - ./containers/php/ini/php.ini:/etc/php/8.1/cli/conf.d/php.ini + +x-mysql-base: + &mysql-base + image: mysql:8.0 + networks: + - traefik + environment: + MYSQL_DATABASE: hskauting + MYSQL_USER: hskauting + MYSQL_PASSWORD: hskauting + MYSQL_ROOT_PASSWORD: root + MYSQL_COLLATION: utf8mb4_czech_ci + MYSQL_CHARACTER_SET: utf8 + +services: + traefik: + image: traefik:latest + restart: unless-stopped + networks: + - traefik + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./containers/traefik/traefik.yml:/traefik.yml:ro + labels: + traefik.enable: true + traefik.http.routers.api.rule: Host(`traefik.localhost`) + traefik.http.routers.api.entrypoints: web + traefik.http.routers.api.service: api@internal + + php: + <<: *php-base + container_name: hskauting.app #Backward Compatibility + env_file: + - containers/php/ini/image.ini + - ./../.env.local + depends_on: + - mysql + labels: + traefik.enable: true + traefik.http.routers.php.entrypoints: web + + php-xdebug: + <<: *php-base + hostname: php-xdebug.localhost + depends_on: + - mysql + env_file: + - containers/php/ini/image.ini + - containers/php/ini/xdebug.ini + - ./../.env.local + + php-test: + <<: *php-base + hostname: php-test.localhost + container_name: hskauting.app-test + depends_on: + - mysql-test + env_file: + - containers/php/ini/image.ini + - containers/php/ini/xdebug.ini + - ./../.env.test + + nginx: + image: nginx:latest + restart: unless-stopped + working_dir: / + hostname: moje-hospodareni.cz + networks: + - traefik + volumes: + - ../:/app + - ./containers/nginx/conf.d:/etc/nginx/conf.d + labels: + traefik.enable: true + traefik.http.routers.nginx.rule: Host(`moje-hospodareni.cz`) + traefik.http.routers.nginx.entrypoints: web + depends_on: + - php + - php-test + + mysql: + <<: *mysql-base + container_name: hskauting.mysql #Backward Compatibility + ports: + - "3306:3306" + hostname: mysql.localhost + volumes: + - mysql:/var/lib/mysql + - ./containers/mysql/my.cnf:/etc/mysql/conf.d/my.cnf + + mysql-test: + container_name: hskauting.mysql-test #Backward Compatibility + <<: *mysql-base + hostname: mysql-test.localhost + volumes: + - mysql-test:/var/lib/mysql + - ./containers/mysql-test/my.cnf:/etc/mysql/conf.d/my.cnf + + selenium: + container_name: hskauting.chrome + hostname: selenium.localhost + image: selenium/standalone-chrome + shm_size: 2g + environment: + SE_VNC_NO_PASSWORD: 1 + networks: + - traefik + labels: + traefik.enable: true + traefik.http.routers.selenium.rule: Host(`selenium.localhost`) + traefik.http.routers.selenium.entrypoints: web + traefik.http.services.selenium.loadbalancer.server.port: 7900 + + adminer: + image: adminer:4.8.1 + volumes: + - ./containers/adminer/plugins/import-directory.php:/var/www/html/plugins/import-directory.php + - ./containers/adminer/plugins/login-password-less.php:/var/www/html/plugins/login-password-less.php + - ./containers/adminer/plugins/readable-dates.php:/var/www/html/plugins/readable-dates.php + - ./containers/adminer/import:/var/www/html/import + environment: + ADMINER_PLUGINS: import-directory readable-dates + ADMINER_IMPORT_SERVER_PATH: import/adminer.sql + ADMINER_USERNAME: hskauting + ADMINER_PASSWORD: hskauting + ADMINER_DEFAULT_SERVER: mysql + networks: + - traefik + labels: + traefik.enable: true + traefik.http.routers.adminer.entrypoints: web + traefik.http.routers.adminer.rule: Host(`adminer.localhost`) + traefik.http.services.adminer.loadbalancer.server.port: 8080 + depends_on: + - mysql diff --git a/docker/php/xdebug.ini b/docker/php/xdebug.ini deleted file mode 100644 index 836de5af8..000000000 --- a/docker/php/xdebug.ini +++ /dev/null @@ -1,7 +0,0 @@ -xdebug.idekey = storm -xdebug.default_enable = 0 -xdebug.remote_enable = On -xdebug.remote_autostart = On -xdebug.remote_connect_back = 1 -xdebug.profiler_enable = 0 -xdebug.remote_host = dockerhost diff --git a/docker/ssh b/docker/ssh deleted file mode 100755 index 163bc14bc..000000000 --- a/docker/ssh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -function compat() { - if hash winpty 2>/dev/null; then - winpty "$@" - else - $@ - fi -} - -compat "docker exec -u www-data -it hskauting.app bash" diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php index ebf0ad51f..eec2e23b3 100644 --- a/tests/_bootstrap.php +++ b/tests/_bootstrap.php @@ -11,5 +11,5 @@ (new RobotLoader()) ->addDirectory(__DIR__ . '/../app') ->addDirectory(__DIR__) - ->setTempDirectory(__DIR__ . '/_temp') + ->setTempDirectory(__DIR__ . '/../temp/cache') ->register(); diff --git a/tests/_data/acceptance_init.sql b/tests/_data/acceptance_init.sql new file mode 100644 index 000000000..674a29ea9 --- /dev/null +++ b/tests/_data/acceptance_init.sql @@ -0,0 +1,4 @@ +INSERT INTO google_oauth (id, unit_id, email, token, updated_at) +VALUES ('42288e92-27fb-453c-9904-36a7ebd14fe2', 27266, 'test@hospodareni.loc', '1//02yV7BM31saaQCgYIAPOOREPSNwF-L9Irbcw-iJEHRUnfxt2KULTjXQkPI-jl8LEN-SwVp6OybduZT21RiDf7RZBA4ZoZu86UXC8', '2017-06-15 00:00:00'); +INSERT INTO pa_bank_account (name, unit_id, token, created_at, allowed_for_subunits, number_prefix, number_number, number_bank_code) +VALUES ('Acceptance', 27266, NULL, '2017-08-24 00:00:00', 1, NULL, '2000942144', '2010'); diff --git a/tests/_data/cleanup.sql b/tests/_data/cleanup.sql deleted file mode 100644 index 1e5df0983..000000000 --- a/tests/_data/cleanup.sql +++ /dev/null @@ -1,11 +0,0 @@ -DELETE FROM ac_cashbook; -DELETE FROM ac_chits; -DELETE FROM ac_chits_item; -DELETE FROM ac_camp_cashbooks; -DELETE FROM ac_event_cashbooks; -DELETE FROM ac_unit_cashbooks; -DELETE FROM ac_units; -DELETE FROM pa_bank_account; -DELETE FROM pa_group; -DELETE FROM pa_group_unit; -DELETE FROM pa_payment; diff --git a/tests/_support/AcceptanceTester.php b/tests/_support/AcceptanceTester.php index 87f47c9ab..77e764811 100644 --- a/tests/_support/AcceptanceTester.php +++ b/tests/_support/AcceptanceTester.php @@ -49,6 +49,7 @@ public function login(string $role) : void $I->fillField('(//input)[9]', self::LOGIN); $I->fillField('(//input)[10]', self::PASSWORD); $I->click('//button'); +// $I->click('Send anyway'); // bypass ssl redirect $I->waitForText('Seznam akcí'); $roleButtonSelector = "//button[contains(@class, 'ui--current-role')]"; diff --git a/tests/_support/IntegrationTest.php b/tests/_support/IntegrationTest.php index 62958caad..8063c5197 100644 --- a/tests/_support/IntegrationTest.php +++ b/tests/_support/IntegrationTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Codeception\Exception\ModuleException; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Tools\SchemaTool; @@ -28,6 +29,20 @@ protected function getTestedAggregateRoots() : array return []; } + /** + * @return void + * @throws ModuleException + */ + public function _setUp() + { + /** @var Contributte\Codeception\Module\NetteDIModule $module */ + $module = $this->getModule('Contributte\Codeception\Module\NetteDIModule'); + $module->onCreateConfigurator[] = function (\Nette\Bootstrap\Configurator $configurator){ + $configurator->addDynamicParameters(['env' => getenv()]); + }; + parent::_setUp(); + } + protected function _before() : void { $this->entityManager = $this->tester->grabService(EntityManager::class); diff --git a/tests/acceptance.suite.yml b/tests/acceptance.suite.yml index 9f076fc77..a70bb76f8 100644 --- a/tests/acceptance.suite.yml +++ b/tests/acceptance.suite.yml @@ -6,22 +6,34 @@ class_name: AcceptanceTester modules: - enabled: - - WebDriver: - url: http://moje-hospodareni.cz - browser: chrome - host: chrome - capabilities: - chromeOptions: - args: ["--headless", "--disable-gpu", "--no-sandbox", "--window-size=1920,1080"] - binary: "/usr/bin/google-chrome" + enabled: + - WebDriver: + url: 'http://moje-hospodareni.cz' # Base URL for tests + browser: 'chrome' # Browser used for testing + host: 'selenium.localhost' # Selenium server host + wait: 10 # Max wait time in seconds for elements + capabilities: + acceptInsecureCerts: true # Accept self-signed SSL certificates + chromeOptions: + args: + - 'allow-insecure-localhost' # Allow insecure connections to localhost + - '--ignore-ssl-errors=yes' # Ignore SSL errors + - '--ignore-certificate-errors' # Ignore certificate errors + - '%HEADLESS%' # Turn off UI + - '--disable-infobars' + - '--disable-gpu' # Disable GPU acceleration + - '--no-sandbox' # Disable Chrome's sandboxing + - '--window-size=1920,1080' # Set the window size + - "--unsafely-treat-insecure-origin-as-secure=http://moje-hospodareni.cz" - - \Helper\Acceptance - - \Helper\WaitForDb - - Db: - dsn: 'mysql:host=mysql-test;dbname=hskauting' - user: 'root' - password: 'root' - populate: true - dump: 'tests/_data/cleanup.sql' + - \Helper\Acceptance + + - \Helper\WaitForDb + - Db: + dsn: "mysql:host=%DB_HOST%;dbname=%DB_NAME%" + user: "%DB_USER%" + password: "%DB_PASSWORD%" + populate: true + dump: 'tests/_data/acceptance_init.sql' + wait: 2 \ No newline at end of file diff --git a/tests/acceptance/BaseAcceptanceCest.php b/tests/acceptance/BaseAcceptanceCest.php new file mode 100644 index 000000000..d69d4e944 --- /dev/null +++ b/tests/acceptance/BaseAcceptanceCest.php @@ -0,0 +1,21 @@ +amOnPage('/'); + $I->setCookie('SELENIUM', 'SELENIUM', [ + 'domain' => '.moje-hospodareni.cz', + 'secure' => false, + 'httpOnly' => false, + ], true); + } +} diff --git a/tests/acceptance/CreateCashbookCest.php b/tests/acceptance/CreateCashbookCest.php new file mode 100644 index 000000000..f26952314 --- /dev/null +++ b/tests/acceptance/CreateCashbookCest.php @@ -0,0 +1,48 @@ +login(AcceptanceTester::UNIT_LEADER_ROLE); + + $I->click('Jednotka'); + $I->amGoingTo('Create first unit cashbook - for current year'); + $this->createCashbook($I, (int) date('Y')); + $I->amGoingTo('Create cashbook for different year'); + $I->click('#unit-cashbook-menu'); + $createCashbookButton = 'Přidat pokladní knihu'; + $I->waitForText($createCashbookButton); + $I->click($createCashbookButton); + $this->fillModalAndSubmit($I, (int) date('Y') + 1); + } + + private function createCashbook(AcceptanceTester $I, int $year): void + { + $I->click('Založit novou pokladní knihu'); + $this->fillModalAndSubmit($I, $year); + } + + private function fillModalAndSubmit(AcceptanceTester $I, int $year): void + { + $I->waitForText('Vyberte rok'); + $I->selectOption('Rok', $year); + $I->click('Založit', '.modal-footer'); + + $I->see('Pokladní kniha byla vytvořena'); + $I->see((string) $year); + } +} diff --git a/tests/acceptance/CreateUnitCashbookCept.php b/tests/acceptance/CreateUnitCashbookCept.php deleted file mode 100644 index 5bc5675ef..000000000 --- a/tests/acceptance/CreateUnitCashbookCept.php +++ /dev/null @@ -1,44 +0,0 @@ -login(AcceptanceTester::UNIT_LEADER_ROLE); - -function fillModalAndSubmit(AcceptanceTester $I, int $year): void -{ - $I->waitForText('Vyberte rok'); - $I->selectOption('Rok', $year); - $I->click('Založit', '.modal-footer'); - - $I->see('Pokladní kniha byla vytvořena'); - $I->see((string) $year); -} - -$I->click('Jednotka'); - -$I->amGoingTo('Create first unit cashbook - for current year'); - -$I->click('Založit novou pokladní knihu'); - -fillModalAndSubmit($I, (int) date('Y')); - -$I->amGoingTo('Create cashbook for different year'); -$I->click('#unit-cashbook-menu'); - -$createCashbookButton = 'Přidat pokladní knihu'; -$I->waitForText($createCashbookButton); -$I->click($createCashbookButton); - -fillModalAndSubmit($I, (int) date('Y') + 1); diff --git a/tests/acceptance/CashbookTest.php b/tests/acceptance/EventCashbookCest.php similarity index 72% rename from tests/acceptance/CashbookTest.php rename to tests/acceptance/EventCashbookCest.php index b0a2a9155..ad3040517 100644 --- a/tests/acceptance/CashbookTest.php +++ b/tests/acceptance/EventCashbookCest.php @@ -6,7 +6,6 @@ use AcceptanceTester; use Cake\Chronos\Date; -use Codeception\Test\Unit; use Facebook\WebDriver\WebDriverKeys; use Model\Cashbook\Operation; @@ -14,24 +13,28 @@ use function sprintf; use function time; -class CashbookTest extends Unit +// phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps +class EventCashbookCest extends BaseAcceptanceCest { private const BALANCE_SELECTOR = '.ui--balance'; private const NO_CHITS_MESSAGE = 'žádné doklady'; - protected AcceptanceTester $tester; - + protected AcceptanceTester $I; private string $eventName; - protected function _before(): void + public function _before(AcceptanceTester $I): void { + parent::_before($I); + $this->eventName = 'Acceptance test event ' . time(); + $this->I = $I; + + $I->login(AcceptanceTester::UNIT_LEADER_ROLE); } - public function test(): void + /** @group cashbook */ + public function createEventCashbook(): void { - $this->tester->login($this->tester::UNIT_LEADER_ROLE); - $this->createEvent(); $this->goToCashbookPage(); $this->createExpenseChit(); @@ -43,7 +46,7 @@ public function test(): void private function createEvent(): void { - $I = $this->tester; + $I = $this->I; $I->amGoingTo('create event'); $I->click('Založit novou akci'); @@ -69,7 +72,7 @@ private function createEvent(): void private function goToCashbookPage(): void { - $I = $this->tester; + $I = $this->I; $I->amGoingTo('open cashbook'); $cashbookButton = 'Evidence plateb'; @@ -81,7 +84,7 @@ private function goToCashbookPage(): void private function createExpenseChit(): void { - $I = $this->tester; + $I = $this->I; $I->click('Nový doklad'); $I->amGoingTo('create expense chit'); @@ -95,7 +98,7 @@ private function createExpenseChit(): void private function editExpenseChit(): void { - $I = $this->tester; + $I = $this->I; $I->wantTo('Update expense chit amount'); $I->scrollTo('h4[id="chitList-payment"]'); $I->click('.ui--editChit'); @@ -109,7 +112,7 @@ private function editExpenseChit(): void private function addIncomeChit(): void { - $I = $this->tester; + $I = $this->I; $I->click('Nový doklad'); $I->amGoingTo('add income chit'); @@ -122,7 +125,7 @@ private function addIncomeChit(): void private function removeBothChits(): void { - $I = $this->tester; + $I = $this->I; $I->amGoingTo('remove both chits'); $I->scrollTo('h4[id="chitList-payment"]'); @@ -135,7 +138,7 @@ private function removeBothChits(): void private function cancelEvent(): void { - $I = $this->tester; + $I = $this->I; $I->amGoingTo('cancel the event'); $I->click('Akce'); @@ -151,28 +154,28 @@ private function cancelEvent(): void private function fillChitForm(Date $date, string $purpose, Operation $type, string $category, string $recipient, string $amount): void { - $this->tester->wait(2); // unroll block - $this->tester->wantToTest('Uložit'); - $this->tester->fillField('Datum', $date->format('d.m. Y')); - $this->tester->pressKey('body', [WebDriverKeys::ESCAPE]); // close datepicker - $this->tester->fillField('Účel', $purpose); - $this->tester->selectOption('#chit-type', $type->equals(Operation::EXPENSE()) ? 'Výdaje' : 'Příjmy'); - $this->tester->selectOption(sprintf('items[0][%sCategories]', $type->equals(Operation::EXPENSE()) ? 'expense' : 'income'), $category); - $this->tester->fillField('Komu/Od', $recipient); - $this->tester->fillField('items[0][price]', $amount); + $this->I->wait(2); // unroll block + $this->I->wantToTest('Uložit'); + $this->I->fillField('Datum', $date->format('d.m. Y')); + $this->I->pressKey('body', [WebDriverKeys::ESCAPE]); // close datepicker + $this->I->fillField('Účel', $purpose); + $this->I->selectOption('#chit-type', $type->equals(Operation::EXPENSE()) ? 'Výdaje' : 'Příjmy'); + $this->I->selectOption(sprintf('items[0][%sCategories]', $type->equals(Operation::EXPENSE()) ? 'expense' : 'income'), $category); + $this->I->fillField('Komu/Od', $recipient); + $this->I->fillField('items[0][price]', $amount); } private function waitForBalance(string $balance): void { - $this->tester->expectTo(sprintf('see %s CZK as final balance', $balance)); - $this->tester->executeJs('window.scrollTo(0, document.body.scrollHeight);'); - $this->tester->wait(2); - $this->tester->waitForText($balance, 10, self::BALANCE_SELECTOR); + $this->I->expectTo(sprintf('see %s CZK as final balance', $balance)); + $this->I->executeJs('window.scrollTo(0, document.body.scrollHeight);'); + $this->I->wait(2); + $this->I->waitForText($balance, 10, self::BALANCE_SELECTOR); } private function removeChit(int $position): void { - $this->tester->disablePopups(); - $this->tester->click('.ui--removeChit'); + $this->I->disablePopups(); + $this->I->click('.ui--removeChit'); } } diff --git a/tests/acceptance/PaymentCept.php b/tests/acceptance/PaymentCept.php deleted file mode 100644 index 4dc3a851d..000000000 --- a/tests/acceptance/PaymentCept.php +++ /dev/null @@ -1,87 +0,0 @@ -wantTo('create payment group'); - -$I->haveInDatabase('google_oauth', [ - 'id' => '42288e92-27fb-453c-9904-36a7ebd14fe2', - 'unit_id' => 27266, - 'email' => 'test@hospodareni.loc', - 'token' => '1//02yV7BM31saaQCgYIAPOOREPSNwF-L9Irbcw-iJEHRUnfxt2KULTjXQkPI-jl8LEN-SwVp6OybduZT21RiDf7RZBA4ZoZu86UXC8', - 'updated_at' => '2017-06-15 00:00:00', -]); - -$I->haveInDatabase('pa_bank_account', [ - 'name' => 'Acceptance', - 'unit_id' => 27266, - 'token' => null, - 'created_at' => '2017-08-24 00:00:00', - 'allowed_for_subunits' => 1, - 'number_prefix' => null, - 'number_number' => '2000942144', - 'number_bank_code' => '2010', -]); - -$I->login($I::UNIT_LEADER_ROLE); -$I->click('Platby'); -$I->waitForText('Platební skupiny'); -$I->click('Založit skupinu plateb'); -$I->waitForText('Obecná'); -$I->click('Obecná'); -$I->fillField('Název', 'Jaráky'); -$I->click('//option[text()="Vyberte e-mail"]'); -$I->click('//option[text()="test@hospodareni.loc"]'); - -$I->click('//option[text()="Vyberte bankovní účet"]'); -$I->click('//option[text()="Acceptance"]'); -$I->scrollTo('input[name="send"]'); -$I->click('Založit skupinu'); - -$I->see('Skupina byla založena'); - -$page = new Payment($I); - -$I->wantTo('create payments'); - -$I->amGoingTo('add first payment'); -$page->addPayment('Testovací platba 1', null, 500); - -$I->amGoingTo('add second payment'); -$page->addPayment('Testovací platba 2', null, 500); - -$I->amGoingTo('add third payment'); -$page->addPayment('Testovací platba 3', 'frantisekmasa1@gmail.com', 300); - -$I->wantTo('complete payment'); - -$I->amGoingTo('mark second payment as complete'); -$I->click('(//*[@title="Zaplaceno"])[2]'); - -$I->canSeeNumberOfElements('(//*[text()="Nezaplacena"])', 2); -$I->see('Dokončena'); - -$I->wantTo('send payment email'); - -$I->amGoingTo('send third payment'); -$I->click('//a[contains(@class, \'ui--sendEmail\')]'); - -$page->seeNumberOfPaymentsWithState('Nezaplacena', 2); -$page->seeNumberOfPaymentsWithState('Dokončena', 1); - -$I->wantTo('close and reopen group'); -$I->click('Uzavřít'); -$I->waitForText('Znovu otevřít'); -$I->click('Znovu otevřít'); -$I->waitForText('Uzavřít'); - - -$I->amGoingTo('close group'); -$I->click('Uzavřít'); diff --git a/tests/acceptance/PaymentCest.php b/tests/acceptance/PaymentCest.php new file mode 100644 index 000000000..6bfcecf7e --- /dev/null +++ b/tests/acceptance/PaymentCest.php @@ -0,0 +1,85 @@ +I = $I; + $this->page = new Payment($I); + $I->login(AcceptanceTester::UNIT_LEADER_ROLE); + } + + /** @group payment */ + public function createPaymentGroup(): void + { + $I = $this->I; + $page = $this->page; + + $I->wantTo('create payment group'); + + $I->click('Platby'); + $I->waitForText('Platební skupiny'); + $I->click('Založit skupinu plateb'); + $I->waitForText('Obecná'); + $I->click('Obecná'); + $I->fillField('Název', 'Jaráky'); + $I->click('//option[text()="Vyberte e-mail"]'); + $I->click('//option[text()="test@hospodareni.loc"]'); + + $I->click('//option[text()="Vyberte bankovní účet"]'); + $I->click('//option[text()="Acceptance"]'); + $I->scrollTo('input[name="send"]'); + $I->click('Založit skupinu'); + + $I->see('Skupina byla založena'); + + $I->wantTo('create payments'); + + $I->amGoingTo('add first payment'); + $page->addPayment('Testovací platba 1', null, 500); + + $I->amGoingTo('add second payment'); + $page->addPayment('Testovací platba 2', null, 500); + + $I->amGoingTo('add third payment'); + $page->addPayment('Testovací platba 3', 'frantisekmasa1@gmail.com', 300); + + $I->wantTo('complete payment'); + + $I->amGoingTo('mark second payment as complete'); + $I->click('(//*[@title="Zaplaceno"])[2]'); + + $I->canSeeNumberOfElements('(//*[text()="Nezaplacena"])', 2); + $I->see('Dokončena'); + + $I->wantTo('send payment email'); + + $I->amGoingTo('send third payment'); + $I->click('//a[contains(@class, \'ui--sendEmail\')]'); + + $page->seeNumberOfPaymentsWithState('Nezaplacena', 2); + $page->seeNumberOfPaymentsWithState('Dokončena', 1); + + $I->wantTo('close and reopen group'); + $I->click('Uzavřít'); + $I->waitForText('Znovu otevřít'); + $I->click('Znovu otevřít'); + $I->waitForText('Uzavřít'); + + $I->amGoingTo('close group'); + $I->click('Uzavřít'); + } +} diff --git a/tests/integration.suite.yml b/tests/integration.suite.yml index 6f7150eff..2c359c92a 100644 --- a/tests/integration.suite.yml +++ b/tests/integration.suite.yml @@ -5,7 +5,7 @@ modules: - \Helper\Integration - Contributte\Codeception\Module\NetteDIModule: - tempDir: ../_temp/integration + tempDir: ../../temp/cache/integration appDir: ../../app configFiles: - config/doctrine.neon @@ -14,8 +14,8 @@ modules: - \Helper\WaitForDb - Db: - dsn: 'mysql:host=mysql-test;dbname=hskauting;charset=utf8' - user: 'root' - password: 'root' + dsn: 'mysql:host=%DB_HOST%;dbname=%DB_NAME%;charset=utf8' + user: "%DB_USER%" + password: "%DB_PASSWORD%" - Mockery diff --git a/tests/integration/Infrastructure/Services/Common/FlysystemScanStorageTest.php b/tests/integration/Infrastructure/Services/Common/FlysystemScanStorageTest.php index 3da021881..2efffd1d4 100644 --- a/tests/integration/Infrastructure/Services/Common/FlysystemScanStorageTest.php +++ b/tests/integration/Infrastructure/Services/Common/FlysystemScanStorageTest.php @@ -25,7 +25,7 @@ final class FlysystemScanStorageTest extends Unit protected function _before(): void { - $this->directory = __DIR__ . '/../../../../_temp/' . uniqid(self::class, true); + $this->directory = __DIR__ . '/../../../../../temp/cache/' . uniqid(self::class, true); $this->storage = new FlysystemScanStorage(new Filesystem(new LocalFilesystemAdapter($this->directory))); } diff --git a/tests/integration/config/doctrine.neon b/tests/integration/config/doctrine.neon index 6e7905bd1..7296aab66 100644 --- a/tests/integration/config/doctrine.neon +++ b/tests/integration/config/doctrine.neon @@ -1,9 +1,9 @@ parameters: database: - host: mysql-test - user: root - password: root - name: hskauting + host: %env.DB_HOST% + user: %env.DB_USER% + password: %env.DB_PASSWORD% + name: %env.DB_NAME% services: - Nette\Caching\Storages\DevNullStorage diff --git a/tests/object-manager.php b/tests/object-manager.php index e93a5c3c2..d2d92ba01 100644 --- a/tests/object-manager.php +++ b/tests/object-manager.php @@ -8,6 +8,7 @@ */ use Doctrine\Persistence\ObjectManager; +use Nette\Bootstrap\Configurator; use Nette\DI\Extensions\ExtensionsExtension; require __DIR__ . '/../vendor/autoload.php'; @@ -17,9 +18,9 @@ putenv('TMPDIR=' . $tempDir); -$configurator = new Nette\Configurator(); +$configurator = new Configurator(); $configurator->setDebugMode(true); -$configurator->enableDebugger($logDir); +$configurator->enableTracy($logDir); $configurator->setTempDirectory($tempDir); $configurator->createRobotLoader() @@ -31,8 +32,9 @@ 'tracy' => [Tracy\Bridges\Nette\TracyExtension::class, ['%debugMode%', '%consoleMode%']], ]; +$configurator->addStaticParameters(['env' => getenv()]); $configurator->addConfig(__DIR__ . '/integration/config/doctrine.neon'); -$configurator->addParameters(['logDir' => $logDir]); +$configurator->addStaticParameters(['logDir' => $logDir]); return $configurator->createContainer()->getByType(ObjectManager::class);