Skip to content

Commit

Permalink
Markdown table formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
tuutti committed Feb 29, 2024
1 parent e2e312c commit d143a54
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 3 deletions.
11 changes: 10 additions & 1 deletion src/Drush/Commands/PackageScannerDrushCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Component\DependencyInjection\ContainerInterface;
use Drupal\helfi_api_base\Package\VersionChecker;
use DrupalTools\OutputFormatters\MarkdownTableFormatter;
use Drush\Attributes\Argument;
use Drush\Attributes\Command;
use Drush\Attributes\FieldLabels;
use Drush\Commands\DrushCommands;
use Psr\Container\ContainerInterface as DrushContainer;

/**
* A drush command to check whether given Helfi packages are up-to-date.
Expand All @@ -33,7 +35,14 @@ public function __construct(
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): self {
public static function create(ContainerInterface $container, DrushContainer $drush): self {
/** @var \Drush\Formatters\DrushFormatterManager $formatterManager */
$formatterManager = $drush->get('formatterManager');

// @todo Figure out if there's a better way to inject this service.
if (!$formatterManager->hasFormatter('markdown_table')) {
$formatterManager->addFormatter('markdown_table', new MarkdownTableFormatter());
}
return new self(
$container->get('helfi_api_base.package_version_checker'),
);
Expand Down
90 changes: 90 additions & 0 deletions src/OutputFormatters/MarkdownTableFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

namespace DrupalTools\OutputFormatters;

use Consolidation\OutputFormatters\Exception\IncompatibleDataException;
use Consolidation\OutputFormatters\Formatters\FormatterInterface;
use Consolidation\OutputFormatters\Formatters\HumanReadableFormat;
use Consolidation\OutputFormatters\Formatters\MetadataFormatterInterface;
use Consolidation\OutputFormatters\Formatters\MetadataFormatterTrait;
use Consolidation\OutputFormatters\Formatters\RenderDataInterface;
use Consolidation\OutputFormatters\Formatters\RenderTableDataTrait;
use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\TableDataInterface;
use Consolidation\OutputFormatters\Transformations\TableTransformation;
use Consolidation\OutputFormatters\Validate\ValidDataTypesInterface;
use Consolidation\OutputFormatters\Validate\ValidDataTypesTrait;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Formats a table using markdown formatter.
*/
final class MarkdownTableFormatter implements FormatterInterface, ValidDataTypesInterface, RenderDataInterface, MetadataFormatterInterface, HumanReadableFormat {

use ValidDataTypesTrait;
use RenderTableDataTrait;
use MetadataFormatterTrait;

/**
* {@inheritdoc}
*/
public function write(
OutputInterface $output,
$data,
FormatterOptions $options
) : void {
assert($data instanceof TableTransformation);
$headers = $data->getHeaders();
$rows = $data->getTableData();

$output->write(self::renderLine($headers));
$output->write(self::renderLine(array_map(fn() => '--', $headers)));
$output->write(implode('', array_map(fn(array $row) => self::renderLine($row), $rows)));
}

/**
* Renders a line.
*
* @param array $fields
* Renders the given fields.
*
* @return string
* The rendered line.
*/
private static function renderLine(array $fields) : string {
return implode(' | ', $fields) . "\n";
}

/**
* {@inheritdoc}
*
* @return array{\ReflectionClass<\Consolidation\OutputFormatters\StructuredData\RowsOfFields|\Consolidation\OutputFormatters\StructuredData\PropertyList>}
* The valid data types.
*/
public function validDataTypes() : array {
return [
new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\RowsOfFields'),
new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\PropertyList'),
];
}

/**
* {@inheritdoc}
*/
public function validate($structuredData) : TableDataInterface {
// If the provided data was of class RowsOfFields
// or PropertyList, it will be converted into
// a TableTransformation object by the restructure call.
if (!$structuredData instanceof TableDataInterface) {
throw new IncompatibleDataException(
$this,
$structuredData,
$this->validDataTypes()
);
}
return $structuredData;
}

}
29 changes: 27 additions & 2 deletions tests/src/Kernel/CheckPackageVersionsCommandsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\helfi_api_base\Traits\ApiTestTrait;
use DrupalTools\Drush\Commands\PackageScannerDrushCommands;
use DrupalTools\OutputFormatters\MarkdownTableFormatter;
use Drush\Commands\DrushCommands;
use Drush\Formatters\DrushFormatterManager;
use GuzzleHttp\Psr7\Response;
use League\Container\Container;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Container\ContainerInterface;

/**
* Tests Package version checker commands.
Expand All @@ -27,11 +31,32 @@ final class CheckPackageVersionsCommandsTest extends KernelTestBase {
*/
protected static $modules = ['helfi_api_base'];

/**
* Initialized a dummy Drush container.
*
* @return \Psr\Container\ContainerInterface
* The Drush container.
*/
private function getDrushContainer() : ContainerInterface {
$container = new Container();
$container->add('formatterManager', new DrushFormatterManager());
return $container;
}

/**
* Tests that Markdown table formatter is registered.
*/
public function testMarkdownTableFormatter() : void {
$container = $this->getDrushContainer();
PackageScannerDrushCommands::create($this->container, $container);
$this->assertInstanceOf(MarkdownTableFormatter::class, $container->get('formatterManager')->getFormatter('markdown_table'));
}

/**
* Tests version check with invalid composer.json file.
*/
public function testInvalidComposerFileException() : void {
$sut = PackageScannerDrushCommands::create($this->container);
$sut = PackageScannerDrushCommands::create($this->container, $this->getDrushContainer());
$this->expectException(\RuntimeException::class);
$sut->checkVersions('nonexistent.lock');
}
Expand Down Expand Up @@ -63,7 +88,7 @@ public function testVersionCheck() : void {
])),
]);

$sut = PackageScannerDrushCommands::create($this->container);
$sut = PackageScannerDrushCommands::create($this->container, $this->getDrushContainer());
// Test with old version and make sure we exit with failure.
$return = $sut->checkVersions(__DIR__ . '/../../fixtures/composer.lock');
$this->assertEquals(DrushCommands::EXIT_FAILURE_WITH_CLARITY, $return->getExitCode());
Expand Down
95 changes: 95 additions & 0 deletions tests/src/Unit/OutputFormatters/MarkdownTableFormatterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace Drupal\Tests\helfi_drupal_tools\Unit;

use Consolidation\OutputFormatters\Exception\IncompatibleDataException;
use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\PropertyList;
use Consolidation\OutputFormatters\StructuredData\RestructureInterface;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Consolidation\OutputFormatters\StructuredData\TableDataInterface;
use Consolidation\OutputFormatters\StructuredData\UnstructuredData;
use DrupalTools\OutputFormatters\MarkdownTableFormatter;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Output\BufferedOutput;

/**
* Tests Markdown table formatter.
*
* @group helfi_drupal_tools
*/
class MarkdownTableFormatterTest extends TestCase {

/**
* Make sure an exception is thrown for incompatible data.
*/
public function testInvalidDataTypeException() : void {
$formatter = new MarkdownTableFormatter();
$data = new UnstructuredData([]);
$this->expectException(IncompatibleDataException::class);
$formatter->validate($data->restructure(new FormatterOptions()));
}

/**
* Tests validation.
*
* @dataProvider validateTestData
*/
public function testValidate(RestructureInterface $data) : void {
$formatter = new MarkdownTableFormatter();
$data = $formatter->validate($data->restructure(new FormatterOptions()));
$this->assertInstanceOf(TableDataInterface::class, $data);
}

/**
* A data provider for validation tests.
*
* @return array
* The data.
*/
public function validateTestData() : array {
return [
[new RowsOfFields([])],
[new PropertyList([])],
];
}

/**
* Make sure the given table is formatted as markdown.
*/
public function testMarkdownTableFormatter() : void {
$formatter = new MarkdownTableFormatter();
$output = new BufferedOutput();
$options = new FormatterOptions([
FormatterOptions::FIELD_LABELS => [
'name' => 'Name',
'version' => 'Version',
'latest' => 'Latest',
],
]);
$data = new RowsOfFields([
[
'name' => 'Test',
'version' => '1.0.0',
'latest' => '1.0.2',
],
[
'name' => 'Test 2',
'version' => '',
'latest' => '',
],
]);
$data = $data->restructure($options);
$formatter->write($output, $data, $options);

$expected = "Name | Version | Latest\n";
$expected .= "-- | -- | --\n";
$expected .= "Test | 1.0.0 | 1.0.2\n";
$expected .= "Test 2 | | \n";

$this->assertEquals($expected, $output->fetch());
}

}

0 comments on commit d143a54

Please sign in to comment.