Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The final task submits the results linking the Test Execution to the Test Plan passed as an argument.



Trigger a Bamboo plan/stage build from a Test Plan, for the Tests contained in the Test Plan

...

In order to add this option in Jira's UI, we'll need to add a custom "web item" that provide provides an action that will interact with a custom ScriptRunner endpoint, which will be the one doing the HTTP request to the Bamboo server, passing the Test Plan issue key. In the ScriptRunner endpoint script, we'll obtain the list of Generic Tests (we're assuming that they will came come from Junit, so they have a certain syntax in the Generic Test Definition field.

In order to submit the request to Bamboo, we need to the credentials of some Bamboo user.

...

No Format
curl -u admin:admin -X DELETE "http://yourjiraserver/rest/scriptrunner/latest/custom/requirementProjects/CALC"




Synchronize Tests from "related" Test Sets to a Test Execution or to a Test Plan

Test Plans and (Sub) Test Executions contain a list of Tests. Although you can add Tests using Test Sets, you're not actually adding the Test Set itself; you're adding the Tests that belong to that Test Set(s) at that given moment. Thus, there is no relation whatsoever between Test Plans<=>Test Sets or between Test Executions<=>Test Sets.

However, and taking the Test Plan as an example, you may find handy to have some sort of "dynamic Test Plan" that will contain the Tests of the Test Sets that would be related to that Test Plan.

Since there is no relation between Test Plans<=>Test Sets, you have to define a convention for that. One option would be to use issue links (e.g. "relates to" or "includes"); in this case, you would need to manually create these links between Test Sets and Test Plans (or Test Executions).

After this, you can create a custom script that will obtain the Tests from the related (e.g. the linked) Test Sets using JQL, followed by REST API requests to specific endpoints to add these Tests to the destination entity (e.g. Test Plan).

We start by adding a button in Jira's UI using a custom "web item", providing an action that will interact with a custom ScriptRunner endpoint containing the following logic:

  • receive the source issue key that triggered the synchronization request (e.g. Test Plan, Test Execution, Sub Test Execution)
  • obtain the linked Test Sets, using JQL
    • obtain the Tests on each Test Set, using JQL
    • submit a REST API request to Xray specific endpoints to add the Test to the entity (e.g Test Plan)

ScripRunner configuration


Image Added   Image Added  




Code Block
languagegroovy
titlesynchTestsFromRelatedTestSets_restapi_endpoint.groovy
collapsetrue
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.json.JsonSlurper;
import groovy.json.StreamingJsonBuilder;
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import java.nio.charset.StandardCharsets
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLinkType
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.builder.JqlQueryBuilder
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.jira.web.bean.PagerFilter;
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.user.UserPropertyManager
import com.atlassian.jira.propertyset.JiraPropertySetFactory;
import com.google.common.collect.ImmutableMap;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.module.propertyset.PropertySetManager;
import com.atlassian.jira.util.BuildUtils
import com.atlassian.jira.util.BuildUtilsInfo
import com.atlassian.jira.util.BuildUtilsInfoImpl
import com.atlassian.plugin.PluginAccessor
import com.atlassian.plugin.PluginManager
import com.atlassian.jira.bc.license.JiraLicenseService
import com.atlassian.jira.bc.license.JiraLicenseServiceImpl
import org.apache.log4j.Level
import org.apache.log4j.Logger
import com.atlassian.jira.issue.IssueManager
import groovy.json.StreamingJsonBuilder
 



@BaseScript CustomEndpointDelegate delegate
 

boolean associateTestsToXrayIssue(endpoint, issueKey,listOfTestKeys){
    def jiraBaseUrl = com.atlassian.jira.component.ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
    def endpointUrl = "${jiraBaseUrl}/rest/raven/1.0/api/${endpoint}/${issueKey}/test"
    
    log.debug("issueKey: "+issueKey)
    log.debug("listOfTestKeys: "+listOfTestKeys)
    log.debug("jirabaseurl: "+jiraBaseUrl)
    log.debug("endpoint: "+endpointUrl)
    url = new URL(endpointUrl);
    def body_req = [ "add": listOfTestKeys ]

    // you should use a specific user for this purpose
    username = "admin"
    password = "admin"
    def authString = "${username}:${password}".bytes.encodeBase64().toString()

    URLConnection connection = url.openConnection();
    connection.requestMethod = "POST"
    connection.doOutput = true
    connection.addRequestProperty("Authorization", "Basic ${authString}")
    connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8")
    connection.outputStream.withWriter("UTF-8") { new StreamingJsonBuilder(it, body_req) }
    connection.connect();
    log.debug(connection.getResponseCode())
    log.debug(connection.getResponseMessage())

    if (connection.getResponseCode() == 200) {
        // OK
        return true;
    } else {
        // error
        return false;
    }
}

boolean associateTestsToTestPlan(testPlanKey, listOfTestKeys){
 return associateTestsToXrayIssue("testplan", testPlanKey, listOfTestKeys)
}

boolean associateTestsToTestExecution(testExecutionKey, listOfTestKeys){
 return associateTestsToXrayIssue("testexec", testExecutionKey, listOfTestKeys)
}



Object getIssues(jqlQuery){
    // A list of GenericValues representing issues
    List<Issue> searchResults = null;
    SearchService.ParseResult parseResult =  searchService.parseQuery(serviceAccount, jqlQuery);
  
    if (parseResult.isValid()) {
        // throws SearchException
        SearchResults results = searchService.search(serviceAccount, parseResult.getQuery(), PagerFilter.getUnlimitedFilter());
        searchResults = results.getIssues();
        return searchResults;
    }
  
     return []
}


synchTestsFromRelatedTestSets(httpMethod: "GET") { MultivaluedMap queryParams ->
    // issue_key may refer to a Test Plan or to a Test Execution
    def issue_key = queryParams.getFirst("issueId") as String // use the issueId to retrieve this issue
    
 
    projectManager = ComponentAccessor.getProjectManager()
    componentManager = ComponentManager.getInstance();
    searchService =  ComponentAccessor.getComponent(SearchService.class);
    issueManager = ComponentAccessor.getIssueManager()
    customFieldManager = ComponentAccessor.getCustomFieldManager()
    userUtil = ComponentAccessor.getUserUtil();
    serviceAccount = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
    pluginAccessor = componentManager.getPluginAccessor();
    
    MutableIssue issue = issueManager.getIssueObject(issue_key)

    Logger.getLogger("com.onresolve").setLevel(Level.DEBUG)

    // assume that Test Plan/Execution is linked to Test Sets using the issue link "relates to"; customize if needed
    jql = "issue in linkedIssues('${issue_key}', 'relates to')"
    testset_issues = getIssues(jql)
    def had_errors = false
    def success = false
    
    testset_issues.each {
        // process only "Test Set" issues
        if (it.issueType.name == "Test Set") {
            jql = "issue in testSetTests('${it.key}')"
            issues = getIssues(jql)
            test_keys = issues.collect{ it.key }
            //log.debug(test_keys)
            if (issue.issueType.name == "Test Plan") {
                success = associateTestsToTestPlan(issue_key, test_keys)
            } else if ((issue.issueType.name == "Sub Test Execution") || (issue.issueType.name == "Test Execution")) {
            	success = associateTestsToTestExecution(issue_key, test_keys)
            }
            if (!success) {
                had_errors = true
            }
        }
    }    
    
    def flag = []
    if (success) {
    	flag = [
            type : 'success',
            title: "Test Sets Synchronization",
            close: 'auto',
            body : "Tests have been synchronized for " + issue_key
    	]
        Response.ok(JsonOutput.toJson(flag)).build() 
    } else {
    	flag = [
            type : 'success',
            title: "Test Sets Synchronization",
            close: 'auto',
            body : "Tests have been synchronized for " + issue_key
    	] 
        Response.serverError().entity([error: "some Tests were no synchronized"]).build()
    }
	
}


Example


Image Added   


Image Added


Image Added