Skip to content

MQE-659: [ALLURE] Include the test "stepKey" in the MFTF Allure Report #354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

use Magento\FunctionalTestingFramework\Suite\Handlers\SuiteObjectHandler;
use Magento\FunctionalTestingFramework\Test\Objects\ActionGroupObject;
use Magento\FunctionalTestingFramework\Allure\Adapter\MagentoAllureStepKeyReader;
use Magento\FunctionalTestingFramework\Util\TestGenerator;
use Yandex\Allure\Adapter\Model\Step;
use Yandex\Allure\Codeception\AllureCodeception;
use Yandex\Allure\Adapter\Event\StepStartedEvent;
Expand All @@ -17,6 +19,7 @@
use Codeception\Event\FailEvent;
use Codeception\Event\SuiteEvent;
use Codeception\Event\StepEvent;
use Codeception\Event\TestEvent;

/**
* Class MagentoAllureAdapter
Expand All @@ -30,6 +33,23 @@
class MagentoAllureAdapter extends AllureCodeception
{
const STEP_PASSED = "passed";
const SAVE_SCREENSHOT = "save screenshot";
const ALLURE_STEPKEY_FORMAT = " ------ @stepKey=";

/**
* @var integer
*/
private $stepCount;

/**
* @var array
*/
private $stepKeys;

/**
* @var MagentoAllureStepKeyReader
*/
private $stepKeyReader;

/**
* Array of group values passed to test runner command
Expand Down Expand Up @@ -179,7 +199,13 @@ public function testEnd()
$formattedSteps = [];
$actionGroupStepContainer = null;

// Get properly ordered step keys for this test
$this->stepKeys = $this->stepKeyReader->getSteps($this->getPassedStepCount($rootStep));

$stepCount = -1;
foreach ($rootStep->getSteps() as $step) {
$stepCount += 1;
$step->setName($this->appendStepKey($stepCount, $step->getName()));
// if actionGroup flag, start nesting
if (strpos($step->getName(), ActionGroupObject::ACTION_GROUP_CONTEXT_START) !== false) {
$step->setName(str_replace(ActionGroupObject::ACTION_GROUP_CONTEXT_START, '', $step->getName()));
Expand Down Expand Up @@ -220,4 +246,60 @@ function () use ($rootStep, $formattedSteps) {

$this->getLifecycle()->fire(new TestCaseFinishedEvent());
}

/**
* Aggregate to parent method to prepare collecting step keys for a test
*
* @param TestEvent $testEvent
*
* @return void
*/
public function testStart(TestEvent $testEvent)
{
$this->stepKeys = [];
$this->stepCount = 0;
$this->stepKeyReader = new MagentoAllureStepKeyReader(
$testEvent->getTest()->getFileName(),
$testEvent->getTest()->getTestMethod()
);
parent::testStart($testEvent);
}

/**
* Append step key to matching step
*
* @param integer $stepCount
* @param string $name
*
* @return string
*/
private function appendStepKey($stepCount, $name)
{
if (isset($this->stepKeys[$stepCount]['action']) && isset($this->stepKeys[$stepCount]['stepKey'])) {
if ($this->stepKeys[$stepCount]['action'] == "comment"
|| strpos($name, $this->stepKeys[$stepCount]['action']) !== false) {
$name .= self::ALLURE_STEPKEY_FORMAT . $this->stepKeys[$stepCount]['stepKey'];
}
}
return $name;
}

/**
* Return number of passed steps for a test
*
* @param Step $rootStep
*
* @return integer
*/
private function getPassedStepCount($rootStep)
{
$counter = 0;
foreach ($rootStep->getSteps() as $step) {
if (trim($step->getName()) == self::SAVE_SCREENSHOT) {
break;
}
$counter += 1;
}
return $counter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\FunctionalTestingFramework\Allure\Adapter;

use Magento\FunctionalTestingFramework\Util\TestGenerator;

/**
* Class MagentoAllureStepKeyReader
*
* Parse a mftf generated Codeception php file for actions and step keys
*
* @package Magento\FunctionalTestingFramework\Allure
*/

class MagentoAllureStepKeyReader
{
const BEFORE_MARK = "before";
const AFTER_MARK = "after";
const FAILED_MARK = "failed";
const TEST_MARK = "test";
const METHOD_BEFORE = "public function _" . self::BEFORE_MARK;
const METHOD_AFTER = "public function _" . self::AFTER_MARK;
const METHOD_FAILED = "public function _" . self::FAILED_MARK;
const METHOD_ENDING = "\t}";
const FAILED_ACTION_NAME = "saveScreenshot";
const FAILED_STEP_KEY = "saveScreenshot";
const REGEX_STEP_KEY = "~(?<=" . TestGenerator::STEPKEY_IN_COMMENT . ").*~";
const REGEX_ACTION_NAME = "~(?<=\\". TestGenerator::ACTOR . ")([^\\(]*)(?=\()~";

/**
* test filename
*
* @var string
*/
private $filename;

/**
* test method name
*
* @var string
*/
private $method;

/**
* array of lines in a file
*
* @var array
*/
private $lines;

/**
* steps in failed
*
* @var array
*/
private $failedSteps;

/**
* steps in _before
*
* @var array
*/
private $beforeSteps;

/**
* steps in _after
*
* @var array
*/
private $afterSteps;

/**
* steps in test
*
* @var array
*/
private $testSteps;

/**
* count of steps in methods
*
* @var array
*/
private $stepCount;

/**
* MagentoAllureStepKeyReader constructor
*
* @param string $filename
* @param string $method
*/
public function __construct($filename, $method)
{
$this->filename = $filename;
$this->method = $method;
$this->load();
}

/**
* Load file contents
*
* @return void
*/
private function load()
{
$this->lines = [];
if (!file_exists($this->filename)) {
return;
}
$lines = file($this->filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines !== false) {
$this->lines = $lines;
}
$this->beforeSteps = $this->parseStepsForMethod(self::METHOD_BEFORE);
$this->testSteps = $this->parseStepsForMethod("public function " . $this->method);
$this->afterSteps = $this->parseStepsForMethod(self::METHOD_AFTER);
$this->failedSteps[] = $this->parseStepsForFailed();
$this->stepCount[self::BEFORE_MARK] = count($this->beforeSteps);
$this->stepCount[self::AFTER_MARK] = count($this->afterSteps);
$this->stepCount[self::TEST_MARK] = count($this->testSteps);
$this->stepCount[self::FAILED_MARK] = count($this->failedSteps);
}

/**
* Return test step actions and step keys based on number of passed steps
*
* @param integer $passedCount
*
* @return array
*/
public function getSteps($passedCount)
{
$steps = [];
$total = $this->stepCount[self::BEFORE_MARK]
+ $this->stepCount[self::TEST_MARK]
+ $this->stepCount[self::AFTER_MARK];
if ($passedCount < 0 || $passedCount > $total) {
return $steps;
}

if ($passedCount == $total) { /* test passed */
$steps = $this->beforeSteps;
$steps = array_merge($steps, $this->testSteps);
$steps = array_merge($steps, $this->afterSteps);
} elseif ($passedCount < $this->stepCount[self::BEFORE_MARK]) { /* failed in _before() */
$steps = array_slice($this->beforeSteps, 0, $passedCount);
$steps = array_merge($steps, $this->failedSteps);
$steps = array_merge($steps, $this->afterSteps);
} elseif ($passedCount < ($this->stepCount[self::BEFORE_MARK] + $this->stepCount[self::TEST_MARK])) {
$steps = $this->beforeSteps; /* failed in test() */
$steps = array_merge(
$steps,
array_slice($this->testSteps, 0, $passedCount - $this->stepCount[self::BEFORE_MARK])
);
$steps = array_merge($steps, $this->failedSteps);
$steps = array_merge($steps, $this->afterSteps);
} else { /* failed in _after() */
$steps = $this->beforeSteps;
$steps = array_merge($steps, $this->testSteps);
$steps = array_merge(
$steps,
array_slice(
$this->afterSteps,
0,
$passedCount - $this->stepCount[self::BEFORE_MARK] - $this->stepCount[self::TEST_MARK]
)
);
$steps = array_merge($steps, $this->failedSteps);
}
return $steps;
}

/**
* Parse test steps for a method
*
* @param string $method
*
* @return array
*/
private function parseStepsForMethod($method)
{
$process = false;
$steps = [];
foreach ($this->lines as $line) {
if (!$process && strpos($line, $method) !== false) {
$process = true;
}
if ($process && strpos($line, self::METHOD_ENDING) !== false) {
$process = false;
}
if ($process && preg_match(self::REGEX_STEP_KEY, $line, $stepKeys)) {
if (preg_match(self::REGEX_ACTION_NAME, $line, $actions)) {
$steps[] = [
'action' => $this->humanize($actions[0]),
'stepKey' => $stepKeys[0]
];
}
}
}
return $steps;
}

/**
* Parse test steps for failed method
*
* @return array
*/
private function parseStepsForFailed()
{
return [
'action' => $this->humanize(self::FAILED_ACTION_NAME),
'stepKey' => self::FAILED_STEP_KEY
];
}

/**
* Convert input string into human readable words in lower case
*
* @param string $inStr
*
* @return string
*/
private function humanize($inStr)
{
$inStr = preg_replace('/([A-Z]+)([A-Z][a-z])/', '\\1 \\2', $inStr);
$inStr = preg_replace('/([a-z\d])([A-Z])/', '\\1 \\2', $inStr);
$inStr = preg_replace('~\bdont\b~', 'don\'t', $inStr);
return strtolower($inStr);
}
}
Loading