diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml
index 5762c66..2f3176f 100644
--- a/.github/workflows/sync-docs.yml
+++ b/.github/workflows/sync-docs.yml
@@ -12,7 +12,7 @@ jobs:
with:
fetch-depth: 0
- - name: Set up JDK 1.8
+ - name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
diff --git a/docs/index-all.html b/docs/index-all.html
index b269017..ad261c0 100644
--- a/docs/index-all.html
+++ b/docs/index-all.html
@@ -148,12 +148,10 @@
C
Process management for Windows (via Powershell) and Linux (via Shell).
cmd - Script in cmd
-crawlRows(def, def) - Method in pipeline2ATX
+crawlRows(def, def, def) - Method in pipeline2ATX
Crawls the current table row for corresponding test execution step items.
-createDescription(def) - Method in pipeline2ATX
- Create a description from the current pipeline step.
-createTestStep(def, def) - Method in pipeline2ATX
+createTestStep(def, def, def) - Method in pipeline2ATX
Create a test step item from the current pipeline step.
createTestStepFolder(def, def) - Method in pipeline2ATX
Create a test step folder item from the current pipeline step.
@@ -205,6 +203,8 @@ G
Gets the console log.
getCurrentResult(def) - Method in pipeline2ATX
Gets the current build result.
+getDescription(def) - Method in pipeline2ATX
+ Create a description from the current pipeline step.
getEscapeCode() - Method in log
Returns the ANSI escape sequence code.
getExecutionSteps(def, def) - Method in pipeline2ATX
@@ -222,6 +222,8 @@ G
getStyleCode(String) - Method in log
Returns the ANSI code for a given style.
+getTestStepName(def) - Method in pipeline2ATX
+ Creates the test step name by combining the row name with its arguments
@@ -229,8 +231,6 @@ G
H
-- hasNodeErrors(def) - Method in pipeline2ATX
-
-
Checks if current node has any errors
@@ -240,14 +240,8 @@ I
info(String) - Method in log
Prints a formatted informative logging message to console output.
-isDescription(def) - Method in pipeline2ATX
- Determines whether the current pipeline step corresponds to a description item.
isRunning(String) - Method in task
Checks whether a process is still running.
-isTestStep(def) - Method in pipeline2ATX
- Determines whether the current pipeline step corresponds to a test step item.
-isTestStepFolder(def) - Method in pipeline2ATX
- Determines whether the current pipeline step corresponds to a test folder item.
diff --git a/docs/vars/cmd.html b/docs/vars/cmd.html
index b0cebbe..d172736 100644
--- a/docs/vars/cmd.html
+++ b/docs/vars/cmd.html
@@ -163,7 +163,7 @@ Inherited Methods Summary
class groovy.lang.Script |
- groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
+ groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
class groovy.lang.GroovyObjectSupport |
diff --git a/docs/vars/log.html b/docs/vars/log.html
index 7c4c407..8623510 100644
--- a/docs/vars/log.html
+++ b/docs/vars/log.html
@@ -205,7 +205,7 @@ Inherited Methods Summary
class groovy.lang.Script |
- groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
+ groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
class groovy.lang.GroovyObjectSupport |
diff --git a/docs/vars/maven.html b/docs/vars/maven.html
index 3f778ae..e7ab744 100644
--- a/docs/vars/maven.html
+++ b/docs/vars/maven.html
@@ -205,7 +205,7 @@ Inherited Methods Summary
class groovy.lang.Script |
- groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
+ groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
class groovy.lang.GroovyObjectSupport |
diff --git a/docs/vars/pipeline2ATX.html b/docs/vars/pipeline2ATX.html
index a39261e..9524dea 100644
--- a/docs/vars/pipeline2ATX.html
+++ b/docs/vars/pipeline2ATX.html
@@ -145,56 +145,56 @@ Methods Summary
|
java.lang.Object |
- crawlRows(java.lang.Object row, java.lang.Object debug) Crawls the current table row for corresponding test execution step items.
+ | crawlRows(java.lang.Object row, java.lang.Object appendLogs, java.lang.Object insideStage = false) Crawls the current table row for corresponding test execution step items.
|
|
java.lang.Object |
- createDescription(java.lang.Object row) Create a description from the current pipeline step. |
+ createTestStep(java.lang.Object row, java.lang.Object appendLogs, java.lang.Object skipped = false) Create a test step item from the current pipeline step. |
|
java.lang.Object |
- createTestStep(java.lang.Object row, java.lang.Object debug) Create a test step item from the current pipeline step. |
+ createTestStepFolder(java.lang.Object row, java.lang.Object appendLogs) Create a test step folder item from the current pipeline step. |
|
java.lang.Object |
- createTestStepFolder(java.lang.Object row, java.lang.Object debug) Create a test step folder item from the current pipeline step. |
+ generateJsonReport(java.lang.Object build, java.lang.Object attributes, java.lang.Object executionTestSteps, java.lang.Object logFile) Generates a TEST-GUIDE compatible JSON report of the pipeline build. |
|
java.lang.Object |
- generateJsonReport(java.lang.Object build, java.lang.Object attributes, java.lang.Object executionTestSteps, java.lang.Object logFile) Generates a TEST-GUIDE compatible JSON report of the pipeline build. |
+ getBuildAttributes(java.lang.Object build) Collects all relevant build information and parameter as a map. |
|
java.lang.Object |
- getBuildAttributes(java.lang.Object build) Collects all relevant build information and parameter as a map. |
+ getConsoleLog(java.lang.Object build) Gets the console log. |
|
java.lang.Object |
- getConsoleLog(java.lang.Object build) Gets the console log. |
+ getCurrentResult(java.lang.Object build) Gets the current build result. |
|
java.lang.Object |
- getCurrentResult(java.lang.Object build) Gets the current build result. |
+ getDescription(java.lang.Object row) Create a description from the current pipeline step. |
|
java.lang.Object |
- getExecutionSteps(java.lang.Object build, java.lang.Object debug) Create test execution steps from the current pipeline build. |
+ getExecutionSteps(java.lang.Object build, java.lang.Object appendLogs) Create test execution steps from the current pipeline build. |
@@ -211,36 +211,18 @@ Methods Summary
-
|
- boolean |
- hasNodeErrors(java.lang.Object node) Checks if current node has any errors |
-
-
-
|
java.lang.Object |
- isDescription(java.lang.Object row) Determines whether the current pipeline step corresponds to a description item. |
-
-
-
-
|
- java.lang.Object |
- isTestStep(java.lang.Object row) Determines whether the current pipeline step corresponds to a test step item. |
+ getTestStepName(java.lang.Object row) Creates the test step name by combining the row name with its arguments |
-
|
- java.lang.Object |
- isTestStepFolder(java.lang.Object row) Determines whether the current pipeline step corresponds to a test folder item. |
-
-
-
null |
static void |
main(java.lang.String[] args) Implicit main method for Groovy Scripts |
-
+
|
static java.lang.Object |
resultToATXVerdict(java.lang.Object result) Maps the given build result to according TEST-GUIDE verdict. |
@@ -262,7 +244,7 @@ Inherited Methods Summary
class groovy.lang.Script |
- groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
+ groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
class groovy.lang.GroovyObjectSupport |
@@ -323,39 +305,31 @@ java.lang.Object call(java.lang.Object log = false, java.la
-
+
-
@com.cloudbees.groovy.cps.NonCPS
-java.lang.Object crawlRows(java.lang.Object row, java.lang.Object debug)
+java.lang.Object crawlRows(java.lang.Object row, java.lang.Object appendLogs, java.lang.Object insideStage = false)
Crawls the current table row for corresponding test execution step items.
Every row checks its child elements and its sibling recursively.
Only description, test step and test step folder items are considered.
- Parameters:
row
-
- the current table rowdebug
-
- the log level
- Returns:
- the test execution steps of the current table row
+ - the current table rowappendLogs
+
- if true, the individual logs of test steps are added in their description fieldinsideStage
+
- flag to indicate that crawling is done inside a stage block- Returns:
- the test execution steps of the current table row
-
+
-
-
-
@@ -363,10 +337,11 @@ @com.cloudbees.groovy.cps.NonCPS
-
@com.cloudbees.groovy.cps.NonCPS
-java.lang.Object createTestStepFolder(java.lang.Object row, java.lang.Object debug)
+java.lang.Object createTestStepFolder(java.lang.Object row, java.lang.Object appendLogs)
Create a test step folder item from the current pipeline step.
- Parameters:
row
-
- the current table row
- Returns:
- the test step folder as a map
+ - the current table rowappendLogs
+
- if true, the logs of row/node are added in the description field- Returns:
- the test step folder as a map
@@ -413,15 +388,26 @@ java.lang.Object getCurrentResult(java.lang.Object build)
+
+
+
-
@com.cloudbees.groovy.cps.NonCPS
-java.lang.Object getExecutionSteps(java.lang.Object build, java.lang.Object debug)
+java.lang.Object getExecutionSteps(java.lang.Object build, java.lang.Object appendLogs)
Create test execution steps from the current pipeline build.
- Parameters:
build
-
- the pipeline builddebug
-
- the log level
- Returns:
- the test execution steps of the pipeline build
+ - the pipeline buildappendLogs
+
- if true, the individual logs of test steps are added in their description field- Returns:
- the test execution steps of the pipeline build
@@ -447,47 +433,14 @@ java.lang.Object getRawBuild(java.lang.String jobName, int
-
-
-
-
-
-
-
-
-
-
+
+ Creates the test step name by combining the row name with its arguments
- Parameters:
row
-
- the current table row
- Returns:
true
if test step folder, false
otherwise
+ - the current table row- Returns:
- the test step name as a string (with a max length of 255)
diff --git a/docs/vars/task.html b/docs/vars/task.html
index e6aab8b..3142368 100644
--- a/docs/vars/task.html
+++ b/docs/vars/task.html
@@ -193,7 +193,7 @@ Inherited Methods Summary
class groovy.lang.Script |
- groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
+ groovy.lang.Script#getBinding(), groovy.lang.Script#setBinding(groovy.lang.Binding), groovy.lang.Script#invokeMethod(java.lang.String, java.lang.Object), groovy.lang.Script#evaluate(java.lang.String), groovy.lang.Script#evaluate(java.io.File), groovy.lang.Script#println(), groovy.lang.Script#println(java.lang.Object), groovy.lang.Script#run(java.io.File, [Ljava.lang.String;), groovy.lang.Script#run(), groovy.lang.Script#getProperty(java.lang.String), groovy.lang.Script#setProperty(java.lang.String, java.lang.Object), groovy.lang.Script#print(java.lang.Object), groovy.lang.Script#printf(java.lang.String, java.lang.Object), groovy.lang.Script#printf(java.lang.String, [Ljava.lang.Object;), groovy.lang.Script#getMetaClass(), groovy.lang.Script#setMetaClass(groovy.lang.MetaClass), groovy.lang.Script#wait(long), groovy.lang.Script#wait(long, int), groovy.lang.Script#wait(), groovy.lang.Script#equals(java.lang.Object), groovy.lang.Script#toString(), groovy.lang.Script#hashCode(), groovy.lang.Script#getClass(), groovy.lang.Script#notify(), groovy.lang.Script#notifyAll() |
class groovy.lang.GroovyObjectSupport |
diff --git a/test/vars/Pipeline2ATXTest.groovy b/test/vars/Pipeline2ATXTest.groovy
index 7beba3c..76a6896 100644
--- a/test/vars/Pipeline2ATXTest.groovy
+++ b/test/vars/Pipeline2ATXTest.groovy
@@ -7,14 +7,18 @@
import com.cloudbees.workflow.flownode.FlowNodeUtil
import groovy.testSupport.PipelineSpockTestBase
import hudson.console.AnnotatedLargeText
-import org.jenkinsci.plugins.workflow.actions.LabelAction
-import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution
-import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode
+import hudson.model.Build
+import hudson.model.Project
+import hudson.model.Result
+import hudson.model.Run
+import org.jenkinsci.plugins.workflow.job.WorkflowRun
+import org.jenkinsci.plugins.workflow.actions.ArgumentsAction
+import org.jenkinsci.plugins.workflow.actions.BodyInvocationAction
import org.jenkinsci.plugins.workflow.graph.AtomNode
import org.jenkinsci.plugins.workflow.graph.BlockStartNode
import org.jenkinsci.plugins.workflow.graph.FlowNode
-import org.jenkinsci.plugins.workflow.steps.StepDescriptor
import org.jenkinsci.plugins.workflow.support.actions.LogStorageAction
+import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper
import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable
class Pipeline2ATXTest extends PipelineSpockTestBase {
@@ -44,21 +48,62 @@ class Pipeline2ATXTest extends PipelineSpockTestBase {
result = getAttributes()
}
+ def 'Create json report'() {
+ given: 'all needed information to create a report'
+ def build = GroovyMock(Run)
+ def parent = Mock(Project)
+ parent.getDisplayName() >> 'UnitTests'
+ parent.getDescription() >> 'My Description'
+ build.getParent() >> parent
+ build.getTimeInMillis() >> 123456000
+ build.getStartTimeInMillis() >> 123457000
+
+ def attributes = [[key: 'testkey', value: 'testvalue']]
+ def teststeps = [['@type':'teststep']]
+
+ helper.registerAllowedMethod('getCurrentResult', [Object], {'SUCCESS'})
+ pipeline2ATX = loadScript(scriptName)
+
+ when: 'json string is generated'
+ String result = pipeline2ATX.generateJsonReport(build, attributes, teststeps, logfile)
+
+ then: 'expect to find the values in the json string'
+ result.contains('"name": "JenkinsPipeline"')
+ result.contains('"@type": "testcase"')
+ result.contains('"name": "UnitTests"') // test testcase name
+ result.contains('"verdict": "PASSED"')
+ result.contains('"timestamp": 123457000')
+ result.contains('"executionTime": 1')
+ result.contains('"key": "testkey"') // test attributes
+ result.contains('"@type": "teststep"') // test teststeps
+ if (logfile) { // test artifacts
+ result.contains('"artifacts":')
+ result.contains(logfile)
+ }
+
+ where:
+ logfile << ["log.file", ""]
+
+ }
+
def 'Get current build result'() {
- given: 'a build'
- def currentBuild = binding.getVariable('currentBuild')
- currentBuild['getResult'] = { return 'FAILED' }
- currentBuild['isInProgress'] = { return inProgress }
+ given: 'a build that will have FAILED result when its in progress and SUCCESS when already finished'
+ def build = Mock(Build)
+ build.isInProgress() >> inProgress
+ build.getResult() >> Result.SUCCESS
+ build.getExternalizableId() >> "test#123"
+ RunWrapper.metaClass.getCurrentResult = {return 'FAILED'}
when: 'get current build result'
- def result = pipeline2ATX.getCurrentResult(currentBuild)
+ def result = pipeline2ATX.getCurrentResult(build)
then: 'expect the current build result depends on progress status'
- result == 'FAILED'
+ result == exectedResult
where:
- // ToDo: implement the rainy day
- inProgress = false
+ inProgress | exectedResult
+ true | 'FAILED'
+ false | 'SUCCESS'
}
def 'Get console log'() {
@@ -91,178 +136,211 @@ class Pipeline2ATXTest extends PipelineSpockTestBase {
'INVALID' | 'NONE'
}
- def 'Test row corresponds to description'() {
- given: 'a description row with node'
- def node
- if (atomNode) {
- node = Mock(AtomNode)
- } else {
- node = Mock(BlockStartNode)
- }
- def row = new FlowGraphTable.Row(node)
- node.getAction(_) >> logStorage
- row.getNode() >> node
- if (child) {
- row.firstTreeChild = row
- } else {
- row.firstTreeChild = null
+ def 'Get execution steps'() {
+ given: 'a list of rows with different display names'
+ def build = GroovyMock(WorkflowRun)
+ def rows = []
+ ['stage', 'stage', 'nostage', 'parallel','stage','test'].each {name ->
+ def row = Mock(FlowGraphTable.Row)
+ row.getDisplayName() >> name
+ rows.add(row)
}
+ FlowGraphTable.metaClass.getRows = {return rows}
+ // use the appendLogs flag to change behavior and test different paths of the function
+ helper.registerAllowedMethod("crawlRows", [Object, Object, Object], {arg1, arg2, arg3 ->
+ if (arg2) {return ['@type':'dummy']}; return [:]
+ })
+ pipeline2ATX = loadScript(scriptName)
- when: 'check row is description'
- Boolean description = pipeline2ATX.isDescription(row)
+ when: 'list of rows is evaluated'
+ def result = pipeline2ATX.getExecutionSteps(build, appendLogs)
- then: 'expect is a description'
- description == result
+ then: 'result has the size of the number of `stage` rows that return a nonempty crawl result'
+ result.size() == expectedSize
where:
- atomNode | logStorage | child | result
- true | Mock(LogStorageAction) | false | true
- false | Mock(LogStorageAction) | false | false
- true | null | false | false
- true | Mock(LogStorageAction) | true | false
+ appendLogs << [true, false]
+ expectedSize << [3, 0]
+
}
- def 'Test row corresponds to test step'() {
- given: 'a testStep row with node'
- def stepDescriptor = Mock(StepDescriptor)
- stepDescriptor.getId() >> script
- def stepStart = new StepStartNode(Mock(CpsFlowExecution),
- stepDescriptor, Mock(FlowNode))
- def node
- if (startNode) {
- node = Mock(StepStartNode)
- } else {
- node = Mock(AtomNode)
- }
- def row = new FlowGraphTable.Row(node)
- def childRow = new FlowGraphTable.Row(stepStart)
- node.getAction(_) >> label
+ def 'Crawl a row with direct match'() {
+ given: 'A row with a specific node type and display name'
+ def row = Mock(FlowGraphTable.Row)
row.getNode() >> node
- if (child) {
- row.firstTreeChild = childRow
- } else {
- row.firstTreeChild = null
- }
- helper.registerAllowedMethod('isDescription', [Object],
- { return description })
+ row.getDisplayName() >> name
+
+ helper.registerAllowedMethod('createTestStep', [Object, Object], { row1, arg2 ->
+ return ['@type': 'teststep', 'name': row1.getDisplayName()]
+ })
+ helper.registerAllowedMethod('createTestStepFolder', [Object, Object], { row1, arg2 ->
+ return ['@type': 'teststepfolder', 'name': row1.getDisplayName()]
+ })
pipeline2ATX = loadScript(scriptName)
- when: 'check row is testStep'
- Boolean testStep = pipeline2ATX.isTestStep(row)
+ when: 'row is crawled'
+ def result = pipeline2ATX.crawlRows(row, false, insideStage)
- then: 'expect is a testStep'
- testStep == result
+ then: 'the correct atx item is returned'
+ result.size() == 2
+ result['name'] == expectedName
+ result['@type'] == expectedType
where:
- startNode | label | child | script | description | result
- true | Mock(LabelAction) | true | 'EchoStep' | false | false
- true | Mock(LabelAction) | true | 'EchoStep' | true | true
- true | Mock(LabelAction) | true | 'ScriptStep' | false | true
- true | Mock(LabelAction) | false | 'EchoStep' | false | true
- true | Mock(LogStorageAction) | true | 'EchoStep' | false | false
- false | Mock(LabelAction) | true | 'EchoStep' | false | false
+ node | name | insideStage | expectedName | expectedType
+ Mock(AtomNode) | "stage" | true | "creating stage" | "teststep"
+ Mock(BlockStartNode) | "stage" | true | "creating stage" | "teststep"
+ Mock(AtomNode) | "parallel" | true | "parallel" | "teststep"
+ Mock(BlockStartNode) | "stage" | false | "stage" | "teststepfolder"
}
- def 'Test row corresponds to test step folder'() {
- given: 'a testStepFolder row with node'
- def node
- if (startNode) {
- node = Mock(StepStartNode)
- } else {
- node = Mock(AtomNode)
+ def 'Crawl a row - edge cases'() {
+ given: 'A undefined row with or without a crawlable child row'
+ def outerRow = new FlowGraphTable.Row(Mock(FlowNode))
+ def innerRow = Mock(FlowGraphTable.Row)
+ innerRow.getNode() >> Mock(AtomNode)
+ innerRow.getDisplayName() >> "bat"
+
+ if (stack) {
+ outerRow.firstTreeChild = innerRow
}
- def row = new FlowGraphTable.Row(node)
- node.getAction(_) >> logStorage
- row.getNode() >> node
- row.firstTreeChild = child
- helper.registerAllowedMethod('isDescription', [Object],
- { return description })
+ helper.registerAllowedMethod('createTestStep', [Object, Object], { row1, arg2 ->
+ return ['@type': 'teststep', 'name': row1.getDisplayName()]
+ })
pipeline2ATX = loadScript(scriptName)
- when: 'check row is testStepFolder'
- Boolean testStepFolder = pipeline2ATX.isTestStepFolder(row)
+ when: 'row is crawled'
+ def result = pipeline2ATX.crawlRows(outerRow, false, false)
- then: 'expect is a testStepFolder'
- result == testStepFolder
+ then: 'crawl result of the child row or an empty result is returned'
+ result.size() == expectedSize
+ if (expectedSize > 0) {
+ result['name'] == 'bat'
+ result['@type'] == 'teststep'
+ }
where:
- startNode | logStorage | child | description | result
- true | null | Mock(FlowGraphTable.Row) | false | true
- false | null | Mock(FlowGraphTable.Row) | false | false
- true | Mock(LogStorageAction) | Mock(FlowGraphTable.Row) | false | false
- true | null | null | false | false
- true | null | Mock(FlowGraphTable.Row) | true | false
+ stack << [true, false]
+ expectedSize << [2, 0]
}
- def 'Create description item from row'() {
+ def 'Get description from row'() {
given: 'a description row with node'
def node = Mock(FlowNode)
def row = Mock(FlowGraphTable.Row)
row.getNode() >> node
- helper.registerAllowedMethod('hasNodeErrors', [Object], { return error })
helper.registerAllowedMethod('getLogText', [Object], { return logText })
pipeline2ATX = loadScript(scriptName)
- when: 'add description of node'
- def description = pipeline2ATX.createDescription(row)
+ when: 'get description of node'
+ def description = pipeline2ATX.getDescription(row)
+
+ then: 'expect a string with max length 120 or empty string'
+ description.length() <= 120
+ result == description
- then: 'expect a list of strings or empty list'
- result.equals(description)
+ where:
+ logText << ['Short test description', 'x'*150, '']
+ result << ['Short test description', 'x'*117+'...', '']
+ }
+
+ def 'Get TestStep Name from row'() {
+ given: 'A row optionally with arguments'
+ def node = Mock(AtomNode)
+ def row = new FlowGraphTable.Row(node)
+ node.getDisplayFunctionName() >> 'testnode'
+
+ ArgumentsAction.metaClass.static.getStepArgumentsAsString = {FlowNode n -> return arguments}
+
+ when: 'get step name of row'
+ String result = pipeline2ATX.getTestStepName(row)
+
+ then: 'result is the expected string'
+ result == expectedResult
where:
- logText << ['Another Test', null, 'Test']
- error << [false, false, true]
- result << [['message': 'Another Test', 'error': false], [:], ['message': 'Test', 'error': true]]
+ arguments | expectedResult
+ '' | 'testnode'
+ 'short' | 'testnode (short)'
+ 'x' * 150 | 'testnode (' + 'x' * 106 + '...)'
+ ' ' * 10 + '\t' * 10 + '\n' * 10 + 'stripped ' | 'testnode (stripped)'
}
def 'Create test step item from row'() {
- // TODO: Add child and sibling test blocked by https://github.com/jenkinsci/JenkinsPipelineUnit/issues/337
given: 'a row with node'
- def node = Mock(FlowNode)
+ def node = Mock(AtomNode)
def row = new FlowGraphTable.Row(node)
- def childRow = new FlowGraphTable.Row(node)
- def siblingRow = new FlowGraphTable.Row(node)
- def testStepMap = getTestStep(false, false)
- def description = [getTestStep(false, false).get('description')]
- node.getDisplayName() >> testStepMap['name']
FlowNodeUtil.metaClass.static.getStatus = { FlowNode n -> 'SUCCESS' }
- siblingRow.nextTreeSibling = null
- childRow.nextTreeSibling = null
-
- row.getNode() >> node
- row.firstTreeChild = null
-
- helper.registerAllowedMethod('resultToATXVerdict', [Object], { return testStepMap['verdict'] })
- // this won't work
- helper.registerAllowedMethod('crawlRows', [Object, Boolean], { return description })
+ helper.registerAllowedMethod('getTestStepName', [Object], { return tsName })
+ helper.registerAllowedMethod('getDescription', [Object], { return logText })
pipeline2ATX = loadScript(scriptName)
when: 'create a testStep'
- Map result = pipeline2ATX.createTestStep(row, false)
+ Map result = pipeline2ATX.createTestStep(row, addLogs, skipped)
then: 'expect a testStep map'
- getTestStep(false, false) == result
+ result['@type'] == 'teststep'
+ result['name'] == expectedName
+ result['verdict'] == expectedVerdict
+ result.containsKey('description') == hasDesciption
+ if (hasDesciption) {
+ result['description'] == logText
+ }
+
+ where:
+ tsName | addLogs | skipped | logText | expectedName | hasDesciption | expectedVerdict
+ "stage" | true | false | "this is a log" | "stage" | true | 'PASSED'
+ "stage" | false | false | "this is a log" | "stage" | false | 'PASSED'
+ "stage" | true | true | "" | "stage --> skipped" | false | 'NONE'
}
def 'Create test step folder from row'() {
- // TODO: Add child test blocked by https://github.com/jenkinsci/JenkinsPipelineUnit/issues/337
given: 'a row with node'
- def node = Mock(FlowNode)
- def row = new FlowGraphTable.Row(node)
- def childRow = new FlowGraphTable.Row(node)
- node.getDisplayName() >> 'testStepFolder'
-
- row.getNode() >> node
- row.firstTreeChild = null
+ def outerNode = Mock(BlockStartNode)
+ def outerRow = new FlowGraphTable.Row(outerNode)
+ def innerNode = Mock(BlockStartNode)
+ def innerRow = new FlowGraphTable.Row(innerNode)
+ innerNode.getAction(BodyInvocationAction.class) >> new BodyInvocationAction()
+
+ outerRow.firstTreeChild = innerRow
+ innerRow.firstTreeChild = new FlowGraphTable.Row(outerNode)
+ if (multiInnerBlock) {
+ innerRow.nextTreeSibling = new FlowGraphTable.Row(innerNode)
+ }
+ helper.registerAllowedMethod("crawlRows", [Object, Object, Object],
+ {arg1, arg2, arg3 -> if (crawlSuccess) {return ['@type':'dummy']} else { return null }})
+ helper.registerAllowedMethod('getTestStepName', [Object], { return 'foldertest' })
+ helper.registerAllowedMethod('getDescription', [Object], { return logText })
+ helper.registerAllowedMethod('createTestStep', [Object, Object, Object],
+ {arg1, arg2, arg3 -> return ['@type':'teststep']})
+ pipeline2ATX = loadScript(scriptName)
when: 'create a testStepFolder'
- Map result = pipeline2ATX.createTestStepFolder(row, false)
+ Map result = pipeline2ATX.createTestStepFolder(outerRow, appendLogs)
then: 'expect a testStepFolder map'
- result == [:]
+ if (crawlSuccess) {
+ result['@type'] == 'teststepfolder'
+ result['name'] == 'foldertest'
+ result['teststeps'].size() == expectedTestSteps
+ result.containsKey('description') == expectDesciption
+ if (expectDesciption) {
+ result['description'] == logText
+ }
+ } else {
+ result['@type'] == 'teststep'
+ }
+
+ where:
+ multiInnerBlock | crawlSuccess | logText | appendLogs | expectDesciption | expectedTestSteps
+ false | true | "log" | true | true | 1
+ false | false | "log" | false | false | 0
+ true | true | "log" | false | false | 2
+ false | true | "" | true | false | 1
+
}
def 'Get log text from pipeline step'() {
@@ -305,23 +383,4 @@ class Pipeline2ATXTest extends PipelineSpockTestBase {
return build
}
-
- private Map getTestStep(Boolean description, Boolean sibling) {
- Map testStep = [:]
- String text = ""
-
- testStep.put("@type", "teststep")
- testStep.put("name", "testStep")
- testStep.put("verdict", "PASSED")
-
- if (description) {
- text = "Test description"
- if (sibling) {
- text = [text, text].join("")
- }
- }
- testStep.put("description", text)
-
- return testStep
- }
}
diff --git a/vars/pipeline2ATX.groovy b/vars/pipeline2ATX.groovy
index c2129e5..0d38daa 100644
--- a/vars/pipeline2ATX.groovy
+++ b/vars/pipeline2ATX.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 TraceTronic GmbH
+ * Copyright (c) 2023 TraceTronic GmbH
*
* SPDX-License-Identifier: MIT
*/
@@ -9,7 +9,9 @@ import com.cloudbees.workflow.flownode.FlowNodeUtil
import groovy.json.JsonBuilder
import groovy.json.JsonOutput
import jenkins.model.Jenkins
-import org.jenkinsci.plugins.workflow.actions.LabelAction
+import org.jenkinsci.plugins.workflow.actions.ArgumentsAction
+import org.jenkinsci.plugins.workflow.actions.BodyInvocationAction
+import org.jenkinsci.plugins.workflow.actions.NotExecutedNodeAction
import org.jenkinsci.plugins.workflow.graph.AtomNode
import org.jenkinsci.plugins.workflow.graph.BlockStartNode
import org.jenkinsci.plugins.workflow.support.actions.LogStorageAction
@@ -39,7 +41,7 @@ import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable
*/
def call(log = false, jobName = '', int buildNumber = 0) {
def build
- String logFile
+ def logFile = ''
build = getRawBuild(jobName, buildNumber)
if (!build) {
@@ -53,13 +55,14 @@ def call(log = false, jobName = '', int buildNumber = 0) {
if (log) {
logText = getConsoleLog(build)
logFile = "${filename}.log"
- writeFile file: logFile, text: logText
}
-
def json = generateJsonReport(build, attributes, executionSteps, logFile)
// reset build because it's not serializable
build = null
+ if (logFile) {
+ writeFile file: logFile, text: logText
+ }
writeJSON file: "${filename}.json", json: json
zip glob: "${filename}.log, ${filename}.json", zipFile: "${filename}_atx.zip"
}
@@ -95,7 +98,9 @@ def getBuildAttributes(build) {
def buildAttributes = [BUILD_URL: buildUrl, BUILD_ID: buildId]
buildAttributes.putAll(params)
buildAttributes.each { k, v ->
- attributes.add([key: k, value: v.toString()])
+ if (v) {
+ attributes.add([key: k, value: v.toString()])
+ }
}
return attributes
}
@@ -199,12 +204,12 @@ static def resultToATXVerdict(result) {
*
* @param build
* the pipeline build
- * @param debug
- * the log level
+ * @param appendLogs
+ * if true, the individual logs of test steps are added in their description field
* @return the test execution steps of the pipeline build
*/
@NonCPS
-def getExecutionSteps(build, debug) {
+def getExecutionSteps(build, appendLogs) {
def executionTestSteps = []
// Table might not exist before execution is finished
@@ -212,9 +217,9 @@ def getExecutionSteps(build, debug) {
table.build()
// Get start of relevant pipeline steps
- def topLevelRows = table.getRows().stream().findAll { r -> r.getTreeDepth() == 3 }.toArray()
- for (FlowGraphTable.Row row : topLevelRows) {
- def item = crawlRows(row, debug)
+ def Stages = table.getRows().stream().findAll { r -> r.getDisplayName() == "stage" }.toArray()
+ for (FlowGraphTable.Row row : Stages) {
+ def item = crawlRows(row, appendLogs, false)
if (item) {
executionTestSteps.add(item)
}
@@ -229,124 +234,78 @@ def getExecutionSteps(build, debug) {
*
* @param row
* the current table row
- * @param debug
- * the log level
+ * @param appendLogs
+ * if true, the individual logs of test steps are added in their description field
+ * @param insideStage
+ * flag to indicate that crawling is done inside a stage block
* @return the test execution steps of the current table row
*/
@NonCPS
-def crawlRows(row, debug) {
- def atxItem = [:]
+def crawlRows(row, appendLogs, insideStage=false) {
def node = row.getNode()
- def item
-
- if (isDescription(row) && debug) {
- item = createDescription(row)
- } else if (isTestStep(row) && !node.isActive()) {
- // Search inside a active stage
- item = createTestStep(row, debug)
- } else if (isTestStepFolder(row)) {
- // Stages might have multiple stages, ignore the active stage
- item = createTestStepFolder(row, debug)
- } else {
- // Pipeline element cannot be mapped to an ATX item, search children and siblings instead
- def child = row.firstTreeChild
- while (child) {
- item = crawlRows(child, debug)
- child = child.nextTreeSibling
- }
- }
- if (item) {
- if (item instanceof Map) {
- atxItem.putAll(item)
- } else {
- return item
- }
+ if (insideStage && row.getDisplayName() == "stage") {
+ // all stages are handled on top-level -> when a stage inside a stage is found stop going down the hierarchy
+ def item = createTestStep(row, false)
+ item["name"] = "creating " + item["name"]
+ return item
+ } else if (node instanceof AtomNode) {
+ // AtomNodes = test steps
+ return createTestStep(row, appendLogs)
+ } else if (node instanceof BlockStartNode) {
+ // blocks = test step folders
+ return createTestStepFolder(row, appendLogs)
}
- return atxItem
-}
-
-/**
- * Determines whether the current pipeline step corresponds to a description item.
- *
- * @param row
- * the current table row
- * @return {@code true} if description, {@code false} otherwise
- */
-@NonCPS
-def isDescription(row) {
- def node = row.getNode()
- return (node.getAction(LogStorageAction.class) &&
- node instanceof AtomNode && !row.firstTreeChild)
+ // Pipeline element cannot be mapped to an ATX item -> skip row
+ def child = row.firstTreeChild
+ if (child) {
+ return crawlRows(child, appendLogs, insideStage)
+ }
+ return [:]
}
/**
- * Determines whether the current pipeline step corresponds to a test step item.
+ * Create a description from the current pipeline step.
*
* @param row
* the current table row
- * @return {@code true} if test step, {@code false} otherwise
+ * @return the description as a string (with a max length of 255)
*/
@NonCPS
-def isTestStep(row) {
- def node = row.getNode()
+def getDescription(row) {
+ def allowedSchemaMaxStringLength = 120
- if (!(node.getAction(LabelAction.class) && node instanceof BlockStartNode)) {
- return false
- }
+ def logText = getLogText(row.getNode())
- // Stage is parent of step or empty
- if (!row.firstTreeChild) {
- return true
- }
-
- // Determine node descriptor and verify a script step
- String scriptDescriptor = row.firstTreeChild.getNode().descriptorId
- def isScript = scriptDescriptor.endsWith('ScriptStep')
- if (isDescription(row.firstTreeChild) || isScript) {
- return true
+ if (logText.length() > allowedSchemaMaxStringLength) {
+ // Set allowedSchemaMaxStringLength to 117 to be able to concatenate it with "..."
+ return logText.take(allowedSchemaMaxStringLength - 3) + "..."
}
-
- return false
+ return logText
}
/**
- * Determines whether the current pipeline step corresponds to a test folder item.
- *
+ * Creates the test step name by combining the row name with its arguments
* @param row
* the current table row
- * @return {@code true} if test step folder, {@code false} otherwise
+ * @return the test step name as a string (with a max length of 255)
*/
@NonCPS
-def isTestStepFolder(row) {
- def node = row.getNode()
- def child = row.firstTreeChild
- if (!node.getAction(LogStorageAction.class) && node instanceof BlockStartNode) {
- return (child && !isDescription(child))
- } else {
- return false
- }
-}
+def getTestStepName(row) {
+ def allowedSchemaMaxNameLength = 120
+ String name = row.getDisplayName()
-/**
- * Create a description from the current pipeline step.
- *
- * @param row
- * the current table row
- * @return the description as an array
- */
-@NonCPS
-def createDescription(row) {
- def node = row.getNode()
- Map description = [:]
- def logText = getLogText(node)
+ def arguments = ArgumentsAction.getStepArgumentsAsString(row.getNode())
+ if (arguments) {
+ arguments = arguments.replaceAll(/\s+/, ' ').trim()
+ def maxArgumentLength = allowedSchemaMaxNameLength - name.length() - 6
+ arguments = arguments.length() > maxArgumentLength ? arguments.take(maxArgumentLength) + "..." : arguments
- if (logText) {
- description.put("message", logText)
- description.put("error", hasNodeErrors(node))
+ name = "$name ($arguments)"
}
- return description
+
+ return name
}
/**
@@ -354,42 +313,34 @@ def createDescription(row) {
*
* @param row
* the current table row
+ * @param appendLogs
+ * if true, the logs of row/node are added in the description field
+ * @param skipped
+ * possibility to mark the test step as skipped (usually for block nodes)
* @return the test step as a map
*/
@NonCPS
-def createTestStep(row, debug) {
+def createTestStep(row, appendLogs, skipped = false) {
def node = row.getNode()
Map testStep = [:]
- def name = node.getDisplayName()
+ def name = getTestStepName(row)
def status = FlowNodeUtil.getStatus(node).toString()
def verdict = resultToATXVerdict(status)
+ if (skipped || node.getAction(NotExecutedNodeAction.class)) {
+ name = name + " --> skipped"
+ verdict = "NONE"
+ }
+
testStep.put("@type", "teststep")
testStep.put("name", name)
testStep.put("verdict", verdict)
- def child = row.firstTreeChild
- def description = []
- while (debug && child) {
- // Stage might has multiple steps
- def descriptionItem = crawlRows(child, true)
- if (descriptionItem) {
- description.add(descriptionItem.message)
- if (descriptionItem.error){
- // overwrite verdict of testStep if pipeline step fails
- testStep.verdict = resultToATXVerdict("FAILED")
- }
+ if (appendLogs) {
+ def description = getDescription(row)
+ if (description) {
+ testStep.put("description", description)
}
- child = child.nextTreeSibling
- }
-
- def allowedSchemaMaxStringLength = 120
- def testCaseMessage = description.join(";") ?: ""
- if (testCaseMessage.length() > allowedSchemaMaxStringLength) {
- // Set allowedSchemaMaxStringLength to 117 to be able to concatenate it with "..."
- testStep.put("description", testCaseMessage.take(allowedSchemaMaxStringLength-3) + "...")
- } else {
- testStep.put("description", "")
}
return testStep
}
@@ -399,32 +350,52 @@ def createTestStep(row, debug) {
*
* @param row
* the current table row
+ * @param appendLogs
+ * if true, the logs of row/node are added in the description field
* @return the test step folder as a map
*/
@NonCPS
-def createTestStepFolder(row, debug) {
- def node = row.getNode()
+def createTestStepFolder(row, appendLogs) {
Map testStepFolder = [:]
- def name = node.getDisplayName()
+ def name = getTestStepName(row)
def child = row.firstTreeChild
+ def skipped = false
+ // there are always two BlockStartNodes stacked. The outer is the actual block, the inner one encapsulates the
+ // body (identified by a BodyInvocationAction). Most of the time a block has just one body, so it does not have
+ // any additional value and can be ignored
+ // One example case for having multiple bodies is a "parallel" block
+ if (child && child.getNode().getAction(BodyInvocationAction.class) && !(child.nextTreeSibling)) {
+ // check if the block was not executed
+ if (child.getNode().getAction(NotExecutedNodeAction.class)) {
+ skipped = true
+ }
+ child = child.firstTreeChild
+ }
def testSteps = []
while (child) {
- // Stages might have more than one stage
- if (crawlRows(child, debug)) {
- testSteps.add(crawlRows(child, debug))
+ def testStepItem = crawlRows(child, appendLogs, true)
+ if (testStepItem) {
+ testSteps.add(testStepItem)
}
child = child.nextTreeSibling
}
- // Safety downstream handling
- if (!testSteps) {
- return testStepFolder
+ // if no inner test steps where found, convert row into a test step instead
+ if (!testSteps || skipped) {
+ return createTestStep(row, appendLogs, skipped)
}
testStepFolder.put("@type", "teststepfolder")
testStepFolder.put("name", name)
testStepFolder.put("teststeps", testSteps)
+
+ if (appendLogs) {
+ def description = getDescription(row)
+ if (description) {
+ testStepFolder.put("description", description)
+ }
+ }
return testStepFolder
}
@@ -448,27 +419,15 @@ def getLogText(node) {
return log
}
- def logLen = annotated.length();
+ def logLen = annotated.length()
if (logLen > 0) {
- def writer = new StringWriter();
+ def writer = new StringWriter()
try {
- annotated.writeHtmlTo(0, writer);
- log = writer.toString();
+ annotated.writeHtmlTo(0, writer)
+ log = writer.toString()
} catch (e) {
- println "Error serializing log for ${e}";
+ println "Error serializing log for ${e}"
}
}
return log
}
-
-/**
- * Checks if current node has any errors
- *
- * @param node
- * the current node
- * @return node has errors as boolean
- */
-@NonCPS
-boolean hasNodeErrors(node) {
- return node.getError() != null
-}