Skip to content
Open
12 changes: 11 additions & 1 deletion src/Behat/Gherkin/Cucumber/BackgroundNodeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Behat\Gherkin\Cucumber;

use Behat\Gherkin\Node\BackgroundNode;
use Cucumber\Messages\Background;
use Cucumber\Messages\FeatureChild;

final class BackgroundNodeMapper
Expand All @@ -26,8 +27,17 @@ public function map(array $children) : ?BackgroundNode
{
foreach($children as $child) {
if ($child->background) {

$title = $child->background->name;
if ($child->background->description) {
$title .= "\n" . $child->background->description;
}

return new BackgroundNode(
$child->background->name,
MultilineStringFormatter::format(
$title,
$child->background->location
),
$this->stepNodeMapper->map($child->background->steps),
$child->background->keyword,
$child->background->location->line
Expand Down
13 changes: 11 additions & 2 deletions src/Behat/Gherkin/Cucumber/ExampleTableNodeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@
use Behat\Gherkin\Node\ExampleTableNode;
use Cucumber\Messages\Examples;
use Cucumber\Messages\TableCell;
use Cucumber\Messages\TableRow;

final class ExampleTableNodeMapper
{
/**
* @var TagMapper
*/
private $tagMapper;

public function __construct(TagMapper $tagMapper)
{
$this->tagMapper = $tagMapper;
}

/**
* @param Examples[] $exampleTables
*
Expand All @@ -22,7 +31,7 @@ public function map(array $exampleTables) : array
$exampleTableNodes[] = new ExampleTableNode(
$this->getTableArray($exampleTable),
$exampleTable->keyword,
[]
$this->tagMapper->map($exampleTable->tags)
);
}

Expand Down
7 changes: 6 additions & 1 deletion src/Behat/Gherkin/Cucumber/FeatureNodeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Behat\Gherkin\Cucumber;

use Behat\Gherkin\Node\FeatureNode;
use Cucumber\Messages\Feature;
use Cucumber\Messages\GherkinDocument;

final class FeatureNodeMapper
Expand Down Expand Up @@ -41,7 +42,10 @@ function map(GherkinDocument $gherkinDocument) : ?FeatureNode

return new FeatureNode(
$gherkinDocument->feature->name,
$gherkinDocument->feature->description,
MultilineStringFormatter::format(
$gherkinDocument->feature->description,
$gherkinDocument->feature->location
) ?: null, // background has empty = null
$this->tagMapper->map($gherkinDocument->feature->tags),
$this->backgroundMapper->map($gherkinDocument->feature->children),
$this->scenarioMapper->map($gherkinDocument->feature->children),
Expand All @@ -51,4 +55,5 @@ function map(GherkinDocument $gherkinDocument) : ?FeatureNode
$gherkinDocument->feature->location->line
);
}

}
23 changes: 23 additions & 0 deletions src/Behat/Gherkin/Cucumber/MultilineStringFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types=1);

namespace Behat\Gherkin\Cucumber;

use Cucumber\Messages\Location;

final class MultilineStringFormatter
{
public static function format(string $string, Location $keywordLocation = null): string
{
if (!$keywordLocation) {
$keywordLocation = new Location(0,1);
}

$maxIndent = ($keywordLocation->column-1 ?: 0) + 2;

return preg_replace(
["/^[^\n\S]{0,$maxIndent}/um", '/[^\n\S]+$/um'],
['', ''],
$string
);
}
}
12 changes: 8 additions & 4 deletions src/Behat/Gherkin/Cucumber/ScenarioNodeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Behat\Gherkin\Node\ScenarioInterface;
use Behat\Gherkin\Node\ScenarioNode;
use Cucumber\Messages\FeatureChild;
use Cucumber\Messages\Scenario;

final class ScenarioNodeMapper
{
Expand All @@ -25,8 +26,8 @@ final class ScenarioNodeMapper
private $exampleTableNodeMapper;

public function __construct(
TagMapper $tagMapper,
StepNodeMapper $stepNodeMapper,
TagMapper $tagMapper,
StepNodeMapper $stepNodeMapper,
ExampleTableNodeMapper $exampleTableNodeMapper
)
{
Expand All @@ -40,7 +41,7 @@ public function __construct(
*
* @return ScenarioInterface[]
*/
public function map(array $children) : array
public function map(array $children): array
{
$scenarios = [];

Expand All @@ -53,7 +54,10 @@ public function map(array $children) : array
}

$scenario = new ScenarioNode (
$title,
MultilineStringFormatter::format(
$title,
$child->scenario->location
),
$this->tagMapper->map($child->scenario->tags),
$this->stepNodeMapper->map($child->scenario->steps),
$child->scenario->keyword,
Expand Down
2 changes: 2 additions & 0 deletions src/Behat/Gherkin/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ protected function scanText()
*/
private function getStepKeywordType($native)
{

// Consider "*" as a AND keyword so that it is normalized to the previous step type
if ('*' === $native) {
return 'And';
Expand All @@ -644,6 +645,7 @@ private function getStepKeywordType($native)

foreach ($this->stepKeywordTypesCache as $type => $keywords) {
if (in_array($native, $keywords) || in_array($native . '<', $keywords)) {

return $type;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/Behat/Gherkin/Loader/CucumberGherkinLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ public function __construct()
new ScenarioNodeMapper(
$tagMapper,
$stepNodeMapper,
new ExampleTableNodeMapper()
new ExampleTableNodeMapper(
$tagMapper
)
)
);

Expand Down
7 changes: 7 additions & 0 deletions test.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature:

this is a description with some important points

this is another paragraph in the same description

Scenario:
57 changes: 57 additions & 0 deletions tests/Behat/Gherkin/Acceptance/CompatibilityTestTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Tests\Behat\Gherkin\Acceptance;

use Behat\Gherkin\Loader\YamlFileLoader;
use Behat\Gherkin\Node\FeatureNode;

trait CompatibilityTestTrait
{
private $yaml;

abstract protected function parseFeature($featureFile) : FeatureNode;

/**
* @dataProvider etalonsProvider
*/
public function testItParsesTheBehatEtalons($yamlFile, $featureFile)
{
$fixture = $this->getYamlParser()->load($yamlFile)[0];
$feature = $this->parseFeature($featureFile);

$this->compare($fixture, $feature);
}

public function etalonsProvider()
{
foreach (glob(__DIR__ . '/../Fixtures/etalons/*.yml') as $file) {
$testname = basename($file, '.yml');

if (!in_array($testname, $this->etalons_skip)) {
yield $testname => [$file, __DIR__ . '/../Fixtures/features/'.$testname.'.feature'];
}

}
}

protected function getYamlParser()
{
if (null === $this->yaml) {
$this->yaml = new YamlFileLoader();
}

return $this->yaml;
}

private function compare(FeatureNode $fixture, ?FeatureNode $feature): void
{
$rc = new \ReflectionClass(FeatureNode::class);
$rp = $rc->getProperty('file');
$rp->setAccessible(true);
$rp->setValue($fixture, null);
$rp->setValue($feature, null);

$this->assertEquals($fixture, $feature);
}

}
28 changes: 28 additions & 0 deletions tests/Behat/Gherkin/Acceptance/CucumberParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types=1);

namespace Tests\Behat\Gherkin\Acceptance;

use Behat\Gherkin\Loader\CucumberGherkinLoader;
use Behat\Gherkin\Node\FeatureNode;
use PHPUnit\Framework\TestCase;

/** @group cucumber */
final class CucumberParserTest extends TestCase
{
protected $etalons_skip = [
'comments', # see https://github.com/cucumber/common/issues/1413
'multiline_name_with_newlines', # cucumber does not preserve leading newlines in description blocks
];

public function setUp() : void
{
$this->loader = new CucumberGherkinLoader();
}

use CompatibilityTestTrait;

protected function parseFeature($featureFile): FeatureNode
{
return $this->loader->load($featureFile)[0];
}
}
69 changes: 69 additions & 0 deletions tests/Behat/Gherkin/Acceptance/LegacyParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Tests\Behat\Gherkin\Acceptance;

use Behat\Gherkin\Keywords\ArrayKeywords;
use Behat\Gherkin\Lexer;
use Behat\Gherkin\Node\FeatureNode;
use Behat\Gherkin\Parser;
use PHPUnit\Framework\TestCase;

final class LegacyParserTest extends TestCase
{
private $gherkin;
protected $etalons_skip = [];

use CompatibilityTestTrait;

protected function parseFeature($featureFile) : FeatureNode
{
return $this->getGherkinParser()->parse(file_get_contents($featureFile), $featureFile);
}

protected function getGherkinParser()
{
if (null === $this->gherkin) {
$keywords = new ArrayKeywords(array(
'en' => array(
'feature' => 'Feature',
'background' => 'Background',
'scenario' => 'Scenario',
'scenario_outline' => 'Scenario Outline',
'examples' => 'Examples',
'given' => 'Given',
'when' => 'When',
'then' => 'Then',
'and' => 'And',
'but' => 'But'
),
'ru' => array(
'feature' => 'Функционал',
'background' => 'Предыстория',
'scenario' => 'Сценарий',
'scenario_outline' => 'Структура сценария',
'examples' => 'Примеры',
'given' => 'Допустим',
'when' => 'Если',
'then' => 'То',
'and' => 'И',
'but' => 'Но'
),
'ja' => array (
'feature' => 'フィーチャ',
'background' => '背景',
'scenario' => 'シナリオ',
'scenario_outline' => 'シナリオアウトライン',
'examples' => '例|サンプル',
'given' => '前提<',
'when' => 'もし<',
'then' => 'ならば<',
'and' => 'かつ<',
'but' => 'しかし<'
)
));
$this->gherkin = new Parser(new Lexer($keywords));
}

return $this->gherkin;
}
}
16 changes: 15 additions & 1 deletion tests/Behat/Gherkin/Cucumber/BackgroundNodeMapperTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
<?php

namespace Behat\Gherkin\Cucumber;
namespace Tests\Behat\Gherkin\Cucumber;

use Behat\Gherkin\Cucumber\BackgroundNodeMapper;
use Behat\Gherkin\Cucumber\KeywordTypeMapper;
use Behat\Gherkin\Cucumber\PyStringNodeMapper;
use Behat\Gherkin\Cucumber\StepNodeMapper;
use Behat\Gherkin\Cucumber\TableNodeMapper;
use Behat\Gherkin\Node\BackgroundNode;
use Behat\Gherkin\Node\StepNode;
use Cucumber\Messages\Background;
Expand Down Expand Up @@ -56,6 +61,15 @@ public function testItPopulatesTitle()
self::assertSame('Background title', $result->getTitle());
}

public function testItPopulatesTitleFromDescriptionWhenMultiline()
{
$result = $this->mapper->map([new FeatureChild(null,
new Background(new Location(), '', 'title', "across\nmany\nlines")
)]);

self::assertSame("title\nacross\nmany\nlines", $result->getTitle());
}

public function testItPopulatesKeyword()
{
$result = $this->mapper->map([new FeatureChild(null,
Expand Down
2 changes: 1 addition & 1 deletion tests/Behat/Gherkin/Cucumber/CompatibilityTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Behat\Gherkin\Cucumber;
namespace Tests\Behat\Gherkin\Cucumber;

use Behat\Gherkin\Exception\ParserException;
use Behat\Gherkin\Gherkin;
Expand Down
Loading