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/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..173edf6d 100644 --- a/src/Behat/Gherkin/Parser.php +++ b/src/Behat/Gherkin/Parser.php @@ -275,6 +275,25 @@ 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 : '' + )); + } + } + } else { + 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 +321,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 +333,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 +375,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 +495,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..622d404b --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline.yml @@ -0,0 +1,28 @@ +feature: + title: Feature with example in the background + 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, 'known_pass'] + + scenarios: + - + type: outline + title: Scenario outline without example table + line: 10 + steps: + - { 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 } + 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 new file mode 100644 index 00000000..df7d337b --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi.yml @@ -0,0 +1,48 @@ +feature: + title: Feature with example in background and multiple scenarios + 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, known_pass] + + scenarios: + - + type: outline + title: Scenario outline without example + line: 10 + steps: + - { 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 , known_pass ] + + - type: scenario + title: A regular scenario + line: 15 + steps: + - { 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: Scenario outline with it's own example table + line: 20 + steps: + - { 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: + 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..d36fac60 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline.feature @@ -0,0 +1,13 @@ +Feature: Feature with example in the background + + Background: + Given a passing step + Examples: + | login | password | + | | | + | unknown_user | known_pass | + + 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 new file mode 100644 index 00000000..e4704463 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi.feature @@ -0,0 +1,27 @@ +Feature: Feature with example in background and multiple scenarios + + Background: + Given a passing step + Examples: + | login | password | + | | | + | unknown_user | known_pass | + + 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: 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: 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: + | 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 ) ) )