diff --git a/example_test_suites/example_macos/case1/sakuli_demo.js b/example_test_suites/example_macos/case1/sakuli_demo.js
index 41dab094..4103b676 100644
--- a/example_test_suites/example_macos/case1/sakuli_demo.js
+++ b/example_test_suites/example_macos/case1/sakuli_demo.js
@@ -32,17 +32,18 @@ try {
testCase.endOfStep("Test Sahi landing page",30);
appCalc.open();
- screen.waitForImage("calculator.png", 10);
+// screen.waitForImage("calculator.png", 10);
+ env.sleep(4);
env.type("525");
env.sleep(2);
screen.find("plus.png").click().type("100");
screen.find("result.png").click();
- screen.waitForImage("625", 5);
+// screen.waitForImage("625", 5);
testCase.endOfStep("Calculation",30);
appGedit.open();
- screen.waitForImage("gedit.png", 10);
+// screen.waitForImage("gedit.png", 5);
env.paste("Initial test passed. Sakuli, Sahi and Sikuli seem to work fine. Exiting...");
testCase.endOfStep("Editor",30);
env.sleep(4);
diff --git a/pom.xml b/pom.xml
index 89752a50..fe592213 100644
--- a/pom.xml
+++ b/pom.xml
@@ -610,6 +610,11 @@
jtwig-spaceless-extension
1.44
+
+ com.google.code.gson
+ gson
+ 2.8.1
+
diff --git a/src/common/pom.xml b/src/common/pom.xml
index 9588e729..a8816637 100644
--- a/src/common/pom.xml
+++ b/src/common/pom.xml
@@ -41,6 +41,7 @@
false
**/version.txt
+ **/sakuli-default.properties
@@ -48,6 +49,7 @@
true
**/version.txt
+ **/sakuli-default.properties
diff --git a/src/common/src/main/resources/org/sakuli/common/bin/installer_scripts/linux/set_sakuli_home.sh b/src/common/src/main/resources/org/sakuli/common/bin/installer_scripts/linux/set_sakuli_home.sh
index 1d194c79..fc72e569 100755
--- a/src/common/src/main/resources/org/sakuli/common/bin/installer_scripts/linux/set_sakuli_home.sh
+++ b/src/common/src/main/resources/org/sakuli/common/bin/installer_scripts/linux/set_sakuli_home.sh
@@ -28,6 +28,21 @@ function set_sakuli_root {
fi
}
+function remove_sakuli_root {
+ echo "remove 'SAKULI_HOME' from '.bashrc'"
+ [ -r ~/.bashrc ] && sed -i '/export SAKULI_HOME/d' ~/.bashrc
+}
+
+function set_sakuli_root {
+ if [ -d "$1" ]; then
+ echo "set 'SAKULI_ROOT' in '.bashrc' to: $1"
+ echo "export SAKULI_ROOT=\"$1\"" >> ~/.bashrc
+ else
+ echo "new 'SAKULI_ROOT' path '$1' does not exists!"
+ exit 1
+ fi
+}
+
function set_path {
echo "$PATH"
if grep -q SAKULI_HOME/bin: ~/.bashrc 2>/dev/null; then
diff --git a/src/common/src/main/resources/org/sakuli/common/config/sakuli-default.properties b/src/common/src/main/resources/org/sakuli/common/config/sakuli-default.properties
index c04b5e81..a418cc9a 100644
--- a/src/common/src/main/resources/org/sakuli/common/config/sakuli-default.properties
+++ b/src/common/src/main/resources/org/sakuli/common/config/sakuli-default.properties
@@ -280,6 +280,16 @@ sakuli.forwarder.check_mk.spoolfile_prefix=sakuli_suite_
# optional service description forwarded to the output check result, when not set, testsuite.id is used
sakuli.forwarder.check_mk.service_description=${testsuite.id}
+
+##### JSON - FORWARDER
+# Save results into a json file. The JSON forwarder is enabled per default.
+
+# DEFAULT: true
+sakuli.forwarder.json.enabled=true
+## database host
+sakuli.forwarder.json.dir=${sakuli.log.folder}/_json
+
+
###############################
# LOGGING & ERROR-SCREENSHOT PROPERTIES
# Common logging settings for Sakuli.
@@ -434,3 +444,10 @@ java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.limit=102400
java.util.logging.FileHandler.count=10
java.util.logging.FileHandler.pattern=%t/sahi%g.log
+
+#TODO ${project.version}
+sakuli.version=1.0.2
+
+###URLs for documentation links
+sahi.doc.url=http://sahipro.com/docs/sahi-apis/action-apis.html
+sakuli.doc.base.url=http://consol.github.io/sakuli/v${sakuli.version}/index.html#
diff --git a/src/core/pom.xml b/src/core/pom.xml
index 1b3dfe4c..ecadd604 100644
--- a/src/core/pom.xml
+++ b/src/core/pom.xml
@@ -448,6 +448,11 @@
org.jtwig
jtwig-spaceless-extension
+
+
+ com.google.code.gson
+ gson
+
diff --git a/src/core/src/main/java/org/sakuli/aop/RhinoAspect.java b/src/core/src/main/java/org/sakuli/aop/RhinoAspect.java
index 44c2f77f..c15ea9c6 100644
--- a/src/core/src/main/java/org/sakuli/aop/RhinoAspect.java
+++ b/src/core/src/main/java/org/sakuli/aop/RhinoAspect.java
@@ -27,12 +27,19 @@
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.sakuli.actions.logging.LogToResult;
+import org.sakuli.datamodel.TestAction;
+import org.sakuli.datamodel.TestCaseStep;
import org.sakuli.datamodel.actions.LogResult;
-import org.sakuli.loader.*;
+import org.sakuli.loader.BaseActionLoader;
+import org.sakuli.loader.BaseActionLoaderImpl;
+import org.sakuli.loader.BeanLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
+import java.util.ArrayList;
+import java.util.List;
+
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.removeEnd;
import static org.apache.commons.lang3.StringUtils.removeStart;
@@ -118,6 +125,8 @@ protected void addActionLog(JoinPoint joinPoint, LogToResult logToResult) {
if (logToResult != null) {
StringBuilder message = createLoggingString(joinPoint, logToResult);
+ addActionsToCurrentStep(extractTestAction(joinPoint, logToResult));
+
//log the action to log file and print
switch (logToResult.level()) {
case ERROR:
@@ -147,6 +156,34 @@ protected void addActionLog(JoinPoint joinPoint, LogToResult logToResult) {
}
}
+ //TODO evtl. move the test action creation to a new service
+ private List cachedTestActions = new ArrayList<>();
+
+ protected void addActionsToCurrentStep(TestAction currentTestAction) {
+ if (currentTestAction != null) {
+ cachedTestActions.add(currentTestAction);
+ }
+ TestCaseStep currentTestCaseStep = BeanLoader.loadBaseActionLoader().getCurrentTestCaseStep();
+ if (currentTestCaseStep != null) {
+ currentTestCaseStep.addActions(cachedTestActions);
+ //reset the list of actions, since those actions have already been added to the current test step.
+ cachedTestActions = new ArrayList<>();
+ }
+ }
+
+ protected TestAction extractTestAction(JoinPoint joinPoint, LogToResult logToResult) {
+ if (logToResult.logArgsOnly()) {
+ return null;
+ }
+ return TestAction.createSakuliTestAction(
+ joinPoint.getSignature().getDeclaringType().getSimpleName(),
+ joinPoint.getSignature().getName(),
+ joinPoint.getArgs(),
+ logToResult.message(),
+ BeanLoader.loadBaseActionLoader().getSakuliProperties().getSakuliDocBaseUrl()
+ );
+ }
+
/**
* @return based on the different arguments of the {@link LogToResult} annotation an different output {@link String}
*/
@@ -213,6 +250,7 @@ public void doHandleRhinoException(JoinPoint joinPoint) {
else if (logResult.getDebugInfo() == null
|| !logResult.getDebugInfo().startsWith("org.sakuli.actions.")) {
logger.info(logResult.getMessage());
+ addActionsToCurrentStep(TestAction.createSahiTestAction(logResult.getMessage(), BeanLoader.loadBaseActionLoader().getSakuliProperties().getSahiDocUrl()));
}
}
diff --git a/src/core/src/main/java/org/sakuli/datamodel/TestAction.java b/src/core/src/main/java/org/sakuli/datamodel/TestAction.java
new file mode 100644
index 00000000..5ed1c323
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/datamodel/TestAction.java
@@ -0,0 +1,72 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.datamodel;
+
+/**
+ * Created by georgi on 21/09/17.
+ */
+public class TestAction {
+ private String object;
+ private String method;
+ private Object[] args;
+ private String message;
+ private String documentationURL;
+
+ private TestAction(String object, String method, Object[] args, String message, String documentationURL) {
+ this.object = object;
+ this.method = method;
+ this.args = args;
+ this.message = message;
+ this.documentationURL = documentationURL;
+ }
+
+ public static TestAction createSakuliTestAction(String object, String method, Object[] args, String message, String documentationURL) {
+ return new TestAction(object, method, args, message,
+ createDocumentationURL(documentationURL, object, method));
+ }
+
+ public static TestAction createSahiTestAction(String method, String documentationURL) {
+ return new TestAction(method, method, null, null, documentationURL);
+ }
+
+ private static String createDocumentationURL(String baseUrl, String clazz, String method) {
+ return baseUrl + clazz + "." + method;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public Object[] getArgs() {
+ return args;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public String getDocumentationURL() {
+ return documentationURL;
+ }
+
+}
diff --git a/src/core/src/main/java/org/sakuli/datamodel/TestCaseStep.java b/src/core/src/main/java/org/sakuli/datamodel/TestCaseStep.java
index bceb410f..7b7e773e 100644
--- a/src/core/src/main/java/org/sakuli/datamodel/TestCaseStep.java
+++ b/src/core/src/main/java/org/sakuli/datamodel/TestCaseStep.java
@@ -19,10 +19,12 @@
package org.sakuli.datamodel;
import org.apache.commons.lang.StringUtils;
-import org.sakuli.datamodel.properties.SakuliProperties;
import org.sakuli.datamodel.state.TestCaseStepState;
import org.sakuli.exceptions.SakuliException;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* test case step based Exceptions and critical times will be currently not supported in {@link
* org.sakuli.actions.TestCaseAction}.
@@ -31,6 +33,8 @@
*/
public class TestCaseStep extends AbstractTestDataEntity {
+ private List testActions = new ArrayList<>();
+
/**
* {@inheritDoc}
*/
@@ -85,4 +89,9 @@ public void setName(String name) {
public void setId(String id) {
this.setName(id);
}
+
+ public void addActions(List testActions) {
+ this.testActions.addAll(testActions);
+ }
+
}
diff --git a/src/core/src/main/java/org/sakuli/datamodel/properties/ForwarderProperties.java b/src/core/src/main/java/org/sakuli/datamodel/properties/ForwarderProperties.java
index 311e406a..e4ff12ee 100644
--- a/src/core/src/main/java/org/sakuli/datamodel/properties/ForwarderProperties.java
+++ b/src/core/src/main/java/org/sakuli/datamodel/properties/ForwarderProperties.java
@@ -31,10 +31,12 @@ public class ForwarderProperties extends AbstractProperties {
public static final String GEARMAN_ENABLED = "sakuli.forwarder.gearman.enabled";
public static final String ICINGA_ENABLED = "sakuli.forwarder.icinga2.enabled";
public static final String CHECK_MK_ENABLED = "sakuli.forwarder.check_mk.enabled";
+ public static final String JSON_ENABLED = "sakuli.forwarder.json.enabled";
protected static final boolean DATABASE_ENABLED_DEFAULT = false;
protected static final boolean GEARMAN_ENABLED_DEFAULT = false;
protected static final boolean ICINGA_ENABLED_DEFAULT = false;
protected static final boolean CHECK_MK_ENABLED_DEFAULT = false;
+ protected static final boolean JSON_ENABLED_DEFAULT = true;
@Value("${" + DATABASE_ENABLED + ":" + DATABASE_ENABLED_DEFAULT + "}")
private boolean databaseEnabled;
@@ -45,6 +47,9 @@ public class ForwarderProperties extends AbstractProperties {
@Value("${" + CHECK_MK_ENABLED + ":" + CHECK_MK_ENABLED_DEFAULT + "}")
private boolean checkMKEnabled;
+ @Value("${" + JSON_ENABLED + ":" + JSON_ENABLED_DEFAULT + "}")
+ private boolean jsonEnabled;
+
public boolean isDatabaseEnabled() {
return databaseEnabled;
}
@@ -73,4 +78,12 @@ public boolean isCheckMKEnabled() {
return checkMKEnabled;
}
+ public void setJsonEnabled(boolean jsonEnabled) {
+ this.jsonEnabled = jsonEnabled;
+ }
+
+ public boolean isJsonEnabled() {
+ return jsonEnabled;
+ }
+
}
diff --git a/src/core/src/main/java/org/sakuli/datamodel/properties/SakuliProperties.java b/src/core/src/main/java/org/sakuli/datamodel/properties/SakuliProperties.java
index a1bd7984..4e44d569 100644
--- a/src/core/src/main/java/org/sakuli/datamodel/properties/SakuliProperties.java
+++ b/src/core/src/main/java/org/sakuli/datamodel/properties/SakuliProperties.java
@@ -48,10 +48,12 @@ public class SakuliProperties extends AbstractProperties {
public static final String LOG_LEVEL_SPRING = "log.level.spring";
public static final String LOG_LEVEL_ROOT = "log.level.root";
public static final String FORWARDER_TEMPLATE_FOLDER = "sakuli.forwarder.template.folder";
+ public static final String SAHI_DOC_URL = "sahi.doc.url";
+ public static final String SAKULI_DOC_BASE_URL = "sakuli.doc.base.url";
public static final String CONFIG_FOLDER_APPEDER = File.separator + "config";
public static final String LIBS_FOLDER_APPEDER = File.separator + "libs";
public static final String JS_LIB_FOLDER_APPEDER = LIBS_FOLDER_APPEDER + File.separator + "js";
- // have to be the common/libs older, so that {@link TextRecognizer} can add "tessdata" to the path!
+ // have to be the common/libs folder, so that {@link TextRecognizer} can add "tessdata" to the path!
public static final String TESSDATA_LIB_FOLDER_APPEDER = LIBS_FOLDER_APPEDER;
private static final boolean SUPPRESS_RESUMED_EXCEPTIONS_DEFAULT = false;
private static final boolean JAVASCRIPT_ENGINE_DEFAULT = true;
@@ -84,6 +86,11 @@ public class SakuliProperties extends AbstractProperties {
private String logLevelRoot;
@Value("${" + FORWARDER_TEMPLATE_FOLDER + ":}")
private String forwarderTemplateFolder;
+ @Value("${" + SAHI_DOC_URL + "}")
+ private String sahiDocumentationUrl;
+ @Value("${" + SAKULI_DOC_BASE_URL + "}")
+ private String sakuliDocumentationBaseUrl;
+
private Path configFolder;
private Path jsLibFolder;
private Path tessDataLibFolder;
@@ -241,4 +248,12 @@ public String getForwarderTemplateFolder() {
return forwarderTemplateFolder;
}
+ public String getSahiDocUrl() {
+ return sahiDocumentationUrl;
+ }
+
+ public String getSakuliDocBaseUrl() {
+ return sakuliDocumentationBaseUrl;
+ }
+
}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/AbstractOutputBuilder.java b/src/core/src/main/java/org/sakuli/services/forwarder/AbstractOutputBuilder.java
index 9d19b62d..b57aec55 100644
--- a/src/core/src/main/java/org/sakuli/services/forwarder/AbstractOutputBuilder.java
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/AbstractOutputBuilder.java
@@ -44,7 +44,7 @@
* @author tschneck
* Date: 2/24/16
*/
-public abstract class AbstractOutputBuilder {
+public abstract class AbstractOutputBuilder {
public final static SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.YY HH:mm:ss");
protected Logger LOGGER = LoggerFactory.getLogger(getClass());
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/ScreenshotDivConverter.java b/src/core/src/main/java/org/sakuli/services/forwarder/ScreenshotDivConverter.java
index 84b5f21b..969d3564 100644
--- a/src/core/src/main/java/org/sakuli/services/forwarder/ScreenshotDivConverter.java
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/ScreenshotDivConverter.java
@@ -26,6 +26,7 @@
import org.sakuli.services.forwarder.gearman.ProfileGearman;
import org.sakuli.services.forwarder.gearman.model.ScreenshotDiv;
import org.sakuli.services.forwarder.icinga2.ProfileIcinga2;
+import org.sakuli.services.forwarder.json.ProfileJson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Encoder;
@@ -41,6 +42,7 @@
@ProfileGearman
@ProfileIcinga2
@ProfileCheckMK
+@ProfileJson
@Component
public class ScreenshotDivConverter {
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/GsonExclusionStrategy.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/GsonExclusionStrategy.java
new file mode 100644
index 00000000..4641357a
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/GsonExclusionStrategy.java
@@ -0,0 +1,43 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+import org.sikuli.script.Region;
+import org.slf4j.Logger;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+public class GsonExclusionStrategy implements ExclusionStrategy {
+
+ @Override
+ public boolean shouldSkipClass(Class> c) {
+ return Logger.class.isAssignableFrom(c)
+ || Region.class.isAssignableFrom(c)
+ ;
+ }
+
+ @Override
+ public boolean shouldSkipField(FieldAttributes f) {
+ return false;
+ }
+
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/GsonOutputBuilder.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/GsonOutputBuilder.java
new file mode 100644
index 00000000..9098ba8c
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/GsonOutputBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.joda.time.DateTime;
+import org.sakuli.exceptions.SakuliForwarderException;
+import org.sakuli.services.forwarder.AbstractOutputBuilder;
+import org.sakuli.services.forwarder.json.serializer.DateTimeSerializer;
+import org.sakuli.services.forwarder.json.serializer.PathSerializer;
+import org.sakuli.services.forwarder.json.serializer.ThrowableSerializer;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+@ProfileJson
+@Component
+public class GsonOutputBuilder extends AbstractOutputBuilder {
+
+ /**
+ * Converts the current test suite object to a json string.
+ *
+ * @return
+ */
+ public String createOutput() throws SakuliForwarderException {
+ try {
+ Gson gsonBuilder = new GsonBuilder()
+ .setExclusionStrategies(new GsonExclusionStrategy())
+ .registerTypeAdapter(DateTime.class, new DateTimeSerializer())
+ .registerTypeAdapter(Path.class, new PathSerializer())
+ .registerTypeHierarchyAdapter(Throwable.class, new ThrowableSerializer())
+ .serializeNulls()
+ .create();
+ return gsonBuilder.toJson(testSuite);
+ } catch (Throwable thr) {
+ throw new SakuliForwarderException(thr, "Exception during serializing testSuite into JSON!");
+ }
+ }
+
+ @Override
+ protected int getSummaryMaxLength() {
+ // operation is not used for the gson output builder
+ return 0;
+ }
+
+ @Override
+ protected String getOutputScreenshotDivWidth() {
+ // operation is not used for the gson output builder
+ return null;
+ }
+
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/JsonProperties.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/JsonProperties.java
new file mode 100644
index 00000000..313db818
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/JsonProperties.java
@@ -0,0 +1,39 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+@ProfileJson
+@Component
+public class JsonProperties {
+ private static final String OUTPUT_DIR = "sakuli.forwarder.json.dir";
+
+ @Value("${" + OUTPUT_DIR + "}")
+ private String outputJsonDir;
+
+ public String getOutputJsonDir() {
+ return outputJsonDir;
+ }
+
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/JsonResultServiceImpl.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/JsonResultServiceImpl.java
new file mode 100644
index 00000000..1eb83886
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/JsonResultServiceImpl.java
@@ -0,0 +1,100 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json;
+
+import org.sakuli.exceptions.SakuliForwarderException;
+import org.sakuli.services.common.AbstractResultService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author Georgi Todorov
+ */
+@ProfileJson
+@Component
+public class JsonResultServiceImpl extends AbstractResultService {
+
+ private static final Logger logger = LoggerFactory.getLogger(JsonResultServiceImpl.class);
+
+ @Autowired
+ private GsonOutputBuilder outputBuilder;
+
+ @Autowired
+ private JsonProperties jsonProperties;
+
+ @Override
+ public int getServicePriority() {
+ return 10;
+ }
+
+ @Override
+ public void saveAllResults() {
+ try {
+ logger.info("======= WRITE TEST OUTPUT AS JSON FILE ======");
+ String output = outputBuilder.createOutput();
+ logger.debug(String.format("JSON Output:\n%s", output));
+ writeToFile(createJsonFilePath(), output);
+ logger.info("======= FINISHED: WRITE TEST OUTPUT AS JSON FILE ======");
+ } catch (SakuliForwarderException e) {
+ exceptionHandler.handleException(e, false);
+ }
+ }
+
+ protected Path createJsonFilePath() throws SakuliForwarderException {
+ String outputDirAsString = jsonProperties.getOutputJsonDir();
+ Path outputDir = Paths.get(outputDirAsString);
+ if (!Files.exists(outputDir)) {
+ try {
+ Files.createDirectories(outputDir);
+ } catch (IOException e) {
+ throw new SakuliForwarderException(e,
+ String.format("Unexpected error during creating the json output directory '%s'", outputDirAsString));
+ }
+ }
+ String fileName = new StringBuilder()
+ .append(testSuite.getId())
+ .append("_")
+ .append(new SimpleDateFormat("yyyy.MM.dd-HH:mm:ss.SSS").format(new Date()))
+ .append(".json")
+ .toString();
+ return Paths.get(outputDirAsString + File.separator + fileName);
+ }
+
+ private void writeToFile(Path file, String output) throws SakuliForwarderException {
+ try {
+ logger.info(String.format("Write file to '%s'", file));
+ Files.write(file, output.getBytes(), StandardOpenOption.CREATE);
+ } catch (IOException e) {
+ throw new SakuliForwarderException(e,
+ String.format("Unexpected error by writing the json output to the following file '%s'", file));
+ }
+ }
+
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/ProfileJson.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/ProfileJson.java
new file mode 100644
index 00000000..77c5b15f
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/ProfileJson.java
@@ -0,0 +1,29 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json;
+
+import org.sakuli.utils.SpringProfilesInitializer;
+import org.springframework.context.annotation.Profile;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+@Profile(SpringProfilesInitializer.JSON)
+public @interface ProfileJson {
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/DateTimeSerializer.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/DateTimeSerializer.java
new file mode 100644
index 00000000..b37cd219
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/DateTimeSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json.serializer;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.lang.reflect.Type;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+public class DateTimeSerializer implements JsonSerializer {
+ final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);
+
+ @Override
+ public JsonElement serialize(final DateTime src, final Type typeOfSrc, final JsonSerializationContext context) {
+ return new JsonPrimitive(src == null ? StringUtils.EMPTY : DATE_TIME_FORMATTER.print(src));
+ }
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/PathSerializer.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/PathSerializer.java
new file mode 100644
index 00000000..aa8e81e4
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/PathSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json.serializer;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import org.apache.commons.lang.StringUtils;
+
+import java.lang.reflect.Type;
+import java.nio.file.Path;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+public final class PathSerializer implements JsonSerializer {
+ @Override
+ public JsonElement serialize(final Path src, final Type typeOfSrc, final JsonSerializationContext context) {
+ return new JsonPrimitive(src == null ? StringUtils.EMPTY : src.toString());
+ }
+}
diff --git a/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/ThrowableSerializer.java b/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/ThrowableSerializer.java
new file mode 100644
index 00000000..821d4c0c
--- /dev/null
+++ b/src/core/src/main/java/org/sakuli/services/forwarder/json/serializer/ThrowableSerializer.java
@@ -0,0 +1,52 @@
+/*
+ * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
+ *
+ * Copyright 2013 - 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sakuli.services.forwarder.json.serializer;
+
+import com.google.gson.*;
+import org.apache.commons.lang.StringUtils;
+import org.sakuli.exceptions.SakuliExceptionWithScreenshot;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Type;
+
+/**
+ * Created by georgi on 27/09/17.
+ */
+public class ThrowableSerializer implements JsonSerializer {
+
+ @Override
+ public JsonElement serialize(final Throwable src, final Type typeOfSrc, final JsonSerializationContext context) {
+
+ if (src == null) {
+ return new JsonPrimitive(StringUtils.EMPTY);
+ }
+ JsonObject obj = new JsonObject();
+ StringWriter sw = new StringWriter();
+ src.printStackTrace(new PrintWriter(sw));
+ obj.addProperty("stackTrace", sw.toString());
+ obj.addProperty("detailMessage", src.getMessage());
+ if (SakuliExceptionWithScreenshot.class.isAssignableFrom(src.getClass())) {
+ obj.addProperty("screenshot", ((SakuliExceptionWithScreenshot)src).getScreenshot().toString());
+ }
+
+ return obj;
+ }
+
+}
diff --git a/src/core/src/main/java/org/sakuli/utils/SpringProfilesInitializer.java b/src/core/src/main/java/org/sakuli/utils/SpringProfilesInitializer.java
index a09168e5..5ed163c6 100644
--- a/src/core/src/main/java/org/sakuli/utils/SpringProfilesInitializer.java
+++ b/src/core/src/main/java/org/sakuli/utils/SpringProfilesInitializer.java
@@ -45,6 +45,7 @@ public class SpringProfilesInitializer implements InitializingBean, ApplicationC
public static final String GEARMAN = "GEARMAN";
public static final String INCINGA2 = "ICINGA2";
public static final String CHECK_MK = "CHECK_MK";
+ public static final String JSON = "JSON";
public static final String CIPHER_INTERFACE = "CIPHER_INTERFACE";
public static final String CIPHER_ENV = "CIPHER_ENV";
@@ -85,6 +86,9 @@ protected String[] getConfiguredProfiles() {
if (forwarderProperties.isCheckMKEnabled()) {
profileNames.add(CHECK_MK);
}
+ if (forwarderProperties.isJsonEnabled()) {
+ profileNames.add(JSON);
+ }
//cipher profiles
if (cipherProperties.isEncryptionModeEnv()) {
diff --git a/test2.json b/test2.json
new file mode 100644
index 00000000..103062d8
--- /dev/null
+++ b/test2.json
@@ -0,0 +1,264 @@
+{
+ "browserName": "firefox",
+ "browserInfo": "Mozilla/5.0 (Macintosh, Intel Mac OS X 10.10, rv:49.0) Gecko/20100101 Firefox/49.0",
+ "host": "MacBookProConsol.fritz.box",
+ "testSuiteFolder": "/Users/georgi/Projects/testautomatisierung/sakuli/example_test_suites/example_macos",
+ "testSuiteFile": "/Users/georgi/Projects/testautomatisierung/sakuli/example_test_suites/example_macos/testsuite.suite",
+ "dbJobPrimaryKey": -1,
+ "testCases": {
+ "case1": {
+ "startUrl": "http://sahi.example.com/_s_/dyn/Driver_initialized",
+ "lastURL": "http://sahi.example.com/_s_/dyn/Driver_initialized",
+ "steps": [
+ {
+ "testActions": [
+ {
+ "object": "TestCaseAction",
+ "method": "getIdFromPath",
+ "args": [
+ "/Users/georgi/Projects/testautomatisierung/sakuli/example_test_suites/example_macos/case1/undefined"
+ ],
+ "message": "convert the path of the test case file to a valid test case ID",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#TestCaseAction.getIdFromPath"
+ },
+ {
+ "object": "TestCaseAction",
+ "method": "init",
+ "args": [
+ "case1",
+ 60,
+ 70,
+ [
+ "/Users/georgi/Projects/testautomatisierung/sakuli/example_test_suites/example_macos/case1"
+ ]
+ ],
+ "message": "init a new test case",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#TestCaseAction.init"
+ },
+ {
+ "object": "_highlight(_link(\"SSL Manager\"));",
+ "method": "_highlight(_link(\"SSL Manager\"));",
+ "args": null,
+ "message": null,
+ "documentationURL": "http://sahipro.com/docs/sahi-apis/action-apis.html"
+ },
+ {
+ "object": "_highlight(_link(\"Logs\"));",
+ "method": "_highlight(_link(\"Logs\"));",
+ "args": null,
+ "message": null,
+ "documentationURL": "http://sahipro.com/docs/sahi-apis/action-apis.html"
+ },
+ {
+ "object": "_highlight(_link(\"Online Documentation\"));",
+ "method": "_highlight(_link(\"Online Documentation\"));",
+ "args": null,
+ "message": null,
+ "documentationURL": "http://sahipro.com/docs/sahi-apis/action-apis.html"
+ },
+ {
+ "object": "_highlight(_link(\"Test Pages\"));",
+ "method": "_highlight(_link(\"Test Pages\"));",
+ "args": null,
+ "message": null,
+ "documentationURL": "http://sahipro.com/docs/sahi-apis/action-apis.html"
+ },
+ {
+ "object": "_highlight(_link(\"Sample Application\"));",
+ "method": "_highlight(_link(\"Sample Application\"));",
+ "args": null,
+ "message": null,
+ "documentationURL": "http://sahipro.com/docs/sahi-apis/action-apis.html"
+ },
+ {
+ "object": "TestCaseAction",
+ "method": "addTestCaseStep",
+ "args": [
+ "Test Sahi landing page",
+ "1506528264044",
+ "1506528265467",
+ 30
+ ],
+ "message": "add a step to the current test case",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#TestCaseAction.addTestCaseStep"
+ }
+ ],
+ "startDate": "Sep 27, 2017 6:04:24 PM",
+ "stopDate": "Sep 27, 2017 6:04:25 PM",
+ "exception": null,
+ "state": "OK",
+ "name": "Test_Sahi_landing_page",
+ "dbPrimaryKey": -1,
+ "warningTime": 30,
+ "criticalTime": -1,
+ "id": "Test_Sahi_landing_page",
+ "creationDate": "2017-09-27T16:04:19.472Z"
+ },
+ {
+ "testActions": [
+ {
+ "object": "Application",
+ "method": "open",
+ "args": [],
+ "message": "open application",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Application.open"
+ },
+ {
+ "object": "Environment",
+ "method": "sleep",
+ "args": [
+ 4.0
+ ],
+ "message": "sleep and do nothing for x seconds",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Environment.sleep"
+ },
+ {
+ "object": "Environment",
+ "method": "type",
+ "args": [
+ "525"
+ ],
+ "message": "type over system keyboard",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Environment.type"
+ },
+ {
+ "object": "Environment",
+ "method": "sleep",
+ "args": [
+ 2.0
+ ],
+ "message": "sleep and do nothing for x seconds",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Environment.sleep"
+ },
+ {
+ "object": "Region",
+ "method": "find",
+ "args": [
+ "plus.png"
+ ],
+ "message": "find the image in this region",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Region.find"
+ },
+ {
+ "object": "Region",
+ "method": "click",
+ "args": [],
+ "message": "",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Region.click"
+ },
+ {
+ "object": "Region",
+ "method": "type",
+ "args": [
+ "100"
+ ],
+ "message": "type over system keyboard",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Region.type"
+ },
+ {
+ "object": "Region",
+ "method": "find",
+ "args": [
+ "result.png"
+ ],
+ "message": "find the image in this region",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Region.find"
+ },
+ {
+ "object": "Region",
+ "method": "click",
+ "args": [],
+ "message": "",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Region.click"
+ },
+ {
+ "object": "TestCaseAction",
+ "method": "addTestCaseStep",
+ "args": [
+ "Calculation",
+ "1506528265467",
+ "1506528276537",
+ 30
+ ],
+ "message": "add a step to the current test case",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#TestCaseAction.addTestCaseStep"
+ }
+ ],
+ "startDate": "Sep 27, 2017 6:04:25 PM",
+ "stopDate": "Sep 27, 2017 6:04:36 PM",
+ "exception": null,
+ "state": "OK",
+ "name": "Calculation",
+ "dbPrimaryKey": -1,
+ "warningTime": 30,
+ "criticalTime": -1,
+ "id": "Calculation",
+ "creationDate": "2017-09-27T16:04:19.473Z"
+ },
+ {
+ "testActions": [
+ {
+ "object": "Application",
+ "method": "open",
+ "args": [],
+ "message": "open application",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Application.open"
+ },
+ {
+ "object": "Environment",
+ "method": "paste",
+ "args": [
+ "Initial test passed. Sakuli, Sahi and Sikuli seem to work fine. Exiting..."
+ ],
+ "message": "",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#Environment.paste"
+ },
+ {
+ "object": "TestCaseAction",
+ "method": "addTestCaseStep",
+ "args": [
+ "Editor",
+ "1506528276537",
+ "1506528278204",
+ 30
+ ],
+ "message": "add a step to the current test case",
+ "documentationURL": "http://consol.github.io/sakuli/v1.0.2/index.html#TestCaseAction.addTestCaseStep"
+ }
+ ],
+ "startDate": "Sep 27, 2017 6:04:36 PM",
+ "stopDate": "Sep 27, 2017 6:04:38 PM",
+ "exception": null,
+ "state": "OK",
+ "name": "Editor",
+ "dbPrimaryKey": -1,
+ "warningTime": 30,
+ "criticalTime": -1,
+ "id": "Editor",
+ "creationDate": "2017-09-27T16:04:19.474Z"
+ }
+ ],
+ "tcFile": "/Users/georgi/Projects/testautomatisierung/sakuli/example_test_suites/example_macos/case1/sakuli_demo.js",
+ "startDate": "Sep 27, 2017 6:04:24 PM",
+ "stopDate": "Sep 27, 2017 6:04:42 PM",
+ "exception": null,
+ "state": "OK",
+ "name": "case1",
+ "dbPrimaryKey": -1,
+ "warningTime": 60,
+ "criticalTime": 70,
+ "id": "case1",
+ "creationDate": "2017-09-27T16:04:19.469Z"
+ }
+ },
+ "startDate": "Sep 27, 2017 6:04:19 PM",
+ "stopDate": "Sep 27, 2017 6:04:46 PM",
+ "exception": null,
+ "state": "OK",
+ "name": "example test suite for Sakuli",
+ "dbPrimaryKey": -1,
+ "warningTime": 120,
+ "criticalTime": 140,
+ "id": "example_macos",
+ "creationDate": "2017-09-27T16:04:19.279Z"
+}
\ No newline at end of file