From 7505014ba06737f1d66161fcc4bb2e90f21093d3 Mon Sep 17 00:00:00 2001 From: Dipak Acharya Date: Wed, 15 Jan 2020 09:06:32 +0545 Subject: [PATCH 1/3] Add support for Outline on background --- src/Behat/Gherkin/Loader/ArrayLoader.php | 18 ++++++- src/Behat/Gherkin/Loader/YamlFileLoader.php | 8 ++++ src/Behat/Gherkin/Node/BackgroundNode.php | 28 ++++++++++- src/Behat/Gherkin/Node/OutlineNode.php | 28 +++++++---- src/Behat/Gherkin/Parser.php | 39 ++++++++++----- .../etalons/background_with_outline.yml | 31 ++++++++++++ .../etalons/background_with_outline_multi.yml | 48 +++++++++++++++++++ .../features/background_with_outline.feature | 13 +++++ .../background_with_outline_multi.feature | 27 +++++++++++ .../Behat/Gherkin/Loader/ArrayLoaderTest.php | 6 ++- 10 files changed, 223 insertions(+), 23 deletions(-) create mode 100644 tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml create mode 100644 tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml create mode 100644 tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature create mode 100644 tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature diff --git a/src/Behat/Gherkin/Loader/ArrayLoader.php b/src/Behat/Gherkin/Loader/ArrayLoader.php index 3492d6e6..3f7d9876 100644 --- a/src/Behat/Gherkin/Loader/ArrayLoader.php +++ b/src/Behat/Gherkin/Loader/ArrayLoader.php @@ -119,7 +119,23 @@ protected function loadBackgroundHash(array $hash) $steps = $this->loadStepsHash($hash['steps']); - return new BackgroundNode($hash['title'], $steps, $hash['keyword'], $hash['line']); + if (isset($hash['examples']['keyword'])) { + $examplesKeyword = $hash['examples']['keyword']; + unset($hash['examples']['keyword']); + } else { + $examplesKeyword = 'Examples'; + } + if (isset($hash['examples'])) { + $examplesTable = $hash['examples']; + } else { + $examplesTable = array(); + } + if (\count($examplesTable) === 0) { + $examples = null; + } else { + $examples = new ExampleTableNode($examplesTable, $examplesKeyword); + } + return new BackgroundNode($hash['title'], $steps, $hash['keyword'], $hash['line'], $examples); } /** diff --git a/src/Behat/Gherkin/Loader/YamlFileLoader.php b/src/Behat/Gherkin/Loader/YamlFileLoader.php index 0c268fd3..52808f55 100644 --- a/src/Behat/Gherkin/Loader/YamlFileLoader.php +++ b/src/Behat/Gherkin/Loader/YamlFileLoader.php @@ -11,6 +11,7 @@ namespace Behat\Gherkin\Loader; use Behat\Gherkin\Node\FeatureNode; +use Behat\Gherkin\Node\OutlineNode; use Symfony\Component\Yaml\Yaml; /** @@ -57,6 +58,13 @@ public function load($path) $filename = $this->findRelativePath($path); return array_map(function (FeatureNode $feature) use ($filename) { + if ($feature->getBackground() !== null && $feature->getBackground()->hasExamples()) { + foreach ($feature->getScenarios() as $scenario) { + if ($scenario instanceof OutlineNode && !$scenario->hasExamples()){ + $scenario->setExampleTable($feature->getBackground()->getExamples()); + } + } + } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), diff --git a/src/Behat/Gherkin/Node/BackgroundNode.php b/src/Behat/Gherkin/Node/BackgroundNode.php index fb1edb4b..a6b1aa8a 100644 --- a/src/Behat/Gherkin/Node/BackgroundNode.php +++ b/src/Behat/Gherkin/Node/BackgroundNode.php @@ -33,6 +33,10 @@ class BackgroundNode implements ScenarioLikeInterface * @var integer */ private $line; + /** + * @var ExampleTableNode + */ + private $exampleTable; /** * Initializes background. @@ -41,13 +45,15 @@ class BackgroundNode implements ScenarioLikeInterface * @param StepNode[] $steps * @param string $keyword * @param integer $line + * @param null|ExampleTableNode $exampleTable */ - public function __construct($title, array $steps, $keyword, $line) + public function __construct($title, array $steps, $keyword, $line, $exampleTable=null) { $this->title = $title; $this->steps = $steps; $this->keyword = $keyword; $this->line = $line; + $this->exampleTable = $exampleTable; } /** @@ -109,4 +115,24 @@ public function getLine() { return $this->line; } + + /** + * Returns if background has ExampleTable + * + * @return boolean + */ + public function hasExamples() + { + return $this->exampleTable !== null; + } + + /** + * Returns if background has ExampleTable + * + * @return null|ExampleTableNode + */ + public function getExamples() + { + return $this->exampleTable; + } } diff --git a/src/Behat/Gherkin/Node/OutlineNode.php b/src/Behat/Gherkin/Node/OutlineNode.php index 62f55181..11edcb22 100644 --- a/src/Behat/Gherkin/Node/OutlineNode.php +++ b/src/Behat/Gherkin/Node/OutlineNode.php @@ -49,18 +49,18 @@ class OutlineNode implements ScenarioInterface /** * Initializes outline. * - * @param null|string $title - * @param string[] $tags - * @param StepNode[] $steps - * @param ExampleTableNode $table - * @param string $keyword - * @param integer $line + * @param null|string $title + * @param string[] $tags + * @param StepNode[] $steps + * @param null|ExampleTableNode $table + * @param string $keyword + * @param integer $line */ public function __construct( $title, array $tags, array $steps, - ExampleTableNode $table, + $table, $keyword, $line ) { @@ -151,7 +151,19 @@ public function getSteps() */ public function hasExamples() { - return 0 < count($this->table->getColumnsHash()); + return $this->table !== null && 0 < count($this->table->getRows()); + } + + /** + * Add Example to the outline. + * + * @param ExampleTableNode + * + * @return void + */ + public function setExampleTable($table) + { + $this->table = $table; } /** diff --git a/src/Behat/Gherkin/Parser.php b/src/Behat/Gherkin/Parser.php index 5cc85424..632b400c 100644 --- a/src/Behat/Gherkin/Parser.php +++ b/src/Behat/Gherkin/Parser.php @@ -275,6 +275,26 @@ protected function parseFeature() } } + if ($background === null || !$background->hasExamples()) { + foreach ($scenarios as $scenario) { + if ($scenario instanceof OutlineNode && !$scenario->hasExamples()) { + throw new ParserException(sprintf( + 'Outline should have examples table, but got none for outline "%s" on line: %d%s', + rtrim($scenario->getTitle()), + $scenario->getLine(), + $this->file ? ' in file: ' . $this->file : '' + )); + } + } + } + if ($background !== null && $background->hasExamples()) {; + foreach ($scenarios as $scenario) { + if ($scenario instanceof OutlineNode && !$scenario->hasExamples()) { + $scenario->setExampleTable($background->getExamples()); + } + } + } + return new FeatureNode( rtrim($title) ?: null, rtrim($description) ?: null, @@ -302,6 +322,7 @@ protected function parseBackground() $title = trim($token['value']); $keyword = $token['keyword']; $line = $token['line']; + $example = null; if (count($this->popTags())) { throw new ParserException(sprintf( @@ -313,10 +334,15 @@ protected function parseBackground() // Parse description and steps $steps = array(); - $allowedTokenTypes = array('Step', 'Newline', 'Text', 'Comment'); + $allowedTokenTypes = array('Step', 'Newline', 'Text', 'Comment', 'Examples'); while (in_array($this->predictTokenType(), $allowedTokenTypes)) { $node = $this->parseExpression(); + if ($node instanceof ExampleTableNode) { + $example = $node; + continue; + } + if ($node instanceof StepNode) { $steps[] = $this->normalizeStepNodeKeywordType($node, $steps); continue; @@ -350,7 +376,7 @@ protected function parseBackground() } } - return new BackgroundNode(rtrim($title) ?: null, $steps, $keyword, $line); + return new BackgroundNode(rtrim($title) ?: null, $steps, $keyword, $line, $example); } /** @@ -470,15 +496,6 @@ protected function parseOutline() } } - if (null === $examples) { - throw new ParserException(sprintf( - 'Outline should have examples table, but got none for outline "%s" on line: %d%s', - rtrim($title), - $line, - $this->file ? ' in file: ' . $this->file : '' - )); - } - return new OutlineNode(rtrim($title) ?: null, $tags, $steps, $examples, $keyword, $line); } diff --git a/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml new file mode 100644 index 00000000..5fe2f0b1 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml @@ -0,0 +1,31 @@ +feature: + title: Feature with background and example + language: en + line: 1 + description: ~ + + background: + line: 3 + steps: + - { keyword_type: Given, type: Given, text: a passing step, line: 4 } + examples: + 6: [login, password] + 7: ['', ''] + 8: [unknown_user, ''] + + scenarios: + - + type: outline + title: ~ + line: 10 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 11 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 12 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 13 } + arguments: + - + type: table + rows: + 6: [ login, password ] + 7: [ '' , '' ] + 8: [ unknown_user , '' ] \ No newline at end of file diff --git a/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml new file mode 100644 index 00000000..97a23e9d --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml @@ -0,0 +1,48 @@ +feature: + title: Feature with background and example + language: en + line: 1 + description: ~ + + background: + line: 3 + steps: + - { keyword_type: Given, type: Given, text: a passing step, line: 4 } + examples: + 6: [login, password] + 7: ['', ''] + 8: [unknown_user, ''] + + scenarios: + - + type: outline + title: scenario1 + line: 10 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 11 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 12 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 13 } + examples: + 6: [ login, password ] + 7: [ '' , '' ] + 8: [ unknown_user , '' ] + + - type: scenario + title: scenario2 + line: 15 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 16 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with "user"', line: 17 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with "password"', line: 18 } + + - type: outline + title: scenario3 + line: 20 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 21 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 22 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 23 } + examples: + 25: [ login, password ] + 26: [ hello, world ] + 27: [ user , pass ] \ No newline at end of file diff --git a/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature b/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature new file mode 100644 index 00000000..eebd708f --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature @@ -0,0 +1,13 @@ +Feature: Feature with background and example + + Background: + Given a passing step + Examples: + | login | password | + | | | + | unknown_user | | + + Scenario Outline: + Given a failing step + When I fill in "login" with "" + And I fill in "password" with "" diff --git a/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature new file mode 100644 index 00000000..a027a653 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature @@ -0,0 +1,27 @@ +Feature: Feature with background and example + + Background: + Given a passing step + Examples: + | login | password | + | | | + | unknown_user | | + + Scenario Outline: scenario1 + Given a failing step + When I fill in "login" with "" + And I fill in "password" with "" + + Scenario: scenario2 + Given a failing step + When I fill in "login" with "user" + And I fill in "password" with "password" + + Scenario Outline: scenario3 + Given a failing step + When I fill in "login" with "" + And I fill in "password" with "" + Examples: + | login | password | + | hello | world | + | user | pass | \ No newline at end of file diff --git a/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php b/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php index 697d1d3d..bbdc0eca 100644 --- a/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php +++ b/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php @@ -217,7 +217,8 @@ public function testLoadSteps() 'steps' => array( array('type' => 'Gangway!', 'keyword_type' => 'Given', 'text' => 'bg step 1', 'line' => 3), array('type' => 'Blimey!', 'keyword_type' => 'When', 'text' => 'bg step 2') - ) + ), + 'examples' => null ), 'scenarios' => array( array( @@ -326,7 +327,8 @@ public function testLoadStepArguments() ) ) ) - ) + ), + 'examples' => null ) ) ) From f85b54ca4b7286e3622efcc18af4463b1b71bf4c Mon Sep 17 00:00:00 2001 From: Dipak Acharya Date: Thu, 16 Jan 2020 11:00:45 +0545 Subject: [PATCH 2/3] Improve features in etalon tests --- src/Behat/Gherkin/Loader/YamlFileLoader.php | 8 -------- .../etalons/background_with_outline.yml | 19 ++++++++---------- .../etalons/background_with_outline_multi.yml | 18 ++++++++--------- .../features/background_with_outline.feature | 12 +++++------ .../background_with_outline_multi.feature | 20 +++++++++---------- 5 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/Behat/Gherkin/Loader/YamlFileLoader.php b/src/Behat/Gherkin/Loader/YamlFileLoader.php index 52808f55..0c268fd3 100644 --- a/src/Behat/Gherkin/Loader/YamlFileLoader.php +++ b/src/Behat/Gherkin/Loader/YamlFileLoader.php @@ -11,7 +11,6 @@ namespace Behat\Gherkin\Loader; use Behat\Gherkin\Node\FeatureNode; -use Behat\Gherkin\Node\OutlineNode; use Symfony\Component\Yaml\Yaml; /** @@ -58,13 +57,6 @@ public function load($path) $filename = $this->findRelativePath($path); return array_map(function (FeatureNode $feature) use ($filename) { - if ($feature->getBackground() !== null && $feature->getBackground()->hasExamples()) { - foreach ($feature->getScenarios() as $scenario) { - if ($scenario instanceof OutlineNode && !$scenario->hasExamples()){ - $scenario->setExampleTable($feature->getBackground()->getExamples()); - } - } - } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), diff --git a/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml index 5fe2f0b1..622d404b 100644 --- a/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml @@ -1,5 +1,5 @@ feature: - title: Feature with background and example + title: Feature with example in the background language: en line: 1 description: ~ @@ -11,21 +11,18 @@ feature: examples: 6: [login, password] 7: ['', ''] - 8: [unknown_user, ''] + 8: [unknown_user, 'known_pass'] scenarios: - type: outline - title: ~ + title: Scenario outline without example table line: 10 steps: - - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 11 } + - { keyword_type: 'Given', type: 'Given', text: 'I browse to the login page', line: 11 } - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 12 } - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 13 } - arguments: - - - type: table - rows: - 6: [ login, password ] - 7: [ '' , '' ] - 8: [ unknown_user , '' ] \ No newline at end of file + examples: + 6: [login, password] + 7: ['', ''] + 8: [unknown_user, 'known_pass'] \ No newline at end of file diff --git a/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml index 97a23e9d..df7d337b 100644 --- a/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml @@ -1,5 +1,5 @@ feature: - title: Feature with background and example + title: Feature with example in background and multiple scenarios language: en line: 1 description: ~ @@ -11,35 +11,35 @@ feature: examples: 6: [login, password] 7: ['', ''] - 8: [unknown_user, ''] + 8: [unknown_user, known_pass] scenarios: - type: outline - title: scenario1 + title: Scenario outline without example line: 10 steps: - - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 11 } + - { keyword_type: 'Given', type: 'Given', text: 'I have browsed to login page', line: 11 } - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 12 } - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 13 } examples: 6: [ login, password ] 7: [ '' , '' ] - 8: [ unknown_user , '' ] + 8: [ unknown_user , known_pass ] - type: scenario - title: scenario2 + title: A regular scenario line: 15 steps: - - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 16 } + - { keyword_type: 'Given', type: 'Given', text: 'I have browsed to login page', line: 16 } - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with "user"', line: 17 } - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with "password"', line: 18 } - type: outline - title: scenario3 + title: Scenario outline with it's own example table line: 20 steps: - - { keyword_type: 'Given', type: 'Given', text: 'a failing step', line: 21 } + - { keyword_type: 'Given', type: 'Given', text: 'I have browsed to login page', line: 21 } - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 22 } - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 23 } examples: diff --git a/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature b/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature index eebd708f..d36fac60 100644 --- a/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature @@ -1,13 +1,13 @@ -Feature: Feature with background and example +Feature: Feature with example in the background Background: Given a passing step Examples: - | login | password | - | | | - | unknown_user | | + | login | password | + | | | + | unknown_user | known_pass | - Scenario Outline: - Given a failing step + Scenario Outline: Scenario outline without example table + Given I browse to the login page When I fill in "login" with "" And I fill in "password" with "" diff --git a/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature index a027a653..e4704463 100644 --- a/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature @@ -1,24 +1,24 @@ -Feature: Feature with background and example +Feature: Feature with example in background and multiple scenarios Background: Given a passing step Examples: - | login | password | - | | | - | unknown_user | | + | login | password | + | | | + | unknown_user | known_pass | - Scenario Outline: scenario1 - Given a failing step + Scenario Outline: Scenario outline without example + Given I have browsed to login page When I fill in "login" with "" And I fill in "password" with "" - Scenario: scenario2 - Given a failing step + Scenario: A regular scenario + Given I have browsed to login page When I fill in "login" with "user" And I fill in "password" with "password" - Scenario Outline: scenario3 - Given a failing step + Scenario Outline: Scenario outline with it's own example table + Given I have browsed to login page When I fill in "login" with "" And I fill in "password" with "" Examples: From 92472896922590cda5862bd72c331175b02c4de1 Mon Sep 17 00:00:00 2001 From: Dipak Acharya Date: Fri, 21 Feb 2020 10:32:21 +0545 Subject: [PATCH 3/3] Change if condition to else in Parser.php --- src/Behat/Gherkin/Parser.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Behat/Gherkin/Parser.php b/src/Behat/Gherkin/Parser.php index 632b400c..173edf6d 100644 --- a/src/Behat/Gherkin/Parser.php +++ b/src/Behat/Gherkin/Parser.php @@ -286,8 +286,7 @@ protected function parseFeature() )); } } - } - if ($background !== null && $background->hasExamples()) {; + } else { foreach ($scenarios as $scenario) { if ($scenario instanceof OutlineNode && !$scenario->hasExamples()) { $scenario->setExampleTable($background->getExamples());