/* * Sakuli - Testing and Monitoring-Tool for Websites and common UIs. * * Copyright 2013 - 2015 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.actions; import org.apache.commons.lang.StringUtils; import org.sakuli.actions.logging.LogToResult; import org.sakuli.datamodel.TestCase; import org.sakuli.datamodel.TestCaseStep; import org.sakuli.datamodel.TestSuite; import org.sakuli.datamodel.actions.ImageLib; import org.sakuli.datamodel.actions.LogLevel; import org.sakuli.datamodel.helper.TestCaseHelper; import org.sakuli.datamodel.helper.TestCaseStepHelper; import org.sakuli.datamodel.helper.TestDataEntityHelper; import org.sakuli.exceptions.SakuliActionException; import org.sakuli.exceptions.SakuliException; import org.sakuli.exceptions.SakuliExceptionHandler; import org.sakuli.exceptions.SakuliValidationException; import org.sakuli.loader.BaseActionLoader; import org.sakuli.loader.BaseActionLoaderImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Date; /** * @author tschneck Date: 19.06.13 */ @Component public class TestCaseAction { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * Represents the current running TestCase. The object will be set at {@link #init(String, int, int, String...)} and * releases at {@link #saveResult(String, String, String, String, String)} */ @Autowired @Qualifier(BaseActionLoaderImpl.QUALIFIER) private BaseActionLoader loader; /**************** * Init functions for the java script engine. *********************/ /** * Set the warning and critical Time to the specific test case. * * @param testCaseID current ID of the test case * @param warningTime warning threshold in seconds. If the threshold is set to 0, * the execution time will never exceed, so the state will be always OK! * @param criticalTime critical threshold in seconds. If the threshold is set to 0, * the execution time will never exceed, so the state will be always OK! * @param imagePaths multiple paths to images */ @LogToResult(message = "init a new test case") public void init(String testCaseID, int warningTime, int criticalTime, String... imagePaths) { loader.init(testCaseID, imagePaths); initWarningAndCritical(warningTime, criticalTime); } /** * Set the warning and critical Time to the specific test case. * * @param testCaseID current ID of the test case * @param warningTime warning threshold in seconds. If the threshold is set to 0, * the execution time will never exceed, so the state will be always OK! * @param criticalTime critical threshold in seconds. If the threshold is set to 0, * the execution time will never exceed, so the state will be always OK! * @param imagePaths multiple paths to images */ @LogToResult(message = "init a new test case") public void initWithPaths(String testCaseID, int warningTime, int criticalTime, Path... imagePaths) { loader.init(testCaseID, imagePaths); initWarningAndCritical(warningTime, criticalTime); } /** * Adds the additional paths to the current {@link ImageLib} object. * If a relative path is assigned, the current testcase folder will be used as current directory. * * @param imagePaths one or more paths as {@link String} elements * @throws SakuliException if an IO error occurs */ public void addImagePathsAsString(String... imagePaths) throws SakuliException { for (String path : imagePaths) { //check if absolute path if (!path.matches("(\\/\\S*|\\w:\\\\\\S*)")) { addImagePaths(loader.getCurrentTestCase().getTcFile().getParent().resolve(path)); } else { addImagePaths(Paths.get(path)); } } } /** * Adds the additional paths to the current {@link ImageLib} object. * * @param imagePaths one or more {@link Path} elements * @throws SakuliException if an IO error occurs */ @LogToResult public void addImagePaths(Path... imagePaths) throws SakuliException { loader.addImagePaths(imagePaths); } private void initWarningAndCritical(int warningTime, int criticalTime) { TestCase currentTestCase = loader.getCurrentTestCase(); String errormsg = TestDataEntityHelper.checkWarningAndCriticalTime(warningTime, criticalTime, currentTestCase.toStringShort()); if (errormsg != null) { handleException(errormsg); } else { //if everything is ok set the times currentTestCase.setWarningTime(warningTime); currentTestCase.setCriticalTime(criticalTime); } } /**************** * TEST CASE HANDLING *********************/ /** * save the Result of a test Case * * @param testCaseId id of the corresponding test case * @param startTime start time in milliseconds * @param stopTime end time in milliseconds * @param lastURL URL to the last visited page during this test case * @param browserInfo detail information about the used browser * @throws SakuliException */ @LogToResult(message = "save the result of the current test case") public void saveResult(String testCaseId, String startTime, String stopTime, String lastURL, String browserInfo) throws SakuliException { if (!loader.getCurrentTestCase().getId().equals(testCaseId)) { handleException("testcaseID '" + testCaseId + "' to save the test case Result ist is not valid!"); } TestSuite testSuite = loader.getTestSuite(); testSuite.setBrowserInfo(browserInfo); //set TestCase vars TestCase tc = loader.getCurrentTestCase(); tc.setLastURL(lastURL); try { tc.setStartDate(new Date(Long.parseLong(startTime))); tc.setStopDate(new Date(Long.parseLong(stopTime))); logger.debug("test case duration = " + tc.getDuration()); tc.refreshState(); } catch (NumberFormatException | NullPointerException e) { handleException("Duration could not be calculated! " + "Check if the warning and critical threshold is set correctly in your test case! " + "=> START date: " + startTime + "\tSTOP date: " + stopTime + "\n" + e.getMessage()); } //release current test case -> indicates that this case is finished loader.setCurrentTestCase(null); } /** * Wrapper for {@link #addTestCaseStep(String, String, String, int)} with warningTime '0'. */ public void addTestCaseStep(String stepName, String startTime, String stopTime) throws SakuliException { addTestCaseStep(stepName, startTime, stopTime, 0); } /** * Save a new step to a existing test case. Must be called before {@link #saveResult(String, String, String, String, * String)} * * @param stepName name of this step * @param startTime start time in milliseconds * @param stopTime end time in milliseconds * @param warningTime warning threshold in seconds. If the threshold is set to 0, the execution time will never exceed, so the state will be always OK! * @throws SakuliException */ @LogToResult(message = "add a step to the current test case") public void addTestCaseStep(String stepName, String startTime, String stopTime, int warningTime) throws SakuliException { if (stepName == null || stepName.isEmpty() || stepName.equals("undefined")) { handleException("Please set a Name - all values of the test case step need to be set!"); } String errormsg = TestCaseStepHelper.checkWarningTime(warningTime, stepName); if (errormsg != null) { handleException(errormsg); } TestCaseStep step = findStep(stepName); try { step.setStartDate(new Date(Long.parseLong(startTime))); step.setStopDate(new Date(Long.parseLong(stopTime))); step.setWarningTime(warningTime); } catch (NullPointerException | NumberFormatException e) { loader.getExceptionHandler().handleException(e); } logger.debug("duration of the step \"" + stepName + "\": " + step.getDuration() + " sec."); step.refreshState(); logger.debug("result of step \"" + stepName + "\": " + step.getState()); loader.getCurrentTestCase().addStep(step); logger.debug("test case step \"" + step.getName() + "\" saved to test case \"" + loader.getCurrentTestCase().getId() + "\""); } protected TestCaseStep findStep(String stepName) { TestCaseStep newStep = new TestCaseStep(); newStep.setId(stepName); for (TestCaseStep step : loader.getCurrentTestCase().getSteps()) { if (StringUtils.equals(step.getId(), newStep.getId())) { return step; } } return newStep; } /** * Creates a new test case based exception with an optional screenshot at the calling time. * Will be called from sakuli.js or in side of 'org.sakuli.javaDSL.AbstractSakuliTest'. * * @param message error message * @param screenshot enable / disable screenshot functionality */ @LogToResult(level = LogLevel.ERROR) public void throwException(String message, boolean screenshot) { loader.getExceptionHandler().handleException(screenshot ? new SakuliActionException(message) : new SakuliValidationException(message)); } /** * calls the method {@link SakuliExceptionHandler#handleException(Throwable)} * * @param e the original exception */ public void handleException(Throwable e) { loader.getExceptionHandler().handleException(e, false); } /** * @param exceptionMessage String message */ public void handleException(String exceptionMessage) { loader.getExceptionHandler().handleException(exceptionMessage, false); } @Override public String toString() { if (loader != null && loader.getCurrentTestCase() != null) { return "test case [" + loader.getCurrentTestCase().getActionValueString() + "]"; } return "test case not initialized"; } /**************** * TEST CASE INFO FUNCTIONS *********************/ /** * @param pathToTestCaseFile path to the test case file "_tc.js" * @return returns test the currentTestCase Name */ @LogToResult(message = "convert the path of the test case file to a valid test case ID") public String getIdFromPath(String pathToTestCaseFile) { logger.info("Return a test-case-id for \"" + pathToTestCaseFile + "\""); String id = TestCaseHelper.convertTestCaseFileToID(pathToTestCaseFile); //check id if (loader.getTestSuite().checkTestCaseID(id)) { logger.info("test-case-id = " + id); return id; } else { handleException("cannot identify testcase for pathToTestCaseFile=" + pathToTestCaseFile); return null; } } /** * @return String value of the last URL */ @LogToResult(message = "return 'lastURL'") public String getLastURL() { return loader.getCurrentTestCase().getLastURL(); } /** * Set a new URL to the current TestCase as last visited URL. * * @param lastURL String value of the last URL */ @LogToResult(message = "set 'lastURL' to new value") public void setLastURL(String lastURL) { loader.getCurrentTestCase().setLastURL(lastURL); } /** * @return the folder path of the current testcase as {@link String}. */ @LogToResult public String getTestCaseFolderPath() { try { return loader.getCurrentTestCase().getTcFile().getParent().toAbsolutePath().toString(); } catch (Exception e) { handleException(new SakuliException(e, String.format("cannot resolve the folder path of the current testcase '%s'", loader.getCurrentTestCase()))); return null; } } /** * @return the folder path of the current testsuite as {@link String}. */ @LogToResult public String getTestSuiteFolderPath() { try { return loader.getTestSuite().getTestSuiteFolder().toAbsolutePath().toString(); } catch (Exception e) { handleException(new SakuliException(e, String.format("cannot resolve the folder path of the current testsuite '%s'", loader.getTestSuite()))); return null; } } }