From 6a89c9b017f885bfee98c38c97ba23c28f19c4d6 Mon Sep 17 00:00:00 2001 From: Andrey Stukalin Date: Thu, 10 Nov 2016 13:51:07 +0100 Subject: [PATCH 1/4] Multiple example tables and tags support #117 --- src/Behat/Gherkin/Filter/LineFilter.php | 36 ++--- src/Behat/Gherkin/Filter/LineRangeFilter.php | 25 ++-- src/Behat/Gherkin/Filter/TagFilter.php | 57 +++++++- src/Behat/Gherkin/Loader/ArrayLoader.php | 38 +++++- src/Behat/Gherkin/Node/ExampleTableNode.php | 20 ++- src/Behat/Gherkin/Node/OutlineNode.php | 63 ++++++--- src/Behat/Gherkin/Node/TableNode.php | 24 ++++ src/Behat/Gherkin/Parser.php | 49 ++++++- tests/Behat/Gherkin/Filter/FilterTest.php | 6 + tests/Behat/Gherkin/Filter/LineFilterTest.php | 27 ++-- .../Gherkin/Filter/LineRangeFilterTest.php | 50 ++++++- tests/Behat/Gherkin/Filter/TagFilterTest.php | 126 ++++++++++++++++++ .../Gherkin/Fixtures/etalons/ja_addition.yml | 8 +- .../outline_with_multiple_examples.yml | 55 ++++++++ .../Gherkin/Fixtures/etalons/tags_sample.yml | 36 ++++- .../outline_with_multiple_examples.feature | 36 +++++ .../Fixtures/features/tags_sample.feature | 41 ++++-- tests/Behat/Gherkin/Keywords/KeywordsTest.php | 2 +- .../Behat/Gherkin/Loader/ArrayLoaderTest.php | 9 +- tests/Behat/Gherkin/Node/ExampleNodeTest.php | 4 +- tests/Behat/Gherkin/Node/OutlineNodeTest.php | 70 ++++++++-- tests/Behat/Gherkin/Node/TableNodeTest.php | 84 ++++++++++++ 22 files changed, 758 insertions(+), 108 deletions(-) create mode 100644 tests/Behat/Gherkin/Fixtures/etalons/outline_with_multiple_examples.yml create mode 100644 tests/Behat/Gherkin/Fixtures/features/outline_with_multiple_examples.feature 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..509ba92c 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,50 @@ public function __construct($filterString) $this->filterString = trim($filterString); } + 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 +92,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..315f12c0 100644 --- a/src/Behat/Gherkin/Loader/ArrayLoader.php +++ b/src/Behat/Gherkin/Loader/ArrayLoader.php @@ -179,7 +179,43 @@ protected function loadOutlineHash(array $hash, $line = 0) $examplesKeyword = 'Examples'; } - $examples = new ExampleTableNode($hash['examples'], $examplesKeyword); + $exHash = $hash['examples']; + $examples = array(); + + // there are 3 cases + // first is examples as a single table - we create an array with the only one element + // examples + // 11: abc + // 12: cde + // + // second is array of arrays + // examples + // - + // 11: abc + // 12: cde + // + // and the 3rd is array of objects + // examples + // - + // tags: [] + // table: + // 11: abc + // 12: cde + + if (isset($exHash[0])) { + // cases #2 & 3 + for ($i = 0; $i < count($exHash); $i++) { + if (isset($exHash[$i]['table'])) { + // we have examples as objects + $exHashTags = isset($exHash[$i]['tags']) ? $exHash[$i]['tags'] : array(); + $examples[] = new ExampleTableNode($exHash[$i]['table'], $examplesKeyword, $exHashTags); + } else { + $examples[] = new ExampleTableNode($exHash[$i], $examplesKeyword); + } + } + } else { + $examples[] = new ExampleTableNode($exHash, $examplesKeyword);; + } return new OutlineNode($hash['title'], $hash['tags'], $steps, $examples, $hash['keyword'], $hash['line']); } 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..7764032c 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[] */ - 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 ExampleTableNode[] $tables * @param string $keyword * @param integer $line */ @@ -60,14 +60,14 @@ public function __construct( $title, array $tags, array $steps, - ExampleTableNode $table, + array $tables, $keyword, $line ) { $this->title = $title; $this->tags = $tags; $this->steps = $steps; - $this->table = $table; + $this->tables = $tables; $this->keyword = $keyword; $this->line = $line; } @@ -151,27 +151,50 @@ public function getSteps() */ public function hasExamples() { - return 0 < count($this->table->getColumnsHash()); + return 0 < count($this->tables); } /** - * 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 +225,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 c308f7c3..dacc7590 100644 --- a/src/Behat/Gherkin/Node/TableNode.php +++ b/src/Behat/Gherkin/Node/TableNode.php @@ -338,6 +338,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..cb96cd1f 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 $stack = array(); + /** * Initializes parser. * @@ -236,6 +238,8 @@ protected function parseFeature() $file = $this->file; $line = $token['line']; + array_push($this->stack, 'Feature'); + // Parse description, background, scenarios & outlines while ('EOS' !== $this->predictTokenType()) { $node = $this->parseExpression(); @@ -369,6 +373,8 @@ protected function parseScenario() $keyword = $token['keyword']; $line = $token['line']; + array_push($this->stack, 'Scenario'); + // Parse description and steps $steps = array(); while (in_array($this->predictTokenType(), array('Step', 'Newline', 'Text', 'Comment'))) { @@ -407,6 +413,8 @@ protected function parseScenario() } } + array_pop($this->stack); + return new ScenarioNode(rtrim($title) ?: null, $tags, $steps, $keyword, $line); } @@ -424,12 +432,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->stack, 'Outline'); + + while (in_array($this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment', 'Tag'))) { $node = $this->parseExpression(); if ($node instanceof StepNode) { @@ -438,7 +451,8 @@ protected function parseOutline() } if ($node instanceof ExampleTableNode) { - $examples = $node; + $examples[] = $node; + continue; } @@ -470,7 +484,7 @@ protected function parseOutline() } } - if (null === $examples) { + if (empty($examples)) { throw new ParserException(sprintf( 'Outline should have examples table, but got none for outline "%s" on line: %d%s', rtrim($title), @@ -496,6 +510,8 @@ protected function parseStep() $text = trim($token['text']); $line = $token['line']; + array_push($this->stack, 'Step'); + $arguments = array(); while (in_array($predicted = $this->predictTokenType(), array('PyStringOp', 'TableRow', 'Newline', 'Comment'))) { if ('Comment' === $predicted || 'Newline' === $predicted) { @@ -510,6 +526,8 @@ protected function parseStep() } } + array_pop($this->stack); + return new StepNode($keyword, $text, $arguments, $line, $keywordType); } @@ -524,7 +542,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 +590,24 @@ 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->stack)) { + $currentType = $this->stack[count($this->stack) - 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 8325e986..68662c19 100644 --- a/tests/Behat/Gherkin/Filter/FilterTest.php +++ b/tests/Behat/Gherkin/Filter/FilterTest.php @@ -50,11 +50,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..853b8a1f 100644 --- a/tests/Behat/Gherkin/Filter/LineFilterTest.php +++ b/tests/Behat/Gherkin/Filter/LineFilterTest.php @@ -37,7 +37,7 @@ public function testIsScenarioMatchFilter() $filter = new LineFilter(5); $this->assertFalse($filter->isScenarioMatch($scenario)); - $outline = new OutlineNode(null, array(), array(), new ExampleTableNode(array(), null), null, 20); + $outline = new OutlineNode(null, array(), array(), array(new ExampleTableNode(array(), null)), null, 20); $filter = new LineFilter(5); $this->assertFalse($filter->isScenarioMatch($outline)); @@ -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 66b3f729..0d6a5089 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; use PHPUnit\Framework\TestCase; @@ -141,5 +143,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/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/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/Keywords/KeywordsTest.php b/tests/Behat/Gherkin/Keywords/KeywordsTest.php index dcb933d9..f06884e5 100644 --- a/tests/Behat/Gherkin/Keywords/KeywordsTest.php +++ b/tests/Behat/Gherkin/Keywords/KeywordsTest.php @@ -86,7 +86,7 @@ public function translationTestDataProvider() ), $keywords[0]); $line += 1; - $scenarios[] = new OutlineNode('Erasing other agents\' memory', array(), $steps, $table, $outlineKeyword, $outlineLine); + $scenarios[] = new OutlineNode('Erasing other agents\' memory', array(), $steps, array($table), $outlineKeyword, $outlineLine); $line += 1; } diff --git a/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php b/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php index 3a2e8bd7..2eeb42cf 100644 --- a/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php +++ b/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php @@ -3,10 +3,12 @@ namespace Tests\Behat\Gherkin\Loader; use Behat\Gherkin\Loader\ArrayLoader; +use Behat\Gherkin\Node\OutlineNode; use PHPUnit\Framework\TestCase; class ArrayLoaderTest extends TestCase { + /** @var ArrayLoader */ private $loader; protected function setUp() @@ -158,9 +160,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( @@ -174,6 +176,7 @@ public function testOutlineExamples() $this->assertEquals(1, count($features)); + /** @var OutlineNode[] $scenarios */ $scenarios = $features[0]->getScenarios(); $scenario = $scenarios[0]; diff --git a/tests/Behat/Gherkin/Node/ExampleNodeTest.php b/tests/Behat/Gherkin/Node/ExampleNodeTest.php index 0b03f49f..09d67399 100644 --- a/tests/Behat/Gherkin/Node/ExampleNodeTest.php +++ b/tests/Behat/Gherkin/Node/ExampleNodeTest.php @@ -26,7 +26,7 @@ public function testCreateExampleSteps() array('example', 'example@example.com') ), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, $table, null, null); + $outline = new OutlineNode(null, array(), $steps, array($table), null, null); $examples = $outline->getExamples(); $this->assertCount(4, $steps = $examples[0]->getSteps()); @@ -79,7 +79,7 @@ public function testCreateExampleStepsWithArguments() array('example', 'example@example.com', 'other page') ), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, $table, null, null); + $outline = new OutlineNode(null, array(), $steps, array($table), null, null); $examples = $outline->getExamples(); $steps = $examples[0]->getSteps(); diff --git a/tests/Behat/Gherkin/Node/OutlineNodeTest.php b/tests/Behat/Gherkin/Node/OutlineNodeTest.php index 8a237188..edeec6b1 100644 --- a/tests/Behat/Gherkin/Node/OutlineNodeTest.php +++ b/tests/Behat/Gherkin/Node/OutlineNodeTest.php @@ -15,22 +15,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); + $outline = new OutlineNode(null, array(), $steps, array($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() @@ -39,14 +85,14 @@ 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( array('name', 'email') ), '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()); } @@ -57,12 +103,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 7e2779d2..c96cbebb 100644 --- a/tests/Behat/Gherkin/Node/TableNodeTest.php +++ b/tests/Behat/Gherkin/Node/TableNodeTest.php @@ -268,6 +268,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 @@ -280,4 +327,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); + } } From 9a56a81f603b5b09cfa408e93148427bd6b7d892 Mon Sep 17 00:00:00 2001 From: Andrey Stukalin Date: Fri, 23 Dec 2016 09:44:25 +0100 Subject: [PATCH 2/4] #117 phpdoc and some rewording --- src/Behat/Gherkin/Filter/TagFilter.php | 9 ++++++++- src/Behat/Gherkin/Parser.php | 19 ++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Behat/Gherkin/Filter/TagFilter.php b/src/Behat/Gherkin/Filter/TagFilter.php index 509ba92c..d25cd2fe 100644 --- a/src/Behat/Gherkin/Filter/TagFilter.php +++ b/src/Behat/Gherkin/Filter/TagFilter.php @@ -32,7 +32,14 @@ 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(); diff --git a/src/Behat/Gherkin/Parser.php b/src/Behat/Gherkin/Parser.php index cb96cd1f..c9a7dff6 100644 --- a/src/Behat/Gherkin/Parser.php +++ b/src/Behat/Gherkin/Parser.php @@ -39,7 +39,7 @@ class Parser private $tags = array(); private $languageSpecifierLine; - private $stack = array(); + private $passedNodesStack = array(); /** * Initializes parser. @@ -238,7 +238,7 @@ protected function parseFeature() $file = $this->file; $line = $token['line']; - array_push($this->stack, 'Feature'); + array_push($this->passedNodesStack, 'Feature'); // Parse description, background, scenarios & outlines while ('EOS' !== $this->predictTokenType()) { @@ -373,7 +373,7 @@ protected function parseScenario() $keyword = $token['keyword']; $line = $token['line']; - array_push($this->stack, 'Scenario'); + array_push($this->passedNodesStack, 'Scenario'); // Parse description and steps $steps = array(); @@ -413,7 +413,7 @@ protected function parseScenario() } } - array_pop($this->stack); + array_pop($this->passedNodesStack); return new ScenarioNode(rtrim($title) ?: null, $tags, $steps, $keyword, $line); } @@ -440,7 +440,7 @@ protected function parseOutline() // Parse description, steps and examples $steps = array(); - array_push($this->stack, 'Outline'); + array_push($this->passedNodesStack, 'Outline'); while (in_array($this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment', 'Tag'))) { $node = $this->parseExpression(); @@ -510,7 +510,7 @@ protected function parseStep() $text = trim($token['text']); $line = $token['line']; - array_push($this->stack, 'Step'); + array_push($this->passedNodesStack, 'Step'); $arguments = array(); while (in_array($predicted = $this->predictTokenType(), array('PyStringOp', 'TableRow', 'Newline', 'Comment'))) { @@ -526,7 +526,7 @@ protected function parseStep() } } - array_pop($this->stack); + array_pop($this->passedNodesStack); return new StepNode($keyword, $text, $arguments, $line, $keywordType); } @@ -599,9 +599,10 @@ protected function parseTags() $currentType = '-1'; // check if that is ok to go inside: - if (!empty($this->stack)) { - $currentType = $this->stack[count($this->stack) - 1]; + 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(); From 19c972b49906070a3a4146275dd4bb725a1df890 Mon Sep 17 00:00:00 2001 From: Andrey Stukalin Date: Thu, 19 Jan 2017 15:05:25 +0100 Subject: [PATCH 3/4] nesting reducing --- src/Behat/Gherkin/Loader/ArrayLoader.php | 69 +++++++++++++----------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/Behat/Gherkin/Loader/ArrayLoader.php b/src/Behat/Gherkin/Loader/ArrayLoader.php index 315f12c0..1f057170 100644 --- a/src/Behat/Gherkin/Loader/ArrayLoader.php +++ b/src/Behat/Gherkin/Loader/ArrayLoader.php @@ -182,38 +182,10 @@ protected function loadOutlineHash(array $hash, $line = 0) $exHash = $hash['examples']; $examples = array(); - // there are 3 cases - // first is examples as a single table - we create an array with the only one element - // examples - // 11: abc - // 12: cde - // - // second is array of arrays - // examples - // - - // 11: abc - // 12: cde - // - // and the 3rd is array of objects - // examples - // - - // tags: [] - // table: - // 11: abc - // 12: cde - - if (isset($exHash[0])) { - // cases #2 & 3 - for ($i = 0; $i < count($exHash); $i++) { - if (isset($exHash[$i]['table'])) { - // we have examples as objects - $exHashTags = isset($exHash[$i]['tags']) ? $exHash[$i]['tags'] : array(); - $examples[] = new ExampleTableNode($exHash[$i]['table'], $examplesKeyword, $exHashTags); - } else { - $examples[] = new ExampleTableNode($exHash[$i], $examplesKeyword); - } - } + 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);; } @@ -302,4 +274,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; + } } From 55ee35c783aa9876a3ccc3337de847db0e9fa860 Mon Sep 17 00:00:00 2001 From: Dipak Acharya Date: Thu, 12 Dec 2019 16:17:02 +0545 Subject: [PATCH 4/4] Fix the OutlineNode object for backward compatibility --- src/Behat/Gherkin/Filter/TagFilter.php | 2 +- src/Behat/Gherkin/Node/OutlineNode.php | 14 ++++++++------ tests/Behat/Gherkin/Filter/LineFilterTest.php | 2 +- tests/Behat/Gherkin/Keywords/KeywordsTest.php | 2 +- tests/Behat/Gherkin/Node/ExampleNodeTest.php | 4 ++-- tests/Behat/Gherkin/Node/OutlineNodeTest.php | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Behat/Gherkin/Filter/TagFilter.php b/src/Behat/Gherkin/Filter/TagFilter.php index d25cd2fe..51f1cfde 100644 --- a/src/Behat/Gherkin/Filter/TagFilter.php +++ b/src/Behat/Gherkin/Filter/TagFilter.php @@ -32,7 +32,7 @@ public function __construct($filterString) { $this->filterString = trim($filterString); } - + /** * Filters feature according to the filter. * diff --git a/src/Behat/Gherkin/Node/OutlineNode.php b/src/Behat/Gherkin/Node/OutlineNode.php index 7764032c..818fbe6b 100644 --- a/src/Behat/Gherkin/Node/OutlineNode.php +++ b/src/Behat/Gherkin/Node/OutlineNode.php @@ -30,7 +30,7 @@ class OutlineNode implements ScenarioInterface */ private $steps; /** - * @var ExampleTableNode[] + * @var ExampleTableNode|ExampleTableNode[] */ private $tables; /** @@ -52,7 +52,7 @@ class OutlineNode implements ScenarioInterface * @param null|string $title * @param string[] $tags * @param StepNode[] $steps - * @param ExampleTableNode[] $tables + * @param ExampleTableNode|ExampleTableNode[] $tables * @param string $keyword * @param integer $line */ @@ -60,16 +60,20 @@ public function __construct( $title, array $tags, array $steps, - array $tables, + $tables, $keyword, $line ) { $this->title = $title; $this->tags = $tags; $this->steps = $steps; - $this->tables = $tables; $this->keyword = $keyword; $this->line = $line; + if (!is_array($tables)) { + $this->tables = array($tables); + } else { + $this->tables = $tables; + } } /** @@ -165,7 +169,6 @@ public function hasExamples() public function getExampleTable() { $table = array(); - foreach ($this->tables[0]->getTable() as $k => $v) { $table[$k] = $v; } @@ -175,7 +178,6 @@ public function getExampleTable() for ($i = 1; $i < count($this->tables); $i++) { $exampleTableNode->mergeRowsFromTable($this->tables[$i]); } - return $exampleTableNode; } diff --git a/tests/Behat/Gherkin/Filter/LineFilterTest.php b/tests/Behat/Gherkin/Filter/LineFilterTest.php index 853b8a1f..5e8be7d3 100644 --- a/tests/Behat/Gherkin/Filter/LineFilterTest.php +++ b/tests/Behat/Gherkin/Filter/LineFilterTest.php @@ -37,7 +37,7 @@ public function testIsScenarioMatchFilter() $filter = new LineFilter(5); $this->assertFalse($filter->isScenarioMatch($scenario)); - $outline = new OutlineNode(null, array(), array(), array(new ExampleTableNode(array(), null)), null, 20); + $outline = new OutlineNode(null, array(), array(), new ExampleTableNode(array(), null), null, 20); $filter = new LineFilter(5); $this->assertFalse($filter->isScenarioMatch($outline)); diff --git a/tests/Behat/Gherkin/Keywords/KeywordsTest.php b/tests/Behat/Gherkin/Keywords/KeywordsTest.php index f06884e5..dcb933d9 100644 --- a/tests/Behat/Gherkin/Keywords/KeywordsTest.php +++ b/tests/Behat/Gherkin/Keywords/KeywordsTest.php @@ -86,7 +86,7 @@ public function translationTestDataProvider() ), $keywords[0]); $line += 1; - $scenarios[] = new OutlineNode('Erasing other agents\' memory', array(), $steps, array($table), $outlineKeyword, $outlineLine); + $scenarios[] = new OutlineNode('Erasing other agents\' memory', array(), $steps, $table, $outlineKeyword, $outlineLine); $line += 1; } diff --git a/tests/Behat/Gherkin/Node/ExampleNodeTest.php b/tests/Behat/Gherkin/Node/ExampleNodeTest.php index 09d67399..0b03f49f 100644 --- a/tests/Behat/Gherkin/Node/ExampleNodeTest.php +++ b/tests/Behat/Gherkin/Node/ExampleNodeTest.php @@ -26,7 +26,7 @@ public function testCreateExampleSteps() array('example', 'example@example.com') ), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, array($table), null, null); + $outline = new OutlineNode(null, array(), $steps, $table, null, null); $examples = $outline->getExamples(); $this->assertCount(4, $steps = $examples[0]->getSteps()); @@ -79,7 +79,7 @@ public function testCreateExampleStepsWithArguments() array('example', 'example@example.com', 'other page') ), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, array($table), null, null); + $outline = new OutlineNode(null, array(), $steps, $table, null, null); $examples = $outline->getExamples(); $steps = $examples[0]->getSteps(); diff --git a/tests/Behat/Gherkin/Node/OutlineNodeTest.php b/tests/Behat/Gherkin/Node/OutlineNodeTest.php index edeec6b1..c3bfd211 100644 --- a/tests/Behat/Gherkin/Node/OutlineNodeTest.php +++ b/tests/Behat/Gherkin/Node/OutlineNodeTest.php @@ -24,7 +24,7 @@ public function testCreatesExamplesForExampleTable() 23 => array('example', 'example@example.com') ), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, array($table), null, null); + $outline = new OutlineNode(null, array(), $steps, $table, null, null); $this->assertCount(2, $examples = $outline->getExamples()); $this->assertEquals(22, $examples[0]->getLine()); @@ -92,7 +92,7 @@ public function testCreatesEmptyExamplesForEmptyExampleTable() array('name', 'email') ), 'Examples'); - $outline = new OutlineNode(null, array(), $steps, array($table), null, null); + $outline = new OutlineNode(null, array(), $steps, $table, null, null); $this->assertCount(0, $examples = $outline->getExamples()); }