From ff2e35bb93a565194000716112e765085f6ebf92 Mon Sep 17 00:00:00 2001 From: Lio Novelli Date: Sun, 1 Jan 2023 23:41:40 +0100 Subject: [PATCH] Add tests, unify input. --- .gitignore | 1 + README.org | 36 +++--- app/composer.json | 8 +- app/composer.lock | 63 ++++++++++- app/phpcs.xml | 2 +- app/src/Commands/InvoiceCommand.php | 106 +++++++----------- app/src/Commands/ReportCommand.php | 63 ++++------- app/src/Commands/SelectReportTrait.php | 80 +++++++++++++ app/src/Utils/CsvReport/ReportCsv.php | 37 +----- .../YoutrackInterface.php | 4 + .../TimeTrackingServices/YoutrackService.php | 28 ++++- app/tests/Kernel/ReportCommandTest.php | 50 +++++++++ app/tests/data/21-03.csv | 53 +++++++++ app/tests/data/email-template.txt | 10 ++ app/tests/data/invoice-template.html | 91 +++++++++++++++ app/tests/data/rprt.config.yml | 47 ++++++++ app/tests/test-dependencies.php | 7 ++ todo.txt | 14 +++ 18 files changed, 534 insertions(+), 166 deletions(-) create mode 100644 app/src/Commands/SelectReportTrait.php create mode 100644 app/tests/Kernel/ReportCommandTest.php create mode 100644 app/tests/data/21-03.csv create mode 100644 app/tests/data/email-template.txt create mode 100644 app/tests/data/invoice-template.html create mode 100644 app/tests/data/rprt.config.yml create mode 100644 app/tests/test-dependencies.php diff --git a/.gitignore b/.gitignore index 7a91d23..ba38f8a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /scratch .phpcs-cache *~undo-tree~ +/app/tests/data/output diff --git a/README.org b/README.org index f3c6ca3..0443fbc 100644 --- a/README.org +++ b/README.org @@ -2,7 +2,7 @@ Automate generating invoices from youtrack reports and other time-tracking related functionality. - + ** Usage ~./rprt.php invoice -y -p -s~ @@ -14,15 +14,14 @@ Asks which report to print from the list of your reports and then prints out a table with that report. - + ** Install/Getting started - Get ~.phar~ file from r, run configuration wizzard (planned for version 1.0). - + Get ~.phar~ file from r, Run configuration wizzard (planned for version 1.0). Before version 1.0 is released you have to navigate into ~/app~ directory and call command through ~.rprt.php~ file. - + *** Requirements 1. You have to create a youtrack API token. @@ -45,7 +44,6 @@ - project categories - hourly wage - folder for reports - ** Components - report service - provides a csv of a report @@ -61,7 +59,7 @@ *** ValueObjects - + *** List my reports #+begin_example bash @@ -76,21 +74,34 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n I'd be using this app. If it get's picked up, by 5 others - it is worth 5 days of development. - remove errors from reports - + +* Development + +** Testing + +Main source at symfony [[https://symfony.com/doc/current/console.html#testing-commands][console command page]]. If you read thouroughly: + +When using the Console component in a standalone project, use Application and +extend the normal \PHPUnit\Framework\TestCase. + ** Plan *** most current 1. For version 0.6.7 - [X] data value objects - - phar file + - [X] phar file - [X] additional expenses 2. For version 1.0 - plugin system - for time tracking services - for reports - symfony/config & symfony/di components + 3. Time tracking service + - youtrack-api + - jira-api + - kimai (https://www.kimai.org/documentation/timesheet.html) -*** current +*** current 1. For Version 1.0 - Track Command (track time from your cli) @@ -106,7 +117,7 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n - Add tests -*** old +*** old 1. Basic structure of the cli-app 1. App preparation - nice specifications @@ -124,7 +135,6 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n 3. Second round of enhancements 1. Invoice output 2. configuration wizard - ** Learning @@ -134,8 +144,8 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n ** API calls - *** Get csv file ~curl 'https://drunomics.myjetbrains.com/youtrack/api/reports/83-554/export/csv?&$top=-1' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H "Authorization: Bearer $TKN" > ~/Documents/Drunomics/workhours/2021/21-09.csv~ + diff --git a/app/composer.json b/app/composer.json index 6783f0c..81012fc 100644 --- a/app/composer.json +++ b/app/composer.json @@ -24,14 +24,15 @@ }, "autoload": { "psr-4": { - "RprtCli\\": "src" + "RprtCli\\": ["src", "tests"] } }, "require-dev": { "squizlabs/php_codesniffer": "^3.7", "phpunit/phpunit": "^9.5", "opsway/psr12-strict-coding-standard": "^0.5.0", - "phpcompatibility/php-compatibility": "^9.3" + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.9" }, "scripts": { "cs": "phpcs --colors --standard=PSR12", @@ -41,5 +42,6 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } - } + }, + "minimum-stability": "alpha" } diff --git a/app/composer.lock b/app/composer.lock index ee8d9fe..eb35a5a 100644 --- a/app/composer.lock +++ b/app/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": "7b0be09c8f282dfcceb075d1455caa28", + "content-hash": "eb0c874610e45fa74d8ccc691f5bd9f8", "packages": [ { "name": "doctrine/lexer", @@ -3287,6 +3287,65 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "time": "2020-08-03T20:32:43+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.9.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2", + "reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.9.4" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-12-17T13:33:52+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.15", @@ -4930,7 +4989,7 @@ } ], "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "alpha", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, diff --git a/app/phpcs.xml b/app/phpcs.xml index 5700a1b..7264bbf 100644 --- a/app/phpcs.xml +++ b/app/phpcs.xml @@ -7,7 +7,7 @@ - + diff --git a/app/src/Commands/InvoiceCommand.php b/app/src/Commands/InvoiceCommand.php index 760f78c..286f924 100644 --- a/app/src/Commands/InvoiceCommand.php +++ b/app/src/Commands/InvoiceCommand.php @@ -38,11 +38,13 @@ use function var_export; /** * Main file - invoice command. */ -class InvoiceCommand extends Command -{ +class InvoiceCommand extends Command { + + use SelectReportTrait; + protected $csv; - protected $configuration; + protected $config; protected $youtrack; @@ -54,14 +56,14 @@ class InvoiceCommand extends Command public function __construct( ReportCsvInterface $csv, ConfigurationInterface $configuration, - YoutrackInterface $youtrack, + YoutrackInterface $trackingService, PdfExportInterface $pdf_export, MailerInterface $mailer, ?string $name = null ) { $this->csv = $csv; - $this->configuration = $configuration; - $this->youtrack = $youtrack; + $this->config = $configuration; + $this->trackingService = $trackingService; $this->pdfExport = $pdf_export; $this->mailer = $mailer; parent::__construct($name); @@ -82,12 +84,12 @@ class InvoiceCommand extends Command InputOption::VALUE_REQUIRED, 'Specify the input csv file to generate report from.' ); - $this->addOption( - 'youtrack', - 'y', - InputOption::VALUE_NONE, - 'Use youtrack api to get a report. If this option is used --file does not have any effect..' - ); + // $this->addOption( + // 'youtrack', + // 'y', + // InputOption::VALUE_NONE, + // 'Use youtrack api to get a report. If this option is used --file does not have any effect..' + // ); $this->addOption( 'pdf', 'p', @@ -150,71 +152,42 @@ class InvoiceCommand extends Command protected function execute(InputInterface $input, OutputInterface $output) : int { if ($input->getOption('test')) { - $test = $this->youtrack->testYoutrackapi(); + $test = $this->trackingService->testYoutrackapi(); $output->writeln($test); } if ($input->getOption('list-reports')) { - $list = $this->youtrack->listReports(); + $list = $this->trackingService->listReports(); $output->writeln(var_export($list, true)); return Command::SUCCESS; } - if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) { - if ($report = $input->getOption('report')) { - $this->youtrack->setReportId($report); - } else { - $reports = $this->youtrack->listReports(); - $count = 1; - foreach ($reports as $id => $name) { - $output->writeln("[{$count}] {$name} ({$id})"); - $count++; - } - $report = readline('Select id of the report: '); - // Asume people are literate. - $this->youtrack->setReportId($report); - } - if ($output->isVerbose()) { - $output->writeln("Setting report: {$report}."); - } - } - if ($youtrack = $input->getOption('youtrack')) { - $report_id = $this->youtrack->getReportId(); - $cache_clear_status = $this->youtrack->clearReportCache($report_id); - if ($output->isVerbose()) { - $output->writeln("Report {$report_id} cache cleared, status: {$cache_clear_status}"); - } - $file = $this->youtrack->downloadReport($report_id); - } + // Gets report parameter. + $this->getReportParameter($input, $output, 'tracking_service.youtrack.invoice.report'); + $report_id = $this->trackingService->getReportId(); + $file = $this->getReportCsvFilePath($input, $output, $report_id); + $report_name = $this->trackingService->getReportName(); if ($input->hasParameterOption('--expenses') || $input->hasParameterOption('-e')) { $expenses = $this->getCustomWorkOrExpenses($input->getOption('expenses'), self::TYPE_EXPENSE); } if ($input->hasParameterOption('--custom') || $input->hasParameterOption('-c')) { + // @TODO Add option for custom time tracking data. $custom = $this->getCustomWorkOrExpenses($input->getOption('custom'), self::TYPE_WORK); } - if ($youtrack || $file = $input->getOption('file')) { - // Youtrack can also provide a file name. - if ($output->isVerbose()) { - $output->writeln("Csv file downloaded to: {$file}"); + $output->writeln("report: {$report_name}"); + $data = $this->csv->getInvoiceData($file); + if (! empty($expenses)) { + $data = array_merge($data, $expenses); + } + $table = $this->getTable($output, $data); + $table->render(); + if ($input->getOption('pdf')) { + $nice_data = $this->csv->arangeDataForDefaultPdfExport($data); + // @TODO method gatherTokens(); + if ($out = $input->getOption('output')) { + $this->pdfExport->setOutput($out); } - $data = $this->csv->getInvoiceData($file); - if (! empty($expenses)) { - $data = array_merge($data, $expenses); - } - // $table = $this->generateTable($output, $data); - $table = $this->getTable($output, $data); - $table->render(); - - if ($input->getOption('pdf')) { - $nice_data = $this->csv->arangeDataForDefaultPdfExport($data); - // @TODO method gatherTokens(); - if ($out = $input->getOption('output')) { - $this->pdfExport->setOutput($out); - } - $output_path = $this->pdfExport->fromDefaultDataToPdf($nice_data); - // Notify the user where the file was generated to. - $output->writeln("The file was generated at ${output_path}."); - } - - // return Command::SUCCESS; + $output_path = $this->pdfExport->fromDefaultDataToPdf($nice_data); + // Notify the user where the file was generated to. + $output->writeln("The file was generated at ${output_path}."); } if ($input->getOption('send') && $output_path) { // @TODO If no output path print an error. @@ -267,7 +240,7 @@ class InvoiceCommand extends Command 'Price', ]); [$rows, $totalHours, $totalPrice] = [[], 0, 0]; - $projectsConfig = $this->configuration->get('projects'); + $projectsConfig = $this->config->get('projects'); foreach ($projectsConfig as $name => $config) { if (! isset($data[$name])) { // @TODO Proper error handling. @@ -353,8 +326,7 @@ class InvoiceCommand extends Command return $output; } - protected function getCustomWorkOrExpenses($custom, $type) - { + protected function getCustomWorkOrExpenses($custom, $type) { $output = []; if (is_string($custom)) { foreach (explode(';', $custom) as $item) { diff --git a/app/src/Commands/ReportCommand.php b/app/src/Commands/ReportCommand.php index 111b4df..f61d56d 100644 --- a/app/src/Commands/ReportCommand.php +++ b/app/src/Commands/ReportCommand.php @@ -14,14 +14,14 @@ use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Question\ChoiceQuestion; -use function array_flip; use function is_array; use function is_null; -class ReportCommand extends Command -{ +class ReportCommand extends Command { + + use SelectReportTrait; + protected $trackingService; protected $config; @@ -37,22 +37,30 @@ class ReportCommand extends Command parent::__construct($name); } - protected function configure() : void - { + protected function configure() : void { $this->setName('report'); $this->setDescription('Get a time-tracking report into command line.'); $this->addOption( 'report', 'r', InputOption::VALUE_OPTIONAL, - 'Select a report from list of your reports' + 'Select a report from list ofo your reports' ); + // Not supported by by ReportCsv service! + // @TODO Build factory for time tracking service (youtrack-csv-export, + // youtrack-api, jira-api, kimai-api (biro.radiostudent.si)). $this->addOption( 'time-range', 't', InputOption::VALUE_REQUIRED, 'Calculates report from tracking service work items directly for time range' ); + $this->addOption( + 'file', + 'f', + InputOption::VALUE_REQUIRED, + 'Specify the input csv file to generate report from.' + ); } protected function execute(InputInterface $input, OutputInterface $output) : int @@ -61,50 +69,19 @@ class ReportCommand extends Command // @TODO: Implement time range option: // - Request workTime items from tracking service // - Filter them, join by issue, project ... + // This will came in other report service that will gather data + // directly from cache. if ($output->isVerbose()) { $output->writeln("Time range: {$timeRange}"); } $output->writeln('This option is not supported yet.'); return Command::FAILURE; } - $reports = $this->trackingService->listReports(); - // Could just parse a csv file or actually get workItems from youtrack ... - if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) { - if (! $report = $input->getOption('report')) { - // @TODO Make selection nicer. - // QuestionHelper: https://symfony.com/doc/current/components/console/helpers/questionhelper.html - $helper = $this->getHelper('question'); - $question = new ChoiceQuestion('Select report:', $reports); - $question - ->setErrorMessage('Report %s does not exist!') - ->setAutocompleterValues($reports); - $report = $helper->ask($input, $output, $question); - $output->writeln('Report ' . $report . ' selected.'); - } - // If parameter option is not recognised check if report name was given. - if (! isset($reports[$report])) { - if (! isset(array_flip($reports)[$report])) { - $output->writeln('Non-existing report ' . $report . '. Exiting.'); - return Command::SUCCESS; - } - $report = array_flip($reports)[$report]; - } - $this->trackingService->setReportId($report); - } elseif ($report = $this->config->get('tracking_service.youtrack.report.default')) { - $this->trackingService->setReportId($report); - } + $this->getReportParameter($input, $output); // Currently we only support csv download. $report_id = $this->trackingService->getReportId(); - $report_name = $reports[$report_id]; - // Code duplication. - $cache_clear_status = $this->trackingService->clearReportCache($report_id); - if ($output->isVerbose()) { - $output->writeln("Report cache cleared, status: {$cache_clear_status}"); - } - $file = $this->trackingService->downloadReport($report_id); - if ($output->isVerbose()) { - $output->writeln("Csv file downloaded to: {$file}"); - } + $file = $this->getReportCsvFilePath($input, $output, $report_id); + $report_name = $this->trackingService->getReportName(); $output->writeln("report: {$report_name}"); $data = $this->csv->generateReportTable($file); $table = $this->buildTable($output, $data); diff --git a/app/src/Commands/SelectReportTrait.php b/app/src/Commands/SelectReportTrait.php new file mode 100644 index 0000000..bcf26bf --- /dev/null +++ b/app/src/Commands/SelectReportTrait.php @@ -0,0 +1,80 @@ +trackingService)) { + return FALSE; + } + return $this instanceof Command; + } + + protected function getReportParameter(InputInterface $input, OutputInterface $output, $default = 'tracking_service.youtrack.report.report') { + if (!$this->checkContext()) { + return Command::FAILURE; + } + $reports = $this->trackingService->listReports(); + // Could just parse a csv file or actually get workItems from youtrack ... + if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) { + if (! $report_id = $input->getOption('report')) { + // @TODO Make selection nicer. + // QuestionHelper: https://symfony.com/doc/current/components/console/helpers/questionhelper.html + $helper = $this->getHelper('question'); + $question = new ChoiceQuestion('Select report:', $reports); + $question + ->setErrorMessage('Report %s does not exist!') + ->setAutocompleterValues($reports); + $report_id = $helper->ask($input, $output, $question); + $output->writeln('Report ' . $report_id . ' selected.'); + } + // If parameter option is not recognised check if report name was given. + if (! isset($reports[$report_id])) { + if (! isset(array_flip($reports)[$report_id])) { + $output->writeln('Non-existing report ' . $report_id . '. Exiting.'); + return Command::FAILURE; + } + $report_id = array_flip($reports)[$report_id]; + } + $this->trackingService->setReportId($report_id); + } elseif ($report_id = $this->config->get($default)) { + // If report is not set, try getting default report from configuration. + // This is dependant on the config. + $this->trackingService->setReportId($report_id); + } + $this->trackingService->setReportName(); + } + + protected function getReportCsvFilePath(InputInterface $input, OutputInterface $output, ?string $report_id) : ?string { + if (!$file = $input->getOption('file')) { + $cache_clear_status = $this->trackingService->clearReportCache($report_id); + if ($output->isVerbose()) { + $output->writeln("Report {$report_id} cache cleared, status: {$cache_clear_status}"); + } + $file = $this->trackingService->downloadReport($report_id); + if ($output->isVerbose()) { + $output->writeln("Csv file downloaded to: {$file}"); + } + } else { + $this->trackingService->setReportName($file); + } + return $file; + } + +} + diff --git a/app/src/Utils/CsvReport/ReportCsv.php b/app/src/Utils/CsvReport/ReportCsv.php index befef19..d17823a 100644 --- a/app/src/Utils/CsvReport/ReportCsv.php +++ b/app/src/Utils/CsvReport/ReportCsv.php @@ -58,7 +58,7 @@ class ReportCsv implements ReportCsvInterface if ($file = fopen($filePath, 'r')) { while (($line = fgetcsv($file)) !== false) { $parsed = $this->parseCsvFile($line); - // $key = reset(array_keys($parsed)); + // $key = reset(array_keys($parsed)); $key = array_key_first($parsed); if (isset($output[$key])) { $output[$key] += (float) reset($parsed); @@ -188,10 +188,11 @@ class ReportCsv implements ReportCsvInterface if (empty($data)) { return []; } - [$previous, $time_sum, $project_time, $table, $all_projects] = [$data[0]['id'], 0, 0, [], []]; + $explodeMinus = fn($ticket) => explode('-', $ticket)[0] ?? 'UNKNOWN'; + [$previous, $time_sum, $project_time, $table, $all_projects] = [$data[0]['id'], 0, 0, [], [$explodeMinus($data[0]['id'])]]; foreach ($data as $line) { - $project = explode('-', $line['id'])[0]; - $previous_project = explode('-', $previous)[0]; + $project = $explodeMinus($line['id']); + $previous_project = $explodeMinus($previous); if ($project !== $previous_project) { // When project changes, add a sum of time for that project. $table[] = ReportCsvInterface::SEPARATOR_MEDIUM; @@ -242,32 +243,4 @@ class ReportCsv implements ReportCsvInterface return $output; } - /** - * Should be moved into test class. - */ - protected function dummyConfig() : array - { - return [ - 'projects' => [ - 'LDP' => [ - 'name' => 'lupus.digital', - 'pattern' => 'LDP-[0-9]+', - 'price' => 25, - // optional specify columns - ], - 'WV' => [ - 'name' => 'Wirtschaftsverlag', - 'pattern' => 'WV-[0-9]+', - 'price' => 25, - // optional specify columns - ], - 'Other' => [ - 'name' => 'Other projects', - 'pattern' => '(?!.\bLDP\b)(?!.\bWV\b)', - 'price' => 25, - // optional specify columns - ], - ], - ]; - } } diff --git a/app/src/Utils/TimeTrackingServices/YoutrackInterface.php b/app/src/Utils/TimeTrackingServices/YoutrackInterface.php index 5230e47..1caa15b 100644 --- a/app/src/Utils/TimeTrackingServices/YoutrackInterface.php +++ b/app/src/Utils/TimeTrackingServices/YoutrackInterface.php @@ -34,4 +34,8 @@ interface YoutrackInterface * Array of reports with ids as keys and names as values. */ public function listReports() : array; + + public function setReportId(string $report_id) :void; + public function setReportName(?string $report_name = NULL) :void; + public function getReportName() : ?string; } diff --git a/app/src/Utils/TimeTrackingServices/YoutrackService.php b/app/src/Utils/TimeTrackingServices/YoutrackService.php index 9b3b992..c383051 100644 --- a/app/src/Utils/TimeTrackingServices/YoutrackService.php +++ b/app/src/Utils/TimeTrackingServices/YoutrackService.php @@ -28,14 +28,15 @@ use function var_dump; class YoutrackService implements YoutrackInterface { - protected $ytToken; - protected $ytBaseUrl; + protected string $ytToken; + protected string $ytBaseUrl; - protected $config; + protected ConfigurationInterface $config; - protected $httpClient; + protected ClientInterface $httpClient; - protected $report_id; + protected string $report_id; + protected ?string $reportName = NULL; public function __construct(ConfigurationInterface $config, ClientInterface $http_client) { @@ -106,6 +107,23 @@ class YoutrackService implements YoutrackInterface $this->report_id = $report_id; } + public function setReportName(?string $report_name = NULL) : void { + if (!$report_name) { + $reports = $this->listReports(); + if ($report_id = $this->getReportId()) { + $report_name = $reports[$report_id] ?? NULL; + } + else { + $report_name = NULL; + } + } + $this->reportName = $report_name; + } + + public function getReportName() : ?string { + return $this->reportName; + } + public function downloadReport(string $report_id) : ?string { $path = "youtrack/api/reports/$report_id/export/csv"; diff --git a/app/tests/Kernel/ReportCommandTest.php b/app/tests/Kernel/ReportCommandTest.php new file mode 100644 index 0000000..cb34dea --- /dev/null +++ b/app/tests/Kernel/ReportCommandTest.php @@ -0,0 +1,50 @@ +addDefinitions(__DIR__ . '/../test-dependencies.php'); + $container = $builder->build(); + + $application = new Application('Command Line Tool to process Youtrack Reports', '0.1.0'); + + $invoiceCommand = $container->get(InvoiceCommand::class); + $application->add($invoiceCommand); + $reportCommand = $container->get(ReportCommand::class); + $application->add($reportCommand); + + + $command = $application->find('report'); + $commandTester = new CommandTester($command); + $commandTester->execute([ + // pass arguments to the helper + '--file' => __DIR__ . '/../data/21-03.csv', + + // prefix the key with two dashes when passing options, + // e.g: '--some-option' => 'option_value', + // use brackets for testing array value, + // e.g: '--some-option' => ['option_value'], + ]); + + $commandTester->assertCommandIsSuccessful(); + + // the output of the command in the console + $output = $commandTester->getDisplay(); + var_dump($output); + $this->assertStringContainsString('21-03.csv', $output); + + // ... + } +} diff --git a/app/tests/data/21-03.csv b/app/tests/data/21-03.csv new file mode 100644 index 0000000..6d360bf --- /dev/null +++ b/app/tests/data/21-03.csv @@ -0,0 +1,53 @@ +Group name,Item,Item Summary,Estimation time,Spent time +-,DEV-46,"Wochenplanung, Weekly",0,315 +-,DEV-56,Developer training,0,60 +-,DEV-122,Daily standup,0,255 +-,INF-6,"Infrastructure misc, diverse",0,75 +-,KUR-181,Sprint meetings (PM),240,390 +-,KUR-507,(TKT-307) Change webhook integration for Slack,0,330 +-,KUR-552,(TKT-740) Allow kicking users out of collections,780,45 +-,KUR-554,(TKT-1448) Adaptions in Backend,0,30 +-,KUR-556,[story] (TKT-1393) Make Paragraph embedding more user friendly,0,75 +-,KUR-558,(TKT-1467) Full copy of article doesn't update the timestamp,0,15 +-,KUR-562,(TKT-1441) Implement SEO title in BE,0,45 +-,LDP-668,Add json-ld schema.org metadata to articles,0,300 +-,LDP-685,"LDP-Contentpool sitemaps. Extend ""simple_sitemap_extended"" setup to support per front-end site sitemaps.",0,15 +-,LDP-692,Output json-ld script,0,30 +-,LDP-693,Add WebPage schema.org support,120,180 +-,LDP-700,LDP onboarding Somebody,0,15 +-,LDP-706,Backend FAQ Marketing Block,0,165 +-,LDP-707,"Backend ""Hero"" Marketing Block",0,1035 +-,LDP-709,"Backend ""Newsletter Signup"" Marketing Block",0,195 +-,LDP-711,"Backend ""Call to action"" Marketing Block",0,165 +-,LDP-712,"Backend ""Feature section"" Marketing Block",0,300 +-,LDP-713,Provide a field type and pre-configuration for choosing icons,300,45 +-,LDP-716,Entity browser UX is not as good as expected,0,15 +-,LDP-720,Improve display of nodes and add support for article heros,0,45 +-,LDP-728,ldp-marketing blocks break the page when they are added to the layout builder,0,195 +-,LDP-729,ldp-cp portal entity,0,480 +-,LDP-740,CP: Add portal path prefixes,480,30 +-,LDP-741,Defects marketing Blocks,0,450 +-,LDP-742,[Red Alert] Develop branch fails to build,0,45 +-,VAL-8,Plan hosting setup on custom hosting provider,0,45 +-,WV-31,"Internal sprint meetings (PM, fixed-price).",0,150 +-,WV-4304,"(ODT-1240) Structured data on article (satellite) pages and overview pages such as the startpage, branch, topic & channel pages.",600,750 +-,WV-4305,(ODT-1202) Add mobile-banner-3 & 4 (advertisement in articles),0,60 +-,WV-4326,(ODT-1174) Make image optional in the Call to Action block,135,60 +-,WV-4355,(ODT-1270) Add article title to breadcrumbs,0,270 +-,WV-4359,"(ODT-1254) Display the text paragraph ""zwischentitel"" in H2",0,90 +-,WV-4372,(ODT-1297) Branch analytics for articles associated with specific branch (GA),135,15 +-,WV-4384,(ODT-1306) Layout-builder rendering in Chrome browser,120,120 +-,WV-4393,WVsat docker build error,0,90 +-,WV-4410,(ODT-1317) SOLR Search optimization >> suggested articles does NOT affect the relevance for search results.,375,30 +-,WV-4411,Extend simple_sitemap_extensions for dynamic variants & Install and configure simple_sitemap_extensions,600,2070 +-,WV-4412,Implement custom dynamic variants for sitemaps to group by month,0,105 +-,WV-4414,Invalid JSON in the cache_tools composer.json file,0,60 +-,WV-4421,WV Satellites Hand bau image file entities are not replicated,0,15 +-,WV-4422,"[Red Alert] Behat test fail. ""Demo content loaded..Error pages work.""",0,75 +-,WV-4423,(ODT-1325) Site managers must be able to change article status from Unpublished to Unpublished,0,15 +-,WV-4425,Unpublished Advertorials are returned,0,30 +-,WV-4427,"(ODT-1332) ""Og: image"" is not specified explicitly",120,15 +-,WV-4437,"(ODT-1339) User (id 511) on wv-contentpool is locked and cannot access the system.",0,30 +-,WV-4442,(ODT-1341) Custom HTML block is not rendered,0,120 +-,WV-4478,(ODT-1368) Hosting provider transition of all projects hosted on their OpenShift infrastructure to the new Kuberneets (k8) infrastructure.,0,90 +-,WV-4480,(ODT-1370) Error: Call to a member function getUrl() on null / cannot save an article on stage systems,0,90 diff --git a/app/tests/data/email-template.txt b/app/tests/data/email-template.txt new file mode 100644 index 0000000..97554c3 --- /dev/null +++ b/app/tests/data/email-template.txt @@ -0,0 +1,10 @@ +Hello, + +I'm sending you the invoice for [[month]] [[year]]. + +This email and invoice were generated and sent by (rprt-cli)[https://git.kompot.si/lio/RprtCli] (WIP). + + +Kind regards + +Liopold Novelli diff --git a/app/tests/data/invoice-template.html b/app/tests/data/invoice-template.html new file mode 100644 index 0000000..24828f5 --- /dev/null +++ b/app/tests/data/invoice-template.html @@ -0,0 +1,91 @@ + + + + + +

Spletni razvoj, Liopold Doron Novelli s.p.,
ADDRESS
NUMBER, liopold@drunomics.com
NUMBER

+



+

Company name
Company Name
Wien
Austria

+


+


+


+

Laibach, am [[today]]

+

Honorarnote
Nummer [[number]]

+


+

Für meine Tätigkeit Programmieren von [[date_start]] bis [[date_end]] erlaube ich mir, folgenden Betrag in Rechnung zu stellen:

+


+
[[table]]
+


Ich ersuche Sie höflich, den oben angeführt auf meine Kontonummer ACCOUNT NUMBER mit der Bankleitzahl BANK zu überweisen.

+


Vielen Dank für den Auftrag,
+ mit besten Grüßen,

+



+



+

Liopold Doron Novelli

+ + diff --git a/app/tests/data/rprt.config.yml b/app/tests/data/rprt.config.yml new file mode 100644 index 0000000..3e8d717 --- /dev/null +++ b/app/tests/data/rprt.config.yml @@ -0,0 +1,47 @@ +tracking_service: + youtrack: + auth_token: 'perm:' + base_url: 'https://.myjetbrains.com' + report_id: '83-554' + # report command + report: + report: '83-541' + # invoice command + invoice: + # default value for report parameter + report: '83-554' +export: + template_path: 'tests/data/invoice-template.html' + output: 'tests/data/output/Invoice-Novelli-[[month]]-[[year]].pdf' + labels: + - 'Tätigkeit' + - 'Stunden' + - 'Stundensatz' + - 'EUR' +email: + template_path: 'tests/data/rprt-cli/email-template.txt' + username: '' + tokens: + me: 'Liopold Novelli' + to: + - 'wolfgang.ziegler@drunomics.com' + - 'accounting@drunomics.com' + from: 'liopolddoron@gmail.com' +projects: + LDP: + name: 'lupus.digital publishing' + pattern: 'LDP' + price: 25.6 + currency: 'EUR' + project_column: 1 + time_column: 4 + # time format m - minutes, h - hours + time_format: 'm' + Other: + name: 'Projektbezogene Entwicklung' + pattern: '(?!.\bLDP\b)' + price: 29.1 + currency: 'EUR' + project_column: 1 + time_column: 4 + time_format: 'm' diff --git a/app/tests/test-dependencies.php b/app/tests/test-dependencies.php new file mode 100644 index 0000000..3675163 --- /dev/null +++ b/app/tests/test-dependencies.php @@ -0,0 +1,7 @@ +