-
Notifications
You must be signed in to change notification settings - Fork 92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run behat fixture features against Cucumber parser #229
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ vendor | |
composer.phar | ||
composer.lock | ||
.phpunit.result.cache | ||
gherkin |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,7 +78,7 @@ private static function getScenarios(array $json) | |
array_map( | ||
static function ($child) { | ||
|
||
if (isset($child['scenario']['examples'])) { | ||
if (isset($child['scenario']['examples']) && count($child['scenario']['examples'])) { | ||
return new OutlineNode( | ||
isset($child['scenario']['name']) ? $child['scenario']['name'] : null, | ||
self::getTags($child['scenario']), | ||
|
@@ -90,7 +90,7 @@ static function ($child) { | |
} | ||
else { | ||
return new ScenarioNode( | ||
$child['scenario']['name'], | ||
isset($child['scenario']['name']) ? $child['scenario']['name'] : null, | ||
self::getTags($child['scenario']), | ||
self::getSteps(isset($child['scenario']['steps']) ? $child['scenario']['steps'] : []), | ||
$child['scenario']['keyword'], | ||
|
@@ -118,7 +118,7 @@ private static function getBackground(array $json) | |
array_map( | ||
static function ($child) { | ||
return new BackgroundNode( | ||
$child['background']['name'], | ||
isset($child['background']['name']) ? $child['background']['name'] : null, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just forgot what versions we support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, you used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a good memory :) |
||
self::getSteps(isset($child['background']['steps']) ? $child['background']['steps'] : []), | ||
$child['background']['keyword'], | ||
$child['background']['location']['line'] | ||
|
@@ -147,8 +147,7 @@ static function(array $json) { | |
trim($json['keyword']), | ||
$json['text'], | ||
[], | ||
$json['location']['line'], | ||
trim($json['keyword']) | ||
$json['location']['line'] | ||
); | ||
}, | ||
$json | ||
|
@@ -167,15 +166,15 @@ static function($tableJson) { | |
|
||
$table[$tableJson['tableHeader']['location']['line']] = array_map( | ||
static function($cell) { | ||
return $cell['value']; | ||
return $cell['value'] ?? ''; | ||
}, | ||
$tableJson['tableHeader']['cells'] | ||
); | ||
|
||
foreach ($tableJson['tableBody'] as $bodyRow) { | ||
$table[$bodyRow['location']['line']] = array_map( | ||
static function($cell) { | ||
return $cell['value']; | ||
return isset($cell['value']) ? $cell['value'] : ''; | ||
}, | ||
$bodyRow['cells'] | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
use Behat\Gherkin\Loader\ArrayLoader; | ||
use Behat\Gherkin\Loader\CucumberNDJsonAstLoader; | ||
use Behat\Gherkin\Loader\LoaderInterface; | ||
use Behat\Gherkin\Loader\YamlFileLoader; | ||
use Behat\Gherkin\Node\FeatureNode; | ||
use Behat\Gherkin\Node\ScenarioInterface; | ||
use Behat\Gherkin\Node\ScenarioNode; | ||
|
@@ -17,15 +18,16 @@ | |
use PHPUnit\Framework\TestCase; | ||
|
||
/** | ||
* Tests the parser against the upstream cucumber/gherkin test data | ||
* Tests the Behat and Cucumber parsers against each other | ||
* | ||
* @group cucumber-compatibility | ||
*/ | ||
class CompatibilityTest extends TestCase | ||
{ | ||
const TESTDATA_PATH = __DIR__ . '/../../../../vendor/cucumber/cucumber/gherkin/testdata'; | ||
const CUCUMBER_TEST_DATA = __DIR__ . '/../../../../vendor/cucumber/cucumber/gherkin/testdata'; | ||
const BEHAT_TEST_DATA = __DIR__ . '/../Fixtures/etalons'; | ||
|
||
private $notParsingCorrectly = [ | ||
private $cucumberFeaturesNotParsingCorrectly = [ | ||
'complex_background.feature' => 'Rule keyword not supported', | ||
'rule.feature' => 'Rule keyword not supported', | ||
'descriptions.feature' => 'Examples table descriptions not supported', | ||
|
@@ -41,7 +43,35 @@ class CompatibilityTest extends TestCase | |
'tags.feature' => 'Tags followed by comments not parsed correctly' | ||
]; | ||
|
||
private $parsedButShouldNotBe = [ | ||
private $behatFeaturesNotParsingCorrectly = [ | ||
'issue_13.yml' => 'Scenario descriptions are not supported', | ||
'complex_descriptions.yml' => 'Scenario descriptions are not supported', | ||
'multiline_name_with_newlines.yml' => 'Scenario descriptions are not supported', | ||
'multiline_name.yml' => 'Scenario descriptions are not supported', | ||
'background_title.yml' => 'Background descriptions are not supported', | ||
|
||
'empty_scenario_without_linefeed.yml' => 'Feature description has wrong whitespace captured', | ||
'addition.yml' => 'Feature description has wrong whitespace captured', | ||
'test_unit.yml' => 'Feature description has wrong whitespace captured', | ||
'ja_addition.yml' => 'Feature description has wrong whitespace captured', | ||
'ru_addition.yml' => 'Feature description has wrong whitespace captured', | ||
'fibonacci.yml' => 'Feature description has wrong whitespace captured', | ||
'ru_commented.yml' => 'Feature description has wrong whitespace captured', | ||
'empty_scenario.yml' => 'Feature description has wrong whitespace captured', | ||
'start_comments.yml' => 'Feature description has wrong whitespace captured', | ||
'empty_scenarios.yml' => 'Feature description has wrong whitespace captured', | ||
'commented_out.yml' => 'Feature description has wrong whitespace captured', | ||
'ru_division.yml' => 'Feature description has wrong whitespace captured', | ||
'hashes_in_quotes.yml' => 'Feature description has wrong whitespace captured', | ||
'outline_with_spaces.yml' => 'Feature description has wrong whitespace captured', | ||
'ru_consecutive_calculations.yml' => 'Feature description has wrong whitespace captured', | ||
]; | ||
|
||
private $behatFeaturesCucumberCannotParseCorrectly = [ | ||
'comments.yml' => 'see https://github.com/cucumber/cucumber/issues/1413' | ||
]; | ||
|
||
private $cucumberFeaturesParsedButShouldNotBe = [ | ||
'invalid_language.feature' => 'Invalid language is silently ignored', | ||
'whitespace_in_tags.feature' => 'Whitespace in tags is tolerated', | ||
]; | ||
|
@@ -54,30 +84,35 @@ class CompatibilityTest extends TestCase | |
/** | ||
* @var LoaderInterface | ||
*/ | ||
private $loader; | ||
private $cucumberLoader; | ||
|
||
/** | ||
* @var LoaderInterface | ||
*/ | ||
private $yamlLoader; | ||
|
||
protected function setUp(): void | ||
{ | ||
$arrKeywords = include __DIR__ . '/../../../../i18n.php'; | ||
$lexer = new Lexer(new Keywords\ArrayKeywords($arrKeywords)); | ||
$this->parser = new Parser($lexer); | ||
$this->loader = new CucumberNDJsonAstLoader(); | ||
$this->cucumberLoader = new CucumberNDJsonAstLoader(); | ||
$this->yamlLoader = new YamlFileLoader(); | ||
} | ||
|
||
/** | ||
* @dataProvider goodCucumberFeatures | ||
*/ | ||
public function testFeaturesParseTheSameAsCucumber(\SplFileInfo $file) | ||
public function testCucumberFeaturesParseTheSame(\SplFileInfo $file) | ||
{ | ||
|
||
if (isset($this->notParsingCorrectly[$file->getFilename()])){ | ||
$this->markTestIncomplete($this->notParsingCorrectly[$file->getFilename()]); | ||
if (isset($this->cucumberFeaturesNotParsingCorrectly[$file->getFilename()])){ | ||
$this->markTestIncomplete($this->cucumberFeaturesNotParsingCorrectly[$file->getFilename()]); | ||
} | ||
|
||
$gherkinFile = $file->getPathname(); | ||
|
||
$actual = $this->parser->parse(file_get_contents($gherkinFile), $gherkinFile); | ||
$cucumberFeatures = $this->loader->load($gherkinFile . '.ast.ndjson'); | ||
$cucumberFeatures = $this->cucumberLoader->load($gherkinFile . '.ast.ndjson'); | ||
$expected = $cucumberFeatures ? $cucumberFeatures[0] : null; | ||
|
||
$this->assertEquals( | ||
|
@@ -86,13 +121,45 @@ public function testFeaturesParseTheSameAsCucumber(\SplFileInfo $file) | |
); | ||
} | ||
|
||
/** | ||
* @dataProvider behatFeatures | ||
*/ | ||
public function testBehatFeaturesParseTheSame(\SplFileInfo $ymlFile) | ||
{ | ||
if (isset($this->behatFeaturesNotParsingCorrectly[$ymlFile->getFilename()])){ | ||
$this->markTestIncomplete($this->behatFeaturesNotParsingCorrectly[$ymlFile->getFilename()]); | ||
} | ||
|
||
if (isset($this->behatFeaturesCucumberCannotParseCorrectly[$ymlFile->getFilename()])){ | ||
$this->markTestIncomplete($this->behatFeaturesCucumberCannotParseCorrectly[$ymlFile->getFilename()]); | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this |
||
} | ||
|
||
exec('which gherkin', $_, $result); | ||
if ($result) { | ||
$this->markTestSkipped("No gherkin executable in path"); | ||
} | ||
|
||
$filename = $ymlFile->getPathname(); | ||
$expected = current($this->yamlLoader->load($filename)); | ||
|
||
$featureFile = preg_replace('/etalons\/(.*).yml$/', 'features/\\1.feature', $filename); | ||
|
||
$tempFile = tempnam(sys_get_temp_dir(), 'behat-cucumber'); | ||
exec("gherkin -format ndjson -no-source -no-pickles $featureFile > $tempFile"); | ||
$actual = current($this->cucumberLoader->load($tempFile)); | ||
unlink($tempFile); | ||
|
||
$this->assertEquals($this->normaliseFeature($expected), $this->normaliseFeature($actual)); | ||
} | ||
|
||
/** | ||
* @dataProvider badCucumberFeatures | ||
*/ | ||
public function testBadFeaturesDoNotParse(\SplFileInfo $file) | ||
public function testBadCucumberFeaturesDoNotParse(\SplFileInfo $file) | ||
{ | ||
if (isset($this->parsedButShouldNotBe[$file->getFilename()])){ | ||
$this->markTestIncomplete($this->parsedButShouldNotBe[$file->getFilename()]); | ||
if (isset($this->cucumberFeaturesParsedButShouldNotBe[$file->getFilename()])){ | ||
$this->markTestIncomplete($this->cucumberFeaturesParsedButShouldNotBe[$file->getFilename()]); | ||
} | ||
|
||
$this->expectException(ParserException::class); | ||
|
@@ -112,13 +179,22 @@ public static function badCucumberFeatures() | |
|
||
private static function getCucumberFeatures($folder) | ||
{ | ||
foreach (new \FilesystemIterator(self::TESTDATA_PATH . $folder) as $file) { | ||
foreach (new \FilesystemIterator(self::CUCUMBER_TEST_DATA . $folder) as $file) { | ||
if ($file->isFile() && $file->getExtension() == 'feature') { | ||
yield $file->getFilename() => array($file); | ||
} | ||
} | ||
} | ||
|
||
public static function behatFeatures(): iterable | ||
{ | ||
foreach (new \FilesystemIterator(self::BEHAT_TEST_DATA) as $file) { | ||
if ($file->isFile() && $file->getExtension() == 'yml') { | ||
yield $file->getFilename() => array($file); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Renove features that aren't present in the cucumber source | ||
*/ | ||
|
@@ -129,7 +205,7 @@ private function normaliseFeature($featureNode) | |
return null; | ||
} | ||
|
||
$scenarios = array_map( | ||
array_map( | ||
function(ScenarioInterface $scenarioNode) { | ||
$steps = array_map( | ||
function(StepNode $step) { | ||
|
@@ -148,6 +224,7 @@ function(StepNode $step) { | |
$featureNode->getScenarios() | ||
); | ||
|
||
$this->setPrivateProperty($featureNode, 'file', 'file.feature'); | ||
|
||
return $featureNode; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should put a
\n
before the new config, to be more readable (otherwise, the first line of the config is stuck with the message)