-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from ueslialmeida/dev-beta
Dev beta
- Loading branch information
Showing
3 changed files
with
209 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
} | ||
} | ||
} |