diff --git a/src/Behat/Gherkin/Filter/LineFilter.php b/src/Behat/Gherkin/Filter/LineFilter.php index 455e9ac3..ba37adde 100644 --- a/src/Behat/Gherkin/Filter/LineFilter.php +++ b/src/Behat/Gherkin/Filter/LineFilter.php @@ -83,24 +83,26 @@ public function filterFeature(FeatureNode $feature) } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { - $table = $scenario->getExampleTable()->getTable(); - $lines = array_keys($table); - - if (in_array($this->filterLine, $lines)) { - $filteredTable = array($lines[0] => $table[$lines[0]]); - - if ($lines[0] !== $this->filterLine) { - $filteredTable[$this->filterLine] = $table[$this->filterLine]; + foreach ($scenario->getExampleTables() as $exampleTable) { + $table = $exampleTable->getTable(); + $lines = array_keys($table); + + if (in_array($this->filterLine, $lines)) { + $filteredTable = array($lines[0] => $table[$lines[0]]); + + if ($lines[0] !== $this->filterLine) { + $filteredTable[$this->filterLine] = $table[$this->filterLine]; + } + + $scenario = new OutlineNode( + $scenario->getTitle(), + $scenario->getTags(), + $scenario->getSteps(), + array(new ExampleTableNode($filteredTable, $exampleTable->getKeyword(), $exampleTable->getTags())), + $scenario->getKeyword(), + $scenario->getLine() + ); } - - $scenario = new OutlineNode( - $scenario->getTitle(), - $scenario->getTags(), - $scenario->getSteps(), - new ExampleTableNode($filteredTable, $scenario->getExampleTable()->getKeyword()), - $scenario->getKeyword(), - $scenario->getLine() - ); } } diff --git a/src/Behat/Gherkin/Filter/LineRangeFilter.php b/src/Behat/Gherkin/Filter/LineRangeFilter.php index b8062bed..297af983 100644 --- a/src/Behat/Gherkin/Filter/LineRangeFilter.php +++ b/src/Behat/Gherkin/Filter/LineRangeFilter.php @@ -94,15 +94,24 @@ public function filterFeature(FeatureNode $feature) } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { - $table = $scenario->getExampleTable()->getTable(); - $lines = array_keys($table); + // first accumulate examples and then create scenario + $exampleTableNodes = array(); - $filteredTable = array($lines[0] => $table[$lines[0]]); - unset($table[$lines[0]]); + foreach ($scenario->getExampleTables() as $exampleTable) { + $table = $exampleTable->getTable(); + $lines = array_keys($table); - foreach ($table as $line => $row) { - if ($this->filterMinLine <= $line && $this->filterMaxLine >= $line) { - $filteredTable[$line] = $row; + $filteredTable = array($lines[0] => $table[$lines[0]]); + unset($table[$lines[0]]); + + foreach ($table as $line => $row) { + if ($this->filterMinLine <= $line && $this->filterMaxLine >= $line) { + $filteredTable[$line] = $row; + } + } + + if (count($filteredTable) > 1) { + $exampleTableNodes[] = new ExampleTableNode($filteredTable, $exampleTable->getKeyword(), $exampleTable->getTags()); } } @@ -110,7 +119,7 @@ public function filterFeature(FeatureNode $feature) $scenario->getTitle(), $scenario->getTags(), $scenario->getSteps(), - new ExampleTableNode($filteredTable, $scenario->getExampleTable()->getKeyword()), + $exampleTableNodes, $scenario->getKeyword(), $scenario->getLine() ); diff --git a/src/Behat/Gherkin/Filter/TagFilter.php b/src/Behat/Gherkin/Filter/TagFilter.php index fed6c1af..51f1cfde 100644 --- a/src/Behat/Gherkin/Filter/TagFilter.php +++ b/src/Behat/Gherkin/Filter/TagFilter.php @@ -11,6 +11,7 @@ namespace Behat\Gherkin\Filter; use Behat\Gherkin\Node\FeatureNode; +use Behat\Gherkin\Node\OutlineNode; use Behat\Gherkin\Node\ScenarioInterface; /** @@ -32,6 +33,57 @@ public function __construct($filterString) $this->filterString = trim($filterString); } + /** + * Filters feature according to the filter. + * + * @param FeatureNode $feature + * + * @return FeatureNode + */ + public function filterFeature(FeatureNode $feature) + { + $scenarios = array(); + foreach ($feature->getScenarios() as $scenario) { + if (!$this->isScenarioMatch($feature, $scenario)) { + continue; + } + + if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { + + $exampleTables = array(); + + foreach ($scenario->getExampleTables() as $exampleTable) { + if ($this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags(), $exampleTable->getTags()))) { + $exampleTables[] = $exampleTable; + } + } + + $scenario = new OutlineNode( + $scenario->getTitle(), + $scenario->getTags(), + $scenario->getSteps(), + $exampleTables, + $scenario->getKeyword(), + $scenario->getLine() + ); + } + + $scenarios[] = $scenario; + } + + return new FeatureNode( + $feature->getTitle(), + $feature->getDescription(), + $feature->getTags(), + $feature->getBackground(), + $scenarios, + $feature->getKeyword(), + $feature->getLanguage(), + $feature->getFile(), + $feature->getLine() + ); + } + /** * Checks if Feature matches specified filter. * @@ -47,13 +99,23 @@ public function isFeatureMatch(FeatureNode $feature) /** * Checks if scenario or outline matches specified filter. * - * @param FeatureNode $feature Feature node instance + * @param FeatureNode $feature Feature node instance * @param ScenarioInterface $scenario Scenario or Outline node instance * * @return Boolean */ public function isScenarioMatch(FeatureNode $feature, ScenarioInterface $scenario) { + if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { + foreach ($scenario->getExampleTables() as $example) { + if ($this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags(), $example->getTags()))) { + return true; + } + } + + return false; + } + return $this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags())); } diff --git a/src/Behat/Gherkin/Loader/ArrayLoader.php b/src/Behat/Gherkin/Loader/ArrayLoader.php index 3492d6e6..314c3a07 100644 --- a/src/Behat/Gherkin/Loader/ArrayLoader.php +++ b/src/Behat/Gherkin/Loader/ArrayLoader.php @@ -119,7 +119,26 @@ protected function loadBackgroundHash(array $hash) $steps = $this->loadStepsHash($hash['steps']); - return new BackgroundNode($hash['title'], $steps, $hash['keyword'], $hash['line']); + $examples = array(); + if (isset($hash['examples'])) { + if (isset($hash['examples']['keyword'])) { + $examplesKeyword = $hash['examples']['keyword']; + unset($hash['examples']['keyword']); + } else { + $examplesKeyword = 'Examples'; + } + + $exHash = $hash['examples']; + + if ($this->examplesAreInArray($exHash)) { + $examples = $this->processExamplesArray($exHash, $examplesKeyword, $examples); + } else { + // examples as a single table - we create an array with the only one element + $examples[] = new ExampleTableNode($exHash, $examplesKeyword);; + } + } + + return new BackgroundNode($hash['title'], $steps, $hash['keyword'], $hash['line'], $examples); } /** @@ -179,7 +198,15 @@ protected function loadOutlineHash(array $hash, $line = 0) $examplesKeyword = 'Examples'; } - $examples = new ExampleTableNode($hash['examples'], $examplesKeyword); + $exHash = $hash['examples']; + $examples = array(); + + if ($this->examplesAreInArray($exHash)) { + $examples = $this->processExamplesArray($exHash, $examplesKeyword, $examples); + } else { + // examples as a single table - we create an array with the only one element + $examples[] = new ExampleTableNode($exHash, $examplesKeyword);; + } return new OutlineNode($hash['title'], $hash['tags'], $steps, $examples, $hash['keyword'], $hash['line']); } @@ -266,4 +293,39 @@ protected function loadPyStringHash(array $hash, $line = 0) return new PyStringNode($strings, $line); } + + /** + * Checks if examples node is an array + * @param $exHash object hash + * @return bool + */ + private function examplesAreInArray($exHash) + { + return isset($exHash[0]); + } + + /** + * Processes cases when examples are in the form of array of arrays + * OR in the form of array of objects + * + * @param $exHash array hash + * @param $examplesKeyword string + * @param $examples array + * @return array + */ + private function processExamplesArray($exHash, $examplesKeyword, $examples) + { + for ($i = 0; $i < count($exHash); $i++) { + if (isset($exHash[$i]['table'])) { + // we have examples as objects, hence there could be tags + $exHashTags = isset($exHash[$i]['tags']) ? $exHash[$i]['tags'] : array(); + $examples[] = new ExampleTableNode($exHash[$i]['table'], $examplesKeyword, $exHashTags); + } else { + // we have examples as arrays + $examples[] = new ExampleTableNode($exHash[$i], $examplesKeyword); + } + } + + return $examples; + } } diff --git a/src/Behat/Gherkin/Node/BackgroundNode.php b/src/Behat/Gherkin/Node/BackgroundNode.php index fb1edb4b..909963f3 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,23 @@ class BackgroundNode implements ScenarioLikeInterface * @param StepNode[] $steps * @param string $keyword * @param integer $line + * @param null|ExampleTableNode|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; + if ($exampleTable === null) { + $this->exampleTable = null; + }else if (is_array($exampleTable)) { + if (!empty($exampleTable)) { + $this->exampleTable = $exampleTable; + } + } else { + $this->$exampleTable = array($exampleTable); + } } /** @@ -109,4 +123,34 @@ public function getLine() { return $this->line; } + + /** + * Returns if background has ExampleTable + * + * @return boolean + */ + public function hasExamples() + { + return $this->exampleTable !== null && count($this->exampleTable) > 0; + } + + /** + * Returns if background has ExampleTable + * + * @return null|ExampleTableNode[] + */ + public function getExamples() + { + return $this->exampleTable; + } + + /** + * Add example Table to the background Node + * + * @param ExampleTableNode + */ + public function addExample($table) + { + $this->exampleTable[] = $table; + } } diff --git a/src/Behat/Gherkin/Node/ExampleTableNode.php b/src/Behat/Gherkin/Node/ExampleTableNode.php index 805e659e..91753511 100644 --- a/src/Behat/Gherkin/Node/ExampleTableNode.php +++ b/src/Behat/Gherkin/Node/ExampleTableNode.php @@ -17,6 +17,11 @@ */ class ExampleTableNode extends TableNode { + /** + * @var string[] + */ + private $tags; + /** * @var string */ @@ -25,12 +30,14 @@ class ExampleTableNode extends TableNode /** * Initializes example table. * - * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] + * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] * @param string $keyword + * @param string[] $tags */ - public function __construct(array $table, $keyword) + public function __construct(array $table, $keyword, array $tags = array()) { $this->keyword = $keyword; + $this->tags = $tags; parent::__construct($table); } @@ -45,6 +52,15 @@ public function getNodeType() return 'ExampleTable'; } + /** + * Returns attached tags + * @return \string[] + */ + public function getTags() + { + return $this->tags; + } + /** * Returns example table keyword. * diff --git a/src/Behat/Gherkin/Node/OutlineNode.php b/src/Behat/Gherkin/Node/OutlineNode.php index 62f55181..04720750 100644 --- a/src/Behat/Gherkin/Node/OutlineNode.php +++ b/src/Behat/Gherkin/Node/OutlineNode.php @@ -30,9 +30,9 @@ class OutlineNode implements ScenarioInterface */ private $steps; /** - * @var ExampleTableNode + * @var ExampleTableNode|ExampleTableNode[] */ - private $table; + private $tables; /** * @var string */ @@ -52,7 +52,7 @@ class OutlineNode implements ScenarioInterface * @param null|string $title * @param string[] $tags * @param StepNode[] $steps - * @param ExampleTableNode $table + * @param null|ExampleTableNode|ExampleTableNode[] $tables * @param string $keyword * @param integer $line */ @@ -60,16 +60,20 @@ public function __construct( $title, array $tags, array $steps, - ExampleTableNode $table, + $tables, $keyword, $line ) { $this->title = $title; $this->tags = $tags; $this->steps = $steps; - $this->table = $table; $this->keyword = $keyword; $this->line = $line; + if (!is_array($tables)) { + $this->tables = array($tables); + } else { + $this->tables = $tables; + } } /** @@ -151,27 +155,60 @@ public function getSteps() */ public function hasExamples() { - return 0 < count($this->table->getColumnsHash()); + return 0 < count($this->tables); + } + + /** + * Add Example to the outline. + * + * @param ExampleTableNode + * + * @return void + */ + public function setExampleTable($table) + { + $this->tables[] = $table; } /** - * Returns examples table. + * Builds and returns examples table for the outline. * + * WARNING: it returns a merged table with tags lost. + * + * @deprecated use getExampleTables instead * @return ExampleTableNode */ public function getExampleTable() { - return $this->table; + $table = array(); + foreach ($this->tables[0]->getTable() as $k => $v) { + $table[$k] = $v; + } + + /** @var ExampleTableNode $exampleTableNode */ + $exampleTableNode = new ExampleTableNode($table, $this->tables[0]->getKeyword()); + for ($i = 1; $i < count($this->tables); $i++) { + $exampleTableNode->mergeRowsFromTable($this->tables[$i]); + } + return $exampleTableNode; } /** * Returns list of examples for the outline. - * * @return ExampleNode[] */ public function getExamples() { - return $this->examples = $this->examples ? : $this->createExamples(); + return $this->examples = $this->examples ?: $this->createExamples(); + } + + /** + * Returns examples tables array for the outline. + * @return ExampleTableNode[] + */ + public function getExampleTables() + { + return $this->tables; } /** @@ -202,15 +239,17 @@ public function getLine() protected function createExamples() { $examples = array(); - foreach ($this->table->getColumnsHash() as $rowNum => $row) { - $examples[] = new ExampleNode( - $this->table->getRowAsString($rowNum + 1), - $this->tags, - $this->getSteps(), - $row, - $this->table->getRowLine($rowNum + 1), - $this->getTitle() - ); + + foreach ($this->getExampleTables() as $exampleTable) { + foreach ($exampleTable->getColumnsHash() as $rowNum => $row) { + $examples[] = new ExampleNode( + $exampleTable->getRowAsString($rowNum + 1), + array_merge($this->tags, $exampleTable->getTags()), + $this->getSteps(), + $row, + $exampleTable->getRowLine($rowNum + 1) + ); + } } return $examples; diff --git a/src/Behat/Gherkin/Node/TableNode.php b/src/Behat/Gherkin/Node/TableNode.php index 735e688e..3a73dcc1 100644 --- a/src/Behat/Gherkin/Node/TableNode.php +++ b/src/Behat/Gherkin/Node/TableNode.php @@ -328,6 +328,30 @@ public function getIterator() return new ArrayIterator($this->getHash()); } + /** + * Obtains and adds rows from another table to the current table. + * The second table should have the same structure as the current one. + * @param TableNode $node + * + * @deprecated remove together with OutlineNode::getExampleTable + */ + public function mergeRowsFromTable(TableNode $node) + { + // check structure + if ($this->getRow(0) !== $node->getRow(0)) { + throw new NodeException("Tables have different structure. Cannot merge one into another"); + } + + $firstLine = $node->getLine(); + foreach ($node->getTable() as $line => $value) { + if ($line === $firstLine) { + continue; + } + + $this->table[$line] = $value; + } + } + /** * Pads string right. * diff --git a/src/Behat/Gherkin/Parser.php b/src/Behat/Gherkin/Parser.php index 5cc85424..4046ad77 100644 --- a/src/Behat/Gherkin/Parser.php +++ b/src/Behat/Gherkin/Parser.php @@ -39,6 +39,8 @@ class Parser private $tags = array(); private $languageSpecifierLine; + private $passedNodesStack = array(); + /** * Initializes parser. * @@ -236,6 +238,8 @@ protected function parseFeature() $file = $this->file; $line = $token['line']; + array_push($this->passedNodesStack, 'Feature'); + // Parse description, background, scenarios & outlines while ('EOS' !== $this->predictTokenType()) { $node = $this->parseExpression(); @@ -256,6 +260,11 @@ protected function parseFeature() continue; } + if ($node instanceof ExampleTableNode) { + $background->addExample($node); + continue; + } + if ($background instanceof BackgroundNode && $node instanceof BackgroundNode) { throw new ParserException(sprintf( 'Each Feature could have only one Background, but found multiple on lines %d and %d%s', @@ -275,6 +284,31 @@ 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()) { + if (is_array($background->getExamples())) { + foreach ($background->getExamples() as $ex) { + $scenario->setExampleTable($ex); + } + } else { + $scenario->setExampleTable($background->getExamples()); + } + } + } + } + return new FeatureNode( rtrim($title) ?: null, rtrim($description) ?: null, @@ -302,6 +336,7 @@ protected function parseBackground() $title = trim($token['value']); $keyword = $token['keyword']; $line = $token['line']; + $example = []; if (count($this->popTags())) { throw new ParserException(sprintf( @@ -313,10 +348,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 +390,7 @@ protected function parseBackground() } } - return new BackgroundNode(rtrim($title) ?: null, $steps, $keyword, $line); + return new BackgroundNode(rtrim($title) ?: null, $steps, $keyword, $line, $example); } /** @@ -369,6 +409,8 @@ protected function parseScenario() $keyword = $token['keyword']; $line = $token['line']; + array_push($this->passedNodesStack, 'Scenario'); + // Parse description and steps $steps = array(); while (in_array($this->predictTokenType(), array('Step', 'Newline', 'Text', 'Comment'))) { @@ -407,6 +449,8 @@ protected function parseScenario() } } + array_pop($this->passedNodesStack); + return new ScenarioNode(rtrim($title) ?: null, $tags, $steps, $keyword, $line); } @@ -424,12 +468,17 @@ protected function parseOutline() $title = trim($token['value']); $tags = $this->popTags(); $keyword = $token['keyword']; - $examples = null; + + /** @var ExampleTableNode $examples */ + $examples = array(); $line = $token['line']; // Parse description, steps and examples $steps = array(); - while (in_array($this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment'))) { + + array_push($this->passedNodesStack, 'Outline'); + + while (in_array($this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment', 'Tag'))) { $node = $this->parseExpression(); if ($node instanceof StepNode) { @@ -438,7 +487,8 @@ protected function parseOutline() } if ($node instanceof ExampleTableNode) { - $examples = $node; + $examples[] = $node; + continue; } @@ -470,15 +520,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); } @@ -496,6 +537,8 @@ protected function parseStep() $text = trim($token['text']); $line = $token['line']; + array_push($this->passedNodesStack, 'Step'); + $arguments = array(); while (in_array($predicted = $this->predictTokenType(), array('PyStringOp', 'TableRow', 'Newline', 'Comment'))) { if ('Comment' === $predicted || 'Newline' === $predicted) { @@ -510,6 +553,8 @@ protected function parseStep() } } + array_pop($this->passedNodesStack); + return new StepNode($keyword, $text, $arguments, $line, $keywordType); } @@ -524,7 +569,9 @@ protected function parseExamples() $keyword = $token['keyword']; - return new ExampleTableNode($this->parseTableRows(), $keyword); + $tags = empty($this->tags) ? array() : $this->popTags(); + + return new ExampleTableNode($this->parseTableRows(), $keyword, $tags); } /** @@ -570,7 +617,25 @@ protected function parseTags() $token = $this->expectTokenType('Tag'); $this->tags = array_merge($this->tags, $token['tags']); - return $this->parseExpression(); + $possibleTransitions = array( + 'Outline' => array( + 'Examples', + 'Step' + ) + ); + + $currentType = '-1'; + // check if that is ok to go inside: + if (!empty($this->passedNodesStack)) { + $currentType = $this->passedNodesStack[count($this->passedNodesStack) - 1]; + } + + $nextType = $this->predictTokenType(); + if (!isset($possibleTransitions[$currentType]) || in_array($nextType, $possibleTransitions[$currentType])) { + return $this->parseExpression(); + } + + return "\n"; } /** diff --git a/tests/Behat/Gherkin/Filter/FilterTest.php b/tests/Behat/Gherkin/Filter/FilterTest.php index 994e6564..abd71dad 100644 --- a/tests/Behat/Gherkin/Filter/FilterTest.php +++ b/tests/Behat/Gherkin/Filter/FilterTest.php @@ -49,11 +49,17 @@ protected function getGherkinFeature() When occurs Then should be visible + @etag1 Examples: | action | outcome | | act#1 | out#1 | | act#2 | out#2 | + + @etag2 + Examples: + | action | outcome | | act#3 | out#3 | + GHERKIN; } diff --git a/tests/Behat/Gherkin/Filter/LineFilterTest.php b/tests/Behat/Gherkin/Filter/LineFilterTest.php index 846a719d..5e8be7d3 100644 --- a/tests/Behat/Gherkin/Filter/LineFilterTest.php +++ b/tests/Behat/Gherkin/Filter/LineFilterTest.php @@ -67,37 +67,42 @@ public function testFilterFeatureOutline() { $filter = new LineFilter(13); $feature = $filter->filterFeature($this->getParsedFeature()); + /** @var OutlineNode[] $scenarios */ $this->assertCount(1, $scenarios = $feature->getScenarios()); $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); $this->assertCount(4, $scenarios[0]->getExampleTable()->getRows()); - $filter = new LineFilter(19); + $filter = new LineFilter(20); $feature = $filter->filterFeature($this->getParsedFeature()); $this->assertCount(1, $scenarios = $feature->getScenarios()); $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(2, $scenarios[0]->getExampleTable()->getRows()); + $exampleTableNodes = $scenarios[0]->getExampleTables(); + $this->assertEquals(1, count($exampleTableNodes)); + $this->assertCount(2, $exampleTableNodes[0]->getRows()); $this->assertSame(array( array('action', 'outcome'), array('act#1', 'out#1'), - ), $scenarios[0]->getExampleTable()->getRows()); + ), $exampleTableNodes[0]->getRows()); + $this->assertEquals(array('etag1'), $exampleTableNodes[0]->getTags()); - $filter = new LineFilter(21); + $filter = new LineFilter(26); $feature = $filter->filterFeature($this->getParsedFeature()); $this->assertCount(1, $scenarios = $feature->getScenarios()); $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(2, $scenarios[0]->getExampleTable()->getRows()); + $exampleTableNodes = $scenarios[0]->getExampleTables(); + $this->assertEquals(1, count($exampleTableNodes)); + $this->assertCount(2, $exampleTableNodes[0]->getRows()); $this->assertSame(array( array('action', 'outcome'), array('act#3', 'out#3'), - ), $scenarios[0]->getExampleTable()->getRows()); + ), $exampleTableNodes[0]->getRows()); + $this->assertEquals(array('etag2'), $exampleTableNodes[0]->getTags()); - $filter = new LineFilter(18); + $filter = new LineFilter(19); $feature = $filter->filterFeature($this->getParsedFeature()); $this->assertCount(1, $scenarios = $feature->getScenarios()); $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); $this->assertCount(1, $scenarios[0]->getExampleTable()->getRows()); - $this->assertSame(array( - array('action', 'outcome'), - ), $scenarios[0]->getExampleTable()->getRows()); + $this->assertSame(array(array('action', 'outcome')), $scenarios[0]->getExampleTable()->getRows()); } } diff --git a/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php b/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php index fb8abe1c..a27c9a71 100644 --- a/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php +++ b/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php @@ -53,7 +53,7 @@ public function scenarioLineRangeProvider() public function testIsScenarioMatchFilter($filterMinLine, $filterMaxLine, $expectedNumberOfMatches) { $scenario = new ScenarioNode(null, array(), array(), null, 2); - $outline = new OutlineNode(null, array(), array(), new ExampleTableNode(array(), null), null, 3); + $outline = new OutlineNode(null, array(), array(), array(new ExampleTableNode(array(), null)), null, 3); $filter = new LineRangeFilter($filterMinLine, $filterMaxLine); $this->assertEquals( @@ -83,19 +83,59 @@ public function testFilterFeatureOutline() { $filter = new LineRangeFilter(12, 14); $feature = $filter->filterFeature($this->getParsedFeature()); + /** @var OutlineNode[] $scenarios */ $this->assertCount(1, $scenarios = $feature->getScenarios()); $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(1, $scenarios[0]->getExampleTable()->getRows()); + $this->assertFalse($scenarios[0]->hasExamples()); - $filter = new LineRangeFilter(15, 20); + $filter = new LineRangeFilter(16, 21); $feature = $filter->filterFeature($this->getParsedFeature()); $this->assertCount(1, $scenarios = $feature->getScenarios()); $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(3, $scenarios[0]->getExampleTable()->getRows()); + $exampleTableNodes = $scenarios[0]->getExampleTables(); + $this->assertEquals(1, count($exampleTableNodes)); + $this->assertCount(3, $exampleTableNodes[0]->getRows()); $this->assertSame(array( array('action', 'outcome'), array('act#1', 'out#1'), array('act#2', 'out#2'), - ), $scenarios[0]->getExampleTable()->getRows()); + ), $exampleTableNodes[0]->getRows()); + $this->assertEquals(array('etag1'), $exampleTableNodes[0]->getTags()); + + $filter = new LineRangeFilter(16, 26); + $feature = $filter->filterFeature($this->getParsedFeature()); + $this->assertCount(1, $scenarios = $feature->getScenarios()); + $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); + $exampleTableNodes = $scenarios[0]->getExampleTables(); + $this->assertEquals(2, count($exampleTableNodes)); + + $this->assertCount(3, $exampleTableNodes[0]->getRows()); + $this->assertSame(array( + array('action', 'outcome'), + array('act#1', 'out#1'), + array('act#2', 'out#2'), + ), $exampleTableNodes[0]->getRows()); + $this->assertEquals(array('etag1'), $exampleTableNodes[0]->getTags()); + + $this->assertCount(2, $exampleTableNodes[1]->getRows()); + $this->assertSame(array( + array('action', 'outcome'), + array('act#3', 'out#3') + ), $exampleTableNodes[1]->getRows()); + + $this->assertEquals(array('etag2'), $exampleTableNodes[1]->getTags()); + + $filter = new LineRangeFilter(25, 26); + $feature = $filter->filterFeature($this->getParsedFeature()); + $this->assertCount(1, $scenarios = $feature->getScenarios()); + $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); + $exampleTableNodes = $scenarios[0]->getExampleTables(); + $this->assertEquals(1, count($exampleTableNodes)); + $this->assertCount(2, $exampleTableNodes[0]->getRows()); + $this->assertSame(array( + array('action', 'outcome'), + array('act#3', 'out#3'), + ), $exampleTableNodes[0]->getRows()); + $this->assertEquals(array('etag2'), $exampleTableNodes[0]->getTags()); } } diff --git a/tests/Behat/Gherkin/Filter/TagFilterTest.php b/tests/Behat/Gherkin/Filter/TagFilterTest.php index 5c6c9ab2..ce2229cc 100644 --- a/tests/Behat/Gherkin/Filter/TagFilterTest.php +++ b/tests/Behat/Gherkin/Filter/TagFilterTest.php @@ -3,7 +3,9 @@ namespace Tests\Behat\Gherkin\Filter; use Behat\Gherkin\Filter\TagFilter; +use Behat\Gherkin\Node\ExampleTableNode; use Behat\Gherkin\Node\FeatureNode; +use Behat\Gherkin\Node\OutlineNode; use Behat\Gherkin\Node\ScenarioNode; class TagFilterTest extends \PHPUnit_Framework_TestCase @@ -140,5 +142,129 @@ public function testIsScenarioMatchFilter() $filter = new TagFilter('@feature-tag&&@user'); $scenario = new ScenarioNode(null, array('wip'), array(), null, 2); $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); + + $scenario = new OutlineNode(null, array('wip'), array(), array( + new ExampleTableNode(array(), null, array('etag1', 'etag2')), + new ExampleTableNode(array(), null, array('etag2', 'etag3')), + ), null, 2); + + $tagFilter = new TagFilter('@etag3'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('~@etag3'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@wip'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@wip&&@etag3'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@feature-tag&&@etag1&&@wip'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@feature-tag&&~@etag11111&&@wip'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@feature-tag&&~@etag1&&@wip'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@feature-tag&&@etag2'); + $this->assertTrue($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('~@etag1&&~@etag3'); + $this->assertFalse($tagFilter->isScenarioMatch($feature, $scenario)); + + $tagFilter = new TagFilter('@etag1&&@etag3'); + $this->assertFalse($tagFilter->isScenarioMatch($feature, $scenario), "Tags from different examples tables"); + } + + public function testFilterFeatureWithTaggedExamples() + { + $exampleTableNode1 = new ExampleTableNode(array(), null, array('etag1', 'etag2')); + $exampleTableNode2 = new ExampleTableNode(array(), null, array('etag2', 'etag3')); + $scenario = new OutlineNode(null, array('wip'), array(), array( + $exampleTableNode1, + $exampleTableNode2, + ), null, 2); + $feature = new FeatureNode(null, null, array('feature-tag'), null, array($scenario), null, null, null, 1); + + $tagFilter = new TagFilter('@etag2'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + $this->assertEquals($scenario, $scenarioInterfaces[0]); + + $tagFilter = new TagFilter('@etag1'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode1), $scenarioInterfaces[0]->getExampleTables()); + + $tagFilter = new TagFilter('~@etag3'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode1), $scenarioInterfaces[0]->getExampleTables()); + + $tagFilter = new TagFilter('@wip'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + $this->assertEquals($scenario, $scenarioInterfaces[0]); + + $tagFilter = new TagFilter('@wip&&@etag3'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode2), $scenarioInterfaces[0]->getExampleTables()); + + $tagFilter = new TagFilter('@feature-tag&&@etag1&&@wip'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode1), $scenarioInterfaces[0]->getExampleTables()); + + $tagFilter = new TagFilter('@feature-tag&&~@etag11111&&@wip'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + $this->assertEquals($scenario, $scenarioInterfaces[0]); + + $tagFilter = new TagFilter('@feature-tag&&~@etag1&&@wip'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode2), $scenarioInterfaces[0]->getExampleTables()); + + $tagFilter = new TagFilter('@feature-tag&&@etag2'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + $this->assertEquals($scenario, $scenarioInterfaces[0]); + + $exampleTableNode1 = new ExampleTableNode(array(), null, array('etag1', 'etag')); + $exampleTableNode2 = new ExampleTableNode(array(), null, array('etag2', 'etag22', 'etag')); + $exampleTableNode3 = new ExampleTableNode(array(), null, array('etag3', 'etag22', 'etag')); + $exampleTableNode4 = new ExampleTableNode(array(), null, array('etag4', 'etag')); + $scenario1 = new OutlineNode(null, array('wip'), array(), array( + $exampleTableNode1, + $exampleTableNode2, + ), null, 2); + $scenario2 = new OutlineNode(null, array('wip'), array(), array( + $exampleTableNode3, + $exampleTableNode4, + ), null, 2); + $feature = new FeatureNode(null, null, array('feature-tag'), null, array($scenario1, $scenario2), null, null, null, 1); + + $tagFilter = new TagFilter('@etag'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + $this->assertEquals(array($scenario1, $scenario2), $scenarioInterfaces); + + $tagFilter = new TagFilter('@etag22'); + $matched = $tagFilter->filterFeature($feature); + $scenarioInterfaces = $matched->getScenarios(); + $this->assertEquals(2, count($scenarioInterfaces)); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode2), $scenarioInterfaces[0]->getExampleTables()); + /** @noinspection PhpUndefinedMethodInspection */ + $this->assertEquals(array($exampleTableNode3), $scenarioInterfaces[1]->getExampleTables()); } } 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/etalons/background_with_outline_multi_examples.yml b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi_examples.yml new file mode 100644 index 00000000..cfec81e4 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/etalons/background_with_outline_multi_examples.yml @@ -0,0 +1,62 @@ +feature: + title: Feature with multiple 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] + + - + tags: [specialChars] + table: + 12: [login, password] + 13: [$hello##, normalPass] + 14: [normalUser, p@$$word] + + scenarios: + - + type: outline + title: Scenario outline without example + line: 16 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'I have browsed to login page', line: 17 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 18 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 19 } + examples: + - 6: [login, password] + 7: ['', ''] + 8: [unknown_user, known_pass] + + - + tags: [specialChars] + table: + 12: [login, password] + 13: [$hello##, normalPass] + 14: [normalUser, p@$$word] + + - type: scenario + title: A regular scenario + line: 21 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'I have browsed to login page', line: 22 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with "user"', line: 23 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with "password"', line: 24 } + + - type: outline + title: Scenario outline with it's own example table + line: 26 + steps: + - { keyword_type: 'Given', type: 'Given', text: 'I have browsed to login page', line: 27 } + - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 28 } + - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 29 } + examples: + 31: [ login, password ] + 32: [ hello, world ] + 33: [ user , pass ] \ No newline at end of file diff --git a/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml b/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml index 6ac42d4a..c2b7a2b0 100644 --- a/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml +++ b/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml @@ -15,7 +15,7 @@ feature: title: '2つの数の加算について' line: 7 steps: - - { keyword_type: 'Given', type: '前提', text: '50 を入力', line: 8 } - - { keyword_type: 'Given', type: 'かつ', text: '70 を入力', line: 9 } - - { keyword_type: 'When', type: 'もし', text: 'add ボタンを押した', line: 10 } - - { keyword_type: 'Then', type: 'ならば', text: '結果は 120 を表示', line: 11 } + - { keyword_type: 'Given', type: '前提', text: '50 を入力', line: 8 } + - { keyword_type: 'Given', type: 'かつ', text: '70 を入力', line: 9 } + - { keyword_type: 'When', type: 'もし', text: 'add ボタンを押した', line: 10 } + - { keyword_type: 'Then', type: 'ならば', text: '結果は 120 を表示', line: 11 } diff --git a/tests/Behat/Gherkin/Fixtures/etalons/outline_with_multiple_examples.yml b/tests/Behat/Gherkin/Fixtures/etalons/outline_with_multiple_examples.yml new file mode 100644 index 00000000..e980b297 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/etalons/outline_with_multiple_examples.yml @@ -0,0 +1,55 @@ +feature: + title: Unsubstituted argument placeholder + language: en + line: 1 + description: ~ + + scenarios: + - + type: outline + title: 'See Annual Leave Details (as Management & Human Resource)' + line: 3 + steps: + - + keyword_type: Given + type: Given + text: the exist in the system + line: 4 + examples: + - + 7: [ role, name ] + 8: [ HUMAN RESOURCE, abc ] + - + 11: [ role, name ] + 12: [ MANAGER, cde ] + - + 15: [ role, name ] + 16: [ CEO, qqq ] + 17: [ CTO, xxx ] + - + type: outline + title: 'See Annual Leave Details (as Management & Human Resource)' + line: 20 + steps: + - + keyword_type: Given + type: Given + text: the exist in the system + line: 21 + examples: + - + tags: [tag1, tag2] + table: + 25: [ role, name ] + 26: [ HUMAN RESOURCE, abc ] + - + tags: [tag1, tag3] + table: + 30: [ role, name ] + 31: [ MANAGER, cde ] + - + tags: [tag4] + table: + 34: [ role, name ] + 35: [ CEO, qqq ] + 36: [ CTO, xxx ] \ No newline at end of file diff --git a/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml b/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml index 486d9585..82f25e1a 100644 --- a/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml +++ b/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml @@ -23,13 +23,41 @@ feature: - { keyword_type: 'Given', type: 'Given', text: '', line: 10 } examples: - 12: [state] - 13: [missing] + - + tags: [examples_tag, examples_tag2] + table: + 13: [state] + 14: [missing] - type: scenario title: Skipped tags: [sample_three, sample_four] - line: 16 + line: 17 steps: - - { keyword_type: 'Given', type: 'Given', text: 'missing', line: 17 } + - { keyword_type: 'Given', type: 'Given', text: 'missing', line: 18 } + + - + type: outline + title: passing + tags: [sample_5] + line: 22 + steps: + - { keyword_type: 'Given', type: 'Given', text: '', line: 23 } + examples: + 25: [state] + 26: [missing] + + - + type: outline + title: passing + tags: [sample_6, sample_7] + line: 29 + steps: + - { keyword_type: 'Given', type: 'Given', text: '', line: 30 } + examples: + - + tags: [examples_tag3, examples_tag4] + table: + 33: [state] + 34: [missing] \ 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/Fixtures/features/background_with_outline_multi_examples.feature b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi_examples.feature new file mode 100644 index 00000000..4fa01e40 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/features/background_with_outline_multi_examples.feature @@ -0,0 +1,33 @@ +Feature: Feature with multiple example in background and multiple scenarios + + Background: + Given a passing step + Examples: + | login | password | + | | | + | unknown_user | known_pass | + + @specialChars + Examples: + | login | password | + | $hello## | normalPass | + | normalUser | p@$$word | + + 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/Fixtures/features/outline_with_multiple_examples.feature b/tests/Behat/Gherkin/Fixtures/features/outline_with_multiple_examples.feature new file mode 100644 index 00000000..a40c3164 --- /dev/null +++ b/tests/Behat/Gherkin/Fixtures/features/outline_with_multiple_examples.feature @@ -0,0 +1,36 @@ +Feature: Unsubstituted argument placeholder + + Scenario Outline: See Annual Leave Details (as Management & Human Resource) + Given the exist in the system + + Examples: + | role | name | + | HUMAN RESOURCE | abc | + + Examples: + | role | name | + | MANAGER | cde | + + Examples: + | role | name | + | CEO | qqq | + | CTO | xxx | + + + Scenario Outline: See Annual Leave Details (as Management & Human Resource) + Given the exist in the system + + @tag1 @tag2 + Examples: + | role | name | + | HUMAN RESOURCE | abc | + + @tag1 @tag3 + Examples: + | role | name | + | MANAGER | cde | + @tag4 + Examples: + | role | name | + | CEO | qqq | + | CTO | xxx | \ No newline at end of file diff --git a/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature b/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature index 21eaaab1..ef3bd626 100644 --- a/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature +++ b/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature @@ -1,17 +1,34 @@ @sample_one Feature: Tag samples - @sample_two @sample_four - Scenario: Passing - Given missing + @sample_two @sample_four + Scenario: Passing + Given missing - @sample_three - Scenario Outline: - Given - Examples: - |state| - |missing| + @sample_three + Scenario Outline: + Given + @examples_tag @examples_tag2 + Examples: + | state | + | missing | - @sample_three @sample_four - Scenario: Skipped - Given missing \ No newline at end of file + @sample_three @sample_four + Scenario: Skipped + Given missing + + + @sample_5 + Scenario Outline: passing + Given + Examples: + | state | + | missing | + + @sample_6 @sample_7 + Scenario Outline: passing + Given + @examples_tag3 @examples_tag4 + Examples: + | state | + | missing | diff --git a/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php b/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php index 697d1d3d..4d38b7c5 100644 --- a/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php +++ b/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php @@ -3,9 +3,11 @@ namespace Tests\Behat\Gherkin\Loader; use Behat\Gherkin\Loader\ArrayLoader; +use Behat\Gherkin\Node\OutlineNode; class ArrayLoaderTest extends \PHPUnit_Framework_TestCase { + /** @var ArrayLoader */ private $loader; protected function setUp() @@ -157,9 +159,9 @@ public function testOutlineExamples() 'title' => 'First outline', 'line' => 2, 'examples' => array( - array('user', 'pass'), - array('ever', 'sdsd'), - array('anto', 'fdfd') + 11 => array('user', 'pass'), + 12 => array('ever', 'sdsd'), + 13 => array('anto', 'fdfd') ) ), array( @@ -173,6 +175,7 @@ public function testOutlineExamples() $this->assertEquals(1, count($features)); + /** @var OutlineNode[] $scenarios */ $scenarios = $features[0]->getScenarios(); $scenario = $scenarios[0]; @@ -217,7 +220,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 +330,8 @@ public function testLoadStepArguments() ) ) ) - ) + ), + 'examples' => null ) ) ) diff --git a/tests/Behat/Gherkin/Node/OutlineNodeTest.php b/tests/Behat/Gherkin/Node/OutlineNodeTest.php index 1e889b80..bbfcc675 100644 --- a/tests/Behat/Gherkin/Node/OutlineNodeTest.php +++ b/tests/Behat/Gherkin/Node/OutlineNodeTest.php @@ -14,22 +14,68 @@ public function testCreatesExamplesForExampleTable() new StepNode('Gangway!', 'I am ', array(), null, 'Given'), new StepNode('Aye!', 'my email is ', array(), null, 'And'), new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), + new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), ); $table = new ExampleTableNode(array( - array('name', 'email'), - array('everzet', 'ever.zet@gmail.com'), - array('example', 'example@example.com') + 2 => array('name', 'email'), + 22 => array('everzet', 'ever.zet@gmail.com'), + 23 => array('example', 'example@example.com') ), 'Examples'); $outline = new OutlineNode(null, array(), $steps, $table, null, null); $this->assertCount(2, $examples = $outline->getExamples()); - $this->assertEquals(1, $examples[0]->getLine()); - $this->assertEquals(2, $examples[1]->getLine()); + $this->assertEquals(22, $examples[0]->getLine()); + $this->assertEquals(23, $examples[1]->getLine()); $this->assertEquals(array('name' => 'everzet', 'email' => 'ever.zet@gmail.com'), $examples[0]->getTokens()); - $this->assertEquals(array('name' => 'example', 'email' => 'example@example.com'), $examples[1]->getTokens()); + $this->assertEquals(array('name' => 'example', 'email' => 'example@example.com'), $examples[1]->getTokens()); + } + + public function testCreatesExamplesForExampleTableWithSeveralExamplesAndTags() + { + $steps = array( + new StepNode('Gangway!', 'I am ', array(), null, 'Given'), + new StepNode('Aye!', 'my email is ', array(), null, 'And'), + new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), + new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), + ); + + $table = new ExampleTableNode(array( + 2 => array('name', 'email'), + 22 => array('everzet', 'ever.zet@gmail.com'), + 23 => array('example', 'example@example.com') + ), 'Examples', array()); + + $table2 = new ExampleTableNode(array( + 3 => array('name', 'email'), + 32 => array('everzet2', 'ever.zet2@gmail.com'), + 33 => array('example2', 'example2@example.com') + ), 'Examples', array('etag1', 'etag2')); + + $outline = new OutlineNode(null, array('otag1', 'otag2'), $steps, array($table, $table2), null, null); + + $this->assertCount(4, $examples = $outline->getExamples()); + $this->assertEquals(22, $examples[0]->getLine()); + $this->assertEquals(23, $examples[1]->getLine()); + $this->assertEquals(32, $examples[2]->getLine()); + $this->assertEquals(33, $examples[3]->getLine()); + $this->assertEquals(array('name' => 'everzet', 'email' => 'ever.zet@gmail.com'), $examples[0]->getTokens()); + $this->assertEquals(array('name' => 'example', 'email' => 'example@example.com'), $examples[1]->getTokens()); + $this->assertEquals(array('name' => 'everzet2', 'email' => 'ever.zet2@gmail.com'), $examples[2]->getTokens()); + $this->assertEquals(array('name' => 'example2', 'email' => 'example2@example.com'), $examples[3]->getTokens()); + + for ($i = 0; $i < 2; $i++) { + foreach (array('otag1', 'otag2') as $tag) { + $this->assertTrue($examples[$i]->hasTag($tag), "there is no tag " . $tag . " in example #" . $i); + } + } + + for ($i = 2; $i < 4; $i++) { + foreach (array('otag1', 'otag2', 'etag1', 'etag2') as $tag) { + $this->assertTrue($examples[$i]->hasTag($tag), "there is no tag " . $tag . " in example #" . $i); + } + } } public function testCreatesEmptyExamplesForEmptyExampleTable() @@ -38,7 +84,7 @@ public function testCreatesEmptyExamplesForEmptyExampleTable() new StepNode('Gangway!', 'I am ', array(), null, 'Given'), new StepNode('Aye!', 'my email is ', array(), null, 'And'), new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), + new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), ); $table = new ExampleTableNode(array( @@ -56,12 +102,12 @@ public function testCreatesEmptyExamplesForNoExampleTable() new StepNode('Gangway!', 'I am ', array(), null, 'Given'), new StepNode('Aye!', 'my email is ', array(), null, 'And'), new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), + new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), ); $table = new ExampleTableNode(array(), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, $table, null, null); + $outline = new OutlineNode(null, array(), $steps, array($table), null, null); $this->assertCount(0, $examples = $outline->getExamples()); } diff --git a/tests/Behat/Gherkin/Node/TableNodeTest.php b/tests/Behat/Gherkin/Node/TableNodeTest.php index a2299551..f19f45ea 100644 --- a/tests/Behat/Gherkin/Node/TableNodeTest.php +++ b/tests/Behat/Gherkin/Node/TableNodeTest.php @@ -254,6 +254,53 @@ public function testFromList() )); $this->assertEquals($expected, $table); } + public function testMergeRowsFromTablePassSeveralTablesShouldBeMerged() + { + $table = new TableNode(array( + 5 => array('id', 'username', 'password'), + 10 => array('42', 'everzet', 'qwerty'), + 13 => array('2', 'antono', 'pa$sword') + )); + + $new = new TableNode(array( + 25 => array('id', 'username', 'password'), + 210 => array('242', '2everzet', '2qwerty'), + 213 => array('22', '2antono', '2pa$sword') + )); + + $new2 = new TableNode(array( + 35 => array('id', 'username', 'password'), + 310 => array('342', '3everzet', '3qwerty'), + 313 => array('32', '3antono', '3pa$sword') + )); + + $table->mergeRowsFromTable($new); + $table->mergeRowsFromTable($new2); + + $this->assertEquals(array('id', 'username', 'password'), $table->getRow(0)); + $this->assertEquals(array('2', 'antono', 'pa$sword'), $table->getRow(2)); + $this->assertEquals(array('242', '2everzet', '2qwerty'), $table->getRow(3)); + $this->assertEquals(array('32', '3antono', '3pa$sword'), $table->getRow(6)); + } + + /** + * @expectedException \Behat\Gherkin\Exception\NodeException + */ + public function testMergeRowsFromTableWrongHeaderNameExceptionThrown() + { + $table = new TableNode(array( + 5 => array('id', 'username', 'password'), + 10 => array('42', 'everzet', 'qwerty'), + 13 => array('2', 'antono', 'pa$sword') + )); + + $new = new TableNode(array( + 25 => array('id', 'QWE', 'password'), + 210 => array('242', '2everzet', '2qwerty') + )); + + $table->mergeRowsFromTable($new); + } /** * @expectedException \Behat\Gherkin\Exception\NodeException @@ -266,4 +313,41 @@ public function testGetTableFromListWithMultidimensionalArrayArgument() )); } + /** + * @expectedException \Behat\Gherkin\Exception\NodeException + */ + public function testMergeRowsFromTableWrongHeaderOrderExceptionThrown() + { + $table = new TableNode(array( + 5 => array('id', 'username', 'password'), + 10 => array('42', 'everzet', 'qwerty'), + 13 => array('2', 'antono', 'pa$sword') + )); + + $new = new TableNode(array( + 25 => array('id', 'password', 'username'), + 210 => array('242', '2everzet', '2qwerty') + )); + + $table->mergeRowsFromTable($new); + } + + /** + * @expectedException \Behat\Gherkin\Exception\NodeException + */ + public function testMergeRowsFromTableWrongHeaderSizeExceptionThrown() + { + $table = new TableNode(array( + 5 => array('id', 'username', 'password'), + 10 => array('42', 'everzet', 'qwerty'), + 13 => array('2', 'antono', 'pa$sword') + )); + + $new = new TableNode(array( + 25 => array('id', 'username'), + 210 => array('242', '2everzet') + )); + + $table->mergeRowsFromTable($new); + } } diff --git a/tests/Behat/Gherkin/ParserTest.php b/tests/Behat/Gherkin/ParserTest.php index 75ea45a2..2a772860 100644 --- a/tests/Behat/Gherkin/ParserTest.php +++ b/tests/Behat/Gherkin/ParserTest.php @@ -36,6 +36,8 @@ public function testParser($fixtureName) $etalon = $this->parseEtalon($fixtureName . '.yml'); $features = $this->parseFixture($fixtureName . '.feature'); +// print_r($etalon->getBackground()); +// print_r($features[0]->getBackground()); $this->assertInternalType('array', $features); $this->assertEquals(1, count($features)); $fixture = $features[0];