public function testArguments() { $step = new StepNode('Given', null); $this->assertEquals(0, count($step->getArguments())); $this->assertFalse($step->hasArguments()); $step->addArgument(new PyStringNode()); $this->assertEquals(1, count($step->getArguments())); $this->assertTrue($step->hasArguments()); $step->addArgument(new TableNode()); $this->assertEquals(2, count($step->getArguments())); $this->assertTrue($step->hasArguments()); $arguments = $step->getArguments(); $this->assertInstanceOf('Behat\\Gherkin\\Node\\PyStringNode', $arguments[0]); $this->assertInstanceOf('Behat\\Gherkin\\Node\\TableNode', $arguments[1]); }
/** * Loads definitions and translations from provided context. * * @param ContextInterface $context * @param StepNode $step * * @return DefinitionSnippet */ public function propose(ContextInterface $context, StepNode $step) { $text = $step->getText(); $regex = preg_replace('/([\\/\\[\\]\\(\\)\\\\^\\$\\.\\|\\?\\*\\+\'])/', '\\\\$1', $text); $regex = preg_replace(array("/(?<= |^)\\\\'(?:((?!\\').)*)\\\\'(?= |\$)/", '/(?<= |^)\\"(?:[^\\"]*)\\"(?= |$)/', '/(\\d+)/'), array("\\'([^\\']*)\\'", "\"([^\"]*)\"", "(\\d+)"), $regex); preg_match('/' . $regex . '/', $text, $matches); $count = count($matches) - 1; $args = array("\$world"); for ($i = 0; $i < $count; $i++) { $args[] = "\$arg" . ($i + 1); } foreach ($step->getArguments() as $argument) { if ($argument instanceof PyStringNode) { $args[] = "\$string"; } elseif ($argument instanceof TableNode) { $args[] = "\$table"; } } $description = sprintf(<<<PHP \$steps->%s('/^%s\$/', function(%s) { throw new \\Behat\\Behat\\Exception\\PendingException(); }); PHP , '%s', $regex, implode(', ', $args)); return new DefinitionSnippet($step, $description); }
/** * Loads definitions and translations from provided context. * * @param ContextInterface $context * @param StepNode $step * * @return DefinitionSnippet */ public function propose(ContextInterface $context, StepNode $step) { $contextRefl = new \ReflectionObject($context); $contextClass = $contextRefl->getName(); $replacePatterns = array("/(?<= |^)\\\\'(?:((?!\\').)*)\\\\'(?= |\$)/", '/(?<= |^)\\"(?:[^\\"]*)\\"(?= |$)/', '/(\\d+)/'); $text = $step->getText(); $text = preg_replace('/([\\/\\[\\]\\(\\)\\\\^\\$\\.\\|\\?\\*\\+\'])/', '\\\\$1', $text); $regex = preg_replace($replacePatterns, array("\\'([^\\']*)\\'", "\"([^\"]*)\"", "(\\d+)"), $text); preg_match('/' . $regex . '/', $step->getText(), $matches); $count = count($matches) - 1; $methodName = preg_replace($replacePatterns, '', $text); $methodName = Transliterator::transliterate($methodName, ' '); $methodName = preg_replace('/[^a-zA-Z\\_\\ ]/', '', $methodName); $methodName = str_replace(' ', '', ucwords($methodName)); if (0 !== strlen($methodName)) { $methodName[0] = strtolower($methodName[0]); } else { $methodName = 'stepDefinition1'; } // get method number from method name $methodNumber = 2; if (preg_match('/(\\d+)$/', $methodName, $matches)) { $methodNumber = intval($matches[1]); } // check that proposed method name isn't arelady defined in context while ($contextRefl->hasMethod($methodName)) { $methodName = preg_replace('/\\d+$/', '', $methodName); $methodName .= $methodNumber++; } // check that proposed method name haven't been proposed earlier if (isset(self::$proposedMethods[$contextClass])) { foreach (self::$proposedMethods[$contextClass] as $proposedRegex => $proposedMethod) { if ($proposedRegex !== $regex) { while ($proposedMethod === $methodName) { $methodName = preg_replace('/\\d+$/', '', $methodName); $methodName .= $methodNumber++; } } } } self::$proposedMethods[$contextClass][$regex] = $methodName; $args = array(); for ($i = 0; $i < $count; $i++) { $args[] = "\$arg" . ($i + 1); } foreach ($step->getArguments() as $argument) { if ($argument instanceof PyStringNode) { $args[] = "PyStringNode \$string"; } elseif ($argument instanceof TableNode) { $args[] = "TableNode \$table"; } } $description = $this->generateSnippet($regex, $methodName, $args); return new DefinitionSnippet($step, $description); }
/** * {@inheritdoc} * * @throws AmbiguousMatchException */ public function searchDefinition(Environment $environment, FeatureNode $feature, StepNode $step) { $suite = $environment->getSuite(); $language = $feature->getLanguage(); $stepText = $step->getText(); $multi = $step->getArguments(); $definitions = array(); $result = null; foreach ($this->repository->getEnvironmentDefinitions($environment) as $definition) { $definition = $this->translator->translateDefinition($suite, $definition, $language); if (!($newResult = $this->match($definition, $stepText, $multi))) { continue; } $result = $newResult; $definitions[] = $newResult->getMatchedDefinition(); } if (count($definitions) > 1) { throw new AmbiguousMatchException($result->getMatchedText(), $definitions); } return $result; }
/** * Finds step definition, that match specified step. * * @param Behat\Gherkin\Node\StepNode $step found step * * @return Behat\Behat\Definition\Definition * * @uses loadDefinitions() * * @throws Behat\Behat\Exception\Ambiguous if step description is ambiguous * @throws Behat\Behat\Exception\Undefined if step definition not found */ public function findDefinition(StepNode $step) { if (!count($this->definitions)) { $this->loadDefinitions(); } $text = $step->getText(); $multiline = $step->getArguments(); $matches = array(); // find step to match foreach ($this->definitions as $origRegex => $definition) { $transRegex = $this->translateDefinitionRegex($origRegex, $step->getLanguage()); if (preg_match($origRegex, $text, $arguments) || $origRegex !== $transRegex && preg_match($transRegex, $text, $arguments)) { // prepare callback arguments $arguments = $this->prepareCallbackArguments($definition->getCallbackReflection(), array_slice($arguments, 1), $multiline); // transform arguments foreach ($arguments as $num => $argument) { foreach ($this->transformations as $transformation) { if ($newArgument = $transformation->transform($argument)) { $arguments[$num] = $newArgument; } } } // set matched definition $definition->setMatchedText($text); $definition->setValues($arguments); $matches[] = $definition; } } if (count($matches) > 1) { throw new Ambiguous($text, $matches); } if (0 === count($matches)) { throw new Undefined($text); } return $matches[0]; }
/** * Returns an array of classes used by the snippet template * * @param StepNode $step * * @return string[] */ private function getUsedClasses(StepNode $step) { $usedClasses = array('Behat\\Behat\\Tester\\Exception\\PendingException'); foreach ($step->getArguments() as $argument) { if ($argument instanceof TableNode) { $usedClasses[] = 'Behat\\Gherkin\\Node\\TableNode'; } elseif ($argument instanceof PyStringNode) { $usedClasses[] = 'Behat\\Gherkin\\Node\\PyStringNode'; } } return $usedClasses; }
protected function runStep(StepNode $stepNode) { $params = []; if ($stepNode->hasArguments()) { $args = $stepNode->getArguments(); $table = $args[0]; if ($table instanceof TableNode) { $params = [$table->getTableAsString()]; } } $meta = new Meta($stepNode->getText(), $params); $meta->setPrefix($stepNode->getKeyword()); $this->scenario->setMetaStep($meta); // enable metastep $stepText = $stepNode->getText(); $this->getScenario()->comment(null); // make metastep to be printed even if no steps foreach ($this->steps as $pattern => $context) { $matches = []; if (!preg_match($pattern, $stepText, $matches)) { continue; } array_shift($matches); if ($stepNode->hasArguments()) { $matches = array_merge($matches, $stepNode->getArguments()); } call_user_func_array($context, $matches); // execute the step break; } $this->scenario->setMetaStep(null); // disable metastep }
/** * Changes step node type for types But, And to type of previous step if it exists else sets to Given * * @param StepNode $node * @param StepNode[] $steps * @return StepNode */ private function normalizeStepNodeKeywordType(StepNode $node, array $steps = array()) { if (in_array($node->getKeywordType(), array('And', 'But'))) { if ($prev = end($steps)) { $keywordType = $prev->getKeywordType(); } else { $keywordType = 'Given'; } $node = new StepNode($node->getKeyword(), $node->getText(), $node->getArguments(), $node->getLine(), $keywordType); } return $node; }
/** * Finds step definition, that match specified step. * * @param ContextInterface $context * @param StepNode $step * @param bool $skip * * @return Definition * * @uses loadDefinitions() * * @throws AmbiguousException if step description is ambiguous * @throws UndefinedException if step definition not found */ public function findDefinition(ContextInterface $context, StepNode $step, $skip = false) { $text = $step->getText(); $multiline = $step->getArguments(); $matches = array(); // find step to match foreach ($this->getDefinitions() as $origRegex => $definition) { $transRegex = $this->translateDefinitionRegex($origRegex, $step->getLanguage()); // if not regex really (string) - transform into it if (0 !== strpos($origRegex, '/')) { $origRegex = '/^' . preg_quote($origRegex, '/') . '$/'; $transRegex = '/^' . preg_quote($transRegex, '/') . '$/'; } if (preg_match($origRegex, $text, $arguments) || $origRegex !== $transRegex && preg_match($transRegex, $text, $arguments)) { // prepare callback arguments $arguments = $this->prepareCallbackArguments($context, $definition->getCallbackReflection(), array_slice($arguments, 1), $multiline); if (!$skip) { // transform arguments foreach ($arguments as &$argument) { foreach ($this->getTransformations() as $trans) { $transRegex = $this->translateDefinitionRegex($trans->getRegex(), $step->getLanguage()); $newArgument = $trans->transform($transRegex, $context, $argument); if (null !== $newArgument) { $argument = $newArgument; } } } } // set matched definition $definition->setMatchedText($text); $definition->setValues($arguments); $matches[] = $definition; } } if (count($matches) > 1) { throw new AmbiguousException($text, $matches); } if (0 === count($matches)) { throw new UndefinedException($text); } return $matches[0]; }
/** * @param StepNode $step * * @return string */ private function getArguments(StepNode $step) { if (!$step->hasArguments()) { return; } return implode(array_map(function (ArgumentInterface $argument) { if (in_array($argument->getNodeType(), ['Table', 'ExampleTable'])) { return implode(array_map(function ($arguments) { return $this->indent(self::INDENTATION * 2 + 4) . trim($arguments) . "\n"; }, explode("\n", $argument->getTableAsString()))); } if ('PyString' === $argument->getNodeType()) { return $this->encapsulateAsPyString(implode(array_map(function ($arguments) { return rtrim($this->indent(self::INDENTATION * 2 + 2) . trim($arguments)) . "\n"; }, $argument->getStrings()))); } }, $step->getArguments())); }
/** * @see Behat\Behat\Definition\Proposal\DefinitionProposalInterface::propose() */ public function propose(ContextInterface $context, StepNode $step) { $contextRefl = new \ReflectionObject($context); $contextClass = $contextRefl->getName(); $text = $step->getText(); $replacePatterns = array('/\'([^\']*)\'/', '/\\"([^\\"]*)\\"/', '/(\\d+)/'); $regex = preg_replace('/([\\/\\[\\]\\(\\)\\\\^\\$\\.\\|\\?\\*\\+])/', '\\\\$1', $text); $regex = preg_replace($replacePatterns, array("\\'([^\\']*)\\'", "\"([^\"]*)\"", "(\\d+)"), $regex); // Single quotes without matching pair (escape in resulting regex): $regex = preg_replace('/\'.*(?<!\')/', '\\\\$0', $regex); preg_match('/' . $regex . '/', $text, $matches); $count = count($matches) - 1; $methodName = preg_replace($replacePatterns, '', $text); $methodName = Transliterator::transliterate($methodName, ' '); $methodName = preg_replace('/[^a-zA-Z\\_\\ ]/', '', $methodName); $methodName = str_replace(' ', '', ucwords($methodName)); if (0 !== strlen($methodName)) { $methodName[0] = strtolower($methodName[0]); } else { $methodName = 'stepDefinition1'; } // get method number from method name $methodNumber = 2; if (preg_match('/(\\d+)$/', $methodName, $matches)) { $methodNumber = intval($matches[1]); } // check that proposed method name isn't arelady defined in context while ($contextRefl->hasMethod($methodName)) { $methodName = preg_replace('/\\d+$/', '', $methodName); $methodName .= $methodNumber++; } // check that proposed method name haven't been proposed earlier if (isset(self::$proposedMethods[$contextClass])) { foreach (self::$proposedMethods[$contextClass] as $proposedRegex => $proposedMethod) { if ($proposedRegex !== $regex) { while ($proposedMethod === $methodName) { $methodName = preg_replace('/\\d+$/', '', $methodName); $methodName .= $methodNumber++; } } } } self::$proposedMethods[$contextClass][$regex] = $methodName; $args = array(); for ($i = 0; $i < $count; $i++) { $args[] = "\$argument" . ($i + 1); } foreach ($step->getArguments() as $argument) { if ($argument instanceof PyStringNode) { $args[] = "PyStringNode \$string"; } elseif ($argument instanceof TableNode) { $args[] = "TableNode \$table"; } } $description = sprintf(<<<PHP /** * @%s /^%s\$/ */ public function %s(%s) { throw new PendingException(); } PHP , '%s', $regex, $methodName, implode(', ', $args)); return new DefinitionSnippet($step, $description); }
/** * Returns an array of method argument names from step and token count. * * @param StepNode $step * @param integer $tokenCount * * @return string[] */ private function getMethodArguments(StepNode $step, $tokenCount) { $args = array(); for ($i = 0; $i < $tokenCount; $i++) { $args[] = '$arg' . ($i + 1); } foreach ($step->getArguments() as $argument) { $args[] = $this->getMethodArgument($argument); } return $args; }