Playback via Gradle
abstract
This section describes playing back Sahi Flowcharts via Gradle.
Playback on a single machine
One can now use theRun Settings dialog to generate the Gradle script automatically. Refer Run Settings for details on the UI.
- Select appropriate settings for
Start Mode,BrowserandStart URL. - Click the
Create Gradle Script (Groovy)button or theCreate Gradle Script (Kotlin)button.
This will generate the contents of the Gradle script file.
If you wish for only the failed scripts to be retried after the run is complete, check the
Add Retry Task checkbox on the Gradle Script dialog.
Once the Gradle script content is generated, copy the content to an empty file. If groovy was selected for gradle script, save the file as build.gradle, and for Kotlin, save the file as build.gradle.kts in your
<SAHI_INSTALLATION_FOLDER>.
Use the gradle command to execute the gradle script.
gradle
or
./gradlew # if the script is saved as a gradle project
info
- Gradle classpath is relative to where gradle is run from.
- The main task is the runsahitests task which is also the default task, so you do not need to specify the task when running the gradle script.
info
If you choose the
Run sequentially in single browser session option, single session attribute would be set to true. Everything else remains the same.
info
By default Gradle suppresses the info logs generated by Sahi.
To view these logs execute the script using
gradle --info or,
create a file named gradle.properties alongside build.gradle and add this line:
org.gradle.logging.level=info
info
When running large projects, you may face heap overflow issues gradle. This can be resolved by increasing the heap size of gradle.
eg., to set heap size to 2048MB
- run gradle with
gradle -Dorg.gradle.jvmargs=-Xmx2048m - create a file named
gradle.propertiesalongsidebuild.gradleand add this line:org.gradle.jvmargs=-Xmx2048m
Distributed playback via Gradle
Distributed playback can be achieved through Gradle tasks as well.These tasks can be used from Jenkins to distribute the scripts on multiple machines. For more details please refer Jenkins Integration.
The machine which distributes the scripts and manages the distributed run is referred to as the
Master. All logs are visible from the Master. The other machines are called Slaves. Any machine in the distributed environment can serve as the Master. The machine which launches the distributed run is referred to as the Initiator.
Distributed run on the same Master
In theRun Settings dialog,
- Under
Advanced Run Settings, chooseRun distributed (multiple machines)option. - Leave the
Use Different Mastercheckbox unchecked. - Click the
Create Gradle Script (Groovy)button or theCreate Gradle Script (Kotlin)button.
Distributed run on a different Master
In theRun Settings dialog,
- Under
Advanced Run Settings, chooseRun distributed (multiple machines)option. - Check the
Use Different Mastercheckbox and choose the Master details. - Click the
Create Gradle Script (Groovy)button or theCreate Gradle Script (Kotlin)button.
info
Depending on the scope of run, the
fcProjectId, fcFlowchartId and fcPathId properties appear differently in the setproperties task of gradle script.
- Project level Run: This occurs when the
Runbutton on theProjectpage is used. All the flowcharts and all the paths within the specified project will be executed. OnlyfcProjectIdproperty will be present in the gradle script. - Flowchart level Run: This occurs when the
Runbutton on theFlowchartpage is used. Only the specified flowchart among all the flowcharts of the selected project will be executed. BothfcProjectIdandfcFlowchartIdproperties will be present in the gradle script. - Path level Run: This occurs when the
Runbutton on theAutomatedialog is used. Only the specified path among all the paths of the selected flowchart will be executed. All three properties,fcProjectId,fcFlowchartIdandfcPathIdwill be present in the gradle script.
Sample Gradle Script
The above UIs for Parallel run/distributed run/different master would generate a Gradle script that looks like the following.# Groovy Script
apply plugin: 'base'
apply plugin: 'java'
buildscript {
repositories {
flatDir name: 'localRepository', dirs: 'lib'
}
dependencies {
classpath ':ant-sahi'
}
}
import net.sf.sahi.util.BuildUtils
import java.text.DateFormat
import java.text.SimpleDateFormat
static def getFormattedDateTime() {
DateFormat df = new SimpleDateFormat("yyyy_MM_dd__HH_mm_ss", new Locale("en", "IN"))
return df.format(new Date())
}
tasks.register("set_timestamp") {
group = "Sahi"
description = "Set timestamp"
extensions.add("timestamp", getFormattedDateTime())
}
tasks.register("setpropertieschrome") {
group = "Sahi"
description = "Initialize properties for run"
dependsOn "set_timestamp"
def ts = tasks.named("set_timestamp").get().timestamp
def browser = "chrome"
def props = [
isFlowchartRun: "true",
scriptDir: "D:/SahiPro/userdata/scripts5/",
host: "localhost",
port: "9999",
threads: "5",
autoThread: "false",
failureRetryCount: "2",
abortedRetryCount: "1",
isAvoidSkipping: "false",
startWith: "BROWSER",
browser: "chrome",
baseurl: "https://demoapps.testflowchart.com/opencart/",
tags: "",
suiteTimeOut: "0.0",
fcIsNormalRun: "true",
fcIsPositiveValidationRun: "true",
fcIsNegativeValidationRun: "true",
fcRetryFailuresEnabled: "true",
failedSuiteName: "almost empty_failed_chrome.flowcharts.dd.csv",
failedSuite: "almost empty_failed_chrome.flowcharts.dd.csv",
fcProjectId: "almost empty",
fcProjectName: "almost empty",
fcFlowchartId: "opencart registration",
fcFlowchartName: "opencart registration",
fcPathId: "0",
sendemail: "true",
emailtrigger: "success,failure,user_aborted,aborted_timeout",
emailattachment: "success,failure",
emailproperties: "D:\\SahiPro\\userdata\\config\\email.properties",
sendemailperiodically: "false",
emailPasswordHidden: "true",
haltonfailure: "false",
showPeriodicSummary: "false",
userDefinedId: "fc-user-id",
isDRun: "true",
ignorePattern: ".*(svn|copied).*",
scriptsPathMaster: "temp/scripts/staging/${ts}_${browser}",
scriptsPathInitiator: "D:/SahiPro/userdata/scripts5/",
isSyncConfig: "true",
isDifferentMaster: "true",
sahihost: "192.168.1.94",
sahiport: "9999",
nodes: "192.168.1.92:9999,172.68.43.69:9999",
reports: "junit,xml:D:/reports/xml,html:D:/reports/html",
customfields: "extraParameters=filePath",
scriptExtensions: "sah;sahi;js;ar.csv",
scenarioExtensions: ".s.csv;xls;xlsx",
csvSeparator: ",",
javaDir: "java",
exposedJavaClasses: "exposed_classes.txt"
]
project.ext.propschrome = props
}
def buildUtils_chrome = new BuildUtils()
tasks.register("runtestschrome") {
group = "Sahi"
description = "Run tests on chrome browser"
dependsOn "setpropertieschrome"
def props_chrome_provider = project.provider {project.ext.propschrome}
doLast {
def ts = tasks.named("set_timestamp").get().timestamp
def properties = props_chrome_provider.get() as HashMap
def argsMap = [:]
properties.each {key,value ->
argsMap.put(key as String, value as String)
}
argsMap.put("reports", "junit:logs/temp/junit/${ts},xml:logs/temp/xml/${ts},html:logs/temp/html/${ts}".toString())
def status = buildUtils_chrome.execute(argsMap , true)
println "runtestschrome STATUS: ${status}"
def deleteParams = [
scriptDir: argsMap.scriptsPathInitiator,
filePath: argsMap.scriptsPathMaster,
isAutohealEnabled: argsMap.isAutohealEnabled,
isSyncConfig: argsMap.isSyncConfig
]
BuildUtils.deleteSuiteFromMasterStaging(argsMap.sahihost, argsMap.sahiport, deleteParams)
def logs = [
file("logs/junit").absolutePath, "D:/reports/xml", "D:/reports/html"
]
BuildUtils.pullOfflineLogsFromMaster(argsMap.sahihost, argsMap.sahiport, argsMap.ignorePattern, logs, argsMap.reports)
runtestschrome.ext.status = status
}
finalizedBy "retrytestschrome"
}
tasks.register("retrytestschrome") {
group = "Sahi"
description = "Retry failed scripts on chrome Browser"
dependsOn "setpropertieschrome"
onlyIf {
runtestschrome.ext.status != "SUCCESS"
}
def props_chrome_provider = project.provider {project.ext.propschrome}
doLast {
def ts = tasks.named("set_timestamp").get().timestamp
def properties = props_chrome_provider.get() as HashMap
def argsMap = [:]
properties.each {key,value ->
argsMap.put(key as String, value as String)
}
argsMap["scriptsPathMaster"] = "${argsMap["scriptsPathMaster"]}_retry".toString()
argsMap["suite"] = argsMap["failedSuite"]
argsMap.put("reports", "junit:logs/temp/junit/${ts}/retry,xml:logs/temp/xml/${ts}/retry,html:logs/temp/html/${ts}/retry".toString())
def status = buildUtils_chrome.retry(argsMap)
println "retrytestschrome STATUS: ${status}"
def deleteParams = [
scriptDir: argsMap.scriptsPathInitiator,
filePath: argsMap.scriptsPathMaster,
isAutohealEnabled: argsMap.isAutohealEnabled,
isSyncConfig: argsMap.isSyncConfig
]
BuildUtils.deleteSuiteFromMasterStaging(argsMap.sahihost, argsMap.sahiport, deleteParams)
def logs = [
file("logs/junit/retry").absolutePath, "D:/reports/xml/retry", "D:/reports/html/retry"
]
BuildUtils.pullOfflineLogsFromMaster(argsMap.sahihost, argsMap.sahiport, argsMap.ignorePattern, logs, argsMap.reports)
if (!"SUCCESS".equalsIgnoreCase(status)) {
throw new GradleException("sahi retry tests failed on browser ${properties.browser} with status ${status}")
}
}
}
tasks.register("runsahitests") {
group = "Sahi"
description = "Run all tests"
dependsOn "runtestschrome"
}
defaultTasks 'runsahitests'
# Kotlin Script
import net.sf.sahi.util.BuildUtils
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Date
plugins {
id ("base")
id("java")
}
buildscript {
dependencies {
classpath(files("lib/ant-sahi.jar"))
}
}
fun getFormattedDateTime(): String {
val df = SimpleDateFormat("yyyy_MM_dd__HH_mm_ss", Locale.of("en", "IN"))
return df.format(Date())
}
tasks.register("settimestamp") {
group = "Sahi"
description = "Set timestamp"
extensions.add("timestamp", getFormattedDateTime())
}
val propschrome: MapProperty<String, String> = project.objects.mapProperty(String::class.java, String::class.java)
tasks.register("setpropertieschrome") {
group = "Sahi"
description = "Initialize properties for run"
dependsOn("settimestamp")
val ts = tasks.named("settimestamp").get().extensions.findByName("timestamp")
val browser = "chrome"
val props = mutableMapOf(
"isFlowchartRun" to "true",
"scriptDir" to "D:/SahiPro/userdata/scripts5/",
"host" to "localhost",
"port" to "9999",
"threads" to "5",
"autoThread" to "false",
"failureRetryCount" to "2",
"abortedRetryCount" to "1",
"isAvoidSkipping" to "false",
"startWith" to "BROWSER",
"browser" to "chrome",
"baseurl" to "https://demoapps.testflowchart.com/opencart/",
"tags" to "",
"suiteTimeOut" to "0.0",
"fcIsNormalRun" to "true",
"fcIsPositiveValidationRun" to "true",
"fcIsNegativeValidationRun" to "true",
"fcRetryFailuresEnabled" to "true",
"failedSuiteName" to "almost empty_failed_chrome.flowcharts.dd.csv",
"failedSuite" to "almost empty_failed_chrome.flowcharts.dd.csv",
"fcProjectId" to "almost empty",
"fcProjectName" to "almost empty",
"fcFlowchartId" to "opencart registration",
"fcFlowchartName" to "opencart registration",
"fcPathId" to "0",
"sendemail" to "true",
"emailtrigger" to "success,failure,user_aborted,aborted_timeout",
"emailattachment" to "success,failure",
"emailproperties" to "D:\\SahiPro\\userdata\\config\\email.properties",
"sendemailperiodically" to "false",
"emailPasswordHidden" to "true",
"haltonfailure" to "false",
"showPeriodicSummary" to "false",
"userDefinedId" to "fc-user-id",
"isDRun" to "true",
"ignorePattern" to ".*(svn|copied).*",
"scriptsPathMaster" to "temp/scripts/staging/${ts}_${browser}",
"scriptsPathInitiator" to "D:/SahiPro/userdata/scripts5/",
"isSyncConfig" to "true",
"isDifferentMaster" to "true",
"sahihost" to "192.168.1.94",
"sahiport" to "9999",
"nodes" to "192.168.1.92:9999,172.68.43.69:9999",
"reports" to "junit,xml:D:/reports/xml,html:D:/reports/html",
"customfields" to "extraParameters=filePath",
"scriptExtensions" to "sah;sahi;js;ar.csv",
"scenarioExtensions" to ".s.csv;xls;xlsx",
"csvSeparator" to ",",
"javaDir" to "java",
"exposedJavaClasses" to "exposed_classes.txt"
)
propschrome.set(props)
}
val buildUtils_chrome = BuildUtils()
tasks.register("runtestschrome") {
group = "Sahi"
description = "Run tests on chrome browser"
dependsOn("setpropertieschrome")
doLast {
val ts = tasks.named("settimestamp").get().extensions.findByName("timestamp")
val properties = propschrome.get()
val argsMap = mutableMapOf<String, String>()
properties.forEach { (key: String, value: String) ->
argsMap.put(key, value)
}
argsMap.put("reports", "junit:logs/temp/junit/${ts},xml:logs/temp/xml/${ts},html:logs/temp/html/${ts}".toString())
val status = buildUtils_chrome.execute(argsMap , true)
println("runtestschrome STATUS: ${status}")
val deleteParams = mutableMapOf(
"scriptDir" to "${argsMap["scriptsPathInitiator"]}",
"filePath" to "${argsMap["scriptsPathMaster"]}",
"isAutohealEnabled" to "${argsMap["isAutohealEnabled"]}",
"isSyncConfig" to "${argsMap["isSyncConfig"]}"
)
BuildUtils.deleteSuiteFromMasterStaging(argsMap["sahihost"], argsMap["sahiport"], deleteParams)
val logs = arrayOf(
file("logs/junit").absolutePath, "D:/reports/xml", "D:/reports/html"
).toMutableList()
BuildUtils.pullOfflineLogsFromMaster(argsMap["sahihost"], argsMap["sahiport"], argsMap["ignorePattern"], logs, argsMap["reports"])
project.extra.set("runtestschromestatus", status)
}
finalizedBy("retrytestschrome")
}
tasks.register("retrytestschrome") {
group="Sahi"
description="Retry failed scripts on chrome browser"
dependsOn("setpropertieschrome")
onlyIf {
!(project.extra.get("runtestschromestatus") as String).equals("SUCCESS", ignoreCase=true)
}
doLast {
val ts = tasks.named("settimestamp").get().extensions.findByName("timestamp")
val properties = propschrome.get()
val argsMap = mutableMapOf<String, String>()
properties.forEach { (key: String, value: String) ->
argsMap.put(key as String, value as String)
}
argsMap["scriptsPathMaster"] = "${argsMap["scriptsPathMaster"]}_retry".toString()
argsMap["suite"] = argsMap["failedSuite"] ?: ""
argsMap.put("reports", "junit:logs/temp/junit/${ts}/retry,xml:logs/temp/xml/${ts}/retry,html:logs/temp/html/${ts}/retry".toString())
val status = buildUtils_chrome.retry(argsMap)
println("retrytestschrome STATUS: ${status}")
val deleteParams = mutableMapOf(
"scriptDir" to "${argsMap["scriptsPathInitiator"]}",
"filePath" to "${argsMap["scriptsPathMaster"]}",
"isAutohealEnabled" to "${argsMap["isAutohealEnabled"]}",
"isSyncConfig" to "${argsMap["isSyncConfig"]}"
)
BuildUtils.deleteSuiteFromMasterStaging(argsMap["sahihost"], argsMap["sahiport"], deleteParams)
val logs = arrayOf(
file("logs/junit/retry").absolutePath, "D:/reports/xml/retry", "D:/reports/html/retry"
).toMutableList()
BuildUtils.pullOfflineLogsFromMaster(argsMap["sahihost"], argsMap["sahiport"], argsMap["ignorePattern"], logs, argsMap["reports"])
if (!"SUCCESS".equals(status, ignoreCase = true)) {
throw GradleException("sahi tests failed on browser ${properties.get("browser")} with status ${status}")
}
}
}
tasks.register("runsahitests") {
group = "Sahi"
description = "run all tests"
dependsOn("runtestschrome")
}
defaultTasks("runsahitests")
danger
NOTE:
The above Gradle Script is for illustration purposes only. Use the Run Settings dialog to generate the gradle script content and save it to a file.
Attributes
Description of the Attributes in gradle scripts:
| scriptDir | Specifies the path to the scripts folder. Can be the absolute path or a path relative to the userdata folder |
| fcProjectId | This contains the name of the project intended to run. |
| fcFlowchartId | This contains the name of the particular flowchart we intended to run. |
| fcPathId | This contains specific path ID which we intend to run. |
| fcIsNormalRun | This contains state of the checkbox "Run Normal Path". If checked, the path will run without any validation i.e; with default or data generator's values. |
| fcIsNegativeValidationRun | This contains state of the checkbox "Run Negative Validations". If checked, the path will run with negative validation values. |
| fcIsPositiveValidationRun | This contains state of the checkbox "Run Positive Validations". If checked, the path will run with positive validation values. |
| fcIsRetryFailuresEnabled | This contains state of the checkbox "Add Retry Task". The value needs to be"true" if the user needs to retry the failed path during first run attempt. If the checkbox for this property is checked, it adds an extra retry task, for example, retrytestschrome for running tests on chrome browser.
|
| browser | The browser on which the file plays back |
| baseUrl | Specifies the starting URL for all the scripts. |
| host | Hostname of the server where Sahi is running (Can be IP as well) |
| port | Port on which Sahi is running |
| threads | Number of simultaneous browser instances on which sahi tests will be run. |
| singlesession | Since this is a parallel run, it will be "false". If "true", runs all scripts in a single browser without closing it between scripts. |
| showPeriodicSummary | Takes "true" or "false". If "true", a periodic summary of script status is logged in the Gradle Console. |
| sendemail | Takes "true" or "false". If "true", Sahi sends an email summarizing the playback. |
| emailtrigger | Takes "success" or "failure" or "success,failure". Triggers email in case of SUCCESS, FAILURE and both cases respectively. |
| emailproperties | Path to the properties file that contains details about the mail viz. information about the sender, receiver, host, port, subject, content etc. Path can be the absolute path or relative to where this gradle task is run from. |
| sendemailperiodically | Takes "true" or "false". If "true", Sahi will send emails periodically till the execution is complete. |
| sendemailperiodicallytime | Specifies the interval in minutes at which periodic emails are to be sent. |
| failedSuiteName | Name of the failed suite. It is of the form <suitename>_failed_<browser>.suite. |
| failedsuite | Relative path to the failed suite. It is relative to scriptDir. |
| tags | Tags should be specified only for dd.csv and .csv suites. Tags are specified so that it is easy to choose which scripts/testcases to run.
eg. if tags are (user||admin)&&medium, all the scripts which have 'medium' tag and 'admin' or 'user' (or both) tag will be run.
|
| userdefinedid | The unique id that the user can pass to differentiate a suite from others. This is an optional attribute. |
| reports | Sahi can set offline logs to be generated in xml, html, pdf, junit, and excel types. The default type is html. Check the options required. This is an optional attribute. |
| customfields | This contains the values present in External Variables field. This is an optional attribute.User can define multiple key-value pairs in the text-box provided(one pair each line). User then needs to copy the content of this text-box to a .propertiesfile and save it with a proper name. Now, the user needs to copy the path of this file into the value section of the .xml file's customfield property. |
| nodes | nodes specify the machines on which the tests should run. Add as many node entries as there are machines to run. The nodes may or may not include the Master machine (localhost). If the Master machine is not included, scripts will not be run on the Master. There can be 1 or more nodes.
This is an optional attribute. |
| sahihost | Hostname of the different Master server where Sahi is running (Can be IP as well) |
| sahiport | Port on which Sahi is running |
| isSyncConfig | Shows status of Sync Configuration checkbox. If checked, it will sync the connfigurations from master machine to other nodes/slave machines. |
| configPath | location of the configuration file |
Killing an Ongoing Execution
warning
WARNING: Killing single or all ongoing executions in the middle of it is not a routine task. You would kill an execution only if something went wrong and you wish to stop the execution.
Stop All from the Run button's dropdownn menu from the UI can be used to kill all ongoing executions.
This is possible through gradle task as well.
The Gradle task gives the ability to kill a specific execution or all ongoing executions.
You can run the Gradle task from the generated gradle script as
gradle kill -Phost=<masterhost> -Pport=<masterport> -PsuiteId=<suiteId> -PuserDefinedId=<userDefinedId>
Parameters and their description
| sahihost | Hostname of the Master where Sahi is running (Can be IP as well). |
| sahiport | Port on which Sahi is running |
| userDefinedId | UserDefinedId specified while running the original suite |
| suiteId | SuiteId of the running suite. Get this value from the Suite Info section of the Suite Report logs |
info
Alternatively, the following URL can be used to kill all the running scripts:
http://<master machine IP>:9999/_s_/dyn/pro/Master_killAll
next
Jenkins Integration