Skip to content

Commit

Permalink
Merge pull request #1 from ueslialmeida/dev-beta
Browse files Browse the repository at this point in the history
Dev beta
  • Loading branch information
ueslialmeida authored Dec 7, 2020
2 parents d297d15 + 7f87452 commit 9b61c4f
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 43 deletions.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
jira-codeception-extension
=============================
This package provides an extension for Codeception to create Jira issues automatically when a test fails.
This package provides an extension for Codeception to create issues in Jira automatically when a test fails.

### How it works?

When you run a test and it fails the extension will connect with your Jira instance through the Jira API and create an issue containing the data generated by Codeception at the moment of the failure. The number of issues created will vary depending on the number of failed tests, if two tests failed then two separated issues will be created.

The issue will contain the following data:
- Test Name
- Failure Message
- Failed Step
- File Name
- Stack Trace

### Configuration Example

This extension creates a Jira issue after a test failure. To use this extension a valid Jira configuration is required.

- host: A Jira instance.
- user: A valid user that has permission to create issues in the specified project.
- token: A valid token for the specified user. The API will not accept the user password so a token is required. You can create a token in the user configuration panel, for more information follow the Jira official documentation [here](https://confluence.atlassian.com/cloud/api-tokens-938839638.html).
- projectKey: A valid Jira project key (e.g. TA, ZTE, ETC).
- issueType: Usually the issue is created as a Bug but you can change it for Task or another valid issue type available in your Jira instance.
- debugMode: In case you are creating tests or debugging tests you may not want to create issues (and I don't recommend it) so setting this config to true the extension will not create issues in production set it back to false.

Configuration 'codeception.yml' example:

extensions:
enabled:
- Codeception\Extension\JiraExtension
config:
Codeception\Extension\JiraExtension:
host: https://yourdomain.atlassian.net
user: [email protected]
token: Tg7womaGGFpn9EC16qD3L7T6
projectKey: JE
issueType: Bug
debugMode: false
47 changes: 23 additions & 24 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
{
"name": "ueslialmeida/jira-codeception-extension",
"description": "This package provides an extension for Codeception to create Jira issues automatically when a test fails.",
"keywords": [
"jira",
"issue",
"codeception",
"extension"
],
"authors": [
{
"name": "Uesli Almeida",
"email": "[email protected]",
"homepage": "https://github.com/ueslialmeida",
"role": "Tester/Developer"
}
],
"autoload": {
"psr-4": {
"Codeception\\": "src"
}
},
"require": {}
}

"name": "ueslialmeida/simple-jira-codeception",
"description": "This package provides an extension for Codeception to create Jira issues automatically when a test fails.",
"keywords": [
"jira",
"issue",
"codeception",
"extension"
],
"authors": [
{
"name": "Uesli Almeida",
"email": "[email protected]",
"homepage": "https://github.com/ueslialmeida",
"role": "Tester/Developer"
}
],
"autoload": {
"psr-4": {
"Codeception\\": "src"
}
},
"require": {}
}
167 changes: 149 additions & 18 deletions src/Extension/JiraExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,171 @@

use Codeception\Events;
use Codeception\Exception\ExtensionException;
use Codeception\Extension;

/**
* @author Uesli Almeida
*
* This extension creates a Jira issue after a test failure.
*
* To use this extension a valid Jira configuration is required.
*
* Configuration 'codeception.yml' example:
*
* extensions:
* enabled:
* - Codeception\Extension\JiraExtension
* config:
* Codeception\Extension\JiraExtension:
* host: https://yourdomain.atlassian.net
* user: [email protected]
* token: Tg7womaGGFpn9EC16qD3L7T6
* projectKey: JE
* issueType: Bug
* debugMode: false
*/
class JiraExtension extends \Codeception\Extension
{
const STRING_LIMIT = 1000;

/**
* Configuration properties.
*/
protected $host;
protected $user;
protected $token;
protected $projectKey;
protected $issueType;
protected $debug;

/**
* Issue fields.
*/
private $failedStep;
private $testName;
private $failureMessage;
private $fileName;
private $stackTrace;

// list events to listen to
// Codeception\Events constants used to set the event

public static $events = array(
Events::SUITE_AFTER => 'afterSuite',
Events::TEST_BEFORE => 'beforeTest',
Events::STEP_BEFORE => 'beforeStep',
Events::STEP_AFTER => 'afterStep',
Events::TEST_FAIL => 'testFailed',
Events::RESULT_PRINT_AFTER => 'print',
);

// methods that handle events
public function _initialize()
{
if (!isset($this->config['host']) or empty($this->config['host'])) {
throw new ExtensionException($this, "Configuration for 'host' is missing.");
}

public function afterSuite(\Codeception\Event\SuiteEvent $e) {
echo('### THIS IS THE AFTER SUITE EVENT ###');
}
$this->host = $this->config['host'];

if (!isset($this->config['user']) or empty($this->config['user'])) {
throw new ExtensionException($this, "Configuration for 'user' is missing.");
}

$this->user = $this->config['user'];

if (!isset($this->config['token']) or empty($this->config['token'])) {
throw new ExtensionException($this, "Configuration for 'token' is missing.");
}

$this->token = $this->config['token'];

if (!isset($this->config['projectKey']) or empty($this->config['projectKey'])) {
throw new ExtensionException($this, "Configuration for 'project key' is missing.");
}

$this->projectKey = $this->config['projectKey'];

if (!isset($this->config['issueType']) or empty($this->config['issueType'])) {
throw new ExtensionException($this, "Configuration for 'issue type' is missing. Recommended using 'Bug'.");
}

$this->issueType = $this->config['issueType'];

public function beforeTest(\Codeception\Event\TestEvent $e) {
echo('@@@ THIS IS THE BEFORE TEST EVENT @@@');
if (!isset($this->config['debugMode'])) {
throw new ExtensionException($this, "Configuration for 'debug mode' is missing. Possible values are 'true' or 'false'.");
}

$this->debug = $this->config['debugMode'];
}

public function beforeStep(\Codeception\Event\StepEvent $e) {
echo('%%% THIS IS THE BEFORE STEP EVENT %%%');
/**
* This method is fired when the event 'step.after' occurs.
* @param \Codeception\Event\StepEvent $e
*/
public function afterStep(\Codeception\Event\StepEvent $e) {
if ($e->getStep()->hasFailed()) {
$this->failedStep = $e->getStep()->toString(self::STRING_LIMIT);
}
}

/**
* This method is fired when the event 'test.fail' occurs.
* @param \Codeception\Event\FailEvent $e
*/
public function testFailed(\Codeception\Event\FailEvent $e) {
echo('$$$ THIS IS THE TEST FAILED EVENT $$$');
if (!$this->debug) {
$this->stackTrace = $e->getFail()->getTraceAsString();
$this->failureMessage = $e->getFail()->getMessage();
$this->fileName = $e->getTest()->getMetadata()->getFilename();
$this->testName = $e->getTest()->getMetadata()->getName();

$this->createIssue();
}
else {
echo("Debug mode is active, no issue will be created in Jira.\n\n");
}
}

private function createIssue() {
echo("Creating issue in Jira...\n");

$jiraAPI = $this->host . '/rest/api/2/issue';

$issue = json_encode($this->getIssueData());

$request = curl_init();
curl_setopt($request, CURLOPT_URL, $jiraAPI);
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($request, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($request, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($request, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($request, CURLOPT_HTTPHEADER, [
'Authorization: Basic ' . base64_encode($this->user . ':' . $this->token),
'Content-Type: application/json',
]);
curl_setopt($request, CURLOPT_POSTFIELDS, $issue);

$response = curl_exec($request);
echo("Jira response: $response \n\n");
}

private function getIssueData() {
$cleanFileName = $this->removeFilePath($this->fileName);

return [
'fields' => [
'project' => ['key' => "$this->projectKey"],
'summary' => $cleanFileName . ' : ' . $this->testName,
'description' => "
Test Name: $this->testName \n
Failure Message: $this->failureMessage \n
Failed Step: I $this->failedStep \n
File Name: $this->fileName \n
Stack Trace:\n $this->stackTrace",
'issuetype' => ['name' => $this->issueType],
]
];
}

public function print(\Codeception\Event\PrintResultEvent $e) {
echo('&&& THIS IS THE RESULT PRINT AFTER EVENT &&&');
private function removeFilePath($filePath) {
$pattern = "/[a-zA-Z\d]+\.[php]+/";
$path = explode('/', $filePath);
$fileName = implode(preg_grep($pattern, $path));

return $fileName;
}
}
}

0 comments on commit 9b61c4f

Please sign in to comment.