Skip to content

Instantly share code, notes, and snippets.

@jakzal
Created June 12, 2018 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakzal/a55e7c1a5ed4abc3ccf0f0f7318dff40 to your computer and use it in GitHub Desktop.
Save jakzal/a55e7c1a5ed4abc3ccf0f0f7318dff40 to your computer and use it in GitHub Desktop.
Execute your markdown specs
<?php
declare(strict_types=1);
namespace Zalas\Test\MarkdownSpec;
class DataSet implements \IteratorAggregate
{
/**
* @var array
*/
private $headers;
/**
* @var array
*/
private $data;
public function __construct(array $headers, array $data)
{
$this->headers = $headers;
$this->data = $data;
}
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->rows());
}
private function rows(): array
{
return array_map(function (array $row) {
return array_combine($this->headers, $row);
}, $this->data);
}
}
<?php
declare(strict_types=1);
namespace Zalas\Test\MarkdownSpec;
class Event
{
/**
* @var string
*/
private $name;
/**
* @var DataSet|null
*/
private $dataSet;
private function __construct(string $name, ?DataSet $dataSet)
{
$this->name = $name;
$this->dataSet = $dataSet;
}
public static function named(string $name): self
{
return new self($name, null);
}
public function withDataSet(DataSet $dataSet): self
{
return new self($this->name, $dataSet);
}
public function name(): string
{
return $this->name;
}
public function dataSet(): ?DataSet
{
return $this->dataSet;
}
}
<?php
declare(strict_types=1);
namespace Zalas\Test\MarkdownSpec;
use League\CommonMark\Block\Element\Document;
use League\CommonMark\Block\Element\Heading;
use League\CommonMark\DocParser;
use League\CommonMark\Environment;
use Webuni\CommonMark\TableExtension\Table;
use Webuni\CommonMark\TableExtension\TableCell;
use Webuni\CommonMark\TableExtension\TableExtension;
use Webuni\CommonMark\TableExtension\TableRow;
trait MarkdownSpec
{
protected function executeScenarios(string $spec): void
{
foreach ($this->loadEvents($spec) as $event) {
$methodName = $this->eventCallback($event);
if (!method_exists($this, $methodName)) {
throw new \LogicException(
sprintf('The %s::%s method does not exist.', get_called_class(), $methodName).PHP_EOL.
PHP_EOL.
sprintf('public function %s(Event $event) {}', $methodName)
);
}
call_user_func([$this, $methodName], $event);
}
}
protected function eventCallback(Event $event): string
{
return sprintf('on%s', preg_replace('#[^a-z0-9_]#i', '', ucwords($event->name())));
}
/**
* @return Event[]
*/
private function loadEvents(string $spec): array
{
$document = $this->loadSpec($spec);
$walker = $document->walker();
$events = [];
$eventId = 0;
while ($event = $walker->next()) {
$node = $event->getNode();
if (!$event->isEntering()) {
continue;
}
if ($node instanceof Heading) {
$events[++$eventId] = Event::named($node->getStringContent());
} else if ($node instanceof Table) {
$events[$eventId] = $events[$eventId]->withDataSet(new DataSet($this->readTableHeader($node), $this->readTableBody($node)));
}
}
return $events;
}
private function loadSpec(string $spec): Document
{
$environment = Environment::createCommonMarkEnvironment();
$environment->addExtension(new TableExtension());
return (new DocParser($environment))->parse(file_get_contents($spec));
}
protected function readTableHeader(Table $node): array
{
$header = [];
$headWalker = $node->getHead()->walker();
while ($headEvent = $headWalker->next()) {
$headNode = $headEvent->getNode();
if ($headNode instanceof TableCell && $headEvent->isEntering()) {
$header[] = $headNode->getStringContent();
}
}
return $header;
}
protected function readTableBody(Table $node): array
{
$tableWalker = $node->getBody()->walker();
$rows = [];
$i = -1;
while ($tableEvent = $tableWalker->next()) {
$tableNode = $tableEvent->getNode();
if ($tableNode instanceof TableRow && $tableEvent->isEntering()) {
$i = $i + 1;
}
if ($tableNode instanceof TableCell && $tableEvent->isEntering()) {
$rows[$i][] = $tableNode->getStringContent();
}
}
return $rows;
}
}
<?php
declare(strict_types=1);
namespace Zalas\Tests;
use Zalas\Test\MarkdownSpec\Event;
use Zalas\Test\MarkdownSpec\MarkdownSpec;
use PHPUnit\Framework\TestCase;
class MySpecTest extends TestCase
{
use MarkdownSpec;
public function test_the_spec()
{
$this->executeScenarios(__DIR__ . '/Fixtures/spec.md');
}
protected function onMySpec(Event $event)
{
}
protected function onWorkingHours(Event $event)
{
$this->assertSame(
[
['Day' => 'Week', 'Start' => '09:00', 'End' => '21:00'],
['Day' => 'Weekend', 'Start' => '09:00', 'End' => '17:00'],
['Day' => 'Bank holiday', 'Start' => '09:00', 'End' => '17:00'],
],
iterator_to_array($event->dataSet())
);
}
public function onBankHolidays(Event $event)
{
var_dump($event);
}
}

My Spec

Working hours

Day Start End
Week 09:00 21:00
Weekend 09:00 17:00
Bank holiday 09:00 17:00

Bank holidays

Day
Tuesday, 12-06-2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment